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