]> Pileus Git - ~andy/gtk/blobdiff - gdk-pixbuf/io-pnm.c
[quartz] Delete the typedef of GdkDevicePrivate
[~andy/gtk] / gdk-pixbuf / io-pnm.c
index c06eb8fbbb2d78a960a0f02b6d7eba0aa9dee2f2..f86826e6788348fb969772a5c4598ec140ac3f2e 100644 (file)
@@ -1,8 +1,10 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /* GdkPixbuf library - PNM image loader
  *
  * Copyright (C) 1999 Red Hat, Inc.
  *
- * Authors: Michael Fulbright <drmike@redhat.com>
+ * Authors: Jeffrey Stedfast <fejj@helixcode.com>
+ *          Michael Fulbright <drmike@redhat.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
  * Boston, MA 02111-1307, USA.
  */
 
-#include <config.h>
-#include <ctype.h>
+#include "config.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <setjmp.h>
-#include <jpeglib.h>
-#include "gdk-pixbuf.h"
+#include "gdk-pixbuf-private.h"
 #include "gdk-pixbuf-io.h"
 
-\f
 
 #define PNM_BUF_SIZE 4096
 
-#define PNM_SUSPEND    0
-#define PNM_OK         1
 #define PNM_FATAL_ERR  -1
+#define PNM_SUSPEND     0
+#define PNM_OK          1
 
 typedef enum {
-       PNM_FORMAT_PGM,
+       PNM_FORMAT_PGM = 1,
        PNM_FORMAT_PGM_RAW,
        PNM_FORMAT_PPM,
        PNM_FORMAT_PPM_RAW,
@@ -48,48 +47,53 @@ typedef enum {
 } PnmFormat;
 
 typedef struct {
-       guchar    buffer[PNM_BUF_SIZE];
-       guchar    *next_byte;
-       guint     bytes_left;
+       guchar buffer[PNM_BUF_SIZE];
+       guchar *byte;
+       guint nbytes;
 } PnmIOBuffer;
 
 typedef struct {
-       ModuleUpdatedNotifyFunc  updated_func;
-       ModulePreparedNotifyFunc prepared_func;
-       gpointer                 user_data;
+       GdkPixbufModuleUpdatedFunc updated_func;
+       GdkPixbufModulePreparedFunc prepared_func;
+       GdkPixbufModuleSizeFunc size_func;
+       gpointer user_data;
        
-       GdkPixbuf                *pixbuf;
-       guchar                   *pixels; /* incoming pixel data buffer */
-       guchar                   *dptr;   /* current position in pixbuf */
-
-       PnmIOBuffer              inbuf;
-
-       guint                    width;
-       guint                    height;
-       guint                    maxval;
-       PnmFormat                type;
+       GdkPixbuf *pixbuf;
+       guchar *pixels;        /* incoming pixel data buffer */
+       guchar *dptr;          /* current position in pixbuf */
+       
+       PnmIOBuffer inbuf;
+       
+       guint width;
+       guint height;
+       guint maxval;
+       guint rowstride;
+       PnmFormat type;
+       
+       guint output_row;      /* last row to be completed */
+       guint output_col;
+       gboolean did_prescan;  /* are we in image data yet? */
+       gboolean got_header;   /* have we loaded pnm header? */
+       
+       guint scan_state;
 
-       guint                    output_row;  /* last row to be completed */
-       guint                    output_col;
-       gboolean                 did_prescan;  /* are we in image data yet? */
-       gboolean                 got_header;  /* have we loaded jpeg header? */
+       GError **error;
+       
 } PnmLoaderContext;
 
-GdkPixbuf *image_load (FILE *f);
-gpointer image_begin_load (ModulePreparedNotifyFunc func, 
-                          ModuleUpdatedNotifyFunc func2, gpointer user_data);
-void image_stop_load (gpointer context);
-gboolean image_load_increment(gpointer context, guchar *buf, guint size);
-
-static void explode_bitmap_into_buf (PnmLoaderContext *context);
-static void explode_gray_into_buf (PnmLoaderContext *context);
+static GdkPixbuf   *gdk_pixbuf__pnm_image_load          (FILE *f, GError **error);
+static gpointer    gdk_pixbuf__pnm_image_begin_load     (GdkPixbufModuleSizeFunc size_func, 
+                                                         GdkPixbufModulePreparedFunc func, 
+                                                        GdkPixbufModuleUpdatedFunc func2,
+                                                        gpointer user_data,
+                                                        GError **error);
+static gboolean    gdk_pixbuf__pnm_image_stop_load      (gpointer context, GError **error);
+static gboolean    gdk_pixbuf__pnm_image_load_increment (gpointer context,
+                                                        const guchar *buf, guint size,
+                                                        GError **error);
 
-/* Destroy notification function for the libart pixbuf */
-static void
-free_buffer (gpointer user_data, gpointer data)
-{
-       free (data);
-}
+static void explode_bitmap_into_buf              (PnmLoaderContext *context);
+static void explode_gray_into_buf                (PnmLoaderContext *context);
 
 
 /* explode bitmap data into rgb components         */
@@ -104,32 +108,32 @@ explode_bitmap_into_buf (PnmLoaderContext *context)
        guchar *from, *to, data;
        gint bit;
        guchar *dptr;
-       gint wid, x, y;
-
+       gint wid, x;
+       
        g_return_if_fail (context != NULL);
        g_return_if_fail (context->dptr != NULL);
-
+       
        /* I'm no clever bit-hacker so I'm sure this can be optimized */
        dptr = context->dptr;
-       y    = context->output_row;
        wid  = context->width;
-
-       from = dptr + (wid - 1)/8;
+       
+       from = dptr + ((wid - 1) / 8);
        to   = dptr + (wid - 1) * 3;
-       bit  = 7 - (((y+1)*wid - 1) % 8);
-
+       bit  = 7 - ((wid-1) % 8);
+       
        /* get first byte and align properly */
        data = from[0];
        for (j = 0; j < bit; j++, data >>= 1);
-
+       
        for (x = wid-1; x >= 0; x--) {
-
-               to[0] = to[1] = to[2] = (data & 1) ? 0x00 : 0xff;
-
+/*             g_print ("%c",  (data & 1) ? '*' : ' '); */
+               
+               to[0] = to[1] = to[2] = (data & 0x01) ? 0x00 : 0xff;
+               
                to -= 3;
                bit++;
-
-               if (bit > 7) {
+               
+               if (bit > 7 && x > 0) {
                        from--;
                        data = from[0];
                        bit = 0;
@@ -137,6 +141,8 @@ explode_bitmap_into_buf (PnmLoaderContext *context)
                        data >>= 1;
                }
        }
+       
+/*     g_print ("\n"); */
 }
 
 /* explode gray image row into rgb components in pixbuf */
@@ -146,10 +152,10 @@ explode_gray_into_buf (PnmLoaderContext *context)
        gint j;
        guchar *from, *to;
        guint w;
-
+       
        g_return_if_fail (context != NULL);
        g_return_if_fail (context->dptr != NULL);
-
+       
        /* Expand grey->colour.  Expand from the end of the
         * memory down, so we can use the same buffer.
         */
@@ -165,557 +171,630 @@ explode_gray_into_buf (PnmLoaderContext *context)
        }
 }
 
-/* skip over whitespace in file from current pos.                      */
-/* also skips comments                                                 */
-/* returns pointer to first non-whitespace char hit or, or NULL if     */
-/* we ran out of data w/o hitting a whitespace                         */
-/* internal pointer in inbuf isnt moved ahead in this case             */
-static guchar *
-skip_ahead_whitespace (PnmIOBuffer *inbuf)
-{
-       gboolean in_comment;
-       guchar *ptr;
-       guint num_left;
-       
-       g_return_val_if_fail (inbuf != NULL, NULL);
-       g_return_val_if_fail (inbuf->next_byte != NULL, NULL);
-       
-       in_comment = FALSE;
-       num_left = inbuf->bytes_left;
-       ptr      = inbuf->next_byte;
-       while (num_left > 0) {
-               if (in_comment) {
-                       if (*ptr == '\n')
-                               in_comment = FALSE;
-               } else if (*ptr == '#') {
-                       in_comment = TRUE;
-               } else if (!isspace (*ptr)) {
-                       inbuf->bytes_left -= (ptr-inbuf->next_byte);
-                       inbuf->next_byte   = ptr;
-                       return ptr;
-               }
-               ptr ++;
-               num_left--;
-       }
-       return NULL;
-}
-
-/* reads into buffer until we hit whitespace in file from current pos,     */
-/* return NULL if ran out of data                                          */
-/* advances inbuf if successful                                            */
-static guchar *
-read_til_whitespace (PnmIOBuffer *inbuf, guchar *buf, guint size)
+/* skip over whitespace and comments in input buffer */
+static gint
+pnm_skip_whitespace (PnmIOBuffer *inbuf, GError **error)
 {
-       guchar *p;
-       guchar *ptr;
-       guint num_left;
-
-       g_return_val_if_fail (inbuf != NULL, NULL);
-       g_return_val_if_fail (inbuf->next_byte != NULL, NULL);
-       
-       p = buf;
-       num_left = inbuf->bytes_left;
-       ptr      = inbuf->next_byte;
-       while (num_left > 0 && (p-buf)+1 < size) {
-               if (isspace (*ptr)) {
-                       *p = '\0';
-                       inbuf->bytes_left = num_left;
-                       inbuf->next_byte  = ptr;
-                       return ptr;
-               } else {
-                       *p = *ptr;
-                       p++;
-                       ptr++;
-                       num_left--;
+       register guchar *inptr;
+       guchar *inend;
+       
+       g_return_val_if_fail (inbuf != NULL, PNM_FATAL_ERR);
+       g_return_val_if_fail (inbuf->byte != NULL, PNM_FATAL_ERR);
+       
+       inend = inbuf->byte + inbuf->nbytes;
+       inptr = inbuf->byte;
+       
+       for ( ; inptr < inend; inptr++) {
+               if (*inptr == '#') {
+                       /* in comment - skip to the end of this line */
+                       for ( ; *inptr != '\n' && inptr < inend; inptr++)
+                               ;
+                       
+                       if ( inptr == inend || *inptr != '\n' ) {
+                               /* couldn't read whole comment */
+                               return PNM_SUSPEND;
+                       }
+                       
+               } else if (!g_ascii_isspace (*inptr)) {
+                       inbuf->byte = inptr;
+                       inbuf->nbytes = (guint) (inend - inptr);
+                       return PNM_OK;
                }
        }
-       return NULL;
+       
+       inbuf->byte = inptr;
+       inbuf->nbytes = (guint) (inend - inptr);
+       
+       return PNM_SUSPEND;
 }
 
-/* read next number from buffer  */
-/* -1 if failed, 0 if successful */
+/* read next number from buffer */
 static gint
-read_next_number (PnmIOBuffer *inbuf, guint *value)
+pnm_read_next_value (PnmIOBuffer *inbuf, gint max_length, guint *value, GError **error)
 {
-       guchar *tmpptr;
-       guchar *old_next_byte;
-       gchar  *errptr;
-       guint  old_bytes_left;
-       guchar buf[128];
-
-       g_return_val_if_fail (inbuf != NULL, -1);
-       g_return_val_if_fail (inbuf->next_byte != NULL, -1);
-       g_return_val_if_fail (value != NULL, -1);
-
-       old_next_byte  = inbuf->next_byte;
-       old_bytes_left = inbuf->bytes_left;
-
-       if ((tmpptr = skip_ahead_whitespace (inbuf)) == NULL)
-               return -1;
-
-       if ((tmpptr = read_til_whitespace (inbuf, buf, 128)) == NULL) {
-               inbuf->next_byte  = old_next_byte;
-               inbuf->bytes_left = old_bytes_left;
-               return -1;
-       }
+       register guchar *inptr, *word, *p;
+       guchar *inend, buf[129];
+       gchar *endptr;
+       gint retval;
+       glong result;
+       
+       g_return_val_if_fail (inbuf != NULL, PNM_FATAL_ERR);
+       g_return_val_if_fail (inbuf->byte != NULL, PNM_FATAL_ERR);
+       g_return_val_if_fail (value != NULL, PNM_FATAL_ERR);
        
-       *value = strtol (buf, &errptr, 10);
+       if (max_length < 0)
+               max_length = 128;
 
-       if (*errptr != '\0') {
-               inbuf->next_byte  = old_next_byte;
-               inbuf->bytes_left = old_bytes_left;
-               return -1;
+       /* skip white space */
+       if ((retval = pnm_skip_whitespace (inbuf, error)) != PNM_OK)
+               return retval;
+       
+       inend = inbuf->byte + inbuf->nbytes;
+       inptr = inbuf->byte;
+       
+       /* copy this pnm 'word' into a temp buffer */
+       for (p = inptr, word = buf; (p < inend) && !g_ascii_isspace (*p) && (*p != '#') && (p - inptr < max_length); p++, word++)
+               *word = *p;
+       *word = '\0';
+       
+       /* hmmm, there must be more data to this 'word' */
+       if (p == inend || (!g_ascii_isspace (*p) && (*p != '#')  && (p - inptr < max_length)))
+           return PNM_SUSPEND;
+       
+       /* get the value */
+       result = strtol ((gchar *)buf, &endptr, 10);
+       if (*endptr != '\0' || result < 0 || result > G_MAXUINT) {
+               g_set_error_literal (error,
+                                     GDK_PIXBUF_ERROR,
+                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                     _("PNM loader expected to find an integer, but didn't"));
+               return PNM_FATAL_ERR;
        }
+       *value = result;
 
-       return 0;
+       inbuf->byte = p;
+       inbuf->nbytes = (guint) (inend - p);
+       
+       return PNM_OK;
 }
 
 /* returns PNM_OK, PNM_SUSPEND, or PNM_FATAL_ERR */
 static gint
 pnm_read_header (PnmLoaderContext *context)
 {
-       guchar *old_next_byte;
-       guint  old_bytes_left;
        PnmIOBuffer *inbuf;
-       guint  w, h;
-       gint rc;
-       PnmFormat  type;
-
-       g_return_val_if_fail (context != NULL, PNM_FATAL_ERR);
-
-       inbuf = &context->inbuf;
-       old_bytes_left = inbuf->bytes_left;
-       old_next_byte  = inbuf->next_byte;
+       gint retval;
        
-       /* file must start with a 'P' followed by a numeral */
-       /* so loop till we get enough data to determine type*/
-       if (inbuf->bytes_left < 2)
-               return PNM_SUSPEND;
+       g_return_val_if_fail (context != NULL, PNM_FATAL_ERR);
        
-       if (*inbuf->next_byte != 'P')
-               return PNM_FATAL_ERR;
+       inbuf = &context->inbuf;
        
-       switch (*(inbuf->next_byte+1)) {
-       case '1':
-               type = PNM_FORMAT_PBM;
-               break;
-       case '2':
-               type = PNM_FORMAT_PGM;
-               break;
-       case '3':
-               type = PNM_FORMAT_PPM;
-               break;
-       case '4':
-               type = PNM_FORMAT_PBM_RAW;
-               break;
-       case '5':
-               type = PNM_FORMAT_PGM_RAW;
-               break;
-       case '6':
-               type = PNM_FORMAT_PPM_RAW;
-               break;
-       default:
-               return PNM_FATAL_ERR;
+       if (!context->type) {
+               /* file must start with a 'P' followed by a numeral  */
+               /* so loop till we get enough data to determine type */
+               if (inbuf->nbytes < 2)
+                       return PNM_SUSPEND;
+               
+               if (*inbuf->byte != 'P') {
+                       g_set_error_literal (context->error,
+                                             GDK_PIXBUF_ERROR,
+                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                             _("PNM file has an incorrect initial byte"));
+                       return PNM_FATAL_ERR;
+               }
+               
+               inbuf->byte++;
+               inbuf->nbytes--;
+               
+               switch (*inbuf->byte) {
+               case '1':
+                       context->type = PNM_FORMAT_PBM;
+                       break;
+               case '2':
+                       context->type = PNM_FORMAT_PGM;
+                       break;
+               case '3':
+                       context->type = PNM_FORMAT_PPM;
+                       break;
+               case '4':
+                       context->type = PNM_FORMAT_PBM_RAW;
+                       break;
+               case '5':
+                       context->type = PNM_FORMAT_PGM_RAW;
+                       break;
+               case '6':
+                       context->type = PNM_FORMAT_PPM_RAW;
+                       break;
+               default:
+                       g_set_error_literal (context->error,
+                                             GDK_PIXBUF_ERROR,
+                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                             _("PNM file is not in a recognized PNM subformat"));
+                       return PNM_FATAL_ERR;
+               }
+               
+               if (!inbuf->nbytes)
+                       return PNM_SUSPEND;
+               
+               inbuf->byte++;
+               inbuf->nbytes--;
        }
        
-       context->type = type;
-
-       inbuf->next_byte  += 2;
-       inbuf->bytes_left -= 2;
-       
-       /* now read remainder of header */
-       if ((rc = read_next_number (inbuf, &w))) {
-               inbuf->next_byte = old_next_byte;
-               inbuf->bytes_left = old_bytes_left;
-               return PNM_SUSPEND;
+       if (!context->width) {
+               /* read the pixmap width */
+               guint width = 0;
+               
+               retval = pnm_read_next_value (inbuf, -1, &width,
+                                             context->error);
+               
+               if (retval != PNM_OK) 
+                       return retval;
+               
+               if (!width) {
+                       g_set_error_literal (context->error,
+                                             GDK_PIXBUF_ERROR,
+                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                             _("PNM file has an image width of 0"));
+                       return PNM_FATAL_ERR;
+               }
+               
+               context->width = width;
        }
        
-       if ((rc = read_next_number (inbuf, &h))) {
-               inbuf->next_byte = old_next_byte;
-               inbuf->bytes_left = old_bytes_left;
-               return PNM_SUSPEND;
+       if (!context->height) {
+               /* read the pixmap height */
+               guint height = 0;
+               
+               retval = pnm_read_next_value (inbuf, -1, &height,
+                                             context->error);
+               
+               if (retval != PNM_OK)
+                       return retval;
+               
+               if (!height) {
+                       g_set_error_literal (context->error,
+                                             GDK_PIXBUF_ERROR,
+                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                             _("PNM file has an image height of 0"));
+                       return PNM_FATAL_ERR;
+               }
+               
+               context->height = height;
        }
        
-       context->width  = w;
-       context->height = h;
-       
-       switch (type) {
+       switch (context->type) {
        case PNM_FORMAT_PPM:
        case PNM_FORMAT_PPM_RAW:
        case PNM_FORMAT_PGM:
        case PNM_FORMAT_PGM_RAW:
-               if ((rc = read_next_number (inbuf, &context->maxval)) < 0) {
-                       inbuf->next_byte = old_next_byte;
-                       inbuf->bytes_left = old_bytes_left;
-                       return PNM_SUSPEND;
+               if (!context->maxval) {
+                       retval = pnm_read_next_value (inbuf, -1, &context->maxval,
+                                                     context->error);
+                       
+                       if (retval != PNM_OK)
+                               return retval;
+                       
+                       if (context->maxval == 0) {
+                               g_set_error_literal (context->error,
+                                                     GDK_PIXBUF_ERROR,
+                                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                                     _("Maximum color value in PNM file is 0"));
+                               return PNM_FATAL_ERR;
+                       }
+
+                       if (context->maxval > 65535) {
+                               g_set_error_literal (context->error,
+                                                     GDK_PIXBUF_ERROR,
+                                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                                     _("Maximum color value in PNM file is too large"));
+                               return PNM_FATAL_ERR;
+                       }
+
                }
                break;
        default:
                break;
        }
-
+       
        return PNM_OK;
 }
 
-
 static gint
 pnm_read_raw_scanline (PnmLoaderContext *context)
 {
-       guint  numpix;
-       guint  numbytes, offset;
        PnmIOBuffer *inbuf;
-
+       guint numbytes, offset;
+       guint numpix = 0;
+       guchar *dest;
+       guint i;
+       
        g_return_val_if_fail (context != NULL, PNM_FATAL_ERR);
-
+       
        inbuf = &context->inbuf;
-
+       
        switch (context->type) {
        case PNM_FORMAT_PBM_RAW:
-               numpix = inbuf->bytes_left * 8;
+               numpix = inbuf->nbytes * 8;
                break;
        case PNM_FORMAT_PGM_RAW:
-               numpix = inbuf->bytes_left;
+               numpix = inbuf->nbytes;
                break;
        case PNM_FORMAT_PPM_RAW:
-               numpix = inbuf->bytes_left/3;
+               numpix = inbuf->nbytes / 3;
                break;
        default:
-               g_warning ("io-pnm.c: Illegal raw pnm type!\n");
+               g_set_error_literal (context->error,
+                                     GDK_PIXBUF_ERROR,
+                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                     _("Raw PNM image type is invalid"));
                return PNM_FATAL_ERR;
-               break;
        }
+       if(context->maxval>255) 
+               numpix/=2;
        
        numpix = MIN (numpix, context->width - context->output_col);
        
-       if (numpix == 0)
+       if (!numpix)
                return PNM_SUSPEND;
        
-       context->dptr = context->pixels + 
-               context->output_row * context->width * 3;
+       context->dptr = context->pixels + context->output_row * context->rowstride;
        
        switch (context->type) {
        case PNM_FORMAT_PBM_RAW:
-               numbytes = numpix/8;
-               offset = context->output_col/8;
+               numbytes = (numpix / 8) + ((numpix % 8) ? 1 : 0);
+               offset = context->output_col / 8;
                break;
        case PNM_FORMAT_PGM_RAW:
                numbytes = numpix;
                offset = context->output_col;
                break;
        case PNM_FORMAT_PPM_RAW:
-               numbytes = numpix*3;
-               offset = context->output_col*3;
+               numbytes = numpix * 3;
+               offset = context->output_col * 3;
                break;
        default:
-               g_warning ("io-pnm.c: Illegal raw pnm type!\n");
-               break;
+               g_set_error_literal (context->error,
+                                     GDK_PIXBUF_ERROR,
+                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                     _("Raw PNM image type is invalid"));
+               return PNM_FATAL_ERR;
        }
+       if(context->maxval>255) 
+               numbytes*=2;                            
        
-       memcpy (context->dptr + offset, inbuf->next_byte, numbytes);
+       switch (context->type) {
+       case PNM_FORMAT_PBM_RAW:
+               dest = context->dptr + offset;          
+               memcpy (dest, inbuf->byte, numbytes);
+               break;
+       case PNM_FORMAT_PGM_RAW:
+       case PNM_FORMAT_PPM_RAW:
+               dest = context->dptr + offset;
+               
+               if (context->maxval == 255) {
+                       /* special-case optimization */
+                       memcpy (dest, inbuf->byte, numbytes);
+               } else if(context->maxval == 65535) {
+                       /* optimized version of the next case */
+                       for(i=0; i < numbytes ; i+=2) {
+                               *dest++=inbuf->byte[i];
+                       }
+               } else if(context->maxval > 255) {
+                       /* scale down to 256 colors */
+                       for(i=0; i < numbytes ; i+=2) {
+                               guint v=inbuf->byte[i]*256+inbuf->byte[i+1];
+                               *dest++=v*255/context->maxval;
+                       }
+               } else {
+                       for (i = 0; i < numbytes; i++) {
+                               guchar *byte = inbuf->byte + i;
+                               
+                               /* scale the color to an 8-bit color depth */
+                               if (*byte > context->maxval)
+                                       *dest++ = 255;
+                               else
+                                       *dest++ = (guchar) (255 * *byte / context->maxval);
+                       }
+               }
+               break;
+       default:
+               g_set_error_literal (context->error,
+                                     GDK_PIXBUF_ERROR,
+                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                     _("Raw PNM image type is invalid"));
+               return PNM_FATAL_ERR;
+       }
        
-       inbuf->next_byte  += numbytes;
-       inbuf->bytes_left -= numbytes;
+       inbuf->byte += numbytes;
+       inbuf->nbytes -= numbytes;
        
        context->output_col += numpix;
        if (context->output_col == context->width) {
-               if ( context->type == PNM_FORMAT_PBM_RAW )
-                       explode_bitmap_into_buf(context);
-               else if ( context->type == PNM_FORMAT_PGM_RAW )
+               if (context->type == PNM_FORMAT_PBM_RAW)
+                       explode_bitmap_into_buf (context);
+               else if (context->type == PNM_FORMAT_PGM_RAW)
                        explode_gray_into_buf (context);
                
                context->output_col = 0;
                context->output_row++;
-               
        } else {
                return PNM_SUSPEND;
        }
-
+       
        return PNM_OK;
 }
 
-
 static gint
-pnm_read_ascii_scanline (PnmLoaderContext *context)
+pnm_read_ascii_mono_scanline (PnmLoaderContext *context)
 {
-       guint  offset;
-       gint   rc;
-       guint  value, numval, i;
-       guchar data;
-       guchar mask;
-       guchar *old_next_byte, *dptr;
-       guint  old_bytes_left;
        PnmIOBuffer *inbuf;
+       guint value;
+       gint retval;
+       guchar *dptr;
+       gint max_length;
 
-       g_return_val_if_fail (context != NULL, PNM_FATAL_ERR);
+       if (context->type == PNM_FORMAT_PBM)
+               max_length = 1;
+       else
+               max_length = -1;
 
        inbuf = &context->inbuf;
 
-       context->dptr = context->pixels + 
-               context->output_row * context->width * 3;
-
-       switch (context->type) {
-       case PNM_FORMAT_PBM:
-               numval = 8;
-               offset = context->output_col/8;
-               break;
-       case PNM_FORMAT_PGM:
-               numval = 1;
-               offset = context->output_col;
-               break;
-       case PNM_FORMAT_PPM:
-               numval = 3;
-               offset = context->output_col*3;
-               break;
-               
-       default:
-               g_warning ("Can't happen\n");
-               return PNM_FATAL_ERR;
-       }
+       context->dptr = context->pixels + context->output_row * context->rowstride;
 
-       dptr = context->dptr + offset;
+       dptr = context->dptr + context->output_col * 3;
 
        while (TRUE) {
+               retval = pnm_read_next_value (inbuf, max_length, &value, context->error);
+               if (retval != PNM_OK)
+                       return retval;
+
                if (context->type == PNM_FORMAT_PBM) {
-                       mask = 0x80;
-                       data = 0;
+                       value = value ? 0 : 0xff;
                }
+               else {
+                       /* scale the color up or down to an 8-bit color depth */
+                       if (value > context->maxval)
+                               value = 255;
+                       else
+                               value = (guchar)(255 * value / context->maxval);
+               }
+                       
+               *dptr++ = value;
+               *dptr++ = value;
+               *dptr++ = value;
 
-               old_next_byte  = inbuf->next_byte;
-               old_bytes_left = inbuf->bytes_left;
-               
-               for (i=0; i<numval; i++) {
-                       if ((rc = read_next_number (inbuf, &value))) {
-                               inbuf->next_byte  = old_next_byte;
-                               inbuf->bytes_left = old_bytes_left;
-                               return PNM_SUSPEND;
-                       }
-                       switch (context->type) {
-                       case PNM_FORMAT_PBM:
-                               if (value)
-                                       data |= mask;
-                               mask >>= 1;
-                               
-                               break;
-                       case PNM_FORMAT_PGM:
-                               *dptr++ = (guchar)(255.0*((double)value/(double)context->maxval));
-                               break;
-                       case PNM_FORMAT_PPM:
-                               *dptr++ = (guchar)(255.0*((double)value/(double)context->maxval));
-                               break;
-                       default:
-                               g_warning ("io-pnm.c: Illegal raw pnm type!\n");
-                               break;
+               context->output_col++;
+
+               if (context->output_col == context->width) {
+                       context->output_col = 0;
+                       context->output_row++;
+                       break;
+               }
+       }
+
+       return PNM_OK;
+}
+
+static gint
+pnm_read_ascii_color_scanline (PnmLoaderContext *context)
+{
+       PnmIOBuffer *inbuf;
+       guint value, i;
+       guchar *dptr;
+       gint retval;
+       
+       inbuf = &context->inbuf;
+       
+       context->dptr = context->pixels + context->output_row * context->rowstride;
+       
+       dptr = context->dptr + context->output_col * 3 + context->scan_state;
+       
+       while (TRUE) {
+               for (i = context->scan_state; i < 3; i++) {
+                       retval = pnm_read_next_value (inbuf, -1, &value, context->error);
+                       if (retval != PNM_OK) {
+                               /* save state and return */
+                               context->scan_state = i;
+                               return retval;
                        }
+                       
+                       if (value > context->maxval)
+                               *dptr++ = 255;
+                       else
+                               *dptr++ = (guchar)(255 * value / context->maxval);
                }
                
-               if (context->type == PNM_FORMAT_PBM)
-                       *dptr++ = value;
-               
+               context->scan_state = 0;
                context->output_col++;
-
+               
                if (context->output_col == context->width) {
-                       if ( context->type == PNM_FORMAT_PBM )
-                               explode_bitmap_into_buf(context);
-                       else if ( context->type == PNM_FORMAT_PGM )
-                               explode_gray_into_buf (context);
-
                        context->output_col = 0;
                        context->output_row++;
                        break;
                }
-
        }
-
+       
        return PNM_OK;
 }
 
-/* returns 1 if a scanline was converted,  0 means we ran out of data */
+/* returns 1 if a scanline was converted, 0 means we ran out of data */
 static gint
 pnm_read_scanline (PnmLoaderContext *context)
 {
-       gint   rc;
-
+       gint retval;
+       
        g_return_val_if_fail (context != NULL, PNM_FATAL_ERR);
-
+       
        /* read in image data */
        /* for raw formats this is trivial */
        switch (context->type) {
        case PNM_FORMAT_PBM_RAW:
        case PNM_FORMAT_PGM_RAW:
        case PNM_FORMAT_PPM_RAW:
-               rc = pnm_read_raw_scanline (context);
-               if (rc == PNM_SUSPEND)
-                       return rc;
+               retval = pnm_read_raw_scanline (context);
+               if (retval != PNM_OK)
+                       return retval;
                break;
-
        case PNM_FORMAT_PBM:
        case PNM_FORMAT_PGM:
+               retval = pnm_read_ascii_mono_scanline (context);
+               if (retval != PNM_OK)
+                       return retval;
+               break;          
        case PNM_FORMAT_PPM:
-               rc = pnm_read_ascii_scanline (context);
-               if (rc == PNM_SUSPEND)
-                       return rc;
+               retval = pnm_read_ascii_color_scanline (context);
+               if (retval != PNM_OK)
+                       return retval;
                break;
-
        default:
-               g_warning ("Cannot load these image types (yet)\n");
+               g_set_error_literal (context->error,
+                                     GDK_PIXBUF_ERROR,
+                                     GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
+                                     _("PNM image loader does not support this PNM subformat"));
+
                return PNM_FATAL_ERR;
        }
-
+       
        return PNM_OK;
 }
 
 /* Shared library entry point */
-GdkPixbuf *
-image_load (FILE *f)
+static GdkPixbuf *
+gdk_pixbuf__pnm_image_load (FILE *f, GError **error)
 {
-       guint  nbytes;
-       gint   rc;
-
        PnmLoaderContext context;
        PnmIOBuffer *inbuf;
-
+       gint nbytes;
+       gint retval;
+       
        /* pretend to be doing progressive loading */
-       context.updated_func = context.prepared_func = NULL;
+       context.updated_func = NULL;
+       context.prepared_func = NULL;
        context.user_data = NULL;
-       context.inbuf.bytes_left = 0;
-       context.inbuf.next_byte  = NULL;
-       context.pixels = context.pixbuf = NULL;
-       context.got_header = context.did_prescan = FALSE;
-
+       context.type = 0;
+       context.inbuf.nbytes = 0;
+       context.inbuf.byte = NULL;
+       context.width = 0;
+       context.height = 0;
+       context.maxval = 0;
+       context.pixels = NULL;
+       context.pixbuf = NULL;
+       context.got_header = FALSE;
+       context.did_prescan = FALSE;
+       context.scan_state = 0;
+       context.error = error;
+       
        inbuf = &context.inbuf;
-
+       
        while (TRUE) {
-               guint  num_to_read;
-
+               guint num_to_read;
+               
                /* keep buffer as full as possible */
-               num_to_read = PNM_BUF_SIZE - inbuf->bytes_left;
-
-               if (inbuf->next_byte != NULL && inbuf->bytes_left > 0)
-                       memmove (inbuf->buffer, inbuf->next_byte, 
-                                inbuf->bytes_left);
-
-               nbytes = fread (inbuf->buffer+inbuf->bytes_left, 
-                               1, num_to_read, f);
-               inbuf->bytes_left += nbytes;
-               inbuf->next_byte   = inbuf->buffer;
+               num_to_read = PNM_BUF_SIZE - inbuf->nbytes;
                
-               /* ran out of data and we haven't exited main loop */
-               if (inbuf->bytes_left == 0) {
+               if (inbuf->byte != NULL && inbuf->nbytes > 0)
+                       memmove (inbuf->buffer, inbuf->byte, inbuf->nbytes);
+               
+               nbytes = fread (inbuf->buffer + inbuf->nbytes, 1, num_to_read, f);
+               
+               /* error checking */
+               if (nbytes == 0) {
+                       /* we ran out of data? */
                        if (context.pixbuf)
-                               gdk_pixbuf_unref (context.pixbuf);
-                       g_warning ("io-pnm.c: Ran out of data...\n");
+                               g_object_unref (context.pixbuf);
+                       g_set_error_literal (error,
+                                             GDK_PIXBUF_ERROR,
+                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                             _("Premature end-of-file encountered"));
                        return NULL;
                }
-
+               
+               inbuf->nbytes += nbytes;
+               inbuf->byte = inbuf->buffer;
+               
                /* get header if needed */
                if (!context.got_header) {
-               
-                       rc = pnm_read_header (&context);
-                       if (rc == PNM_FATAL_ERR)
+                       retval = pnm_read_header (&context);
+                       if (retval == PNM_FATAL_ERR)
                                return NULL;
-                       else if (rc == PNM_SUSPEND)
+                       else if (retval == PNM_SUSPEND)
                                continue;
-
+                       
                        context.got_header = TRUE;
                }
-
+               
                /* scan until we hit image data */
                if (!context.did_prescan) {
-                       if (skip_ahead_whitespace (inbuf) == NULL)
-                               continue;
-
+                       switch (context.type) {
+                       case PNM_FORMAT_PBM_RAW:
+                       case PNM_FORMAT_PGM_RAW:
+                       case PNM_FORMAT_PPM_RAW:
+                               if (inbuf->nbytes <= 0)
+                                       continue;
+                               /* raw formats require exactly one whitespace */
+                               if (!g_ascii_isspace(*(inbuf->byte))) 
+                                       {
+                                               g_set_error_literal (error,
+                                                                     GDK_PIXBUF_ERROR,
+                                                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                                                     _("Raw PNM formats require exactly one whitespace before sample data"));
+                                               return NULL;
+                                       }
+                               inbuf->nbytes--;
+                               inbuf->byte++;
+                               break;
+                       default:
+                               retval = pnm_skip_whitespace (inbuf,
+                                                             context.error);
+                               if (retval == PNM_FATAL_ERR)
+                                       return NULL;
+                               else if (retval == PNM_SUSPEND)
+                                       continue;
+                               break;
+                       }
                        context.did_prescan = TRUE;
                        context.output_row = 0;
                        context.output_col = 0;
-
-                       context.pixbuf = gdk_pixbuf_new(ART_PIX_RGB, 
-                                                        /*have_alpha*/ FALSE,
-                                                        8, 
-                                                        context.width,
-                                                        context.height);
-
-
-                       if (context.pixbuf == NULL) {
+                       
+                       context.pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8,
+                                                        context.width, context.height);
+                       
+                       if (!context.pixbuf) {
                                /* Failed to allocate memory */
-                               g_error ("Couldn't allocate gdkpixbuf");
+                               g_set_error_literal (error,
+                                                     GDK_PIXBUF_ERROR,
+                                                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+                                                     _("Cannot allocate memory for loading PNM image"));
+                               return NULL;
                        }
 
-                       context.pixels = context.pixbuf->art_pixbuf->pixels;
+                       context.rowstride = context.pixbuf->rowstride;
+                       context.pixels = context.pixbuf->pixels;
                }
-
+               
                /* if we got here we're reading image data */
                while (context.output_row < context.height) {
-                       rc = pnm_read_scanline (&context);
-
-                       if (rc == PNM_SUSPEND) {
+                       retval = pnm_read_scanline (&context);
+                       
+                       if (retval == PNM_SUSPEND) {
                                break;
-                       } else if (rc == PNM_FATAL_ERR) {
+                       } else if (retval == PNM_FATAL_ERR) {
                                if (context.pixbuf)
-                                       gdk_pixbuf_unref (context.pixbuf);
-                               g_warning ("io-pnm.c: error reading rows..\n");
+                                       g_object_unref (context.pixbuf);
+
                                return NULL;
                        }
                }
-
+               
                if (context.output_row < context.height)
                        continue;
                else
                        break;
        }
-
-       return gdk_pixbuf_new_from_data (context.pixels, ART_PIX_RGB, FALSE,
-                                        context.width, context.height, 
-                                        context.width * 3, free_buffer, NULL);
-
-}
-
-#if 0
-/**** Progressive image loading handling *****/
-
-/* these routines required because we are acting as a source manager for */
-/* libjpeg. */
-static void
-init_source (j_decompress_ptr cinfo)
-{
-       my_src_ptr src = (my_src_ptr) cinfo->src;
-
-       src->skip_next = 0;
-}
-
-
-static void
-term_source (j_decompress_ptr cinfo)
-{
-       /* XXXX - probably should scream something has happened */
-}
-
-
-/* for progressive loading (called "I/O Suspension" by libjpeg docs) */
-/* we do nothing except return "FALSE"                               */
-static boolean
-fill_input_buffer (j_decompress_ptr cinfo)
-{
-       return FALSE;
-}
-
-
-static void
-skip_input_data (j_decompress_ptr cinfo, long num_bytes)
-{
-       my_src_ptr src = (my_src_ptr) cinfo->src;
-       long   num_can_do;
-
-       /* move as far as we can into current buffer */
-       /* then set skip_next to catch the rest      */
-       if (num_bytes > 0) {
-               num_can_do = MIN (src->pub.bytes_in_buffer, num_bytes);
-               src->pub.next_input_byte += (size_t) num_can_do;
-               src->pub.bytes_in_buffer -= (size_t) num_can_do;
-
-               src->skip_next = num_bytes - num_can_do;
-       }
+       
+       return context.pixbuf;
 }
-#endif
 
 /* 
  * func - called when we have pixmap created (but no image data)
@@ -723,24 +802,41 @@ skip_input_data (j_decompress_ptr cinfo, long num_bytes)
  * return context (opaque to user)
  */
 
-gpointer
-image_begin_load (ModulePreparedNotifyFunc prepared_func, 
-                 ModuleUpdatedNotifyFunc  updated_func,
-                 gpointer user_data)
+static gpointer
+gdk_pixbuf__pnm_image_begin_load (GdkPixbufModuleSizeFunc size_func, 
+                                  GdkPixbufModulePreparedFunc prepared_func, 
+                                 GdkPixbufModuleUpdatedFunc  updated_func,
+                                 gpointer user_data,
+                                 GError **error)
 {
        PnmLoaderContext *context;
-
-       context = g_new0 (PnmLoaderContext, 1);
+       
+       context = g_try_malloc (sizeof (PnmLoaderContext));
+       if (!context) {
+               g_set_error_literal (error, GDK_PIXBUF_ERROR, 
+                                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+                                     _("Insufficient memory to load PNM context struct"));
+               return NULL;
+       }
+       memset (context, 0, sizeof (PnmLoaderContext));
+       context->size_func = size_func;
        context->prepared_func = prepared_func;
        context->updated_func  = updated_func;
        context->user_data = user_data;
-       context->pixbuf = context->pixels = NULL;
+       context->width = 0;
+       context->height = 0;
+       context->maxval = 0;
+       context->pixbuf = NULL;
+       context->pixels = NULL;
        context->got_header = FALSE;
        context->did_prescan = FALSE;
+       context->scan_state = 0;
+       
+       context->inbuf.nbytes = 0;
+       context->inbuf.byte  = NULL;
 
-       context->inbuf.bytes_left = 0;
-       context->inbuf.next_byte  = NULL;
-
+       context->error = error;
+       
        return (gpointer) context;
 }
 
@@ -749,21 +845,36 @@ image_begin_load (ModulePreparedNotifyFunc prepared_func,
  *
  * free context, unref gdk_pixbuf
  */
-void
-image_stop_load (gpointer data)
+static gboolean
+gdk_pixbuf__pnm_image_stop_load (gpointer data,
+                                GError **error)
 {
        PnmLoaderContext *context = (PnmLoaderContext *) data;
-
-       g_return_if_fail (context != NULL);
-
+       gboolean retval = TRUE;
+       
+       g_return_val_if_fail (context != NULL, TRUE);
+       
        if (context->pixbuf)
-               gdk_pixbuf_unref (context->pixbuf);
+               g_object_unref (context->pixbuf);
 
+#if 0
+       /* We should ignore trailing newlines and we can't
+          generally complain about trailing stuff at all, since 
+          pnm allows to put multiple images in a file
+       */
+       if (context->inbuf.nbytes > 0) {
+               g_set_error_literal (error,
+                                     GDK_PIXBUF_ERROR,
+                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                     _("Unexpected end of PNM image data"));
+               retval = FALSE;
+       }
+#endif
+       
        g_free (context);
-}
-
-
 
+       return retval;
+}
 
 /*
  * context - from image_begin_load
@@ -772,121 +883,204 @@ image_stop_load (gpointer data)
  *
  * append image data onto inrecrementally built output image
  */
-gboolean
-image_load_increment (gpointer data, guchar *buf, guint size)
+static gboolean
+gdk_pixbuf__pnm_image_load_increment (gpointer data,
+                                     const guchar *buf, guint size,
+                                     GError **error)
 {
        PnmLoaderContext *context = (PnmLoaderContext *)data;
-       PnmIOBuffer      *inbuf;
-
-       guchar *old_next_byte;
-       guint  old_bytes_left;
-       guchar *bufhd;
-       guint  num_left, spinguard;
-       gint   rc;
-
+       PnmIOBuffer *inbuf;
+       const guchar *bufhd;
+       guint num_left, spinguard;
+       gint retval;
+       
        g_return_val_if_fail (context != NULL, FALSE);
        g_return_val_if_fail (buf != NULL, FALSE);
 
+       context->error = error;
+       
        bufhd = buf;
        inbuf = &context->inbuf;
-       old_bytes_left = inbuf->bytes_left;
-       old_next_byte  = inbuf->next_byte;
-
+       
        num_left = size;
        spinguard = 0;
        while (TRUE) {
                guint num_to_copy;
-
+               
                /* keep buffer as full as possible */
-               num_to_copy = MIN (PNM_BUF_SIZE - inbuf->bytes_left, num_left);
+               num_to_copy = MIN (PNM_BUF_SIZE - inbuf->nbytes, num_left);
                
                if (num_to_copy == 0)
                        spinguard++;
-
+               
                if (spinguard > 1)
                        return TRUE;
-
-               if (inbuf->next_byte != NULL && inbuf->bytes_left > 0)
-                       memmove (inbuf->buffer, inbuf->next_byte, 
-                                inbuf->bytes_left);
-
-               memcpy (inbuf->buffer + inbuf->bytes_left, bufhd, num_to_copy);
+               
+               if (inbuf->byte != NULL && inbuf->nbytes > 0)
+                       memmove (inbuf->buffer, inbuf->byte, inbuf->nbytes);
+               
+               memcpy (inbuf->buffer + inbuf->nbytes, bufhd, num_to_copy);
                bufhd += num_to_copy;
-               inbuf->bytes_left += num_to_copy;
-               inbuf->next_byte   = inbuf->buffer;
+               inbuf->nbytes += num_to_copy;
+               inbuf->byte = inbuf->buffer;
                num_left -= num_to_copy;
                
                /* ran out of data and we haven't exited main loop */
-               if (inbuf->bytes_left == 0)
+               if (inbuf->nbytes == 0)
                        return TRUE;
-
+               
                /* get header if needed */
                if (!context->got_header) {
-                       rc = pnm_read_header (context);
-                       if (rc == PNM_FATAL_ERR)
+                       retval = pnm_read_header (context);
+                       
+                       if (retval == PNM_FATAL_ERR)
                                return FALSE;
-                       else if (rc == PNM_SUSPEND)
+                       else if (retval == PNM_SUSPEND)
                                continue;
-
+                       
                        context->got_header = TRUE;
                }
 
+               if (context->size_func) {
+                       gint w = context->width;
+                       gint h = context->height;
+                       (*context->size_func) (&w, &h, context->user_data);
+                       
+                       if (w == 0 || h == 0) 
+                               return FALSE;
+               }
+               
+               
                /* scan until we hit image data */
                if (!context->did_prescan) {
-                       if (skip_ahead_whitespace (inbuf) == NULL)
-                               continue;
-
+                       switch (context->type) {
+                       case PNM_FORMAT_PBM_RAW:
+                       case PNM_FORMAT_PGM_RAW:
+                       case PNM_FORMAT_PPM_RAW:
+                               if (inbuf->nbytes <= 0)
+                                       continue;
+                               /* raw formats require exactly one whitespace */
+                               if (!g_ascii_isspace(*(inbuf->byte)))
+                                       {
+                                               g_set_error_literal (error,
+                                                                     GDK_PIXBUF_ERROR,
+                                                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                                                     _("Raw PNM formats require exactly one whitespace before sample data"));
+                                               return FALSE;
+                                       }
+                               inbuf->nbytes--;
+                               inbuf->byte++;
+                               break;
+                       default:
+                               retval = pnm_skip_whitespace (inbuf,
+                                                             context->error);
+                               if (retval == PNM_FATAL_ERR)
+                                       return FALSE;
+                               else if (retval == PNM_SUSPEND)
+                                       continue;
+                               break;
+                       }
                        context->did_prescan = TRUE;
                        context->output_row = 0;
                        context->output_col = 0;
-
-                       context->pixbuf = gdk_pixbuf_new(ART_PIX_RGB, 
-                                                        /*have_alpha*/ FALSE,
-                                                        8, 
-                                                        context->width,
-                                                        context->height);
-
+                       
+                       context->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, 
+                                                         FALSE,
+                                                         8, 
+                                                         context->width,
+                                                         context->height);
+                       
                        if (context->pixbuf == NULL) {
-                               /* Failed to allocate memory */
-                               g_error ("Couldn't allocate gdkpixbuf");
+                               g_set_error_literal (error,
+                                                     GDK_PIXBUF_ERROR,
+                                                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+                                                     _("Insufficient memory to load PNM file"));
+                               return FALSE;
                        }
-
-                       context->pixels = context->pixbuf->art_pixbuf->pixels;
-
+                       
+                       context->pixels = context->pixbuf->pixels;
+                       context->rowstride = context->pixbuf->rowstride;
+                       
                        /* Notify the client that we are ready to go */
-                       (* context->prepared_func) (context->pixbuf,
-                                                   context->user_data);
-
+                       if (context->prepared_func)
+                               (* context->prepared_func) (context->pixbuf,
+                                                           NULL,
+                                                           context->user_data);
                }
-
+               
                /* if we got here we're reading image data */
                while (context->output_row < context->height) {
-
-                       rc = pnm_read_scanline (context);
-
-                       if (rc == PNM_SUSPEND) {
+                       retval = pnm_read_scanline (context);
+                       
+                       if (retval == PNM_SUSPEND) {
                                break;
-                       } else if (rc == PNM_FATAL_ERR) {
-                               if (context->pixbuf)
-                                       gdk_pixbuf_unref (context->pixbuf);
-                               g_warning ("io-pnm.c: error reading rows..\n");
+                       } else if (retval == PNM_FATAL_ERR) {
                                return FALSE;
-                       } else if (rc == PNM_OK) {
+                       } else if (retval == PNM_OK && context->updated_func) { 
                                /* send updated signal */
                                (* context->updated_func) (context->pixbuf,
-                                                context->user_data,
-                                                0, 
-                                                context->output_row-1,
-                                                context->width, 
-                                                1);
+                                                          0, 
+                                                          context->output_row-1,
+                                                          context->width, 
+                                                          1,
+                                                          context->user_data);
                        }
                }
-
+               
                if (context->output_row < context->height)
                        continue;
                else
                        break;
        }
-
+       
        return TRUE;
 }
+
+#ifndef INCLUDE_pnm
+#define MODULE_ENTRY(function) G_MODULE_EXPORT void function
+#else
+#define MODULE_ENTRY(function) void _gdk_pixbuf__pnm_ ## function
+#endif
+
+MODULE_ENTRY (fill_vtable) (GdkPixbufModule *module)
+{
+       module->load = gdk_pixbuf__pnm_image_load;
+       module->begin_load = gdk_pixbuf__pnm_image_begin_load;
+       module->stop_load = gdk_pixbuf__pnm_image_stop_load;
+       module->load_increment = gdk_pixbuf__pnm_image_load_increment;
+}
+
+MODULE_ENTRY (fill_info) (GdkPixbufFormat *info)
+{
+       static GdkPixbufModulePattern signature[] = {
+               { "P1", NULL, 100 },
+               { "P2", NULL, 100 },
+               { "P3", NULL, 100 },
+               { "P4", NULL, 100 },
+               { "P5", NULL, 100 },
+               { "P6", NULL, 100 },
+               { NULL, NULL, 0 }
+       };
+       static gchar * mime_types[] = {
+               "image/x-portable-anymap",
+               "image/x-portable-bitmap",
+               "image/x-portable-graymap",
+               "image/x-portable-pixmap",
+               NULL
+       };
+       static gchar * extensions[] = {
+               "pnm",
+               "pbm",
+               "pgm",
+               "ppm",
+               NULL
+       };
+
+       info->name = "pnm";
+       info->signature = signature;
+       info->description = N_("The PNM/PBM/PGM/PPM image format family");
+       info->mime_types = mime_types;
+       info->extensions = extensions;
+       info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
+       info->license = "LGPL";
+}