]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/io-tiff.c
Bump libtiff requirement to 3.6.0, by requiring presence of
[~andy/gtk] / gdk-pixbuf / io-tiff.c
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  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public
14  * License as published by the Free Software Foundation; either
15  * version 2 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public
23  * License along with this library; if not, write to the
24  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25  * Boston, MA 02111-1307, USA.
26  */
27
28 /* Following code (almost) blatantly ripped from Imlib */
29
30 #include <config.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36 #include <tiffio.h>
37 #include <errno.h>
38 #include "gdk-pixbuf-private.h"
39 #include "gdk-pixbuf-io.h"
40
41 #ifdef G_OS_WIN32
42 #include <fcntl.h>
43 #include <io.h>
44 #define lseek(a,b,c) _lseek(a,b,c)
45 #define O_RDWR _O_RDWR
46 #endif
47 \f
48
49 typedef struct _TiffContext TiffContext;
50 struct _TiffContext
51 {
52         GdkPixbufModuleSizeFunc size_func;
53         GdkPixbufModulePreparedFunc prepare_func;
54         GdkPixbufModuleUpdatedFunc update_func;
55         gpointer user_data;
56         
57         guchar *buffer;
58         guint allocated;
59         guint used;
60         guint pos;
61 };
62
63 \f
64
65 static char *global_error = NULL;
66 static TIFFErrorHandler orig_error_handler = NULL;
67 static TIFFErrorHandler orig_warning_handler = NULL;
68
69 static void
70 tiff_warning_handler (const char *mod, const char *fmt, va_list ap)
71 {
72         /* Don't print anything; we should not be dumping junk to
73          * stderr, since that may be bad for some apps.
74          */
75
76         /* libTIFF seems to occasionally warn about things that
77          * are really errors, so maybe we should just call tiff_error_handler
78          * here.
79          */
80 }
81
82 static void
83 tiff_error_handler (const char *mod, const char *fmt, va_list ap)
84 {
85         if (global_error) {                
86                 /* Blah, loader called us twice */
87                 return;
88         }
89
90         global_error = g_strdup_vprintf (fmt, ap);
91 }
92
93 static void
94 tiff_push_handlers (void)
95 {
96         if (global_error)
97                 g_warning ("TIFF loader left crufty global_error around, FIXME");
98         
99         orig_error_handler = TIFFSetErrorHandler (tiff_error_handler);
100         orig_warning_handler = TIFFSetWarningHandler (tiff_warning_handler);
101 }
102
103 static void
104 tiff_pop_handlers (void)
105 {
106         if (global_error)
107                 g_warning ("TIFF loader left crufty global_error around, FIXME");
108         
109         TIFFSetErrorHandler (orig_error_handler);
110         TIFFSetWarningHandler (orig_warning_handler);
111 }
112
113 static void
114 tiff_set_error (GError    **error,
115                 int         error_code,
116                 const char *msg)
117 {
118         /* Take the error message from libtiff and merge it with
119          * some context we provide.
120          */
121         if (global_error) {
122                 g_set_error (error,
123                              GDK_PIXBUF_ERROR,
124                              error_code,
125                              "%s%s%s", msg, ": ", global_error);
126
127                 g_free (global_error);
128                 global_error = NULL;
129         }
130         else {
131                 g_set_error (error,
132                              GDK_PIXBUF_ERROR,
133                              error_code, msg);
134         }
135 }
136
137 \f
138
139 static void free_buffer (guchar *pixels, gpointer data)
140 {
141         g_free (pixels);
142 }
143
144 static GdkPixbuf *
145 tiff_image_parse (TIFF *tiff, TiffContext *context, GError **error)
146 {
147         guchar *pixels = NULL;
148         gint width, height, rowstride, bytes;
149         GdkPixbuf *pixbuf;
150         uint16 orientation = 0;
151         uint16 transform = 0;
152
153         /* We're called with the lock held. */
154         
155         g_return_val_if_fail (global_error == NULL, NULL);
156
157         if (!TIFFGetField (tiff, TIFFTAG_IMAGEWIDTH, &width) || global_error) {
158                 tiff_set_error (error,
159                                 GDK_PIXBUF_ERROR_FAILED,
160                                 _("Could not get image width (bad TIFF file)"));
161                 return NULL;
162         }
163         
164         if (!TIFFGetField (tiff, TIFFTAG_IMAGELENGTH, &height) || global_error) {
165                 tiff_set_error (error,
166                                 GDK_PIXBUF_ERROR_FAILED,
167                                 _("Could not get image height (bad TIFF file)"));
168                 return NULL;
169         }
170
171         if (width <= 0 || height <= 0) {
172                 g_set_error (error,
173                              GDK_PIXBUF_ERROR,
174                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
175                              _("Width or height of TIFF image is zero"));
176                 return NULL;                
177         }
178         
179         rowstride = width * 4;
180         if (rowstride / 4 != width) { /* overflow */
181                 g_set_error (error,
182                              GDK_PIXBUF_ERROR,
183                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
184                              _("Dimensions of TIFF image too large"));
185                 return NULL;                
186         }
187         
188         bytes = height * rowstride;
189         if (bytes / rowstride != height) { /* overflow */
190                 g_set_error (error,
191                              GDK_PIXBUF_ERROR,
192                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
193                              _("Dimensions of TIFF image too large"));
194                 return NULL;                
195         }
196
197         if (context && context->size_func) {
198                 gint w = width;
199                 gint h = height;
200                 (* context->size_func) (&w, &h, context->user_data);
201                 
202                 if (w == 0 || h == 0)
203                     return NULL;
204         }
205
206         pixels = g_try_malloc (bytes);
207
208         if (!pixels) {
209                 g_set_error (error,
210                              GDK_PIXBUF_ERROR,
211                              GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
212                              _("Insufficient memory to open TIFF file"));
213                 return NULL;
214         }
215
216         pixbuf = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, TRUE, 8, 
217                                            width, height, rowstride,
218                                            free_buffer, NULL);
219         if (!pixbuf) {
220                 g_free (pixels);
221                 g_set_error (error,
222                              GDK_PIXBUF_ERROR,
223                              GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
224                              _("Insufficient memory to open TIFF file"));
225                 return NULL;
226         }
227
228         /* Set the "orientation" key associated with this image. libtiff 
229            orientation handling is odd, so further processing is required
230            by higher-level functions based on this tag. If the embedded
231            orientation tag is 1-4, libtiff flips/mirrors the image as
232            required, and no client processing is required - so we report 
233            no orientation. Orientations 5-8 require rotations which would 
234            swap the width and height of the image. libtiff does not do this. 
235            Instead it interprets orientations 5-8 the same as 1-4. 
236            See http://bugzilla.remotesensing.org/show_bug.cgi?id=1548.
237            To correct for this, the client must apply the transform normally
238            used for orientation 5 to both orientations 5 and 7, and apply
239            the transform normally used for orientation 7 for both
240            orientations 6 and 8. Then everythings works out OK! */
241         
242         TIFFGetField (tiff, TIFFTAG_ORIENTATION, &orientation);
243
244         switch (orientation) {
245                 case 5:
246                 case 7:
247                         transform = 5;
248                         break;
249                 case 6:
250                 case 8:
251                         transform = 7;
252                         break;
253                 default:
254                         transform = 0;
255                         break;
256         }
257
258         if (transform > 0 ) {
259                 gchar str[5];
260                 snprintf (str, sizeof (str), "%d", transform);
261                 gdk_pixbuf_set_option (pixbuf, "orientation", str);
262         }
263
264         if (context && context->prepare_func)
265                 (* context->prepare_func) (pixbuf, NULL, context->user_data);
266
267         if (!TIFFReadRGBAImageOriented (tiff, width, height, (uint32 *)pixels, ORIENTATION_TOPLEFT, 1) || global_error) {
268                 tiff_set_error (error,
269                                 GDK_PIXBUF_ERROR_FAILED,
270                                 _("Failed to load RGB data from TIFF file"));
271                 g_object_unref (pixbuf);
272                 return NULL;
273         }
274
275 #if G_BYTE_ORDER == G_BIG_ENDIAN
276         /* Turns out that the packing used by TIFFRGBAImage depends on 
277          * the host byte order... 
278          */ 
279         while (pixels < pixbuf->pixels + bytes) {
280                 uint32 pixel = *(uint32 *)pixels;
281                 int r = TIFFGetR(pixel);
282                 int g = TIFFGetG(pixel);
283                 int b = TIFFGetB(pixel);
284                 int a = TIFFGetA(pixel);
285                 *pixels++ = r;
286                 *pixels++ = g;
287                 *pixels++ = b;
288                 *pixels++ = a;
289         }
290 #endif
291
292         if (context && context->update_func)
293                 (* context->update_func) (pixbuf, 0, 0, width, height, context->user_data);
294
295         return pixbuf;
296 }
297
298 \f
299
300 /* Static loader */
301
302 static GdkPixbuf *
303 gdk_pixbuf__tiff_image_load (FILE *f, GError **error)
304 {
305         TIFF *tiff;
306         int fd;
307         GdkPixbuf *pixbuf;
308         
309         g_return_val_if_fail (f != NULL, NULL);
310
311         tiff_push_handlers ();
312         
313         fd = fileno (f);
314
315         /* On OSF, apparently fseek() works in some on-demand way, so
316          * the fseek gdk_pixbuf_new_from_file() doesn't work here
317          * since we are using the raw file descriptor. So, we call lseek() on the fd
318          * before using it. (#60840)
319          */
320         lseek (fd, 0, SEEK_SET);
321         tiff = TIFFFdOpen (fd, "libpixbuf-tiff", "r");
322         
323         if (!tiff || global_error) {
324                 tiff_set_error (error,
325                                 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
326                                 _("Failed to open TIFF image"));
327                 tiff_pop_handlers ();
328
329                 return NULL;
330         }
331
332         pixbuf = tiff_image_parse (tiff, NULL, error);
333         
334         TIFFClose (tiff);
335         if (global_error) {
336                 tiff_set_error (error,
337                                 GDK_PIXBUF_ERROR_FAILED,
338                                 _("TIFFClose operation failed"));
339         }
340         
341         tiff_pop_handlers ();
342
343         return pixbuf;
344 }
345
346 \f
347
348 /* Progressive loader */
349
350 static gpointer
351 gdk_pixbuf__tiff_image_begin_load (GdkPixbufModuleSizeFunc size_func,
352                                    GdkPixbufModulePreparedFunc prepare_func,
353                                    GdkPixbufModuleUpdatedFunc update_func,
354                                    gpointer user_data,
355                                    GError **error)
356 {
357         TiffContext *context;
358         
359         context = g_new0 (TiffContext, 1);
360         context->size_func = size_func;
361         context->prepare_func = prepare_func;
362         context->update_func = update_func;
363         context->user_data = user_data;
364         context->buffer = NULL;
365         context->allocated = 0;
366         context->used = 0;
367         context->pos = 0;
368         
369         return context;
370 }
371
372 static tsize_t
373 tiff_load_read (thandle_t handle, tdata_t buf, tsize_t size)
374 {
375         TiffContext *context = (TiffContext *)handle;
376         
377         if (context->pos + size > context->used)
378                 return 0;
379         
380         memcpy (buf, context->buffer + context->pos, size);
381         context->pos += size;
382         return size;
383 }
384
385 static tsize_t
386 tiff_load_write (thandle_t handle, tdata_t buf, tsize_t size)
387 {
388         return -1;
389 }
390
391 static toff_t
392 tiff_load_seek (thandle_t handle, toff_t offset, int whence)
393 {
394         TiffContext *context = (TiffContext *)handle;
395         
396         switch (whence) {
397         case SEEK_SET:
398                 if (offset > context->used)
399                         return -1;
400                 context->pos = offset;
401                 break;
402         case SEEK_CUR:
403                 if (offset + context->pos >= context->used)
404                         return -1;
405                 context->pos += offset;
406                 break;
407         case SEEK_END:
408                 if (offset + context->used > context->used)
409                         return -1;
410                 context->pos = context->used + offset;
411                 break;
412         default:
413                 return -1;
414         }
415         return context->pos;
416 }
417
418 static int
419 tiff_load_close (thandle_t context)
420 {
421         return 0;
422 }
423
424 static toff_t
425 tiff_load_size (thandle_t handle)
426 {
427         TiffContext *context = (TiffContext *)handle;
428         
429         return context->used;
430 }
431
432 static int
433 tiff_load_map_file (thandle_t handle, tdata_t *buf, toff_t *size)
434 {
435         TiffContext *context = (TiffContext *)handle;
436         
437         *buf = context->buffer;
438         *size = context->used;
439         
440         return 0;
441 }
442
443 static void
444 tiff_load_unmap_file (thandle_t handle, tdata_t data, toff_t offset)
445 {
446 }
447
448 static gboolean
449 gdk_pixbuf__tiff_image_stop_load (gpointer data,
450                                   GError **error)
451 {
452         TiffContext *context = data;
453         TIFF *tiff;
454         gboolean retval;
455         
456         g_return_val_if_fail (data != NULL, FALSE);
457
458         tiff_push_handlers ();
459         
460         tiff = TIFFClientOpen ("libtiff-pixbuf", "r", data, 
461                                tiff_load_read, tiff_load_write, 
462                                tiff_load_seek, tiff_load_close, 
463                                tiff_load_size, 
464                                tiff_load_map_file, tiff_load_unmap_file);
465         if (!tiff || global_error) {
466                 tiff_set_error (error,
467                                 GDK_PIXBUF_ERROR_FAILED,
468                                 _("Failed to load TIFF image"));
469                 retval = FALSE;
470         } else {
471                 GdkPixbuf *pixbuf;
472                 
473                 pixbuf = tiff_image_parse (tiff, context, error);
474                 if (pixbuf)
475                         g_object_unref (pixbuf);
476                 retval = pixbuf != NULL;
477                 if (global_error)
478                         {
479                                 tiff_set_error (error,
480                                                 GDK_PIXBUF_ERROR_FAILED,
481                                                 _("Failed to load TIFF image"));
482                                 tiff_pop_handlers ();
483
484                                 retval = FALSE;
485                         }
486         }
487
488         if (tiff)
489                 TIFFClose (tiff);
490
491         g_assert (!global_error);
492         
493         g_free (context->buffer);
494         g_free (context);
495
496         tiff_pop_handlers ();
497
498         return retval;
499 }
500
501 static gboolean
502 make_available_at_least (TiffContext *context, guint needed)
503 {
504         guchar *new_buffer = NULL;
505         guint need_alloc;
506         
507         need_alloc = context->used + needed;
508         if (need_alloc > context->allocated) {
509                 guint new_size = 1;
510                 while (new_size < need_alloc)
511                         new_size *= 2;
512                 
513                 new_buffer = g_try_realloc (context->buffer, new_size);
514                 if (new_buffer) {
515                         context->buffer = new_buffer;
516                         context->allocated = new_size;
517                         return TRUE;
518                 }
519                 return FALSE;
520         }
521         return TRUE;
522 }
523
524 static gboolean
525 gdk_pixbuf__tiff_image_load_increment (gpointer data, const guchar *buf,
526                                        guint size, GError **error)
527 {
528         TiffContext *context = (TiffContext *) data;
529         
530         g_return_val_if_fail (data != NULL, FALSE);
531         
532         if (!make_available_at_least (context, size)) {
533                 g_set_error (error,
534                              GDK_PIXBUF_ERROR,
535                              GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
536                              _("Insufficient memory to open TIFF file"));
537                 return FALSE;
538         }
539         
540         memcpy (context->buffer + context->used, buf, size);
541         context->used += size;
542         return TRUE;
543 }
544
545 typedef struct {
546         gchar *buffer;
547         guint allocated;
548         guint used;
549         guint pos;
550 } TiffSaveContext;
551
552 static tsize_t
553 tiff_save_read (thandle_t handle, tdata_t buf, tsize_t size)
554 {
555         return -1;
556 }
557
558 static tsize_t
559 tiff_save_write (thandle_t handle, tdata_t buf, tsize_t size)
560 {
561         TiffSaveContext *context = (TiffSaveContext *)handle;
562
563         /* Modify buffer length */
564         if (context->pos + size > context->used)
565                 context->used = context->pos + size;
566
567         /* Realloc */
568         if (context->used > context->allocated) {
569                 context->buffer = g_realloc (context->buffer, context->pos + size);
570                 context->allocated = context->used;
571         }
572
573         /* Now copy the data */
574         memcpy (context->buffer + context->pos, buf, size);
575
576         /* Update pos */
577         context->pos += size;
578
579         return size;
580 }
581
582 static toff_t
583 tiff_save_seek (thandle_t handle, toff_t offset, int whence)
584 {
585         TiffSaveContext *context = (TiffSaveContext *)handle;
586
587         switch (whence) {
588         case SEEK_SET:
589                 context->pos = offset;
590                 break;
591         case SEEK_CUR:
592                 context->pos += offset;
593                 break;
594         case SEEK_END:
595                 context->pos = context->used + offset;
596                 break;
597         default:
598                 return -1;
599         }
600         return context->pos;
601 }
602
603 static int
604 tiff_save_close (thandle_t context)
605 {
606         return 0;
607 }
608
609 static toff_t
610 tiff_save_size (thandle_t handle)
611 {
612         return -1;
613 }
614
615 static TiffSaveContext *
616 create_save_context (void)
617 {
618         TiffSaveContext *context;
619
620         context = g_new (TiffSaveContext, 1);
621         context->buffer = NULL;
622         context->allocated = 0;
623         context->used = 0;
624         context->pos = 0;
625
626         return context;
627 }
628
629 static void
630 free_save_context (TiffSaveContext *context)
631 {
632         g_free (context->buffer);
633         g_free (context);
634 }
635
636 static gboolean
637 gdk_pixbuf__tiff_image_save_to_callback (GdkPixbufSaveFunc   save_func,
638                                          gpointer            user_data,
639                                          GdkPixbuf          *pixbuf, 
640                                          gchar             **keys,
641                                          gchar             **values,
642                                          GError            **error)
643 {
644         TIFF *tiff;
645         gint width, height, rowstride;
646         guchar *pixels;
647         gboolean has_alpha;
648         gushort alpha_samples[1] = { EXTRASAMPLE_UNASSALPHA };
649         int y;
650         TiffSaveContext *context;
651         gboolean retval;
652
653         tiff_push_handlers ();
654
655         context = create_save_context ();
656         tiff = TIFFClientOpen ("libtiff-pixbuf", "w", context,  
657                                tiff_save_read, tiff_save_write, 
658                                tiff_save_seek, tiff_save_close, 
659                                tiff_save_size, 
660                                NULL, NULL);
661
662         if (!tiff || global_error) {
663                 tiff_set_error (error,
664                                 GDK_PIXBUF_ERROR_FAILED,
665                                 _("Failed to save TIFF image"));
666
667                 tiff_pop_handlers ();
668
669                 free_save_context (context);
670                 return FALSE;
671         }
672
673         rowstride = gdk_pixbuf_get_rowstride (pixbuf);
674         pixels = gdk_pixbuf_get_pixels (pixbuf);
675
676         has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
677
678         height = gdk_pixbuf_get_height (pixbuf);
679         width = gdk_pixbuf_get_width (pixbuf);
680
681         TIFFSetField (tiff, TIFFTAG_IMAGEWIDTH, width);
682         TIFFSetField (tiff, TIFFTAG_IMAGELENGTH, height);
683         TIFFSetField (tiff, TIFFTAG_BITSPERSAMPLE, 8);
684         TIFFSetField (tiff, TIFFTAG_SAMPLESPERPIXEL, has_alpha ? 4 : 3);
685         TIFFSetField (tiff, TIFFTAG_ROWSPERSTRIP, height);
686
687         if (has_alpha)
688                 TIFFSetField (tiff, TIFFTAG_EXTRASAMPLES, 1, alpha_samples);
689
690         TIFFSetField (tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
691         TIFFSetField (tiff, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB);        
692         TIFFSetField (tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
693
694         for (y = 0; y < height; y++) {
695                 if (TIFFWriteScanline (tiff, pixels + y * rowstride, y, 0) == -1 ||
696                     global_error)
697                         break;
698         }
699
700         if (global_error) {
701                 tiff_set_error (error,
702                                 GDK_PIXBUF_ERROR_FAILED,
703                                 _("Failed to write TIFF data"));
704
705                 TIFFClose (tiff);
706
707                 free_save_context (context);               
708                 tiff_pop_handlers ();
709                 
710                 return FALSE;
711         }
712
713         TIFFClose (tiff);
714         if (global_error) {
715                 tiff_set_error (error,
716                                 GDK_PIXBUF_ERROR_FAILED,
717                                 _("TIFFClose operation failed"));
718
719                 free_save_context (context);               
720                 tiff_pop_handlers ();
721                 
722                 return FALSE;
723         }
724
725         tiff_pop_handlers ();
726
727         /* Now call the callback */
728         retval = save_func (context->buffer, context->used, error, user_data);
729
730         free_save_context (context);               
731         
732         return retval;
733 }
734
735 static gboolean
736 save_to_file_cb (const gchar *buf,
737                  gsize count,
738                  GError **error,
739                  gpointer data)
740 {
741         gint bytes;
742         
743         while (count > 0) {
744                 bytes = fwrite (buf, sizeof (gchar), count, (FILE *) data);
745                 if (bytes <= 0)
746                         break;
747                 count -= bytes;
748                 buf += bytes;
749         }
750
751         if (count) {
752                 g_set_error (error,
753                              GDK_PIXBUF_ERROR,
754                              GDK_PIXBUF_ERROR_FAILED,
755                              _("Couldn't write to TIFF file"));
756                 return FALSE;
757         }
758         
759         return TRUE;
760 }
761
762 static gboolean
763 gdk_pixbuf__tiff_image_save (FILE          *f, 
764                              GdkPixbuf     *pixbuf, 
765                              gchar        **keys,
766                              gchar        **values,
767                              GError       **error)
768 {
769         return gdk_pixbuf__tiff_image_save_to_callback (save_to_file_cb,
770                                                         f, pixbuf, keys,
771                                                         values, error);
772 }
773
774 #ifndef INCLUDE_tiff
775 #define MODULE_ENTRY(type,function) function
776 #else
777 #define MODULE_ENTRY(type,function) _gdk_pixbuf__ ## type ## _ ## function
778 #endif
779
780 void
781 MODULE_ENTRY (tiff, fill_vtable) (GdkPixbufModule *module)
782 {
783         module->load = gdk_pixbuf__tiff_image_load;
784         module->begin_load = gdk_pixbuf__tiff_image_begin_load;
785         module->stop_load = gdk_pixbuf__tiff_image_stop_load;
786         module->load_increment = gdk_pixbuf__tiff_image_load_increment;
787         module->save = gdk_pixbuf__tiff_image_save;
788         module->save_to_callback = gdk_pixbuf__tiff_image_save_to_callback;
789 }
790
791 void
792 MODULE_ENTRY (tiff, fill_info) (GdkPixbufFormat *info)
793 {
794         static GdkPixbufModulePattern signature[] = {
795                 { "MM \x2a", "  z ", 100 },
796                 { "II\x2a ", "   z", 100 },
797                 { NULL, NULL, 0 }
798         };
799         static gchar * mime_types[] = {
800                 "image/tiff",
801                 NULL
802         };
803         static gchar * extensions[] = {
804                 "tiff",
805                 "tif",
806                 NULL
807         };
808
809         info->name = "tiff";
810         info->signature = signature;
811         info->description = N_("The TIFF image format");
812         info->mime_types = mime_types;
813         info->extensions = extensions;
814         /* not threadsafe, due to the error handler handling */
815         info->flags = GDK_PIXBUF_FORMAT_WRITABLE;
816         info->license = "LGPL";
817 }