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