]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/io-tiff.c
Always set an error when returning NULL. (453365, Michael Chudobiak)
[~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                     g_set_error (error,
204                                  GDK_PIXBUF_ERROR,
205                                  GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
206                                  _("Width or height of TIFF image is zero"));
207                     return NULL;
208                 }
209         }
210
211         pixels = g_try_malloc (bytes);
212
213         if (!pixels) {
214                 g_set_error (error,
215                              GDK_PIXBUF_ERROR,
216                              GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
217                              _("Insufficient memory to open TIFF file"));
218                 return NULL;
219         }
220
221         pixbuf = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, TRUE, 8, 
222                                            width, height, rowstride,
223                                            free_buffer, NULL);
224         if (!pixbuf) {
225                 g_free (pixels);
226                 g_set_error (error,
227                              GDK_PIXBUF_ERROR,
228                              GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
229                              _("Insufficient memory to open TIFF file"));
230                 return NULL;
231         }
232
233         /* Set the "orientation" key associated with this image. libtiff 
234            orientation handling is odd, so further processing is required
235            by higher-level functions based on this tag. If the embedded
236            orientation tag is 1-4, libtiff flips/mirrors the image as
237            required, and no client processing is required - so we report 
238            no orientation. Orientations 5-8 require rotations which would 
239            swap the width and height of the image. libtiff does not do this. 
240            Instead it interprets orientations 5-8 the same as 1-4. 
241            See http://bugzilla.remotesensing.org/show_bug.cgi?id=1548.
242            To correct for this, the client must apply the transform normally
243            used for orientation 5 to both orientations 5 and 7, and apply
244            the transform normally used for orientation 7 for both
245            orientations 6 and 8. Then everythings works out OK! */
246         
247         TIFFGetField (tiff, TIFFTAG_ORIENTATION, &orientation);
248
249         switch (orientation) {
250                 case 5:
251                 case 7:
252                         transform = 5;
253                         break;
254                 case 6:
255                 case 8:
256                         transform = 7;
257                         break;
258                 default:
259                         transform = 0;
260                         break;
261         }
262
263         if (transform > 0 ) {
264                 gchar str[5];
265                 snprintf (str, sizeof (str), "%d", transform);
266                 gdk_pixbuf_set_option (pixbuf, "orientation", str);
267         }
268
269         if (context && context->prepare_func)
270                 (* context->prepare_func) (pixbuf, NULL, context->user_data);
271
272         if (!TIFFReadRGBAImageOriented (tiff, width, height, (uint32 *)pixels, ORIENTATION_TOPLEFT, 1) || global_error) {
273                 tiff_set_error (error,
274                                 GDK_PIXBUF_ERROR_FAILED,
275                                 _("Failed to load RGB data from TIFF file"));
276                 g_object_unref (pixbuf);
277                 return NULL;
278         }
279
280 #if G_BYTE_ORDER == G_BIG_ENDIAN
281         /* Turns out that the packing used by TIFFRGBAImage depends on 
282          * the host byte order... 
283          */ 
284         while (pixels < pixbuf->pixels + bytes) {
285                 uint32 pixel = *(uint32 *)pixels;
286                 int r = TIFFGetR(pixel);
287                 int g = TIFFGetG(pixel);
288                 int b = TIFFGetB(pixel);
289                 int a = TIFFGetA(pixel);
290                 *pixels++ = r;
291                 *pixels++ = g;
292                 *pixels++ = b;
293                 *pixels++ = a;
294         }
295 #endif
296
297         if (context && context->update_func)
298                 (* context->update_func) (pixbuf, 0, 0, width, height, context->user_data);
299
300         return pixbuf;
301 }
302
303 \f
304
305 /* Static loader */
306
307 static GdkPixbuf *
308 gdk_pixbuf__tiff_image_load (FILE *f, GError **error)
309 {
310         TIFF *tiff;
311         int fd;
312         GdkPixbuf *pixbuf;
313         
314         g_return_val_if_fail (f != NULL, NULL);
315
316         tiff_push_handlers ();
317         
318         fd = fileno (f);
319
320         /* On OSF, apparently fseek() works in some on-demand way, so
321          * the fseek gdk_pixbuf_new_from_file() doesn't work here
322          * since we are using the raw file descriptor. So, we call lseek() on the fd
323          * before using it. (#60840)
324          */
325         lseek (fd, 0, SEEK_SET);
326         tiff = TIFFFdOpen (fd, "libpixbuf-tiff", "r");
327         
328         if (!tiff || global_error) {
329                 tiff_set_error (error,
330                                 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
331                                 _("Failed to open TIFF image"));
332                 tiff_pop_handlers ();
333
334                 return NULL;
335         }
336
337         pixbuf = tiff_image_parse (tiff, NULL, error);
338         
339         TIFFClose (tiff);
340         if (global_error) {
341                 tiff_set_error (error,
342                                 GDK_PIXBUF_ERROR_FAILED,
343                                 _("TIFFClose operation failed"));
344         }
345         
346         tiff_pop_handlers ();
347
348         return pixbuf;
349 }
350
351 \f
352
353 /* Progressive loader */
354
355 static gpointer
356 gdk_pixbuf__tiff_image_begin_load (GdkPixbufModuleSizeFunc size_func,
357                                    GdkPixbufModulePreparedFunc prepare_func,
358                                    GdkPixbufModuleUpdatedFunc update_func,
359                                    gpointer user_data,
360                                    GError **error)
361 {
362         TiffContext *context;
363         
364         context = g_new0 (TiffContext, 1);
365         context->size_func = size_func;
366         context->prepare_func = prepare_func;
367         context->update_func = update_func;
368         context->user_data = user_data;
369         context->buffer = NULL;
370         context->allocated = 0;
371         context->used = 0;
372         context->pos = 0;
373         
374         return context;
375 }
376
377 static tsize_t
378 tiff_load_read (thandle_t handle, tdata_t buf, tsize_t size)
379 {
380         TiffContext *context = (TiffContext *)handle;
381         
382         if (context->pos + size > context->used)
383                 return 0;
384         
385         memcpy (buf, context->buffer + context->pos, size);
386         context->pos += size;
387         return size;
388 }
389
390 static tsize_t
391 tiff_load_write (thandle_t handle, tdata_t buf, tsize_t size)
392 {
393         return -1;
394 }
395
396 static toff_t
397 tiff_load_seek (thandle_t handle, toff_t offset, int whence)
398 {
399         TiffContext *context = (TiffContext *)handle;
400         
401         switch (whence) {
402         case SEEK_SET:
403                 if (offset > context->used)
404                         return -1;
405                 context->pos = offset;
406                 break;
407         case SEEK_CUR:
408                 if (offset + context->pos >= context->used)
409                         return -1;
410                 context->pos += offset;
411                 break;
412         case SEEK_END:
413                 if (offset + context->used > context->used)
414                         return -1;
415                 context->pos = context->used + offset;
416                 break;
417         default:
418                 return -1;
419         }
420         return context->pos;
421 }
422
423 static int
424 tiff_load_close (thandle_t context)
425 {
426         return 0;
427 }
428
429 static toff_t
430 tiff_load_size (thandle_t handle)
431 {
432         TiffContext *context = (TiffContext *)handle;
433         
434         return context->used;
435 }
436
437 static int
438 tiff_load_map_file (thandle_t handle, tdata_t *buf, toff_t *size)
439 {
440         TiffContext *context = (TiffContext *)handle;
441         
442         *buf = context->buffer;
443         *size = context->used;
444         
445         return 0;
446 }
447
448 static void
449 tiff_load_unmap_file (thandle_t handle, tdata_t data, toff_t offset)
450 {
451 }
452
453 static gboolean
454 gdk_pixbuf__tiff_image_stop_load (gpointer data,
455                                   GError **error)
456 {
457         TiffContext *context = data;
458         TIFF *tiff;
459         gboolean retval;
460         
461         g_return_val_if_fail (data != NULL, FALSE);
462
463         tiff_push_handlers ();
464         
465         tiff = TIFFClientOpen ("libtiff-pixbuf", "r", data, 
466                                tiff_load_read, tiff_load_write, 
467                                tiff_load_seek, tiff_load_close, 
468                                tiff_load_size, 
469                                tiff_load_map_file, tiff_load_unmap_file);
470         if (!tiff || global_error) {
471                 tiff_set_error (error,
472                                 GDK_PIXBUF_ERROR_FAILED,
473                                 _("Failed to load TIFF image"));
474                 retval = FALSE;
475         } else {
476                 GdkPixbuf *pixbuf;
477                 
478                 pixbuf = tiff_image_parse (tiff, context, error);
479                 if (pixbuf)
480                         g_object_unref (pixbuf);
481                 retval = pixbuf != NULL;
482                 if (global_error)
483                         {
484                                 tiff_set_error (error,
485                                                 GDK_PIXBUF_ERROR_FAILED,
486                                                 _("Failed to load TIFF image"));
487                                 tiff_pop_handlers ();
488
489                                 retval = FALSE;
490                         }
491         }
492
493         if (tiff)
494                 TIFFClose (tiff);
495
496         g_assert (!global_error);
497         
498         g_free (context->buffer);
499         g_free (context);
500
501         tiff_pop_handlers ();
502
503         return retval;
504 }
505
506 static gboolean
507 make_available_at_least (TiffContext *context, guint needed)
508 {
509         guchar *new_buffer = NULL;
510         guint need_alloc;
511         
512         need_alloc = context->used + needed;
513         if (need_alloc > context->allocated) {
514                 guint new_size = 1;
515                 while (new_size < need_alloc)
516                         new_size *= 2;
517                 
518                 new_buffer = g_try_realloc (context->buffer, new_size);
519                 if (new_buffer) {
520                         context->buffer = new_buffer;
521                         context->allocated = new_size;
522                         return TRUE;
523                 }
524                 return FALSE;
525         }
526         return TRUE;
527 }
528
529 static gboolean
530 gdk_pixbuf__tiff_image_load_increment (gpointer data, const guchar *buf,
531                                        guint size, GError **error)
532 {
533         TiffContext *context = (TiffContext *) data;
534         
535         g_return_val_if_fail (data != NULL, FALSE);
536         
537         if (!make_available_at_least (context, size)) {
538                 g_set_error (error,
539                              GDK_PIXBUF_ERROR,
540                              GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
541                              _("Insufficient memory to open TIFF file"));
542                 return FALSE;
543         }
544         
545         memcpy (context->buffer + context->used, buf, size);
546         context->used += size;
547         return TRUE;
548 }
549
550 typedef struct {
551         gchar *buffer;
552         guint allocated;
553         guint used;
554         guint pos;
555 } TiffSaveContext;
556
557 static tsize_t
558 tiff_save_read (thandle_t handle, tdata_t buf, tsize_t size)
559 {
560         return -1;
561 }
562
563 static tsize_t
564 tiff_save_write (thandle_t handle, tdata_t buf, tsize_t size)
565 {
566         TiffSaveContext *context = (TiffSaveContext *)handle;
567
568         /* Modify buffer length */
569         if (context->pos + size > context->used)
570                 context->used = context->pos + size;
571
572         /* Realloc */
573         if (context->used > context->allocated) {
574                 context->buffer = g_realloc (context->buffer, context->pos + size);
575                 context->allocated = context->used;
576         }
577
578         /* Now copy the data */
579         memcpy (context->buffer + context->pos, buf, size);
580
581         /* Update pos */
582         context->pos += size;
583
584         return size;
585 }
586
587 static toff_t
588 tiff_save_seek (thandle_t handle, toff_t offset, int whence)
589 {
590         TiffSaveContext *context = (TiffSaveContext *)handle;
591
592         switch (whence) {
593         case SEEK_SET:
594                 context->pos = offset;
595                 break;
596         case SEEK_CUR:
597                 context->pos += offset;
598                 break;
599         case SEEK_END:
600                 context->pos = context->used + offset;
601                 break;
602         default:
603                 return -1;
604         }
605         return context->pos;
606 }
607
608 static int
609 tiff_save_close (thandle_t context)
610 {
611         return 0;
612 }
613
614 static toff_t
615 tiff_save_size (thandle_t handle)
616 {
617         return -1;
618 }
619
620 static TiffSaveContext *
621 create_save_context (void)
622 {
623         TiffSaveContext *context;
624
625         context = g_new (TiffSaveContext, 1);
626         context->buffer = NULL;
627         context->allocated = 0;
628         context->used = 0;
629         context->pos = 0;
630
631         return context;
632 }
633
634 static void
635 free_save_context (TiffSaveContext *context)
636 {
637         g_free (context->buffer);
638         g_free (context);
639 }
640
641 static gboolean
642 gdk_pixbuf__tiff_image_save_to_callback (GdkPixbufSaveFunc   save_func,
643                                          gpointer            user_data,
644                                          GdkPixbuf          *pixbuf, 
645                                          gchar             **keys,
646                                          gchar             **values,
647                                          GError            **error)
648 {
649         TIFF *tiff;
650         gint width, height, rowstride;
651         guchar *pixels;
652         gboolean has_alpha;
653         gushort alpha_samples[1] = { EXTRASAMPLE_UNASSALPHA };
654         int y;
655         TiffSaveContext *context;
656         gboolean retval;
657
658         tiff_push_handlers ();
659
660         context = create_save_context ();
661         tiff = TIFFClientOpen ("libtiff-pixbuf", "w", context,  
662                                tiff_save_read, tiff_save_write, 
663                                tiff_save_seek, tiff_save_close, 
664                                tiff_save_size, 
665                                NULL, NULL);
666
667         if (!tiff || global_error) {
668                 tiff_set_error (error,
669                                 GDK_PIXBUF_ERROR_FAILED,
670                                 _("Failed to save TIFF image"));
671
672                 tiff_pop_handlers ();
673
674                 free_save_context (context);
675                 return FALSE;
676         }
677
678         rowstride = gdk_pixbuf_get_rowstride (pixbuf);
679         pixels = gdk_pixbuf_get_pixels (pixbuf);
680
681         has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
682
683         height = gdk_pixbuf_get_height (pixbuf);
684         width = gdk_pixbuf_get_width (pixbuf);
685
686         TIFFSetField (tiff, TIFFTAG_IMAGEWIDTH, width);
687         TIFFSetField (tiff, TIFFTAG_IMAGELENGTH, height);
688         TIFFSetField (tiff, TIFFTAG_BITSPERSAMPLE, 8);
689         TIFFSetField (tiff, TIFFTAG_SAMPLESPERPIXEL, has_alpha ? 4 : 3);
690         TIFFSetField (tiff, TIFFTAG_ROWSPERSTRIP, height);
691
692         if (has_alpha)
693                 TIFFSetField (tiff, TIFFTAG_EXTRASAMPLES, 1, alpha_samples);
694
695         TIFFSetField (tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
696         TIFFSetField (tiff, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB);        
697         TIFFSetField (tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
698
699         for (y = 0; y < height; y++) {
700                 if (TIFFWriteScanline (tiff, pixels + y * rowstride, y, 0) == -1 ||
701                     global_error)
702                         break;
703         }
704
705         if (global_error) {
706                 tiff_set_error (error,
707                                 GDK_PIXBUF_ERROR_FAILED,
708                                 _("Failed to write TIFF data"));
709
710                 TIFFClose (tiff);
711
712                 free_save_context (context);               
713                 tiff_pop_handlers ();
714                 
715                 return FALSE;
716         }
717
718         TIFFClose (tiff);
719         if (global_error) {
720                 tiff_set_error (error,
721                                 GDK_PIXBUF_ERROR_FAILED,
722                                 _("TIFFClose operation failed"));
723
724                 free_save_context (context);               
725                 tiff_pop_handlers ();
726                 
727                 return FALSE;
728         }
729
730         tiff_pop_handlers ();
731
732         /* Now call the callback */
733         retval = save_func (context->buffer, context->used, error, user_data);
734
735         free_save_context (context);               
736         
737         return retval;
738 }
739
740 static gboolean
741 save_to_file_cb (const gchar *buf,
742                  gsize count,
743                  GError **error,
744                  gpointer data)
745 {
746         gint bytes;
747         
748         while (count > 0) {
749                 bytes = fwrite (buf, sizeof (gchar), count, (FILE *) data);
750                 if (bytes <= 0)
751                         break;
752                 count -= bytes;
753                 buf += bytes;
754         }
755
756         if (count) {
757                 g_set_error (error,
758                              GDK_PIXBUF_ERROR,
759                              GDK_PIXBUF_ERROR_FAILED,
760                              _("Couldn't write to TIFF file"));
761                 return FALSE;
762         }
763         
764         return TRUE;
765 }
766
767 static gboolean
768 gdk_pixbuf__tiff_image_save (FILE          *f, 
769                              GdkPixbuf     *pixbuf, 
770                              gchar        **keys,
771                              gchar        **values,
772                              GError       **error)
773 {
774         return gdk_pixbuf__tiff_image_save_to_callback (save_to_file_cb,
775                                                         f, pixbuf, keys,
776                                                         values, error);
777 }
778
779 #ifndef INCLUDE_tiff
780 #define MODULE_ENTRY(type,function) function
781 #else
782 #define MODULE_ENTRY(type,function) _gdk_pixbuf__ ## type ## _ ## function
783 #endif
784
785 void
786 MODULE_ENTRY (tiff, fill_vtable) (GdkPixbufModule *module)
787 {
788         module->load = gdk_pixbuf__tiff_image_load;
789         module->begin_load = gdk_pixbuf__tiff_image_begin_load;
790         module->stop_load = gdk_pixbuf__tiff_image_stop_load;
791         module->load_increment = gdk_pixbuf__tiff_image_load_increment;
792         module->save = gdk_pixbuf__tiff_image_save;
793         module->save_to_callback = gdk_pixbuf__tiff_image_save_to_callback;
794 }
795
796 void
797 MODULE_ENTRY (tiff, fill_info) (GdkPixbufFormat *info)
798 {
799         static GdkPixbufModulePattern signature[] = {
800                 { "MM \x2a", "  z ", 100 },
801                 { "II\x2a ", "   z", 100 },
802                 { NULL, NULL, 0 }
803         };
804         static gchar * mime_types[] = {
805                 "image/tiff",
806                 NULL
807         };
808         static gchar * extensions[] = {
809                 "tiff",
810                 "tif",
811                 NULL
812         };
813
814         info->name = "tiff";
815         info->signature = signature;
816         info->description = N_("The TIFF image format");
817         info->mime_types = mime_types;
818         info->extensions = extensions;
819         /* not threadsafe, due to the error handler handling */
820         info->flags = GDK_PIXBUF_FORMAT_WRITABLE;
821         info->license = "LGPL";
822 }