filters/gp_filter_resize_alloc: Check w and h
[gfxprim.git] / libs / loaders / GP_JPG.c
blob8b83cf15c701c086053593d246b12d1c3f668746
1 /*****************************************************************************
2 * This file is part of gfxprim library. *
3 * *
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. *
8 * *
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. *
13 * *
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 *
18 * *
19 * Copyright (C) 2009-2014 Cyril Hrubis <metan@ucw.cz> *
20 * *
21 *****************************************************************************/
25 JPG image support using jpeg library.
29 #include <stdint.h>
30 #include <inttypes.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <stdio.h>
35 #include <setjmp.h>
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);
56 #ifdef HAVE_JPEG
58 #include <jpeglib.h>
59 #include <jerror.h>
61 struct my_jpg_err {
62 struct jpeg_error_mgr error_mgr;
63 jmp_buf setjmp_buf;
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) {
81 case JCS_GRAYSCALE:
82 return "Grayscale";
83 case JCS_RGB:
84 return "RGB";
85 case JCS_YCbCr:
86 return "YCbCr";
87 case JCS_CMYK:
88 return "CMYK";
89 case JCS_YCCK:
90 return "YCCK";
91 default:
92 return "Unknown";
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");
107 return ECANCELED;
111 return 0;
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);
123 unsigned int i;
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");
137 return ECANCELED;
141 return 0;
145 struct my_source_mgr {
146 struct jpeg_source_mgr mgr;
147 int start_of_file;
148 void *buffer;
149 size_t size;
150 gp_io *io;
153 static void dummy_src(j_decompress_ptr GP_UNUSED(cinfo))
157 static boolean fill_input_buffer(struct jpeg_decompress_struct *cinfo)
159 int ret;
160 struct my_source_mgr* src = (void*)cinfo->src;
162 ret = gp_io_read(src->io, src->buffer, src->size);
164 if (ret <= 0) {
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;
176 b[0] = 0xff;
177 b[1] = JPEG_EOI;
178 ret = 2;
181 src->mgr.next_input_byte = src->buffer;
182 src->mgr.bytes_in_buffer = ret;
183 src->start_of_file = 0;
185 return TRUE;
188 static void skip_input_data(struct jpeg_decompress_struct *cinfo, long num_bytes)
190 struct my_source_mgr* src = (void*)cinfo->src;
191 off_t ret;
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;
201 } else {
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;
217 src->io = io;
218 src->buffer = buf;
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,
226 gp_storage *storage)
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) {
238 case JPEG_COM: {
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);
248 } break;
249 case JPEG_APP0:
250 GP_TODO("JFIF");
251 break;
252 case JPEG_APP0 + 1: {
253 gp_io *io = gp_io_mem(marker->data, marker->data_length, NULL);
254 if (!io) {
255 GP_WARN("Failed to create MemIO");
256 return;
258 gp_read_exif(io, storage);
259 gp_io_close(io);
260 } break;
265 static void save_jpg_markers(struct jpeg_decompress_struct *cinfo)
267 /* Comment marker */
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;
284 uint8_t buf[1024];
285 int err;
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)) {
291 err = EIO;
292 goto err2;
295 jpeg_create_decompress(&cinfo);
296 init_source_mgr(&src, io, buf, sizeof(buf));
297 cinfo.src = (void*)&src;
299 if (storage)
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?
310 if (storage)
311 read_jpg_metadata(&cinfo, storage);
313 if (!img)
314 goto exit;
316 gp_pixel pixel_type;
318 switch (cinfo.out_color_space) {
319 case JCS_GRAYSCALE:
320 pixel_type = GP_PIXEL_G8;
321 break;
322 case JCS_RGB:
323 pixel_type = GP_PIXEL_BGR888;
324 break;
325 case JCS_CMYK:
326 pixel_type = GP_PIXEL_CMYK8888;
327 break;
328 default:
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));
335 err = ENOSYS;
336 goto err1;
339 ret = gp_pixmap_alloc(cinfo.image_width, cinfo.image_height,
340 pixel_type);
342 if (ret == NULL) {
343 GP_DEBUG(1, "Malloc failed :(");
344 err = ENOMEM;
345 goto err1;
348 jpeg_start_decompress(&cinfo);
350 switch (pixel_type) {
351 case GP_PIXEL_BGR888:
352 case GP_PIXEL_G8:
353 err = load(&cinfo, ret, callback);
354 break;
355 case GP_PIXEL_CMYK8888:
356 err = load_cmyk(&cinfo, ret, callback);
357 break;
358 default:
359 err = EINVAL;
362 if (err)
363 goto err2;
365 jpeg_finish_decompress(&cinfo);
366 exit:
367 jpeg_destroy_decompress(&cinfo);
369 gp_progress_cb_done(callback);
371 if (img)
372 *img = ret;
374 return 0;
375 err2:
376 gp_pixmap_free(ret);
377 err1:
378 jpeg_destroy_decompress(&cinfo);
379 errno = err;
380 return 1;
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");
404 return ECANCELED;
408 return 0;
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");
423 return ECANCELED;
427 return 0;
430 struct my_dest_mgr {
431 struct jpeg_destination_mgr mgr;
432 void *buffer;
433 ssize_t size;
434 gp_io *io;
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");
447 return FALSE;
450 dest->mgr.next_output_byte = dest->buffer;
451 dest->mgr.free_in_buffer = dest->size;
453 return TRUE;
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;
461 if (to_write > 0) {
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
465 return;
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;
479 dst->io = io;
480 dst->buffer = buf;
481 dst->size = buf_size;
484 static gp_pixel_type out_pixel_types[] = {
485 GP_PIXEL_BGR888,
486 GP_PIXEL_G8,
487 GP_PIXEL_UNKNOWN
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;
497 uint8_t buf[1024];
498 int err;
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));
507 errno = ENOSYS;
508 return 1;
511 if (setjmp(my_err.setjmp_buf)) {
512 errno = EIO;
513 return 1;
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;
528 switch (out_pix) {
529 case GP_PIXEL_BGR888:
530 cinfo.input_components = 3;
531 cinfo.in_color_space = JCS_RGB;
532 break;
533 case GP_PIXEL_G8:
534 cinfo.input_components = 1;
535 cinfo.in_color_space = JCS_GRAYSCALE;
536 break;
537 default:
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);
547 else
548 err = save(&cinfo, src, callback);
550 if (err) {
551 jpeg_destroy_compress(&cinfo);
552 errno = err;
553 return 1;
556 jpeg_finish_compress(&cinfo);
557 jpeg_destroy_compress(&cinfo);
559 gp_progress_cb_done(callback);
560 return 0;
563 #else
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))
569 errno = ENOSYS;
570 return 1;
573 int gp_write_jpg(const gp_pixmap GP_UNUSED(*src), gp_io GP_UNUSED(*io),
574 gp_progress_cb GP_UNUSED(*callback))
576 errno = ENOSYS;
577 return 1;
580 #endif /* HAVE_JPEG */
582 const gp_loader gp_jpg = {
583 #ifdef HAVE_JPEG
584 .Read = gp_read_jpg_ex,
585 .Write = gp_write_jpg,
586 .save_ptypes = out_pixel_types,
587 #endif
588 .Match = gp_match_jpg,
590 .fmt_name = "JPEG",
591 .extensions = {"jpg", "jpeg", NULL},