]> Pileus Git - ~andy/gtk/blobdiff - gdk-pixbuf/io-tga.c
Always check for NULL when using callbacks. (#330563, Benjamin Otte)
[~andy/gtk] / gdk-pixbuf / io-tga.c
index ff453e528f665f26af8e6d4f743e21a15937546a..2cde75f767cc38c867cd0e9b0d57bc0d3a9e9b5c 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: C; c-file-style: "linux" -*- */
 /* 
  * GdkPixbuf library - TGA image loader
  * Copyright (C) 1999 Nicola Girardi <nikke@swlibero.org>
 /*
  * Some NOTES about the TGA loader (2001/06/07, nikke@swlibero.org)
  *
- * - The module doesn't currently provide support for TGA images where the
- *   order of the pixels isn't left-to-right and top-to-bottom.  I plan to
- *   add support for those files as soon as I get one of them.  I haven't
- *   run into one yet.  (And I don't seem to be able to create it with GIMP.)
- *
  * - The TGAFooter isn't present in all TGA files.  In fact, there's an older
  *   format specification, still in use, which doesn't cover the TGAFooter.
  *   Actually, most TGA files I have are of the older type.  Anyway I put the 
  * - Error handling was designed to be very paranoid.
  */
 
+#include <config.h>
 #include <stdio.h>
 #include <string.h>
 
-#include "gdk-pixbuf.h"
-#include "gdk-pixbuf-io.h"
 #include "gdk-pixbuf-private.h"
+#include "gdk-pixbuf-io.h"
 
 #undef DEBUG_TGA
 
@@ -50,9 +46,7 @@
 #define TGA_INTERLEAVE_4WAY     0x80
 
 #define TGA_ORIGIN_MASK         0x30
-#define TGA_ORIGIN_LEFT         0x00
 #define TGA_ORIGIN_RIGHT        0x10
-#define TGA_ORIGIN_LOWER        0x00
 #define TGA_ORIGIN_UPPER        0x20
 
 enum {
@@ -139,8 +133,9 @@ struct _TGAContext {
        gboolean prepared;
        gboolean done;
 
-       ModulePreparedNotifyFunc pfunc;
-       ModuleUpdatedNotifyFunc ufunc;
+       GdkPixbufModuleSizeFunc sfunc;
+       GdkPixbufModulePreparedFunc pfunc;
+       GdkPixbufModuleUpdatedFunc ufunc;
        gpointer udata;
 };
 
@@ -156,7 +151,7 @@ static IOBuffer *io_buffer_new(GError **err)
        if (!buffer) {
                g_set_error(err, GDK_PIXBUF_ERROR,
                            GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
-                           _("Can't allocate memory for IOBuffer struct"));
+                           _("Cannot allocate memory for IOBuffer struct"));
                return NULL;
        }
        buffer->data = NULL;
@@ -175,7 +170,7 @@ static IOBuffer *io_buffer_append(IOBuffer *buffer,
                if (!buffer->data) {
                        g_set_error(err, GDK_PIXBUF_ERROR,
                                    GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
-                                   _("Can't allocate memory for IOBuffer data"));
+                                   _("Cannot allocate memory for IOBuffer data"));
                        g_free(buffer);
                        return NULL;
                }
@@ -186,7 +181,7 @@ static IOBuffer *io_buffer_append(IOBuffer *buffer,
                if (!tmp) {
                        g_set_error(err, GDK_PIXBUF_ERROR,
                                    GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
-                                   _("Can't realloc IOBuffer data"));
+                                   _("Cannot realloc IOBuffer data"));
                        g_free(buffer);
                        return NULL;
                }
@@ -216,7 +211,7 @@ static IOBuffer *io_buffer_free_segment(IOBuffer *buffer,
                if (!new_buf) {
                        g_set_error(err, GDK_PIXBUF_ERROR,
                                    GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
-                                   _("Can't allocate temporary IOBuffer data"));
+                                   _("Cannot allocate temporary IOBuffer data"));
                        g_free(buffer->data);
                        g_free(buffer);
                        return NULL;
@@ -274,26 +269,43 @@ static GdkPixbuf *get_contiguous_pixbuf (guint width,
                                         width, height, rowstride, free_buffer, NULL);
 }
 
-static gboolean fread_check(gpointer dest, 
-                           size_t size, size_t count, 
-                           FILE *f, GError **err)
+static void pixbuf_flip_row (GdkPixbuf *pixbuf, guchar *ph)
 {
-       if (fread(dest, size, count, f) != count) {
-               g_set_error(err, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED,
-                           _("fread() failed -- premature end-of-file probably encountered"));
-               return FALSE;
-       }
-       return TRUE;
+       guchar *p, *s;
+       guchar tmp;
+       gint count;
+
+       p = ph;
+       s = p + pixbuf->n_channels * (pixbuf->width - 1);
+       while (p < s) {
+               for (count = pixbuf->n_channels; count > 0; count--, p++, s++) {
+                       tmp = *p;
+                       *p = *s;
+                       *s = tmp;
+               }
+               s -= 2 * pixbuf->n_channels;
+       }               
 }
 
-static gboolean fseek_check(FILE *f, glong offset, gint whence, GError **err)
+static void pixbuf_flip_vertically (GdkPixbuf *pixbuf)
 {
-       if (fseek(f, offset, whence) != 0) {
-               g_set_error(err, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED,
-                           _("fseek() failed -- premature end-of-file probably encountered"));
-               return FALSE;
+       guchar *ph, *sh, *p, *s;
+       guchar tmp;
+       gint count;
+
+       ph = pixbuf->pixels;
+       sh = pixbuf->pixels + pixbuf->height*pixbuf->rowstride;
+       while (ph < sh - pixbuf->rowstride) {
+               p = ph;
+               s = sh - pixbuf->rowstride;
+               for (count = pixbuf->n_channels * pixbuf->width; count > 0; count--, p++, s++) {
+                       tmp = *p;
+                       *p = *s;
+                       *s = tmp;
+               }
+               sh -= pixbuf->rowstride;
+               ph += pixbuf->rowstride;
        }
-       return TRUE;
 }
 
 static gboolean fill_in_context(TGAContext *ctx, GError **err)
@@ -319,16 +331,29 @@ static gboolean fill_in_context(TGAContext *ctx, GError **err)
        w = LE16(ctx->hdr->width);
        h = LE16(ctx->hdr->height);
 
+       if (ctx->sfunc) {
+               gint wi = w;
+               gint hi = h;
+               
+               (*ctx->sfunc) (&wi, &hi, ctx->udata);
+               
+               if (wi == 0 || hi == 0) 
+                       return FALSE;
+       }
+
        ctx->pbuf = get_contiguous_pixbuf (w, h, alpha);
 
        if (!ctx->pbuf) {
                g_set_error(err, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
-                           _("Can't allocate new pixbuf"));
+                           _("Cannot allocate new pixbuf"));
                return FALSE;
        }
 
        ctx->pbuf_bytes = ctx->pbuf->rowstride * ctx->pbuf->height;
-       ctx->pptr = ctx->pbuf->pixels;
+       if (ctx->hdr->flags & TGA_ORIGIN_UPPER || ctx->run_length_encoded)
+               ctx->pptr = ctx->pbuf->pixels;
+       else
+               ctx->pptr = ctx->pbuf->pixels + (ctx->pbuf->height - 1)*ctx->pbuf->rowstride;
 
        if (ctx->hdr->type == TGA_TYPE_PSEUDOCOLOR)
          ctx->rowstride = ctx->pbuf->width;
@@ -354,10 +379,6 @@ static void parse_data_for_row_pseudocolor(TGAContext *ctx)
                if (ctx->hdr->cmap_bpp == 32)
                        *p++ = ctx->cmap->cols[*s].a;
        }
-       ctx->pptr += ctx->pbuf->rowstride;
-       ctx->pbuf_bytes_done += ctx->pbuf->rowstride;
-       if (ctx->pbuf_bytes_done == ctx->pbuf_bytes)
-               ctx->done = TRUE;
 }
 
 static void swap_channels(TGAContext *ctx)
@@ -377,10 +398,6 @@ static void parse_data_for_row_truecolor(TGAContext *ctx)
 {
        g_memmove(ctx->pptr, ctx->in->data, ctx->pbuf->rowstride);
        swap_channels(ctx);
-       ctx->pptr += ctx->pbuf->rowstride;
-       ctx->pbuf_bytes_done += ctx->pbuf->rowstride;
-       if (ctx->pbuf_bytes_done == ctx->pbuf_bytes)
-               ctx->done = TRUE;
 }
 
 static void parse_data_for_row_grayscale(TGAContext *ctx)
@@ -395,26 +412,35 @@ static void parse_data_for_row_grayscale(TGAContext *ctx)
                  p[3] = *s++;
                p += ctx->pbuf->n_channels;
        }
-       ctx->pptr += ctx->pbuf->rowstride;
-       ctx->pbuf_bytes_done += ctx->pbuf->rowstride;
-       if (ctx->pbuf_bytes_done == ctx->pbuf_bytes)
-               ctx->done = TRUE;
 }
 
 static gboolean parse_data_for_row(TGAContext *ctx, GError **err)
 {
+       guint row;
+
        if (ctx->hdr->type == TGA_TYPE_PSEUDOCOLOR)
                parse_data_for_row_pseudocolor(ctx);
        else if (ctx->hdr->type == TGA_TYPE_TRUECOLOR)
                parse_data_for_row_truecolor(ctx);
        else if (ctx->hdr->type == TGA_TYPE_GRAYSCALE)
                parse_data_for_row_grayscale(ctx);
+
+       if (ctx->hdr->flags & TGA_ORIGIN_RIGHT)
+               pixbuf_flip_row (ctx->pbuf, ctx->pptr);
+       if (ctx->hdr->flags & TGA_ORIGIN_UPPER)
+               ctx->pptr += ctx->pbuf->rowstride;
+       else
+               ctx->pptr -= ctx->pbuf->rowstride;
+       ctx->pbuf_bytes_done += ctx->pbuf->rowstride;
+       if (ctx->pbuf_bytes_done == ctx->pbuf_bytes)
+               ctx->done = TRUE;
+       
        ctx->in = io_buffer_free_segment(ctx->in, ctx->rowstride, err);
        if (!ctx->in)
                return FALSE;
-       (*ctx->ufunc) (ctx->pbuf, 0,
-                      (ctx->pbuf_bytes_done / ctx->pbuf->rowstride) - 1,
-                      ctx->pbuf->width, 1, ctx->udata);
+       row = (ctx->pptr - ctx->pbuf->pixels) / ctx->pbuf->rowstride - 1;
+       if (ctx->ufunc)
+               (*ctx->ufunc) (ctx->pbuf, 0, row, ctx->pbuf->width, 1, ctx->udata);
        return TRUE;
 }
 
@@ -604,24 +630,44 @@ static guint parse_rle_data_grayscale(TGAContext *ctx)
 
 static gboolean parse_rle_data(TGAContext *ctx, GError **err)
 {
+       guint rows = 0;
        guint count = 0;
-       guint pbuf_count = 0;
-       if (ctx->hdr->type == TGA_TYPE_RLE_PSEUDOCOLOR) {
+       guint bytes_done_before = ctx->pbuf_bytes_done;
+
+       if (ctx->hdr->type == TGA_TYPE_RLE_PSEUDOCOLOR)
                count = parse_rle_data_pseudocolor(ctx);
-               pbuf_count = count * ctx->pbuf->n_channels;
-       } else if (ctx->hdr->type == TGA_TYPE_RLE_TRUECOLOR) {
+       else if (ctx->hdr->type == TGA_TYPE_RLE_TRUECOLOR)
                count = parse_rle_data_truecolor(ctx);
-               pbuf_count = count;
-       } else if (ctx->hdr->type == TGA_TYPE_RLE_GRAYSCALE) {
+       else if (ctx->hdr->type == TGA_TYPE_RLE_GRAYSCALE)
                count = parse_rle_data_grayscale(ctx);
-               pbuf_count = count * (ctx->pbuf->n_channels == 4 ? 2 : 3);
+
+       if (ctx->hdr->flags & TGA_ORIGIN_RIGHT) {
+               guchar *row = ctx->pbuf->pixels + (bytes_done_before / ctx->pbuf->rowstride) * ctx->pbuf->rowstride;
+               guchar *row_after = ctx->pbuf->pixels + (ctx->pbuf_bytes_done / ctx->pbuf->rowstride) * ctx->pbuf->rowstride;
+               for (; row < row_after; row += ctx->pbuf->rowstride)
+                       pixbuf_flip_row (ctx->pbuf, row);
        }
+
        ctx->in = io_buffer_free_segment(ctx->in, count, err);
        if (!ctx->in)
                return FALSE;
-       (*ctx->ufunc) (ctx->pbuf, 0, ctx->pbuf_bytes_done / ctx->pbuf->rowstride,
-                      ctx->pbuf->width, pbuf_count / ctx->pbuf->rowstride,
-                      ctx->udata);
+
+       if (ctx->done) {
+               /* FIXME doing the vertical flipping afterwards is not
+                * perfect, but doing it during the rle decoding in place
+                * is considerably more work. 
+                */
+               if (!(ctx->hdr->flags & TGA_ORIGIN_UPPER))
+                       pixbuf_flip_vertically (ctx->pbuf);
+
+       }
+               
+       rows = ctx->pbuf_bytes_done / ctx->pbuf->rowstride - bytes_done_before / ctx->pbuf->rowstride;
+       if (ctx->ufunc)
+               (*ctx->ufunc) (ctx->pbuf, 0, bytes_done_before / ctx->pbuf->rowstride,
+                              ctx->pbuf->width, rows,
+                              ctx->udata);
+
        return TRUE;
 }
 
@@ -636,14 +682,14 @@ static gboolean try_colormap(TGAContext *ctx, GError **err)
        ctx->cmap = g_try_malloc(sizeof(TGAColormap));
        if (!ctx->cmap) {
                g_set_error(err, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
-                           _("Can't allocate colormap structure"));
+                           _("Cannot allocate colormap structure"));
                return FALSE;
        }
        ctx->cmap->size = LE16(ctx->hdr->cmap_n_colors);
        ctx->cmap->cols = g_try_malloc(sizeof(TGAColor) * ctx->cmap->size);
        if (!ctx->cmap->cols) {
                g_set_error(err, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
-                           _("Can't allocate colormap entries"));
+                           _("Cannot allocate colormap entries"));
                return FALSE;
        }
 
@@ -683,7 +729,7 @@ static gboolean try_preload(TGAContext *ctx, GError **err)
                        if (!ctx->hdr) {
                                g_set_error(err, GDK_PIXBUF_ERROR,
                                            GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
-                                           _("Can't allocate TGA header memory"));
+                                           _("Cannot allocate TGA header memory"));
                                return FALSE;
                        }
                        g_memmove(ctx->hdr, ctx->in->data, sizeof(TGAHeader));
@@ -719,16 +765,7 @@ static gboolean try_preload(TGAContext *ctx, GError **err)
                                            _("TGA image has invalid dimensions"));
                                return FALSE;
                        }
-                       if (ctx->hdr->infolen > 255) {
-                               g_set_error(err, GDK_PIXBUF_ERROR,
-                                           GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
-                                           _("TGA image comment length is too long"));
-                               return FALSE;
-                       }
-                       if ((ctx->hdr->flags & TGA_INTERLEAVE_MASK) != 
-                           TGA_INTERLEAVE_NONE ||
-                           ctx->hdr->flags & TGA_ORIGIN_RIGHT || 
-                           ctx->hdr->flags & TGA_ORIGIN_LOWER) {
+                       if ((ctx->hdr->flags & TGA_INTERLEAVE_MASK) != TGA_INTERLEAVE_NONE) {
                                g_set_error(err, GDK_PIXBUF_ERROR, 
                                            GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
                                            _("TGA image type not supported"));
@@ -795,16 +832,17 @@ static gboolean try_preload(TGAContext *ctx, GError **err)
                }
        }
        if (!ctx->prepared) {
-               (*ctx->pfunc) (ctx->pbuf, NULL, ctx->udata);
+               if (ctx->pfunc)
+                       (*ctx->pfunc) (ctx->pbuf, NULL, ctx->udata);
                ctx->prepared = TRUE;
        }
        /* We shouldn't get here anyway. */
        return TRUE;
 }
 
-static gpointer gdk_pixbuf__tga_begin_load(ModuleSizeFunc f0,
-                                           ModulePreparedNotifyFunc f1,
-                                          ModuleUpdatedNotifyFunc f2,
+static gpointer gdk_pixbuf__tga_begin_load(GdkPixbufModuleSizeFunc f0,
+                                           GdkPixbufModulePreparedFunc f1,
+                                          GdkPixbufModuleUpdatedFunc f2,
                                           gpointer udata, GError **err)
 {
        TGAContext *ctx;
@@ -813,7 +851,7 @@ static gpointer gdk_pixbuf__tga_begin_load(ModuleSizeFunc f0,
        if (!ctx) {
                g_set_error(err, GDK_PIXBUF_ERROR, 
                            GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
-                           _("Can't allocate memory for TGA context struct"));
+                           _("Cannot allocate memory for TGA context struct"));
                return NULL;
        }
 
@@ -839,6 +877,7 @@ static gpointer gdk_pixbuf__tga_begin_load(ModuleSizeFunc f0,
        ctx->prepared = FALSE;
        ctx->done = FALSE;
 
+       ctx->sfunc = f0;
        ctx->pfunc = f1;
        ctx->ufunc = f2;
        ctx->udata = udata;
@@ -903,7 +942,7 @@ static gboolean gdk_pixbuf__tga_stop_load(gpointer data, GError **err)
        }
        if (ctx->pbuf)
                g_object_unref (ctx->pbuf);
-       if (ctx->in->size)
+       if (ctx->in && ctx->in->size)
                ctx->in = io_buffer_free_segment (ctx->in, ctx->in->size, err);
        if (!ctx->in) {
                g_free (ctx);
@@ -914,431 +953,41 @@ static gboolean gdk_pixbuf__tga_stop_load(gpointer data, GError **err)
        return TRUE;
 }
 
-static TGAHeader *get_header_from_file(FILE *f, GError **err)
-{
-       TGAHeader *hdr;
-
-       if (!fseek_check(f, 0, SEEK_SET, err))
-               return NULL;
-       if (!(hdr = g_try_malloc(sizeof(TGAHeader)))) {
-               g_set_error(err, GDK_PIXBUF_ERROR, 
-                           GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
-                           _("Can't allocate memory for TGA header"));
-               return NULL;
-       }
-       if (!fread_check(hdr, sizeof(TGAHeader), 1, f, err)) {
-               g_free(hdr);
-               return NULL;
-       }
-       if (hdr->infolen > 255) {
-               g_set_error(err, GDK_PIXBUF_ERROR, 
-                           GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
-                           _("Too big value in the infolen field of TGA header."));
-               g_free(hdr);
-               return NULL;
-       }
-
-       return hdr;
-}
-
-static TGAColormap *get_colormap_from_file(FILE *f, 
-                                          TGAHeader *hdr,
-                                          GError **err)
-{
-       TGAColormap *cmap;
-       guchar *pal_buf, *p;
-       guint n, pal_size;
-  
-       if (!fseek_check(f, sizeof(TGAHeader) + hdr->infolen, SEEK_SET, err))
-               return NULL;
-
-       pal_size = LE16(hdr->cmap_n_colors) * ((hdr->cmap_bpp + 7) >> 3);
-       pal_buf = g_try_malloc(pal_size);
-       if (!pal_buf) {
-               g_set_error(err, GDK_PIXBUF_ERROR, 
-                           GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
-                           _("Can't allocate memory for TGA cmap temporary buffer"));
-               return NULL;
-       }
-       if (!fread_check(pal_buf, pal_size, 1, f, err)) {
-               g_free(pal_buf);
-               return NULL;
-       }
-       p = pal_buf;
-
-       if (!(cmap = g_try_malloc(sizeof(TGAColormap)))) {
-               g_set_error(err, GDK_PIXBUF_ERROR, 
-                           GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
-                           _("Can't allocate memory for TGA colormap struct"));
-               g_free(pal_buf);
-               return NULL;
-       }
-       cmap->size = LE16(hdr->cmap_n_colors);
-       cmap->cols = g_try_malloc(sizeof(TGAColor) * cmap->size);
-       if (!cmap->cols) {
-               g_set_error(err, GDK_PIXBUF_ERROR, 
-                           GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
-                           _("Can't allocate memory for TGA colormap entries"));
-               g_free(pal_buf);
-               g_free(cmap);
-               return NULL;
-       }
-
-       if (hdr->cmap_bpp != 15 && hdr->cmap_bpp != 16 &&
-           hdr->cmap_bpp != 24 && hdr->cmap_bpp != 32) {
-               g_set_error(err, GDK_PIXBUF_ERROR, 
-                           GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
-                           _("Unexpected bitdepth for TGA colormap"));
-               g_free(pal_buf);
-               g_free(cmap->cols);
-               g_free(cmap);
-               return NULL;
-       }
-
-       for (n = 0; n < cmap->size; n++) {
-               if ((hdr->cmap_bpp == 15) || (hdr->cmap_bpp == 16)) {
-                       guint16 col = p[0] + (p[1] << 8);
-                       p += 2;
-                       cmap->cols[n].b = (col >> 7) & 0xf8;
-                       cmap->cols[n].g = (col >> 2) & 0xf8;
-                       cmap->cols[n].r = col << 3;
-               } else if ((hdr->cmap_bpp == 24) || (hdr->cmap_bpp == 32)) {
-                       cmap->cols[n].b = *p++;
-                       cmap->cols[n].g = *p++;
-                       cmap->cols[n].r = *p++;
-                       if (hdr->cmap_bpp == 32)
-                               cmap->cols[n].a = *p++;
-               }
-       }
-
-       g_free(pal_buf);
-       return cmap;
-}
-
-static GdkPixbuf *get_image_pseudocolor(FILE *f, TGAHeader *hdr,
-                                       TGAColormap *cmap, gboolean rle,
-                                       GError **err)
-{
-       GdkPixbuf *pbuf;
-       guchar *p, color, tag;
-       glong n, image_offset;
-       guint count, w, h;
-       gboolean alpha;
-
-       image_offset = sizeof(TGAHeader) + hdr->infolen;
-       if (!hdr->has_cmap) {
-               g_set_error(err, GDK_PIXBUF_ERROR, 
-                           GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
-                           _("Pseudo-Color image without colormap"));
-               return NULL;
-       } else {
-               image_offset += cmap->size * ((hdr->cmap_bpp + 7) >> 3);
-       }
-       if (!fseek_check(f, image_offset, SEEK_SET, err)) {
-               g_set_error(err, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED,
-                           _("Can't seek to image offset -- end-of-file probably encountered"));
-               return NULL;
-       }
-
-       w = LE16(hdr->width);
-       h = LE16(hdr->height);
-
-       alpha = (hdr->cmap_bpp == 32);
-
-       pbuf = get_contiguous_pixbuf (w, h, alpha);
-
-       if (!pbuf) {
-               g_set_error(err, GDK_PIXBUF_ERROR, 
-                           GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
-                           _("Can't allocate pixbuf"));
-               return NULL;
-       }
-       pbuf->destroy_fn = free_buffer;
-       pbuf->destroy_fn_data = NULL;
-       p = pbuf->pixels;
-
-       if (rle) {
-               n = count = 0;
-               for (; n < pbuf->width * pbuf->height;) {
-                       if (!fread_check(&tag, 1, 1, f, err)) {
-                               g_object_unref(pbuf);
-                               return NULL;
-                       }
-                       if (tag & 0x80) {
-                               count = (tag & 0x7f) + 1;
-                               n += count;
-                               if (!fread_check(&color, 1, 1, f, err)) {
-                                       g_object_unref(pbuf);
-                                       return NULL;
-                               }
-                               for (; count; count--) {
-                                       *p++ = cmap->cols[color].r;
-                                       *p++ = cmap->cols[color].g;
-                                       *p++ = cmap->cols[color].b;
-                                       if (hdr->cmap_bpp == 32)
-                                               *p++ = cmap->cols[color].a;
-                               }
-                       } else {
-                               count = tag + 1;
-                               n += count;
-                               for (; count; count--) {
-                                       if (!fread_check(&color, 1, 1, f, err)) {
-                                               g_object_unref(pbuf);
-                                               return NULL;
-                                       }
-                                       *p++ = cmap->cols[color].r;
-                                       *p++ = cmap->cols[color].g;
-                                       *p++ = cmap->cols[color].b;
-                                       if (hdr->cmap_bpp == 32)
-                                               *p++ = cmap->cols[color].a;
-                               }
-                       }
-               }
-       } else {
-               for (n = 0; n < pbuf->width * pbuf->height; n++) {
-                       if (!fread_check(&color, 1, 1, f, err)) {
-                               g_object_unref(pbuf);
-                               return NULL;
-                       }
-                       *p++ = cmap->cols[color].r;
-                       *p++ = cmap->cols[color].g;
-                       *p++ = cmap->cols[color].b;
-                       if (hdr->cmap_bpp == 32)
-                               *p++ = cmap->cols[color].a;
-               }
-       }
-
-       return pbuf;
-}
-
-static void swap_channels_pixbuf(GdkPixbuf *pbuf)
-{
-       guchar *p, swap;
-       glong n;
-
-       p = pbuf->pixels;
-       for (n = 0; n < pbuf->width * pbuf->height; n++) {
-               swap = p[0];
-               p[0] = p[2];
-               p[2] = swap;
-               p += pbuf->n_channels;
-       }
-}
-
-static GdkPixbuf *get_image_truecolor(FILE *f, TGAHeader *hdr,
-                                     gboolean rle, GError **err)
-{
-       GdkPixbuf *pbuf;
-       guchar *p, tag;
-       glong n, image_offset;
-       guint32 pixel;
-       guint count, w, h;
-       gboolean alpha;
-
-       image_offset = sizeof(TGAHeader) + hdr->infolen;
-       /* A truecolor image shouldn't actually have a colormap. */
-       if (hdr->has_cmap)
-               image_offset += LE16(hdr->cmap_n_colors) * ((hdr->cmap_bpp + 7) >> 3);
-       if (!fseek_check(f, image_offset, SEEK_SET, err))
-               return NULL;
-
-       w = LE16(hdr->width);
-       h = LE16(hdr->height);
-       
-       alpha = (hdr->bpp == 32);
-
-       pbuf = get_contiguous_pixbuf (w, h, alpha);
-
-       if (!pbuf) {
-               g_set_error(err, GDK_PIXBUF_ERROR, 
-                           GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
-                           _("Can't allocate pixbuf"));
-               return NULL;
-       }
-       p = pbuf->pixels;
-
-       if (rle) {
-               n = count = 0;
-               for (; n < pbuf->width * pbuf->height;) {
-                       if (!fread_check(&tag, 1, 1, f, err)) {
-                               g_object_unref(pbuf);
-                               return NULL;
-                       }
-                       if (tag & 0x80) {
-                               count = (tag & 0x7f) + 1;
-                               n += count;
-                               if (!fread_check(&pixel, pbuf->n_channels, 1, f, err)) {
-                                       g_object_unref(pbuf);
-                                       return NULL;
-                               }
-                               for (; count; count--) {
-                                       g_memmove(p, &pixel, pbuf->n_channels);
-                                       p += pbuf->n_channels;
-                               }
-                       } else {
-                               count = tag + 1;
-                               n += count;
-                               if (!fread_check(p, pbuf->n_channels * count, 1, f, err)) {
-                                       g_object_unref(pbuf);
-                                       return NULL;
-                               }
-                               p += pbuf->n_channels * count;
-                       }
-               }
-       } else {
-               if (!fread_check(p, pbuf->rowstride * pbuf->height, 1, f, err)) {
-                       g_object_unref(pbuf);
-                       return NULL;
-               }
-       }
-
-       swap_channels_pixbuf(pbuf);
-       return pbuf;
-}
-
-static GdkPixbuf *get_image_grayscale(FILE *f, TGAHeader *hdr,
-                                     gboolean rle, GError **err)
-{
-       GdkPixbuf *pbuf;
-       glong n, image_offset;
-       guchar *p, color[2], tag;
-       guint count, w, h;
-       gboolean alpha;
-
-       image_offset = sizeof(TGAHeader) + hdr->infolen;
-       /* A grayscale image shouldn't actually have a colormap. */
-       if (hdr->has_cmap)
-               image_offset += LE16(hdr->cmap_n_colors) * ((hdr->cmap_bpp + 7) >> 3);
-       if (!fseek_check(f, image_offset, SEEK_SET, err))
-               return NULL;
-
-       w = LE16(hdr->width);
-       h = LE16(hdr->height);
-
-       alpha = (hdr->bpp == 16);
-
-       pbuf = get_contiguous_pixbuf (w, h, alpha);
-       
-       if (!pbuf) {
-               g_set_error(err, GDK_PIXBUF_ERROR, 
-                           GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
-                           _("Can't allocate pixbuf"));
-               return NULL;
-       }
-       p = pbuf->pixels;
-
-       if (rle) {
-               n = count = 0;
-               for (; n < pbuf->width * pbuf->height;) {
-                       if (!fread_check(&tag, 1, 1, f, err)) {
-                               g_object_unref(pbuf);
-                               return NULL;
-                       }
-                       if (tag & 0x80) {
-                               count = (tag & 0x7f) + 1;
-                               n += count;
-                               if (!fread_check(color, (alpha ? 2 : 1), 1, f, err)) {
-                                       g_object_unref(pbuf);
-                                       return NULL;
-                               }
-                               for (; count; count--) {
-                                       p[0] = p[1] = p[2] = color[0];
-                                       if (alpha)
-                                               p[3] = color[1];
-                                       p += pbuf->n_channels;
-                               }
-                       } else {
-                               count = tag + 1;
-                               n += count;
-                               for (; count; count--) {
-                                       if (!fread_check(color, (alpha ? 2 : 1), 1, f, err)) {
-                                               g_object_unref(pbuf);
-                                               return NULL;
-                                       }
-                                       p[0] = p[1] = p[2] = color[0];
-                                       if (alpha)
-                                               p[3] = color[1];
-                                       p += pbuf->n_channels;
-                               }
-                       }
-               }
-       } else {
-               for (n = 0; n < pbuf->width * pbuf->height; n++) {
-                       if (!fread_check(color, (alpha ? 2 : 1), 1, f, err)) {
-                               g_object_unref(pbuf);
-                               return NULL;
-                       }
-                       p[0] = p[1] = p[2] = color[0];
-                       if (alpha)
-                               p[3] = color[1];
-                       p += pbuf->n_channels;
-               }
-       }
-
-       return pbuf;
-}
-
-static GdkPixbuf *gdk_pixbuf__tga_load(FILE *f, GError **err)
-{
-       TGAHeader *hdr;
-       TGAColormap *cmap;
-       GdkPixbuf *pbuf;
-
-       cmap = NULL;
-       hdr = get_header_from_file(f, err);
-       if (!hdr)
-               return NULL;
-       if ((hdr->flags & TGA_INTERLEAVE_MASK) != TGA_INTERLEAVE_NONE ||
-           hdr->flags & TGA_ORIGIN_RIGHT || hdr->flags & TGA_ORIGIN_LOWER) {
-               g_set_error(err, GDK_PIXBUF_ERROR, 
-                           GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
-                           _("Unsupported TGA image type"));
-               g_free(hdr);
-               return NULL;
-       }
-  
-       if (hdr->has_cmap && ((hdr->type == TGA_TYPE_PSEUDOCOLOR) || 
-                             (hdr->type == TGA_TYPE_RLE_PSEUDOCOLOR))) {
-               cmap = get_colormap_from_file(f, hdr, err);
-               if (!cmap) {
-                       g_free(hdr);
-                       return NULL;
-               }
-       }
-
-       if (hdr->type == TGA_TYPE_PSEUDOCOLOR)
-               pbuf = get_image_pseudocolor(f, hdr, cmap, FALSE, err);
-       else if (hdr->type == TGA_TYPE_RLE_PSEUDOCOLOR)
-               pbuf = get_image_pseudocolor(f, hdr, cmap, TRUE, err);
-       else if (hdr->type == TGA_TYPE_TRUECOLOR)
-               pbuf = get_image_truecolor(f, hdr, FALSE, err);
-       else if (hdr->type == TGA_TYPE_RLE_TRUECOLOR)
-               pbuf = get_image_truecolor(f, hdr, TRUE, err);
-       else if (hdr->type == TGA_TYPE_GRAYSCALE)
-               pbuf = get_image_grayscale(f, hdr, FALSE, err);
-       else if (hdr->type == TGA_TYPE_RLE_GRAYSCALE)
-               pbuf = get_image_grayscale(f, hdr, TRUE, err);
-       else {
-               g_set_error(err, GDK_PIXBUF_ERROR, 
-                           GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
-                           _("Unsupported TGA image type"));
-               pbuf = NULL;
-       }
-  
-       if (cmap) {
-               g_free(cmap->cols);
-               g_free(cmap);
-       }
-       g_free(hdr);
-  
-       return pbuf;
-}
-
 void
-gdk_pixbuf__tga_fill_vtable (GdkPixbufModule *module)
+MODULE_ENTRY (tga, fill_vtable) (GdkPixbufModule *module)
 {
-       module->load = gdk_pixbuf__tga_load;
        module->begin_load = gdk_pixbuf__tga_begin_load;
        module->stop_load = gdk_pixbuf__tga_stop_load;
        module->load_increment = gdk_pixbuf__tga_load_increment;
 }
+
+void
+MODULE_ENTRY (tga, fill_info) (GdkPixbufFormat *info)
+{
+       static GdkPixbufModulePattern signature[] = {
+               { " \x1\x1", "x  ", 100 },
+               { " \x1\x9", "x  ", 100 },
+               { "  \x2", "xz ",  99 }, /* only 99 since .CUR also matches this */
+               { "  \x3", "xz ", 100 },
+               { "  \xa", "xz ", 100 },
+               { "  \xb", "xz ", 100 },
+               { NULL, NULL, 0 }
+       };
+       static gchar * mime_types[] = {
+               "image/x-tga",
+               NULL
+       };
+       static gchar * extensions[] = {
+               "tga",
+               "targa",
+               NULL
+       };
+
+       info->name = "tga";
+       info->signature = signature;
+       info->description = N_("The Targa image format");
+       info->mime_types = mime_types;
+       info->extensions = extensions;
+       info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
+       info->license = "LGPL";
+}