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
58typedef struct _TiffContext TiffContext;
59struct _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
72static void
73tiff_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
80static void
81tiff_set_handlers (void)
82{
83 TIFFSetErrorHandler (tiff_warning_handler);
84 TIFFSetWarningHandler (tiff_warning_handler);
85}
86
87
88
89static void free_buffer (guchar *pixels, gpointer data)
90{
91 g_free (mem: pixels);
92}
93
94static GdkPixbuf *
95tiff_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
330static GdkPixbuf *
331gdk_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
393static gpointer
394gdk_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
419static tsize_t
420tiff_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
432static tsize_t
433tiff_load_write (thandle_t handle, tdata_t buf, tsize_t size)
434{
435 return -1;
436}
437
438static toff_t
439tiff_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
465static int
466tiff_load_close (thandle_t context)
467{
468 return 0;
469}
470
471static toff_t
472tiff_load_size (thandle_t handle)
473{
474 TiffContext *context = (TiffContext *)handle;
475
476 return context->used;
477}
478
479static int
480tiff_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
490static void
491tiff_load_unmap_file (thandle_t handle, tdata_t data, toff_t offset)
492{
493}
494
495static gboolean
496gdk_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
541static gboolean
542make_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
571static gboolean
572gdk_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
594typedef struct {
595 gchar *buffer;
596 guint allocated;
597 guint used;
598 guint pos;
599} TiffSaveContext;
600
601static tsize_t
602tiff_save_read (thandle_t handle, tdata_t buf, tsize_t size)
603{
604 return -1;
605}
606
607static tsize_t
608tiff_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
631static toff_t
632tiff_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
652static int
653tiff_save_close (thandle_t context)
654{
655 return 0;
656}
657
658static toff_t
659tiff_save_size (thandle_t handle)
660{
661 return -1;
662}
663
664static TiffSaveContext *
665create_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
678static void
679free_save_context (TiffSaveContext *context)
680{
681 g_free (mem: context->buffer);
682 g_free (mem: context);
683}
684
685static void
686copy_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
723static gboolean
724gdk_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
993cleanup:
994 free_save_context (context);
995 return retval;
996}
997
998static gboolean
999save_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
1025static gboolean
1026gdk_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
1037static gboolean
1038gdk_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
1056MODULE_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
1067MODULE_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

source code of gtk/subprojects/gdk-pixbuf/gdk-pixbuf/io-tiff.c