]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/io-pnm.c
Added support for ASCII PNM files. Note I haven't tested loaded ASCII PBM
[~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 /* Notes (11/05/99):
24  *
25  * raw pnm, pgm, and pbm files will load now.
26  *
27  * ascii support coming soon.
28  *
29  * next will be incremental loading.
30  */
31  
32
33 #include <config.h>
34 #include <ctype.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <setjmp.h>
39 #include <jpeglib.h>
40 #include "gdk-pixbuf.h"
41 #include "gdk-pixbuf-io.h"
42
43 \f
44
45
46 #define PNM_BUF_SIZE 4096
47
48 #define PNM_SUSPEND    0
49 #define PNM_OK         1
50 #define PNM_FATAL_ERR  -1
51
52 typedef enum {
53         PNM_FORMAT_PGM,
54         PNM_FORMAT_PGM_RAW,
55         PNM_FORMAT_PPM,
56         PNM_FORMAT_PPM_RAW,
57         PNM_FORMAT_PBM,
58         PNM_FORMAT_PBM_RAW
59 } PnmFormat;
60
61 typedef struct {
62         guchar    buffer[PNM_BUF_SIZE];
63         guchar    *next_byte;
64         guint     bytes_left;
65 } PnmIOBuffer;
66
67 typedef struct {
68         ModuleUpdatedNotifyFunc  updated_func;
69         ModulePreparedNotifyFunc prepared_func;
70         gpointer                 user_data;
71         
72         GdkPixbuf                *pixbuf;
73         guchar                   *pixels; /* incoming pixel data buffer */
74         guchar                   *dptr;   /* current position in pixbuf */
75
76         PnmIOBuffer              inbuf;
77
78         guint                    width;
79         guint                    height;
80         guint                    maxval;
81         PnmFormat                type;
82
83         guint                    output_row;  /* last row to be completed */
84         guint                    output_col;
85         gboolean                 did_prescan;  /* are we in image data yet? */
86         gboolean                 got_header;  /* have we loaded jpeg header? */
87 } PnmLoaderContext;
88
89 GdkPixbuf *image_load (FILE *f);
90 gpointer image_begin_load (ModulePreparedNotifyFunc func, 
91                            ModuleUpdatedNotifyFunc func2, gpointer user_data);
92 void image_stop_load (gpointer context);
93 gboolean image_load_increment(gpointer context, guchar *buf, guint size);
94
95 static void explode_bitmap_into_buf (PnmLoaderContext *context);
96 static void explode_gray_into_buf (PnmLoaderContext *context);
97
98 /* Destroy notification function for the libart pixbuf */
99 static void
100 free_buffer (gpointer user_data, gpointer data)
101 {
102         free (data);
103 }
104
105
106 /* explode bitmap data into rgb components         */
107 /* we need to know what the row so we can          */
108 /* do sub-byte expansion (since 1 byte = 8 pixels) */
109 /* context->dptr MUST point at first byte in incoming data  */
110 /* which corresponds to first pixel of row y       */
111 static void
112 explode_bitmap_into_buf (PnmLoaderContext *context)
113 {
114         gint j;
115         guchar *from, *to, data;
116         gint bit;
117         guchar *dptr;
118         gint wid, x, y;
119
120         g_return_if_fail (context != NULL);
121         g_return_if_fail (context->dptr != NULL);
122
123         /* I'm no clever bit-hacker so I'm sure this can be optimized */
124         dptr = context->dptr;
125         y    = context->output_row;
126         wid  = context->width;
127
128         from = dptr + (wid - 1)/8;
129         to   = dptr + (wid - 1) * 3;
130         bit  = 7 - (((y+1)*wid - 1) % 8);
131
132         /* get first byte and align properly */
133         data = from[0];
134         for (j = 0; j < bit; j++, data >>= 1);
135
136         for (x = wid-1; x >= 0; x--) {
137
138                 to[0] = to[1] = to[2] = (data & 1) ? 0x00 : 0xff;
139
140                 to -= 3;
141                 bit++;
142
143                 if (bit > 7) {
144                         from--;
145                         data = from[0];
146                         bit = 0;
147                 } else {
148                         data >>= 1;
149                 }
150         }
151 }
152
153 /* explode gray image row into rgb components in pixbuf */
154 static void
155 explode_gray_into_buf (PnmLoaderContext *context)
156 {
157         gint j;
158         guchar *from, *to;
159         guint w;
160
161         g_return_if_fail (context != NULL);
162         g_return_if_fail (context->dptr != NULL);
163
164         /* Expand grey->colour.  Expand from the end of the
165          * memory down, so we can use the same buffer.
166          */
167         w = context->width;
168         from = context->dptr + w - 1;
169         to = context->dptr + (w - 1) * 3;
170         for (j = w - 1; j >= 0; j--) {
171                 to[0] = from[0];
172                 to[1] = from[0];
173                 to[2] = from[0];
174                 to -= 3;
175                 from--;
176         }
177 }
178
179 /* skip over whitespace in file from current pos.                      */
180 /* also skips comments                                                 */
181 /* returns pointer to first non-whitespace char hit or, or NULL if     */
182 /* we ran out of data w/o hitting a whitespace                         */
183 /* internal pointer in inbuf isnt moved ahead in this case             */
184 static guchar *
185 skip_ahead_whitespace (PnmIOBuffer *inbuf)
186 {
187         gboolean in_comment;
188         guchar *ptr;
189         guint num_left;
190         
191         g_return_val_if_fail (inbuf != NULL, NULL);
192         g_return_val_if_fail (inbuf->next_byte != NULL, NULL);
193         
194         in_comment = FALSE;
195         num_left = inbuf->bytes_left;
196         ptr      = inbuf->next_byte;
197         while (num_left > 0) {
198                 if (in_comment) {
199                         if (*ptr == '\n')
200                                 in_comment = FALSE;
201                 } else if (*ptr == '#') {
202                         in_comment = TRUE;
203                 } else if (!isspace (*ptr)) {
204                         inbuf->bytes_left -= (ptr-inbuf->next_byte);
205                         inbuf->next_byte   = ptr;
206                         return ptr;
207                 }
208                 ptr ++;
209                 num_left--;
210         }
211         return NULL;
212 }
213
214 /* reads into buffer until we hit whitespace in file from current pos,     */
215 /* return NULL if ran out of data                                          */
216 /* advances inbuf if successful                                            */
217 static guchar *
218 read_til_whitespace (PnmIOBuffer *inbuf, guchar *buf, guint size)
219 {
220         guchar *p;
221         guchar *ptr;
222         guint num_left;
223
224         g_return_val_if_fail (inbuf != NULL, NULL);
225         g_return_val_if_fail (inbuf->next_byte != NULL, NULL);
226         
227         p = buf;
228         num_left = inbuf->bytes_left;
229         ptr      = inbuf->next_byte;
230         while (num_left > 0 && (p-buf)+1 < size) {
231                 if (isspace (*ptr)) {
232                         *p = '\0';
233                         inbuf->bytes_left = num_left;
234                         inbuf->next_byte  = ptr;
235                         return ptr;
236                 } else {
237                         *p = *ptr;
238                         p++;
239                         ptr++;
240                         num_left--;
241                 }
242         }
243         return NULL;
244 }
245
246 /* read next number from buffer  */
247 /* -1 if failed, 0 if successful */
248 static gint
249 read_next_number (PnmIOBuffer *inbuf, guint *value)
250 {
251         guchar *tmpptr;
252         guchar *old_next_byte;
253         gchar  *errptr;
254         guint  old_bytes_left;
255         guchar buf[128];
256
257         g_return_val_if_fail (inbuf != NULL, -1);
258         g_return_val_if_fail (inbuf->next_byte != NULL, -1);
259         g_return_val_if_fail (value != NULL, -1);
260
261         old_next_byte  = inbuf->next_byte;
262         old_bytes_left = inbuf->bytes_left;
263
264         if ((tmpptr = skip_ahead_whitespace (inbuf)) == NULL)
265                 return -1;
266
267         if ((tmpptr = read_til_whitespace (inbuf, buf, 128)) == NULL) {
268                 inbuf->next_byte  = old_next_byte;
269                 inbuf->bytes_left = old_bytes_left;
270                 return -1;
271         }
272         
273         *value = strtol (buf, &errptr, 10);
274
275         if (*errptr != '\0') {
276                 inbuf->next_byte  = old_next_byte;
277                 inbuf->bytes_left = old_bytes_left;
278                 return -1;
279         }
280
281         return 0;
282 }
283
284 /* returns PNM_OK, PNM_SUSPEND, or PNM_FATAL_ERR */
285 static gint
286 pnm_read_header (PnmLoaderContext *context)
287 {
288         guchar *old_next_byte;
289         guint  old_bytes_left;
290         PnmIOBuffer *inbuf;
291         guint  w, h;
292         gint rc;
293         PnmFormat  type;
294
295         g_return_val_if_fail (context != NULL, PNM_FATAL_ERR);
296
297         inbuf = &context->inbuf;
298         old_bytes_left = inbuf->bytes_left;
299         old_next_byte  = inbuf->next_byte;
300         
301         /* file must start with a 'P' followed by a numeral */
302         /* so loop till we get enough data to determine type*/
303         if (inbuf->bytes_left < 2)
304                 return PNM_SUSPEND;
305         
306         if (*inbuf->next_byte != 'P')
307                 return PNM_FATAL_ERR;
308         
309         switch (*(inbuf->next_byte+1)) {
310         case '1':
311                 type = PNM_FORMAT_PBM;
312                 break;
313         case '2':
314                 type = PNM_FORMAT_PGM;
315                 break;
316         case '3':
317                 type = PNM_FORMAT_PPM;
318                 break;
319         case '4':
320                 type = PNM_FORMAT_PBM_RAW;
321                 break;
322         case '5':
323                 type = PNM_FORMAT_PGM_RAW;
324                 break;
325         case '6':
326                 type = PNM_FORMAT_PPM_RAW;
327                 break;
328         default:
329                 return PNM_FATAL_ERR;
330         }
331         
332         context->type = type;
333
334         inbuf->next_byte  += 2;
335         inbuf->bytes_left -= 2;
336         
337         /* now read remainder of header */
338         if ((rc = read_next_number (inbuf, &w))) {
339                 inbuf->next_byte = old_next_byte;
340                 inbuf->bytes_left = old_bytes_left;
341                 return PNM_SUSPEND;
342         }
343         
344         if ((rc = read_next_number (inbuf, &h))) {
345                 inbuf->next_byte = old_next_byte;
346                 inbuf->bytes_left = old_bytes_left;
347                 return PNM_SUSPEND;
348         }
349         
350         context->width  = w;
351         context->height = h;
352         
353         switch (type) {
354         case PNM_FORMAT_PPM:
355         case PNM_FORMAT_PPM_RAW:
356         case PNM_FORMAT_PGM:
357         case PNM_FORMAT_PGM_RAW:
358                 if ((rc = read_next_number (inbuf, &context->maxval)) < 0) {
359                         inbuf->next_byte = old_next_byte;
360                         inbuf->bytes_left = old_bytes_left;
361                         return PNM_SUSPEND;
362                 }
363                 break;
364         default:
365                 break;
366         }
367
368         return PNM_OK;
369 }
370
371
372 static gint
373 pnm_read_raw_scanline (PnmLoaderContext *context)
374 {
375         guint  numpix;
376         guint  numbytes, offset;
377         PnmIOBuffer *inbuf;
378
379         g_return_val_if_fail (context != NULL, PNM_FATAL_ERR);
380
381         inbuf = &context->inbuf;
382
383         switch (context->type) {
384         case PNM_FORMAT_PBM_RAW:
385                 numpix = inbuf->bytes_left * 8;
386                 break;
387         case PNM_FORMAT_PGM_RAW:
388                 numpix = inbuf->bytes_left;
389                 break;
390         case PNM_FORMAT_PPM_RAW:
391                 numpix = inbuf->bytes_left/3;
392                 break;
393         default:
394                 g_warning ("io-pnm.c: Illegal raw pnm type!\n");
395                 return PNM_FATAL_ERR;
396                 break;
397         }
398         
399         numpix = MIN (numpix, context->width - context->output_col);
400         
401         if (numpix == 0)
402                 return PNM_SUSPEND;
403         
404         context->dptr = context->pixels + 
405                 context->output_row * context->width * 3;
406         
407         switch (context->type) {
408         case PNM_FORMAT_PBM_RAW:
409                 numbytes = numpix/8;
410                 offset = context->output_col/8;
411                 break;
412         case PNM_FORMAT_PGM_RAW:
413                 numbytes = numpix;
414                 offset = context->output_col;
415                 break;
416         case PNM_FORMAT_PPM_RAW:
417                 numbytes = numpix*3;
418                 offset = context->output_col*3;
419                 break;
420         default:
421                 g_warning ("io-pnm.c: Illegal raw pnm type!\n");
422                 break;
423         }
424         
425         memcpy (context->dptr + offset, inbuf->next_byte, numbytes);
426         
427         inbuf->next_byte  += numbytes;
428         inbuf->bytes_left -= numbytes;
429         
430         context->output_col += numpix;
431         if (context->output_col == context->width) {
432                 if ( context->type == PNM_FORMAT_PBM_RAW )
433                         explode_bitmap_into_buf(context);
434                 else if ( context->type == PNM_FORMAT_PGM_RAW )
435                         explode_gray_into_buf (context);
436                 
437                 context->output_col = 0;
438                 context->output_row++;
439                 
440         } else {
441                 return PNM_SUSPEND;
442         }
443
444         return PNM_OK;
445 }
446
447
448 static gint
449 pnm_read_ascii_scanline (PnmLoaderContext *context)
450 {
451         guint  offset;
452         gint   rc;
453         guint  value, numval, i;
454         guchar data;
455         guchar mask;
456         guchar *old_next_byte, *dptr;
457         guint  old_bytes_left;
458         PnmIOBuffer *inbuf;
459
460         g_return_val_if_fail (context != NULL, PNM_FATAL_ERR);
461
462         inbuf = &context->inbuf;
463
464         context->dptr = context->pixels + 
465                 context->output_row * context->width * 3;
466
467         switch (context->type) {
468         case PNM_FORMAT_PBM:
469                 numval = 8;
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                 }
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++ = value;
524                 
525                 context->output_col++;
526                 if (context->output_col == context->width) {
527                         if ( context->type == PNM_FORMAT_PBM )
528                                 explode_bitmap_into_buf(context);
529                         else if ( context->type == PNM_FORMAT_PGM )
530                                 explode_gray_into_buf (context);
531
532                         context->output_col = 0;
533                         context->output_row++;
534                         break;
535                 }
536
537         }
538
539         return PNM_OK;
540 }
541
542 /* returns 1 if a scanline was converted,  0 means we ran out of data */
543 static gint
544 pnm_read_scanline (PnmLoaderContext *context)
545 {
546         gint   rc;
547
548         g_return_val_if_fail (context != NULL, PNM_FATAL_ERR);
549
550         /* read in image data */
551         /* for raw formats this is trivial */
552         switch (context->type) {
553         case PNM_FORMAT_PBM_RAW:
554         case PNM_FORMAT_PGM_RAW:
555         case PNM_FORMAT_PPM_RAW:
556                 rc = pnm_read_raw_scanline (context);
557                 if (rc == PNM_SUSPEND)
558                         return rc;
559                 break;
560
561         case PNM_FORMAT_PBM:
562         case PNM_FORMAT_PGM:
563         case PNM_FORMAT_PPM:
564                 rc = pnm_read_ascii_scanline (context);
565                 if (rc == PNM_SUSPEND)
566                         return rc;
567                 break;
568
569         default:
570                 g_print ("Cannot load these image types (yet)\n");
571                 return PNM_FATAL_ERR;
572         }
573
574         return PNM_OK;
575 }
576
577 /* Shared library entry point */
578 GdkPixbuf *
579 image_load (FILE *f)
580 {
581         guint  nbytes;
582         gint   rc;
583
584         PnmLoaderContext context;
585         PnmIOBuffer *inbuf;
586
587         /* pretend to be doing progressive loading */
588         context.updated_func = context.prepared_func = NULL;
589         context.user_data = NULL;
590         context.inbuf.bytes_left = 0;
591         context.inbuf.next_byte  = NULL;
592         context.pixels = context.pixbuf = NULL;
593         context.got_header = context.did_prescan = FALSE;
594
595         inbuf = &context.inbuf;
596
597         while (TRUE) {
598                 guint  num_to_read;
599
600                 /* keep buffer as full as possible */
601                 num_to_read = PNM_BUF_SIZE - inbuf->bytes_left;
602
603                 if (inbuf->next_byte != NULL && inbuf->bytes_left > 0)
604                         memmove (inbuf->buffer, inbuf->next_byte, 
605                                  inbuf->bytes_left);
606
607                 nbytes = fread (inbuf->buffer+inbuf->bytes_left, 
608                                 1, num_to_read, f);
609                 inbuf->bytes_left += nbytes;
610                 inbuf->next_byte   = inbuf->buffer;
611                 
612                 /* ran out of data and we haven't exited main loop */
613                 /* something is wrong                              */
614                 if (inbuf->bytes_left == 0) {
615                         if (context.pixels)
616                                 g_free (context.pixels);
617                         g_warning ("io-pnm.c: Ran out of data...\n");
618                         return NULL;
619                 }
620
621                 /* get header if needed */
622                 if (!context.got_header) {
623                 
624                         rc = pnm_read_header (&context);
625                         if (rc == PNM_FATAL_ERR)
626                                 return NULL;
627                         else if (rc == PNM_SUSPEND)
628                                 continue;
629
630                         context.got_header = TRUE;
631                 }
632
633                 /* scan until we hit image data */
634                 if (!context.did_prescan) {
635                         if (skip_ahead_whitespace (inbuf) == NULL)
636                                 continue;
637
638                         context.did_prescan = TRUE;
639                         context.output_row = 0;
640                         context.output_col = 0;
641
642                         context.pixels = g_malloc (context.height * 
643                                            context.width * 3);
644                         if (!context.pixels)
645                                 return NULL;
646                 }
647
648                 /* if we got here we're reading image data */
649                 while (context.output_row < context.height) {
650                         rc = pnm_read_scanline (&context);
651
652                         if (rc == PNM_SUSPEND) {
653                                 break;
654                         } else if (rc == PNM_FATAL_ERR) {
655                                 if (context.pixels)
656                                         g_free (context.pixels);
657                                 g_warning ("io-pnm.c: error reading rows..\n");
658                                 return NULL;
659                         }
660                 }
661
662                 if (context.output_row < context.height)
663                         continue;
664                 else
665                         break;
666         }
667
668         return gdk_pixbuf_new_from_data (context.pixels, ART_PIX_RGB, FALSE,
669                                          context.width, context.height, 
670                                          context.width * 3, free_buffer, NULL);
671
672 }
673
674 #if 0
675 /**** Progressive image loading handling *****/
676
677 /* these routines required because we are acting as a source manager for */
678 /* libjpeg. */
679 static void
680 init_source (j_decompress_ptr cinfo)
681 {
682         my_src_ptr src = (my_src_ptr) cinfo->src;
683
684         src->skip_next = 0;
685 }
686
687
688 static void
689 term_source (j_decompress_ptr cinfo)
690 {
691         /* XXXX - probably should scream something has happened */
692 }
693
694
695 /* for progressive loading (called "I/O Suspension" by libjpeg docs) */
696 /* we do nothing except return "FALSE"                               */
697 static boolean
698 fill_input_buffer (j_decompress_ptr cinfo)
699 {
700         return FALSE;
701 }
702
703
704 static void
705 skip_input_data (j_decompress_ptr cinfo, long num_bytes)
706 {
707         my_src_ptr src = (my_src_ptr) cinfo->src;
708         long   num_can_do;
709
710         /* move as far as we can into current buffer */
711         /* then set skip_next to catch the rest      */
712         if (num_bytes > 0) {
713                 num_can_do = MIN (src->pub.bytes_in_buffer, num_bytes);
714                 src->pub.next_input_byte += (size_t) num_can_do;
715                 src->pub.bytes_in_buffer -= (size_t) num_can_do;
716
717                 src->skip_next = num_bytes - num_can_do;
718         }
719 }
720
721  
722 /* 
723  * func - called when we have pixmap created (but no image data)
724  * user_data - passed as arg 1 to func
725  * return context (opaque to user)
726  */
727
728 gpointer
729 image_begin_load (ModulePreparedNotifyFunc prepared_func, 
730                   ModuleUpdatedNotifyFunc  updated_func,
731                   gpointer user_data)
732 {
733         JpegProgContext *context;
734         my_source_mgr   *src;
735
736         context = g_new0 (JpegProgContext, 1);
737         context->prepared_func = prepared_func;
738         context->updated_func  = updated_func;
739         context->user_data = user_data;
740         context->pixbuf = NULL;
741         context->got_header = FALSE;
742         context->did_prescan = FALSE;
743         context->src_initialized = FALSE;
744
745         /* create libjpeg structures */
746         jpeg_create_decompress (&context->cinfo);
747
748         context->cinfo.src = (struct jpeg_source_mgr *) g_new0 (my_source_mgr, 1);
749         src = (my_src_ptr) context->cinfo.src;
750
751         context->cinfo.err = jpeg_std_error (&context->jerr.pub);
752
753         src = (my_src_ptr) context->cinfo.src;
754         src->pub.init_source = init_source;
755         src->pub.fill_input_buffer = fill_input_buffer;
756         src->pub.skip_input_data = skip_input_data;
757         src->pub.resync_to_restart = jpeg_resync_to_restart;
758         src->pub.term_source = term_source;
759         src->pub.bytes_in_buffer = 0;
760         src->pub.next_input_byte = NULL;
761
762         return (gpointer) context;
763 }
764
765 /*
766  * context - returned from image_begin_load
767  *
768  * free context, unref gdk_pixbuf
769  */
770 void
771 image_stop_load (gpointer data)
772 {
773         JpegProgContext *context = (JpegProgContext *) data;
774
775         g_return_if_fail (context != NULL);
776
777         if (context->pixbuf)
778                 gdk_pixbuf_unref (context->pixbuf);
779
780         jpeg_finish_decompress(&context->cinfo);
781         jpeg_destroy_decompress(&context->cinfo);
782
783         if (context->cinfo.src) {
784                 my_src_ptr src = (my_src_ptr) context->cinfo.src;
785                 
786                 g_free (src);
787         }
788
789         g_free (context);
790 }
791
792
793
794
795 /*
796  * context - from image_begin_load
797  * buf - new image data
798  * size - length of new image data
799  *
800  * append image data onto inrecrementally built output image
801  */
802 gboolean
803 image_load_increment (gpointer data, guchar *buf, guint size)
804 {
805         JpegProgContext *context = (JpegProgContext *)data;
806         struct jpeg_decompress_struct *cinfo;
807         my_src_ptr  src;
808         guint       num_left, num_copy;
809         guint       last_bytes_left;
810         guint       spinguard;
811         guchar *bufhd;
812
813         g_return_val_if_fail (context != NULL, FALSE);
814         g_return_val_if_fail (buf != NULL, FALSE);
815
816         src = (my_src_ptr) context->cinfo.src;
817
818         cinfo = &context->cinfo;
819
820         /* XXXXXXX (drmike) - loop(s) below need to be recoded now I
821          *                    have a grasp of what the flow needs to be!
822          */
823
824
825         /* skip over data if requested, handle unsigned int sizes cleanly */
826         /* only can happen if we've already called jpeg_get_header once   */
827         if (context->src_initialized && src->skip_next) {
828                 if (src->skip_next > size) {
829                         src->skip_next -= size;
830                         return TRUE;
831                 } else {
832                         num_left = size - src->skip_next;
833                         bufhd = buf + src->skip_next;
834                         src->skip_next = 0;
835                 }
836         } else {
837                 num_left = size;
838                 bufhd = buf;
839         }
840
841
842         last_bytes_left = 0;
843         spinguard = 0;
844         while (src->pub.bytes_in_buffer != 0 || num_left != 0) {
845
846                 /* handle any data from caller we haven't processed yet */
847                 if (num_left > 0) {
848                         if(src->pub.bytes_in_buffer && 
849                            src->pub.next_input_byte != src->buffer)
850                                 memmove(src->buffer, src->pub.next_input_byte,
851                                         src->pub.bytes_in_buffer);
852
853
854                         num_copy = MIN (JPEG_PROG_BUF_SIZE - src->pub.bytes_in_buffer,
855                                         num_left);
856
857 /*                      if (num_copy == 0) 
858                                 g_error ("Buffer overflow!");
859 */
860                         memcpy(src->buffer + src->pub.bytes_in_buffer, bufhd,num_copy);
861                         src->pub.next_input_byte = src->buffer;
862                         src->pub.bytes_in_buffer += num_copy;
863                         bufhd += num_copy;
864                         num_left -= num_copy;
865                 } else {
866                 /* did anything change from last pass, if not return */
867                         if (last_bytes_left == 0)
868                                 last_bytes_left = src->pub.bytes_in_buffer;
869                         else if (src->pub.bytes_in_buffer == last_bytes_left)
870                                 spinguard++;
871                         else
872                                 last_bytes_left = src->pub.bytes_in_buffer;
873                 }
874
875                 /* should not go through twice and not pull bytes out of buf */
876                 if (spinguard > 2)
877                         return TRUE;
878
879                 /* try to load jpeg header */
880                 if (!context->got_header) {
881                         int rc;
882
883                         rc = jpeg_read_header (cinfo, TRUE);
884                         context->src_initialized = TRUE;
885
886                         if (rc == JPEG_SUSPENDED)
887                                 continue;
888
889                         context->got_header = TRUE;
890
891                         if (jpeg_has_multiple_scans (cinfo)) {
892                                 g_print ("io-jpeg.c: Does not currently "
893                                          "support progressive jpeg files.\n");
894                                 return FALSE;
895                         }
896
897                         context->pixbuf = gdk_pixbuf_new(ART_PIX_RGB, 
898                                                          /*have_alpha*/ FALSE,
899                                                          8, 
900                                                          cinfo->image_width,
901                                                          cinfo->image_height);
902
903                         if (context->pixbuf == NULL) {
904                                 /* Failed to allocate memory */
905                                 g_error ("Couldn't allocate gdkpixbuf");
906                         }
907
908                         /* Use pixbuf buffer to store decompressed data */
909                         context->dptr = context->pixbuf->art_pixbuf->pixels;
910
911                         /* Notify the client that we are ready to go */
912                         (* context->prepared_func) (context->pixbuf,
913                                                     context->user_data);
914
915                 } else if (!context->did_prescan) {
916                         int rc;
917
918                         /* start decompression */
919                         rc = jpeg_start_decompress (cinfo);
920                         cinfo->do_fancy_upsampling = FALSE;
921                         cinfo->do_block_smoothing = FALSE;
922
923                         if (rc == JPEG_SUSPENDED)
924                                 continue;
925
926                         context->did_prescan = TRUE;
927                 } else {
928                         /* we're decompressing so feed jpeg lib scanlines */
929                         guchar *lines[4];
930                         guchar **lptr;
931                         guchar *rowptr, *p;
932                         gint   nlines, i;
933                         gint   start_scanline;
934
935                         /* keep going until we've done all scanlines */
936                         while (cinfo->output_scanline < cinfo->output_height) {
937                                 start_scanline = cinfo->output_scanline;
938                                 lptr = lines;
939                                 rowptr = context->dptr;
940                                 for (i=0; i < cinfo->rec_outbuf_height; i++) {
941                                         *lptr++ = rowptr;
942                                         rowptr += context->pixbuf->art_pixbuf->rowstride;;
943                                 }
944
945 #ifdef IO_JPEG_DEBUG_GREY
946                                 for (p=lines[0],i=0; i< context->pixbuf->art_pixbuf->rowstride;i++, p++)
947                                         *p = 0;
948                                 
949 #endif
950                                 nlines = jpeg_read_scanlines (cinfo, lines,
951                                                               cinfo->rec_outbuf_height);
952                                 if (nlines == 0)
953                                         break;
954
955                                 /* handle gray */
956                                 if (cinfo->output_components == 1)
957                                         explode_gray_into_buf (cinfo, lines);
958
959                                 context->dptr += nlines * context->pixbuf->art_pixbuf->rowstride;
960
961                                 /* send updated signal */
962                                 (* context->updated_func) (context->pixbuf,
963                                                  context->user_data,
964                                                  0, 
965                                                  cinfo->output_scanline-1,
966                                                  cinfo->image_width, 
967                                                  nlines);
968
969 #undef DEBUG_JPEG_PROGRESSIVE
970 #ifdef DEBUG_JPEG_PROGRESSIVE
971                                 
972                                 if (start_scanline != cinfo->output_scanline)
973                                         g_print("jpeg: Input pass=%2d, next input scanline=%3d,"
974                                                 " emitted %3d - %3d\n",
975                                                 cinfo->input_scan_number, cinfo->input_iMCU_row * 16,
976                                                 start_scanline, cinfo->output_scanline - 1);
977                                 
978                                 
979                                 
980                                 g_print ("Scanline %d of %d - ", 
981                                          cinfo->output_scanline,
982                                          cinfo->output_height);
983 /*                      g_print ("rec_height %d -", cinfo->rec_outbuf_height); */
984                                 g_print ("Processed %d lines - bytes left = %d\n",
985                                          nlines, cinfo->src->bytes_in_buffer);
986 #endif
987                         }
988                         /* did entire image */
989                         if (cinfo->output_scanline >= cinfo->output_height)
990                                 return TRUE;
991                         else
992                                 continue;
993                 }
994         }
995
996         return TRUE;
997 }
998 #endif