]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/io-pnm.c
Warning fixes - Federico
[~andy/gtk] / gdk-pixbuf / io-pnm.c
1 /* GdkPixbuf library - PNM image loader
2  *
3  * Copyright (C) 1999 Red Hat, Inc.
4  *
5  * Authors: Michael Fulbright <drmike@redhat.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #include <config.h>
24 #include <ctype.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <setjmp.h>
29 #include <jpeglib.h>
30 #include "gdk-pixbuf.h"
31 #include "gdk-pixbuf-io.h"
32
33 \f
34
35 #define PNM_BUF_SIZE 4096
36
37 #define PNM_SUSPEND    0
38 #define PNM_OK         1
39 #define PNM_FATAL_ERR  -1
40
41 typedef enum {
42         PNM_FORMAT_PGM,
43         PNM_FORMAT_PGM_RAW,
44         PNM_FORMAT_PPM,
45         PNM_FORMAT_PPM_RAW,
46         PNM_FORMAT_PBM,
47         PNM_FORMAT_PBM_RAW
48 } PnmFormat;
49
50 typedef struct {
51         guchar    buffer[PNM_BUF_SIZE];
52         guchar    *next_byte;
53         guint     bytes_left;
54 } PnmIOBuffer;
55
56 typedef struct {
57         ModuleUpdatedNotifyFunc  updated_func;
58         ModulePreparedNotifyFunc prepared_func;
59         gpointer                 user_data;
60         
61         GdkPixbuf                *pixbuf;
62         guchar                   *pixels; /* incoming pixel data buffer */
63         guchar                   *dptr;   /* current position in pixbuf */
64
65         PnmIOBuffer              inbuf;
66
67         guint                    width;
68         guint                    height;
69         guint                    maxval;
70         guint                    rowstride;
71         PnmFormat                type;
72
73         guint                    output_row;  /* last row to be completed */
74         guint                    output_col;
75         gboolean                 did_prescan;  /* are we in image data yet? */
76         gboolean                 got_header;  /* have we loaded jpeg header? */
77 } PnmLoaderContext;
78
79 GdkPixbuf *image_load (FILE *f);
80 gpointer image_begin_load (ModulePreparedNotifyFunc func, 
81                            ModuleUpdatedNotifyFunc func2, gpointer user_data);
82 void image_stop_load (gpointer context);
83 gboolean image_load_increment(gpointer context, guchar *buf, guint size);
84
85 static void explode_bitmap_into_buf (PnmLoaderContext *context);
86 static void explode_gray_into_buf (PnmLoaderContext *context);
87
88 /* Destroy notification function for the libart pixbuf */
89 static void
90 free_buffer (gpointer user_data, gpointer data)
91 {
92         free (data);
93 }
94
95
96 /* explode bitmap data into rgb components         */
97 /* we need to know what the row so we can          */
98 /* do sub-byte expansion (since 1 byte = 8 pixels) */
99 /* context->dptr MUST point at first byte in incoming data  */
100 /* which corresponds to first pixel of row y       */
101 static void
102 explode_bitmap_into_buf (PnmLoaderContext *context)
103 {
104         gint j;
105         guchar *from, *to, data;
106         gint bit;
107         guchar *dptr;
108         gint wid, x, y;
109
110         g_return_if_fail (context != NULL);
111         g_return_if_fail (context->dptr != NULL);
112
113         /* I'm no clever bit-hacker so I'm sure this can be optimized */
114         dptr = context->dptr;
115         y    = context->output_row;
116         wid  = context->width;
117
118         from = dptr + (wid - 1)/8;
119         to   = dptr + (wid - 1) * 3;
120 /*      bit  = 7 - (((y+1)*wid-1) % 8); */
121         bit  = 7 - ((wid-1) % 8); 
122
123         /* get first byte and align properly */
124         data = from[0];
125         for (j = 0; j < bit; j++, data >>= 1);
126
127         for (x = wid-1; x >= 0; x--) {
128
129 /*              g_print ("%c",  (data & 1) ? '*' : ' '); */
130
131                 to[0] = to[1] = to[2] = (data & 1) ? 0x00 : 0xff;
132
133                 to -= 3;
134                 bit++;
135
136                 if (bit > 7) {
137                         from--;
138                         data = from[0];
139                         bit = 0;
140                 } else {
141                         data >>= 1;
142                 }
143         }
144
145 /*      g_print ("\n"); */
146 }
147
148 /* explode gray image row into rgb components in pixbuf */
149 static void
150 explode_gray_into_buf (PnmLoaderContext *context)
151 {
152         gint j;
153         guchar *from, *to;
154         guint w;
155
156         g_return_if_fail (context != NULL);
157         g_return_if_fail (context->dptr != NULL);
158
159         /* Expand grey->colour.  Expand from the end of the
160          * memory down, so we can use the same buffer.
161          */
162         w = context->width;
163         from = context->dptr + w - 1;
164         to = context->dptr + (w - 1) * 3;
165         for (j = w - 1; j >= 0; j--) {
166                 to[0] = from[0];
167                 to[1] = from[0];
168                 to[2] = from[0];
169                 to -= 3;
170                 from--;
171         }
172 }
173
174 /* skip over whitespace in file from current pos.                      */
175 /* also skips comments                                                 */
176 /* returns pointer to first non-whitespace char hit or, or NULL if     */
177 /* we ran out of data w/o hitting a whitespace                         */
178 /* internal pointer in inbuf isnt moved ahead in this case             */
179 static guchar *
180 skip_ahead_whitespace (PnmIOBuffer *inbuf)
181 {
182         gboolean in_comment;
183         guchar *ptr;
184         guint num_left;
185         
186         g_return_val_if_fail (inbuf != NULL, NULL);
187         g_return_val_if_fail (inbuf->next_byte != NULL, NULL);
188         
189         in_comment = FALSE;
190         num_left = inbuf->bytes_left;
191         ptr      = inbuf->next_byte;
192         while (num_left > 0) {
193                 if (in_comment) {
194                         if (*ptr == '\n')
195                                 in_comment = FALSE;
196                 } else if (*ptr == '#') {
197                         in_comment = TRUE;
198                 } else if (!isspace (*ptr)) {
199                         inbuf->bytes_left -= (ptr-inbuf->next_byte);
200                         inbuf->next_byte   = ptr;
201                         return ptr;
202                 }
203                 ptr ++;
204                 num_left--;
205         }
206         return NULL;
207 }
208
209 /* reads into buffer until we hit whitespace in file from current pos,     */
210 /* return NULL if ran out of data                                          */
211 /* advances inbuf if successful                                            */
212 static guchar *
213 read_til_whitespace (PnmIOBuffer *inbuf, guchar *buf, guint size)
214 {
215         guchar *p;
216         guchar *ptr;
217         guint num_left;
218
219         g_return_val_if_fail (inbuf != NULL, NULL);
220         g_return_val_if_fail (inbuf->next_byte != NULL, NULL);
221         
222         p = buf;
223         num_left = inbuf->bytes_left;
224         ptr      = inbuf->next_byte;
225         while (num_left > 0 && (p-buf)+1 < size) {
226                 if (isspace (*ptr)) {
227                         *p = '\0';
228                         inbuf->bytes_left = num_left;
229                         inbuf->next_byte  = ptr;
230                         return ptr;
231                 } else {
232                         *p = *ptr;
233                         p++;
234                         ptr++;
235                         num_left--;
236                 }
237         }
238         return NULL;
239 }
240
241 /* read next number from buffer  */
242 /* -1 if failed, 0 if successful */
243 static gint
244 read_next_number (PnmIOBuffer *inbuf, guint *value)
245 {
246         guchar *tmpptr;
247         guchar *old_next_byte;
248         gchar  *errptr;
249         guint  old_bytes_left;
250         guchar buf[128];
251
252         g_return_val_if_fail (inbuf != NULL, -1);
253         g_return_val_if_fail (inbuf->next_byte != NULL, -1);
254         g_return_val_if_fail (value != NULL, -1);
255
256         old_next_byte  = inbuf->next_byte;
257         old_bytes_left = inbuf->bytes_left;
258
259         if ((tmpptr = skip_ahead_whitespace (inbuf)) == NULL)
260                 return -1;
261
262         if ((tmpptr = read_til_whitespace (inbuf, buf, 128)) == NULL) {
263                 inbuf->next_byte  = old_next_byte;
264                 inbuf->bytes_left = old_bytes_left;
265                 return -1;
266         }
267         
268         *value = strtol (buf, &errptr, 10);
269
270         if (*errptr != '\0') {
271                 inbuf->next_byte  = old_next_byte;
272                 inbuf->bytes_left = old_bytes_left;
273                 return -1;
274         }
275
276         return 0;
277 }
278
279 /* returns PNM_OK, PNM_SUSPEND, or PNM_FATAL_ERR */
280 static gint
281 pnm_read_header (PnmLoaderContext *context)
282 {
283         guchar *old_next_byte;
284         guint  old_bytes_left;
285         PnmIOBuffer *inbuf;
286         guint  w, h;
287         gint rc;
288         PnmFormat  type;
289
290         g_return_val_if_fail (context != NULL, PNM_FATAL_ERR);
291
292         inbuf = &context->inbuf;
293         old_bytes_left = inbuf->bytes_left;
294         old_next_byte  = inbuf->next_byte;
295         
296         /* file must start with a 'P' followed by a numeral */
297         /* so loop till we get enough data to determine type*/
298         if (inbuf->bytes_left < 2)
299                 return PNM_SUSPEND;
300         
301         if (*inbuf->next_byte != 'P')
302                 return PNM_FATAL_ERR;
303         
304         switch (*(inbuf->next_byte+1)) {
305         case '1':
306                 type = PNM_FORMAT_PBM;
307                 break;
308         case '2':
309                 type = PNM_FORMAT_PGM;
310                 break;
311         case '3':
312                 type = PNM_FORMAT_PPM;
313                 break;
314         case '4':
315                 type = PNM_FORMAT_PBM_RAW;
316                 break;
317         case '5':
318                 type = PNM_FORMAT_PGM_RAW;
319                 break;
320         case '6':
321                 type = PNM_FORMAT_PPM_RAW;
322                 break;
323         default:
324                 return PNM_FATAL_ERR;
325         }
326         
327         context->type = type;
328
329         inbuf->next_byte  += 2;
330         inbuf->bytes_left -= 2;
331         
332         /* now read remainder of header */
333         if ((rc = read_next_number (inbuf, &w))) {
334                 inbuf->next_byte = old_next_byte;
335                 inbuf->bytes_left = old_bytes_left;
336                 return PNM_SUSPEND;
337         }
338         
339         if ((rc = read_next_number (inbuf, &h))) {
340                 inbuf->next_byte = old_next_byte;
341                 inbuf->bytes_left = old_bytes_left;
342                 return PNM_SUSPEND;
343         }
344         
345         context->width  = w;
346         context->height = h;
347         
348         switch (type) {
349         case PNM_FORMAT_PPM:
350         case PNM_FORMAT_PPM_RAW:
351         case PNM_FORMAT_PGM:
352         case PNM_FORMAT_PGM_RAW:
353                 if ((rc = read_next_number (inbuf, &context->maxval)) < 0) {
354                         inbuf->next_byte = old_next_byte;
355                         inbuf->bytes_left = old_bytes_left;
356                         return PNM_SUSPEND;
357                 }
358                 break;
359         default:
360                 break;
361         }
362
363         return PNM_OK;
364 }
365
366
367 static gint
368 pnm_read_raw_scanline (PnmLoaderContext *context)
369 {
370         guint  numpix;
371         guint  numbytes, offset;
372         PnmIOBuffer *inbuf;
373
374         g_return_val_if_fail (context != NULL, PNM_FATAL_ERR);
375
376 /*G_BREAKPOINT(); */
377
378         inbuf = &context->inbuf;
379
380         switch (context->type) {
381         case PNM_FORMAT_PBM_RAW:
382                 numpix = inbuf->bytes_left * 8;
383                 break;
384         case PNM_FORMAT_PGM_RAW:
385                 numpix = inbuf->bytes_left;
386                 break;
387         case PNM_FORMAT_PPM_RAW:
388                 numpix = inbuf->bytes_left/3;
389                 break;
390         default:
391                 g_warning ("io-pnm.c: Illegal raw pnm type!\n");
392                 return PNM_FATAL_ERR;
393                 break;
394         }
395         
396         numpix = MIN (numpix, context->width - context->output_col);
397
398         if (numpix == 0)
399                 return PNM_SUSPEND;
400         
401         context->dptr = context->pixels + 
402                 context->output_row * context->rowstride;
403         
404         switch (context->type) {
405         case PNM_FORMAT_PBM_RAW:
406                 numbytes = numpix/8 + ((numpix % 8) ? 1 : 0);
407                 offset = context->output_col/8;
408                 break;
409         case PNM_FORMAT_PGM_RAW:
410                 numbytes = numpix;
411                 offset = context->output_col;
412                 break;
413         case PNM_FORMAT_PPM_RAW:
414                 numbytes = numpix*3;
415                 offset = context->output_col*3;
416                 break;
417         default:
418                 g_warning ("io-pnm.c: Illegal raw pnm type!\n");
419                 break;
420         }
421         
422         memcpy (context->dptr + offset, inbuf->next_byte, numbytes);
423
424         inbuf->next_byte  += numbytes;
425         inbuf->bytes_left -= numbytes;
426
427         context->output_col += numpix;
428         if (context->output_col == context->width) {
429                 if ( context->type == PNM_FORMAT_PBM_RAW )
430                         explode_bitmap_into_buf(context);
431                 else if ( context->type == PNM_FORMAT_PGM_RAW )
432                         explode_gray_into_buf (context);
433                 
434                 context->output_col = 0;
435                 context->output_row++;
436                 
437         } else {
438                 return PNM_SUSPEND;
439         }
440
441         return PNM_OK;
442 }
443
444
445 static gint
446 pnm_read_ascii_scanline (PnmLoaderContext *context)
447 {
448         guint  offset;
449         gint   rc;
450         guint  value, numval, i;
451         guchar data;
452         guchar mask;
453         guchar *old_next_byte, *dptr;
454         guint  old_bytes_left;
455         PnmIOBuffer *inbuf;
456
457         g_return_val_if_fail (context != NULL, PNM_FATAL_ERR);
458
459         inbuf = &context->inbuf;
460
461         context->dptr = context->pixels + 
462                 context->output_row * context->rowstride;
463
464         switch (context->type) {
465         case PNM_FORMAT_PBM:
466                 numval = MIN (8, context->width - context->output_col);
467                 offset = context->output_col/8;
468                 break;
469         case PNM_FORMAT_PGM:
470                 numval = 1;
471                 offset = context->output_col;
472                 break;
473         case PNM_FORMAT_PPM:
474                 numval = 3;
475                 offset = context->output_col*3;
476                 break;
477                 
478         default:
479                 g_warning ("Can't happen\n");
480                 return PNM_FATAL_ERR;
481         }
482
483         dptr = context->dptr + offset;
484
485         while (TRUE) {
486                 if (context->type == PNM_FORMAT_PBM) {
487                         mask = 0x80;
488                         data = 0;
489                         numval = MIN (8, context->width - context->output_col);
490                 }
491
492                 old_next_byte  = inbuf->next_byte;
493                 old_bytes_left = inbuf->bytes_left;
494                 
495                 for (i=0; i<numval; i++) {
496                         if ((rc = read_next_number (inbuf, &value))) {
497                                 inbuf->next_byte  = old_next_byte;
498                                 inbuf->bytes_left = old_bytes_left;
499                                 return PNM_SUSPEND;
500                         }
501                         switch (context->type) {
502                         case PNM_FORMAT_PBM:
503                                 if (value)
504                                         data |= mask;
505                                 mask >>= 1;
506                                 
507                                 break;
508                         case PNM_FORMAT_PGM:
509                                 *dptr++ = (guchar)(255.0*((double)value/(double)context->maxval));
510                                 break;
511                         case PNM_FORMAT_PPM:
512                                 *dptr++ = (guchar)(255.0*((double)value/(double)context->maxval));
513                                 break;
514                         default:
515                                 g_warning ("io-pnm.c: Illegal raw pnm type!\n");
516                                 break;
517                         }
518                 }
519                 
520                 if (context->type == PNM_FORMAT_PBM) {
521                         *dptr++ = data;
522                         context->output_col += 8;
523                 } else {
524                         context->output_col++;
525                 }
526
527                 if (context->output_col == context->width) {
528                         if ( context->type == PNM_FORMAT_PBM )
529                                 explode_bitmap_into_buf(context);
530                         else if ( context->type == PNM_FORMAT_PGM )
531                                 explode_gray_into_buf (context);
532
533                         context->output_col = 0;
534                         context->output_row++;
535                         break;
536                 }
537
538         }
539
540         return PNM_OK;
541 }
542
543 /* returns 1 if a scanline was converted,  0 means we ran out of data */
544 static gint
545 pnm_read_scanline (PnmLoaderContext *context)
546 {
547         gint   rc;
548
549         g_return_val_if_fail (context != NULL, PNM_FATAL_ERR);
550
551         /* read in image data */
552         /* for raw formats this is trivial */
553         switch (context->type) {
554         case PNM_FORMAT_PBM_RAW:
555         case PNM_FORMAT_PGM_RAW:
556         case PNM_FORMAT_PPM_RAW:
557                 rc = pnm_read_raw_scanline (context);
558                 if (rc == PNM_SUSPEND)
559                         return rc;
560                 break;
561
562         case PNM_FORMAT_PBM:
563         case PNM_FORMAT_PGM:
564         case PNM_FORMAT_PPM:
565                 rc = pnm_read_ascii_scanline (context);
566                 if (rc == PNM_SUSPEND)
567                         return rc;
568                 break;
569
570         default:
571                 g_warning ("Cannot load these image types (yet)\n");
572                 return PNM_FATAL_ERR;
573         }
574
575         return PNM_OK;
576 }
577
578 /* Shared library entry point */
579 GdkPixbuf *
580 image_load (FILE *f)
581 {
582         gint  nbytes;
583         gint   rc;
584
585         PnmLoaderContext context;
586         PnmIOBuffer *inbuf;
587
588         /* pretend to be doing progressive loading */
589         context.updated_func = NULL;
590         context.prepared_func = NULL;
591         context.user_data = NULL;
592         context.inbuf.bytes_left = 0;
593         context.inbuf.next_byte  = NULL;
594         context.pixels = NULL;
595         context.pixbuf = NULL;
596         context.got_header = context.did_prescan = FALSE;
597
598         inbuf = &context.inbuf;
599
600         while (TRUE) {
601                 guint  num_to_read;
602
603                 /* keep buffer as full as possible */
604                 num_to_read = PNM_BUF_SIZE - inbuf->bytes_left;
605
606                 if (inbuf->next_byte != NULL && inbuf->bytes_left > 0)
607                         memmove (inbuf->buffer, inbuf->next_byte, 
608                                  inbuf->bytes_left);
609
610                 nbytes = fread (inbuf->buffer+inbuf->bytes_left, 
611                                 1, num_to_read, f);
612                 inbuf->bytes_left += nbytes;
613                 inbuf->next_byte   = inbuf->buffer;
614
615                 /* ran out of data and we haven't exited main loop */
616                 if (inbuf->bytes_left == 0) {
617                         if (context.pixbuf)
618                                 gdk_pixbuf_unref (context.pixbuf);
619                         g_warning ("io-pnm.c: Ran out of data...\n");
620                         return NULL;
621                 }
622
623                 /* get header if needed */
624                 if (!context.got_header) {
625                 
626                         rc = pnm_read_header (&context);
627                         if (rc == PNM_FATAL_ERR)
628                                 return NULL;
629                         else if (rc == PNM_SUSPEND)
630                                 continue;
631
632                         context.got_header = TRUE;
633                 }
634
635                 /* scan until we hit image data */
636                 if (!context.did_prescan) {
637
638                         if (skip_ahead_whitespace (inbuf) == NULL)
639                                 continue;
640
641                         context.did_prescan = TRUE;
642                         context.output_row = 0;
643                         context.output_col = 0;
644
645                         context.rowstride = context.width * 3;
646                         context.pixels = g_malloc (context.height * 
647                                                    context.width  * 3);
648                         if (!context.pixels) {
649                                 /* Failed to allocate memory */
650                                 g_error ("Couldn't allocate pixel buf");
651                         }
652                 }
653
654                 /* if we got here we're reading image data */
655                 while (context.output_row < context.height) {
656
657                         rc = pnm_read_scanline (&context);
658
659                         if (rc == PNM_SUSPEND) {
660                                 break;
661                         } else if (rc == PNM_FATAL_ERR) {
662                                 if (context.pixbuf)
663                                         gdk_pixbuf_unref (context.pixbuf);
664                                 g_warning ("io-pnm.c: error reading rows..\n");
665                                 return NULL;
666                         }
667                 }
668
669                 if (context.output_row < context.height)
670                         continue;
671                 else
672                         break;
673         }
674
675         return gdk_pixbuf_new_from_data (context.pixels, ART_PIX_RGB, FALSE,
676                                          context.width, context.height, 
677                                          context.width * 3, free_buffer, NULL);
678
679 }
680
681 /* 
682  * func - called when we have pixmap created (but no image data)
683  * user_data - passed as arg 1 to func
684  * return context (opaque to user)
685  */
686
687 gpointer
688 image_begin_load (ModulePreparedNotifyFunc prepared_func, 
689                   ModuleUpdatedNotifyFunc  updated_func,
690                   gpointer user_data)
691 {
692         PnmLoaderContext *context;
693
694         context = g_new0 (PnmLoaderContext, 1);
695         context->prepared_func = prepared_func;
696         context->updated_func  = updated_func;
697         context->user_data = user_data;
698         context->pixbuf = NULL;
699         context->pixels = NULL;
700         context->got_header = FALSE;
701         context->did_prescan = FALSE;
702
703         context->inbuf.bytes_left = 0;
704         context->inbuf.next_byte  = NULL;
705
706         return (gpointer) context;
707 }
708
709 /*
710  * context - returned from image_begin_load
711  *
712  * free context, unref gdk_pixbuf
713  */
714 void
715 image_stop_load (gpointer data)
716 {
717         PnmLoaderContext *context = (PnmLoaderContext *) data;
718
719         g_return_if_fail (context != NULL);
720
721         if (context->pixbuf)
722                 gdk_pixbuf_unref (context->pixbuf);
723
724         g_free (context);
725 }
726
727
728
729
730 /*
731  * context - from image_begin_load
732  * buf - new image data
733  * size - length of new image data
734  *
735  * append image data onto inrecrementally built output image
736  */
737 gboolean
738 image_load_increment (gpointer data, guchar *buf, guint size)
739 {
740         PnmLoaderContext *context = (PnmLoaderContext *)data;
741         PnmIOBuffer      *inbuf;
742
743         guchar *old_next_byte;
744         guint  old_bytes_left;
745         guchar *bufhd;
746         guint  num_left, spinguard;
747         gint   rc;
748
749         g_return_val_if_fail (context != NULL, FALSE);
750         g_return_val_if_fail (buf != NULL, FALSE);
751
752         bufhd = buf;
753         inbuf = &context->inbuf;
754         old_bytes_left = inbuf->bytes_left;
755         old_next_byte  = inbuf->next_byte;
756
757         num_left = size;
758         spinguard = 0;
759         while (TRUE) {
760                 guint num_to_copy;
761
762                 /* keep buffer as full as possible */
763                 num_to_copy = MIN (PNM_BUF_SIZE - inbuf->bytes_left, num_left);
764                 
765                 if (num_to_copy == 0)
766                         spinguard++;
767
768                 if (spinguard > 1)
769                         return TRUE;
770
771                 if (inbuf->next_byte != NULL && inbuf->bytes_left > 0)
772                         memmove (inbuf->buffer, inbuf->next_byte, 
773                                  inbuf->bytes_left);
774
775                 memcpy (inbuf->buffer + inbuf->bytes_left, bufhd, num_to_copy);
776                 bufhd += num_to_copy;
777                 inbuf->bytes_left += num_to_copy;
778                 inbuf->next_byte   = inbuf->buffer;
779                 num_left -= num_to_copy;
780                 
781                 /* ran out of data and we haven't exited main loop */
782                 if (inbuf->bytes_left == 0)
783                         return TRUE;
784
785                 /* get header if needed */
786                 if (!context->got_header) {
787                         rc = pnm_read_header (context);
788                         if (rc == PNM_FATAL_ERR)
789                                 return FALSE;
790                         else if (rc == PNM_SUSPEND)
791                                 continue;
792
793                         context->got_header = TRUE;
794                 }
795
796                 /* scan until we hit image data */
797                 if (!context->did_prescan) {
798                         if (skip_ahead_whitespace (inbuf) == NULL)
799                                 continue;
800
801                         context->did_prescan = TRUE;
802                         context->output_row = 0;
803                         context->output_col = 0;
804
805                         context->pixbuf = gdk_pixbuf_new(ART_PIX_RGB, 
806                                                          /*have_alpha*/ FALSE,
807                                                          8, 
808                                                          context->width,
809                                                          context->height);
810
811                         if (context->pixbuf == NULL) {
812                                 /* Failed to allocate memory */
813                                 g_error ("Couldn't allocate gdkpixbuf");
814                         }
815
816                         context->pixels = context->pixbuf->art_pixbuf->pixels;
817                         context->rowstride = context->pixbuf->art_pixbuf->rowstride;
818
819                         /* Notify the client that we are ready to go */
820                         (* context->prepared_func) (context->pixbuf,
821                                                     context->user_data);
822
823                 }
824
825                 /* if we got here we're reading image data */
826                 while (context->output_row < context->height) {
827                         rc = pnm_read_scanline (context);
828
829                         if (rc == PNM_SUSPEND) {
830                                 break;
831                         } else if (rc == PNM_FATAL_ERR) {
832                                 if (context->pixbuf)
833                                         gdk_pixbuf_unref (context->pixbuf);
834                                 g_warning ("io-pnm.c: error reading rows..\n");
835                                 return FALSE;
836                         } else if (rc == PNM_OK) {
837
838                                 /* send updated signal */
839                                 (* context->updated_func) (context->pixbuf,
840                                                  context->user_data,
841                                                  0, 
842                                                  context->output_row-1,
843                                                  context->width, 
844                                                  1);
845                         }
846                 }
847
848                 if (context->output_row < context->height)
849                         continue;
850                 else
851                         break;
852         }
853
854         return TRUE;
855 }