]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/io-jpeg.c
Add ico and wbmp tests. Fail with a suitable error if buffer allocation
[~andy/gtk] / gdk-pixbuf / io-jpeg.c
1 /* GdkPixbuf library - JPEG image loader
2  *
3  * Copyright (C) 1999 Michael Zucchi
4  * Copyright (C) 1999 The Free Software Foundation
5  * 
6  * Progressive loading code Copyright (C) 1999 Red Hat, Inc.
7  *
8  * Authors: Michael Zucchi <zucchi@zedzone.mmc.com.au>
9  *          Federico Mena-Quintero <federico@gimp.org>
10  *          Michael Fulbright <drmike@redhat.com>
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public
14  * License as published by the Free Software Foundation; either
15  * version 2 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public
23  * License along with this library; if not, write to the
24  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25  * Boston, MA 02111-1307, USA.
26  */
27
28
29 /*
30   Progressive file loading notes (11/03/1999) <drmike@redhat.com>...
31
32   These are issues I know of and will be dealing with shortly:
33
34     -  Currently does not handle progressive jpegs - this
35        requires a change in the way image_load_increment () calls
36        libjpeg. Progressive jpegs are rarer but I will add this
37        support asap.
38
39     - error handling is not as good as it should be
40
41  */
42
43
44 #include <config.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <setjmp.h>
49 #include <jpeglib.h>
50 #include "gdk-pixbuf-private.h"
51 #include "gdk-pixbuf-io.h"
52
53 #ifndef HAVE_SIGSETJMP
54 #define sigjmp_buf jmp_buf
55 #define sigsetjmp(jb, x) setjmp(jb)
56 #define siglongjmp longjmp
57 #endif
58 \f
59
60 /* we are a "source manager" as far as libjpeg is concerned */
61 #define JPEG_PROG_BUF_SIZE 4096
62
63 typedef struct {
64         struct jpeg_source_mgr pub;   /* public fields */
65
66         JOCTET buffer[JPEG_PROG_BUF_SIZE];              /* start of buffer */
67         long  skip_next;              /* number of bytes to skip next read */
68
69 } my_source_mgr;
70
71 typedef my_source_mgr * my_src_ptr;
72
73 /* error handler data */
74 struct error_handler_data {
75         struct jpeg_error_mgr pub;
76         sigjmp_buf setjmp_buffer;
77         GError **error;
78 };
79
80 /* progressive loader context */
81 typedef struct {
82         ModuleUpdatedNotifyFunc  updated_func;
83         ModulePreparedNotifyFunc prepared_func;
84         gpointer                 user_data;
85         
86         GdkPixbuf                *pixbuf;
87         guchar                   *dptr;   /* current position in pixbuf */
88
89         gboolean                 did_prescan;  /* are we in image data yet? */
90         gboolean                 got_header;  /* have we loaded jpeg header? */
91         gboolean                 src_initialized;/* TRUE when jpeg lib initialized */
92         struct jpeg_decompress_struct cinfo;
93         struct error_handler_data     jerr;
94 } JpegProgContext;
95
96 static GdkPixbuf *gdk_pixbuf__jpeg_image_load (FILE *f, GError **error);
97 static gpointer gdk_pixbuf__jpeg_image_begin_load (ModulePreparedNotifyFunc func, 
98                                                    ModuleUpdatedNotifyFunc func2,
99                                                    gpointer user_data,
100                                                    GError **error);
101 static gboolean gdk_pixbuf__jpeg_image_stop_load (gpointer context, GError **error);
102 static gboolean gdk_pixbuf__jpeg_image_load_increment(gpointer context,
103                                                       const guchar *buf, guint size,
104                                                       GError **error);
105
106
107 static void
108 fatal_error_handler (j_common_ptr cinfo)
109 {
110         struct error_handler_data *errmgr;
111         char buffer[JMSG_LENGTH_MAX];
112         
113         errmgr = (struct error_handler_data *) cinfo->err;
114         
115         /* Create the message */
116         (* cinfo->err->format_message) (cinfo, buffer);
117
118         /* broken check for *error == NULL for robustness against
119          * crappy JPEG library
120          */
121         if (errmgr->error && *errmgr->error == NULL) {
122                 g_set_error (errmgr->error,
123                              GDK_PIXBUF_ERROR,
124                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
125                              _("Error interpreting JPEG image file (%s)"),
126                              buffer);
127         }
128         
129         siglongjmp (errmgr->setjmp_buffer, 1);
130
131         g_assert_not_reached ();
132 }
133
134 static void
135 output_message_handler (j_common_ptr cinfo)
136 {
137   /* This method keeps libjpeg from dumping crap to stderr */
138
139   /* do nothing */
140 }
141
142 /* Destroy notification function for the pixbuf */
143 static void
144 free_buffer (guchar *pixels, gpointer data)
145 {
146         g_free (pixels);
147 }
148
149
150 /* explode gray image data from jpeg library into rgb components in pixbuf */
151 static void
152 explode_gray_into_buf (struct jpeg_decompress_struct *cinfo,
153                        guchar **lines) 
154 {
155         gint i, j;
156         guint w;
157
158         g_return_if_fail (cinfo != NULL);
159         g_return_if_fail (cinfo->output_components == 1);
160
161         /* Expand grey->colour.  Expand from the end of the
162          * memory down, so we can use the same buffer.
163          */
164         w = cinfo->image_width;
165         for (i = cinfo->rec_outbuf_height - 1; i >= 0; i--) {
166                 guchar *from, *to;
167                 
168                 from = lines[i] + w - 1;
169                 to = lines[i] + (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
180 /* Shared library entry point */
181 static GdkPixbuf *
182 gdk_pixbuf__jpeg_image_load (FILE *f, GError **error)
183 {
184         gint w, h, i;
185         guchar *pixels = NULL;
186         guchar *dptr;
187         guchar *lines[4]; /* Used to expand rows, via rec_outbuf_height, 
188                            * from the header file: 
189                            * " Usually rec_outbuf_height will be 1 or 2, 
190                            * at most 4."
191                            */
192         guchar **lptr;
193         struct jpeg_decompress_struct cinfo;
194         struct error_handler_data jerr;
195
196         /* setup error handler */
197         cinfo.err = jpeg_std_error (&jerr.pub);
198         jerr.pub.error_exit = fatal_error_handler;
199         jerr.pub.output_message = output_message_handler;
200
201         jerr.error = error;
202         
203         if (sigsetjmp (jerr.setjmp_buffer, 1)) {
204                 /* Whoops there was a jpeg error */
205                 if (pixels)
206                         free (pixels);
207
208                 jpeg_destroy_decompress (&cinfo);
209                 return NULL;
210         }
211
212         /* load header, setup */
213         jpeg_create_decompress (&cinfo);
214         jpeg_stdio_src (&cinfo, f);
215         jpeg_read_header (&cinfo, TRUE);
216         jpeg_start_decompress (&cinfo);
217         cinfo.do_fancy_upsampling = FALSE;
218         cinfo.do_block_smoothing = FALSE;
219
220         w = cinfo.output_width;
221         h = cinfo.output_height;
222
223         pixels = g_try_malloc (h * w * 3);
224         if (!pixels) {
225                 jpeg_destroy_decompress (&cinfo);
226
227                 /* broken check for *error == NULL for robustness against
228                  * crappy JPEG library
229                  */
230                 if (error && *error == NULL) {
231                         g_set_error (error,
232                                      GDK_PIXBUF_ERROR,
233                                      GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
234                                      _("Insufficient memory to load image, try exiting some applications to free memory"));
235                 }
236                 
237                 return NULL;
238         }
239
240         dptr = pixels;
241
242         /* decompress all the lines, a few at a time */
243
244         while (cinfo.output_scanline < cinfo.output_height) {
245                 lptr = lines;
246                 for (i = 0; i < cinfo.rec_outbuf_height; i++) {
247                         *lptr++ = dptr;
248                         dptr += w * 3;
249                 }
250
251                 jpeg_read_scanlines (&cinfo, lines, cinfo.rec_outbuf_height);
252
253                 if (cinfo.output_components == 1)
254                         explode_gray_into_buf (&cinfo, lines);
255         }
256
257         jpeg_finish_decompress (&cinfo);
258         jpeg_destroy_decompress (&cinfo);
259
260         return gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, FALSE, 8,
261                                          w, h, w * 3,
262                                          free_buffer, NULL);
263 }
264
265
266 /**** Progressive image loading handling *****/
267
268 /* these routines required because we are acting as a source manager for */
269 /* libjpeg. */
270 static void
271 init_source (j_decompress_ptr cinfo)
272 {
273         my_src_ptr src = (my_src_ptr) cinfo->src;
274
275         src->skip_next = 0;
276 }
277
278
279 static void
280 term_source (j_decompress_ptr cinfo)
281 {
282         /* XXXX - probably should scream something has happened */
283 }
284
285
286 /* for progressive loading (called "I/O Suspension" by libjpeg docs) */
287 /* we do nothing except return "FALSE"                               */
288 static boolean
289 fill_input_buffer (j_decompress_ptr cinfo)
290 {
291         return FALSE;
292 }
293
294
295 static void
296 skip_input_data (j_decompress_ptr cinfo, long num_bytes)
297 {
298         my_src_ptr src = (my_src_ptr) cinfo->src;
299         long   num_can_do;
300
301         /* move as far as we can into current buffer */
302         /* then set skip_next to catch the rest      */
303         if (num_bytes > 0) {
304                 num_can_do = MIN (src->pub.bytes_in_buffer, num_bytes);
305                 src->pub.next_input_byte += (size_t) num_can_do;
306                 src->pub.bytes_in_buffer -= (size_t) num_can_do;
307
308                 src->skip_next = num_bytes - num_can_do;
309         }
310 }
311
312  
313 /* 
314  * func - called when we have pixmap created (but no image data)
315  * user_data - passed as arg 1 to func
316  * return context (opaque to user)
317  */
318
319 gpointer
320 gdk_pixbuf__jpeg_image_begin_load (ModulePreparedNotifyFunc prepared_func, 
321                                    ModuleUpdatedNotifyFunc  updated_func,
322                                    gpointer user_data,
323                                    GError **error)
324 {
325         JpegProgContext *context;
326         my_source_mgr   *src;
327
328         context = g_new0 (JpegProgContext, 1);
329         context->prepared_func = prepared_func;
330         context->updated_func  = updated_func;
331         context->user_data = user_data;
332         context->pixbuf = NULL;
333         context->got_header = FALSE;
334         context->did_prescan = FALSE;
335         context->src_initialized = FALSE;
336
337         /* create libjpeg structures */
338         jpeg_create_decompress (&context->cinfo);
339
340         context->cinfo.src = (struct jpeg_source_mgr *) g_new0 (my_source_mgr, 1);
341         src = (my_src_ptr) context->cinfo.src;
342
343         context->cinfo.err = jpeg_std_error (&context->jerr.pub);
344         context->jerr.pub.error_exit = fatal_error_handler;
345         context->jerr.pub.output_message = output_message_handler;
346         context->jerr.error = error;
347         
348         src = (my_src_ptr) context->cinfo.src;
349         src->pub.init_source = init_source;
350         src->pub.fill_input_buffer = fill_input_buffer;
351         src->pub.skip_input_data = skip_input_data;
352         src->pub.resync_to_restart = jpeg_resync_to_restart;
353         src->pub.term_source = term_source;
354         src->pub.bytes_in_buffer = 0;
355         src->pub.next_input_byte = NULL;
356
357         context->jerr.error = NULL;
358         
359         return (gpointer) context;
360 }
361
362 /*
363  * context - returned from image_begin_load
364  *
365  * free context, unref gdk_pixbuf
366  */
367 static gboolean
368 gdk_pixbuf__jpeg_image_stop_load (gpointer data, GError **error)
369 {
370         JpegProgContext *context = (JpegProgContext *) data;
371
372         g_return_val_if_fail (context != NULL, TRUE);
373
374         /* FIXME this thing needs to report errors if
375          * we have unused image data
376          */
377         
378         if (context->pixbuf)
379                 gdk_pixbuf_unref (context->pixbuf);
380
381         /* if we have an error? */
382         if (sigsetjmp (context->jerr.setjmp_buffer, 1)) {
383                 jpeg_destroy_decompress (&context->cinfo);
384         } else {
385                 jpeg_finish_decompress(&context->cinfo);
386                 jpeg_destroy_decompress(&context->cinfo);
387         }
388
389         if (context->cinfo.src) {
390                 my_src_ptr src = (my_src_ptr) context->cinfo.src;
391                 
392                 g_free (src);
393         }
394
395         g_free (context);
396
397         return TRUE;
398 }
399
400
401
402
403 /*
404  * context - from image_begin_load
405  * buf - new image data
406  * size - length of new image data
407  *
408  * append image data onto inrecrementally built output image
409  */
410 static gboolean
411 gdk_pixbuf__jpeg_image_load_increment (gpointer data,
412                                        const guchar *buf, guint size,
413                                        GError **error)
414 {
415         JpegProgContext *context = (JpegProgContext *)data;
416         struct jpeg_decompress_struct *cinfo;
417         my_src_ptr  src;
418         guint       num_left, num_copy;
419         guint       last_bytes_left;
420         guint       spinguard;
421         gboolean    first;
422         const guchar *bufhd;
423
424         g_return_val_if_fail (context != NULL, FALSE);
425         g_return_val_if_fail (buf != NULL, FALSE);
426
427         src = (my_src_ptr) context->cinfo.src;
428
429         cinfo = &context->cinfo;
430
431         context->jerr.error = error;
432         
433         /* XXXXXXX (drmike) - loop(s) below need to be recoded now I
434          *                    have a grasp of what the flow needs to be!
435          */
436
437         /* check for fatal error */
438         if (sigsetjmp (context->jerr.setjmp_buffer, 1)) {
439                 return FALSE;
440         }
441
442         /* skip over data if requested, handle unsigned int sizes cleanly */
443         /* only can happen if we've already called jpeg_get_header once   */
444         if (context->src_initialized && src->skip_next) {
445                 if (src->skip_next > size) {
446                         src->skip_next -= size;
447                         return TRUE;
448                 } else {
449                         num_left = size - src->skip_next;
450                         bufhd = buf + src->skip_next;
451                         src->skip_next = 0;
452                 }
453         } else {
454                 num_left = size;
455                 bufhd = buf;
456         }
457
458         if (num_left == 0)
459                 return TRUE;
460
461         last_bytes_left = 0;
462         spinguard = 0;
463         first = TRUE;
464         while (TRUE) {
465
466                 /* handle any data from caller we haven't processed yet */
467                 if (num_left > 0) {
468                         if(src->pub.bytes_in_buffer && 
469                            src->pub.next_input_byte != src->buffer)
470                                 memmove(src->buffer, src->pub.next_input_byte,
471                                         src->pub.bytes_in_buffer);
472
473
474                         num_copy = MIN (JPEG_PROG_BUF_SIZE - src->pub.bytes_in_buffer,
475                                         num_left);
476
477 /*                      if (num_copy == 0) 
478                                 g_error ("Buffer overflow!");
479 */
480                         memcpy(src->buffer + src->pub.bytes_in_buffer, bufhd,num_copy);
481                         src->pub.next_input_byte = src->buffer;
482                         src->pub.bytes_in_buffer += num_copy;
483                         bufhd += num_copy;
484                         num_left -= num_copy;
485                 } else {
486                 /* did anything change from last pass, if not return */
487                         if (first) {
488                                 last_bytes_left = src->pub.bytes_in_buffer;
489                                 first = FALSE;
490                         } else if (src->pub.bytes_in_buffer == last_bytes_left)
491                                 spinguard++;
492                         else
493                                 last_bytes_left = src->pub.bytes_in_buffer;
494                 }
495
496                 /* should not go through twice and not pull bytes out of buf */
497                 if (spinguard > 2)
498                         return TRUE;
499
500                 /* try to load jpeg header */
501                 if (!context->got_header) {
502                         int rc;
503
504                         rc = jpeg_read_header (cinfo, TRUE);
505                         context->src_initialized = TRUE;
506
507                         if (rc == JPEG_SUSPENDED)
508                                 continue;
509
510                         context->got_header = TRUE;
511
512 #if 0
513                         if (jpeg_has_multiple_scans (cinfo)) {
514                                 g_print ("io-jpeg.c: Does not currently "
515                                          "support progressive jpeg files.\n");
516                                 return FALSE;
517                         }
518 #endif
519                         context->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, 
520                                                          FALSE,
521                                                          8, 
522                                                          cinfo->image_width,
523                                                          cinfo->image_height);
524
525                         if (context->pixbuf == NULL) {
526                                 g_set_error (error,
527                                              GDK_PIXBUF_ERROR,
528                                              GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
529                                              _("Couldn't allocate memory for loading JPEG file"));
530                                 return FALSE;
531                         }
532
533                         /* Use pixbuf buffer to store decompressed data */
534                         context->dptr = context->pixbuf->pixels;
535
536                         /* Notify the client that we are ready to go */
537                         (* context->prepared_func) (context->pixbuf,
538                                                     NULL,
539                                                     context->user_data);
540
541                 } else if (!context->did_prescan) {
542                         int rc;
543
544                         /* start decompression */
545                         rc = jpeg_start_decompress (cinfo);
546                         cinfo->do_fancy_upsampling = FALSE;
547                         cinfo->do_block_smoothing = FALSE;
548
549                         if (rc == JPEG_SUSPENDED)
550                                 continue;
551
552                         context->did_prescan = TRUE;
553                 } else {
554
555                         /* we're decompressing so feed jpeg lib scanlines */
556                         guchar *lines[4];
557                         guchar **lptr;
558                         guchar *rowptr;
559                         gint   nlines, i;
560                         gint   start_scanline;
561
562                         /* keep going until we've done all scanlines */
563                         while (cinfo->output_scanline < cinfo->output_height) {
564                                 start_scanline = cinfo->output_scanline;
565                                 lptr = lines;
566                                 rowptr = context->dptr;
567                                 for (i=0; i < cinfo->rec_outbuf_height; i++) {
568                                         *lptr++ = rowptr;
569                                         rowptr += context->pixbuf->rowstride;
570                                 }
571
572                                 nlines = jpeg_read_scanlines (cinfo, lines,
573                                                               cinfo->rec_outbuf_height);
574                                 if (nlines == 0)
575                                         break;
576
577                                 /* handle gray */
578                                 if (cinfo->output_components == 1)
579                                         explode_gray_into_buf (cinfo, lines);
580
581                                 context->dptr += nlines * context->pixbuf->rowstride;
582
583                                 /* send updated signal */
584                                 (* context->updated_func) (context->pixbuf,
585                                                            0, 
586                                                            cinfo->output_scanline-1,
587                                                            cinfo->image_width, 
588                                                            nlines,
589                                                            context->user_data);
590
591 #undef DEBUG_JPEG_PROGRESSIVE
592 #ifdef DEBUG_JPEG_PROGRESSIVE
593                                 
594                                 if (start_scanline != cinfo->output_scanline)
595                                         g_print("jpeg: Input pass=%2d, next input scanline=%3d,"
596                                                 " emitted %3d - %3d\n",
597                                                 cinfo->input_scan_number, cinfo->input_iMCU_row * 16,
598                                                 start_scanline, cinfo->output_scanline - 1);
599                                 
600                                 
601                                 
602                                 g_print ("Scanline %d of %d - ", 
603                                          cinfo->output_scanline,
604                                          cinfo->output_height);
605 /*                      g_print ("rec_height %d -", cinfo->rec_outbuf_height); */
606                                 g_print ("Processed %d lines - bytes left = %d\n",
607                                          nlines, cinfo->src->bytes_in_buffer);
608 #endif
609                         }
610                         /* did entire image */
611                         if (cinfo->output_scanline >= cinfo->output_height)
612                                 return TRUE;
613                         else
614                                 continue;
615                 }
616         }
617
618         return TRUE;
619 }
620
621 static gboolean
622 gdk_pixbuf__jpeg_image_save (FILE          *f, 
623                              GdkPixbuf     *pixbuf, 
624                              gchar        **keys,
625                              gchar        **values,
626                              GError       **error)
627 {
628         /* FIXME error handling is broken */
629         
630        struct jpeg_compress_struct cinfo;
631        guchar *buf = NULL;
632        guchar *ptr;
633        guchar *pixels = NULL;
634        JSAMPROW *jbuf;
635        int y = 0;
636        int quality = 75; /* default; must be between 0 and 100 */
637        int i, j;
638        int w, h = 0;
639        int rowstride = 0;
640        struct error_handler_data jerr;
641
642        if (keys && *keys) {
643                gchar **kiter = keys;
644                gchar **viter = values;
645
646                while (*kiter) {
647                        if (strcmp (*kiter, "quality") == 0) {
648                                char *endptr = NULL;
649                                quality = strtol (*viter, &endptr, 10);
650
651                                if (endptr == *viter) {
652                                        g_set_error (error,
653                                                     GDK_PIXBUF_ERROR,
654                                                     GDK_PIXBUF_ERROR_BAD_OPTION_VALUE,
655                                                     _("JPEG quality must be a value between 0 and 100; value '%s' could not be parsed."),
656                                                     *viter);
657
658                                        return FALSE;
659                                }
660                                
661                                if (quality < 0 ||
662                                    quality > 100) {
663                                        /* This is a user-visible error;
664                                         * lets people skip the range-checking
665                                         * in their app.
666                                         */
667                                        g_set_error (error,
668                                                     GDK_PIXBUF_ERROR,
669                                                     GDK_PIXBUF_ERROR_BAD_OPTION_VALUE,
670                                                     _("JPEG quality must be a value between 0 and 100; value '%d' is not allowed."),
671                                                     quality);
672
673                                        return FALSE;
674                                }
675                        } else {
676                                g_warning ("Bad option name '%s' passed to JPEG saver",
677                                           *kiter);
678                                return FALSE;
679                        }
680                
681                        ++kiter;
682                        ++viter;
683                }
684        }
685        
686        rowstride = gdk_pixbuf_get_rowstride (pixbuf);
687
688        w = gdk_pixbuf_get_width (pixbuf);
689        h = gdk_pixbuf_get_height (pixbuf);
690
691        /* no image data? abort */
692        pixels = gdk_pixbuf_get_pixels (pixbuf);
693        g_return_val_if_fail (pixels != NULL, FALSE);
694
695        /* allocate a small buffer to convert image data */
696        buf = g_try_malloc (w * 3 * sizeof (guchar));
697        if (!buf) {
698                g_set_error (error,
699                             GDK_PIXBUF_ERROR,
700                             GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
701                             _("Couldn't allocate memory for loading JPEG file"));
702                return FALSE;
703        }
704
705        /* set up error handling */
706        jerr.pub.error_exit = fatal_error_handler;
707        jerr.pub.output_message = output_message_handler;
708        jerr.error = error;
709        
710        cinfo.err = jpeg_std_error (&(jerr.pub));
711        if (sigsetjmp (jerr.setjmp_buffer, 1)) {
712                jpeg_destroy_compress (&cinfo);
713                free (buf);
714                return FALSE;
715        }
716
717        /* setup compress params */
718        jpeg_create_compress (&cinfo);
719        jpeg_stdio_dest (&cinfo, f);
720        cinfo.image_width      = w;
721        cinfo.image_height     = h;
722        cinfo.input_components = 3; 
723        cinfo.in_color_space   = JCS_RGB;
724
725        /* set up jepg compression parameters */
726        jpeg_set_defaults (&cinfo);
727        jpeg_set_quality (&cinfo, quality, TRUE);
728        jpeg_start_compress (&cinfo, TRUE);
729        /* get the start pointer */
730        ptr = pixels;
731        /* go one scanline at a time... and save */
732        i = 0;
733        while (cinfo.next_scanline < cinfo.image_height) {
734                /* convert scanline from ARGB to RGB packed */
735                for (j = 0; j < w; j++)
736                        memcpy (&(buf[j*3]), &(ptr[i*rowstride + j*3]), 3);
737
738                /* write scanline */
739                jbuf = (JSAMPROW *)(&buf);
740                jpeg_write_scanlines (&cinfo, jbuf, 1);
741                i++;
742                y++;
743
744        }
745        
746        /* finish off */
747        jpeg_finish_compress (&cinfo);   
748        free (buf);
749        return TRUE;
750 }
751
752 void
753 gdk_pixbuf__jpeg_fill_vtable (GdkPixbufModule *module)
754 {
755   module->load = gdk_pixbuf__jpeg_image_load;
756   module->begin_load = gdk_pixbuf__jpeg_image_begin_load;
757   module->stop_load = gdk_pixbuf__jpeg_image_stop_load;
758   module->load_increment = gdk_pixbuf__jpeg_image_load_increment;
759   module->save = gdk_pixbuf__jpeg_image_save;
760 }