]> Pileus Git - ~andy/gtk/blobdiff - gdk-pixbuf/io-bmp.c
Properly determine the number of colors in an OS/2 BMP file. (#150003,
[~andy/gtk] / gdk-pixbuf / io-bmp.c
index 13a173824b6c22d007b2139678bb0e9e4b5acf3e..82ddba1301a6491fb1381a49a30553b1101a8b86 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: C; c-file-style: "linux" -*- */
 /* GdkPixbuf library - Windows Bitmap image loader
  *
  * Copyright (C) 1999 The Free Software Foundation
@@ -8,79 +9,86 @@
  * Based on io-ras.c
  *
  * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
+ * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2 of the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the
  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  * Boston, MA 02111-1307, USA.
  */
 
-/*
-
-Known bugs:
-       * 4bpp compressed files don't work
-       * bi-tonal files aren't tested with palettes
-
-*/
-
 #include <config.h>
 #include <stdio.h>
+#ifdef HAVE_UNISTD_H
 #include <unistd.h>
+#endif
 #include <string.h>
-#include <gdk-pixbuf/gdk-pixbuf.h>
-#include <gdk-pixbuf/gdk-pixbuf-io.h>
-\f
-
-
-
-
-/* 
+#include "gdk-pixbuf-private.h"
+#include "gdk-pixbuf-io.h"
 
-These structures are actually dummies. These are according to
-the "Windows API reference guide volume II" as written by 
-Borland International, but GCC fiddles with the alignment of 
-the internal members, so these aren't actually usable.
-
-*/
+\f
 
+#if 0
+/* If these structures were unpacked, they would define the two headers of the
+ * BMP file.  After them comes the palette, and then the image data.
+ *
+ * We do not use these structures; we just keep them here for reference.
+ */
 struct BitmapFileHeader {
-       gushort bfType;
-       guint bfSize;
-       guint reserverd;
-       guint bfOffbits;
+       guint16 magic;
+       guint32 file_size;
+       guint32 reserved;
+       guint32 data_offset;
 };
 
 struct BitmapInfoHeader {
-       guint biSize;
-       guint biWidth;
-       guint biHeight;
-       gushort biPlanes;
-       gushort biBitCount;
-       guint biCompression;
-       guint biSizeImage;
-       guint biXPelsPerMeter;
-       guint biYPelsPerMeter;
-       guint biClrUsed;
-       guint biClrImportant;
+       guint32 header_size;
+       guint32 width;
+       guint32 height;
+       guint16 planes;
+       guint16 bpp;
+       guint32 compression;
+       guint32 data_size;
+       guint32 x_ppm;
+       guint32 y_ppm;
+       guint32 n_colors;
+       guint32 n_important_colors;
 };
+#endif
 
-/* 
+/* Compression values */
 
-DumpBIH printf's the values in a BitmapInfoHeader to the screen, for 
+#define BI_RGB 0
+#define BI_RLE8 1
+#define BI_RLE4 2
+#define BI_BITFIELDS 3
+
+/* State machine */
+typedef enum {
+       READ_STATE_HEADERS,     /* Reading the bitmap file header and bitmap info header */
+       READ_STATE_PALETTE,     /* Reading the palette */
+       READ_STATE_BITMASKS,    /* Reading the bitmasks for BI_BITFIELDS */
+       READ_STATE_DATA,        /* Reading the actual image data */
+       READ_STATE_ERROR,       /* An error occurred; further data will be ignored */
+       READ_STATE_DONE         /* Done reading the image; further data will be ignored */
+} ReadState;
+
+/*
+
+DumpBIH printf's the values in a BitmapInfoHeader to the screen, for
 debugging purposes.
 
 */
 #if DUMPBIH
 static void DumpBIH(unsigned char *BIH)
-{                              
+{
        printf("biSize      = %i \n",
               (int) (BIH[3] << 24) + (BIH[2] << 16) + (BIH[1] << 8) +
               (BIH[0]));
@@ -116,133 +124,172 @@ static void DumpBIH(unsigned char *BIH)
    the current bitmap */
 
 struct headerpair {
-       guint width;
-       guint height;
+       guint32 size;
+       gint32 width;
+       gint32 height;
        guint depth;
-       guint Negative;         /* Negative = 1 -> top down BMP,  
+       guint Negative;         /* Negative = 1 -> top down BMP,
                                   Negative = 0 -> bottom up BMP */
+       guint  n_colors;
 };
 
 /* Data needed for the "state" during decompression */
 struct bmp_compression_state {
-       gint phase;             /* 0 = clean, 
-                                  1 = count received
-                                  2 = escape received
-                                  3 = in "raw" run
-                                  4 = Relative part 1 is next
-                                  5 = Relative part 2 is next
-                                  6 = end of image -> No more input allowed
-                                */
-       gint RunCount;
-       gint XDelta;
-       gint YDelta;
+       gint phase;
+       gint run;
+       gint count;
+       gint x, y;
+       guchar *p;
 };
 
 /* Progressive loading */
 
 struct bmp_progressive_state {
-       ModulePreparedNotifyFunc prepared_func;
-       ModuleUpdatedNotifyFunc updated_func;
+       GdkPixbufModuleSizeFunc size_func;
+       GdkPixbufModulePreparedFunc prepared_func;
+       GdkPixbufModuleUpdatedFunc updated_func;
        gpointer user_data;
 
-       gint HeaderSize;        /* The size of the header-part (incl colormap) */
-       guchar *HeaderBuf;      /* The buffer for the header (incl colormap) */
-       gint HeaderDone;        /* The nr of bytes actually in HeaderBuf */
+       ReadState read_state;
+
+       guint LineWidth;
+       guint Lines;            /* # of finished lines */
+
+       guchar *buff;
+       guint BufferSize;
+       guint BufferDone;
 
-       gint LineWidth;         /* The width of a line in bytes */
-       guchar *LineBuf;        /* Buffer for 1 line */
-       gint LineDone;          /* # of bytes in LineBuf */
-       gint Lines;             /* # of finished lines */
+       guchar (*Colormap)[3];
 
-       gint Type;              /*  
+       gint Type;              /*
                                   32 = RGB + alpha
                                   24 = RGB
+                                  16 = RGB
                                   4  = 4 bpp colormapped
                                   8  = 8 bpp colormapped
-                                  1  = 1 bit bitonal 
+                                  1  = 1 bit bitonal
                                 */
-       gint Compressed;
+       guint Compressed;
        struct bmp_compression_state compr;
 
 
        struct headerpair Header;       /* Decoded (BE->CPU) header */
 
+       /* Bit masks, shift amounts, and significant bits for BI_BITFIELDS coding */
+       int r_mask, r_shift, r_bits;
+       int g_mask, g_shift, g_bits;
+       int b_mask, b_shift, b_bits;
 
        GdkPixbuf *pixbuf;      /* Our "target" */
 };
 
-gpointer
-gdk_pixbuf__bmp_image_begin_load(ModulePreparedNotifyFunc prepared_func,
-                                ModuleUpdatedNotifyFunc updated_func,
-                                ModuleFrameDoneNotifyFunc frame_done_func,
-                                ModuleAnimationDoneNotifyFunc anim_done_func,
-                                gpointer user_data);
+static gpointer
+gdk_pixbuf__bmp_image_begin_load(GdkPixbufModuleSizeFunc size_func,
+                                 GdkPixbufModulePreparedFunc prepared_func,
+                                GdkPixbufModuleUpdatedFunc updated_func,
+                                 gpointer user_data,
+                                 GError **error);
 
-void gdk_pixbuf__bmp_image_stop_load(gpointer data);
-gboolean gdk_pixbuf__bmp_image_load_increment(gpointer data, guchar * buf, guint size);
+static gboolean gdk_pixbuf__bmp_image_stop_load(gpointer data, GError **error);
+static gboolean gdk_pixbuf__bmp_image_load_increment(gpointer data,
+                                                     const guchar * buf,
+                                                     guint size,
+                                                     GError **error);
 
 
-
-/* Shared library entry point --> This should be removed when
-   generic_image_load enters gdk-pixbuf-io. */
-GdkPixbuf *gdk_pixbuf__bmp_image_load(FILE * f)
+/* Picks up a 32-bit little-endian integer starting at the specified location.
+ * Does it by hand instead of dereferencing a simple (gint *) cast due to
+ * alignment constraints many platforms.
+ */
+static int
+lsb_32 (guchar *src)
 {
-       guchar *membuf;
-       size_t length;
-       struct bmp_progressive_state *State;
-
-       GdkPixbuf *pb;
+       return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
 
-       State = gdk_pixbuf__bmp_image_begin_load(NULL, NULL, NULL, NULL, NULL);
-       membuf = g_malloc(4096);
+/* Same as above, but for 16-bit little-endian integers. */
+static short
+lsb_16 (guchar *src)
+{
+       return src[0] | (src[1] << 8);
+}
 
-       g_assert(membuf != NULL);
+static gboolean grow_buffer (struct bmp_progressive_state *State,
+                             GError **error)
+{
+  guchar *tmp = g_try_realloc (State->buff, State->BufferSize);
+  if (!tmp) {
+    g_set_error (error,
+                GDK_PIXBUF_ERROR,
+                GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+                _("Not enough memory to load bitmap image"));
+    State->read_state = READ_STATE_ERROR;
+    return FALSE;
+  }
+  State->buff = tmp;
+  return TRUE;
+}
 
+static gboolean DecodeHeader(unsigned char *BFH, unsigned char *BIH,
+                             struct bmp_progressive_state *State,
+                             GError **error)
+{
+       gint clrUsed;
 
-       while (feof(f) == 0) {
-               length = fread(membuf, 1, 4096, f);
-               if (length > 0)
-                       (void) gdk_pixbuf__bmp_image_load_increment(State, membuf, length);
+        /* FIXME this is totally unrobust against bogus image data. */
 
+       if (State->BufferSize < lsb_32 (&BIH[0]) + 14) {
+               State->BufferSize = lsb_32 (&BIH[0]) + 14;
+               if (!grow_buffer (State, error))
+                       return FALSE;
+               return TRUE;
        }
-       g_free(membuf);
-       if (State->pixbuf != NULL)
-               gdk_pixbuf_ref(State->pixbuf);
-
-       pb = State->pixbuf;
-
-       gdk_pixbuf__bmp_image_stop_load(State);
-       return pb;
-}
 
-static void DecodeHeader(unsigned char *BFH, unsigned char *BIH,
-                        struct bmp_progressive_state *State)
-{
 #if DUMPBIH
        DumpBIH(BIH);
-#endif
+#endif    
+
+       State->Header.size = lsb_32 (&BIH[0]);
+       if (State->Header.size == 40) {
+               State->Header.width = lsb_32 (&BIH[4]);
+               State->Header.height = lsb_32 (&BIH[8]);
+               State->Header.depth = lsb_16 (&BIH[14]);
+               State->Compressed = lsb_32 (&BIH[16]);
+       } else if (State->Header.size == 12) {
+               State->Header.width = lsb_16 (&BIH[4]);
+               State->Header.height = lsb_16 (&BIH[6]);
+               State->Header.depth = lsb_16 (&BIH[10]);
+               State->Compressed = BI_RGB;
+       } else {
+               g_set_error (error,
+                            GDK_PIXBUF_ERROR,
+                            GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                            _("BMP image has unsupported header size"));
+               State->read_state = READ_STATE_ERROR;
+               return FALSE;
+       }
 
-       State->Header.width =
-           (int) (BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) +
-           (BIH[4]);
-       State->Header.height =
-           (int) (BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) +
-           (BIH[8]);
-       State->Header.depth = (int) (BIH[15] << 8) + (BIH[14]);;
+       if (State->Header.size == 12)
+               clrUsed = 1 << State->Header.depth;
+       else
+               clrUsed = (int) (BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) + (BIH[32]);
 
-       State->Type = State->Header.depth;      /* This may be less trivial someday */
-       State->HeaderSize =
-           (int) ((BFH[13] << 24) + (BFH[12] << 16) + (BFH[11] << 8) +
-                  (BFH[10]));
-       if (State->HeaderSize >= 14 + 40 + 1024)
-               State->HeaderBuf =
-                   g_realloc(State->HeaderBuf, State->HeaderSize);
-
-       if ((BIH[16] != 0) || (BIH[17] != 0) || (BIH[18] != 0)
-           || (BIH[19] != 0)) {
-               State->Compressed = 1;
+       if (clrUsed != 0)
+               State->Header.n_colors = clrUsed;
+       else
+            State->Header.n_colors = 1 << State->Header.depth;
+       
+       if (State->Header.n_colors > 1 << State->Header.depth) {
+               g_set_error (error,
+                            GDK_PIXBUF_ERROR,
+                            GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                            _("BMP image has bogus header data"));
+               State->read_state = READ_STATE_ERROR;
+               return FALSE;
        }
 
+       State->Type = State->Header.depth;      /* This may be less trivial someday */
+
        /* Negative heights indicates bottom-down pixelorder */
        if (State->Header.height < 0) {
                State->Header.height = -State->Header.height;
@@ -253,82 +300,248 @@ static void DecodeHeader(unsigned char *BFH, unsigned char *BIH,
                State->Header.Negative = 0;
        }
 
+       if (State->Header.width == 0 || State->Header.height == 0 ||
+           (State->Compressed == BI_RLE4 && State->Type != 4)    ||
+           (State->Compressed == BI_RLE8 && State->Type != 8)    ||
+           (State->Compressed == BI_BITFIELDS && !(State->Type == 16 || State->Type == 32)) ||
+           State->Compressed > BI_BITFIELDS) {
+               g_set_error (error,
+                            GDK_PIXBUF_ERROR,
+                            GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                            _("BMP image has bogus header data"));
+               State->read_state = READ_STATE_ERROR;
+               return FALSE;
+       }
+
        if (State->Type == 32)
                State->LineWidth = State->Header.width * 4;
-       if (State->Type == 24)
+       else if (State->Type == 24)
                State->LineWidth = State->Header.width * 3;
-       if (State->Type == 8)
+       else if (State->Type == 16)
+               State->LineWidth = State->Header.width * 2;
+       else if (State->Type == 8)
                State->LineWidth = State->Header.width * 1;
-       if (State->Type == 4)
+       else if (State->Type == 4)
                State->LineWidth = (State->Header.width + 1) / 2;
-       if (State->Type == 1) {
+       else if (State->Type == 1) {
                State->LineWidth = State->Header.width / 8;
                if ((State->Header.width & 7) != 0)
                        State->LineWidth++;
+       } else {
+               g_set_error (error,
+                            GDK_PIXBUF_ERROR,
+                            GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                            _("BMP image has bogus header data"));
+               State->read_state = READ_STATE_ERROR;
+               return FALSE;
        }
 
        /* Pad to a 32 bit boundary */
-       if (((State->LineWidth % 4) > 0) && (State->Compressed == 0))
+       if (((State->LineWidth % 4) > 0)
+           && (State->Compressed == BI_RGB || State->Compressed == BI_BITFIELDS))
                State->LineWidth = (State->LineWidth / 4) * 4 + 4;
 
-
-       if (State->LineBuf == NULL)
-               State->LineBuf = g_malloc(State->LineWidth);
-
-       g_assert(State->LineBuf != NULL);
-
-
        if (State->pixbuf == NULL) {
-               if (State->Type == 32)
+               if (State->size_func) {
+                       gint width = State->Header.width;
+                       gint height = State->Header.height;
+
+                       (*State->size_func) (&width, &height, State->user_data);
+                       if (width == 0 || height == 0) {
+                               State->read_state = READ_STATE_DONE;
+                               State->BufferSize = 0;
+                               return TRUE;
+                       }
+               }
+
+               if (State->Type == 32 || 
+                   State->Compressed == BI_RLE4 || 
+                   State->Compressed == BI_RLE8)
                        State->pixbuf =
-                           gdk_pixbuf_new(ART_PIX_RGB, TRUE, 8,
-                                          (gint) State->Header.width,
-                                          (gint) State->Header.height);
+                               gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
+                                              (gint) State->Header.width,
+                                              (gint) State->Header.height);
                else
                        State->pixbuf =
-                           gdk_pixbuf_new(ART_PIX_RGB, FALSE, 8,
-                                          (gint) State->Header.width,
-                                          (gint) State->Header.height);
-
+                               gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
+                                              (gint) State->Header.width,
+                                              (gint) State->Header.height);
+               
+               if (State->pixbuf == NULL) {
+                       g_set_error (error,
+                                    GDK_PIXBUF_ERROR,
+                                    GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+                                    _("Not enough memory to load bitmap image"));
+                       State->read_state = READ_STATE_ERROR;
+                       return FALSE;
+                       }
+               
                if (State->prepared_func != NULL)
                        /* Notify the client that we are ready to go */
-                       (*State->prepared_func) (State->pixbuf,
-                                                State->user_data);
+                       (*State->prepared_func) (State->pixbuf, NULL, State->user_data);
+               
+               /* make all pixels initially transparent */
+               if (State->Compressed == BI_RLE4 || State->Compressed == BI_RLE8) {
+                       memset (State->pixbuf->pixels, 0, State->pixbuf->rowstride * State->Header.height);
+                       State->compr.p = State->pixbuf->pixels 
+                               + State->pixbuf->rowstride * (State->Header.height- 1);
+               }
+       }
+       
+       State->BufferDone = 0;
+       if (State->Type <= 8) {
+               State->read_state = READ_STATE_PALETTE;
+               State->BufferSize = lsb_32 (&BFH[10]) - 14 - State->Header.size; 
+       } else if (State->Compressed == BI_RGB) {
+               State->read_state = READ_STATE_DATA;
+               State->BufferSize = State->LineWidth;
+       } else if (State->Compressed == BI_BITFIELDS) {
+               State->read_state = READ_STATE_BITMASKS;
+               State->BufferSize = 12;
+       } else {
+               g_set_error (error,
+                            GDK_PIXBUF_ERROR,
+                            GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                            _("BMP image has bogus header data"));
+               State->read_state = READ_STATE_ERROR;
+               return FALSE;
+       }
+
+       if (!grow_buffer (State, error)) 
+               return FALSE;
 
+        return TRUE;
+}
+
+static gboolean DecodeColormap (guchar *buff,
+                               struct bmp_progressive_state *State,
+                               GError **error)
+{
+       gint i;
+       gint samples;
+
+       g_assert (State->read_state == READ_STATE_PALETTE);
+
+       samples = (State->Header.size == 12 ? 3 : 4);
+       if (State->BufferSize < State->Header.n_colors * samples) {
+               State->BufferSize = State->Header.n_colors * samples;
+               if (!grow_buffer (State, error))
+                       return FALSE;
+               return TRUE;
        }
 
+       State->Colormap = g_malloc ((1 << State->Header.depth) * sizeof (*State->Colormap));
+       for (i = 0; i < State->Header.n_colors; i++)
+
+       {
+               State->Colormap[i][0] = buff[i * samples];
+               State->Colormap[i][1] = buff[i * samples + 1];
+               State->Colormap[i][2] = buff[i * samples + 2];
+#ifdef DUMPCMAP
+               g_print ("color %d %x %x %x\n", i,
+                        State->Colormap[i][0],
+                        State->Colormap[i][1],
+                        State->Colormap[i][2]);
+#endif
+       }
+
+       State->read_state = READ_STATE_DATA;
+
+       State->BufferDone = 0;
+       if (!(State->Compressed == BI_RGB || State->Compressed == BI_BITFIELDS))
+               State->BufferSize = 2;
+       else
+               State->BufferSize = State->LineWidth;
+       
+       if (!grow_buffer (State, error))
+               return FALSE;
+
+       return TRUE;
 }
 
-/* 
+/* Finds the lowest set bit and the number of set bits */
+static void
+find_bits (int n, int *lowest, int *n_set)
+{
+       int i;
+
+       *n_set = 0;
+
+       for (i = 31; i >= 0; i--)
+               if (n & (1 << i)) {
+                       *lowest = i;
+                       (*n_set)++;
+               }
+}
+
+/* Decodes the 3 shorts that follow for the bitmasks for BI_BITFIELDS coding */
+static gboolean
+decode_bitmasks (guchar *buf,
+                struct bmp_progressive_state *State, 
+                GError **error)
+{
+       State->r_mask = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+       buf += 4;
+
+       State->g_mask = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+       buf += 4;
+
+       State->b_mask = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+
+       find_bits (State->r_mask, &State->r_shift, &State->r_bits);
+       find_bits (State->g_mask, &State->g_shift, &State->g_bits);
+       find_bits (State->b_mask, &State->b_shift, &State->b_bits);
+
+       if (State->r_bits == 0 || State->g_bits == 0 || State->b_bits == 0) {
+               State->r_mask = 0x7c00;
+               State->r_shift = 10;
+               State->g_mask = 0x03e0;
+               State->g_shift = 5;
+               State->b_mask = 0x001f;
+               State->b_shift = 0;
+
+               State->r_bits = State->g_bits = State->b_bits = 5;
+       }
+
+       State->read_state = READ_STATE_DATA;
+       State->BufferDone = 0;
+       State->BufferSize = State->LineWidth;
+       if (!grow_buffer (State, error)) 
+               return FALSE;
+
+       return TRUE;
+}
+
+/*
  * func - called when we have pixmap created (but no image data)
  * user_data - passed as arg 1 to func
  * return context (opaque to user)
  */
 
-gpointer
-gdk_pixbuf__bmp_image_begin_load(ModulePreparedNotifyFunc prepared_func,
-                                ModuleUpdatedNotifyFunc updated_func,
-                                ModuleFrameDoneNotifyFunc frame_done_func,
-                                ModuleAnimationDoneNotifyFunc anim_done_func,
-                                gpointer user_data)
+static gpointer
+gdk_pixbuf__bmp_image_begin_load(GdkPixbufModuleSizeFunc size_func,
+                                 GdkPixbufModulePreparedFunc prepared_func,
+                                GdkPixbufModuleUpdatedFunc updated_func,
+                                 gpointer user_data,
+                                 GError **error)
 {
        struct bmp_progressive_state *context;
-
+       
        context = g_new0(struct bmp_progressive_state, 1);
+       context->size_func = size_func;
        context->prepared_func = prepared_func;
        context->updated_func = updated_func;
        context->user_data = user_data;
 
-       context->HeaderSize = 54;
-       context->HeaderBuf = g_malloc(14 + 40 + 1024);
-       /* 14 for the BitmapFileHeader, 40 for the BitmapImageHeader and 
-          1024 for the colormap */
+       context->read_state = READ_STATE_HEADERS;
+
+       context->BufferSize = 26;
+       context->buff = g_malloc(26);
+       context->BufferDone = 0;
+       /* 14 for the BitmapFileHeader, 12 for the BitmapImageHeader */
 
-       context->HeaderDone = 0;
+       context->Colormap = NULL;
 
-       context->LineWidth = 0;
-       context->LineBuf = NULL;
-       context->LineDone = 0;
        context->Lines = 0;
 
        context->Type = 0;
@@ -338,8 +551,7 @@ gdk_pixbuf__bmp_image_begin_load(ModulePreparedNotifyFunc prepared_func,
 
 
        context->pixbuf = NULL;
-
-
+       
        return (gpointer) context;
 }
 
@@ -348,26 +560,27 @@ gdk_pixbuf__bmp_image_begin_load(ModulePreparedNotifyFunc prepared_func,
  *
  * free context, unref gdk_pixbuf
  */
-void gdk_pixbuf__bmp_image_stop_load(gpointer data)
+static gboolean gdk_pixbuf__bmp_image_stop_load(gpointer data, GError **error)
 {
        struct bmp_progressive_state *context =
            (struct bmp_progressive_state *) data;
 
+        /* FIXME this thing needs to report errors if
+         * we have unused image data
+         */
+       
+       g_return_val_if_fail(context != NULL, TRUE);
 
-       g_return_if_fail(context != NULL);
-
-       if (context->LineBuf != NULL)
-               g_free(context->LineBuf);
-       context->LineBuf = NULL;
-
-       if (context->HeaderBuf != NULL)
-               g_free(context->HeaderBuf);
-       context->LineBuf = NULL;
+       if (context->Colormap != NULL)
+               g_free(context->Colormap);
 
        if (context->pixbuf)
-               gdk_pixbuf_unref(context->pixbuf);
+               g_object_unref(context->pixbuf);
 
+       g_free(context->buff);
        g_free(context);
+
+        return TRUE;
 }
 
 
@@ -377,26 +590,57 @@ OneLine24 is the 24 bpp-version.
 */
 static void OneLine32(struct bmp_progressive_state *context)
 {
-       gint X;
-       guchar *Pixels;
+       int i;
+       guchar *pixels;
+       guchar *src;
 
-       X = 0;
-       if (context->Header.Negative == 0)
-               Pixels = context->pixbuf->art_pixbuf->pixels +
-                   gdk_pixbuf_get_rowstride(context->pixbuf) *
-                   (context->Header.height - context->Lines - 1);
+       if (!context->Header.Negative)
+               pixels = (context->pixbuf->pixels +
+                         context->pixbuf->rowstride * (context->Header.height - context->Lines - 1));
        else
-               Pixels = context->pixbuf->art_pixbuf->pixels +
-                   gdk_pixbuf_get_rowstride(context->pixbuf) *
-                   context->Lines;
-       while (X < context->Header.width) {
-               Pixels[X * 4 + 0] = context->LineBuf[X * 4 + 2];
-               Pixels[X * 4 + 1] = context->LineBuf[X * 4 + 1];
-               Pixels[X * 4 + 2] = context->LineBuf[X * 4 + 0];
-               Pixels[X * 4 + 3] = context->LineBuf[X * 4 + 3];
-               X++;
-       }
+               pixels = (context->pixbuf->pixels +
+                         context->pixbuf->rowstride * context->Lines);
+
+       src = context->buff;
+
+       if (context->Compressed == BI_BITFIELDS) {
+               int r_lshift, r_rshift;
+               int g_lshift, g_rshift;
+               int b_lshift, b_rshift;
+
+               r_lshift = 8 - context->r_bits;
+               g_lshift = 8 - context->g_bits;
+               b_lshift = 8 - context->b_bits;
+
+               r_rshift = context->r_bits - r_lshift;
+               g_rshift = context->g_bits - g_lshift;
+               b_rshift = context->b_bits - b_lshift;
 
+               for (i = 0; i < context->Header.width; i++) {
+                       int v, r, g, b;
+
+                       v = src[0] | (src[1] << 8) | (src[2] << 16);
+
+                       r = (v & context->r_mask) >> context->r_shift;
+                       g = (v & context->g_mask) >> context->g_shift;
+                       b = (v & context->b_mask) >> context->b_shift;
+
+                       *pixels++ = (r << r_lshift) | (r >> r_rshift);
+                       *pixels++ = (g << g_lshift) | (g >> g_rshift);
+                       *pixels++ = (b << b_lshift) | (b >> b_rshift);
+                       *pixels++ = 0xff;
+
+                       src += 4;
+               }
+       } else
+               for (i = 0; i < context->Header.width; i++) {
+                       *pixels++ = src[2];
+                       *pixels++ = src[1];
+                       *pixels++ = src[0];
+                       *pixels++ = 0xff;
+
+                       src += 4;
+               }
 }
 
 static void OneLine24(struct bmp_progressive_state *context)
@@ -406,22 +650,83 @@ static void OneLine24(struct bmp_progressive_state *context)
 
        X = 0;
        if (context->Header.Negative == 0)
-               Pixels = context->pixbuf->art_pixbuf->pixels +
-                   gdk_pixbuf_get_rowstride(context->pixbuf) *
-                   (context->Header.height - context->Lines - 1);
+               Pixels = (context->pixbuf->pixels +
+                         context->pixbuf->rowstride *
+                         (context->Header.height - context->Lines - 1));
        else
-               Pixels = context->pixbuf->art_pixbuf->pixels +
-                   gdk_pixbuf_get_rowstride(context->pixbuf) *
-                   context->Lines;
+               Pixels = (context->pixbuf->pixels +
+                         context->pixbuf->rowstride *
+                         context->Lines);
        while (X < context->Header.width) {
-               Pixels[X * 3 + 0] = context->LineBuf[X * 3 + 2];
-               Pixels[X * 3 + 1] = context->LineBuf[X * 3 + 1];
-               Pixels[X * 3 + 2] = context->LineBuf[X * 3 + 0];
+               Pixels[X * 3 + 0] = context->buff[X * 3 + 2];
+               Pixels[X * 3 + 1] = context->buff[X * 3 + 1];
+               Pixels[X * 3 + 2] = context->buff[X * 3 + 0];
                X++;
        }
 
 }
 
+static void OneLine16(struct bmp_progressive_state *context)
+{
+       int i;
+       guchar *pixels;
+       guchar *src;
+
+       if (!context->Header.Negative)
+               pixels = (context->pixbuf->pixels +
+                         context->pixbuf->rowstride * (context->Header.height - context->Lines - 1));
+       else
+               pixels = (context->pixbuf->pixels +
+                         context->pixbuf->rowstride * context->Lines);
+
+       src = context->buff;
+
+       if (context->Compressed == BI_BITFIELDS) {
+               int r_lshift, r_rshift;
+               int g_lshift, g_rshift;
+               int b_lshift, b_rshift;
+
+               r_lshift = 8 - context->r_bits;
+               g_lshift = 8 - context->g_bits;
+               b_lshift = 8 - context->b_bits;
+
+               r_rshift = context->r_bits - r_lshift;
+               g_rshift = context->g_bits - g_lshift;
+               b_rshift = context->b_bits - b_lshift;
+
+               for (i = 0; i < context->Header.width; i++) {
+                       int v, r, g, b;
+
+                       v = (int) src[0] | ((int) src[1] << 8);
+
+                       r = (v & context->r_mask) >> context->r_shift;
+                       g = (v & context->g_mask) >> context->g_shift;
+                       b = (v & context->b_mask) >> context->b_shift;
+
+                       *pixels++ = (r << r_lshift) | (r >> r_rshift);
+                       *pixels++ = (g << g_lshift) | (g >> g_rshift);
+                       *pixels++ = (b << b_lshift) | (b >> b_rshift);
+
+                       src += 2;
+               }
+       } else
+               for (i = 0; i < context->Header.width; i++) {
+                       int v, r, g, b;
+
+                       v = src[0] | (src[1] << 8);
+
+                       r = (v >> 10) & 0x1f;
+                       g = (v >> 5) & 0x1f;
+                       b = v & 0x1f;
+
+                       *pixels++ = (r << 3) | (r >> 2);
+                       *pixels++ = (g << 3) | (g >> 2);
+                       *pixels++ = (b << 3) | (b >> 2);
+
+                       src += 2;
+               }
+}
+
 static void OneLine8(struct bmp_progressive_state *context)
 {
        gint X;
@@ -429,20 +734,20 @@ static void OneLine8(struct bmp_progressive_state *context)
 
        X = 0;
        if (context->Header.Negative == 0)
-               Pixels = context->pixbuf->art_pixbuf->pixels +
-                   gdk_pixbuf_get_rowstride(context->pixbuf) *
-                   (context->Header.height - context->Lines - 1);
+               Pixels = (context->pixbuf->pixels +
+                         context->pixbuf->rowstride *
+                         (context->Header.height - context->Lines - 1));
        else
-               Pixels = context->pixbuf->art_pixbuf->pixels +
-                   gdk_pixbuf_get_rowstride(context->pixbuf) *
-                   context->Lines;
+               Pixels = (context->pixbuf->pixels +
+                         context->pixbuf->rowstride *
+                         context->Lines);
        while (X < context->Header.width) {
                Pixels[X * 3 + 0] =
-                   context->HeaderBuf[4 * context->LineBuf[X] + 56];
+                   context->Colormap[context->buff[X]][2];
                Pixels[X * 3 + 1] =
-                   context->HeaderBuf[4 * context->LineBuf[X] + 55];
+                   context->Colormap[context->buff[X]][1];
                Pixels[X * 3 + 2] =
-                   context->HeaderBuf[4 * context->LineBuf[X] + 54];
+                   context->Colormap[context->buff[X]][0];
                X++;
        }
 }
@@ -454,34 +759,34 @@ static void OneLine4(struct bmp_progressive_state *context)
 
        X = 0;
        if (context->Header.Negative == 0)
-               Pixels = context->pixbuf->art_pixbuf->pixels +
-                   gdk_pixbuf_get_rowstride(context->pixbuf) *
-                   (context->Header.height - context->Lines - 1);
+               Pixels = (context->pixbuf->pixels +
+                         context->pixbuf->rowstride *
+                         (context->Header.height - context->Lines - 1));
        else
-               Pixels = context->pixbuf->art_pixbuf->pixels +
-                   gdk_pixbuf_get_rowstride(context->pixbuf) *
-                   context->Lines;
+               Pixels = (context->pixbuf->pixels +
+                         context->pixbuf->rowstride *
+                         context->Lines);
 
        while (X < context->Header.width) {
                guchar Pix;
 
-               Pix = context->LineBuf[X / 2];
+               Pix = context->buff[X / 2];
 
                Pixels[X * 3 + 0] =
-                   context->HeaderBuf[4 * (Pix >> 4) + 56];
+                   context->Colormap[Pix >> 4][2];
                Pixels[X * 3 + 1] =
-                   context->HeaderBuf[4 * (Pix >> 4) + 55];
+                   context->Colormap[Pix >> 4][1];
                Pixels[X * 3 + 2] =
-                   context->HeaderBuf[4 * (Pix >> 4) + 54];
+                   context->Colormap[Pix >> 4][0];
                X++;
                if (X < context->Header.width) {
                        /* Handle the other 4 bit pixel only when there is one */
                        Pixels[X * 3 + 0] =
-                           context->HeaderBuf[4 * (Pix & 15) + 56];
+                           context->Colormap[Pix & 15][2];
                        Pixels[X * 3 + 1] =
-                           context->HeaderBuf[4 * (Pix & 15) + 55];
+                           context->Colormap[Pix & 15][1];
                        Pixels[X * 3 + 2] =
-                           context->HeaderBuf[4 * (Pix & 15) + 54];
+                           context->Colormap[Pix & 15][0];
                        X++;
                }
        }
@@ -495,21 +800,21 @@ static void OneLine1(struct bmp_progressive_state *context)
 
        X = 0;
        if (context->Header.Negative == 0)
-               Pixels = context->pixbuf->art_pixbuf->pixels +
-                   gdk_pixbuf_get_rowstride(context->pixbuf) *
-                   (context->Header.height - context->Lines - 1);
+               Pixels = (context->pixbuf->pixels +
+                         context->pixbuf->rowstride *
+                         (context->Header.height - context->Lines - 1));
        else
-               Pixels = context->pixbuf->art_pixbuf->pixels +
-                   gdk_pixbuf_get_rowstride(context->pixbuf) *
-                   context->Lines;
+               Pixels = (context->pixbuf->pixels +
+                         context->pixbuf->rowstride *
+                         context->Lines);
        while (X < context->Header.width) {
                gint Bit;
 
-               Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
+               Bit = (context->buff[X / 8]) >> (7 - (X & 7));
                Bit = Bit & 1;
-               Pixels[X * 3 + 0] = Bit*255;
-               Pixels[X * 3 + 1] = Bit*255;
-               Pixels[X * 3 + 2] = Bit*255;
+               Pixels[X * 3 + 0] = context->Colormap[Bit][2];
+               Pixels[X * 3 + 1] = context->Colormap[Bit][1];
+               Pixels[X * 3 + 2] = context->Colormap[Bit][0];
                X++;
        }
 }
@@ -517,139 +822,193 @@ static void OneLine1(struct bmp_progressive_state *context)
 
 static void OneLine(struct bmp_progressive_state *context)
 {
-       context->LineDone = 0;
+       context->BufferDone = 0;
        if (context->Lines >= context->Header.height)
                return;
 
        if (context->Type == 32)
                OneLine32(context);
-       if (context->Type == 24)
+       else if (context->Type == 24)
                OneLine24(context);
-       if (context->Type == 8)
+       else if (context->Type == 16)
+               OneLine16(context);
+       else if (context->Type == 8)
                OneLine8(context);
-       if (context->Type == 4)
+       else if (context->Type == 4)
                OneLine4(context);
-       if (context->Type == 1)
+       else if (context->Type == 1)
                OneLine1(context);
+       else
+               g_assert_not_reached ();
 
        context->Lines++;
 
        if (context->updated_func != NULL) {
                (*context->updated_func) (context->pixbuf,
                                          0,
-                                         context->Lines,
+                                         (context->Header.Negative ?
+                                          (context->Lines - 1) :
+                                          (context->Header.height - context->Lines)),
                                          context->Header.width,
-                                         context->Header.height,
+                                         1,
                                          context->user_data);
 
        }
 }
 
-/* DoCompressedByte handles 1 byte of incomming compressed data */
-void DoCompressedByte(struct bmp_progressive_state *context, guchar ** buf,
-                     gint * size)
+#define NEUTRAL       0
+#define ENCODED       1
+#define ESCAPE        2   
+#define DELTA_X       3
+#define DELTA_Y       4
+#define ABSOLUTE      5
+#define SKIP          6
+
+#define END_OF_LINE   0
+#define END_OF_BITMAP 1
+#define DELTA         2
+
+static gboolean 
+DoCompressed(struct bmp_progressive_state *context, GError **error)
 {
-       gint BytesToCopy;
-       switch (context->compr.phase) {
-       case 0:         /* Neutral state */
-               if (buf[0] != 0) {      /* run count */
-                       context->compr.phase = 1;
-                       context->compr.RunCount = (*buf)[0];
-               } else {        /* Escape */
-                       context->compr.phase = 2;
-               }
-               (*buf)++;
-               (*size)--;
-               break;
-       case 1:         /* Run count received.... */
-               while (context->compr.RunCount > 0) {
-                       BytesToCopy =
-                           context->LineWidth - context->LineDone;
-                       if (BytesToCopy > context->compr.RunCount)
-                               BytesToCopy = context->compr.RunCount;
-                       if (BytesToCopy > 0) {
-                               memset(context->LineBuf +
-                                      context->LineDone,
-                                      (*buf)[0], BytesToCopy);
-
-                               context->compr.RunCount -= BytesToCopy;
-                               context->LineDone += BytesToCopy;
-                       }
-                       if ((context->LineDone >= context->LineWidth)
-                           && (context->LineWidth > 0)) {
-                               OneLine(context);
-                       }
-               }
-               context->compr.phase = 0;
-               (*buf)++;
-               (*size)--;
-               break;
-       case 2:         /* Escape received */
-               if ((*buf)[0] == 0) {   /* End of line */
-                       context->compr.phase = 0;
-                       if (context->LineDone > 0)
-                               OneLine(context);
-               } else if ((*buf)[0] == 1) {    /* End of image */
-                       OneLine(context);
-                       context->compr.phase = 6;
-                       (*size) = 0;
-                       break;
-               } else if ((*buf)[0] == 2) {    /* Cursor displacement */
-                       context->compr.phase = 4;
-               } else {
-                       context->compr.phase = 3;
-                       context->compr.RunCount = (*buf)[0];
+       gint i, j;
+       gint y;
+       guchar c;
+       gint idx;
+
+       if (context->compr.y >= context->Header.height)
+               return TRUE;
+
+       y = context->compr.y;
+
+       for (i = 0; i < context->BufferSize; i++) {
+               c = context->buff[i];
+               switch (context->compr.phase) {
+                   case NEUTRAL:
+                           if (c) {
+                                   context->compr.run = c;
+                                   context->compr.phase = ENCODED;
+                           }
+                           else
+                                   context->compr.phase = ESCAPE;
+                           break;
+                   case ENCODED:
+                           for (j = 0; j < context->compr.run; j++) {
+                                   if (context->Compressed == BI_RLE8)
+                                           idx = c;
+                                   else if (j & 1) 
+                                           idx = c & 0x0f;
+                                   else 
+                                           idx = (c >> 4) & 0x0f;
+                                   if (context->compr.x < context->Header.width) {
+                                           *context->compr.p++ = context->Colormap[idx][2];
+                                           *context->compr.p++ = context->Colormap[idx][1];
+                                           *context->compr.p++ = context->Colormap[idx][0];
+                                           *context->compr.p++ = 0xff;
+                                           context->compr.x++;    
+                                   }
+                           }
+                           context->compr.phase = NEUTRAL;
+                           break;
+                   case ESCAPE:
+                           switch (c) {
+                               case END_OF_LINE:
+                                       context->compr.x = 0;
+                                       context->compr.y++;
+                                       context->compr.p = context->pixbuf->pixels 
+                                               + (context->pixbuf->rowstride * (context->Header.height - context->compr.y - 1))
+                                               + (4 * context->compr.x);
+                                       context->compr.phase = NEUTRAL;
+                                       break;
+                               case END_OF_BITMAP:
+                                       context->compr.x = 0;
+                                       context->compr.y = context->Header.height;
+                                       context->compr.phase = NEUTRAL;
+                                       break;
+                               case DELTA:
+                                       context->compr.phase = DELTA_X;
+                                       break;
+                               default:
+                                       context->compr.run = c;
+                                       context->compr.count = 0;
+                                       context->compr.phase = ABSOLUTE;
+                                       break;
+                           }
+                           break;
+                   case DELTA_X:
+                           context->compr.x += c;
+                           context->compr.phase = DELTA_Y;
+                           break;
+                   case DELTA_Y:
+                           context->compr.y += c;
+                           context->compr.p = context->pixbuf->pixels 
+                                   + (context->pixbuf->rowstride * (context->Header.height - context->compr.y - 1))
+                                   + (4 * context->compr.x);
+                           context->compr.phase = NEUTRAL;
+                           break;
+                   case ABSOLUTE:
+                           if (context->Compressed == BI_RLE8) {
+                                   idx = c;
+                                   if (context->compr.x < context->Header.width) {
+                                           *context->compr.p++ = context->Colormap[idx][2];
+                                           *context->compr.p++ = context->Colormap[idx][1];
+                                           *context->compr.p++ = context->Colormap[idx][0];
+                                           *context->compr.p++ = 0xff;
+                                           context->compr.x++;    
+                                   }
+                                   context->compr.count++;
+
+                                   if (context->compr.count == context->compr.run) {
+                                           if (context->compr.run & 1)
+                                                   context->compr.phase = SKIP;
+                                           else
+                                                   context->compr.phase = NEUTRAL;
+                                   }
+                           }
+                           else {
+                                   for (j = 0; j < 2; j++) {
+                                           if (context->compr.count & 1)
+                                                   idx = c & 0x0f;
+                                           else 
+                                                   idx = (c >> 4) & 0x0f;
+                                           if (context->compr.x < context->Header.width) {
+                                                   *context->compr.p++ = context->Colormap[idx][2];
+                                                   *context->compr.p++ = context->Colormap[idx][1];
+                                                   *context->compr.p++ = context->Colormap[idx][0];
+                                                   *context->compr.p++ = 0xff;
+                                                   context->compr.x++;    
+                                           }
+                                           context->compr.count++;
+
+                                           if (context->compr.count == context->compr.run) {
+                                                   if ((context->compr.run & 3) == 1
+                                                       || (context->compr.run & 3) == 2) 
+                                                           context->compr.phase = SKIP;
+                                                   else
+                                                           context->compr.phase = NEUTRAL;
+                                                   break;
+                                           }
+                                   }
+                           }
+                           break;
+                   case SKIP:
+                           context->compr.phase = NEUTRAL;
+                           break;
                }
-               (*buf)++;
-               (*size)--;
+       }
+       if (context->updated_func != NULL) {
+               if (context->compr.y > y)
+                       (*context->updated_func) (context->pixbuf,
+                                                 0,
+                                                 y,
+                                                 context->Header.width,
+                                                 context->compr.y - y,
+                                                 context->user_data);
 
-               break;
-       case 3:
-               while ((context->compr.RunCount > 0)
-                      && (size > 0)) {
-                       BytesToCopy =
-                           context->LineWidth - context->LineDone;
-                       if (BytesToCopy > context->compr.RunCount)
-                               BytesToCopy = context->compr.RunCount;
-                       if (BytesToCopy > *size)
-                               BytesToCopy = *size;
-
-                       if (BytesToCopy > 0) {
-                               memcpy(context->LineBuf +
-                                      context->LineDone,
-                                      *buf, BytesToCopy);
-
-                               context->compr.RunCount -= BytesToCopy;
-                               (*buf) += BytesToCopy;
-                               (*size) -= BytesToCopy;
-                               context->LineDone += BytesToCopy;
-                       }
-                       if ((context->LineDone >= context->LineWidth)
-                           && (context->LineWidth > 0))
-                               OneLine(context);
-               }
-               if (context->compr.RunCount <= 0)
-                       context->compr.phase = 0;
-
-               break;
-       case 4:
-               context->compr.phase = 5;
-               context->compr.XDelta = (*buf)[0];
-               (*buf)++;
-               (*size)--;
-               break;
-       case 5:
-               context->compr.phase = 0;
-               context->compr.YDelta = (*buf)[0];
-               g_assert(0);    /* No implementatio of this yet */
-               /* If this happens, please email me (arjan@fenrus.demon.nl)
-                  the image concerned. */
-               (*buf)++;
-               (*size)--;
-               break;
-       case 6:
-               (*size) = 0;
        }
+
+       context->BufferDone = 0;
+       return TRUE;
 }
 
 /*
@@ -657,64 +1016,113 @@ void DoCompressedByte(struct bmp_progressive_state *context, guchar ** buf,
  * buf - new image data
  * size - length of new image data
  *
- * append image data onto inrecrementally built output image
+ * append image data onto incrementally built output image
  */
-gboolean gdk_pixbuf__bmp_image_load_increment(gpointer data, guchar * buf, guint size)
+static gboolean
+gdk_pixbuf__bmp_image_load_increment(gpointer data,
+                                     const guchar * buf,
+                                     guint size,
+                                     GError **error)
 {
        struct bmp_progressive_state *context =
            (struct bmp_progressive_state *) data;
 
        gint BytesToCopy;
 
+       if (context->read_state == READ_STATE_DONE)
+               return TRUE;
+       else if (context->read_state == READ_STATE_ERROR)
+               return FALSE;
+
        while (size > 0) {
-               g_assert(context->LineDone >= 0);
-               if (context->HeaderDone < context->HeaderSize) {        /* We still 
+               if (context->BufferDone < context->BufferSize) {        /* We still
                                                                           have headerbytes to do */
                        BytesToCopy =
-                           context->HeaderSize - context->HeaderDone;
+                           context->BufferSize - context->BufferDone;
                        if (BytesToCopy > size)
                                BytesToCopy = size;
 
-                       memmove(context->HeaderBuf + context->HeaderDone,
-                              buf, BytesToCopy);
+                       memmove(context->buff + context->BufferDone,
+                               buf, BytesToCopy);
 
                        size -= BytesToCopy;
                        buf += BytesToCopy;
-                       context->HeaderDone += BytesToCopy;
-
-               } else if (context->Compressed) {
-                       /* Compression is done 1 byte at a time for now */
-                       DoCompressedByte(context, &buf, &size);
+                       context->BufferDone += BytesToCopy;
 
-               } else {
-                       /* Uncompressed pixeldata */
-                       BytesToCopy =
-                           context->LineWidth - context->LineDone;
-                       if (BytesToCopy > size)
-                               BytesToCopy = size;
+                       if (context->BufferDone != context->BufferSize)
+                               break;
+               }
 
-                       if (BytesToCopy > 0) {
-                               memmove(context->LineBuf +
-                                      context->LineDone, buf,
-                                      BytesToCopy);
+               switch (context->read_state) {
+               case READ_STATE_HEADERS:
+                       if (!DecodeHeader (context->buff,
+                                          context->buff + 14, context,
+                                          error))
+                               return FALSE;
 
-                               size -= BytesToCopy;
-                               buf += BytesToCopy;
-                               context->LineDone += BytesToCopy;
-                       }
-                       if ((context->LineDone >= context->LineWidth) &&
-                           (context->LineWidth > 0))
-                               OneLine(context);
+                       break;
 
+               case READ_STATE_PALETTE:
+                       if (!DecodeColormap (context->buff, context, error))
+                               return FALSE;
+                       break;
 
-               }
+               case READ_STATE_BITMASKS:
+                       if (!decode_bitmasks (context->buff, context, error))
+                               return FALSE;
+                       break;
 
-               if (context->HeaderDone >= 14 + 40)
-                       DecodeHeader(context->HeaderBuf,
-                                    context->HeaderBuf + 14, context);
+               case READ_STATE_DATA:
+                       if (context->Compressed == BI_RGB || context->Compressed == BI_BITFIELDS)
+                               OneLine (context);
+                       else if (!DoCompressed (context, error))
+                               return FALSE;
 
+                       break;
+               case READ_STATE_DONE:
+                       return TRUE;
+                       break;
 
+               default:
+                       g_assert_not_reached ();
+               }
        }
 
        return TRUE;
 }
+
+void
+MODULE_ENTRY (bmp, fill_vtable) (GdkPixbufModule *module)
+{
+       module->begin_load = gdk_pixbuf__bmp_image_begin_load;
+       module->stop_load = gdk_pixbuf__bmp_image_stop_load;
+       module->load_increment = gdk_pixbuf__bmp_image_load_increment;
+}
+
+void
+MODULE_ENTRY (bmp, fill_info) (GdkPixbufFormat *info)
+{
+       static GdkPixbufModulePattern signature[] = {
+               { "BM", NULL, 100 },
+               { NULL, NULL, 0 }
+       };
+       static gchar * mime_types[] = {
+               "image/bmp",
+               "image/x-bmp",
+               "image/x-MS-bmp",
+               NULL
+       };
+       static gchar * extensions[] = {
+               "bmp",
+               NULL
+       };
+
+       info->name = "bmp";
+       info->signature = signature;
+       info->description = N_("The BMP image format");
+       info->mime_types = mime_types;
+       info->extensions = extensions;
+       info->flags = 0;
+       info->license = "LGPL";
+}
+