1 /* GXPSImages
2 *
3 * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library 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 GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #include <config.h>
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdint.h>
26
27 #ifdef HAVE_LIBPNG
28 #include <png.h>
29 #endif
30
31 #ifdef HAVE_LIBJPEG
32 #include <jpeglib.h>
33 #include <setjmp.h>
34 #endif
35
36 #ifdef HAVE_LIBTIFF
37 #include <tiffio.h>
38 #endif
39
40 #include "gxps-images.h"
41 #include "gxps-error.h"
42 #include "gxps-debug.h"
43
44 #define METERS_PER_INCH 0.0254
45 #define CENTIMETERS_PER_INCH 2.54
46
47 /* PNG */
48 #ifdef HAVE_LIBPNG
49
50 static const cairo_user_data_key_t image_data_cairo_key;
51
52 static void
_read_png(png_structp png_ptr,png_bytep data,png_size_t len)53 _read_png (png_structp png_ptr,
54 png_bytep data,
55 png_size_t len)
56 {
57 GInputStream *stream;
58
59 stream = png_get_io_ptr (png_ptr);
60 g_input_stream_read (stream, data, len, NULL, NULL);
61 }
62
63 static void
png_error_callback(png_structp png_ptr,png_const_charp error_msg)64 png_error_callback (png_structp png_ptr,
65 png_const_charp error_msg)
66 {
67 char **msg;
68
69 msg = png_get_error_ptr (png_ptr);
70 *msg = g_strdup (error_msg);
71 longjmp (png_jmpbuf (png_ptr), 1);
72 }
73
74 static void
png_warning_callback(png_structp png,png_const_charp error_msg)75 png_warning_callback (png_structp png,
76 png_const_charp error_msg)
77 {
78 }
79
80 /* From cairo's cairo-png.c <http://cairographics.org> */
81 static inline int
multiply_alpha(int alpha,int color)82 multiply_alpha (int alpha, int color)
83 {
84 int temp = (alpha * color) + 0x80;
85
86 return ((temp + (temp >> 8)) >> 8);
87 }
88
89 /* Premultiplies data and converts RGBA bytes => native endian
90 * From cairo's cairo-png.c <http://cairographics.org> */
91 static void
premultiply_data(png_structp png,png_row_infop row_info,png_bytep data)92 premultiply_data (png_structp png,
93 png_row_infop row_info,
94 png_bytep data)
95 {
96 unsigned int i;
97
98 for (i = 0; i < row_info->rowbytes; i += 4) {
99 uint8_t *base = &data[i];
100 uint8_t alpha = base[3];
101 uint32_t p;
102
103 if (alpha == 0) {
104 p = 0;
105 } else {
106 uint8_t red = base[0];
107 uint8_t green = base[1];
108 uint8_t blue = base[2];
109
110 if (alpha != 0xff) {
111 red = multiply_alpha (alpha, red);
112 green = multiply_alpha (alpha, green);
113 blue = multiply_alpha (alpha, blue);
114 }
115 p = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
116 }
117 memcpy (base, &p, sizeof (uint32_t));
118 }
119 }
120
121 /* Converts RGBx bytes to native endian xRGB
122 * From cairo's cairo-png.c <http://cairographics.org> */
123 static void
convert_bytes_to_data(png_structp png,png_row_infop row_info,png_bytep data)124 convert_bytes_to_data (png_structp png, png_row_infop row_info, png_bytep data)
125 {
126 unsigned int i;
127
128 for (i = 0; i < row_info->rowbytes; i += 4) {
129 uint8_t *base = &data[i];
130 uint8_t red = base[0];
131 uint8_t green = base[1];
132 uint8_t blue = base[2];
133 uint32_t pixel;
134
135 pixel = (0xff << 24) | (red << 16) | (green << 8) | (blue << 0);
136 memcpy (base, &pixel, sizeof (uint32_t));
137 }
138 }
139
140 static void
fill_png_error(GError ** error,const gchar * image_uri,const gchar * msg)141 fill_png_error (GError **error,
142 const gchar *image_uri,
143 const gchar *msg)
144 {
145 if (msg) {
146 g_set_error (error,
147 GXPS_ERROR,
148 GXPS_ERROR_IMAGE,
149 "Error loading PNG image %s: %s",
150 image_uri, msg);
151 } else {
152 g_set_error (error,
153 GXPS_ERROR,
154 GXPS_ERROR_IMAGE,
155 "Error loading PNG image %s",
156 image_uri);
157 }
158 }
159
160 #endif /* HAVE_LIBPNG */
161
162 /* Adapted from cairo's read_png in cairo-png.c
163 * http://cairographics.org/ */
164 static GXPSImage *
gxps_images_create_from_png(GXPSArchive * zip,const gchar * image_uri,GError ** error)165 gxps_images_create_from_png (GXPSArchive *zip,
166 const gchar *image_uri,
167 GError **error)
168 {
169 #ifdef HAVE_LIBPNG
170 GInputStream *stream;
171 GXPSImage *image = NULL;
172 char *png_err_msg = NULL;
173 png_struct *png;
174 png_info *info;
175 png_byte *data = NULL;
176 png_byte **row_pointers = NULL;
177 png_uint_32 png_width, png_height;
178 int depth, color_type, interlace, stride;
179 unsigned int i;
180 cairo_format_t format;
181 cairo_status_t status;
182
183 stream = gxps_archive_open (zip, image_uri);
184 if (!stream) {
185 g_set_error (error,
186 GXPS_ERROR,
187 GXPS_ERROR_SOURCE_NOT_FOUND,
188 "Image source %s not found in archive",
189 image_uri);
190 return NULL;
191 }
192
193 png = png_create_read_struct (PNG_LIBPNG_VER_STRING,
194 &png_err_msg,
195 png_error_callback,
196 png_warning_callback);
197 if (png == NULL) {
198 fill_png_error (error, image_uri, NULL);
199 g_object_unref (stream);
200 return NULL;
201 }
202
203 info = png_create_info_struct (png);
204 if (info == NULL) {
205 fill_png_error (error, image_uri, NULL);
206 g_object_unref (stream);
207 png_destroy_read_struct (&png, NULL, NULL);
208 return NULL;
209 }
210
211 png_set_read_fn (png, stream, _read_png);
212
213 if (setjmp (png_jmpbuf (png))) {
214 fill_png_error (error, image_uri, png_err_msg);
215 g_free (png_err_msg);
216 g_object_unref (stream);
217 png_destroy_read_struct (&png, &info, NULL);
218 gxps_image_free (image);
219 g_free (row_pointers);
220 g_free (data);
221 return NULL;
222 }
223
224 png_read_info (png, info);
225
226 png_get_IHDR (png, info,
227 &png_width, &png_height, &depth,
228 &color_type, &interlace, NULL, NULL);
229
230 /* convert palette/gray image to rgb */
231 if (color_type == PNG_COLOR_TYPE_PALETTE)
232 png_set_palette_to_rgb (png);
233
234 /* expand gray bit depth if needed */
235 if (color_type == PNG_COLOR_TYPE_GRAY)
236 png_set_expand_gray_1_2_4_to_8 (png);
237
238 /* transform transparency to alpha */
239 if (png_get_valid (png, info, PNG_INFO_tRNS))
240 png_set_tRNS_to_alpha (png);
241
242 if (depth == 16)
243 png_set_strip_16 (png);
244
245 if (depth < 8)
246 png_set_packing (png);
247
248 /* convert grayscale to RGB */
249 if (color_type == PNG_COLOR_TYPE_GRAY ||
250 color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
251 png_set_gray_to_rgb (png);
252
253 if (interlace != PNG_INTERLACE_NONE)
254 png_set_interlace_handling (png);
255
256 png_set_filler (png, 0xff, PNG_FILLER_AFTER);
257
258 /* recheck header after setting EXPAND options */
259 png_read_update_info (png, info);
260 png_get_IHDR (png, info,
261 &png_width, &png_height, &depth,
262 &color_type, &interlace, NULL, NULL);
263 if (depth != 8 ||
264 !(color_type == PNG_COLOR_TYPE_RGB ||
265 color_type == PNG_COLOR_TYPE_RGB_ALPHA)) {
266 fill_png_error (error, image_uri, NULL);
267 g_object_unref (stream);
268 png_destroy_read_struct (&png, &info, NULL);
269 return NULL;
270 }
271
272 switch (color_type) {
273 default:
274 g_assert_not_reached();
275 /* fall-through just in case ;-) */
276
277 case PNG_COLOR_TYPE_RGB_ALPHA:
278 format = CAIRO_FORMAT_ARGB32;
279 png_set_read_user_transform_fn (png, premultiply_data);
280 break;
281
282 case PNG_COLOR_TYPE_RGB:
283 format = CAIRO_FORMAT_RGB24;
284 png_set_read_user_transform_fn (png, convert_bytes_to_data);
285 break;
286 }
287
288 stride = cairo_format_stride_for_width (format, png_width);
289 if (stride < 0 || png_height >= INT_MAX / stride) {
290 fill_png_error (error, image_uri, NULL);
291 g_object_unref (stream);
292 png_destroy_read_struct (&png, &info, NULL);
293 return NULL;
294 }
295
296 image = g_slice_new0 (GXPSImage);
297 image->res_x = png_get_x_pixels_per_meter (png, info) * METERS_PER_INCH;
298 if (image->res_x == 0)
299 image->res_x = 96;
300 image->res_y = png_get_y_pixels_per_meter (png, info) * METERS_PER_INCH;
301 if (image->res_y == 0)
302 image->res_y = 96;
303
304 data = g_malloc (png_height * stride);
305 row_pointers = g_new (png_byte *, png_height);
306
307 for (i = 0; i < png_height; i++)
308 row_pointers[i] = &data[i * stride];
309
310 png_read_image (png, row_pointers);
311 png_read_end (png, info);
312 png_destroy_read_struct (&png, &info, NULL);
313 g_object_unref (stream);
314 g_free (row_pointers);
315
316 image->surface = cairo_image_surface_create_for_data (data, format,
317 png_width, png_height,
318 stride);
319 if (cairo_surface_status (image->surface)) {
320 fill_png_error (error, image_uri, NULL);
321 gxps_image_free (image);
322 g_free (data);
323 return NULL;
324 }
325
326 status = cairo_surface_set_user_data (image->surface,
327 &image_data_cairo_key,
328 data,
329 (cairo_destroy_func_t) g_free);
330 if (status) {
331 fill_png_error (error, image_uri, NULL);
332 gxps_image_free (image);
333 g_free (data);
334 return NULL;
335 }
336
337 return image;
338 #else
339 return NULL;
340 #endif /* HAVE_LIBPNG */
341 }
342
343 /* JPEG */
344 #ifdef HAVE_LIBJPEG
345 #define JPEG_PROG_BUF_SIZE 65536
346
347 struct _jpeg_src_mgr {
348 struct jpeg_source_mgr pub;
349 GInputStream *stream;
350 JOCTET *buffer;
351 jmp_buf setjmp_buffer;
352 };
353
354 #ifdef GXPS_ENABLE_DEBUG
355 static const gchar *
_jpeg_color_space_name(const J_COLOR_SPACE jpeg_color_space)356 _jpeg_color_space_name (const J_COLOR_SPACE jpeg_color_space)
357 {
358 switch (jpeg_color_space) {
359 case JCS_UNKNOWN: return "UNKNOWN";
360 case JCS_GRAYSCALE: return "GRAYSCALE";
361 case JCS_RGB: return "RGB";
362 case JCS_YCbCr: return "YCbCr";
363 case JCS_CMYK: return "CMYK";
364 case JCS_YCCK: return "YCCK";
365 default: return "invalid";
366 }
367 }
368 #endif
369
370 static void
_jpeg_init_source(j_decompress_ptr cinfo)371 _jpeg_init_source (j_decompress_ptr cinfo)
372 {
373 }
374
375 static boolean
_jpeg_fill_input_buffer(j_decompress_ptr cinfo)376 _jpeg_fill_input_buffer (j_decompress_ptr cinfo)
377 {
378 struct _jpeg_src_mgr *src = (struct _jpeg_src_mgr *)cinfo->src;
379 gssize num_bytes;
380
381 num_bytes = g_input_stream_read (src->stream, src->buffer, JPEG_PROG_BUF_SIZE, NULL, NULL);
382 if (num_bytes <= 0) {
383 /* Insert a fake EOI marker */
384 src->buffer[0] = (JOCTET) 0xFF;
385 src->buffer[1] = (JOCTET) JPEG_EOI;
386 }
387
388 src->pub.next_input_byte = src->buffer;
389 src->pub.bytes_in_buffer = num_bytes;
390
391 return TRUE;
392 }
393
394 static void
_jpeg_skip_input_data(j_decompress_ptr cinfo,long num_bytes)395 _jpeg_skip_input_data (j_decompress_ptr cinfo, long num_bytes)
396 {
397 struct _jpeg_src_mgr *src = (struct _jpeg_src_mgr *)cinfo->src;
398
399 if (num_bytes > 0) {
400 while (num_bytes > (long) src->pub.bytes_in_buffer) {
401 num_bytes -= (long) src->pub.bytes_in_buffer;
402 _jpeg_fill_input_buffer (cinfo);
403 }
404 src->pub.next_input_byte += (size_t) num_bytes;
405 src->pub.bytes_in_buffer -= (size_t) num_bytes;
406 }
407 }
408
409 static void
_jpeg_term_source(j_decompress_ptr cinfo)410 _jpeg_term_source (j_decompress_ptr cinfo)
411 {
412 }
413
414 static void
_jpeg_error_exit(j_common_ptr error)415 _jpeg_error_exit (j_common_ptr error)
416 {
417 j_decompress_ptr cinfo = (j_decompress_ptr)error;
418 struct _jpeg_src_mgr *src = (struct _jpeg_src_mgr *)cinfo->src;
419
420 longjmp (src->setjmp_buffer, 1);
421 }
422 #endif /* HAVE_LIBJPEG */
423
424 static GXPSImage *
gxps_images_create_from_jpeg(GXPSArchive * zip,const gchar * image_uri,GError ** error)425 gxps_images_create_from_jpeg (GXPSArchive *zip,
426 const gchar *image_uri,
427 GError **error)
428 {
429 #ifdef HAVE_LIBJPEG
430 GInputStream *stream;
431 struct jpeg_error_mgr error_mgr;
432 struct jpeg_decompress_struct cinfo;
433 struct _jpeg_src_mgr src;
434 GXPSImage *image;
435 guchar *data;
436 gint stride;
437 JSAMPARRAY lines;
438 gint jpeg_stride;
439 gint i;
440
441 stream = gxps_archive_open (zip, image_uri);
442 if (!stream) {
443 g_set_error (error,
444 GXPS_ERROR,
445 GXPS_ERROR_SOURCE_NOT_FOUND,
446 "Image source %s not found in archive",
447 image_uri);
448 return NULL;
449 }
450
451 jpeg_std_error (&error_mgr);
452 error_mgr.error_exit = _jpeg_error_exit;
453
454 jpeg_create_decompress (&cinfo);
455 cinfo.err = &error_mgr;
456
457 src.stream = stream;
458 src.buffer = (JOCTET *) (*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT,
459 JPEG_PROG_BUF_SIZE * sizeof (JOCTET));
460
461 src.pub.init_source = _jpeg_init_source;
462 src.pub.fill_input_buffer = _jpeg_fill_input_buffer;
463 src.pub.skip_input_data = _jpeg_skip_input_data;
464 src.pub.resync_to_restart = jpeg_resync_to_restart;
465 src.pub.term_source = _jpeg_term_source;
466 src.pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
467 src.pub.next_input_byte = NULL; /* until buffer loaded */
468 cinfo.src = (struct jpeg_source_mgr *)&src;
469
470 if (setjmp (src.setjmp_buffer)) {
471 g_set_error (error,
472 GXPS_ERROR,
473 GXPS_ERROR_IMAGE,
474 "Error loading JPEG image %s",
475 image_uri);
476 g_object_unref (stream);
477 return NULL;
478 }
479
480 jpeg_read_header (&cinfo, TRUE);
481
482 cinfo.do_fancy_upsampling = FALSE;
483 jpeg_start_decompress (&cinfo);
484
485 image = g_slice_new (GXPSImage);
486 image->surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
487 cinfo.output_width,
488 cinfo.output_height);
489 image->res_x = 96;
490 image->res_y = 96;
491 if (cairo_surface_status (image->surface)) {
492 g_set_error (error,
493 GXPS_ERROR,
494 GXPS_ERROR_IMAGE,
495 "Error loading JPEG image %s: %s",
496 image_uri,
497 cairo_status_to_string (cairo_surface_status (image->surface)));
498 jpeg_destroy_decompress (&cinfo);
499 gxps_image_free (image);
500 g_object_unref (stream);
501
502 return NULL;
503 }
504
505 data = cairo_image_surface_get_data (image->surface);
506 stride = cairo_image_surface_get_stride (image->surface);
507 jpeg_stride = cinfo.output_width * cinfo.out_color_components;
508 lines = cinfo.mem->alloc_sarray((j_common_ptr) &cinfo, JPOOL_IMAGE, jpeg_stride, 4);
509
510 while (cinfo.output_scanline < cinfo.output_height) {
511 gint n_lines, x;
512
513 n_lines = jpeg_read_scanlines (&cinfo, lines, cinfo.rec_outbuf_height);
514 for (i = 0; i < n_lines; i++) {
515 JSAMPLE *line = lines[i];
516 guchar *p = data;
517
518 for (x = 0; x < cinfo.output_width; x++) {
519 switch (cinfo.out_color_space) {
520 case JCS_RGB:
521 p[0] = line[2];
522 p[1] = line[1];
523 p[2] = line[0];
524 p[3] = 0xff;
525 break;
526 case JCS_GRAYSCALE:
527 p[0] = line[0];
528 p[1] = line[0];
529 p[2] = line[0];
530 p[3] = 0xff;
531 break;
532 default:
533 GXPS_DEBUG (g_message ("Unsupported jpeg color space %s",
534 _jpeg_color_space_name (cinfo.out_color_space)));
535
536 gxps_image_free (image);
537 jpeg_destroy_decompress (&cinfo);
538 g_object_unref (stream);
539 return NULL;
540 }
541 line += cinfo.out_color_components;
542 p += 4;
543 }
544
545 data += stride;
546 }
547 }
548
549 if (cinfo.density_unit == 1) { /* dots/inch */
550 image->res_x = cinfo.X_density;
551 image->res_y = cinfo.Y_density;
552 } else if (cinfo.density_unit == 2) { /* dots/cm */
553 image->res_x = cinfo.X_density * CENTIMETERS_PER_INCH;
554 image->res_y = cinfo.Y_density * CENTIMETERS_PER_INCH;
555 }
556
557 jpeg_finish_decompress (&cinfo);
558 jpeg_destroy_decompress (&cinfo);
559 g_object_unref (stream);
560
561 cairo_surface_mark_dirty (image->surface);
562
563 if (cairo_surface_status (image->surface)) {
564 g_set_error (error,
565 GXPS_ERROR,
566 GXPS_ERROR_IMAGE,
567 "Error loading JPEG image %s: %s",
568 image_uri,
569 cairo_status_to_string (cairo_surface_status (image->surface)));
570 gxps_image_free (image);
571
572 return NULL;
573 }
574
575 return image;
576 #else
577 return NULL;
578 #endif /* HAVE_LIBJPEG */
579 }
580
581 /* Tiff */
582 #ifdef HAVE_LIBTIFF
583 static TIFFErrorHandler orig_error_handler = NULL;
584 static TIFFErrorHandler orig_warning_handler = NULL;
585 static gchar *_tiff_error = NULL;
586
587 typedef struct {
588 guchar *buffer;
589 gsize buffer_len;
590 guint pos;
591 } TiffBuffer;
592
593 static void
fill_tiff_error(GError ** error,const gchar * image_uri)594 fill_tiff_error (GError **error,
595 const gchar *image_uri)
596 {
597 if (_tiff_error) {
598 g_set_error (error,
599 GXPS_ERROR,
600 GXPS_ERROR_IMAGE,
601 "Error loading TIFF image %s: %s",
602 image_uri, _tiff_error);
603 g_free (_tiff_error);
604 _tiff_error = NULL;
605 } else {
606 g_set_error (error,
607 GXPS_ERROR,
608 GXPS_ERROR_IMAGE,
609 "Error loading TIFF image %s",
610 image_uri);
611 }
612 }
613
614 G_GNUC_PRINTF (2, 0)
615 static void
_tiff_error_handler(const char * mod,const char * fmt,va_list ap)616 _tiff_error_handler (const char *mod,
617 const char *fmt,
618 va_list ap)
619 {
620 if (G_UNLIKELY (_tiff_error))
621 return;
622
623 _tiff_error = g_strdup_vprintf (fmt, ap);
624 }
625
626 static void
_tiff_push_handlers(void)627 _tiff_push_handlers (void)
628 {
629 orig_error_handler = TIFFSetErrorHandler (_tiff_error_handler);
630 orig_warning_handler = TIFFSetWarningHandler (NULL);
631 }
632
633 static void
_tiff_pop_handlers(void)634 _tiff_pop_handlers (void)
635 {
636 TIFFSetErrorHandler (orig_error_handler);
637 TIFFSetWarningHandler (orig_warning_handler);
638 }
639
640 static tsize_t
_tiff_read(thandle_t handle,tdata_t buf,tsize_t size)641 _tiff_read (thandle_t handle,
642 tdata_t buf,
643 tsize_t size)
644 {
645 TiffBuffer *buffer = (TiffBuffer *)handle;
646
647 if (buffer->pos + size > buffer->buffer_len)
648 return 0;
649
650 memcpy (buf, buffer->buffer + buffer->pos, size);
651 buffer->pos += size;
652
653 return size;
654 }
655
656 static tsize_t
_tiff_write(thandle_t handle,tdata_t buf,tsize_t size)657 _tiff_write (thandle_t handle,
658 tdata_t buf,
659 tsize_t size)
660 {
661 return -1;
662 }
663
664 static toff_t
_tiff_seek(thandle_t handle,toff_t offset,int whence)665 _tiff_seek (thandle_t handle,
666 toff_t offset,
667 int whence)
668 {
669 TiffBuffer *buffer = (TiffBuffer *)handle;
670
671 switch (whence) {
672 case SEEK_SET:
673 if (offset > buffer->buffer_len)
674 return -1;
675 buffer->pos = offset;
676 break;
677 case SEEK_CUR:
678 if (offset + buffer->pos >= buffer->buffer_len)
679 return -1;
680 buffer->pos += offset;
681 break;
682 case SEEK_END:
683 if (offset + buffer->buffer_len > buffer->buffer_len)
684 return -1;
685 buffer->pos = buffer->buffer_len + offset;
686 break;
687 default:
688 return -1;
689 }
690
691 return buffer->pos;
692 }
693
694 static int
_tiff_close(thandle_t context)695 _tiff_close (thandle_t context)
696 {
697 return 0;
698 }
699
700 static toff_t
_tiff_size(thandle_t handle)701 _tiff_size (thandle_t handle)
702 {
703 TiffBuffer *buffer = (TiffBuffer *)handle;
704
705 return buffer->buffer_len;
706 }
707
708 static int
_tiff_map_file(thandle_t handle,tdata_t * buf,toff_t * size)709 _tiff_map_file (thandle_t handle,
710 tdata_t *buf,
711 toff_t *size)
712 {
713 TiffBuffer *buffer = (TiffBuffer *)handle;
714
715 *buf = buffer->buffer;
716 *size = buffer->buffer_len;
717
718 return 0;
719 }
720
721 static void
_tiff_unmap_file(thandle_t handle,tdata_t data,toff_t offset)722 _tiff_unmap_file (thandle_t handle,
723 tdata_t data,
724 toff_t offset)
725 {
726 }
727 #endif /* #ifdef HAVE_LIBTIFF */
728
729 static GXPSImage *
gxps_images_create_from_tiff(GXPSArchive * zip,const gchar * image_uri,GError ** error)730 gxps_images_create_from_tiff (GXPSArchive *zip,
731 const gchar *image_uri,
732 GError **error)
733 {
734 #ifdef HAVE_LIBTIFF
735 TIFF *tiff;
736 TiffBuffer buffer;
737 GXPSImage *image;
738 gint width, height;
739 guint16 res_unit;
740 float res_x, res_y;
741 gint stride;
742 guchar *data;
743 guchar *p;
744
745 if (!gxps_archive_read_entry (zip, image_uri,
746 &buffer.buffer,
747 &buffer.buffer_len,
748 error)) {
749 return NULL;
750 }
751
752 buffer.pos = 0;
753
754 _tiff_push_handlers ();
755
756 tiff = TIFFClientOpen ("libgxps-tiff", "r", &buffer,
757 _tiff_read,
758 _tiff_write,
759 _tiff_seek,
760 _tiff_close,
761 _tiff_size,
762 _tiff_map_file,
763 _tiff_unmap_file);
764
765 if (!tiff || _tiff_error) {
766 fill_tiff_error (error, image_uri);
767 if (tiff)
768 TIFFClose (tiff);
769 _tiff_pop_handlers ();
770 g_free (buffer.buffer);
771 return NULL;
772 }
773
774 if (!TIFFGetField (tiff, TIFFTAG_IMAGEWIDTH, &width) || _tiff_error) {
775 fill_tiff_error (error, image_uri);
776 TIFFClose (tiff);
777 _tiff_pop_handlers ();
778 g_free (buffer.buffer);
779 return NULL;
780 }
781
782 if (!TIFFGetField (tiff, TIFFTAG_IMAGELENGTH, &height) || _tiff_error) {
783 fill_tiff_error (error, image_uri);
784 TIFFClose (tiff);
785 _tiff_pop_handlers ();
786 g_free (buffer.buffer);
787 return NULL;
788 }
789
790 if (width <= 0 || height <= 0) {
791 fill_tiff_error (error, image_uri);
792 TIFFClose (tiff);
793 _tiff_pop_handlers ();
794 g_free (buffer.buffer);
795 return NULL;
796 }
797
798 image = g_slice_new (GXPSImage);
799 image->surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
800 width, height);
801 image->res_x = 96;
802 image->res_y = 96;
803
804 if (!TIFFGetField (tiff, TIFFTAG_RESOLUTIONUNIT, &res_unit))
805 res_unit = 0;
806 if (TIFFGetField (tiff, TIFFTAG_XRESOLUTION, &res_x)) {
807 if (res_unit == 2) { /* inches */
808 image->res_x = res_x;
809 } else if (res_unit == 3) { /* centimeters */
810 image->res_x = res_x * CENTIMETERS_PER_INCH;
811 }
812 }
813 if (TIFFGetField (tiff, TIFFTAG_YRESOLUTION, &res_y)) {
814 if (res_unit == 2) { /* inches */
815 image->res_y = res_y;
816 } else if (res_unit == 3) { /* centimeters */
817 image->res_y = res_y * CENTIMETERS_PER_INCH;
818 }
819 }
820
821 if (cairo_surface_status (image->surface)) {
822 g_set_error (error,
823 GXPS_ERROR,
824 GXPS_ERROR_IMAGE,
825 "Error loading TIFF image %s: %s",
826 image_uri,
827 cairo_status_to_string (cairo_surface_status (image->surface)));
828 gxps_image_free (image);
829 TIFFClose (tiff);
830 _tiff_pop_handlers ();
831 g_free (buffer.buffer);
832 return NULL;
833 }
834
835 data = cairo_image_surface_get_data (image->surface);
836 if (!TIFFReadRGBAImageOriented (tiff, width, height,
837 (uint32 *)data,
838 ORIENTATION_TOPLEFT, 1) || _tiff_error) {
839 fill_tiff_error (error, image_uri);
840 gxps_image_free (image);
841 TIFFClose (tiff);
842 _tiff_pop_handlers ();
843 g_free (buffer.buffer);
844 return NULL;
845 }
846
847 TIFFClose (tiff);
848 _tiff_pop_handlers ();
849 g_free (buffer.buffer);
850
851 stride = cairo_image_surface_get_stride (image->surface);
852 p = data;
853 while (p < data + (height * stride)) {
854 guint32 *pixel = (guint32 *)p;
855 guint8 r = TIFFGetR (*pixel);
856 guint8 g = TIFFGetG (*pixel);
857 guint8 b = TIFFGetB (*pixel);
858 guint8 a = TIFFGetA (*pixel);
859
860 *pixel = (a << 24) | (r << 16) | (g << 8) | b;
861
862 p += 4;
863 }
864
865 cairo_surface_mark_dirty (image->surface);
866
867 return image;
868 #else
869 return NULL;
870 #endif /* #ifdef HAVE_LIBTIFF */
871 }
872
873 static gchar *
gxps_images_guess_content_type(GXPSArchive * zip,const gchar * image_uri)874 gxps_images_guess_content_type (GXPSArchive *zip,
875 const gchar *image_uri)
876 {
877 GInputStream *stream;
878 guchar buffer[1024];
879 gssize bytes_read;
880 gchar *mime_type;
881
882 stream = gxps_archive_open (zip, image_uri);
883 if (!stream)
884 return NULL;
885
886 bytes_read = g_input_stream_read (stream, buffer, 1024, NULL, NULL);
887 mime_type = g_content_type_guess (NULL, buffer, bytes_read, NULL);
888 g_object_unref (stream);
889
890 return mime_type;
891 }
892
893 GXPSImage *
gxps_images_get_image(GXPSArchive * zip,const gchar * image_uri,GError ** error)894 gxps_images_get_image (GXPSArchive *zip,
895 const gchar *image_uri,
896 GError **error)
897 {
898 GXPSImage *image = NULL;
899 gchar *image_uri_lower;
900
901 /* First try with extensions,
902 * as it's recommended by the spec
903 * (2.1.5 Image Parts)
904 */
905 image_uri_lower = g_utf8_strdown (image_uri, -1);
906 if (g_str_has_suffix (image_uri_lower, ".png")) {
907 image = gxps_images_create_from_png (zip, image_uri, error);
908 } else if (g_str_has_suffix (image_uri_lower, ".jpg")) {
909 image = gxps_images_create_from_jpeg (zip, image_uri, error);
910 } else if (g_str_has_suffix (image_uri_lower, ".tif")) {
911 image = gxps_images_create_from_tiff (zip, image_uri, error);
912 } else if (g_str_has_suffix (image_uri_lower, "wdp")) {
913 GXPS_DEBUG (g_message ("Unsupported image format windows media photo"));
914 g_free (image_uri_lower);
915 return NULL;
916 }
917
918 g_free (image_uri_lower);
919
920 if (!image) {
921 gchar *mime_type;
922
923 g_clear_error(error);
924
925 mime_type = gxps_images_guess_content_type (zip, image_uri);
926 if (g_strcmp0 (mime_type, "image/png") == 0) {
927 image = gxps_images_create_from_png (zip, image_uri, error);
928 } else if (g_strcmp0 (mime_type, "image/jpeg") == 0) {
929 image = gxps_images_create_from_jpeg (zip, image_uri, error);
930 } else if (g_strcmp0 (mime_type, "image/tiff") == 0) {
931 image = gxps_images_create_from_tiff (zip, image_uri, error);
932 } else {
933 GXPS_DEBUG (g_message ("Unsupported image format: %s", mime_type));
934 }
935 g_free (mime_type);
936 }
937
938 return image;
939 }
940
941 void
gxps_image_free(GXPSImage * image)942 gxps_image_free (GXPSImage *image)
943 {
944 if (G_UNLIKELY (!image))
945 return;
946
947 if (G_LIKELY (image->surface))
948 cairo_surface_destroy (image->surface);
949
950 g_slice_free (GXPSImage, image);
951 }
952