]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/io-pnm.c
Robustness fixes and test images for the jpeg, tiff, pnm, gif, xpm and tga
[~andy/gtk] / gdk-pixbuf / io-pnm.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* GdkPixbuf library - PNM image loader
3  *
4  * Copyright (C) 1999 Red Hat, Inc.
5  *
6  * Authors: Jeffrey Stedfast <fejj@helixcode.com>
7  *          Michael Fulbright <drmike@redhat.com>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 #include <config.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <setjmp.h>
30 #include "gdk-pixbuf-private.h"
31 #include "gdk-pixbuf-io.h"
32
33
34 #define PNM_BUF_SIZE 4096
35
36 #define PNM_FATAL_ERR  -1
37 #define PNM_SUSPEND     0
38 #define PNM_OK          1
39
40 typedef enum {
41         PNM_FORMAT_PGM = 1,
42         PNM_FORMAT_PGM_RAW,
43         PNM_FORMAT_PPM,
44         PNM_FORMAT_PPM_RAW,
45         PNM_FORMAT_PBM,
46         PNM_FORMAT_PBM_RAW
47 } PnmFormat;
48
49 typedef struct {
50         guchar buffer[PNM_BUF_SIZE];
51         guchar *byte;
52         guint nbytes;
53 } PnmIOBuffer;
54
55 typedef struct {
56         ModuleUpdatedNotifyFunc updated_func;
57         ModulePreparedNotifyFunc prepared_func;
58         gpointer user_data;
59         
60         GdkPixbuf *pixbuf;
61         guchar *pixels;        /* incoming pixel data buffer */
62         guchar *dptr;          /* current position in pixbuf */
63         
64         PnmIOBuffer inbuf;
65         
66         guint width;
67         guint height;
68         guint maxval;
69         guint rowstride;
70         PnmFormat type;
71         
72         guint output_row;      /* last row to be completed */
73         guint output_col;
74         gboolean did_prescan;  /* are we in image data yet? */
75         gboolean got_header;   /* have we loaded pnm header? */
76         
77         guint scan_state;
78
79         GError **error;
80         
81 } PnmLoaderContext;
82
83 static GdkPixbuf   *gdk_pixbuf__pnm_image_load          (FILE *f, GError **error);
84 static gpointer    gdk_pixbuf__pnm_image_begin_load     (ModulePreparedNotifyFunc func, 
85                                                          ModuleUpdatedNotifyFunc func2,
86                                                          gpointer user_data,
87                                                          GError **error);
88 static gboolean    gdk_pixbuf__pnm_image_stop_load      (gpointer context, GError **error);
89 static gboolean    gdk_pixbuf__pnm_image_load_increment (gpointer context,
90                                                          const guchar *buf, guint size,
91                                                          GError **error);
92
93 static void explode_bitmap_into_buf              (PnmLoaderContext *context);
94 static void explode_gray_into_buf                (PnmLoaderContext *context);
95
96 /* Destroy notification function for the pixbuf */
97 static void
98 free_buffer (guchar *pixels, gpointer data)
99 {
100         g_free (pixels);
101 }
102
103
104 /* explode bitmap data into rgb components         */
105 /* we need to know what the row so we can          */
106 /* do sub-byte expansion (since 1 byte = 8 pixels) */
107 /* context->dptr MUST point at first byte in incoming data  */
108 /* which corresponds to first pixel of row y       */
109 static void
110 explode_bitmap_into_buf (PnmLoaderContext *context)
111 {
112         gint j;
113         guchar *from, *to, data;
114         gint bit;
115         guchar *dptr;
116         gint wid, x, y;
117         
118         g_return_if_fail (context != NULL);
119         g_return_if_fail (context->dptr != NULL);
120         
121         /* I'm no clever bit-hacker so I'm sure this can be optimized */
122         dptr = context->dptr;
123         y    = context->output_row;
124         wid  = context->width;
125         
126         from = dptr + ((wid - 1) / 8);
127         to   = dptr + (wid - 1) * 3;
128 /*      bit  = 7 - (((y+1)*wid-1) % 8); */
129         bit  = 7 - ((wid-1) % 8);
130         
131         /* get first byte and align properly */
132         data = from[0];
133         for (j = 0; j < bit; j++, data >>= 1);
134         
135         for (x = wid-1; x >= 0; x--) {
136 /*              g_print ("%c",  (data & 1) ? '*' : ' '); */
137                 
138                 to[0] = to[1] = to[2] = (data & 0x01) ? 0x00 : 0xff;
139                 
140                 to -= 3;
141                 bit++;
142                 
143                 if (bit > 7) {
144                         from--;
145                         data = from[0];
146                         bit = 0;
147                 } else {
148                         data >>= 1;
149                 }
150         }
151         
152 /*      g_print ("\n"); */
153 }
154
155 /* explode gray image row into rgb components in pixbuf */
156 static void
157 explode_gray_into_buf (PnmLoaderContext *context)
158 {
159         gint j;
160         guchar *from, *to;
161         guint w;
162         
163         g_return_if_fail (context != NULL);
164         g_return_if_fail (context->dptr != NULL);
165         
166         /* Expand grey->colour.  Expand from the end of the
167          * memory down, so we can use the same buffer.
168          */
169         w = context->width;
170         from = context->dptr + w - 1;
171         to = context->dptr + (w - 1) * 3;
172         for (j = w - 1; j >= 0; j--) {
173                 to[0] = from[0];
174                 to[1] = from[0];
175                 to[2] = from[0];
176                 to -= 3;
177                 from--;
178         }
179 }
180
181 /* skip over whitespace and comments in input buffer */
182 static gint
183 pnm_skip_whitespace (PnmIOBuffer *inbuf, GError **error)
184 {
185         register guchar *inptr;
186         guchar *inend;
187         
188         g_return_val_if_fail (inbuf != NULL, PNM_FATAL_ERR);
189         g_return_val_if_fail (inbuf->byte != NULL, PNM_FATAL_ERR);
190         
191         inend = inbuf->byte + inbuf->nbytes;
192         inptr = inbuf->byte;
193         
194         for ( ; inptr < inend; inptr++) {
195                 if (*inptr == '#') {
196                         /* in comment - skip to the end of this line */
197                         for ( ; *inptr != '\n' && inptr < inend; inptr++)
198                                 ;
199                         
200                         if ( *inptr != '\n' ) {
201                                 /* couldn't read whole comment */
202                                 return PNM_SUSPEND;
203                         }
204                         
205                 } else if (!g_ascii_isspace (*inptr)) {
206                         inbuf->byte = inptr;
207                         inbuf->nbytes = (guint) (inend - inptr);
208                         return PNM_OK;
209                 }
210         }
211         
212         inbuf->byte = inptr;
213         inbuf->nbytes = (guint) (inend - inptr);
214         
215         return PNM_SUSPEND;
216 }
217
218 /* read next number from buffer */
219 static gint
220 pnm_read_next_value (PnmIOBuffer *inbuf, guint *value, GError **error)
221 {
222         register guchar *inptr, *word, *p;
223         guchar *inend, buf[129];
224         gchar *endptr;
225         gint retval;
226         glong result;
227         
228         g_return_val_if_fail (inbuf != NULL, PNM_FATAL_ERR);
229         g_return_val_if_fail (inbuf->byte != NULL, PNM_FATAL_ERR);
230         g_return_val_if_fail (value != NULL, PNM_FATAL_ERR);
231         
232         /* skip white space */
233         if ((retval = pnm_skip_whitespace (inbuf, error)) != PNM_OK)
234                 return retval;
235         
236         inend = inbuf->byte + inbuf->nbytes;
237         inptr = inbuf->byte;
238         
239         /* copy this pnm 'word' into a temp buffer */
240         for (p = inptr, word = buf; (p < inend) && !g_ascii_isspace (*p) && (*p != '#') && (p - inptr < 128); p++, word++)
241                 *word = *p;
242         *word = '\0';
243         
244         /* hmmm, there must be more data to this 'word' */
245         if (!g_ascii_isspace (*p) && (*p != '#')  && (p - inptr < 128))
246             return PNM_SUSPEND;
247         
248         /* get the value */
249         result = strtol (buf, &endptr, 10);
250         if (*endptr != '\0' || result < 0) {
251                 g_set_error (error,
252                              GDK_PIXBUF_ERROR,
253                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
254                              _("PNM loader expected to find an integer, but didn't"));
255                 return PNM_FATAL_ERR;
256         }
257         *value = result;
258
259         inbuf->byte = p;
260         inbuf->nbytes = (guint) (inend - p);
261         
262         return PNM_OK;
263 }
264
265 /* returns PNM_OK, PNM_SUSPEND, or PNM_FATAL_ERR */
266 static gint
267 pnm_read_header (PnmLoaderContext *context)
268 {
269         PnmIOBuffer *inbuf;
270         gint retval;
271         
272         g_return_val_if_fail (context != NULL, PNM_FATAL_ERR);
273         
274         inbuf = &context->inbuf;
275         
276         if (!context->type) {
277                 /* file must start with a 'P' followed by a numeral  */
278                 /* so loop till we get enough data to determine type */
279                 if (inbuf->nbytes < 2)
280                         return PNM_SUSPEND;
281                 
282                 if (*inbuf->byte != 'P') {
283                         g_set_error (context->error,
284                                      GDK_PIXBUF_ERROR,
285                                      GDK_PIXBUF_ERROR_HEADER_CORRUPT,
286                                      _("PNM file has an incorrect initial byte"));
287                         return PNM_FATAL_ERR;
288                 }
289                 
290                 inbuf->byte++;
291                 inbuf->nbytes--;
292                 
293                 switch (*inbuf->byte) {
294                 case '1':
295                         context->type = PNM_FORMAT_PBM;
296                         break;
297                 case '2':
298                         context->type = PNM_FORMAT_PGM;
299                         break;
300                 case '3':
301                         context->type = PNM_FORMAT_PPM;
302                         break;
303                 case '4':
304                         context->type = PNM_FORMAT_PBM_RAW;
305                         break;
306                 case '5':
307                         context->type = PNM_FORMAT_PGM_RAW;
308                         break;
309                 case '6':
310                         context->type = PNM_FORMAT_PPM_RAW;
311                         break;
312                 default:
313                         g_set_error (context->error,
314                                      GDK_PIXBUF_ERROR,
315                                      GDK_PIXBUF_ERROR_HEADER_CORRUPT,
316                                      _("PNM file is not in a recognized PNM subformat"));
317                         return PNM_FATAL_ERR;
318                 }
319                 
320                 if (!inbuf->nbytes)
321                         return PNM_SUSPEND;
322                 
323                 inbuf->byte++;
324                 inbuf->nbytes--;
325         }
326         
327         if (!context->width) {
328                 /* read the pixmap width */
329                 guint width = 0;
330                 
331                 retval = pnm_read_next_value (inbuf, &width,
332                                               context->error);
333                 
334                 if (retval != PNM_OK) 
335                         return retval;
336                 
337                 if (!width) {
338                         g_set_error (context->error,
339                                      GDK_PIXBUF_ERROR,
340                                      GDK_PIXBUF_ERROR_HEADER_CORRUPT,
341                                      _("PNM file has an image width of 0"));
342                         return PNM_FATAL_ERR;
343                 }
344                 
345                 context->width = width;
346         }
347         
348         if (!context->height) {
349                 /* read the pixmap height */
350                 guint height = 0;
351                 
352                 retval = pnm_read_next_value (inbuf, &height,
353                                               context->error);
354                 
355                 if (retval != PNM_OK)
356                         return retval;
357                 
358                 if (!height) {
359                         g_set_error (context->error,
360                                      GDK_PIXBUF_ERROR,
361                                      GDK_PIXBUF_ERROR_HEADER_CORRUPT,
362                                      _("PNM file has an image height of 0"));
363                         return PNM_FATAL_ERR;
364                 }
365                 
366                 context->height = height;
367         }
368         
369         switch (context->type) {
370         case PNM_FORMAT_PPM:
371         case PNM_FORMAT_PPM_RAW:
372         case PNM_FORMAT_PGM:
373         case PNM_FORMAT_PGM_RAW:
374                 if (!context->maxval) {
375                         retval = pnm_read_next_value (inbuf, &context->maxval,
376                                                       context->error);
377                         
378                         if (retval != PNM_OK)
379                                 return retval;
380                         
381                         if (context->maxval == 0) {
382                                 g_set_error (context->error,
383                                              GDK_PIXBUF_ERROR,
384                                              GDK_PIXBUF_ERROR_HEADER_CORRUPT,
385                                              _("Maximum color value in PNM file is 0"));
386                                 return PNM_FATAL_ERR;
387                         }
388
389                         if (context->maxval > 65535) {
390                                 g_set_error (context->error,
391                                              GDK_PIXBUF_ERROR,
392                                              GDK_PIXBUF_ERROR_HEADER_CORRUPT,
393                                              _("Maximum color value in PNM file is too large"));
394                                 return PNM_FATAL_ERR;
395                         }
396
397                         if (context->maxval > 255) {
398                                 g_set_error (context->error,
399                                              GDK_PIXBUF_ERROR,
400                                              GDK_PIXBUF_ERROR_HEADER_CORRUPT,
401                                              _("Can't handle PNM files with maximum color values greater than 255"));
402                                 return PNM_FATAL_ERR;
403                         }
404                 }
405                 break;
406         default:
407                 break;
408         }
409         
410         return PNM_OK;
411 }
412
413 static gint
414 pnm_read_raw_scanline (PnmLoaderContext *context)
415 {
416         PnmIOBuffer *inbuf;
417         guint numbytes, offset;
418         guint numpix = 0;
419         guchar *dest;
420         guint i;
421         
422         g_return_val_if_fail (context != NULL, PNM_FATAL_ERR);
423         
424         inbuf = &context->inbuf;
425         
426         switch (context->type) {
427         case PNM_FORMAT_PBM_RAW:
428                 numpix = inbuf->nbytes * 8;
429                 break;
430         case PNM_FORMAT_PGM_RAW:
431                 numpix = inbuf->nbytes;
432                 break;
433         case PNM_FORMAT_PPM_RAW:
434                 numpix = inbuf->nbytes / 3;
435                 break;
436         default:
437                 g_set_error (context->error,
438                              GDK_PIXBUF_ERROR,
439                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
440                              _("Raw PNM image type is invalid"));
441                 return PNM_FATAL_ERR;
442         }
443         
444         numpix = MIN (numpix, context->width - context->output_col);
445         
446         if (!numpix)
447                 return PNM_SUSPEND;
448         
449         context->dptr = context->pixels + context->output_row * context->rowstride;
450         
451         switch (context->type) {
452         case PNM_FORMAT_PBM_RAW:
453                 numbytes = (numpix / 8) + ((numpix % 8) ? 1 : 0);
454                 offset = context->output_col / 8;
455                 break;
456         case PNM_FORMAT_PGM_RAW:
457                 numbytes = numpix;
458                 offset = context->output_col;
459                 break;
460         case PNM_FORMAT_PPM_RAW:
461                 numbytes = numpix * 3;
462                 offset = context->output_col * 3;
463                 break;
464         default:
465                 g_set_error (context->error,
466                              GDK_PIXBUF_ERROR,
467                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
468                              _("Raw PNM image type is invalid"));
469                 return PNM_FATAL_ERR;
470         }
471         
472         switch (context->type) {
473         case PNM_FORMAT_PBM_RAW:
474                 dest = context->dptr + offset;          
475                 memcpy (dest, inbuf->byte, numbytes);
476                 break;
477         case PNM_FORMAT_PGM_RAW:
478         case PNM_FORMAT_PPM_RAW:
479                 dest = context->dptr + offset;
480                 
481                 if (context->maxval == 255) {
482                         /* special-case optimization */
483                         memcpy (dest, inbuf->byte, numbytes);
484                 } else {
485                         for (i = 0; i < numbytes; i++) {
486                                 guchar *byte = inbuf->byte + i;
487                                 
488                                 /* scale the color to an 8-bit color depth */
489                                 if (*byte > context->maxval)
490                                         *dest++ = 255;
491                                 else
492                                         *dest++ = (guchar) (255 * *byte / context->maxval);
493                         }
494                 }
495                 break;
496         default:
497                 g_set_error (context->error,
498                              GDK_PIXBUF_ERROR,
499                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
500                              _("Raw PNM image type is invalid"));
501                 return PNM_FATAL_ERR;
502         }
503         
504         inbuf->byte += numbytes;
505         inbuf->nbytes -= numbytes;
506         
507         context->output_col += numpix;
508         if (context->output_col == context->width) {
509                 if (context->type == PNM_FORMAT_PBM_RAW)
510                         explode_bitmap_into_buf (context);
511                 else if (context->type == PNM_FORMAT_PGM_RAW)
512                         explode_gray_into_buf (context);
513                 
514                 context->output_col = 0;
515                 context->output_row++;
516         } else {
517                 return PNM_SUSPEND;
518         }
519         
520         return PNM_OK;
521 }
522
523 static gint
524 pnm_read_ascii_scanline (PnmLoaderContext *context)
525 {
526         PnmIOBuffer *inbuf;
527         guint offset;
528         guint value, numval, i;
529         guchar data;
530         guchar mask;
531         guchar *dptr;
532         gint retval;
533         
534         g_return_val_if_fail (context != NULL, PNM_FATAL_ERR);
535         
536         data = mask = 0;
537         
538         inbuf = &context->inbuf;
539         
540         context->dptr = context->pixels + context->output_row * context->rowstride;
541         
542         switch (context->type) {
543         case PNM_FORMAT_PBM:
544                 numval = MIN (8, context->width - context->output_col);
545                 offset = context->output_col / 8;
546                 break;
547         case PNM_FORMAT_PGM:
548                 numval = 1;
549                 offset = context->output_col;
550                 break;
551         case PNM_FORMAT_PPM:
552                 numval = 3;
553                 offset = context->output_col * 3;
554                 break;
555                 
556         default:
557                 g_set_error (context->error,
558                              GDK_PIXBUF_ERROR,
559                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
560                              _("PNM image format is invalid"));
561
562                 return PNM_FATAL_ERR;
563         }
564         
565         dptr = context->dptr + offset + context->scan_state;
566         
567         while (TRUE) {
568                 if (context->type == PNM_FORMAT_PBM) {
569                         mask = 0x80;
570                         data = 0;
571                         numval = MIN (8, context->width - context->output_col);
572                 }
573                 
574                 for (i = context->scan_state; i < numval; i++) {
575                         retval = pnm_read_next_value (inbuf, &value,
576                                                       context->error);
577                         if (retval != PNM_OK) {
578                                 /* save state and return */
579                                 context->scan_state = i;
580                                 return retval;
581                         }
582                         
583                         switch (context->type) {
584                         case PNM_FORMAT_PBM:
585                                 if (value)
586                                         data |= mask;
587                                 mask >>= 1;
588                                 
589                                 break;
590                         case PNM_FORMAT_PGM:
591                         case PNM_FORMAT_PPM:
592                                 /* scale the color to an 8-bit color depth */
593                                 if (value > context->maxval)
594                                         *dptr++ = 255;
595                                 else
596                                         *dptr++ = (guchar)(255 * value / context->maxval);
597                                 break;
598                         default:
599                                 g_set_error (context->error,
600                                              GDK_PIXBUF_ERROR,
601                                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
602                                              _("PNM image format is invalid"));
603                                 return PNM_FATAL_ERR;
604                                 break;
605                         }
606                 }
607                 
608                 context->scan_state = 0;
609                 
610                 if (context->type == PNM_FORMAT_PBM) {
611                         *dptr++ = data;
612                         context->output_col += numval;
613                 } else {
614                         context->output_col++;
615                 }
616                 
617                 if (context->output_col == context->width) {
618                         if (context->type == PNM_FORMAT_PBM)
619                                 explode_bitmap_into_buf (context);
620                         else if (context->type == PNM_FORMAT_PGM)
621                                 explode_gray_into_buf (context);
622                         
623                         context->output_col = 0;
624                         context->output_row++;
625                         break;
626                 }
627         }
628         
629         return PNM_OK;
630 }
631
632 /* returns 1 if a scanline was converted, 0 means we ran out of data */
633 static gint
634 pnm_read_scanline (PnmLoaderContext *context)
635 {
636         gint retval;
637         
638         g_return_val_if_fail (context != NULL, PNM_FATAL_ERR);
639         
640         /* read in image data */
641         /* for raw formats this is trivial */
642         switch (context->type) {
643         case PNM_FORMAT_PBM_RAW:
644         case PNM_FORMAT_PGM_RAW:
645         case PNM_FORMAT_PPM_RAW:
646                 retval = pnm_read_raw_scanline (context);
647                 if (retval != PNM_OK)
648                         return retval;
649                 break;
650         case PNM_FORMAT_PBM:
651         case PNM_FORMAT_PGM:
652         case PNM_FORMAT_PPM:
653                 retval = pnm_read_ascii_scanline (context);
654                 if (retval != PNM_OK)
655                         return retval;
656                 break;
657         default:
658                 g_set_error (context->error,
659                              GDK_PIXBUF_ERROR,
660                              GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
661                              _("PNM image loader does not support this PNM subformat"));
662
663                 return PNM_FATAL_ERR;
664         }
665         
666         return PNM_OK;
667 }
668
669 /* Shared library entry point */
670 static GdkPixbuf *
671 gdk_pixbuf__pnm_image_load (FILE *f, GError **error)
672 {
673         PnmLoaderContext context;
674         PnmIOBuffer *inbuf;
675         gint nbytes;
676         gint retval;
677         
678         /* pretend to be doing progressive loading */
679         context.updated_func = NULL;
680         context.prepared_func = NULL;
681         context.user_data = NULL;
682         context.type = 0;
683         context.inbuf.nbytes = 0;
684         context.inbuf.byte = NULL;
685         context.width = 0;
686         context.height = 0;
687         context.maxval = 0;
688         context.pixels = NULL;
689         context.pixbuf = NULL;
690         context.got_header = FALSE;
691         context.did_prescan = FALSE;
692         context.scan_state = 0;
693         context.error = error;
694         
695         inbuf = &context.inbuf;
696         
697         while (TRUE) {
698                 guint num_to_read;
699                 
700                 /* keep buffer as full as possible */
701                 num_to_read = PNM_BUF_SIZE - inbuf->nbytes;
702                 
703                 if (inbuf->byte != NULL && inbuf->nbytes > 0)
704                         memmove (inbuf->buffer, inbuf->byte, inbuf->nbytes);
705                 
706                 nbytes = fread (inbuf->buffer + inbuf->nbytes, 1, num_to_read, f);
707                 
708                 /* error checking */
709                 if (nbytes == 0) {
710                         /* we ran out of data? */
711                         if (context.pixbuf)
712                                 gdk_pixbuf_unref (context.pixbuf);
713                         g_set_error (error,
714                                      GDK_PIXBUF_ERROR,
715                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
716                                      _("Premature end-of-file encountered"));
717                         return NULL;
718                 }
719                 
720                 inbuf->nbytes += nbytes;
721                 inbuf->byte = inbuf->buffer;
722                 
723                 /* get header if needed */
724                 if (!context.got_header) {
725                         retval = pnm_read_header (&context);
726                         if (retval == PNM_FATAL_ERR)
727                                 return NULL;
728                         else if (retval == PNM_SUSPEND)
729                                 continue;
730                         
731                         context.got_header = TRUE;
732                 }
733                 
734                 /* scan until we hit image data */
735                 if (!context.did_prescan) {
736                         switch (context.type) {
737                         case PNM_FORMAT_PBM_RAW:
738                         case PNM_FORMAT_PGM_RAW:
739                         case PNM_FORMAT_PPM_RAW:
740                                 if (inbuf->nbytes <= 0)
741                                         continue;
742                                 /* raw formats require exactly one whitespace */
743                                 if (!g_ascii_isspace(*(inbuf->byte))) 
744                                         {
745                                                 g_set_error (error,
746                                                              GDK_PIXBUF_ERROR,
747                                                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
748                                                              _("Raw PNM formats require exactly one whitespace before sample data"));
749                                                 return NULL;
750                                         }
751                                 inbuf->nbytes--;
752                                 inbuf->byte++;
753                                 break;
754                         default:
755                                 retval = pnm_skip_whitespace (inbuf,
756                                                               context.error);
757                                 if (retval == PNM_FATAL_ERR)
758                                         return NULL;
759                                 else if (retval == PNM_SUSPEND)
760                                         continue;
761                                 break;
762                         }
763                         context.did_prescan = TRUE;
764                         context.output_row = 0;
765                         context.output_col = 0;
766                         
767                         context.rowstride = context.width * 3;
768                         context.pixels = g_try_malloc (context.height * context.width * 3);
769                         
770                         if (!context.pixels) {
771                                 /* Failed to allocate memory */
772                                 g_set_error (error,
773                                              GDK_PIXBUF_ERROR,
774                                              GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
775                                              _("Can't allocate memory for loading PNM image"));
776                                 return NULL;
777                         }
778                 }
779                 
780                 /* if we got here we're reading image data */
781                 while (context.output_row < context.height) {
782                         retval = pnm_read_scanline (&context);
783                         
784                         if (retval == PNM_SUSPEND) {
785                                 break;
786                         } else if (retval == PNM_FATAL_ERR) {
787                                 if (context.pixbuf)
788                                         gdk_pixbuf_unref (context.pixbuf);
789
790                                 return NULL;
791                         }
792                 }
793                 
794                 if (context.output_row < context.height)
795                         continue;
796                 else
797                         break;
798         }
799         
800         return gdk_pixbuf_new_from_data (context.pixels, GDK_COLORSPACE_RGB, FALSE, 8,
801                                          context.width, context.height, 
802                                          context.width * 3, free_buffer, NULL);
803
804 }
805
806 /* 
807  * func - called when we have pixmap created (but no image data)
808  * user_data - passed as arg 1 to func
809  * return context (opaque to user)
810  */
811
812 static gpointer
813 gdk_pixbuf__pnm_image_begin_load (ModulePreparedNotifyFunc prepared_func, 
814                                   ModuleUpdatedNotifyFunc  updated_func,
815                                   gpointer user_data,
816                                   GError **error)
817 {
818         PnmLoaderContext *context;
819         
820         context = g_try_malloc (sizeof (PnmLoaderContext));
821         if (!context) {
822                 g_set_error(error, GDK_PIXBUF_ERROR, 
823                             GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
824                             _("Insufficient memory to load PNM context struct"));
825                 return NULL;
826         }
827         memset (context, 0, sizeof (PnmLoaderContext));
828         context->prepared_func = prepared_func;
829         context->updated_func  = updated_func;
830         context->user_data = user_data;
831         context->width = 0;
832         context->height = 0;
833         context->maxval = 0;
834         context->pixbuf = NULL;
835         context->pixels = NULL;
836         context->got_header = FALSE;
837         context->did_prescan = FALSE;
838         context->scan_state = 0;
839         
840         context->inbuf.nbytes = 0;
841         context->inbuf.byte  = NULL;
842
843         context->error = error;
844         
845         return (gpointer) context;
846 }
847
848 /*
849  * context - returned from image_begin_load
850  *
851  * free context, unref gdk_pixbuf
852  */
853 static gboolean
854 gdk_pixbuf__pnm_image_stop_load (gpointer data,
855                                  GError **error)
856 {
857         PnmLoaderContext *context = (PnmLoaderContext *) data;
858         gboolean retval = TRUE;
859         
860         g_return_val_if_fail (context != NULL, TRUE);
861         
862         if (context->pixbuf)
863                 gdk_pixbuf_unref (context->pixbuf);
864
865 #if 0
866         /* We should ignore trailing newlines and we can't
867            generally complain about trailing stuff at all, since 
868            pnm allows to put multiple images in a file
869         */
870         if (context->inbuf.nbytes > 0) {
871                 g_set_error (error,
872                              GDK_PIXBUF_ERROR,
873                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
874                              _("Unexpected end of PNM image data"));
875                 retval = FALSE;
876         }
877 #endif
878         
879         g_free (context);
880
881         return retval;
882 }
883
884 /*
885  * context - from image_begin_load
886  * buf - new image data
887  * size - length of new image data
888  *
889  * append image data onto inrecrementally built output image
890  */
891 static gboolean
892 gdk_pixbuf__pnm_image_load_increment (gpointer data,
893                                       const guchar *buf, guint size,
894                                       GError **error)
895 {
896         PnmLoaderContext *context = (PnmLoaderContext *)data;
897         PnmIOBuffer *inbuf;
898         guchar *old_byte;
899         guint old_nbytes;
900         const guchar *bufhd;
901         guint num_left, spinguard;
902         gint retval;
903         
904         g_return_val_if_fail (context != NULL, FALSE);
905         g_return_val_if_fail (buf != NULL, FALSE);
906
907         context->error = error;
908         
909         bufhd = buf;
910         inbuf = &context->inbuf;
911         old_nbytes = inbuf->nbytes;
912         old_byte  = inbuf->byte;
913         
914         num_left = size;
915         spinguard = 0;
916         while (TRUE) {
917                 guint num_to_copy;
918                 
919                 /* keep buffer as full as possible */
920                 num_to_copy = MIN (PNM_BUF_SIZE - inbuf->nbytes, num_left);
921                 
922                 if (num_to_copy == 0)
923                         spinguard++;
924                 
925                 if (spinguard > 1)
926                         return TRUE;
927                 
928                 if (inbuf->byte != NULL && inbuf->nbytes > 0)
929                         memmove (inbuf->buffer, inbuf->byte, inbuf->nbytes);
930                 
931                 memcpy (inbuf->buffer + inbuf->nbytes, bufhd, num_to_copy);
932                 bufhd += num_to_copy;
933                 inbuf->nbytes += num_to_copy;
934                 inbuf->byte = inbuf->buffer;
935                 num_left -= num_to_copy;
936                 
937                 /* ran out of data and we haven't exited main loop */
938                 if (inbuf->nbytes == 0)
939                         return TRUE;
940                 
941                 /* get header if needed */
942                 if (!context->got_header) {
943                         retval = pnm_read_header (context);
944                         
945                         if (retval == PNM_FATAL_ERR)
946                                 return FALSE;
947                         else if (retval == PNM_SUSPEND)
948                                 continue;
949                         
950                         context->got_header = TRUE;
951                 }
952                 
953                 /* scan until we hit image data */
954                 if (!context->did_prescan) {
955                         switch (context->type) {
956                         case PNM_FORMAT_PBM_RAW:
957                         case PNM_FORMAT_PGM_RAW:
958                         case PNM_FORMAT_PPM_RAW:
959                                 if (inbuf->nbytes <= 0)
960                                         continue;
961                                 /* raw formats require exactly one whitespace */
962                                 if (!g_ascii_isspace(*(inbuf->byte)))
963                                         {
964                                                 g_set_error (error,
965                                                              GDK_PIXBUF_ERROR,
966                                                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
967                                                              _("Raw PNM formats require exactly one whitespace before sample data"));
968                                                 return FALSE;
969                                         }
970                                 inbuf->nbytes--;
971                                 inbuf->byte++;
972                                 break;
973                         default:
974                                 retval = pnm_skip_whitespace (inbuf,
975                                                               context->error);
976                                 if (retval == PNM_FATAL_ERR)
977                                         return FALSE;
978                                 else if (retval == PNM_SUSPEND)
979                                         continue;
980                                 break;
981                         }
982                         context->did_prescan = TRUE;
983                         context->output_row = 0;
984                         context->output_col = 0;
985                         
986                         context->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, 
987                                                           FALSE,
988                                                           8, 
989                                                           context->width,
990                                                           context->height);
991                         
992                         if (context->pixbuf == NULL) {
993                                 g_set_error (error,
994                                              GDK_PIXBUF_ERROR,
995                                              GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
996                                              _("Insufficient memory to load PNM file"));
997                                 return FALSE;
998                         }
999                         
1000                         context->pixels = context->pixbuf->pixels;
1001                         context->rowstride = context->pixbuf->rowstride;
1002                         
1003                         /* Notify the client that we are ready to go */
1004                         (* context->prepared_func) (context->pixbuf,
1005                                                     NULL,
1006                                                     context->user_data);
1007                 }
1008                 
1009                 /* if we got here we're reading image data */
1010                 while (context->output_row < context->height) {
1011                         retval = pnm_read_scanline (context);
1012                         
1013                         if (retval == PNM_SUSPEND) {
1014                                 break;
1015                         } else if (retval == PNM_FATAL_ERR) {
1016                                 if (context->pixbuf)
1017                                         gdk_pixbuf_unref (context->pixbuf);
1018                                 return FALSE;
1019                         } else if (retval == PNM_OK) {  
1020                                 /* send updated signal */
1021                                 (* context->updated_func) (context->pixbuf,
1022                                                            0, 
1023                                                            context->output_row-1,
1024                                                            context->width, 
1025                                                            1,
1026                                                            context->user_data);
1027                         }
1028                 }
1029                 
1030                 if (context->output_row < context->height)
1031                         continue;
1032                 else
1033                         break;
1034         }
1035         
1036         return TRUE;
1037 }
1038
1039 void
1040 gdk_pixbuf__pnm_fill_vtable (GdkPixbufModule *module)
1041 {
1042   module->load = gdk_pixbuf__pnm_image_load;
1043   module->begin_load = gdk_pixbuf__pnm_image_begin_load;
1044   module->stop_load = gdk_pixbuf__pnm_image_stop_load;
1045   module->load_increment = gdk_pixbuf__pnm_image_load_increment;
1046 }