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