1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ |
2 | /* GdkPixbuf library - TIFF image loader |
3 | * |
4 | * Copyright (C) 1999 Mark Crichton |
5 | * Copyright (C) 1999 The Free Software Foundation |
6 | * |
7 | * Authors: Mark Crichton <crichton@gimp.org> |
8 | * Federico Mena-Quintero <federico@gimp.org> |
9 | * Jonathan Blandford <jrb@redhat.com> |
10 | * S�ren Sandmann <sandmann@daimi.au.dk> |
11 | * Christian Dywan <christian@lanedo.com> |
12 | * Mukund Sivaraman <muks@banu.com> |
13 | * |
14 | * This library is free software; you can redistribute it and/or |
15 | * modify it under the terms of the GNU Lesser General Public |
16 | * License as published by the Free Software Foundation; either |
17 | * version 2 of the License, or (at your option) any later version. |
18 | * |
19 | * This library is distributed in the hope that it will be useful, |
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
22 | * Lesser General Public License for more details. |
23 | * |
24 | * You should have received a copy of the GNU Lesser General Public |
25 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
26 | */ |
27 | |
28 | /* Following code (almost) blatantly ripped from Imlib */ |
29 | |
30 | #include "config.h" |
31 | #include <stdlib.h> |
32 | #include <stdint.h> |
33 | #include <string.h> |
34 | #ifdef HAVE_UNISTD_H |
35 | #include <unistd.h> |
36 | #endif |
37 | #include <tiffio.h> |
38 | #include <errno.h> |
39 | #include <glib-object.h> |
40 | #include <glib/gi18n-lib.h> |
41 | |
42 | #include "gdk-pixbuf-core.h" |
43 | #include "gdk-pixbuf-io.h" |
44 | #include "fallback-c89.c" |
45 | |
46 | #ifdef G_OS_WIN32 |
47 | #include <fcntl.h> |
48 | #include <io.h> |
49 | #include <windows.h> |
50 | #define lseek(a,b,c) _lseek(a,b,c) |
51 | #define O_RDWR _O_RDWR |
52 | #endif |
53 | |
54 | |
55 | /* Helper macros to convert between density units */ |
56 | #define DPCM_TO_DPI(value) ((int) round ((value) * 2.54)) |
57 | |
58 | typedef struct _TiffContext TiffContext; |
59 | struct _TiffContext |
60 | { |
61 | GdkPixbufModuleSizeFunc size_func; |
62 | GdkPixbufModulePreparedFunc prepared_func; |
63 | GdkPixbufModuleUpdatedFunc updated_func; |
64 | gpointer user_data; |
65 | |
66 | guchar *buffer; |
67 | guint allocated; |
68 | guint used; |
69 | guint pos; |
70 | }; |
71 | |
72 | static void |
73 | tiff_warning_handler (const char *mod, const char *fmt, va_list ap) |
74 | { |
75 | /* Don't print anything; we should not be dumping junk to |
76 | * stderr, since that may be bad for some apps. |
77 | */ |
78 | } |
79 | |
80 | static void |
81 | tiff_set_handlers (void) |
82 | { |
83 | TIFFSetErrorHandler (tiff_warning_handler); |
84 | TIFFSetWarningHandler (tiff_warning_handler); |
85 | } |
86 | |
87 | |
88 | |
89 | static void free_buffer (guchar *pixels, gpointer data) |
90 | { |
91 | g_free (mem: pixels); |
92 | } |
93 | |
94 | static GdkPixbuf * |
95 | tiff_image_parse (TIFF *tiff, TiffContext *context, GError **error) |
96 | { |
97 | guchar *pixels = NULL; |
98 | gint width, height, rowstride, bytes; |
99 | GdkPixbuf *pixbuf; |
100 | guint16 bits_per_sample = 0; |
101 | uint16_t orientation = 0; |
102 | uint16_t transform = 0; |
103 | uint16_t codec; |
104 | gchar *icc_profile_base64; |
105 | const gchar *icc_profile; |
106 | guint icc_profile_size; |
107 | uint16_t resolution_unit; |
108 | gchar *density_str; |
109 | gint retval; |
110 | |
111 | /* We're called with the lock held. */ |
112 | |
113 | if (!TIFFGetField (tif: tiff, TIFFTAG_IMAGEWIDTH, &width)) { |
114 | g_set_error_literal (err: error, |
115 | GDK_PIXBUF_ERROR, |
116 | code: GDK_PIXBUF_ERROR_FAILED, |
117 | _("Could not get image width (bad TIFF file)" )); |
118 | return NULL; |
119 | } |
120 | |
121 | if (!TIFFGetField (tif: tiff, TIFFTAG_IMAGELENGTH, &height)) { |
122 | g_set_error_literal (err: error, |
123 | GDK_PIXBUF_ERROR, |
124 | code: GDK_PIXBUF_ERROR_FAILED, |
125 | _("Could not get image height (bad TIFF file)" )); |
126 | return NULL; |
127 | } |
128 | |
129 | if (width <= 0 || height <= 0) { |
130 | g_set_error_literal (err: error, |
131 | GDK_PIXBUF_ERROR, |
132 | code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
133 | _("Width or height of TIFF image is zero" )); |
134 | return NULL; |
135 | } |
136 | |
137 | if (width > G_MAXINT / 4) { /* overflow */ |
138 | g_set_error_literal (err: error, |
139 | GDK_PIXBUF_ERROR, |
140 | code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
141 | _("Dimensions of TIFF image too large" )); |
142 | return NULL; |
143 | } |
144 | |
145 | rowstride = width * 4; |
146 | |
147 | if (height > G_MAXINT / rowstride) { /* overflow */ |
148 | g_set_error_literal (err: error, |
149 | GDK_PIXBUF_ERROR, |
150 | code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
151 | _("Dimensions of TIFF image too large" )); |
152 | return NULL; |
153 | } |
154 | |
155 | bytes = height * rowstride; |
156 | |
157 | if (context) { |
158 | gint w = width; |
159 | gint h = height; |
160 | (* context->size_func) (&w, &h, context->user_data); |
161 | |
162 | /* This is a signal that this function is being called |
163 | to support gdk_pixbuf_get_file_info, so we can stop |
164 | parsing the tiff file at this point. It is not an |
165 | error condition. */ |
166 | |
167 | if (w == 0 || h == 0) |
168 | return NULL; |
169 | } |
170 | |
171 | pixels = g_try_malloc (n_bytes: bytes); |
172 | |
173 | if (!pixels) { |
174 | g_set_error_literal (err: error, |
175 | GDK_PIXBUF_ERROR, |
176 | code: GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, |
177 | _("Insufficient memory to open TIFF file" )); |
178 | return NULL; |
179 | } |
180 | |
181 | pixbuf = gdk_pixbuf_new_from_data (data: pixels, colorspace: GDK_COLORSPACE_RGB, TRUE, bits_per_sample: 8, |
182 | width, height, rowstride, |
183 | destroy_fn: free_buffer, NULL); |
184 | if (!pixbuf) { |
185 | g_free (mem: pixels); |
186 | g_set_error_literal (err: error, |
187 | GDK_PIXBUF_ERROR, |
188 | code: GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, |
189 | _("Insufficient memory to open TIFF file" )); |
190 | return NULL; |
191 | } |
192 | |
193 | /* Save the bits per sample as an option since pixbufs are |
194 | expected to be always 8 bits per sample. */ |
195 | TIFFGetField (tif: tiff, TIFFTAG_BITSPERSAMPLE, &bits_per_sample); |
196 | if (bits_per_sample > 0) { |
197 | gchar str[5]; |
198 | g_snprintf (string: str, n: sizeof (str), format: "%d" , bits_per_sample); |
199 | gdk_pixbuf_set_option (pixbuf, key: "bits-per-sample" , value: str); |
200 | } |
201 | |
202 | /* Set the "orientation" key associated with this image. libtiff |
203 | orientation handling is odd, so further processing is required |
204 | by higher-level functions based on this tag. If the embedded |
205 | orientation tag is 1-4, libtiff flips/mirrors the image as |
206 | required, and no client processing is required - so we report |
207 | no orientation. Orientations 5-8 require rotations which would |
208 | swap the width and height of the image. libtiff does not do this. |
209 | Instead it interprets orientations 5-8 the same as 1-4. |
210 | See http://bugzilla.remotesensing.org/show_bug.cgi?id=1548. |
211 | To correct for this, the client must apply the transform normally |
212 | used for orientation 5 to both orientations 5 and 7, and apply |
213 | the transform normally used for orientation 7 for both |
214 | orientations 6 and 8. Then everythings works out OK! */ |
215 | |
216 | TIFFGetField (tif: tiff, TIFFTAG_ORIENTATION, &orientation); |
217 | |
218 | switch (orientation) { |
219 | case 5: |
220 | case 7: |
221 | transform = 5; |
222 | break; |
223 | case 6: |
224 | case 8: |
225 | transform = 7; |
226 | break; |
227 | default: |
228 | transform = 0; |
229 | break; |
230 | } |
231 | |
232 | if (transform > 0 ) { |
233 | gchar str[5]; |
234 | g_snprintf (string: str, n: sizeof (str), format: "%d" , transform); |
235 | gdk_pixbuf_set_option (pixbuf, key: "orientation" , value: str); |
236 | } |
237 | |
238 | TIFFGetField (tif: tiff, TIFFTAG_COMPRESSION, &codec); |
239 | if (codec > 0) { |
240 | gchar str[5]; |
241 | g_snprintf (string: str, n: sizeof (str), format: "%d" , codec); |
242 | gdk_pixbuf_set_option (pixbuf, key: "compression" , value: str); |
243 | } |
244 | |
245 | /* Extract embedded ICC profile */ |
246 | retval = TIFFGetField (tif: tiff, TIFFTAG_ICCPROFILE, &icc_profile_size, &icc_profile); |
247 | if (retval == 1) { |
248 | icc_profile_base64 = g_base64_encode (data: (const guchar *) icc_profile, len: icc_profile_size); |
249 | gdk_pixbuf_set_option (pixbuf, key: "icc-profile" , value: icc_profile_base64); |
250 | g_free (mem: icc_profile_base64); |
251 | } |
252 | |
253 | retval = TIFFGetField (tif: tiff, TIFFTAG_RESOLUTIONUNIT, &resolution_unit); |
254 | if (retval == 1) { |
255 | float x_resolution = 0, y_resolution = 0; |
256 | |
257 | TIFFGetField (tif: tiff, TIFFTAG_XRESOLUTION, &x_resolution); |
258 | TIFFGetField (tif: tiff, TIFFTAG_YRESOLUTION, &y_resolution); |
259 | |
260 | switch (resolution_unit) { |
261 | case RESUNIT_INCH: |
262 | density_str = g_strdup_printf (format: "%d" , (int) round (x: x_resolution)); |
263 | gdk_pixbuf_set_option (pixbuf, key: "x-dpi" , value: density_str); |
264 | g_free (mem: density_str); |
265 | density_str = g_strdup_printf (format: "%d" , (int) round (x: y_resolution)); |
266 | gdk_pixbuf_set_option (pixbuf, key: "y-dpi" , value: density_str); |
267 | g_free (mem: density_str); |
268 | break; |
269 | case RESUNIT_CENTIMETER: |
270 | density_str = g_strdup_printf (format: "%d" , DPCM_TO_DPI (x_resolution)); |
271 | gdk_pixbuf_set_option (pixbuf, key: "x-dpi" , value: density_str); |
272 | g_free (mem: density_str); |
273 | density_str = g_strdup_printf (format: "%d" , DPCM_TO_DPI (y_resolution)); |
274 | gdk_pixbuf_set_option (pixbuf, key: "y-dpi" , value: density_str); |
275 | g_free (mem: density_str); |
276 | break; |
277 | } |
278 | } |
279 | |
280 | if (context) |
281 | (* context->prepared_func) (pixbuf, NULL, context->user_data); |
282 | |
283 | if (!TIFFReadRGBAImageOriented (tiff, width, height, (uint32_t *)pixels, ORIENTATION_TOPLEFT, 1)) { |
284 | g_set_error_literal (err: error, |
285 | GDK_PIXBUF_ERROR, |
286 | code: GDK_PIXBUF_ERROR_FAILED, |
287 | _("Failed to load RGB data from TIFF file" )); |
288 | g_object_unref (object: pixbuf); |
289 | return NULL; |
290 | } |
291 | |
292 | /* Flag multi-page documents, because this loader only handles the |
293 | first page. The client app may wish to warn the user. */ |
294 | if (TIFFReadDirectory (tif: tiff)) |
295 | gdk_pixbuf_set_option (pixbuf, key: "multipage" , value: "yes" ); |
296 | |
297 | #if G_BYTE_ORDER == G_BIG_ENDIAN |
298 | { |
299 | guchar *pixbuf_pixels = gdk_pixbuf_get_pixels (pixbuf); |
300 | |
301 | pixels = pixbuf_pixels; |
302 | |
303 | /* Turns out that the packing used by TIFFRGBAImage depends on |
304 | * the host byte order... |
305 | */ |
306 | while (pixels < pixbuf_pixels + bytes) { |
307 | uint32 pixel = *(uint32 *)pixels; |
308 | int r = TIFFGetR(pixel); |
309 | int g = TIFFGetG(pixel); |
310 | int b = TIFFGetB(pixel); |
311 | int a = TIFFGetA(pixel); |
312 | *pixels++ = r; |
313 | *pixels++ = g; |
314 | *pixels++ = b; |
315 | *pixels++ = a; |
316 | } |
317 | } |
318 | #endif |
319 | |
320 | if (context) |
321 | (* context->updated_func) (pixbuf, 0, 0, width, height, context->user_data); |
322 | |
323 | return pixbuf; |
324 | } |
325 | |
326 | |
327 | |
328 | /* Static loader */ |
329 | |
330 | static GdkPixbuf * |
331 | gdk_pixbuf__tiff_image_load (FILE *f, GError **error) |
332 | { |
333 | TIFF *tiff = NULL; |
334 | int fd; |
335 | GdkPixbuf *pixbuf; |
336 | |
337 | g_return_val_if_fail (f != NULL, NULL); |
338 | |
339 | tiff_set_handlers (); |
340 | |
341 | fd = fileno (stream: f); |
342 | |
343 | /* On OSF, apparently fseek() works in some on-demand way, so |
344 | * the fseek gdk_pixbuf_new_from_file() doesn't work here |
345 | * since we are using the raw file descriptor. So, we call lseek() on the fd |
346 | * before using it. (#60840) |
347 | */ |
348 | lseek (fd: fd, offset: 0, SEEK_SET); |
349 | #ifndef G_OS_WIN32 |
350 | tiff = TIFFFdOpen (fd, "libpixbuf-tiff" , "r" ); |
351 | #else |
352 | /* W32 version of this function takes HANDLE. |
353 | * What's worse, the caller will close the file, |
354 | * but TIFFClose() will *also* close it, so we |
355 | * need to make a duplicate. |
356 | */ |
357 | { |
358 | HANDLE h; |
359 | |
360 | if (DuplicateHandle (GetCurrentProcess (), |
361 | (HANDLE) _get_osfhandle (fd), |
362 | GetCurrentProcess (), |
363 | &h, |
364 | 0, |
365 | FALSE, |
366 | DUPLICATE_SAME_ACCESS)) { |
367 | tiff = TIFFFdOpen ((intptr_t) h, "libpixbuf-tiff" , "r" ); |
368 | if (tiff == NULL) |
369 | CloseHandle (h); |
370 | } |
371 | } |
372 | #endif |
373 | |
374 | if (!tiff) { |
375 | g_set_error_literal (err: error, |
376 | GDK_PIXBUF_ERROR, |
377 | code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
378 | _("Failed to open TIFF image" )); |
379 | return NULL; |
380 | } |
381 | |
382 | pixbuf = tiff_image_parse (tiff, NULL, error); |
383 | |
384 | TIFFClose (tif: tiff); |
385 | |
386 | return pixbuf; |
387 | } |
388 | |
389 | |
390 | |
391 | /* Progressive loader */ |
392 | |
393 | static gpointer |
394 | gdk_pixbuf__tiff_image_begin_load (GdkPixbufModuleSizeFunc size_func, |
395 | GdkPixbufModulePreparedFunc prepared_func, |
396 | GdkPixbufModuleUpdatedFunc updated_func, |
397 | gpointer user_data, |
398 | GError **error) |
399 | { |
400 | TiffContext *context; |
401 | |
402 | g_assert (size_func != NULL); |
403 | g_assert (prepared_func != NULL); |
404 | g_assert (updated_func != NULL); |
405 | |
406 | context = g_new0 (TiffContext, 1); |
407 | context->size_func = size_func; |
408 | context->prepared_func = prepared_func; |
409 | context->updated_func = updated_func; |
410 | context->user_data = user_data; |
411 | context->buffer = NULL; |
412 | context->allocated = 0; |
413 | context->used = 0; |
414 | context->pos = 0; |
415 | |
416 | return context; |
417 | } |
418 | |
419 | static tsize_t |
420 | tiff_load_read (thandle_t handle, tdata_t buf, tsize_t size) |
421 | { |
422 | TiffContext *context = (TiffContext *)handle; |
423 | |
424 | if (context->pos + size > context->used) |
425 | return 0; |
426 | |
427 | memcpy (dest: buf, src: context->buffer + context->pos, n: size); |
428 | context->pos += size; |
429 | return size; |
430 | } |
431 | |
432 | static tsize_t |
433 | tiff_load_write (thandle_t handle, tdata_t buf, tsize_t size) |
434 | { |
435 | return -1; |
436 | } |
437 | |
438 | static toff_t |
439 | tiff_load_seek (thandle_t handle, toff_t offset, int whence) |
440 | { |
441 | TiffContext *context = (TiffContext *)handle; |
442 | |
443 | switch (whence) { |
444 | case SEEK_SET: |
445 | if (offset > context->used) |
446 | return -1; |
447 | context->pos = offset; |
448 | break; |
449 | case SEEK_CUR: |
450 | if (offset + context->pos >= context->used) |
451 | return -1; |
452 | context->pos += offset; |
453 | break; |
454 | case SEEK_END: |
455 | if (offset + context->used > context->used) |
456 | return -1; |
457 | context->pos = context->used + offset; |
458 | break; |
459 | default: |
460 | return -1; |
461 | } |
462 | return context->pos; |
463 | } |
464 | |
465 | static int |
466 | tiff_load_close (thandle_t context) |
467 | { |
468 | return 0; |
469 | } |
470 | |
471 | static toff_t |
472 | tiff_load_size (thandle_t handle) |
473 | { |
474 | TiffContext *context = (TiffContext *)handle; |
475 | |
476 | return context->used; |
477 | } |
478 | |
479 | static int |
480 | tiff_load_map_file (thandle_t handle, tdata_t *buf, toff_t *size) |
481 | { |
482 | TiffContext *context = (TiffContext *)handle; |
483 | |
484 | *buf = context->buffer; |
485 | *size = context->used; |
486 | |
487 | return 0; |
488 | } |
489 | |
490 | static void |
491 | tiff_load_unmap_file (thandle_t handle, tdata_t data, toff_t offset) |
492 | { |
493 | } |
494 | |
495 | static gboolean |
496 | gdk_pixbuf__tiff_image_stop_load (gpointer data, |
497 | GError **error) |
498 | { |
499 | TiffContext *context = data; |
500 | TIFF *tiff; |
501 | gboolean retval = FALSE; |
502 | |
503 | g_return_val_if_fail (data != NULL, FALSE); |
504 | |
505 | tiff_set_handlers (); |
506 | |
507 | tiff = TIFFClientOpen ("libtiff-pixbuf" , "r" , data, |
508 | tiff_load_read, tiff_load_write, |
509 | tiff_load_seek, tiff_load_close, |
510 | tiff_load_size, |
511 | tiff_load_map_file, tiff_load_unmap_file); |
512 | if (!tiff) { |
513 | g_set_error_literal (err: error, |
514 | GDK_PIXBUF_ERROR, |
515 | code: GDK_PIXBUF_ERROR_FAILED, |
516 | _("Failed to load TIFF image" )); |
517 | } else { |
518 | GdkPixbuf *pixbuf; |
519 | |
520 | pixbuf = tiff_image_parse (tiff, context, error); |
521 | retval = (pixbuf != NULL); |
522 | g_clear_object (&pixbuf); |
523 | /* tiff_image_parse() can return NULL on success in a particular case */ |
524 | if (!retval && error && !*error) { |
525 | g_set_error_literal (err: error, |
526 | GDK_PIXBUF_ERROR, |
527 | code: GDK_PIXBUF_ERROR_FAILED, |
528 | _("Failed to load TIFF image" )); |
529 | } |
530 | } |
531 | |
532 | if (tiff) |
533 | TIFFClose (tif: tiff); |
534 | |
535 | g_free (mem: context->buffer); |
536 | g_free (mem: context); |
537 | |
538 | return retval; |
539 | } |
540 | |
541 | static gboolean |
542 | make_available_at_least (TiffContext *context, guint needed) |
543 | { |
544 | guchar *new_buffer = NULL; |
545 | guint need_alloc; |
546 | |
547 | need_alloc = context->used + needed; |
548 | if (need_alloc > context->allocated) { |
549 | guint new_size = 1; |
550 | while (new_size < need_alloc) { |
551 | if (!g_uint_checked_mul (&new_size, new_size, 2)) { |
552 | new_size = 0; |
553 | break; |
554 | } |
555 | } |
556 | |
557 | if (new_size == 0) |
558 | return FALSE; |
559 | |
560 | new_buffer = g_try_realloc (mem: context->buffer, n_bytes: new_size); |
561 | if (new_buffer) { |
562 | context->buffer = new_buffer; |
563 | context->allocated = new_size; |
564 | return TRUE; |
565 | } |
566 | return FALSE; |
567 | } |
568 | return TRUE; |
569 | } |
570 | |
571 | static gboolean |
572 | gdk_pixbuf__tiff_image_load_increment (gpointer data, const guchar *buf, |
573 | guint size, GError **error) |
574 | { |
575 | TiffContext *context = (TiffContext *) data; |
576 | |
577 | g_return_val_if_fail (data != NULL, FALSE); |
578 | |
579 | tiff_set_handlers (); |
580 | |
581 | if (!make_available_at_least (context, needed: size)) { |
582 | g_set_error_literal (err: error, |
583 | GDK_PIXBUF_ERROR, |
584 | code: GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, |
585 | _("Insufficient memory to open TIFF file" )); |
586 | return FALSE; |
587 | } |
588 | |
589 | memcpy (dest: context->buffer + context->used, src: buf, n: size); |
590 | context->used += size; |
591 | return TRUE; |
592 | } |
593 | |
594 | typedef struct { |
595 | gchar *buffer; |
596 | guint allocated; |
597 | guint used; |
598 | guint pos; |
599 | } TiffSaveContext; |
600 | |
601 | static tsize_t |
602 | tiff_save_read (thandle_t handle, tdata_t buf, tsize_t size) |
603 | { |
604 | return -1; |
605 | } |
606 | |
607 | static tsize_t |
608 | tiff_save_write (thandle_t handle, tdata_t buf, tsize_t size) |
609 | { |
610 | TiffSaveContext *context = (TiffSaveContext *)handle; |
611 | |
612 | /* Modify buffer length */ |
613 | if (context->pos + size > context->used) |
614 | context->used = context->pos + size; |
615 | |
616 | /* Realloc */ |
617 | if (context->used > context->allocated) { |
618 | context->buffer = g_realloc (mem: context->buffer, n_bytes: context->pos + size); |
619 | context->allocated = context->used; |
620 | } |
621 | |
622 | /* Now copy the data */ |
623 | memcpy (dest: context->buffer + context->pos, src: buf, n: size); |
624 | |
625 | /* Update pos */ |
626 | context->pos += size; |
627 | |
628 | return size; |
629 | } |
630 | |
631 | static toff_t |
632 | tiff_save_seek (thandle_t handle, toff_t offset, int whence) |
633 | { |
634 | TiffSaveContext *context = (TiffSaveContext *)handle; |
635 | |
636 | switch (whence) { |
637 | case SEEK_SET: |
638 | context->pos = offset; |
639 | break; |
640 | case SEEK_CUR: |
641 | context->pos += offset; |
642 | break; |
643 | case SEEK_END: |
644 | context->pos = context->used + offset; |
645 | break; |
646 | default: |
647 | return -1; |
648 | } |
649 | return context->pos; |
650 | } |
651 | |
652 | static int |
653 | tiff_save_close (thandle_t context) |
654 | { |
655 | return 0; |
656 | } |
657 | |
658 | static toff_t |
659 | tiff_save_size (thandle_t handle) |
660 | { |
661 | return -1; |
662 | } |
663 | |
664 | static TiffSaveContext * |
665 | create_save_context (void) |
666 | { |
667 | TiffSaveContext *context; |
668 | |
669 | context = g_new (TiffSaveContext, 1); |
670 | context->buffer = NULL; |
671 | context->allocated = 0; |
672 | context->used = 0; |
673 | context->pos = 0; |
674 | |
675 | return context; |
676 | } |
677 | |
678 | static void |
679 | free_save_context (TiffSaveContext *context) |
680 | { |
681 | g_free (mem: context->buffer); |
682 | g_free (mem: context); |
683 | } |
684 | |
685 | static void |
686 | copy_gray_row (gint *dest, |
687 | guchar *src, |
688 | gint width, |
689 | gboolean has_alpha) |
690 | { |
691 | gint i; |
692 | guchar *p; |
693 | |
694 | p = src; |
695 | for (i = 0; i < width; i++) { |
696 | int pr, pg, pb, pv; |
697 | |
698 | pr = *p++; |
699 | pg = *p++; |
700 | pb = *p++; |
701 | |
702 | if (has_alpha) { |
703 | int pa = *p++; |
704 | |
705 | /* Premul alpha to simulate it */ |
706 | if (pa > 0) { |
707 | pr = pr * pa / 255; |
708 | pg = pg * pa / 255; |
709 | pb = pb * pa / 255; |
710 | } else { |
711 | pr = pg = pb = 0; |
712 | } |
713 | } |
714 | |
715 | /* Calculate value MAX(MAX(r,g),b) */ |
716 | pv = pr > pg ? pr : pg; |
717 | pv = pv > pb ? pv : pb; |
718 | |
719 | *dest++ = pv; |
720 | } |
721 | } |
722 | |
723 | static gboolean |
724 | gdk_pixbuf__tiff_image_save_to_callback (GdkPixbufSaveFunc save_func, |
725 | gpointer user_data, |
726 | GdkPixbuf *pixbuf, |
727 | gchar **keys, |
728 | gchar **values, |
729 | GError **error) |
730 | { |
731 | TIFF *tiff; |
732 | gint width, height, rowstride; |
733 | const gchar *bits_per_sample = NULL; |
734 | long bps; |
735 | const gchar *compression = NULL; |
736 | guchar *pixels; |
737 | gboolean has_alpha; |
738 | gushort alpha_samples[1] = { EXTRASAMPLE_UNASSALPHA }; |
739 | int y; |
740 | TiffSaveContext *context; |
741 | gboolean retval; |
742 | const gchar *icc_profile = NULL; |
743 | const gchar *x_dpi = NULL; |
744 | const gchar *y_dpi = NULL; |
745 | guint16 codec; |
746 | |
747 | tiff_set_handlers (); |
748 | |
749 | context = create_save_context (); |
750 | tiff = TIFFClientOpen ("libtiff-pixbuf" , "w" , context, |
751 | tiff_save_read, tiff_save_write, |
752 | tiff_save_seek, tiff_save_close, |
753 | tiff_save_size, |
754 | NULL, NULL); |
755 | |
756 | if (!tiff) { |
757 | g_set_error_literal (err: error, |
758 | GDK_PIXBUF_ERROR, |
759 | code: GDK_PIXBUF_ERROR_FAILED, |
760 | _("Failed to save TIFF image" )); |
761 | free_save_context (context); |
762 | return FALSE; |
763 | } |
764 | |
765 | rowstride = gdk_pixbuf_get_rowstride (pixbuf); |
766 | pixels = gdk_pixbuf_get_pixels (pixbuf); |
767 | |
768 | has_alpha = gdk_pixbuf_get_has_alpha (pixbuf); |
769 | |
770 | height = gdk_pixbuf_get_height (pixbuf); |
771 | width = gdk_pixbuf_get_width (pixbuf); |
772 | |
773 | /* Guaranteed by the caller. */ |
774 | g_assert (width >= 0); |
775 | g_assert (height >= 0); |
776 | g_assert (rowstride >= 0); |
777 | |
778 | TIFFSetField (tiff, TIFFTAG_IMAGEWIDTH, width); |
779 | TIFFSetField (tiff, TIFFTAG_IMAGELENGTH, height); |
780 | |
781 | /* libtiff supports a number of 'codecs' such as: |
782 | 1 None, 2 Huffman, 5 LZW, 7 JPEG, 8 Deflate, see tiff.h */ |
783 | if (keys && *keys && values && *values) { |
784 | guint i = 0; |
785 | |
786 | while (keys[i]) { |
787 | if (g_str_equal (v1: keys[i], v2: "bits-per-sample" )) |
788 | bits_per_sample = values[i]; |
789 | else if (g_str_equal (v1: keys[i], v2: "compression" )) |
790 | compression = values[i]; |
791 | else if (g_str_equal (v1: keys[i], v2: "icc-profile" )) |
792 | icc_profile = values[i]; |
793 | else if (g_str_equal (v1: keys[i], v2: "x-dpi" )) |
794 | x_dpi = values[i]; |
795 | else if (g_str_equal (v1: keys[i], v2: "y-dpi" )) |
796 | y_dpi = values[i]; |
797 | i++; |
798 | } |
799 | } |
800 | |
801 | /* Use 8 bits per sample by default, if none was recorded or |
802 | specified. */ |
803 | if (!bits_per_sample) |
804 | bits_per_sample = "8" ; |
805 | |
806 | /* Use DEFLATE compression (8) by default, if none was recorded |
807 | or specified. */ |
808 | if (!compression) |
809 | compression = "8" ; |
810 | |
811 | /* libtiff supports a number of 'codecs' such as: |
812 | 1 None, 2 Huffman, 5 LZW, 7 JPEG, 8 Deflate, see tiff.h */ |
813 | codec = strtol (nptr: compression, NULL, base: 0); |
814 | |
815 | if (TIFFIsCODECConfigured (codec)) |
816 | TIFFSetField (tiff, TIFFTAG_COMPRESSION, codec); |
817 | else { |
818 | g_set_error_literal (err: error, |
819 | GDK_PIXBUF_ERROR, |
820 | code: GDK_PIXBUF_ERROR_FAILED, |
821 | _("TIFF compression doesn’t refer to a valid codec." )); |
822 | retval = FALSE; |
823 | goto cleanup; |
824 | } |
825 | |
826 | /* We support 1-bit or 8-bit saving */ |
827 | bps = atol (nptr: bits_per_sample); |
828 | if (bps == 1) { |
829 | TIFFSetField (tiff, TIFFTAG_BITSPERSAMPLE, 1); |
830 | TIFFSetField (tiff, TIFFTAG_SAMPLESPERPIXEL, 1); |
831 | TIFFSetField (tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); |
832 | TIFFSetField (tiff, TIFFTAG_COMPRESSION, COMPRESSION_CCITTFAX4); |
833 | } else if (bps == 8) { |
834 | TIFFSetField (tiff, TIFFTAG_BITSPERSAMPLE, 8); |
835 | TIFFSetField (tiff, TIFFTAG_SAMPLESPERPIXEL, has_alpha ? 4 : 3); |
836 | TIFFSetField (tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); |
837 | |
838 | if (has_alpha) |
839 | TIFFSetField (tiff, TIFFTAG_EXTRASAMPLES, 1, alpha_samples); |
840 | |
841 | if (icc_profile != NULL) { |
842 | guchar *icc_profile_buf; |
843 | gsize icc_profile_size; |
844 | |
845 | /* decode from base64 */ |
846 | icc_profile_buf = g_base64_decode (text: icc_profile, out_len: &icc_profile_size); |
847 | if (icc_profile_size < 127) { |
848 | g_set_error (err: error, |
849 | GDK_PIXBUF_ERROR, |
850 | code: GDK_PIXBUF_ERROR_BAD_OPTION, |
851 | _("Color profile has invalid length %d." ), |
852 | (gint) icc_profile_size); |
853 | retval = FALSE; |
854 | g_free (mem: icc_profile_buf); |
855 | goto cleanup; |
856 | } |
857 | |
858 | TIFFSetField (tiff, TIFFTAG_ICCPROFILE, icc_profile_size, icc_profile_buf); |
859 | g_free (mem: icc_profile_buf); |
860 | } |
861 | } else { |
862 | /* The passed bits-per-sample is not supported. */ |
863 | g_set_error_literal (err: error, |
864 | GDK_PIXBUF_ERROR, |
865 | code: GDK_PIXBUF_ERROR_FAILED, |
866 | _("TIFF bits-per-sample doesn’t contain a supported value." )); |
867 | retval = FALSE; |
868 | goto cleanup; |
869 | } |
870 | |
871 | TIFFSetField (tiff, TIFFTAG_ROWSPERSTRIP, height); |
872 | TIFFSetField (tiff, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB); |
873 | TIFFSetField (tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); |
874 | |
875 | if (bps == 1) { |
876 | guchar *mono_row; |
877 | gint *dith_row_1, *dith_row_2, *dith_row_tmp; |
878 | |
879 | dith_row_1 = g_new (gint, width); |
880 | dith_row_2 = g_new (gint, width); |
881 | mono_row = g_malloc (n_bytes: (width + 7) / 8); |
882 | |
883 | copy_gray_row (dest: dith_row_1, src: pixels, width, has_alpha); |
884 | |
885 | for (y = 0; y < height; y++) { |
886 | guint x; |
887 | gint *p; |
888 | |
889 | memset (s: mono_row, c: 0, n: (width + 7) / 8); |
890 | |
891 | if (y > 0) { |
892 | dith_row_tmp = dith_row_1; |
893 | dith_row_1 = dith_row_2; |
894 | dith_row_2 = dith_row_tmp; |
895 | } |
896 | |
897 | if (y < (height - 1)) |
898 | copy_gray_row (dest: dith_row_2, src: pixels + ((y + 1) * rowstride), width, has_alpha); |
899 | |
900 | p = dith_row_1; |
901 | for (x = 0; x < width; x++) { |
902 | gint p_old, p_new, quant_error; |
903 | |
904 | /* Apply Floyd-Steinberg dithering */ |
905 | |
906 | p_old = *p++; |
907 | |
908 | if (p_old > 127) |
909 | p_new = 255; |
910 | else |
911 | p_new = 0; |
912 | |
913 | quant_error = p_old - p_new; |
914 | if (x < (width - 1)) |
915 | dith_row_1[x + 1] += 7 * quant_error / 16; |
916 | if (y < (height - 1)) { |
917 | if (x > 0) |
918 | dith_row_2[x - 1] += 3 * quant_error / 16; |
919 | |
920 | dith_row_2[x] += 5 * quant_error / 16; |
921 | |
922 | if (x < (width - 1)) |
923 | dith_row_2[x + 1] += quant_error / 16; |
924 | } |
925 | |
926 | if (p_new > 127) |
927 | mono_row[x / 8] |= (0x1 << (7 - (x % 8))); |
928 | } |
929 | |
930 | if (TIFFWriteScanline (tif: tiff, buf: mono_row, row: y, sample: 0) == -1) |
931 | break; |
932 | } |
933 | g_free (mem: mono_row); |
934 | g_free (mem: dith_row_1); |
935 | g_free (mem: dith_row_2); |
936 | } else { |
937 | for (y = 0; y < height; y++) { |
938 | if (TIFFWriteScanline (tif: tiff, buf: pixels + y * rowstride, row: y, sample: 0) == -1) |
939 | break; |
940 | } |
941 | } |
942 | |
943 | if (y < height) { |
944 | g_set_error_literal (err: error, |
945 | GDK_PIXBUF_ERROR, |
946 | code: GDK_PIXBUF_ERROR_FAILED, |
947 | _("Failed to write TIFF data" )); |
948 | TIFFClose (tif: tiff); |
949 | retval = FALSE; |
950 | goto cleanup; |
951 | } |
952 | |
953 | if (x_dpi != NULL && y_dpi != NULL) { |
954 | char *endptr = NULL; |
955 | uint16_t resolution_unit = RESUNIT_INCH; |
956 | float x_dpi_value, y_dpi_value; |
957 | |
958 | x_dpi_value = strtol (nptr: x_dpi, endptr: &endptr, base: 10); |
959 | if (x_dpi[0] != '\0' && *endptr != '\0') |
960 | x_dpi_value = -1; |
961 | if (x_dpi_value <= 0) { |
962 | g_set_error (err: error, |
963 | GDK_PIXBUF_ERROR, |
964 | code: GDK_PIXBUF_ERROR_BAD_OPTION, |
965 | _("TIFF x-dpi must be greater than zero; value “%s” is not allowed." ), |
966 | x_dpi); |
967 | retval = FALSE; |
968 | goto cleanup; |
969 | } |
970 | y_dpi_value = strtol (nptr: y_dpi, endptr: &endptr, base: 10); |
971 | if (y_dpi[0] != '\0' && *endptr != '\0') |
972 | y_dpi_value = -1; |
973 | if (y_dpi_value <= 0) { |
974 | g_set_error (err: error, |
975 | GDK_PIXBUF_ERROR, |
976 | code: GDK_PIXBUF_ERROR_BAD_OPTION, |
977 | _("TIFF y-dpi must be greater than zero; value “%s” is not allowed." ), |
978 | y_dpi); |
979 | retval = FALSE; |
980 | goto cleanup; |
981 | } |
982 | |
983 | TIFFSetField (tiff, TIFFTAG_RESOLUTIONUNIT, resolution_unit); |
984 | TIFFSetField (tiff, TIFFTAG_XRESOLUTION, x_dpi_value); |
985 | TIFFSetField (tiff, TIFFTAG_YRESOLUTION, y_dpi_value); |
986 | } |
987 | |
988 | TIFFClose (tif: tiff); |
989 | |
990 | /* Now call the callback */ |
991 | retval = save_func (context->buffer, context->used, error, user_data); |
992 | |
993 | cleanup: |
994 | free_save_context (context); |
995 | return retval; |
996 | } |
997 | |
998 | static gboolean |
999 | save_to_file_cb (const gchar *buf, |
1000 | gsize count, |
1001 | GError **error, |
1002 | gpointer data) |
1003 | { |
1004 | gint bytes; |
1005 | |
1006 | while (count > 0) { |
1007 | bytes = fwrite (ptr: buf, size: sizeof (gchar), n: count, s: (FILE *) data); |
1008 | if (bytes <= 0) |
1009 | break; |
1010 | count -= bytes; |
1011 | buf += bytes; |
1012 | } |
1013 | |
1014 | if (count) { |
1015 | g_set_error_literal (err: error, |
1016 | GDK_PIXBUF_ERROR, |
1017 | code: GDK_PIXBUF_ERROR_FAILED, |
1018 | _("Couldn’t write to TIFF file" )); |
1019 | return FALSE; |
1020 | } |
1021 | |
1022 | return TRUE; |
1023 | } |
1024 | |
1025 | static gboolean |
1026 | gdk_pixbuf__tiff_image_save (FILE *f, |
1027 | GdkPixbuf *pixbuf, |
1028 | gchar **keys, |
1029 | gchar **values, |
1030 | GError **error) |
1031 | { |
1032 | return gdk_pixbuf__tiff_image_save_to_callback (save_func: save_to_file_cb, |
1033 | user_data: f, pixbuf, keys, |
1034 | values, error); |
1035 | } |
1036 | |
1037 | static gboolean |
1038 | gdk_pixbuf__tiff_is_save_option_supported (const gchar *option_key) |
1039 | { |
1040 | if (g_strcmp0 (str1: option_key, str2: "bits-per-sample" ) == 0 || |
1041 | g_strcmp0 (str1: option_key, str2: "compression" ) == 0 || |
1042 | g_strcmp0 (str1: option_key, str2: "icc-profile" ) == 0 || |
1043 | g_strcmp0 (str1: option_key, str2: "x-dpi" ) == 0 || |
1044 | g_strcmp0 (str1: option_key, str2: "y-dpi" ) == 0) |
1045 | return TRUE; |
1046 | |
1047 | return FALSE; |
1048 | } |
1049 | |
1050 | #ifndef INCLUDE_tiff |
1051 | #define MODULE_ENTRY(function) G_MODULE_EXPORT void function |
1052 | #else |
1053 | #define MODULE_ENTRY(function) void _gdk_pixbuf__tiff_ ## function |
1054 | #endif |
1055 | |
1056 | MODULE_ENTRY (fill_vtable) (GdkPixbufModule *module) |
1057 | { |
1058 | module->load = gdk_pixbuf__tiff_image_load; |
1059 | module->begin_load = gdk_pixbuf__tiff_image_begin_load; |
1060 | module->stop_load = gdk_pixbuf__tiff_image_stop_load; |
1061 | module->load_increment = gdk_pixbuf__tiff_image_load_increment; |
1062 | module->save = gdk_pixbuf__tiff_image_save; |
1063 | module->save_to_callback = gdk_pixbuf__tiff_image_save_to_callback; |
1064 | module->is_save_option_supported = gdk_pixbuf__tiff_is_save_option_supported; |
1065 | } |
1066 | |
1067 | MODULE_ENTRY (fill_info) (GdkPixbufFormat *info) |
1068 | { |
1069 | static const GdkPixbufModulePattern signature[] = { |
1070 | { "MM \x2a" , " z " , 100 }, |
1071 | { "II\x2a " , " z" , 100 }, |
1072 | { "II* \020 CR\002 " , " z zzz z" , 0 }, |
1073 | { NULL, NULL, 0 } |
1074 | }; |
1075 | static const gchar *mime_types[] = { |
1076 | "image/tiff" , |
1077 | NULL |
1078 | }; |
1079 | static const gchar *extensions[] = { |
1080 | "tiff" , |
1081 | "tif" , |
1082 | NULL |
1083 | }; |
1084 | |
1085 | info->name = "tiff" ; |
1086 | info->signature = (GdkPixbufModulePattern *) signature; |
1087 | info->description = NC_("image format" , "TIFF" ); |
1088 | info->mime_types = (gchar **) mime_types; |
1089 | info->extensions = (gchar **) extensions; |
1090 | info->flags = GDK_PIXBUF_FORMAT_WRITABLE | GDK_PIXBUF_FORMAT_THREADSAFE; |
1091 | info->license = "LGPL" ; |
1092 | } |
1093 | |