Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
imageio_jpeg.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2009-2011, 2014 johannes hanika.
4 Copyright (C) 2010 Henrik Andersson.
5 Copyright (C) 2012 Christian Tellefsen.
6 Copyright (C) 2012 Michal Babej.
7 Copyright (C) 2012 Richard Wonka.
8 Copyright (C) 2012, 2014-2017 Tobias Ellinghaus.
9 Copyright (C) 2012 Ulrich Pegelow.
10 Copyright (C) 2013-2016 Roman Lebedev.
11 Copyright (C) 2019, 2022, 2025-2026 Aurélien PIERRE.
12 Copyright (C) 2019-2021 Pascal Obry.
13 Copyright (C) 2019 Philippe Weyland.
14 Copyright (C) 2020 Heiko Bauke.
15 Copyright (C) 2020 Hubert Kowalski.
16 Copyright (C) 2021 Miloš Komarčević.
17 Copyright (C) 2022 Martin Bařinka.
18 Copyright (C) 2023-2024 Alynx Zhou.
19
20 darktable is free software: you can redistribute it and/or modify
21 it under the terms of the GNU General Public License as published by
22 the Free Software Foundation, either version 3 of the License, or
23 (at your option) any later version.
24
25 darktable is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 GNU General Public License for more details.
29
30 You should have received a copy of the GNU General Public License
31 along with darktable. If not, see <http://www.gnu.org/licenses/>.
32*/
33
34
35
36#ifdef HAVE_CONFIG_H
37#include "config.h"
39#endif
40#include "metadata/exif.h"
43#include "develop/imageop.h" // for IOP_CS_RGB
44#include <glib/gstdio.h>
45#include <setjmp.h>
47
48// error functions
49
51{
52 struct jpeg_error_mgr pub;
53 jmp_buf setjmp_buffer;
55
57
58static void dt_imageio_jpeg_error_exit(j_common_ptr cinfo)
59{
61 (*cinfo->err->output_message)(cinfo);
62 longjmp(myerr->setjmp_buffer, 1);
63}
64
65// destination functions
66static void dt_imageio_jpeg_init_destination(j_compress_ptr cinfo)
67{
68}
69static boolean dt_imageio_jpeg_empty_output_buffer(j_compress_ptr cinfo)
70{
71 fprintf(stderr, "[imageio_jpeg] output buffer full!\n");
72 return FALSE;
73}
74static void dt_imageio_jpeg_term_destination(j_compress_ptr cinfo)
75{
76}
77
78// source functions
79static void dt_imageio_jpeg_init_source(j_decompress_ptr cinfo)
80{
81}
82static boolean dt_imageio_jpeg_fill_input_buffer(j_decompress_ptr cinfo)
83{
84 return 1;
85}
86static void dt_imageio_jpeg_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
87{
88 ssize_t i = cinfo->src->bytes_in_buffer - num_bytes;
89 if(i < 0) i = 0;
90 cinfo->src->bytes_in_buffer = i;
91 cinfo->src->next_input_byte += num_bytes;
92}
93static void dt_imageio_jpeg_term_source(j_decompress_ptr cinfo)
94{
95}
96
97
98/*
99 * Since an ICC profile can be larger than the maximum size of a JPEG marker
100 * (64K), we need provisions to split it into multiple markers. The format
101 * defined by the ICC specifies one or more APP2 markers containing the
102 * following data:
103 * Identifying string ASCII "ICC_PROFILE\0" (12 bytes)
104 * Marker sequence number 1 for first APP2, 2 for next, etc (1 byte)
105 * Number of markers Total number of APP2's used (1 byte)
106 * Profile data (remainder of APP2 data)
107 * Decoders should use the marker sequence numbers to reassemble the profile,
108 * rather than assuming that the APP2 markers appear in the correct sequence.
109 */
110
111#define EXIF_MARKER (JPEG_APP0 + 1) /* JPEG marker code for Exif */
112#define ICC_MARKER (JPEG_APP0 + 2) /* JPEG marker code for ICC */
113#define ICC_OVERHEAD_LEN 14 /* size of non-profile data in APP2 */
114#define MAX_BYTES_IN_MARKER 65533 /* maximum data len of a JPEG marker */
115#define MAX_DATA_BYTES_IN_MARKER (MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN)
116
117
118/*
119 * Prepare for reading an ICC profile
120 */
121
122static void setup_read_icc_profile(j_decompress_ptr cinfo)
123{
124 /* Tell the library to keep any APP2 data it may find */
125 jpeg_save_markers(cinfo, ICC_MARKER, 0xFFFF);
126}
127
128/*
129 * Prepare for reading an Exif blob
130 */
131
132static void setup_read_exif(j_decompress_ptr cinfo)
133{
134 /* Tell the library to keep any APP1 data it may find */
135 jpeg_save_markers(cinfo, EXIF_MARKER, 0xFFFF);
136}
137
138
139int dt_imageio_jpeg_decompress_header(const void *in, size_t length, dt_imageio_jpeg_t *jpg)
140{
141 jpeg_create_decompress(&(jpg->dinfo));
142 jpg->src.init_source = dt_imageio_jpeg_init_source;
143 jpg->src.fill_input_buffer = dt_imageio_jpeg_fill_input_buffer;
144 jpg->src.skip_input_data = dt_imageio_jpeg_skip_input_data;
145 jpg->src.resync_to_restart = jpeg_resync_to_restart;
146 jpg->src.term_source = dt_imageio_jpeg_term_source;
147 jpg->src.next_input_byte = (JOCTET *)in;
148 jpg->src.bytes_in_buffer = length;
149
150 struct dt_imageio_jpeg_error_mgr jerr;
151 jpg->dinfo.err = jpeg_std_error(&jerr.pub);
152 jerr.pub.error_exit = dt_imageio_jpeg_error_exit;
153 if(setjmp(jerr.setjmp_buffer))
154 {
155 jpeg_destroy_decompress(&(jpg->dinfo));
156 return 1;
157 }
158
159 jpg->dinfo.src = &(jpg->src);
160 setup_read_exif(&(jpg->dinfo));
162 jpeg_read_header(&(jpg->dinfo), TRUE);
163#ifdef JCS_EXTENSIONS
164 jpg->dinfo.out_color_space = JCS_EXT_RGBX;
165 jpg->dinfo.out_color_components = 4;
166#else
167 jpg->dinfo.out_color_space = JCS_RGB;
168 jpg->dinfo.out_color_components = 3;
169#endif
170 // jpg->dinfo.buffered_image = TRUE;
171 jpg->width = jpg->dinfo.image_width;
172 jpg->height = jpg->dinfo.image_height;
173 return 0;
174}
175
176#ifdef JCS_EXTENSIONS
177static int decompress_jsc(dt_imageio_jpeg_t *jpg, uint8_t *out)
178{
179 uint8_t *tmp = out;
180 while(jpg->dinfo.output_scanline < jpg->dinfo.image_height)
181 {
182 if(jpeg_read_scanlines(&(jpg->dinfo), &tmp, 1) != 1)
183 {
184 return 1;
185 }
186 tmp += 4 * jpg->width;
187 }
188 return 0;
189}
190#endif
191
192static int decompress_plain(dt_imageio_jpeg_t *jpg, uint8_t *out)
193{
194 JSAMPROW row_pointer[1];
195 row_pointer[0] = (uint8_t *)dt_pixelpipe_cache_alloc_align_cache(
196 (size_t)jpg->dinfo.output_width * jpg->dinfo.num_components, 0);
197 if(row_pointer[0] == NULL) return 1;
198 uint8_t *tmp = out;
199 while(jpg->dinfo.output_scanline < jpg->dinfo.image_height)
200 {
201 if(jpeg_read_scanlines(&(jpg->dinfo), row_pointer, 1) != 1)
202 {
203 dt_pixelpipe_cache_free_align(row_pointer[0]);
204 return 1;
205 }
206 for(unsigned int i = 0; i < jpg->dinfo.image_width; i++)
207 {
208 for(int k = 0; k < 3; k++) tmp[4 * i + k] = row_pointer[0][3 * i + k];
209 }
210 tmp += 4 * jpg->width;
211 }
212
213 dt_pixelpipe_cache_free_align(row_pointer[0]);
214 return 0;
215}
216
218{
219 struct dt_imageio_jpeg_error_mgr jerr;
220 jpg->dinfo.err = jpeg_std_error(&jerr.pub);
221 jerr.pub.error_exit = dt_imageio_jpeg_error_exit;
222 if(setjmp(jerr.setjmp_buffer))
223 {
224 jpeg_destroy_decompress(&(jpg->dinfo));
225 return 1;
226 }
227
228#ifdef JCS_EXTENSIONS
229 /*
230 * Do a run-time detection for JCS_EXTENSIONS:
231 * it might have been only available at build-time
232 */
233 int jcs_alpha_valid = 1;
234 if(setjmp(jerr.setjmp_buffer))
235 {
236 if(jpg->dinfo.out_color_space == JCS_EXT_RGBX && jpg->dinfo.out_color_components == 4)
237 {
238 // ok, no JCS_EXTENSIONS, fall-back to slow plain code.
239 jpg->dinfo.out_color_components = 3;
240 jpg->dinfo.out_color_space = JCS_RGB;
241 jcs_alpha_valid = 0;
242 }
243 else
244 {
245 jpeg_destroy_decompress(&(jpg->dinfo));
246 return 1;
247 }
248 }
249#endif
250
251 (void)jpeg_start_decompress(&(jpg->dinfo));
252
253 if(setjmp(jerr.setjmp_buffer))
254 {
255 jpeg_destroy_decompress(&(jpg->dinfo));
256 return 1;
257 }
258
259#ifdef JCS_EXTENSIONS
260 if(jcs_alpha_valid)
261 {
262 if(decompress_jsc(jpg, out)) return 1;
263 }
264 else
265 {
266 if(decompress_plain(jpg, out)) return 1;
267 }
268#else
269 if(decompress_plain(jpg, out)) return 1;
270#endif
271
272 if(setjmp(jerr.setjmp_buffer))
273 {
274 jpeg_destroy_decompress(&(jpg->dinfo));
275 return 1;
276 }
277
278 // jpg->dinfo.src = NULL;
279 (void)jpeg_finish_decompress(&(jpg->dinfo));
280 jpeg_destroy_decompress(&(jpg->dinfo));
281 return 0;
282}
283
284// TODO: find out why this function is not used anymore. Where do we compress ???
285int dt_imageio_jpeg_compress(const uint8_t *in, uint8_t *out, const int width, const int height,
286 const int quality)
287{
288 struct dt_imageio_jpeg_error_mgr jerr;
290 jpg.dest.init_destination = dt_imageio_jpeg_init_destination;
291 jpg.dest.empty_output_buffer = dt_imageio_jpeg_empty_output_buffer;
292 jpg.dest.term_destination = dt_imageio_jpeg_term_destination;
293 jpg.dest.next_output_byte = (JOCTET *)out;
294 jpg.dest.free_in_buffer = sizeof(uint8_t) * 4 * width * height;
295
296 jpg.cinfo.err = jpeg_std_error(&jerr.pub);
297 jerr.pub.error_exit = dt_imageio_jpeg_error_exit;
298 if(setjmp(jerr.setjmp_buffer))
299 {
300 jpeg_destroy_compress(&(jpg.cinfo));
301 return 1;
302 }
303 jpeg_create_compress(&(jpg.cinfo));
304 jpg.cinfo.dest = &(jpg.dest);
305
306 jpg.cinfo.image_width = width;
307 jpg.cinfo.image_height = height;
308 jpg.cinfo.input_components = 3;
309 jpg.cinfo.in_color_space = JCS_RGB;
310 jpeg_set_defaults(&(jpg.cinfo));
311 jpeg_set_quality(&(jpg.cinfo), quality, TRUE);
312 if(quality > 90) jpg.cinfo.comp_info[0].v_samp_factor = 1;
313 if(quality > 92) jpg.cinfo.comp_info[0].h_samp_factor = 1;
314 jpeg_start_compress(&(jpg.cinfo), TRUE);
315 JSAMPARRAY row = (*jpg.cinfo.mem->alloc_sarray)((j_common_ptr)&jpg.cinfo, JPOOL_IMAGE,
316 (JDIMENSION)(3 * width), 1);
317 const uint8_t *buf;
318 while(jpg.cinfo.next_scanline < jpg.cinfo.image_height)
319 {
320 buf = in + jpg.cinfo.next_scanline * jpg.cinfo.image_width * 4;
321 for(int i = 0; i < width; i++)
322 for(int k = 0; k < 3; k++) row[0][3 * i + k] = buf[4 * i + k];
323 jpeg_write_scanlines(&(jpg.cinfo), row, 1);
324 }
325 jpeg_finish_compress(&(jpg.cinfo));
326 jpeg_destroy_compress(&(jpg.cinfo));
327 return sizeof(uint8_t) * 4 * width * height - jpg.dest.free_in_buffer;
328}
329
330
331/*
332 * This routine writes the given ICC profile data into a JPEG file.
333 * It *must* be called AFTER calling jpeg_start_compress() and BEFORE
334 * the first call to jpeg_write_scanlines().
335 * (This ordering ensures that the APP2 marker(s) will appear after the
336 * SOI and JFIF or Adobe markers, but before all else.)
337 */
338
339static void write_icc_profile(j_compress_ptr cinfo, const JOCTET *icc_data_ptr, unsigned int icc_data_len)
340{
341 unsigned int num_markers; /* total number of markers we'll write */
342 int cur_marker = 1; /* per spec, counting starts at 1 */
343
344 /* Calculate the number of markers we'll need, rounding up of course */
345 num_markers = icc_data_len / MAX_DATA_BYTES_IN_MARKER;
346 if(num_markers * MAX_DATA_BYTES_IN_MARKER != icc_data_len) num_markers++;
347
348 while(icc_data_len > 0)
349 {
350 /* length of profile to put in this marker */
351 unsigned int length = icc_data_len;
353 icc_data_len -= length;
354
355 /* Write the JPEG marker header (APP2 code and marker length) */
356 jpeg_write_m_header(cinfo, ICC_MARKER, (unsigned int)(length + ICC_OVERHEAD_LEN));
357
358 /* Write the marker identifying string "ICC_PROFILE" (null-terminated).
359 * We code it in this less-than-transparent way so that the code works
360 * even if the local character set is not ASCII.
361 */
362 jpeg_write_m_byte(cinfo, 0x49);
363 jpeg_write_m_byte(cinfo, 0x43);
364 jpeg_write_m_byte(cinfo, 0x43);
365 jpeg_write_m_byte(cinfo, 0x5F);
366 jpeg_write_m_byte(cinfo, 0x50);
367 jpeg_write_m_byte(cinfo, 0x52);
368 jpeg_write_m_byte(cinfo, 0x4F);
369 jpeg_write_m_byte(cinfo, 0x46);
370 jpeg_write_m_byte(cinfo, 0x49);
371 jpeg_write_m_byte(cinfo, 0x4C);
372 jpeg_write_m_byte(cinfo, 0x45);
373 jpeg_write_m_byte(cinfo, 0x0);
374
375 /* Add the sequencing info */
376 jpeg_write_m_byte(cinfo, cur_marker);
377 jpeg_write_m_byte(cinfo, (int)num_markers);
378
379 /* Add the profile data */
380 while(length--)
381 {
382 jpeg_write_m_byte(cinfo, *icc_data_ptr);
383 icc_data_ptr++;
384 }
385 cur_marker++;
386 }
387}
388
389
390/*
391 * Handy subroutine to test whether a saved marker is an ICC profile marker.
392 */
393
394static boolean marker_is_icc(jpeg_saved_marker_ptr marker)
395{
396 return marker->marker == ICC_MARKER && marker->data_length >= ICC_OVERHEAD_LEN
397 &&
398 /* verify the identifying string */
399 GETJOCTET(marker->data[0]) == 0x49 && GETJOCTET(marker->data[1]) == 0x43
400 && GETJOCTET(marker->data[2]) == 0x43 && GETJOCTET(marker->data[3]) == 0x5F
401 && GETJOCTET(marker->data[4]) == 0x50 && GETJOCTET(marker->data[5]) == 0x52
402 && GETJOCTET(marker->data[6]) == 0x4F && GETJOCTET(marker->data[7]) == 0x46
403 && GETJOCTET(marker->data[8]) == 0x49 && GETJOCTET(marker->data[9]) == 0x4C
404 && GETJOCTET(marker->data[10]) == 0x45 && GETJOCTET(marker->data[11]) == 0x0;
405}
406
407
408/*
409 * See if there was an ICC profile in the JPEG file being read;
410 * if so, reassemble and return the profile data.
411 *
412 * TRUE is returned if an ICC profile was found, FALSE if not.
413 * If TRUE is returned, *icc_data_ptr is set to point to the
414 * returned data, and *icc_data_len is set to its length.
415 *
416 * IMPORTANT: the data at **icc_data_ptr has been allocated with malloc()
417 * and must be freed by the caller with g_free() when the caller no longer
418 * needs it. (Alternatively, we could write this routine to use the
419 * IJG library's memory allocator, so that the data would be freed implicitly
420 * at jpeg_finish_decompress() time. But it seems likely that many apps
421 * will prefer to have the data stick around after decompression finishes.)
422 *
423 * NOTE: if the file contains invalid ICC APP2 markers, we just silently
424 * return FALSE. You might want to issue an error message instead.
425 */
426
427static boolean read_icc_profile(j_decompress_ptr dinfo, JOCTET **icc_data_ptr, unsigned int *icc_data_len)
428{
429 jpeg_saved_marker_ptr marker;
430 int num_markers = 0;
431 int seq_no;
432 JOCTET *icc_data;
433 unsigned int total_length;
434#define MAX_SEQ_NO 255 /* sufficient since marker numbers are bytes */
435 char marker_present[MAX_SEQ_NO + 1]; /* 1 if marker found */
436 unsigned int data_length[MAX_SEQ_NO + 1]; /* size of profile data in marker */
437 unsigned int data_offset[MAX_SEQ_NO + 1]; /* offset for data in marker */
438
439 *icc_data_ptr = NULL; /* avoid confusion if FALSE return */
440 *icc_data_len = 0;
441
442 /* This first pass over the saved markers discovers whether there are
443 * any ICC markers and verifies the consistency of the marker numbering.
444 */
445
446 for(seq_no = 1; seq_no <= MAX_SEQ_NO; seq_no++) marker_present[seq_no] = 0;
447
448 for(marker = dinfo->marker_list; !IS_NULL_PTR(marker); marker = marker->next)
449 {
450 if(marker_is_icc(marker))
451 {
452 if(num_markers == 0)
453 num_markers = GETJOCTET(marker->data[13]);
454 else if(num_markers != GETJOCTET(marker->data[13]))
455 return FALSE; /* inconsistent num_markers fields */
456 seq_no = GETJOCTET(marker->data[12]);
457 if(seq_no <= 0 || seq_no > num_markers) return FALSE; /* bogus sequence number */
458 if(marker_present[seq_no]) return FALSE; /* duplicate sequence numbers */
459 marker_present[seq_no] = 1;
460 data_length[seq_no] = marker->data_length - ICC_OVERHEAD_LEN;
461 }
462 }
463
464 if(num_markers == 0) return FALSE;
465
466 /* Check for missing markers, count total space needed,
467 * compute offset of each marker's part of the data.
468 */
469
470 total_length = 0;
471 for(seq_no = 1; seq_no <= num_markers; seq_no++)
472 {
473 if(marker_present[seq_no] == 0) return FALSE; /* missing sequence number */
474 data_offset[seq_no] = total_length;
475 total_length += data_length[seq_no];
476 }
477
478 if(total_length == 0) return FALSE; /* found only empty markers? */
479
480 /* Allocate space for assembled data */
481 icc_data = (JOCTET *)g_malloc(total_length * sizeof(JOCTET));
482
483 /* and fill it in */
484 for(marker = dinfo->marker_list; !IS_NULL_PTR(marker); marker = marker->next)
485 {
486 if(marker_is_icc(marker))
487 {
488 JOCTET FAR *src_ptr;
489 JOCTET *dst_ptr;
490 unsigned int length;
491 seq_no = GETJOCTET(marker->data[12]);
492 dst_ptr = icc_data + data_offset[seq_no];
493 src_ptr = marker->data + ICC_OVERHEAD_LEN;
494 length = data_length[seq_no];
495 while(length--)
496 {
497 *dst_ptr++ = *src_ptr++;
498 }
499 }
500 }
501
502 *icc_data_ptr = icc_data;
503 *icc_data_len = total_length;
504
505 return TRUE;
506}
507#undef ICC_MARKER
508#undef ICC_OVERHEAD_LEN
509#undef MAX_BYTES_IN_MARKER
510#undef MAX_DATA_BYTES_IN_MARKER
511#undef MAX_SEQ_NO
512
513
514int dt_imageio_jpeg_write_with_icc_profile(const char *filename, const uint8_t *in, const int width,
515 const int height, const int quality, const void *exif, int exif_len,
516 int32_t imgid)
517{
518 struct dt_imageio_jpeg_error_mgr jerr;
520
521 jpg.cinfo.err = jpeg_std_error(&jerr.pub);
522 jerr.pub.error_exit = dt_imageio_jpeg_error_exit;
523 if(setjmp(jerr.setjmp_buffer))
524 {
525 jpeg_destroy_compress(&(jpg.cinfo));
526 return 1;
527 }
528 jpeg_create_compress(&(jpg.cinfo));
529 FILE *f = g_fopen(filename, "wb");
530 if(IS_NULL_PTR(f)) return 1;
531 jpeg_stdio_dest(&(jpg.cinfo), f);
532
533 jpg.cinfo.image_width = width;
534 jpg.cinfo.image_height = height;
535 jpg.cinfo.input_components = 3;
536 jpg.cinfo.in_color_space = JCS_RGB;
537 jpeg_set_defaults(&(jpg.cinfo));
538 jpeg_set_quality(&(jpg.cinfo), quality, TRUE);
539 if(quality > 90) jpg.cinfo.comp_info[0].v_samp_factor = 1;
540 if(quality > 92) jpg.cinfo.comp_info[0].h_samp_factor = 1;
541 jpeg_start_compress(&(jpg.cinfo), TRUE);
542
543 if(imgid > 0)
544 {
545 // the code in this block is never being used. should that ever change make sure to honour the
546 // color profile overwriting the one set in colorout, too. dt_colorspaces_get_output_profile() doesn't do that!
548 cmsHPROFILE out_profile = dt_colorspaces_get_output_profile(imgid, &type, "")->profile;
549 uint32_t len = 0;
550 cmsSaveProfileToMem(out_profile, 0, &len);
551 if(len > 0)
552 {
553 unsigned char *buf = dt_pixelpipe_cache_alloc_align_cache(sizeof(unsigned char) * len, 0);
554 cmsSaveProfileToMem(out_profile, buf, &len);
555 write_icc_profile(&(jpg.cinfo), buf, len);
557 }
558 }
559
560 if(exif && exif_len > 0 && exif_len < 65534) jpeg_write_marker(&(jpg.cinfo), JPEG_APP0 + 1, exif, exif_len);
561
562 JSAMPARRAY row = (*jpg.cinfo.mem->alloc_sarray)((j_common_ptr)&jpg.cinfo, JPOOL_IMAGE,
563 (JDIMENSION)(3 * width), 1);
564 const uint8_t *buf;
565 while(jpg.cinfo.next_scanline < jpg.cinfo.image_height)
566 {
567 buf = in + jpg.cinfo.next_scanline * jpg.cinfo.image_width * 4;
568 for(int i = 0; i < width; i++)
569 for(int k = 0; k < 3; k++) row[0][3 * i + k] = buf[4 * i + k];
570 jpeg_write_scanlines(&(jpg.cinfo), row, 1);
571 }
572 jpeg_finish_compress(&(jpg.cinfo));
573 jpeg_destroy_compress(&(jpg.cinfo));
574 fclose(f);
575 return 0;
576}
577
578int dt_imageio_jpeg_write(const char *filename, const uint8_t *in, const int width, const int height,
579 const int quality, const void *exif, int exif_len)
580{
581 return dt_imageio_jpeg_write_with_icc_profile(filename, in, width, height, quality, exif, exif_len, -1);
582}
583
584int dt_imageio_jpeg_read_header(const char *filename, dt_imageio_jpeg_t *jpg)
585{
586 jpg->f = g_fopen(filename, "rb");
587 if(IS_NULL_PTR(jpg->f)) return 1;
588
589 struct dt_imageio_jpeg_error_mgr jerr;
590 jpg->dinfo.err = jpeg_std_error(&jerr.pub);
591 jerr.pub.error_exit = dt_imageio_jpeg_error_exit;
592 if(setjmp(jerr.setjmp_buffer))
593 {
594 jpeg_destroy_decompress(&(jpg->dinfo));
595 fclose(jpg->f);
596 return 1;
597 }
598 jpeg_create_decompress(&(jpg->dinfo));
599 jpeg_stdio_src(&(jpg->dinfo), jpg->f);
600 setup_read_exif(&(jpg->dinfo));
602 // jpg->dinfo.buffered_image = TRUE;
603 jpeg_read_header(&(jpg->dinfo), TRUE);
604#ifdef JCS_EXTENSIONS
605 jpg->dinfo.out_color_space = JCS_EXT_RGBX;
606 jpg->dinfo.out_color_components = 4;
607#else
608 jpg->dinfo.out_color_space = JCS_RGB;
609 jpg->dinfo.out_color_components = 3;
610#endif
611 jpg->width = jpg->dinfo.image_width;
612 jpg->height = jpg->dinfo.image_height;
613 return 0;
614}
615
616#ifdef JCS_EXTENSIONS
617static int read_jsc(dt_imageio_jpeg_t *jpg, uint8_t *out)
618{
619 uint8_t *tmp = out;
620 while(jpg->dinfo.output_scanline < jpg->dinfo.image_height)
621 {
622 if(jpeg_read_scanlines(&(jpg->dinfo), &tmp, 1) != 1)
623 {
624 return 1;
625 }
626 tmp += 4 * jpg->width;
627 }
628 return 0;
629}
630#endif
631
632static int read_plain(dt_imageio_jpeg_t *jpg, uint8_t *out)
633{
634 JSAMPROW row_pointer[1];
635 row_pointer[0] = (uint8_t *)dt_pixelpipe_cache_alloc_align_cache(
636 (size_t)jpg->dinfo.output_width * jpg->dinfo.num_components, 0);
637 uint8_t *tmp = out;
638 while(jpg->dinfo.output_scanline < jpg->dinfo.image_height)
639 {
640 if(jpeg_read_scanlines(&(jpg->dinfo), row_pointer, 1) != 1)
641 {
642 jpeg_destroy_decompress(&(jpg->dinfo));
643 dt_pixelpipe_cache_free_align(row_pointer[0]);
644 fclose(jpg->f);
645 return 1;
646 }
647 for(unsigned int i = 0; i < jpg->dinfo.image_width; i++)
648 for(int k = 0; k < 3; k++) tmp[4 * i + k] = row_pointer[0][3 * i + k];
649 tmp += 4 * jpg->width;
650 }
651 dt_pixelpipe_cache_free_align(row_pointer[0]);
652 return 0;
653}
654
656{
657 struct dt_imageio_jpeg_error_mgr jerr;
658 jpg->dinfo.err = jpeg_std_error(&jerr.pub);
659 jerr.pub.error_exit = dt_imageio_jpeg_error_exit;
660 if(setjmp(jerr.setjmp_buffer))
661 {
662 jpeg_destroy_decompress(&(jpg->dinfo));
663 fclose(jpg->f);
664 return 1;
665 }
666
667#ifdef JCS_EXTENSIONS
668 /*
669 * Do a run-time detection for JCS_EXTENSIONS:
670 * it might have been only available at build-time
671 */
672 int jcs_alpha_valid = 1;
673 if(setjmp(jerr.setjmp_buffer))
674 {
675 if(jpg->dinfo.out_color_space == JCS_EXT_RGBX && jpg->dinfo.out_color_components == 4)
676 {
677 // ok, no JCS_EXTENSIONS, fall-back to slow plain code.
678 jpg->dinfo.out_color_components = 3;
679 jpg->dinfo.out_color_space = JCS_RGB;
680 jcs_alpha_valid = 0;
681 }
682 else
683 {
684 jpeg_destroy_decompress(&(jpg->dinfo));
685 return 1;
686 }
687 }
688#endif
689 (void)jpeg_start_decompress(&(jpg->dinfo));
690
691 if(setjmp(jerr.setjmp_buffer))
692 {
693 jpeg_destroy_decompress(&(jpg->dinfo));
694 fclose(jpg->f);
695 return 1;
696 }
697
698#ifdef JCS_EXTENSIONS
699 if(jcs_alpha_valid)
700 {
701 read_jsc(jpg, out);
702 }
703 else
704 {
705 read_plain(jpg, out);
706 }
707#else
708 read_plain(jpg, out);
709#endif
710
711 if(setjmp(jerr.setjmp_buffer))
712 {
713 jpeg_destroy_decompress(&(jpg->dinfo));
714 fclose(jpg->f);
715 return 1;
716 }
717
718 (void)jpeg_finish_decompress(&(jpg->dinfo));
719
720 jpeg_destroy_decompress(&(jpg->dinfo));
721 fclose(jpg->f);
722 return 0;
723}
724
726{
727 unsigned int length = 0;
728 boolean res = read_icc_profile(&(jpg->dinfo), out, &length);
729 jpeg_destroy_decompress(&(jpg->dinfo));
730 fclose(jpg->f);
731 return res ? length : 0;
732}
733
735{
736 for(jpeg_saved_marker_ptr marker = jpg->dinfo.marker_list; !IS_NULL_PTR(marker); marker = marker->next)
737 {
738 if(marker->marker == EXIF_MARKER && marker->data_length > 6)
739 return dt_exif_get_color_space(marker->data + 6, marker->data_length - 6);
740 }
741
742 return DT_COLORSPACE_DISPLAY; // nothing embedded
743}
744
746{
747 const char *ext = filename + strlen(filename);
748 while(*ext != '.' && ext > filename) ext--;
749 if(strncmp(ext, ".jpg", 4) && strncmp(ext, ".JPG", 4) && strncmp(ext, ".jpeg", 5)
750 && strncmp(ext, ".JPEG", 5))
752
753 if(!img->exif_inited) (void)dt_exif_read(img, filename);
754
757 img->width = jpg.width;
758 img->height = jpg.height;
759
760 img->dsc.channels = 4;
761 img->dsc.datatype = TYPE_FLOAT;
762 img->dsc.bpp = 4 * sizeof(float);
763 img->dsc.cst = IOP_CS_RGB; // jpeg is always RGB
764 img->dsc.filters = 0u;
765 img->flags &= ~DT_IMAGE_RAW;
766 img->flags &= ~DT_IMAGE_S_RAW;
767 img->flags &= ~DT_IMAGE_HDR;
768 img->flags |= DT_IMAGE_LDR;
769 img->loader = LOADER_JPEG;
770
771 if(IS_NULL_PTR(mbuf))
772 {
773 jpeg_destroy_decompress(&(jpg.dinfo));
774 fclose(jpg.f);
775 return DT_IMAGEIO_OK;
776 }
777
778 uint8_t *tmp = (uint8_t *)dt_pixelpipe_cache_alloc_align_cache(
779 sizeof(uint8_t) * 4 * jpg.width * jpg.height,
780 0);
781 if(dt_imageio_jpeg_read(&jpg, tmp))
782 {
785 }
786
787 void *buf = dt_mipmap_cache_alloc(mbuf, img);
788 if(IS_NULL_PTR(buf))
789 {
792 }
793
794 dt_imageio_flip_buffers_ui8_to_float((float *)buf, tmp, 0.0f, 255.0f, 4, jpg.width, jpg.height, jpg.width,
795 jpg.height, 4 * jpg.width, 0);
796
798 return DT_IMAGEIO_OK;
799}
800
801
802
803// clang-format off
804// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
805// vim: shiftwidth=2 expandtab tabstop=2 cindent
806// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
807// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
@ IOP_CS_RGB
const float f
const dt_colormatrix_t dt_aligned_pixel_t out
static const int row
const int res
Definition dtpthread.h:351
dt_colorspaces_color_profile_type_t dt_exif_get_color_space(const uint8_t *data, size_t size)
Definition exif.cc:2330
int dt_exif_read(dt_image_t *img, const char *path)
Definition exif.cc:1994
What a photograph says about itself: the EXIF, IPTC and XMP tags a camera and a cataloguer write,...
@ TYPE_FLOAT
Definition format.h:56
dt_imageio_retval_t
Definition image.h:91
@ DT_IMAGEIO_OK
Definition image.h:92
@ DT_IMAGEIO_CACHE_FULL
Definition image.h:95
@ DT_IMAGEIO_FILE_CORRUPTED
Definition image.h:94
@ DT_IMAGE_LDR
Definition image.h:122
@ LOADER_JPEG
Definition image.h:287
void dt_imageio_flip_buffers_ui8_to_float(float *out, const uint8_t *in, const float black, const float white, const int ch, const int wd, const int ht, const int fwd, const int fht, const int stride, const dt_image_orientation_t orientation)
int dt_imageio_jpeg_write_with_icc_profile(const char *filename, const uint8_t *in, const int width, const int height, const int quality, const void *exif, int exif_len, int32_t imgid)
static void dt_imageio_jpeg_init_destination(j_compress_ptr cinfo)
static void setup_read_icc_profile(j_decompress_ptr cinfo)
int dt_imageio_jpeg_read_profile(dt_imageio_jpeg_t *jpg, uint8_t **out)
#define ICC_MARKER
static int read_plain(dt_imageio_jpeg_t *jpg, uint8_t *out)
static void dt_imageio_jpeg_error_exit(j_common_ptr cinfo)
static void dt_imageio_jpeg_term_source(j_decompress_ptr cinfo)
static void dt_imageio_jpeg_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
int dt_imageio_jpeg_read(dt_imageio_jpeg_t *jpg, uint8_t *out)
#define MAX_SEQ_NO
static void setup_read_exif(j_decompress_ptr cinfo)
int dt_imageio_jpeg_read_header(const char *filename, dt_imageio_jpeg_t *jpg)
static boolean marker_is_icc(jpeg_saved_marker_ptr marker)
static void dt_imageio_jpeg_term_destination(j_compress_ptr cinfo)
static void dt_imageio_jpeg_init_source(j_decompress_ptr cinfo)
static int decompress_plain(dt_imageio_jpeg_t *jpg, uint8_t *out)
dt_colorspaces_color_profile_type_t dt_imageio_jpeg_read_color_space(dt_imageio_jpeg_t *jpg)
struct dt_imageio_jpeg_error_mgr * dt_imageio_jpeg_error_ptr
int dt_imageio_jpeg_decompress_header(const void *in, size_t length, dt_imageio_jpeg_t *jpg)
dt_imageio_retval_t dt_imageio_open_jpeg(dt_image_t *img, const char *filename, dt_mipmap_buffer_t *mbuf)
static void write_icc_profile(j_compress_ptr cinfo, const JOCTET *icc_data_ptr, unsigned int icc_data_len)
int dt_imageio_jpeg_compress(const uint8_t *in, uint8_t *out, const int width, const int height, const int quality)
int dt_imageio_jpeg_decompress(dt_imageio_jpeg_t *jpg, uint8_t *out)
int dt_imageio_jpeg_write(const char *filename, const uint8_t *in, const int width, const int height, const int quality, const void *exif, int exif_len)
#define MAX_DATA_BYTES_IN_MARKER
static boolean read_icc_profile(j_decompress_ptr dinfo, JOCTET **icc_data_ptr, unsigned int *icc_data_len)
static boolean dt_imageio_jpeg_fill_input_buffer(j_decompress_ptr cinfo)
static boolean dt_imageio_jpeg_empty_output_buffer(j_compress_ptr cinfo)
#define ICC_OVERHEAD_LEN
#define EXIF_MARKER
const dt_colorspaces_color_profile_t * dt_colorspaces_get_output_profile(const int32_t imgid, dt_colorspaces_color_profile_type_t *over_type, const char *over_filename)
_lib_location_type_t type
Definition location.c:1
float *const restrict const size_t k
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
void * dt_mipmap_cache_alloc(dt_mipmap_buffer_t *buf, const dt_image_t *img)
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
#define dt_pixelpipe_cache_alloc_align_cache(size, id)
#define dt_pixelpipe_cache_free_align(mem)
dt_colorspaces_color_profile_type_t
@ DT_COLORSPACE_DISPLAY
The monitor profile – the one list entry that mutates after init.
@ DT_COLORSPACE_NONE
No profile / "take it from the image settings". Never matches a list entry.
cmsHPROFILE profile
the actual profile; NULL for the three category entries
int32_t height
Definition image.h:397
dt_image_loader_t loader
Definition image.h:417
int32_t exif_inited
Definition image.h:365
int32_t flags
Definition image.h:401
int32_t width
Definition image.h:397
dt_iop_buffer_dsc_t dsc
Definition image.h:419
struct jpeg_error_mgr pub
Definition jpeg.c:88
struct jpeg_decompress_struct dinfo
Definition jpeg.c:74
struct jpeg_compress_struct cinfo
Definition jpeg.c:75
struct jpeg_source_mgr src
Definition jpeg.c:72
struct jpeg_destination_mgr dest
Definition jpeg.c:73
uint32_t filters
Definition format.h:89
unsigned int channels
Definition format.h:83
dt_iop_buffer_type_t datatype
Definition format.h:85