]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/io-pnm.c
aa1b193916463e2a58f85cf39f7cb36876334f2b
[~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 Lesser 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  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser 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-private.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 *gdk_pixbuf__pnm_image_load (FILE *f);
79 gpointer gdk_pixbuf__pnm_image_begin_load (ModulePreparedNotifyFunc func, 
80                                            ModuleUpdatedNotifyFunc func2,
81                                            ModuleFrameDoneNotifyFunc frame_done_func,
82                                            ModuleAnimationDoneNotifyFunc anim_done_func,
83                                            gpointer user_data);
84 void gdk_pixbuf__pnm_image_stop_load (gpointer context);
85 gboolean gdk_pixbuf__pnm_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 pixbuf */
91 static void
92 free_buffer (guchar *pixels, gpointer data)
93 {
94         free (pixels);
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         }
396         
397         numpix = MIN (numpix, context->width - context->output_col);
398
399         if (numpix == 0)
400                 return PNM_SUSPEND;
401         
402         context->dptr = context->pixels + 
403                 context->output_row * context->rowstride;
404         
405         switch (context->type) {
406         case PNM_FORMAT_PBM_RAW:
407                 numbytes = numpix/8 + ((numpix % 8) ? 1 : 0);
408                 offset = context->output_col/8;
409                 break;
410         case PNM_FORMAT_PGM_RAW:
411                 numbytes = numpix;
412                 offset = context->output_col;
413                 break;
414         case PNM_FORMAT_PPM_RAW:
415                 numbytes = numpix*3;
416                 offset = context->output_col*3;
417                 break;
418         default:
419                 g_warning ("io-pnm.c: Illegal raw pnm type!\n");
420                 return PNM_FATAL_ERR;
421         }
422         
423         memcpy (context->dptr + offset, inbuf->next_byte, numbytes);
424
425         inbuf->next_byte  += numbytes;
426         inbuf->bytes_left -= numbytes;
427
428         context->output_col += numpix;
429         if (context->output_col == context->width) {
430                 if ( context->type == PNM_FORMAT_PBM_RAW )
431                         explode_bitmap_into_buf(context);
432                 else if ( context->type == PNM_FORMAT_PGM_RAW )
433                         explode_gray_into_buf (context);
434                 
435                 context->output_col = 0;
436                 context->output_row++;
437                 
438         } else {
439                 return PNM_SUSPEND;
440         }
441
442         return PNM_OK;
443 }
444
445
446 static gint
447 pnm_read_ascii_scanline (PnmLoaderContext *context)
448 {
449         guint  offset;
450         gint   rc;
451         guint  value, numval, i;
452         guchar data;
453         guchar mask;
454         guchar *old_next_byte, *dptr;
455         guint  old_bytes_left;
456         PnmIOBuffer *inbuf;
457
458         g_return_val_if_fail (context != NULL, PNM_FATAL_ERR);
459
460         data = mask = 0;
461
462         inbuf = &context->inbuf;
463
464         context->dptr = context->pixels + 
465                 context->output_row * context->rowstride;
466
467         switch (context->type) {
468         case PNM_FORMAT_PBM:
469                 numval = MIN (8, context->width - context->output_col);
470                 offset = context->output_col/8;
471                 break;
472         case PNM_FORMAT_PGM:
473                 numval = 1;
474                 offset = context->output_col;
475                 break;
476         case PNM_FORMAT_PPM:
477                 numval = 3;
478                 offset = context->output_col*3;
479                 break;
480                 
481         default:
482                 g_warning ("Can't happen\n");
483                 return PNM_FATAL_ERR;
484         }
485
486         dptr = context->dptr + offset;
487
488         while (TRUE) {
489                 if (context->type == PNM_FORMAT_PBM) {
490                         mask = 0x80;
491                         data = 0;
492                         numval = MIN (8, context->width - context->output_col);
493                 }
494
495                 old_next_byte  = inbuf->next_byte;
496                 old_bytes_left = inbuf->bytes_left;
497                 
498                 for (i=0; i<numval; i++) {
499                         if ((rc = read_next_number (inbuf, &value))) {
500                                 inbuf->next_byte  = old_next_byte;
501                                 inbuf->bytes_left = old_bytes_left;
502                                 return PNM_SUSPEND;
503                         }
504                         switch (context->type) {
505                         case PNM_FORMAT_PBM:
506                                 if (value)
507                                         data |= mask;
508                                 mask >>= 1;
509                                 
510                                 break;
511                         case PNM_FORMAT_PGM:
512                                 *dptr++ = (guchar)(255.0*((double)value/(double)context->maxval));
513                                 break;
514                         case PNM_FORMAT_PPM:
515                                 *dptr++ = (guchar)(255.0*((double)value/(double)context->maxval));
516                                 break;
517                         default:
518                                 g_warning ("io-pnm.c: Illegal raw pnm type!\n");
519                                 break;
520                         }
521                 }
522                 
523                 if (context->type == PNM_FORMAT_PBM) {
524                         *dptr++ = data;
525                         context->output_col += numval;
526                 } else {
527                         context->output_col++;
528                 }
529
530                 if (context->output_col == context->width) {
531                         if ( context->type == PNM_FORMAT_PBM )
532                                 explode_bitmap_into_buf(context);
533                         else if ( context->type == PNM_FORMAT_PGM )
534                                 explode_gray_into_buf (context);
535
536                         context->output_col = 0;
537                         context->output_row++;
538                         break;
539                 }
540
541         }
542
543         return PNM_OK;
544 }
545
546 /* returns 1 if a scanline was converted,  0 means we ran out of data */
547 static gint
548 pnm_read_scanline (PnmLoaderContext *context)
549 {
550         gint   rc;
551
552         g_return_val_if_fail (context != NULL, PNM_FATAL_ERR);
553
554         /* read in image data */
555         /* for raw formats this is trivial */
556         switch (context->type) {
557         case PNM_FORMAT_PBM_RAW:
558         case PNM_FORMAT_PGM_RAW:
559         case PNM_FORMAT_PPM_RAW:
560                 rc = pnm_read_raw_scanline (context);
561                 if (rc == PNM_SUSPEND)
562                         return rc;
563                 break;
564
565         case PNM_FORMAT_PBM:
566         case PNM_FORMAT_PGM:
567         case PNM_FORMAT_PPM:
568                 rc = pnm_read_ascii_scanline (context);
569                 if (rc == PNM_SUSPEND)
570                         return rc;
571                 break;
572
573         default:
574                 g_warning ("Cannot load these image types (yet)\n");
575                 return PNM_FATAL_ERR;
576         }
577
578         return PNM_OK;
579 }
580
581 /* Shared library entry point */
582 GdkPixbuf *
583 gdk_pixbuf__pnm_image_load (FILE *f)
584 {
585         gint  nbytes;
586         gint   rc;
587
588         PnmLoaderContext context;
589         PnmIOBuffer *inbuf;
590
591         /* pretend to be doing progressive loading */
592         context.updated_func = NULL;
593         context.prepared_func = NULL;
594         context.user_data = NULL;
595         context.inbuf.bytes_left = 0;
596         context.inbuf.next_byte  = NULL;
597         context.pixels = NULL;
598         context.pixbuf = NULL;
599         context.got_header = context.did_prescan = FALSE;
600
601         inbuf = &context.inbuf;
602
603         while (TRUE) {
604                 guint  num_to_read;
605
606                 /* keep buffer as full as possible */
607                 num_to_read = PNM_BUF_SIZE - inbuf->bytes_left;
608
609                 if (inbuf->next_byte != NULL && inbuf->bytes_left > 0)
610                         memmove (inbuf->buffer, inbuf->next_byte, 
611                                  inbuf->bytes_left);
612
613                 nbytes = fread (inbuf->buffer+inbuf->bytes_left, 
614                                 1, num_to_read, f);
615                 inbuf->bytes_left += nbytes;
616                 inbuf->next_byte   = inbuf->buffer;
617
618                 /* ran out of data and we haven't exited main loop */
619                 if (inbuf->bytes_left == 0) {
620                         if (context.pixbuf)
621                                 gdk_pixbuf_unref (context.pixbuf);
622                         g_warning ("io-pnm.c: Ran out of data...\n");
623                         return NULL;
624                 }
625
626                 /* get header if needed */
627                 if (!context.got_header) {
628                 
629                         rc = pnm_read_header (&context);
630                         if (rc == PNM_FATAL_ERR)
631                                 return NULL;
632                         else if (rc == PNM_SUSPEND)
633                                 continue;
634
635                         context.got_header = TRUE;
636                 }
637
638                 /* scan until we hit image data */
639                 if (!context.did_prescan) {
640
641                         if (skip_ahead_whitespace (inbuf) == NULL)
642                                 continue;
643
644                         context.did_prescan = TRUE;
645                         context.output_row = 0;
646                         context.output_col = 0;
647
648                         context.rowstride = context.width * 3;
649                         context.pixels = g_malloc (context.height * 
650                                                    context.width  * 3);
651                         if (!context.pixels) {
652                                 /* Failed to allocate memory */
653                                 g_error ("Couldn't allocate pixel buf");
654                         }
655                 }
656
657                 /* if we got here we're reading image data */
658                 while (context.output_row < context.height) {
659
660                         rc = pnm_read_scanline (&context);
661
662                         if (rc == PNM_SUSPEND) {
663                                 break;
664                         } else if (rc == PNM_FATAL_ERR) {
665                                 if (context.pixbuf)
666                                         gdk_pixbuf_unref (context.pixbuf);
667                                 g_warning ("io-pnm.c: error reading rows..\n");
668                                 return NULL;
669                         }
670                 }
671
672                 if (context.output_row < context.height)
673                         continue;
674                 else
675                         break;
676         }
677
678         return gdk_pixbuf_new_from_data (context.pixels, GDK_COLORSPACE_RGB, FALSE, 8,
679                                          context.width, context.height, 
680                                          context.width * 3, free_buffer, NULL);
681
682 }
683
684 /* 
685  * func - called when we have pixmap created (but no image data)
686  * user_data - passed as arg 1 to func
687  * return context (opaque to user)
688  */
689
690 gpointer
691 gdk_pixbuf__pnm_image_begin_load (ModulePreparedNotifyFunc prepared_func, 
692                                   ModuleUpdatedNotifyFunc  updated_func,
693                                   ModuleFrameDoneNotifyFunc frame_done_func,
694                                   ModuleAnimationDoneNotifyFunc anim_done_func,
695                                   gpointer user_data)
696 {
697         PnmLoaderContext *context;
698
699         context = g_new0 (PnmLoaderContext, 1);
700         context->prepared_func = prepared_func;
701         context->updated_func  = updated_func;
702         context->user_data = user_data;
703         context->pixbuf = NULL;
704         context->pixels = NULL;
705         context->got_header = FALSE;
706         context->did_prescan = FALSE;
707
708         context->inbuf.bytes_left = 0;
709         context->inbuf.next_byte  = NULL;
710
711         return (gpointer) context;
712 }
713
714 /*
715  * context - returned from image_begin_load
716  *
717  * free context, unref gdk_pixbuf
718  */
719 void
720 gdk_pixbuf__pnm_image_stop_load (gpointer data)
721 {
722         PnmLoaderContext *context = (PnmLoaderContext *) data;
723
724         g_return_if_fail (context != NULL);
725
726         if (context->pixbuf)
727                 gdk_pixbuf_unref (context->pixbuf);
728
729         g_free (context);
730 }
731
732
733
734
735 /*
736  * context - from image_begin_load
737  * buf - new image data
738  * size - length of new image data
739  *
740  * append image data onto inrecrementally built output image
741  */
742 gboolean
743 gdk_pixbuf__pnm_image_load_increment (gpointer data, guchar *buf, guint size)
744 {
745         PnmLoaderContext *context = (PnmLoaderContext *)data;
746         PnmIOBuffer      *inbuf;
747
748         guchar *old_next_byte;
749         guint  old_bytes_left;
750         guchar *bufhd;
751         guint  num_left, spinguard;
752         gint   rc;
753
754         g_return_val_if_fail (context != NULL, FALSE);
755         g_return_val_if_fail (buf != NULL, FALSE);
756
757         bufhd = buf;
758         inbuf = &context->inbuf;
759         old_bytes_left = inbuf->bytes_left;
760         old_next_byte  = inbuf->next_byte;
761
762         num_left = size;
763         spinguard = 0;
764         while (TRUE) {
765                 guint num_to_copy;
766
767                 /* keep buffer as full as possible */
768                 num_to_copy = MIN (PNM_BUF_SIZE - inbuf->bytes_left, num_left);
769                 
770                 if (num_to_copy == 0)
771                         spinguard++;
772
773                 if (spinguard > 1)
774                         return TRUE;
775
776                 if (inbuf->next_byte != NULL && inbuf->bytes_left > 0)
777                         memmove (inbuf->buffer, inbuf->next_byte, 
778                                  inbuf->bytes_left);
779
780                 memcpy (inbuf->buffer + inbuf->bytes_left, bufhd, num_to_copy);
781                 bufhd += num_to_copy;
782                 inbuf->bytes_left += num_to_copy;
783                 inbuf->next_byte   = inbuf->buffer;
784                 num_left -= num_to_copy;
785                 
786                 /* ran out of data and we haven't exited main loop */
787                 if (inbuf->bytes_left == 0)
788                         return TRUE;
789
790                 /* get header if needed */
791                 if (!context->got_header) {
792                         rc = pnm_read_header (context);
793                         if (rc == PNM_FATAL_ERR)
794                                 return FALSE;
795                         else if (rc == PNM_SUSPEND)
796                                 continue;
797
798                         context->got_header = TRUE;
799                 }
800
801                 /* scan until we hit image data */
802                 if (!context->did_prescan) {
803                         if (skip_ahead_whitespace (inbuf) == NULL)
804                                 continue;
805
806                         context->did_prescan = TRUE;
807                         context->output_row = 0;
808                         context->output_col = 0;
809
810                         context->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, 
811                                                          FALSE,
812                                                          8, 
813                                                          context->width,
814                                                          context->height);
815
816                         if (context->pixbuf == NULL) {
817                                 /* Failed to allocate memory */
818                                 g_error ("Couldn't allocate gdkpixbuf");
819                         }
820
821                         context->pixels = context->pixbuf->pixels;
822                         context->rowstride = context->pixbuf->rowstride;
823
824                         /* Notify the client that we are ready to go */
825                         (* context->prepared_func) (context->pixbuf,
826                                                     context->user_data);
827
828                 }
829
830                 /* if we got here we're reading image data */
831                 while (context->output_row < context->height) {
832                         rc = pnm_read_scanline (context);
833
834                         if (rc == PNM_SUSPEND) {
835                                 break;
836                         } else if (rc == PNM_FATAL_ERR) {
837                                 if (context->pixbuf)
838                                         gdk_pixbuf_unref (context->pixbuf);
839                                 g_warning ("io-pnm.c: error reading rows..\n");
840                                 return FALSE;
841                         } else if (rc == PNM_OK) {
842
843                                 /* send updated signal */
844                                 (* context->updated_func) (context->pixbuf,
845                                                            0, 
846                                                            context->output_row-1,
847                                                            context->width, 
848                                                            1,
849                                                            context->user_data);
850                         }
851                 }
852
853                 if (context->output_row < context->height)
854                         continue;
855                 else
856                         break;
857         }
858
859         return TRUE;
860 }