1 /*
2  * This file is part of Siril, an astronomy image processor.
3  * Copyright (C) 2005-2011 Francois Meyer (dulle at free.fr)
4  * Copyright (C) 2012-2021 team free-astro (see more in AUTHORS file)
5  * Reference site is https://free-astro.org/index.php/Siril
6  *
7  * Siril is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * Siril is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with Siril. If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <stdint.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <math.h>
28 
29 #ifdef HAVE_LIBTIFF
30 #define uint64 uint64_hack_
31 #define int64 int64_hack_
32 #include <tiffio.h>
33 #undef uint64
34 #undef int64
35 #endif
36 #ifdef HAVE_LIBJPEG
37 #include <jpeglib.h>
38 #endif
39 #ifdef HAVE_LIBPNG
40 #include <png.h>
41 #include <setjmp.h>
42 #endif
43 #ifdef HAVE_LIBRAW
44 #include <libraw/libraw.h>
45 #include <libraw/libraw_version.h>
46 #endif
47 #ifdef HAVE_LIBHEIF
48 #include <libheif/heif.h>
49 #endif
50 
51 #include "core/siril.h"
52 #include "core/proto.h"
53 #include "core/icc_profile.h"
54 #include "algos/geometry.h"
55 #include "gui/utils.h"
56 #include "gui/progress_and_log.h"
57 #include "single_image.h"
58 #include "image_format_fits.h"
59 
60 /********************* TIFF IMPORT AND EXPORT *********************/
61 
62 #ifdef HAVE_LIBTIFF
63 
readtifstrip(TIFF * tif,uint32_t width,uint32_t height,uint16_t nsamples,WORD ** data)64 static int readtifstrip(TIFF* tif, uint32_t width, uint32_t height, uint16_t nsamples, WORD **data) {
65 	uint32_t rowsperstrip;
66 	uint16_t config;
67 	int retval = nsamples;
68 
69 	TIFFGetFieldDefaulted(tif, TIFFTAG_PLANARCONFIG, &config);
70 	TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
71 
72 	size_t npixels = width * height;
73 	*data = malloc(npixels * sizeof(WORD) * nsamples);
74 	if (!*data) {
75 		PRINT_ALLOC_ERR;
76 		return OPEN_IMAGE_ERROR;
77 	}
78 	WORD *gbuf[3] = {*data, *data, *data};
79 	if (nsamples == 4) {
80 		siril_log_message(_("Alpha channel is ignored.\n"));
81 	}
82 	if ((nsamples == 3) || (nsamples == 4)) {
83 		gbuf[GLAYER] = *data + npixels;
84 		gbuf[BLAYER] = *data + npixels * 2;
85 	}
86 
87 	const tmsize_t scanline = TIFFScanlineSize(tif);
88 	WORD *buf = (WORD *)_TIFFmalloc(TIFFStripSize(tif));
89 	if (!buf) {
90 		PRINT_ALLOC_ERR;
91 		return OPEN_IMAGE_ERROR;
92 	}
93 	for (uint32_t row = 0; row < height; row += rowsperstrip){
94 		uint32_t nrow = (row + rowsperstrip > height ? height - row : rowsperstrip);
95 		switch (config) {
96 		case PLANARCONFIG_CONTIG:
97 			if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, row, 0), buf, nrow * scanline) < 0) {
98 				siril_log_color_message(_("An unexpected error was encountered while trying to read the file.\n"), "red");
99 				retval = OPEN_IMAGE_ERROR;
100 				break;
101 			}
102 			for (size_t i = 0; i < width * nrow; i++) {
103 				*gbuf[RLAYER]++ = buf[i * nsamples + 0];
104 				if ((nsamples == 3) || (nsamples == 4)) {
105 					*gbuf[GLAYER]++ = buf[i * nsamples + 1];
106 					*gbuf[BLAYER]++ = buf[i * nsamples + 2];
107 				}
108 			}
109 			break;
110 		case PLANARCONFIG_SEPARATE:
111 			if (nsamples >= 3)		//don't need to read the alpha
112 				nsamples = 3;
113 			for (int j = 0; j < nsamples; j++) {	//loop on the layer
114 				if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, row, j), buf, nrow * scanline) < 0) {
115 					siril_log_color_message(_("An unexpected error was encountered while trying to read the file.\n"), "red");
116 					retval = OPEN_IMAGE_ERROR;
117 					break;
118 				}
119 				for (size_t i = 0; i < width * nrow; i++)
120 					*gbuf[j]++ = buf[i];
121 			}
122 			break;
123 		default:
124 			siril_log_color_message(_("Unknown TIFF file.\n"), "red");
125 			retval = OPEN_IMAGE_ERROR;
126 		}
127 	}
128 	_TIFFfree(buf);
129 	return retval;
130 }
131 
readtifstrip32(TIFF * tif,uint32_t width,uint32_t height,uint16_t nsamples,float ** data)132 static int readtifstrip32(TIFF* tif, uint32_t width, uint32_t height, uint16_t nsamples, float **data) {
133 	uint32_t rowsperstrip;
134 	uint16_t config;
135 	int retval = nsamples;
136 
137 	TIFFGetFieldDefaulted(tif, TIFFTAG_PLANARCONFIG, &config);
138 	TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
139 
140 	size_t npixels = width * height;
141 	*data = malloc(npixels * sizeof(float) * nsamples);
142 	if (!*data) {
143 		PRINT_ALLOC_ERR;
144 		return OPEN_IMAGE_ERROR;
145 	}
146 	float *gbuf[3] = { *data, *data, *data };
147 	if (nsamples == 4) {
148 		siril_log_message(_("Alpha channel is ignored.\n"));
149 	}
150 	if ((nsamples == 3) || (nsamples == 4)) {
151 		gbuf[1] = *data + npixels;
152 		gbuf[2] = *data + npixels * 2;
153 	}
154 
155 	const tmsize_t scanline = TIFFScanlineSize(tif);
156 	float *buf = (float *)_TIFFmalloc(TIFFStripSize(tif));
157 	if (!buf) {
158 		PRINT_ALLOC_ERR;
159 		return OPEN_IMAGE_ERROR;
160 	}
161 	for (uint32_t row = 0; row < height; row += rowsperstrip) {
162 		uint32_t nrow = (row + rowsperstrip > height ? height - row : rowsperstrip);
163 		switch (config) {
164 		case PLANARCONFIG_CONTIG:
165 			if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, row, 0), buf, nrow * scanline) < 0) {
166 				siril_log_color_message(_("An unexpected error was encountered while trying to read the file.\n"), "red");
167 				retval = OPEN_IMAGE_ERROR;
168 				break;
169 			}
170 			for (size_t i = 0; i < width * nrow; i++) {
171 				*gbuf[RLAYER]++ = buf[i * nsamples + 0];
172 				if ((nsamples == 3) || (nsamples == 4)) {
173 					*gbuf[GLAYER]++ = buf[i * nsamples + 1];
174 					*gbuf[BLAYER]++ = buf[i * nsamples + 2];
175 				}
176 			}
177 			break;
178 		case PLANARCONFIG_SEPARATE:
179 			if (nsamples >= 3)		//don't need to read the alpha
180 				nsamples = 3;
181 			for (int j = 0; j < nsamples; j++) {	//loop on the layer
182 				if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, row, j),
183 						buf, nrow * scanline) < 0) {
184 					siril_log_color_message(_("An unexpected error was encountered while trying to read the file.\n"), "red");
185 					retval = OPEN_IMAGE_ERROR;
186 					break;
187 				}
188 				for (size_t i = 0; i < width * nrow; i++)
189 					*gbuf[j]++ = buf[i];
190 			}
191 			break;
192 		default:
193 			siril_log_color_message(_("Unknown TIFF file.\n"), "red");
194 			retval = OPEN_IMAGE_ERROR;
195 		}
196 	}
197 	_TIFFfree(buf);
198 	return retval;
199 }
200 
readtifstrip32uint(TIFF * tif,uint32_t width,uint32_t height,uint16_t nsamples,float ** data)201 static int readtifstrip32uint(TIFF* tif, uint32_t width, uint32_t height, uint16_t nsamples, float **data) {
202 	uint32_t rowsperstrip;
203 	uint16_t config;
204 	int retval = nsamples;
205 
206 	TIFFGetFieldDefaulted(tif, TIFFTAG_PLANARCONFIG, &config);
207 	TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
208 
209 	size_t npixels = width * height;
210 	*data = malloc(npixels * sizeof(float) * nsamples);
211 	if (!*data) {
212 		PRINT_ALLOC_ERR;
213 		return OPEN_IMAGE_ERROR;
214 	}
215 	float *gbuf[3] = { *data, *data, *data };
216 	if (nsamples == 4) {
217 		siril_log_message(_("Alpha channel is ignored.\n"));
218 	}
219 	if ((nsamples == 3) || (nsamples == 4)) {
220 		gbuf[1] = *data + npixels;
221 		gbuf[2] = *data + npixels * 2;
222 	}
223 
224 	const tmsize_t scanline = TIFFScanlineSize(tif);
225 	uint32_t *buf = (uint32_t *)_TIFFmalloc(TIFFStripSize(tif));
226 	if (!buf) {
227 		PRINT_ALLOC_ERR;
228 		return OPEN_IMAGE_ERROR;
229 	}
230 	for (uint32_t row = 0; row < height; row += rowsperstrip) {
231 		uint32_t nrow = (row + rowsperstrip > height ? height - row : rowsperstrip);
232 		switch (config) {
233 		case PLANARCONFIG_CONTIG:
234 			if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, row, 0), buf, nrow * scanline) < 0) {
235 				siril_log_color_message(_("An unexpected error was encountered while trying to read the file.\n"), "red");
236 				retval = OPEN_IMAGE_ERROR;
237 				break;
238 			}
239 			for (size_t i = 0; i < width * nrow; i++) {
240 				*gbuf[RLAYER]++ = buf[i * nsamples + 0] / (float) UINT32_MAX;
241 				if ((nsamples == 3) || (nsamples == 4)) {
242 					*gbuf[GLAYER]++ = buf[i * nsamples + 1] / (float) UINT32_MAX;
243 					*gbuf[BLAYER]++ = buf[i * nsamples + 2] / (float) UINT32_MAX;
244 				}
245 			}
246 			break;
247 		case PLANARCONFIG_SEPARATE:
248 			if (nsamples >= 3)		//don't need to read the alpha
249 				nsamples = 3;
250 			for (int j = 0; j < nsamples; j++) {	//loop on the layer
251 				if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, row, j),
252 						buf, nrow * scanline) < 0) {
253 					siril_log_color_message(_("An unexpected error was encountered while trying to read the file.\n"), "red");
254 					retval = OPEN_IMAGE_ERROR;
255 					break;
256 				}
257 				for (size_t i = 0; i < width * nrow; i++)
258 					*gbuf[j]++ = buf[i] / (float) UINT32_MAX;
259 			}
260 			break;
261 		default:
262 			siril_log_color_message(_("Unknown TIFF file.\n"), "red");
263 			retval = OPEN_IMAGE_ERROR;
264 		}
265 	}
266 	_TIFFfree(buf);
267 	return retval;
268 }
269 
readtif8bits(TIFF * tif,uint32_t width,uint32_t height,uint16_t nsamples,WORD ** data)270 static int readtif8bits(TIFF* tif, uint32_t width, uint32_t height, uint16_t nsamples, WORD **data) {
271 	int retval = nsamples;
272 
273 	size_t npixels = width * height;
274 	*data = malloc(npixels * sizeof(WORD) * nsamples);
275 	if (!*data) {
276 		PRINT_ALLOC_ERR;
277 		return OPEN_IMAGE_ERROR;
278 	}
279 	WORD *gbuf[3] = { *data, *data, *data };
280 	if (nsamples == 4) {
281 		siril_log_message(_("Alpha channel is ignored.\n"));
282 	}
283 	if ((nsamples == 3) || (nsamples == 4)) {
284 		gbuf[1] = *data + npixels;
285 		gbuf[2] = *data + npixels * 2;
286 	}
287 
288 	/* get the data */
289 	uint32_t *raster = (uint32_t*) _TIFFmalloc(npixels * sizeof(uint32_t));
290 	if (raster != NULL) {
291 		if (TIFFReadRGBAImage(tif, width, height, raster, 0)) {
292 			for (int j = 0; j < height; j++) {
293 				int istart = j * width;
294 				for (int i = 0; i < width; i++) {
295 					*gbuf[RLAYER]++ = (WORD)TIFFGetR(raster[istart + i]);
296 					if ((nsamples == 3) || (nsamples == 4)) {
297 						*gbuf[GLAYER]++ = (WORD)TIFFGetG(raster[istart + i]);
298 						*gbuf[BLAYER]++ = (WORD)TIFFGetB(raster[istart + i]);
299 					}
300 				}
301 			}
302 		}
303 		else {
304 			siril_log_color_message(_("An unexpected error was encountered while trying to read the file.\n"), "red");
305 			retval = OPEN_IMAGE_ERROR;
306 		}
307 		_TIFFfree(raster);
308 	}
309 	else retval = OPEN_IMAGE_ERROR;
310 	return retval;
311 }
312 
get_compression_mode()313 static uint16_t get_compression_mode() {
314 	if (!com.headless) {
315 		GtkToggleButton *button = GTK_TOGGLE_BUTTON(lookup_widget("radiobuttonCompDeflate"));
316 		if (gtk_toggle_button_get_active(button))
317 			return (uint16_t) COMPRESSION_ADOBE_DEFLATE;
318 	}
319 	return (uint16_t) COMPRESSION_NONE;
320 }
321 
Siril_TIFFOpen(const char * name,const char * mode)322 static TIFF* Siril_TIFFOpen(const char *name, const char *mode) {
323 #ifdef _WIN32
324 	wchar_t *wname;
325 
326 	wname = g_utf8_to_utf16(name, -1, NULL, NULL, NULL);
327 	if (wname == NULL) {
328 		return NULL;
329 	}
330 
331 	TIFF* tif = TIFFOpenW(wname, mode);
332 	g_free(wname);
333 	return tif;
334 #else
335 	return(TIFFOpen(name, mode));
336 #endif
337 }
338 
339 /* reads a TIFF file and stores it in the fits argument.
340  * If file loading fails, the argument is untouched.
341  */
readtif(const char * name,fits * fit,gboolean force_float)342 int readtif(const char *name, fits *fit, gboolean force_float) {
343 	int retval = 0;
344 	uint32_t height, width;
345 	uint16_t nbits, nsamples, color;
346 	WORD *data = NULL;
347 	float *fdata = NULL;
348 	uint16_t sampleformat = 0;
349 
350 	TIFF* tif = Siril_TIFFOpen(name, "r");
351 	if (!tif) {
352 		siril_log_message(_("Could not open the TIFF file %s\n"), name);
353 		return OPEN_IMAGE_ERROR;
354 	}
355 
356 	TIFFGetFieldDefaulted(tif, TIFFTAG_IMAGEWIDTH, &width);
357 	TIFFGetFieldDefaulted(tif, TIFFTAG_IMAGELENGTH, &height);
358 	TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &nsamples);
359 	TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLEFORMAT, &sampleformat);
360 	TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &nbits);
361 	TIFFGetFieldDefaulted(tif, TIFFTAG_PHOTOMETRIC, &color);
362 
363 	// Retrieve the Date/Time as in the TIFF TAG
364 	gchar *date_time = NULL;
365 	int year, month, day, h, m, s;
366 
367 	if (TIFFGetField(tif, TIFFTAG_DATETIME, &date_time)) {
368 		sscanf(date_time, "%04d:%02d:%02d %02d:%02d:%02d", &year, &month, &day, &h, &m, &s);
369 	}
370 
371 	size_t npixels = width * height;
372 
373 	switch(nbits){
374 		case 8:
375 			/* High level functions in readtif8bits: should read every 8-bit TIFF file */
376 			retval = readtif8bits(tif, width, height, nsamples, &data);
377 			break;
378 
379 		case 16:
380 			retval = readtifstrip(tif, width, height, nsamples, &data);
381 			break;
382 
383 		case 32:
384 			if (sampleformat == SAMPLEFORMAT_IEEEFP) {
385 				retval = readtifstrip32(tif, width, height, nsamples, &fdata);
386 			} else if (sampleformat == SAMPLEFORMAT_UINT) {
387 				retval = readtifstrip32uint(tif, width, height, nsamples, &fdata);
388 			} else {
389 				siril_log_color_message(_("Siril cannot read this TIFF format.\n"), "red");
390 				retval = OPEN_IMAGE_ERROR;
391 			}
392 			break;
393 
394 		default :
395 			siril_log_color_message(_("Siril cannot read this TIFF format.\n"), "red");
396 			retval = OPEN_IMAGE_ERROR;
397 	}
398 	TIFFClose(tif);
399 	if (retval < 0) {
400 		free(data);
401 		free(fdata);
402 		return OPEN_IMAGE_ERROR;
403 	}
404 	clearfits(fit);
405 	if (date_time) {
406 		GTimeZone *tz = g_time_zone_new_utc();
407 		fit->date_obs = g_date_time_new(tz, year, month, day, h, m, s);
408 		g_time_zone_unref(tz);
409 	}
410 	fit->rx = width;
411 	fit->ry = height;
412 	fit->naxes[0] = width;
413 	fit->naxes[1] = height;
414 	fit->data = data;
415 	fit->fdata = fdata;
416 	fit->binning_x = fit->binning_y = 1;
417 	if (nsamples == 1 || nsamples == 2) {
418 		fit->naxes[2] = 1;
419 		fit->naxis = 2;
420 		if (data) {
421 			fit->pdata[RLAYER] = fit->data;
422 			fit->pdata[GLAYER] = fit->data;
423 			fit->pdata[BLAYER] = fit->data;
424 		} else {
425 			fit->fpdata[RLAYER] = fit->fdata;
426 			fit->fpdata[GLAYER] = fit->fdata;
427 			fit->fpdata[BLAYER] = fit->fdata;
428 		}
429 	} else {
430 		fit->naxes[2] = 3;
431 		fit->naxis = 3;
432 		if (data) {
433 			fit->pdata[RLAYER] = fit->data;
434 			fit->pdata[GLAYER] = fit->data + npixels;
435 			fit->pdata[BLAYER] = fit->data + npixels * 2;
436 		} else {
437 			fit->fpdata[RLAYER] = fit->fdata;
438 			fit->fpdata[GLAYER] = fit->fdata + npixels;
439 			fit->fpdata[BLAYER] = fit->fdata + npixels * 2;
440 		}
441 	}
442 	switch (nbits) {
443 	case 8:
444 		fit->bitpix = BYTE_IMG;
445 		fit->type = DATA_USHORT;
446 		if (force_float) {
447 			size_t ndata = fit->naxes[0] * fit->naxes[1] * fit->naxes[2];
448 			fit_replace_buffer(fit, ushort8_buffer_to_float(fit->data, ndata), DATA_FLOAT);
449 		}
450 		break;
451 	case 16:
452 		fit->bitpix = USHORT_IMG;
453 		fit->type = DATA_USHORT;
454 		if (force_float) {
455 			size_t ndata = fit->naxes[0] * fit->naxes[1] * fit->naxes[2];
456 			fit_replace_buffer(fit, ushort_buffer_to_float(fit->data, ndata), DATA_FLOAT);
457 		}
458 		mirrorx(fit, FALSE);
459 		break;
460 	case 32:
461 		fit->bitpix = FLOAT_IMG;
462 		fit->type = DATA_FLOAT;
463 		mirrorx(fit, FALSE);
464 	}
465 	fit->orig_bitpix = fit->bitpix;
466 	g_snprintf(fit->row_order, FLEN_VALUE, "%s", "TOP-DOWN");
467 
468 	retval = nsamples;
469 
470 	gchar *basename = g_path_get_basename(name);
471 	siril_log_message(_("Reading TIFF: %d-bit file %s, %ld layer(s), %ux%u pixels\n"),
472 						nbits, basename, fit->naxes[2], fit->rx, fit->ry);
473 	g_free(basename);
474 
475 	return retval;
476 }
477 
get_tif_data_from_ui(gchar ** description,gchar ** copyright,gboolean * embeded_icc)478 static void get_tif_data_from_ui(gchar **description, gchar **copyright, gboolean *embeded_icc) {
479 	if (!com.script && !com.headless) {
480 		/*******************************************************************
481 		 * If the user saves a tif from the graphical menu, he can set
482 		 * the Description and the Copyright of the Image
483 		 ******************************************************************/
484 		GtkToggleButton *icc_toggle = GTK_TOGGLE_BUTTON(lookup_widget("check_button_icc_profile"));
485 		GtkTextView *description_txt_view = GTK_TEXT_VIEW(lookup_widget("Description_txt"));
486 		GtkTextView *copyright_txt_view = GTK_TEXT_VIEW(lookup_widget("Copyright_txt"));
487 		GtkTextBuffer *desbuf = gtk_text_view_get_buffer(description_txt_view);
488 		GtkTextBuffer *copybuf = gtk_text_view_get_buffer(copyright_txt_view);
489 		GtkTextIter itDebut;
490 		GtkTextIter itFin;
491 
492 		gtk_text_buffer_get_start_iter(desbuf, &itDebut);
493 		gtk_text_buffer_get_end_iter(desbuf, &itFin);
494 		*description = gtk_text_buffer_get_text(desbuf, &itDebut, &itFin, TRUE);
495 		gtk_text_buffer_get_bounds(desbuf, &itDebut, &itFin);
496 		gtk_text_buffer_delete(desbuf, &itDebut, &itFin);
497 
498 		gtk_text_buffer_get_start_iter(copybuf, &itDebut);
499 		gtk_text_buffer_get_end_iter(copybuf, &itFin);
500 		*copyright = gtk_text_buffer_get_text(copybuf, &itDebut, &itFin, TRUE);
501 		gtk_text_buffer_get_bounds(copybuf, &itDebut, &itFin);
502 		gtk_text_buffer_delete(copybuf, &itDebut, &itFin);
503 
504 		*embeded_icc = gtk_toggle_button_get_active(icc_toggle);
505 	}
506 }
507 
508 /*** This function save the current image into a uncompressed 8- or 16-bit file *************/
509 
savetif(const char * name,fits * fit,uint16_t bitspersample)510 int savetif(const char *name, fits *fit, uint16_t bitspersample){
511 	int retval = 0;
512 	float norm;
513 	gchar *description = NULL, *copyright = NULL;
514 	gchar *filename = g_strdup(name);
515 	uint32_t profile_len = 0;
516 	const unsigned char *profile;
517 	gboolean write_ok = TRUE;
518 	gboolean embeded_icc = TRUE;
519 
520 	if (!g_str_has_suffix(filename, ".tif") && (!g_str_has_suffix(filename, ".tiff"))) {
521 		filename = str_append(&filename, ".tif");
522 	}
523 
524 	TIFF* tif = Siril_TIFFOpen(filename, "w");
525 	if (!tif) {
526 		siril_log_color_message(_("Siril cannot create TIFF file.\n"), "red");
527 		free(filename);
528 		return 1;
529 	}
530 	const uint16_t nsamples = (uint16_t) fit->naxes[2];
531 	const uint32_t width = (uint32_t) fit->rx;
532 	const uint32_t height = (uint32_t) fit->ry;
533 
534 	get_tif_data_from_ui(&description, &copyright, &embeded_icc);
535 
536 	/*******************************************************************/
537 
538 	/* TIFF TAG FIELD */
539 	TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bitspersample);
540 	TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, bitspersample == 32 ? SAMPLEFORMAT_IEEEFP : SAMPLEFORMAT_UINT);
541 	TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width);
542 	TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height);
543 	TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
544 	TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, -1));
545 	TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
546 	TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, nsamples);
547 	TIFFSetField(tif, TIFFTAG_COMPRESSION, get_compression_mode());
548 	if (description) {
549 		TIFFSetField(tif, TIFFTAG_IMAGEDESCRIPTION, description);
550 		g_free(description);
551 	}
552 	if (copyright) {
553 		TIFFSetField(tif, TIFFTAG_COPYRIGHT, copyright);
554 		g_free(copyright);
555 	}
556 	TIFFSetField(tif, TIFFTAG_MINSAMPLEVALUE, fit->mini);
557 	TIFFSetField(tif, TIFFTAG_MAXSAMPLEVALUE, fit->maxi);
558 	TIFFSetField(tif, TIFFTAG_SOFTWARE, PACKAGE " v" VERSION);
559 
560 	if (nsamples == 1) {
561 		TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
562 		profile = get_gray_profile_data(&profile_len);
563 	} else if (nsamples == 3) {
564 		TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
565 		profile = get_sRGB_profile_data(&profile_len);
566 	} else {
567 		TIFFClose(tif);
568 		siril_log_color_message(_("TIFF file has unexpected number of channels (not 1 or 3).\n"), "red");
569 		free(filename);
570 		return 1;
571 	}
572 
573 	if (fit->date_obs) {
574 		gchar *date_time = g_date_time_format(fit->date_obs, "%Y:%m:%d %H:%M:%S");
575 
576 		TIFFSetField(tif, TIFFTAG_DATETIME, date_time);
577 		g_free(date_time);
578 	}
579 
580 	if (embeded_icc && profile_len > 0) {
581 		TIFFSetField(tif, TIFFTAG_ICCPROFILE, profile_len, profile);
582 	}
583 
584 	WORD *gbuf[3] =	{ fit->pdata[RLAYER], fit->pdata[GLAYER], fit->pdata[BLAYER] };
585 	float *gbuff[3] = { fit->fpdata[RLAYER], fit->fpdata[GLAYER], fit->fpdata[BLAYER] };
586 
587 	switch (bitspersample) {
588 	case 8:
589 		siril_debug_print("Saving 8-bit TIFF file.\n");
590 		BYTE *buf8 = _TIFFmalloc(width * sizeof(unsigned char) * nsamples);
591 		if (!buf8) {
592 			PRINT_ALLOC_ERR;
593 			retval = OPEN_IMAGE_ERROR;
594 			write_ok = FALSE;
595 			break;
596 		}
597 
598 		norm = fit->orig_bitpix != BYTE_IMG ? UCHAR_MAX_SINGLE / USHRT_MAX_SINGLE : 1.f;
599 
600 		for (uint32_t row = height; row-- > 0;) {
601 			for (uint32_t col = 0; col < width; col++) {
602 				for (uint16_t n = 0; n < nsamples; n++) {
603 					buf8[col * nsamples + n] =
604 							(fit->type == DATA_USHORT) ?
605 									gbuf[n][col + row * width] * norm :
606 									float_to_uchar_range(gbuff[n][col + row * width]);
607 				}
608 			}
609 			if (TIFFWriteScanline(tif, buf8, height - 1 - row, 0) < 0) {
610 				siril_debug_print("Error while writing in TIFF File.\n");
611 				retval = OPEN_IMAGE_ERROR;
612 				write_ok = FALSE;
613 				break;
614 			}
615 		}
616 		_TIFFfree(buf8);
617 		break;
618 	case 16:
619 		siril_debug_print("Saving 16-bit TIFF file.\n");
620 		WORD *buf16 = _TIFFmalloc(width * sizeof(WORD) * nsamples);
621 		if (!buf16) {
622 			PRINT_ALLOC_ERR;
623 			retval = OPEN_IMAGE_ERROR;
624 			write_ok = FALSE;
625 			break;
626 		}
627 
628 		norm = fit->orig_bitpix == BYTE_IMG ? USHRT_MAX_SINGLE / UCHAR_MAX_SINGLE : 1.f;
629 
630 		for (uint32_t row = height; row-- > 0;) {
631 			for (uint32_t col = 0; col < width; col++) {
632 				for (uint16_t n = 0; n < nsamples; n++) {
633 					buf16[col * nsamples + n] =
634 							(fit->type == DATA_USHORT) ?
635 									gbuf[n][(col + row * width)] * norm :
636 									float_to_ushort_range(gbuff[n][col + row * width]);
637 				}
638 			}
639 			if (TIFFWriteScanline(tif, buf16, height - 1 - row, 0) < 0) {
640 				siril_debug_print("Error while writing in TIFF File.\n");
641 				retval = OPEN_IMAGE_ERROR;
642 				write_ok = FALSE;
643 				break;
644 			}
645 		}
646 		_TIFFfree(buf16);
647 		break;
648 	case 32:
649 		siril_debug_print("Saving 32-bit TIFF file.\n");
650 		float *buf32 = _TIFFmalloc(width * sizeof(float) * nsamples);
651 		if (!buf32) {
652 			PRINT_ALLOC_ERR;
653 			retval = OPEN_IMAGE_ERROR;
654 			write_ok = FALSE;
655 			break;
656 		}
657 
658 		for (uint32_t row = height; row-- > 0;) {
659 			for (uint32_t col = 0; col < width; col++) {
660 				for (uint16_t n = 0; n < nsamples; n++) {
661 					buf32[col * nsamples + n] =
662 							(fit->type == DATA_USHORT) ?
663 									(fit->orig_bitpix == BYTE_IMG ?
664 											gbuf[n][col + row * width] / UCHAR_MAX_SINGLE :
665 											gbuf[n][col + row * width] / USHRT_MAX_SINGLE) : gbuff[n][col + row * width];
666 				}
667 			}
668 			if (TIFFWriteScanline(tif, buf32, height - 1 - row, 0) < 0) {
669 				siril_debug_print("Error while writing in TIFF File.\n");
670 				retval = OPEN_IMAGE_ERROR;
671 				write_ok = FALSE;
672 				break;
673 			}
674 		}
675 		_TIFFfree(buf32);
676 		break;
677 	default:		// Should not happen
678 		retval = OPEN_IMAGE_ERROR;
679 		write_ok = FALSE;
680 	}
681 
682 	if (TIFFFlush(tif) != 1) {
683 		write_ok = FALSE;
684 	}
685 
686 	TIFFClose(tif);
687 
688 	if (!write_ok) {
689 		siril_log_color_message(_("Saving TIFF: Cannot write TIFF file.\n"), "red");
690 		retval = OPEN_IMAGE_ERROR;
691 		g_remove(filename);
692 	} else {
693 		siril_log_message(_("Saving TIFF: %d-bit file %s, %ld layer(s), %ux%u pixels\n"),
694 				bitspersample, filename, nsamples, width, height);
695 	}
696 
697 	g_free(filename);
698 	return retval;
699 }
700 #endif	// HAVE_LIBTIFF
701 
702 
703 /********************* JPEG IMPORT AND EXPORT *********************/
704 
705 #ifdef HAVE_LIBJPEG
readjpg(const char * name,fits * fit)706 int readjpg(const char* name, fits *fit){
707 	struct jpeg_decompress_struct cinfo;
708 	struct jpeg_error_mgr jerr;
709 
710 	FILE *f = g_fopen(name, "rb");
711 	if (f == NULL) {
712 		siril_log_color_message(_("Sorry but Siril cannot open the file: %s.\n"), "red", name);
713 		return OPEN_IMAGE_ERROR;
714 	}
715 	cinfo.err = jpeg_std_error(&jerr);
716 	jpeg_create_decompress(&cinfo);
717 	jpeg_stdio_src(&cinfo, f);
718 	jpeg_read_header(&cinfo, TRUE);
719 	jpeg_start_decompress(&cinfo);
720 
721 	size_t npixels = cinfo.output_width * cinfo.output_height;
722 	WORD *data = malloc(npixels * sizeof(WORD) * 3);
723 	if (!data) {
724 		PRINT_ALLOC_ERR;
725 		fclose(f);
726 		return OPEN_IMAGE_ERROR;
727 	}
728 	WORD *buf[3] = { data, data + npixels, data + npixels * 2 };
729 	int row_stride = cinfo.output_width * cinfo.output_components;
730 	JSAMPARRAY pJpegBuffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE,	row_stride, 1);
731 
732 	while (cinfo.output_scanline < cinfo.output_height) {
733 		jpeg_read_scanlines(&cinfo, pJpegBuffer, 1);
734 		for (int i = 0; i < cinfo.output_width; i++) {
735 			*buf[RLAYER]++ = pJpegBuffer[0][cinfo.output_components * i + 0];
736 			*buf[GLAYER]++ = pJpegBuffer[0][cinfo.output_components * i + 1];
737 			*buf[BLAYER]++ = pJpegBuffer[0][cinfo.output_components * i + 2];
738 		}
739 	}
740 
741 	fclose(f);
742 	jpeg_finish_decompress(&cinfo);
743 	jpeg_destroy_decompress(&cinfo);
744 	clearfits(fit);
745 	fit->bitpix = fit->orig_bitpix = BYTE_IMG;
746 	if (cinfo.output_components == 1)
747 		fit->naxis = 2;
748 	else
749 		fit->naxis = 3;
750 	fit->rx = cinfo.output_width;
751 	fit->ry = cinfo.output_height;
752 	fit->naxes[0] = cinfo.output_width;
753 	fit->naxes[1] = cinfo.output_height;
754 	fit->naxes[2] = cinfo.output_components;
755 	fit->data = data;
756 	fit->pdata[RLAYER] = fit->data;
757 	fit->pdata[GLAYER] = fit->data + npixels;
758 	fit->pdata[BLAYER] = fit->data + npixels * 2;
759 	fit->binning_x = fit->binning_y = 1;
760 	fit->type = DATA_USHORT;
761 	mirrorx(fit, FALSE);
762 	gchar *basename = g_path_get_basename(name);
763 	siril_log_message(_("Reading JPG: file %s, %ld layer(s), %ux%u pixels\n"),
764 			basename, fit->naxes[2], fit->rx, fit->ry);
765 	g_free(basename);
766 
767 	return cinfo.output_components;
768 }
769 
savejpg(const char * name,fits * fit,int quality)770 int savejpg(const char *name, fits *fit, int quality){
771 	struct jpeg_compress_struct cinfo;    // Basic info for JPEG properties.
772 	struct jpeg_error_mgr jerr;           // In case of error.
773 
774 	//## ALLOCATE AND INITIALIZE JPEG COMPRESSION OBJECT
775 	cinfo.err = jpeg_std_error(&jerr);
776 	jpeg_create_compress(&cinfo);
777 
778 	char *filename = strdup(name);
779 	if (!g_str_has_suffix(filename, ".jpg") && (!g_str_has_suffix(filename, ".jpeg"))) {
780 		filename = str_append(&filename, ".jpg");
781 	}
782 
783 	//## OPEN FILE FOR DATA DESTINATION:
784 	FILE *f = g_fopen(filename, "wb");
785 	if (f == NULL) {
786 		siril_log_color_message(_("Siril cannot create JPG file.\n"), "red");
787 		free(filename);
788 		return 1;
789 	}
790 	jpeg_stdio_dest(&cinfo, f);
791 
792 	//## SET PARAMETERS FOR COMPRESSION:
793 	cinfo.image_width  = fit->rx;   // |-- Image width and height in pixels.
794 	cinfo.image_height = fit->ry;   // |
795 	cinfo.input_components = fit->naxes[2];     // Number of color components per pixel.
796 	cinfo.in_color_space = (fit->naxes[2] == 3) ? JCS_RGB : JCS_GRAYSCALE; // Colorspace of input image as RGB.
797 
798 	WORD *gbuf[3] =	{ fit->pdata[RLAYER], fit->pdata[GLAYER], fit->pdata[BLAYER] };
799 	float *gbuff[3] = { fit->fpdata[RLAYER], fit->fpdata[GLAYER], fit->fpdata[BLAYER] };
800 
801 	jpeg_set_defaults(&cinfo);
802 	jpeg_set_quality(&cinfo, quality, TRUE);
803 
804 	//## CREATE IMAGE BUFFER TO WRITE FROM AND MODIFY THE IMAGE TO LOOK LIKE CHECKERBOARD:
805 	unsigned char *image_buffer = (unsigned char*) malloc(
806 			cinfo.image_width * cinfo.image_height * cinfo.num_components);
807 	if (!image_buffer) {
808 		PRINT_ALLOC_ERR;
809 		free(filename);
810 		fclose(f);
811 		return 1;
812 	}
813 
814 	float norm = (fit->orig_bitpix != BYTE_IMG ?
815 			UCHAR_MAX_SINGLE / USHRT_MAX_SINGLE : 1.f);
816 
817 	for (int i = (cinfo.image_height - 1); i >= 0; i--) {
818 		for (int j = 0; j < cinfo.image_width; j++) {
819 			int pixelIdx = ((i * cinfo.image_width) + j) * cinfo.input_components;
820 			if (fit->type == DATA_USHORT) {
821 				WORD red = *gbuf[RLAYER]++;
822 				image_buffer[pixelIdx + 0] = round_to_BYTE(red * norm); // r |-- Set r,g,b components to
823 				if (cinfo.input_components == 3) {
824 					WORD green = *gbuf[GLAYER]++;
825 					WORD blue = *gbuf[BLAYER]++;
826 					image_buffer[pixelIdx + 1] = round_to_BYTE(green * norm); // g |   make this pixel
827 					image_buffer[pixelIdx + 2] = round_to_BYTE(blue * norm); // b |
828 				}
829 			} else {
830 				float red = *gbuff[RLAYER]++;
831 				image_buffer[pixelIdx + 0] = float_to_uchar_range(red); // r |-- Set r,g,b components to
832 				if (cinfo.input_components == 3) {
833 					float green = *gbuff[GLAYER]++;
834 					float blue = *gbuff[BLAYER]++;
835 					image_buffer[pixelIdx + 1] = float_to_uchar_range(green); // g |   make this pixel
836 					image_buffer[pixelIdx + 2] = float_to_uchar_range(blue); // b |
837 				}
838 			}
839 		}
840 	}
841 	//## START COMPRESSION:
842 	jpeg_start_compress(&cinfo, TRUE);
843 	int row_stride = cinfo.image_width * cinfo.input_components;        // JSAMPLEs per row in image_buffer
844 
845 	JSAMPROW row_pointer[1];
846 	while (cinfo.next_scanline < cinfo.image_height) {
847 		row_pointer[0] = &image_buffer[cinfo.next_scanline * row_stride];
848 		(void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
849 	}
850 	// NOTE: jpeg_write_scanlines expects an array of pointers to scanlines.
851 	//       Here the array is only one element long, but you could pass
852 	//       more than one scanline at a time if that's more convenient.
853 
854 	//## FINISH COMPRESSION AND CLOSE FILE:
855 	jpeg_finish_compress(&cinfo);
856 
857 	fclose(f);
858 	jpeg_destroy_compress(&cinfo);
859 	free(image_buffer);
860 	siril_log_message(_("Saving JPG: file %s, quality=%d%%, %ld layer(s), %ux%u pixels\n"),
861 						filename, quality, fit->naxes[2], fit->rx, fit->ry);
862 	free(filename);
863 	return OPEN_IMAGE_OK;
864 }
865 
866 #endif	// HAVE_LIBJPEG
867 
868 /********************* PNG IMPORT *********************/
869 
870 #ifdef HAVE_LIBPNG
871 /* reads a PNG file and stores it in the fits argument.
872  */
readpng(const char * name,fits * fit)873 int readpng(const char *name, fits* fit) {
874 	png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL,	NULL);
875 	if (!png) {
876 		siril_log_color_message(_("Sorry but Siril cannot open the file: %s.\n"), "red", name);
877 		return OPEN_IMAGE_ERROR;
878 	}
879 
880 	png_infop info = png_create_info_struct(png);
881 	if (!info)
882 		return OPEN_IMAGE_ERROR;
883 
884 	if (setjmp(png_jmpbuf(png)))
885 		return OPEN_IMAGE_ERROR;
886 
887 	FILE *f = g_fopen(name, "rb");
888 	png_init_io(png, f);
889 
890 	png_read_info(png, info);
891 
892 	const int width = png_get_image_width(png, info);
893 	const int height = png_get_image_height(png, info);
894 	size_t npixels = width * height;
895 	png_byte color_type = png_get_color_type(png, info);
896 	png_byte bit_depth = png_get_bit_depth(png, info);
897 
898 	WORD *data = malloc(npixels * sizeof(WORD) * 3);
899 	if (!data) {
900 		PRINT_ALLOC_ERR;
901 		fclose(f);
902 		return OPEN_IMAGE_ERROR;
903 	}
904 	WORD *buf[3] = { data, data + npixels, data + npixels * 2 };
905 
906 	if (color_type == PNG_COLOR_TYPE_PALETTE)
907 		png_set_palette_to_rgb(png);
908 
909 	// PNG_COLOR_TYPE_GRAY_ALPHA is always 8 or 16bit depth.
910 	if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
911 		png_set_expand_gray_1_2_4_to_8(png);
912 
913 	if (png_get_valid(png, info, PNG_INFO_tRNS))
914 		png_set_tRNS_to_alpha(png);
915 
916 	// These color_type don't have an alpha channel then fill it with 0xff.
917 	if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY
918 			|| color_type == PNG_COLOR_TYPE_PALETTE)
919 		png_set_filler(png, 0xFF, PNG_FILLER_AFTER);
920 
921 	if (color_type == PNG_COLOR_TYPE_GRAY
922 			|| color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
923 		png_set_gray_to_rgb(png);
924 
925 	png_read_update_info(png, info);
926 
927 	png_bytep *row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * height);
928 	for (int y = 0; y < height; y++) {
929 		row_pointers[y] = (png_byte*) malloc(png_get_rowbytes(png, info));
930 	}
931 
932 	png_read_image(png, row_pointers);
933 
934 	fclose(f);
935 
936 	if (bit_depth == 16) {			//in 16-bit: it is stored as RRGGBB
937 		for (int y = height - 1; y > -1; y--) {
938 			png_byte* row = row_pointers[y];
939 			for (int x = 0; x < width; x++) {
940 				png_byte* ptr = &(row[x * 8]);
941 				*buf[RLAYER]++ = ptr[0] * (UCHAR_MAX + 1) + ptr[1];
942 				*buf[GLAYER]++ = ptr[2] * (UCHAR_MAX + 1) + ptr[3];
943 				*buf[BLAYER]++ = ptr[4] * (UCHAR_MAX + 1) + ptr[5];
944 			}
945 		}
946 	} else {
947 		for (int y = height - 1; y > -1; y--) {
948 			png_byte* row = row_pointers[y];
949 			for (int x = 0; x < width; x++) {
950 				png_byte* ptr = &(row[x * 4]);
951 				*buf[RLAYER]++ = ptr[0];
952 				*buf[GLAYER]++ = ptr[1];
953 				*buf[BLAYER]++ = ptr[2];
954 			}
955 		}
956 	}
957 	// We define the number of channel we have
958 	int nbplanes;
959 	switch (color_type) {
960 	case PNG_COLOR_TYPE_RGB_ALPHA:
961 	case PNG_COLOR_TYPE_RGB:
962 		nbplanes = 3;
963 		break;
964 	case PNG_COLOR_TYPE_GRAY_ALPHA:
965 	case PNG_COLOR_TYPE_GRAY:
966 		nbplanes = 1;
967 		break;
968 	default:
969 		nbplanes = 0;
970 	}
971 
972 	// free allocated memory
973 	for (int y = 0; y < height; y++)
974 		free(row_pointers[y]);
975 	free(row_pointers);
976 
977 	if (data != NULL) {
978 		clearfits(fit);
979 		fit->rx = width;
980 		fit->ry = height;
981 		fit->naxes[0] = width;
982 		fit->naxes[1] = height;
983 		fit->naxes[2] = nbplanes;
984 		if (nbplanes == 1)
985 			fit->naxis = 2;
986 		else
987 			fit->naxis = 3;
988 		fit->bitpix = (bit_depth == 16) ? USHORT_IMG : BYTE_IMG;
989 		fit->type = DATA_USHORT;
990 		fit->orig_bitpix = fit->bitpix;
991 		fit->data = data;
992 		fit->pdata[RLAYER] = fit->data;
993 		fit->pdata[GLAYER] = fit->data + npixels;
994 		fit->pdata[BLAYER] = fit->data + npixels * 2;
995 		fit->binning_x = fit->binning_y = 1;
996 		g_snprintf(fit->row_order, FLEN_VALUE, "%s", "TOP-DOWN");
997 	}
998 	gchar *basename = g_path_get_basename(name);
999 	siril_log_message(_("Reading PNG: %d-bit file %s, %ld layer(s), %ux%u pixels\n"),
1000 			bit_depth, basename, fit->naxes[2], fit->rx, fit->ry);
1001 	g_free(basename);
1002 
1003 	return nbplanes;
1004 }
1005 
convert_data(fits * image)1006 static WORD *convert_data(fits *image) {
1007 	size_t ndata = image->rx * image->ry;
1008 	int ch = image->naxes[2];
1009 
1010 	WORD *buffer = malloc(ndata * ch * sizeof(WORD));
1011 	if (!buffer) {
1012 		PRINT_ALLOC_ERR;
1013 		return NULL;
1014 	}
1015 	for (size_t i = 0, j = 0; i < ndata * ch; i += ch, j++) {
1016 		if (image->type == DATA_USHORT) {
1017 			buffer[i + 0] = image->pdata[RLAYER][j];
1018 			if (ch > 1) {
1019 				buffer[i + 1] = image->pdata[GLAYER][j];
1020 				buffer[i + 2] = image->pdata[BLAYER][j];
1021 			}
1022 		} else if (image->type == DATA_FLOAT) {
1023 			buffer[i + 0] = float_to_ushort_range(image->fpdata[RLAYER][j]);
1024 			if (ch > 1) {
1025 				buffer[i + 1] = float_to_ushort_range(image->fpdata[GLAYER][j]);
1026 				buffer[i + 2] = float_to_ushort_range(image->fpdata[BLAYER][j]);
1027 			}
1028 		}
1029 		else {
1030 			free(buffer);
1031 			return NULL;
1032 		}
1033 	}
1034 	return buffer;
1035 }
1036 
convert_data8(fits * image)1037 static uint8_t *convert_data8(fits *image) {
1038 	size_t ndata = image->rx * image->ry;
1039 	const long ch = image->naxes[2];
1040 
1041 	uint8_t *buffer = malloc(ndata * ch * sizeof(uint8_t));
1042 	if (!buffer) {
1043 		PRINT_ALLOC_ERR;
1044 		return NULL;
1045 	}
1046 	for (size_t i = 0, j = 0; i < ndata * ch; i += ch, j++) {
1047 		if (image->type == DATA_USHORT) {
1048 			buffer[i + 0] = (uint8_t) image->pdata[RLAYER][j];
1049 			if (ch > 1) {
1050 				buffer[i + 1] = (uint8_t) image->pdata[GLAYER][j];
1051 				buffer[i + 2] = (uint8_t) image->pdata[BLAYER][j];
1052 			}
1053 		} else if (image->type == DATA_FLOAT) {
1054 			buffer[i + 0] = float_to_uchar_range(image->fpdata[RLAYER][j]);
1055 			if (ch > 1) {
1056 				buffer[i + 1] = float_to_uchar_range(image->fpdata[GLAYER][j]);
1057 				buffer[i + 2] = float_to_uchar_range(image->fpdata[BLAYER][j]);
1058 			}
1059 		}
1060 		else {
1061 			free(buffer);
1062 			return NULL;
1063 		}
1064 	}
1065 	return buffer;
1066 }
1067 
savepng(const char * name,fits * fit,uint32_t bytes_per_sample,gboolean is_colour)1068 int savepng(const char *name, fits *fit, uint32_t bytes_per_sample,
1069 		gboolean is_colour) {
1070 	int32_t ret = -1;
1071 	png_structp png_ptr;
1072 	png_infop info_ptr;
1073 	const uint32_t width = fit->rx;
1074 	const uint32_t height = fit->ry;
1075 
1076 	char *filename = strdup(name);
1077 	if (!g_str_has_suffix(filename, ".png")) {
1078 		filename = str_append(&filename, ".png");
1079 	}
1080 
1081 	FILE *p_png_file = g_fopen(filename, "wb");
1082 	if (p_png_file == NULL) {
1083 		return ret;
1084 	}
1085 
1086 	/* Create and initialize the png_struct with the desired error handler
1087 	 * functions.  If you want to use the default stderr and longjump method,
1088 	 * you can supply NULL for the last three parameters.  We also check that
1089 	 * the library version is compatible with the one used at compile time,
1090 	 * in case we are using dynamically linked libraries.  REQUIRED.
1091 	 */
1092 	png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1093 	if (png_ptr == NULL) {
1094 		fclose(p_png_file);
1095 		return ret;
1096 	}
1097 
1098 	/* Allocate/initialize the image information data.  REQUIRED */
1099 	info_ptr = png_create_info_struct(png_ptr);
1100 	if (info_ptr == NULL) {
1101 		fclose(p_png_file);
1102 		png_destroy_write_struct(&png_ptr, NULL);
1103 		return ret;
1104 	}
1105 
1106 	/* Set error handling.  REQUIRED if you aren't supplying your own
1107 	 * error handling functions in the png_create_write_struct() call.
1108 	 */
1109 	if (setjmp(png_jmpbuf(png_ptr))) {
1110 		/* If we get here, we had a problem writing the file */
1111 		fclose(p_png_file);
1112 		png_destroy_write_struct(&png_ptr, &info_ptr);
1113 		return ret;
1114 	}
1115 
1116 	/* Set up the output control if you are using standard C streams */
1117 	png_init_io(png_ptr, p_png_file);
1118 
1119 	/* Set the image information here.  Width and height are up to 2^31,
1120 	 * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
1121 	 * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
1122 	 * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
1123 	 * or PNG_COLOR_TYPE_RGB_ALPHA.  interlace is either PNG_INTERLACE_NONE or
1124 	 * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
1125 	 * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
1126 	 */
1127 	uint32_t profile_len = 0;
1128 	const unsigned char *profile;
1129 
1130 	if (is_colour) {
1131 		png_set_IHDR(png_ptr, info_ptr, width, height, bytes_per_sample * 8,
1132 				PNG_COLOR_TYPE_RGB,
1133 				PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
1134 				PNG_FILTER_TYPE_DEFAULT);
1135 		profile = get_sRGB_profile_data(&profile_len);
1136 
1137 		if (profile_len > 0) {
1138 			png_set_iCCP(png_ptr, info_ptr, "icc", 0, (png_const_bytep) profile, profile_len);
1139 		}
1140 	} else {
1141 		png_set_IHDR(png_ptr, info_ptr, width, height, bytes_per_sample * 8,
1142 				PNG_COLOR_TYPE_GRAY,
1143 				PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
1144 				PNG_FILTER_TYPE_DEFAULT);
1145 		profile = get_gray_profile_data(&profile_len);
1146 	}
1147 
1148 	if (profile_len > 0) {
1149 		png_set_iCCP(png_ptr, info_ptr, "icc", 0, (png_const_bytep) profile, profile_len);
1150 	}
1151 
1152 	/* Write the file header information.  REQUIRED */
1153 	png_write_info(png_ptr, info_ptr);
1154 
1155 	png_bytep *row_pointers = malloc((size_t) height * sizeof(png_bytep));
1156 
1157 	int samples_per_pixel;
1158 	if (is_colour) {
1159 		samples_per_pixel = 3;
1160 	} else {
1161 		samples_per_pixel = 1;
1162 	}
1163 
1164 	if (bytes_per_sample == 2) {
1165 		/* swap bytes of 16 bit files to most significant bit first */
1166 		png_set_swap(png_ptr);
1167 		WORD *data = convert_data(fit);
1168 		for (unsigned i = 0, j = height - 1; i < height; i++)
1169 			row_pointers[j--] = (png_bytep) ((uint16_t*) data + (size_t) samples_per_pixel * i * width);
1170 	} else {
1171 		uint8_t *data = convert_data8(fit);
1172 		for (unsigned i = 0, j = height - 1; i < height; i++)
1173 			row_pointers[j--] = (uint8_t*) data + (size_t) samples_per_pixel * i * width;
1174 	}
1175 
1176 	png_write_image(png_ptr, row_pointers);
1177 
1178 	/* Clean up after the write, and free any memory allocated */
1179 	png_write_end(png_ptr, info_ptr);
1180 	png_destroy_write_struct(&png_ptr, &info_ptr);
1181 
1182 	siril_log_message(_("Saving PNG: file %s, %ld layer(s), %ux%u pixels\n"),
1183 				filename, fit->naxes[2], fit->rx, fit->ry);
1184 
1185 	/* Close the file */
1186 	fclose(p_png_file);
1187 	free(row_pointers);
1188 	free(filename);
1189 	return 0;
1190 }
1191 #endif	// HAVE_LIBPNG
1192 
1193 /********************* RAW IMPORT *********************/
1194 #ifdef HAVE_LIBRAW
1195 
1196 #if LIBRAW_VERSION < LIBRAW_MAKE_VERSION(0, 18, 0)
1197 #define LIBRAW_FORMAT_1INCH 5
1198 #endif
1199 
1200 /* this is an estimation of the pixel size. Indeed, we cannot know
1201  * the real width resolution with libraw.
1202  * However, this approximation should be good enough.
1203  */
estimate_pixel_pitch(libraw_data_t * raw)1204 static float estimate_pixel_pitch(libraw_data_t *raw) {
1205 	float s_width;
1206 
1207 	switch (raw->lens.makernotes.CameraFormat) {
1208 	case LIBRAW_FORMAT_APSC:
1209 		if (!g_ascii_strncasecmp("Canon", raw->idata.make, 5))
1210 			s_width = 22.3f;
1211 		else
1212 			s_width = 23.6f;
1213 		break;
1214 	case LIBRAW_FORMAT_FF:
1215 		if (!g_ascii_strncasecmp("Sony", raw->idata.make, 4))
1216 			s_width = 35.6f;
1217 		else
1218 			s_width = 36.0f;
1219 		break;
1220 	case LIBRAW_FORMAT_FT:
1221 		s_width = 17.3f;
1222 		break;
1223 	case LIBRAW_FORMAT_APSH:
1224 		s_width = 28.7f;
1225 		break;
1226 	case LIBRAW_FORMAT_1INCH:
1227 		s_width = 13.2f;
1228 		break;
1229 	case LIBRAW_FORMAT_MF:
1230 		s_width = 44.0f;
1231 		break;
1232 	default:
1233 		s_width = 0.0f;
1234 		break;
1235 	}
1236 //	printf("s_width=%f\n", s_width);
1237 	float pitch = s_width / (float) raw->sizes.width * 1000.f;
1238 	return roundf(pitch * 100.f) / 100.f;
1239 }
1240 
siril_libraw_open_file(libraw_data_t * rawdata,const char * name)1241 static int siril_libraw_open_file(libraw_data_t* rawdata, const char *name) {
1242 /* libraw_open_wfile is not defined for all windows compilers */
1243 #if defined(_WIN32) && !defined(__MINGW32__) && defined(_MSC_VER) && (_MSC_VER > 1310)
1244 	wchar_t *wname;
1245 
1246 	wname = g_utf8_to_utf16(name, -1, NULL, NULL, NULL);
1247 	if (wname == NULL) {
1248 		return 1;
1249 	}
1250 
1251 	int ret = libraw_open_wfile(rawdata, wname);
1252 	g_free(wname);
1253 	return ret;
1254 #elif defined(_WIN32)
1255 	gchar *localefilename = g_win32_locale_filename_from_utf8(name);
1256 	int ret = libraw_open_file(rawdata, localefilename);
1257 	g_free(localefilename);
1258 	return ret;
1259 #else
1260 	return(libraw_open_file(rawdata, name));
1261 #endif
1262 }
1263 
readraw(const char * name,fits * fit)1264 static int readraw(const char *name, fits *fit) {
1265 	libraw_data_t *raw = libraw_init(0);
1266 
1267 	int ret = siril_libraw_open_file(raw, name);
1268 	if (ret) {
1269 		siril_log_color_message(_("Error in libraw %s.\n"), "red", libraw_strerror(ret));
1270 		libraw_recycle(raw);
1271 		libraw_close(raw);
1272 		return OPEN_IMAGE_ERROR;
1273 	}
1274 
1275 	if (raw->other.shutter > 1.0)
1276 		siril_log_message(_("Decoding %s %s file (ISO=%g, Exposure=%gs)\n"),
1277 				raw->idata.make, raw->idata.model, raw->other.iso_speed, raw->other.shutter);
1278 	else
1279 		siril_log_message(_("Decoding %s %s file (ISO=%g, Exposure=1/%gs)\n"),
1280 				raw->idata.make, raw->idata.model, raw->other.iso_speed, 1/raw->other.shutter);
1281 
1282 	raw->params.output_bps = 16;						/* 16-bits files                           */
1283 	raw->params.four_color_rgb = 0;						/* If == 1, interpolate RGB as four colors.*/
1284 	raw->params.no_auto_bright = 1;						/* no auto_bright                          */
1285 	raw->params.gamm[0] = 1.0 / com.pref.raw_set.gamm[0];    /* Gamma curve set by the user             */
1286 	raw->params.gamm[1] = com.pref.raw_set.gamm[1];
1287 	raw->params.bright = com.pref.raw_set.bright;			/* Brightness                              */
1288 	raw->params.user_flip = 0;							/* no flip                                 */
1289 	raw->params.use_camera_wb = com.pref.raw_set.use_camera_wb;
1290 	raw->params.use_auto_wb = com.pref.raw_set.use_auto_wb;
1291 	if (com.pref.raw_set.user_black == 1)
1292 		raw->params.user_black = 0;						/* user black level equivalent to dcraw -k 0 */
1293 	raw->params.output_color = 0;						/* output colorspace, 0=raw, 1=sRGB, 2=Adobe, 3=Wide, 4=ProPhoto, 5=XYZ*/
1294 
1295 	if (!(com.pref.raw_set.auto_mul)) { /* 4 multipliers (r,g,b,g) of the user's white balance.    */
1296 		raw->params.user_mul[0] = (float) com.pref.raw_set.mul[0];
1297 		raw->params.user_mul[1] = raw->params.user_mul[3] = 1.0f;
1298 		raw->params.user_mul[2] = (float) com.pref.raw_set.mul[2];
1299 		siril_log_message(_("Daylight multipliers: %f, %f, %f\n"),
1300 				raw->params.user_mul[0], raw->params.user_mul[1],
1301 				raw->params.user_mul[2]);
1302 	} else {
1303 		float mul[4]; /* 3 multipliers (r,g,b) from the camera white balance.  */
1304 		mul[0] = raw->color.pre_mul[0] / raw->color.pre_mul[1];
1305 		mul[1] = 1.0; /* raw->color.pre_mul[1]/raw->color.pre_mul[1]; */
1306 		mul[2] = raw->color.pre_mul[2] / raw->color.pre_mul[1];
1307 		mul[3] = raw->color.pre_mul[3] / raw->color.pre_mul[1];
1308 		siril_log_message(_("Daylight multipliers: %f, %f, %f\n"), mul[0],
1309 				mul[1], mul[2]);
1310 	}
1311 
1312 	if (raw->idata.filters == 9) {
1313 		siril_log_color_message(_("XTRANS Sensor detected.\n"), "salmon");
1314 	}
1315 
1316 	switch (com.pref.raw_set.user_qual) { /* Set interpolation                                        */
1317 	case 0: /* bilinear interpolaton */
1318 		raw->params.user_qual = 0;
1319 		siril_log_message(_("Bilinear interpolation...\n"));
1320 		break;
1321 	case 2: /* VNG interpolaton */
1322 		raw->params.user_qual = 1;
1323 		siril_log_message(_("VNG interpolation...\n"));
1324 		break;
1325 	case 3: /* PPG interpolaton */
1326 		raw->params.user_qual = 2;
1327 		siril_log_message(_("PPG interpolation...\n"));
1328 		break;
1329 	default:
1330 	case 1: /* AHD interpolaton */
1331 		raw->params.user_qual = 3;
1332 		siril_log_message(_("AHD interpolation...\n"));
1333 		break;
1334 	}
1335 
1336 	const ushort width = raw->sizes.iwidth;
1337 	const ushort height = raw->sizes.iheight;
1338 	const float pitch = estimate_pixel_pitch(raw);
1339 	size_t npixels = width * height;
1340 
1341 	WORD *data = malloc(npixels * sizeof(WORD) * 3);
1342 	if (!data) {
1343 		PRINT_ALLOC_ERR;
1344 		return OPEN_IMAGE_ERROR;
1345 	}
1346 	WORD *buf[3] = { data, data + npixels, data + npixels * 2 };
1347 	ret = libraw_unpack(raw);
1348 	if (ret) {
1349 		printf("Error in libraw %s\n", libraw_strerror(ret));
1350 		free(data);
1351 		libraw_recycle(raw);
1352 		libraw_close(raw);
1353 		return OPEN_IMAGE_ERROR;
1354 	}
1355 
1356 	ret = libraw_dcraw_process(raw);
1357 	if (ret) {
1358 		printf("Error in libraw %s\n", libraw_strerror(ret));
1359 		free(data);
1360 		libraw_recycle(raw);
1361 		libraw_close(raw);
1362 		return OPEN_IMAGE_ERROR;
1363 	}
1364 
1365 	libraw_processed_image_t *image = libraw_dcraw_make_mem_image(raw, &ret);
1366 	if (ret) {
1367 		printf("Error in libraw %s\n", libraw_strerror(ret));
1368 		free(data);
1369 		libraw_dcraw_clear_mem(image);
1370 		libraw_recycle(raw);
1371 		libraw_close(raw);
1372 		return OPEN_IMAGE_ERROR;
1373 	}
1374 
1375 	int nbplanes = image->colors;
1376 	if (nbplanes != 3) {
1377 		free(data);
1378 		libraw_dcraw_clear_mem(image);
1379 		libraw_recycle(raw);
1380 		libraw_close(raw);
1381 		return OPEN_IMAGE_ERROR;
1382 	}
1383 	// only for 16-bits because of endianness. Are there 8-bits RAW ???
1384 
1385 	for (unsigned int i = 0; i < image->data_size; i += 6) {
1386 		*buf[RLAYER]++ = (image->data[i + 0]) + (image->data[i + 1] << 8);
1387 		*buf[GLAYER]++ = (image->data[i + 2]) + (image->data[i + 3] << 8);
1388 		*buf[BLAYER]++ = (image->data[i + 4]) + (image->data[i + 5] << 8);
1389 	}
1390 
1391 	/*  Here we compute the correct size of the output image (imgdata.sizes.iwidth and imgdata.sizes.iheight) for the following cases:
1392     	- Files from Fuji cameras (with a 45-degree rotation)
1393     	- Files from cameras with non-square pixels
1394     	- Images shot by a rotated camera.
1395 	 */
1396 	libraw_adjust_sizes_info_only(raw);
1397 
1398 	if (data != NULL) {
1399 		clearfits(fit);
1400 		fit->bitpix = fit->orig_bitpix = USHORT_IMG;
1401 		fit->type = DATA_USHORT;
1402 		fit->rx = (unsigned int) width;
1403 		fit->ry = (unsigned int) height;
1404 		fit->naxes[0] = (long) width;
1405 		fit->naxes[1] = (long) height;
1406 		fit->naxes[2] = nbplanes;
1407 		fit->naxis = 3;
1408 		fit->data = data;
1409 		fit->pdata[RLAYER] = fit->data;
1410 		fit->pdata[GLAYER] = fit->data + npixels;
1411 		fit->pdata[BLAYER] = fit->data + npixels * 2;
1412 		fit->binning_x = fit->binning_y = 1;
1413 		if (pitch > 0.f)
1414 			fit->pixel_size_x = fit->pixel_size_y = pitch;
1415 		if (raw->other.focal_len > 0.f)
1416 			fit->focal_length = raw->other.focal_len;
1417 		if (raw->other.iso_speed > 0.f)
1418 			fit->iso_speed = raw->other.iso_speed;
1419 		if (raw->other.shutter > 0.f)
1420 			fit->exposure = raw->other.shutter;
1421 		if (raw->other.aperture > 0.f)
1422 			fit->aperture = raw->other.aperture;
1423 		g_snprintf(fit->instrume, FLEN_VALUE, "%s %s", raw->idata.make,
1424 				raw->idata.model);
1425 		fit->date_obs = g_date_time_new_from_unix_utc(raw->other.timestamp);
1426 		mirrorx(fit, FALSE);
1427 	}
1428 
1429 	libraw_dcraw_clear_mem(image);
1430 	libraw_recycle(raw);
1431 	libraw_close(raw);
1432 
1433 	return nbplanes;
1434 }
1435 
1436 #define FC(filters, row, col) \
1437 	(filters >> ((((row) << 1 & 14) + ((col) & 1)) << 1) & 3)
1438 
1439 static const char filter[16][16] =
1440 { { 2,1,1,3,2,3,2,0,3,2,3,0,1,2,1,0 },
1441   { 0,3,0,2,0,1,3,1,0,1,1,2,0,3,3,2 },
1442   { 2,3,3,2,3,1,1,3,3,1,2,1,2,0,0,3 },
1443   { 0,1,0,1,0,2,0,2,2,0,3,0,1,3,2,1 },
1444   { 3,1,1,2,0,1,0,2,1,3,1,3,0,1,3,0 },
1445   { 2,0,0,3,3,2,3,1,2,0,2,0,3,2,2,1 },
1446   { 2,3,3,1,2,1,2,1,2,1,1,2,3,0,0,1 },
1447   { 1,0,0,2,3,0,0,3,0,3,0,3,2,1,2,3 },
1448   { 2,3,3,1,1,2,1,0,3,2,3,0,2,3,1,3 },
1449   { 1,0,2,0,3,0,3,2,0,1,1,2,0,1,0,2 },
1450   { 0,1,1,3,3,2,2,1,1,3,3,0,2,1,3,2 },
1451   { 2,3,2,0,0,1,3,0,2,0,1,2,3,0,1,0 },
1452   { 1,3,1,2,3,2,3,2,0,2,0,1,1,0,3,0 },
1453   { 0,2,0,3,1,0,0,1,1,3,3,2,3,2,2,1 },
1454   { 2,1,3,2,3,1,2,1,0,3,0,2,0,2,0,2 },
1455   { 0,3,1,0,0,2,0,3,2,1,3,1,1,3,1,3 } };
1456 
fcol(libraw_data_t * raw,int row,int col)1457 static int fcol(libraw_data_t *raw, int row, int col) {
1458 	if (raw->idata.filters == 1)
1459 		return filter[(row + raw->rawdata.sizes.top_margin) & 15][(col
1460 				+ raw->rawdata.sizes.left_margin) & 15];
1461 	if (raw->idata.filters == 9)
1462 		return raw->idata.xtrans[(row + 6) % 6][(col + 6) % 6];
1463 	return FC(raw->idata.filters, row, col);
1464 }
1465 
readraw_in_cfa(const char * name,fits * fit)1466 static int readraw_in_cfa(const char *name, fits *fit) {
1467 	libraw_data_t *raw = libraw_init(0);
1468 	char pattern[FLEN_VALUE];
1469 
1470 	int ret = siril_libraw_open_file(raw, name);
1471 	if (ret) {
1472 		printf("Error in libraw %s\n", libraw_strerror(ret));
1473 		return OPEN_IMAGE_ERROR;
1474 	}
1475 
1476 	ret = libraw_unpack(raw);
1477 	if (ret) {
1478 		printf("Error in libraw %s\n", libraw_strerror(ret));
1479 		return OPEN_IMAGE_ERROR;
1480 	}
1481 
1482 	/* This test checks if raw data exist. Sometimes it doesn't. This is
1483 	 * the case for DNG built from lightroom for example */
1484 	if (raw->rawdata.raw_image == NULL
1485 			&& (raw->rawdata.color3_image || raw->rawdata.color4_image)) {
1486 		siril_log_color_message(_("Siril cannot open this file in CFA mode (no data available). "
1487 				"Try to switch into RGB.\n"), "red");
1488 		return OPEN_IMAGE_ERROR;
1489 	}
1490 
1491 	raw->params.user_flip = 0;				/* no flip                                 */
1492 	raw->params.output_color = 0;			/* output colorspace, 0=raw, 1=sRGB, 2=Adobe, 3=Wide, 4=ProPhoto, 5=XYZ*/
1493 
1494 	const ushort raw_width = raw->sizes.raw_width;
1495 	const ushort raw_height = raw->sizes.raw_height;
1496 	const ushort left_margin = raw->rawdata.sizes.left_margin;
1497 	const ushort top_margin = raw->rawdata.sizes.top_margin;
1498 
1499 	ushort width, height;
1500 
1501 	if (raw->rawdata.ioparams.fuji_width) {
1502 		const ushort right_margin = raw_width - raw->rawdata.ioparams.fuji_width
1503 				- left_margin;
1504 		width = raw_width - right_margin;
1505 		height = raw_height;
1506 	} else {
1507 		width = raw->sizes.iwidth;
1508 		height = raw->sizes.iheight;
1509 	}
1510 
1511 	float pitch = estimate_pixel_pitch(raw);
1512 	size_t npixels = width * height;
1513 
1514 	if (raw->other.shutter > 0 && raw->other.shutter < 1)
1515 		siril_log_message(_("Decoding %s %s file (ISO=%g, Exposure=1/%0.1f sec)\n"),
1516 						raw->idata.make, raw->idata.model, raw->other.iso_speed, 1/raw->other.shutter);
1517 	else
1518 		siril_log_message(_("Decoding %s %s file (ISO=%g, Exposure=%0.1f sec)\n"),
1519 						raw->idata.make, raw->idata.model, raw->other.iso_speed, raw->other.shutter);
1520 
1521 	unsigned filters = raw->idata.filters;
1522 
1523 	if (filters) {
1524 		int fhigh = 2, fwide = 2;
1525 		if ((filters ^ (filters >> 8)) & 0xff)
1526 			fhigh = 4;
1527 		if ((filters ^ (filters >> 16)) & 0xffff)
1528 			fhigh = 8;
1529 		if (filters == 1) /* Leaf Catchlight with 16x16 bayer matrix */
1530 			fhigh = fwide = 16;
1531 		if (filters == 9) /* Fuji X-Trans (6x6 matrix) */
1532 			fhigh = fwide = 6;
1533 
1534 		int j = 0;
1535 		for (int i = 0; i < fhigh; i++) {
1536 			for (int c = i /*&& (pattern[j++] = '/')*/ && 0; c < fwide; c++) {
1537 				pattern[j++] = raw->idata.cdesc[fcol(raw, i, c)];
1538 			}
1539 		}
1540 		pattern[j++] = '\0';
1541 		siril_log_message(_("Filter pattern: %s\n"), pattern);
1542 	}
1543 
1544 	WORD *data = (WORD*) calloc(1, npixels * sizeof(WORD));
1545 	if (!data) {
1546 		PRINT_ALLOC_ERR;
1547 		libraw_recycle(raw);
1548 		libraw_close(raw);
1549 		return OPEN_IMAGE_ERROR;
1550 	}
1551 
1552 	WORD *buf = data;
1553 
1554 	int offset = raw_width * top_margin + left_margin;
1555 
1556 	int i = 0;
1557 	for (int row = height - 1; row > -1; row--) {
1558 		for (int col = 0; col < width; col++) {
1559 			buf[i++] = raw->rawdata.raw_image[offset + col + (raw_width * row)];
1560 		}
1561 	}
1562 
1563 	clearfits(fit);
1564 	fit->bitpix = fit->orig_bitpix = USHORT_IMG;
1565 	fit->type = DATA_USHORT;
1566 	fit->rx = (unsigned int) (width);
1567 	fit->ry = (unsigned int) (height);
1568 	fit->naxes[0] = (long) (width);
1569 	fit->naxes[1] = (long) (height);
1570 	fit->naxes[2] = 1;
1571 	fit->naxis = 2;
1572 	fit->data = data;
1573 	fit->pdata[RLAYER] = fit->data;
1574 	fit->pdata[GLAYER] = fit->data;
1575 	fit->pdata[BLAYER] = fit->data;
1576 	fit->binning_x = fit->binning_y = 1;
1577 	if (pitch > 0.f)
1578 		fit->pixel_size_x = fit->pixel_size_y = pitch;
1579 	if (raw->other.focal_len > 0.f)
1580 		fit->focal_length = raw->other.focal_len;
1581 	if (raw->other.iso_speed > 0.f)
1582 		fit->iso_speed = raw->other.iso_speed;
1583 	if (raw->other.shutter > 0.f)
1584 		fit->exposure = raw->other.shutter;
1585 	if (raw->other.aperture > 0.f)
1586 		fit->aperture = raw->other.aperture;
1587 	g_snprintf(fit->instrume, FLEN_VALUE, "%s %s", raw->idata.make,
1588 			raw->idata.model);
1589 	fit->date_obs = g_date_time_new_from_unix_utc(raw->other.timestamp);
1590 	if (filters)
1591 		g_snprintf(fit->bayer_pattern, FLEN_VALUE, "%s", pattern);
1592 
1593 	g_snprintf(fit->row_order, FLEN_VALUE, "%s", "BOTTOM-UP");
1594 
1595 	libraw_recycle(raw);
1596 	libraw_close(raw);
1597 	return 1;
1598 }
1599 
open_raw_files(const char * name,fits * fit,gboolean debayer)1600 int open_raw_files(const char *name, fits *fit, gboolean debayer) {
1601 	int retval = 1;
1602 	if (debayer)
1603 		retval = readraw(name, fit);
1604 	else retval = readraw_in_cfa(name, fit);
1605 	if (retval >= 0) {
1606 		gchar *basename = g_path_get_basename(name);
1607 		siril_log_message(_("Reading RAW: file %s, %ld layer(s), %ux%u pixels\n"),
1608 				basename, fit->naxes[2], fit->rx, fit->ry);
1609 		g_free(basename);
1610 	}
1611 	return retval;
1612 }
1613 #endif
1614 
1615 #ifdef HAVE_LIBHEIF
1616 #define MAX_THUMBNAIL_SIZE com.pref.thumbnail_size
1617 
1618 struct HeifImage {
1619 	uint32_t ID;
1620 	char caption[100]; // image text (filled with resolution description)
1621 	struct heif_image *thumbnail;
1622 	int width, height;
1623 };
1624 
load_thumbnails(struct heif_context * heif,struct HeifImage * images)1625 static gboolean load_thumbnails(struct heif_context *heif, struct HeifImage *images) {
1626 	int numImages = heif_context_get_number_of_top_level_images(heif);
1627 
1628 	// get list of all (top level) image IDs
1629 
1630 	uint32_t *IDs = malloc(numImages * sizeof(uint32_t));
1631 	heif_context_get_list_of_top_level_image_IDs(heif, IDs, numImages);
1632 
1633 	// --- Load a thumbnail for each image.
1634 
1635 	for (int i = 0; i < numImages; i++) {
1636 
1637 		images[i].ID = IDs[i];
1638 		images[i].caption[0] = 0;
1639 		images[i].thumbnail = NULL;
1640 
1641 		// get image handle
1642 
1643 		struct heif_image_handle *handle;
1644 		struct heif_error err = heif_context_get_image_handle(heif, IDs[i],
1645 				&handle);
1646 		if (err.code) {
1647 			g_printf("%s\n", err.message);
1648 			continue;
1649 		}
1650 
1651 		// generate image caption
1652 
1653 		int width = heif_image_handle_get_width(handle);
1654 		int height = heif_image_handle_get_height(handle);
1655 
1656 		if (heif_image_handle_is_primary_image(handle)) {
1657 			sprintf(images[i].caption, "%dx%d (%s)", width, height,
1658 					_("primary"));
1659 		} else {
1660 			sprintf(images[i].caption, "%dx%d", width, height);
1661 		}
1662 
1663 		// get handle to thumbnail image
1664 		// if there is no thumbnail image, just the the image itself (will be scaled down later)
1665 
1666 		struct heif_image_handle *thumbnail_handle;
1667 		heif_item_id thumbnail_ID;
1668 
1669 		int nThumbnails = heif_image_handle_get_list_of_thumbnail_IDs(handle,
1670 				&thumbnail_ID, 1);
1671 
1672 		if (nThumbnails > 0) {
1673 			err = heif_image_handle_get_thumbnail(handle, thumbnail_ID,
1674 					&thumbnail_handle);
1675 			if (err.code) {
1676 				g_printf("%s\n", err.message);
1677 				continue;
1678 			}
1679 		} else {
1680 			err = heif_context_get_image_handle(heif, IDs[i],
1681 					&thumbnail_handle);
1682 			if (err.code) {
1683 				g_printf("%s\n", err.message);
1684 				continue;
1685 			}
1686 		}
1687 
1688 		// decode the thumbnail image
1689 
1690 		struct heif_image *thumbnail_img;
1691 		err = heif_decode_image(thumbnail_handle, &thumbnail_img,
1692 				heif_colorspace_RGB, heif_chroma_interleaved_24bit,
1693 				NULL);
1694 		if (err.code) {
1695 			g_printf("%s\n", err.message);
1696 			continue;
1697 		}
1698 
1699 		// if thumbnail image size exceeds the maximum, scale it down
1700 
1701 		int thumbnail_width = heif_image_handle_get_width(thumbnail_handle);
1702 		int thumbnail_height = heif_image_handle_get_height(thumbnail_handle);
1703 
1704 		if (thumbnail_width > MAX_THUMBNAIL_SIZE
1705 				|| thumbnail_height > MAX_THUMBNAIL_SIZE) {
1706 
1707 			// compute scaling factor to fit into a max sized box
1708 
1709 			float factor_h = thumbnail_width / (float) MAX_THUMBNAIL_SIZE;
1710 			float factor_v = thumbnail_height / (float) MAX_THUMBNAIL_SIZE;
1711 
1712 			int new_width, new_height;
1713 
1714 			if (factor_v > factor_h) {
1715 				new_height = MAX_THUMBNAIL_SIZE;
1716 				new_width = thumbnail_width / factor_v;
1717 			} else {
1718 				new_height = thumbnail_height / factor_h;
1719 				new_width = MAX_THUMBNAIL_SIZE;
1720 			}
1721 
1722 			// scale the image
1723 
1724 			struct heif_image *scaled_img = NULL;
1725 
1726 			err = heif_image_scale_image(thumbnail_img,
1727 					&scaled_img, new_width, new_height,
1728 					NULL);
1729 			if (err.code) {
1730 				g_printf("%s\n", err.message);
1731 				continue;
1732 			}
1733 
1734 			// release the old image and only keep the scaled down version
1735 
1736 			heif_image_release(thumbnail_img);
1737 			thumbnail_img = scaled_img;
1738 
1739 			thumbnail_width = new_width;
1740 			thumbnail_height = new_height;
1741 		}
1742 
1743 		heif_image_handle_release(thumbnail_handle);
1744 		heif_image_handle_release(handle);
1745 
1746 		// remember the HEIF thumbnail image (we need it for the GdkPixbuf)
1747 
1748 		images[i].thumbnail = thumbnail_img;
1749 
1750 		images[i].width = thumbnail_width;
1751 		images[i].height = thumbnail_height;
1752 	}
1753 
1754 	return TRUE;
1755 }
1756 
heif_dialog(struct heif_context * heif,uint32_t * selected_image)1757 static gboolean heif_dialog(struct heif_context *heif, uint32_t *selected_image) {
1758 	int numImages = heif_context_get_number_of_top_level_images(heif);
1759 
1760 	struct HeifImage *heif_images = malloc(numImages * sizeof(struct HeifImage));
1761 	gboolean success = load_thumbnails(heif, heif_images);
1762 	if (!success) {
1763 		free(heif_images);
1764 		return FALSE;
1765 	}
1766 
1767 	GtkWidget *dlg = gtk_dialog_new_with_buttons(_("Load HEIF image content"),
1768 			GTK_WINDOW(lookup_widget("control_window")), GTK_DIALOG_MODAL,
1769 			_("_Cancel"), GTK_RESPONSE_CANCEL, _("_OK"), GTK_RESPONSE_OK, NULL);
1770 	gtk_dialog_set_default_response(GTK_DIALOG(dlg), GTK_RESPONSE_OK);
1771 
1772 	GtkContainer *content_area = GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dlg)));
1773 	gtk_container_set_border_width(GTK_CONTAINER(content_area), 12);
1774 
1775 	GtkWidget *frame = gtk_frame_new(_("Select image"));
1776 	gtk_container_add(content_area, GTK_WIDGET(frame));
1777 	gtk_widget_show(frame);
1778 
1779 // prepare list store with all thumbnails and caption
1780 
1781 	GtkListStore *liststore;
1782 	GtkTreeIter iter;
1783 
1784 	liststore = gtk_list_store_new(2, G_TYPE_STRING, GDK_TYPE_PIXBUF);
1785 
1786 	for (int i = 0; i < numImages; i++) {
1787 		gtk_list_store_append(liststore, &iter);
1788 		gtk_list_store_set(liststore, &iter, 0, heif_images[i].caption, -1);
1789 
1790 		int stride;
1791 		const uint8_t *data = heif_image_get_plane_readonly(
1792 				heif_images[i].thumbnail, heif_channel_interleaved, &stride);
1793 
1794 		GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB,
1795 				FALSE, 8, heif_images[i].width, heif_images[i].height, stride,
1796 				NULL, NULL);
1797 		gtk_list_store_set(liststore, &iter, 1, pixbuf, -1);
1798 	}
1799 
1800 	GtkWidget *iconview = gtk_icon_view_new();
1801 	gtk_icon_view_set_model((GtkIconView*) iconview, (GtkTreeModel*) liststore);
1802 	gtk_icon_view_set_text_column((GtkIconView*) iconview, 0);
1803 	gtk_icon_view_set_pixbuf_column((GtkIconView*) iconview, 1);
1804 	gtk_icon_view_set_item_width((GtkIconView*) iconview, MAX_THUMBNAIL_SIZE);
1805 
1806 	GtkWidget *scroll = gtk_scrolled_window_new(NULL, NULL);
1807 	gtk_widget_set_size_request(scroll, -1, 400);
1808 	g_object_set(scroll, "expand", TRUE, NULL);
1809 
1810 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),	GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
1811 
1812 	gtk_container_add(GTK_CONTAINER(frame), scroll);
1813 	gtk_container_add(GTK_CONTAINER(scroll), iconview);
1814 
1815 	gtk_widget_show(scroll);
1816 	gtk_widget_show(iconview);
1817 
1818 // pre-select the primary image
1819 
1820 	int selected_idx = -1;
1821 	for (int i = 0; i < numImages; i++) {
1822 		if (heif_images[i].ID == *selected_image) {
1823 			selected_idx = i;
1824 			break;
1825 		}
1826 	}
1827 
1828 	if (selected_idx != -1) {
1829 		GtkTreePath *path = gtk_tree_path_new_from_indices(selected_idx, -1);
1830 		gtk_icon_view_select_path((GtkIconView*) iconview, path);
1831 		gtk_tree_path_free(path);
1832 	}
1833 
1834 	gtk_widget_show(dlg);
1835 
1836 	gboolean run = (gtk_dialog_run(GTK_DIALOG(dlg)) == GTK_RESPONSE_OK);
1837 
1838 	if (run) {
1839 		GList *selected_items = gtk_icon_view_get_selected_items(
1840 				(GtkIconView*) iconview);
1841 
1842 		if (selected_items) {
1843 			GtkTreePath *path = (GtkTreePath*) (selected_items->data);
1844 			gint *indices = gtk_tree_path_get_indices(path);
1845 
1846 			*selected_image = heif_images[indices[0]].ID;
1847 
1848 			g_list_free_full(selected_items,
1849 					(GDestroyNotify) gtk_tree_path_free);
1850 		}
1851 	}
1852 
1853 	gtk_widget_destroy(dlg);
1854 
1855 // release thumbnail images
1856 
1857 	for (int i = 0; i < numImages; i++) {
1858 		heif_image_release(heif_images[i].thumbnail);
1859 	}
1860 
1861 	free(heif_images);
1862 
1863 	return run;
1864 }
1865 
readheif(const char * name,fits * fit,gboolean interactive)1866 int readheif(const char* name, fits *fit, gboolean interactive){
1867 	struct heif_error err;
1868 
1869 	struct heif_context *ctx = heif_context_alloc();
1870 	err = heif_context_read_from_file(ctx, name, NULL);
1871 	if (err.code) {
1872 		g_printf("%s\n", err.message);
1873 		heif_context_free(ctx);
1874 		return OPEN_IMAGE_ERROR;
1875 	}
1876 
1877 	// analyze image content
1878 	int num = heif_context_get_number_of_top_level_images(ctx);
1879 	if (num == 0) {
1880 		siril_log_color_message(_("Input file contains no readable images.\n"), "red");
1881 		heif_context_free(ctx);
1882 		return OPEN_IMAGE_ERROR;
1883 	}
1884 
1885 	  // get the primary image
1886 
1887 	heif_item_id primary;
1888 
1889 	err = heif_context_get_primary_image_ID(ctx, &primary);
1890 	if (err.code) {
1891 		g_printf("%s\n", err.message);
1892 		heif_context_free(ctx);
1893 		return OPEN_IMAGE_ERROR;
1894 	}
1895 
1896 	// if primary image is no top level image or not present (invalid file), just take the first image
1897 
1898 	if (!heif_context_is_top_level_image_ID(ctx, primary)) {
1899 		int n = heif_context_get_list_of_top_level_image_IDs(ctx, &primary, 1);
1900 		g_assert(n == 1);
1901 	}
1902 
1903 	heif_item_id selected_image = primary;
1904 
1905 	if (num > 1) {
1906 		if (!interactive) {
1907 			siril_log_message(_("This is a sequence of %d images: "
1908 					"loading the primary one.\n"), num);
1909 		} else {
1910 			if (!heif_dialog(ctx, &selected_image)) {
1911 				heif_context_free(ctx);
1912 				return OPEN_IMAGE_CANCEL;
1913 			}
1914 		}
1915 	}
1916 
1917 	// get a handle to the primary image
1918 	struct heif_image_handle *handle;
1919 	err = heif_context_get_image_handle(ctx, selected_image, &handle);
1920 	if (err.code) {
1921 		g_printf("%s\n", err.message);
1922 		heif_context_free(ctx);
1923 		return OPEN_IMAGE_ERROR;
1924 	}
1925 
1926 	int has_alpha = heif_image_handle_has_alpha_channel(handle);
1927 
1928 	struct heif_image *img = 0;
1929 	err = heif_decode_image(handle, &img, heif_colorspace_RGB,
1930 			has_alpha ? heif_chroma_interleaved_32bit :
1931 			heif_chroma_interleaved_24bit, NULL);
1932 	if (err.code) {
1933 		g_printf("%s\n", err.message);
1934 		heif_image_handle_release(handle);
1935 		heif_context_free(ctx);
1936 		return OPEN_IMAGE_ERROR;
1937 	}
1938 
1939 	int stride;
1940 	const uint8_t* udata = heif_image_get_plane_readonly(img, heif_channel_interleaved, &stride);
1941 	const int width = heif_image_get_width(img, heif_channel_interleaved);
1942 	const int height = heif_image_get_height(img, heif_channel_interleaved);
1943 
1944 	size_t npixels = width * height;
1945 
1946 	WORD *data = malloc(npixels * sizeof(WORD) * 3);
1947 	if (!data) {
1948 		PRINT_ALLOC_ERR;
1949 		heif_image_handle_release(handle);
1950 		heif_context_free(ctx);
1951 		return OPEN_IMAGE_ERROR;
1952 	}
1953 	WORD *buf[3] = { data, data + npixels, data + npixels * 2 };
1954 
1955 	unsigned int nchannels = has_alpha ? 4 : 3;
1956 	for (int row = 0; row < height; row += stride) {
1957 		int nrow = (row + stride > height ? height - row : stride);
1958 		for (int i = 0; i < width * nrow; i++) {
1959 			*buf[RLAYER]++ = udata[i * nchannels + RLAYER];
1960 			*buf[GLAYER]++ = udata[i * nchannels + GLAYER];
1961 			*buf[BLAYER]++ = udata[i * nchannels + BLAYER];
1962 		}
1963 	}
1964 
1965 	clearfits(fit);
1966 	fit->bitpix = fit->orig_bitpix = BYTE_IMG;
1967 	fit->type = DATA_USHORT;
1968 	fit->naxis = 3;
1969 	fit->rx = width;
1970 	fit->ry = height;
1971 	fit->naxes[0] = fit->rx;
1972 	fit->naxes[1] = fit->ry;
1973 	fit->naxes[2] = 3;
1974 	fit->data = data;
1975 	fit->pdata[RLAYER] = fit->data;
1976 	fit->pdata[GLAYER] = fit->data + npixels;
1977 	fit->pdata[BLAYER] = fit->data + npixels * 2;
1978 	fit->binning_x = fit->binning_y = 1;
1979 	mirrorx(fit, FALSE);
1980 
1981 	heif_image_handle_release(handle);
1982 	heif_context_free(ctx);
1983 	heif_image_release(img);
1984 	gchar *basename = g_path_get_basename(name);
1985 	siril_log_message(_("Reading HEIF: file %s, %ld layer(s), %ux%u pixels\n"),
1986 			basename, fit->naxes[2], fit->rx, fit->ry);
1987 	g_free(basename);
1988 
1989 	return OPEN_IMAGE_OK;
1990 }
1991 #endif
1992