1 /*****************************************************************************
2 * This file is part of gfxprim library. *
4 * Gfxprim is free software; you can redistribute it and/or *
5 * modify it under the terms of the GNU Lesser General Public *
6 * License as published by the Free Software Foundation; either *
7 * version 2.1 of the License, or (at your option) any later version. *
9 * Gfxprim is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
12 * Lesser General Public License for more details. *
14 * You should have received a copy of the GNU Lesser General Public *
15 * License along with gfxprim; if not, write to the Free Software *
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
17 * Boston, MA 02110-1301 USA *
19 * Copyright (C) 2009-2014 Cyril Hrubis <metan@ucw.cz> *
21 *****************************************************************************/
25 JPG image support using jpeg library.
37 #include "../../config.h"
38 #include "core/GP_Debug.h"
40 #include <loaders/GP_Exif.h>
41 #include <loaders/GP_LineConvert.h>
42 #include <loaders/GP_Loaders.gen.h>
45 * 0xff 0xd8 - start of image
46 * 0xff 0x.. - start of frame
48 #define JPEG_SIGNATURE "\xff\xd8\xff"
49 #define JPEG_SIGNATURE_LEN 3
51 int gp_match_jpg(const void *buf
)
53 return !memcmp(buf
, JPEG_SIGNATURE
, JPEG_SIGNATURE_LEN
);
62 struct jpeg_error_mgr error_mgr
;
66 static void my_error_exit(j_common_ptr cinfo
)
68 struct my_jpg_err
*my_err
= (struct my_jpg_err
*) cinfo
->err
;
69 char msg
[JMSG_LENGTH_MAX
];
71 cinfo
->err
->format_message(cinfo
, msg
);
73 GP_WARN("jpeg lib error: %s", msg
);
75 longjmp(my_err
->setjmp_buf
, 1);
78 static const char *get_colorspace(J_COLOR_SPACE color_space
)
80 switch (color_space
) {
96 static int load(struct jpeg_decompress_struct
*cinfo
, gp_pixmap
*ret
,
97 gp_progress_cb
*callback
)
99 while (cinfo
->output_scanline
< cinfo
->output_height
) {
100 uint32_t y
= cinfo
->output_scanline
;
101 JSAMPROW addr
= (void*)GP_PIXEL_ADDR(ret
, 0, y
);
103 jpeg_read_scanlines(cinfo
, &addr
, 1);
105 if (gp_progress_cb_report(callback
, y
, ret
->h
, ret
->w
)) {
106 GP_DEBUG(1, "Operation aborted");
114 static int load_cmyk(struct jpeg_decompress_struct
*cinfo
, gp_pixmap
*ret
,
115 gp_progress_cb
*callback
)
117 while (cinfo
->output_scanline
< cinfo
->output_height
) {
118 uint32_t y
= cinfo
->output_scanline
;
120 JSAMPROW addr
= (void*)GP_PIXEL_ADDR(ret
, 0, y
);
121 jpeg_read_scanlines(cinfo
, &addr
, 1);
124 uint8_t *buf
= GP_PIXEL_ADDR(ret
, 0, y
);
126 for (i
= 0; i
< ret
->w
; i
++) {
127 unsigned int j
= 4 * i
;
129 buf
[j
] = 0xff - buf
[j
];
130 buf
[j
+1] = 0xff - buf
[j
+1];
131 buf
[j
+2] = 0xff - buf
[j
+2];
132 buf
[j
+3] = 0xff - buf
[j
+3];
135 if (gp_progress_cb_report(callback
, y
, ret
->h
, ret
->w
)) {
136 GP_DEBUG(1, "Operation aborted");
145 struct my_source_mgr
{
146 struct jpeg_source_mgr mgr
;
153 static void dummy_src(j_decompress_ptr
GP_UNUSED(cinfo
))
157 static boolean
fill_input_buffer(struct jpeg_decompress_struct
*cinfo
)
160 struct my_source_mgr
* src
= (void*)cinfo
->src
;
162 ret
= gp_io_read(src
->io
, src
->buffer
, src
->size
);
165 GP_WARN("Failed to fill buffer, IORead returned %i", ret
);
166 if (src
->start_of_file
)
167 ERREXIT(cinfo
, JERR_INPUT_EMPTY
);
170 * Insert fake EOI so that we proceed with whater has been read
171 * so far, likely the libjpeg will emit premature end of data
172 * segment or other kind of error but we may as well happen to
173 * load slightly corrupted image.
175 char *b
= src
->buffer
;
181 src
->mgr
.next_input_byte
= src
->buffer
;
182 src
->mgr
.bytes_in_buffer
= ret
;
183 src
->start_of_file
= 0;
188 static void skip_input_data(struct jpeg_decompress_struct
*cinfo
, long num_bytes
)
190 struct my_source_mgr
* src
= (void*)cinfo
->src
;
193 GP_DEBUG(3, "Skipping %li bytes", num_bytes
);
195 if (src
->mgr
.bytes_in_buffer
< (unsigned long)num_bytes
) {
196 ret
= gp_io_seek(src
->io
, num_bytes
- src
->mgr
.bytes_in_buffer
, GP_IO_SEEK_CUR
);
197 //TODO: Call jpeg error
198 if (ret
== (off_t
)-1)
199 GP_FATAL("Failed to skip data: %s", strerror(errno
));
200 src
->mgr
.bytes_in_buffer
= 0;
202 src
->mgr
.bytes_in_buffer
-= num_bytes
;
203 src
->mgr
.next_input_byte
+= num_bytes
;
207 static inline void init_source_mgr(struct my_source_mgr
*src
, gp_io
*io
,
208 void *buf
, size_t buf_size
)
210 src
->mgr
.init_source
= dummy_src
;
211 src
->mgr
.resync_to_restart
= jpeg_resync_to_restart
;
212 src
->mgr
.term_source
= dummy_src
;
213 src
->mgr
.fill_input_buffer
= fill_input_buffer
;
214 src
->mgr
.skip_input_data
= skip_input_data
;
215 src
->mgr
.bytes_in_buffer
= 0;
216 src
->mgr
.next_input_byte
= NULL
;
219 src
->size
= buf_size
;
220 src
->start_of_file
= 1;
223 #define JPEG_COM_MAX 128
225 static void read_jpg_metadata(struct jpeg_decompress_struct
*cinfo
,
228 jpeg_saved_marker_ptr marker
;
230 gp_storage_add_int(storage
, NULL
, "Width", cinfo
->image_width
);
231 gp_storage_add_int(storage
, NULL
, "Height", cinfo
->image_height
);
232 gp_storage_add_int(storage
, NULL
, "Channels", cinfo
->num_components
);
233 gp_storage_add_string(storage
, NULL
, "Color Space",
234 get_colorspace(cinfo
->out_color_space
));
236 for (marker
= cinfo
->marker_list
; marker
!= NULL
; marker
= marker
->next
) {
237 switch (marker
->marker
) {
239 //TODO: Is comment NULL terminated?
240 char buf
[marker
->data_length
+1];
242 GP_DEBUG(3, "JPEG_COM comment block, size %u",
243 (unsigned int)marker
->data_length
);
245 memcpy(buf
, marker
->data
, marker
->data_length
);
246 buf
[marker
->data_length
] = '\0';
247 gp_storage_add_string(storage
, NULL
, "Comment", buf
);
252 case JPEG_APP0
+ 1: {
253 gp_io
*io
= gp_io_mem(marker
->data
, marker
->data_length
, NULL
);
255 GP_WARN("Failed to create MemIO");
258 gp_read_exif(io
, storage
);
265 static void save_jpg_markers(struct jpeg_decompress_struct
*cinfo
)
268 jpeg_save_markers(cinfo
, JPEG_COM
, JPEG_COM_MAX
);
270 /* APP0 marker = JFIF data */
271 jpeg_save_markers(cinfo
, JPEG_APP0
+ 1, 0xffff);
273 /* APP1 marker = Exif data */
274 jpeg_save_markers(cinfo
, JPEG_APP0
+ 1, 0xffff);
277 int gp_read_jpg_ex(gp_io
*io
, gp_pixmap
**img
,
278 gp_storage
*storage
, gp_progress_cb
*callback
)
280 struct jpeg_decompress_struct cinfo
;
281 struct my_source_mgr src
;
282 struct my_jpg_err my_err
;
283 gp_pixmap
*ret
= NULL
;
287 cinfo
.err
= jpeg_std_error(&my_err
.error_mgr
);
288 my_err
.error_mgr
.error_exit
= my_error_exit
;
290 if (setjmp(my_err
.setjmp_buf
)) {
295 jpeg_create_decompress(&cinfo
);
296 init_source_mgr(&src
, io
, buf
, sizeof(buf
));
297 cinfo
.src
= (void*)&src
;
300 save_jpg_markers(&cinfo
);
302 jpeg_read_header(&cinfo
, TRUE
);
304 GP_DEBUG(1, "Have %s JPEG size %ux%u %i channels",
305 get_colorspace(cinfo
.jpeg_color_space
),
306 cinfo
.image_width
, cinfo
.image_height
,
307 cinfo
.num_components
);
309 //TODO: Propagate failure?
311 read_jpg_metadata(&cinfo
, storage
);
318 switch (cinfo
.out_color_space
) {
320 pixel_type
= GP_PIXEL_G8
;
323 pixel_type
= GP_PIXEL_BGR888
;
326 pixel_type
= GP_PIXEL_CMYK8888
;
329 pixel_type
= GP_PIXEL_UNKNOWN
;
332 if (pixel_type
== GP_PIXEL_UNKNOWN
) {
333 GP_DEBUG(1, "Can't handle %s JPEG output format",
334 get_colorspace(cinfo
.out_color_space
));
339 ret
= gp_pixmap_alloc(cinfo
.image_width
, cinfo
.image_height
,
343 GP_DEBUG(1, "Malloc failed :(");
348 jpeg_start_decompress(&cinfo
);
350 switch (pixel_type
) {
351 case GP_PIXEL_BGR888
:
353 err
= load(&cinfo
, ret
, callback
);
355 case GP_PIXEL_CMYK8888
:
356 err
= load_cmyk(&cinfo
, ret
, callback
);
365 jpeg_finish_decompress(&cinfo
);
367 jpeg_destroy_decompress(&cinfo
);
369 gp_progress_cb_done(callback
);
378 jpeg_destroy_decompress(&cinfo
);
383 static int save_convert(struct jpeg_compress_struct
*cinfo
,
384 const gp_pixmap
*src
,
385 gp_pixel_type out_pix
,
386 gp_progress_cb
*callback
)
388 uint8_t tmp
[(src
->w
* gp_pixel_size(out_pix
)) / 8 + 1];
389 gp_line_convert convert
;
391 convert
= gp_line_convert_get(src
->pixel_type
, out_pix
);
393 while (cinfo
->next_scanline
< cinfo
->image_height
) {
394 uint32_t y
= cinfo
->next_scanline
;
395 void *in
= GP_PIXEL_ADDR(src
, 0, y
);
397 convert(in
, tmp
, src
->w
);
399 JSAMPROW row
= (void*)tmp
;
400 jpeg_write_scanlines(cinfo
, &row
, 1);
402 if (gp_progress_cb_report(callback
, y
, src
->h
, src
->w
)) {
403 GP_DEBUG(1, "Operation aborted");
411 static int save(struct jpeg_compress_struct
*cinfo
,
412 const gp_pixmap
*src
,
413 gp_progress_cb
*callback
)
415 while (cinfo
->next_scanline
< cinfo
->image_height
) {
416 uint32_t y
= cinfo
->next_scanline
;
418 JSAMPROW row
= (void*)GP_PIXEL_ADDR(src
, 0, y
);
419 jpeg_write_scanlines(cinfo
, &row
, 1);
421 if (gp_progress_cb_report(callback
, y
, src
->h
, src
->w
)) {
422 GP_DEBUG(1, "Operation aborted");
431 struct jpeg_destination_mgr mgr
;
437 static void dummy_dst(j_compress_ptr
GP_UNUSED(cinfo
))
441 static boolean
empty_output_buffer(j_compress_ptr cinfo
)
443 struct my_dest_mgr
*dest
= (void*)cinfo
->dest
;
445 if (gp_io_write(dest
->io
, dest
->buffer
, dest
->size
) != dest
->size
) {
446 GP_DEBUG(1, "Failed to write JPEG buffer");
450 dest
->mgr
.next_output_byte
= dest
->buffer
;
451 dest
->mgr
.free_in_buffer
= dest
->size
;
456 static void term_destination(j_compress_ptr cinfo
)
458 struct my_dest_mgr
*dest
= (void*)cinfo
->dest
;
459 ssize_t to_write
= dest
->size
- dest
->mgr
.free_in_buffer
;
462 if (gp_io_write(dest
->io
, dest
->buffer
, to_write
) != to_write
) {
463 GP_DEBUG(1, "Failed to write JPEG buffer");
464 //TODO: Error handling
470 static inline void init_dest_mgr(struct my_dest_mgr
*dst
, gp_io
*io
,
471 void *buf
, size_t buf_size
)
473 dst
->mgr
.init_destination
= dummy_dst
;
474 dst
->mgr
.empty_output_buffer
= empty_output_buffer
;
475 dst
->mgr
.term_destination
= term_destination
;
476 dst
->mgr
.next_output_byte
= buf
;
477 dst
->mgr
.free_in_buffer
= buf_size
;
481 dst
->size
= buf_size
;
484 static gp_pixel_type out_pixel_types
[] = {
490 int gp_write_jpg(const gp_pixmap
*src
, gp_io
*io
,
491 gp_progress_cb
*callback
)
493 struct jpeg_compress_struct cinfo
;
494 gp_pixel_type out_pix
;
495 struct my_jpg_err my_err
;
496 struct my_dest_mgr dst
;
500 GP_DEBUG(1, "Writing JPG Image to I/O (%p)", io
);
502 out_pix
= gp_line_convertible(src
->pixel_type
, out_pixel_types
);
504 if (out_pix
== GP_PIXEL_UNKNOWN
) {
505 GP_DEBUG(1, "Unsupported pixel type %s",
506 gp_pixel_type_name(src
->pixel_type
));
511 if (setjmp(my_err
.setjmp_buf
)) {
517 cinfo
.err
= jpeg_std_error(&my_err
.error_mgr
);
518 my_err
.error_mgr
.error_exit
= my_error_exit
;
520 jpeg_create_compress(&cinfo
);
522 init_dest_mgr(&dst
, io
, buf
, sizeof(buf
));
523 cinfo
.dest
= (void*)&dst
;
525 cinfo
.image_width
= src
->w
;
526 cinfo
.image_height
= src
->h
;
529 case GP_PIXEL_BGR888
:
530 cinfo
.input_components
= 3;
531 cinfo
.in_color_space
= JCS_RGB
;
534 cinfo
.input_components
= 1;
535 cinfo
.in_color_space
= JCS_GRAYSCALE
;
538 GP_BUG("Don't know how to set color_space and compoments");
541 jpeg_set_defaults(&cinfo
);
543 jpeg_start_compress(&cinfo
, TRUE
);
545 if (out_pix
!= src
->pixel_type
)
546 err
= save_convert(&cinfo
, src
, out_pix
, callback
);
548 err
= save(&cinfo
, src
, callback
);
551 jpeg_destroy_compress(&cinfo
);
556 jpeg_finish_compress(&cinfo
);
557 jpeg_destroy_compress(&cinfo
);
559 gp_progress_cb_done(callback
);
565 int gp_read_jpg_ex(gp_io
GP_UNUSED(*io
), gp_pixmap
GP_UNUSED(**img
),
566 gp_storage
GP_UNUSED(*storage
),
567 gp_progress_cb
GP_UNUSED(*callback
))
573 int gp_write_jpg(const gp_pixmap
GP_UNUSED(*src
), gp_io
GP_UNUSED(*io
),
574 gp_progress_cb
GP_UNUSED(*callback
))
580 #endif /* HAVE_JPEG */
582 const gp_loader gp_jpg
= {
584 .Read
= gp_read_jpg_ex
,
585 .Write
= gp_write_jpg
,
586 .save_ptypes
= out_pixel_types
,
588 .Match
= gp_match_jpg
,
591 .extensions
= {"jpg", "jpeg", NULL
},