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, ©right, &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