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