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-2012 Cyril Hrubis <metan@ucw.cz> *
21 *****************************************************************************/
25 JPG image support using jpeg library.
37 #include "../../config.h"
38 #include "core/GP_Debug.h"
46 int GP_OpenJPG(const char *src_path
, FILE **f
)
50 *f
= fopen(src_path
, "rb");
54 GP_DEBUG(1, "Failed to open '%s' : %s",
55 src_path
, strerror(errno
));
60 //TODO: check signature and rewind the stream
66 struct jpeg_error_mgr error_mgr
;
70 static void my_error_exit(j_common_ptr cinfo
)
72 struct my_jpg_err
*my_err
= (struct my_jpg_err
*) cinfo
->err
;
74 GP_DEBUG(1, "ERROR reading/writing jpeg file");
76 longjmp(my_err
->setjmp_buf
, 1);
79 static const char *get_colorspace(J_COLOR_SPACE color_space
)
81 switch (color_space
) {
97 GP_Context
*GP_ReadJPG(FILE *f
, GP_ProgressCallback
*callback
)
99 struct jpeg_decompress_struct cinfo
;
100 struct my_jpg_err my_err
;
101 GP_Context
*ret
= NULL
;
104 cinfo
.err
= jpeg_std_error(&my_err
.error_mgr
);
105 my_err
.error_mgr
.error_exit
= my_error_exit
;
107 if (setjmp(my_err
.setjmp_buf
)) {
112 jpeg_create_decompress(&cinfo
);
113 jpeg_stdio_src(&cinfo
, f
);
115 jpeg_read_header(&cinfo
, TRUE
);
117 GP_DEBUG(1, "Have %s JPEG size %ux%u %i channels",
118 get_colorspace(cinfo
.jpeg_color_space
),
119 cinfo
.image_width
, cinfo
.image_height
,
120 cinfo
.num_components
);
124 switch (cinfo
.out_color_space
) {
126 pixel_type
= GP_PIXEL_G8
;
129 pixel_type
= GP_PIXEL_RGB888
;
132 pixel_type
= GP_PIXEL_UNKNOWN
;
135 if (pixel_type
== GP_PIXEL_UNKNOWN
) {
136 GP_DEBUG(1, "Can't handle %s JPEG output format",
137 get_colorspace(cinfo
.out_color_space
));
142 ret
= GP_ContextAlloc(cinfo
.image_width
, cinfo
.image_height
,
146 GP_DEBUG(1, "Malloc failed :(");
151 jpeg_start_decompress(&cinfo
);
153 while (cinfo
.output_scanline
< cinfo
.output_height
) {
154 uint32_t y
= cinfo
.output_scanline
;
156 JSAMPROW addr
= (void*)GP_PIXEL_ADDR(ret
, 0, y
);
157 jpeg_read_scanlines(&cinfo
, &addr
, 1);
159 if (pixel_type
!= GP_PIXEL_RGB888
)
162 //TODO: fixme bigendian?
163 /* fix the pixel, as we want in fact BGR */
166 for (i
= 0; i
< ret
->w
; i
++) {
167 uint8_t *pix
= GP_PIXEL_ADDR(ret
, i
, y
);
168 GP_SWAP(pix
[0], pix
[2]);
171 if (GP_ProgressCallbackReport(callback
, y
, ret
->h
, ret
->w
)) {
172 GP_DEBUG(1, "Operation aborted");
178 jpeg_finish_decompress(&cinfo
);
179 jpeg_destroy_decompress(&cinfo
);
181 GP_ProgressCallbackDone(callback
);
187 jpeg_destroy_decompress(&cinfo
);
192 GP_Context
*GP_LoadJPG(const char *src_path
, GP_ProgressCallback
*callback
)
197 if (GP_OpenJPG(src_path
, &f
))
201 res
= GP_ReadJPG(f
, callback
);
208 #define JPEG_COM_MAX 128
210 static void read_jpg_metadata(struct jpeg_decompress_struct
*cinfo
,
213 jpeg_saved_marker_ptr marker
;
215 for (marker
= cinfo
->marker_list
; marker
!= NULL
; marker
= marker
->next
) {
216 switch (marker
->marker
) {
218 GP_MetaDataCreateString(data
, "comment", (void*)marker
->data
,
219 marker
->data_length
, 1);
225 GP_MetaDataFromExif(data
, marker
->data
, marker
->data_length
);
231 static void save_jpg_markers(struct jpeg_decompress_struct
*cinfo
)
234 jpeg_save_markers(cinfo
, JPEG_COM
, JPEG_COM_MAX
);
236 /* APP0 marker = JFIF data */
237 jpeg_save_markers(cinfo
, JPEG_APP0
+ 1, 0xffff);
239 /* APP1 marker = Exif data */
240 jpeg_save_markers(cinfo
, JPEG_APP0
+ 1, 0xffff);
243 int GP_ReadJPGMetaData(FILE *f
, GP_MetaData
*data
)
245 struct jpeg_decompress_struct cinfo
;
246 struct my_jpg_err my_err
;
249 cinfo
.err
= jpeg_std_error(&my_err
.error_mgr
);
250 my_err
.error_mgr
.error_exit
= my_error_exit
;
252 if (setjmp(my_err
.setjmp_buf
)) {
257 jpeg_create_decompress(&cinfo
);
258 jpeg_stdio_src(&cinfo
, f
);
260 save_jpg_markers(&cinfo
);
262 jpeg_read_header(&cinfo
, TRUE
);
264 GP_DEBUG(1, "Have %s JPEG size %ux%u %i channels",
265 get_colorspace(cinfo
.jpeg_color_space
),
266 cinfo
.image_width
, cinfo
.image_height
,
267 cinfo
.num_components
);
269 read_jpg_metadata(&cinfo
, data
);
271 // jpeg_finish_decompress(&cinfo);
272 jpeg_destroy_decompress(&cinfo
);
276 jpeg_destroy_decompress(&cinfo
);
281 int GP_LoadJPGMetaData(const char *src_path
, GP_MetaData
*data
)
286 if (GP_OpenJPG(src_path
, &f
))
289 ret
= GP_ReadJPGMetaData(f
, data
);
296 int GP_SaveJPG(const GP_Context
*src
, const char *dst_path
,
297 GP_ProgressCallback
*callback
)
300 struct jpeg_compress_struct cinfo
;
301 struct my_jpg_err my_err
;
304 GP_DEBUG(1, "Saving JPG Image '%s'", dst_path
);
306 if (src
->pixel_type
!= GP_PIXEL_RGB888
&&
307 src
->pixel_type
!= GP_PIXEL_G8
) {
308 GP_DEBUG(1, "Can't save png with pixel type %s",
309 GP_PixelTypeName(src
->pixel_type
));
314 f
= fopen(dst_path
, "wb");
318 GP_DEBUG(1, "Failed to open '%s' for writing: %s",
319 dst_path
, strerror(errno
));
323 if (setjmp(my_err
.setjmp_buf
)) {
328 cinfo
.err
= jpeg_std_error(&my_err
.error_mgr
);
329 my_err
.error_mgr
.error_exit
= my_error_exit
;
331 jpeg_create_compress(&cinfo
);
333 jpeg_stdio_dest(&cinfo
, f
);
335 cinfo
.image_width
= src
->w
;
336 cinfo
.image_height
= src
->h
;
337 cinfo
.input_components
= src
->pixel_type
== GP_PIXEL_RGB888
? 3 : 1;
338 cinfo
.in_color_space
= JCS_RGB
;
340 jpeg_set_defaults(&cinfo
);
342 jpeg_start_compress(&cinfo
, TRUE
);
344 while (cinfo
.next_scanline
< cinfo
.image_height
) {
345 uint32_t y
= cinfo
.next_scanline
;
347 if (src
->pixel_type
== GP_PIXEL_RGB888
) {
349 uint8_t tmp
[3 * src
->w
];
351 memcpy(tmp
, GP_PIXEL_ADDR(src
, 0, y
), 3 * src
->w
);
353 /* fix the pixels as we want in fact BGR */
354 for (i
= 0; i
< src
->w
; i
++) {
355 uint8_t *pix
= tmp
+ 3 * i
;
356 GP_SWAP(pix
[0], pix
[2]);
359 JSAMPROW row
= (void*)tmp
;
360 jpeg_write_scanlines(&cinfo
, &row
, 1);
362 JSAMPROW row
= (void*)GP_PIXEL_ADDR(src
, 0, y
);
363 jpeg_write_scanlines(&cinfo
, &row
, 1);
366 if (GP_ProgressCallbackReport(callback
, y
, src
->h
, src
->w
)) {
367 GP_DEBUG(1, "Operation aborted");
373 jpeg_finish_compress(&cinfo
);
377 GP_DEBUG(1, "Failed to close file '%s': %s",
378 dst_path
, strerror(errno
));
382 GP_ProgressCallbackDone(callback
);
384 //TODO: is cinfo allocated?
386 jpeg_destroy_compress(&cinfo
);
397 int GP_OpenJPG(const char GP_UNUSED(*src_path
), FILE GP_UNUSED(**f
))
403 GP_Context
*GP_ReadJPG(FILE GP_UNUSED(*f
),
404 GP_ProgressCallback
GP_UNUSED(*callback
))
410 GP_Context
*GP_LoadJPG(const char GP_UNUSED(*src_path
),
411 GP_ProgressCallback
GP_UNUSED(*callback
))
417 int GP_ReadJPGMetaData(FILE GP_UNUSED(*f
), GP_MetaData
GP_UNUSED(*data
))
423 int GP_LoadJPGMetaData(const char GP_UNUSED(*src_path
),
424 GP_MetaData
GP_UNUSED(*data
))
430 int GP_SaveJPG(const GP_Context
GP_UNUSED(*src
),
431 const char GP_UNUSED(*dst_path
),
432 GP_ProgressCallback
GP_UNUSED(*callback
))
438 #endif /* HAVE_JPEG */