]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/io-jpeg.c
Pixbuf saving, patch from David Welton.
[~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 };
78
79 /* progressive loader context */
80 typedef struct {
81         ModuleUpdatedNotifyFunc  updated_func;
82         ModulePreparedNotifyFunc prepared_func;
83         gpointer                 user_data;
84         
85         GdkPixbuf                *pixbuf;
86         guchar                   *dptr;   /* current position in pixbuf */
87
88         gboolean                 did_prescan;  /* are we in image data yet? */
89         gboolean                 got_header;  /* have we loaded jpeg header? */
90         gboolean                 src_initialized;/* TRUE when jpeg lib initialized */
91         struct jpeg_decompress_struct cinfo;
92         struct error_handler_data     jerr;
93 } JpegProgContext;
94
95 GdkPixbuf *gdk_pixbuf__jpeg_image_load (FILE *f);
96 gpointer gdk_pixbuf__jpeg_image_begin_load (ModulePreparedNotifyFunc func, 
97                                             ModuleUpdatedNotifyFunc func2,
98                                             ModuleFrameDoneNotifyFunc func3,
99                                             ModuleAnimationDoneNotifyFunc func4,
100                                             gpointer user_data);
101 void gdk_pixbuf__jpeg_image_stop_load (gpointer context);
102 gboolean gdk_pixbuf__jpeg_image_load_increment(gpointer context, guchar *buf, guint size);
103
104
105 static void
106 fatal_error_handler (j_common_ptr cinfo)
107 {
108         /* FIXME:
109          * We should somehow signal what error occurred to the caller so the
110          * caller can handle the error message */
111         struct error_handler_data *errmgr;
112
113         errmgr = (struct error_handler_data *) cinfo->err;
114         cinfo->err->output_message (cinfo);
115         siglongjmp (errmgr->setjmp_buffer, 1);
116
117         /* incase the jmp buf isn't initted? */
118         exit(1);
119 }
120
121 /* Destroy notification function for the pixbuf */
122 static void
123 free_buffer (guchar *pixels, gpointer data)
124 {
125         free (pixels);
126 }
127
128
129 /* explode gray image data from jpeg library into rgb components in pixbuf */
130 static void
131 explode_gray_into_buf (struct jpeg_decompress_struct *cinfo,
132                        guchar **lines) 
133 {
134         gint i, j;
135         guint w;
136
137         g_return_if_fail (cinfo != NULL);
138         g_return_if_fail (cinfo->output_components == 1);
139
140         /* Expand grey->colour.  Expand from the end of the
141          * memory down, so we can use the same buffer.
142          */
143         w = cinfo->image_width;
144         for (i = cinfo->rec_outbuf_height - 1; i >= 0; i--) {
145                 guchar *from, *to;
146                 
147                 from = lines[i] + w - 1;
148                 to = lines[i] + (w - 1) * 3;
149                 for (j = w - 1; j >= 0; j--) {
150                         to[0] = from[0];
151                         to[1] = from[0];
152                         to[2] = from[0];
153                         to -= 3;
154                         from--;
155                 }
156         }
157 }
158
159 /* Shared library entry point */
160 GdkPixbuf *
161 gdk_pixbuf__jpeg_image_load (FILE *f)
162 {
163         gint w, h, i;
164         guchar *pixels = NULL;
165         guchar *dptr;
166         guchar *lines[4]; /* Used to expand rows, via rec_outbuf_height, 
167                            * from the header file: 
168                            * " Usually rec_outbuf_height will be 1 or 2, 
169                            * at most 4."
170                            */
171         guchar **lptr;
172         struct jpeg_decompress_struct cinfo;
173         struct error_handler_data jerr;
174
175         /* setup error handler */
176         cinfo.err = jpeg_std_error (&jerr.pub);
177         jerr.pub.error_exit = fatal_error_handler;
178
179         if (sigsetjmp (jerr.setjmp_buffer, 1)) {
180                 /* Whoops there was a jpeg error */
181                 if (pixels)
182                         free (pixels);
183
184                 jpeg_destroy_decompress (&cinfo);
185                 return NULL;
186         }
187
188         /* load header, setup */
189         jpeg_create_decompress (&cinfo);
190         jpeg_stdio_src (&cinfo, f);
191         jpeg_read_header (&cinfo, TRUE);
192         jpeg_start_decompress (&cinfo);
193         cinfo.do_fancy_upsampling = FALSE;
194         cinfo.do_block_smoothing = FALSE;
195
196         w = cinfo.output_width;
197         h = cinfo.output_height;
198
199         pixels = g_malloc (h * w * 3);
200         if (!pixels) {
201                 jpeg_destroy_decompress (&cinfo);
202                 return NULL;
203         }
204
205         dptr = pixels;
206
207         /* decompress all the lines, a few at a time */
208
209         while (cinfo.output_scanline < cinfo.output_height) {
210                 lptr = lines;
211                 for (i = 0; i < cinfo.rec_outbuf_height; i++) {
212                         *lptr++ = dptr;
213                         dptr += w * 3;
214                 }
215
216                 jpeg_read_scanlines (&cinfo, lines, cinfo.rec_outbuf_height);
217
218                 if (cinfo.output_components == 1)
219                         explode_gray_into_buf (&cinfo, lines);
220         }
221
222         jpeg_finish_decompress (&cinfo);
223         jpeg_destroy_decompress (&cinfo);
224
225         return gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, FALSE, 8,
226                                          w, h, w * 3,
227                                          free_buffer, NULL);
228 }
229
230
231 /**** Progressive image loading handling *****/
232
233 /* these routines required because we are acting as a source manager for */
234 /* libjpeg. */
235 static void
236 init_source (j_decompress_ptr cinfo)
237 {
238         my_src_ptr src = (my_src_ptr) cinfo->src;
239
240         src->skip_next = 0;
241 }
242
243
244 static void
245 term_source (j_decompress_ptr cinfo)
246 {
247         /* XXXX - probably should scream something has happened */
248 }
249
250
251 /* for progressive loading (called "I/O Suspension" by libjpeg docs) */
252 /* we do nothing except return "FALSE"                               */
253 static boolean
254 fill_input_buffer (j_decompress_ptr cinfo)
255 {
256         return FALSE;
257 }
258
259
260 static void
261 skip_input_data (j_decompress_ptr cinfo, long num_bytes)
262 {
263         my_src_ptr src = (my_src_ptr) cinfo->src;
264         long   num_can_do;
265
266         /* move as far as we can into current buffer */
267         /* then set skip_next to catch the rest      */
268         if (num_bytes > 0) {
269                 num_can_do = MIN (src->pub.bytes_in_buffer, num_bytes);
270                 src->pub.next_input_byte += (size_t) num_can_do;
271                 src->pub.bytes_in_buffer -= (size_t) num_can_do;
272
273                 src->skip_next = num_bytes - num_can_do;
274         }
275 }
276
277  
278 /* 
279  * func - called when we have pixmap created (but no image data)
280  * user_data - passed as arg 1 to func
281  * return context (opaque to user)
282  */
283
284 gpointer
285 gdk_pixbuf__jpeg_image_begin_load (ModulePreparedNotifyFunc prepared_func, 
286                                    ModuleUpdatedNotifyFunc  updated_func,
287                                    ModuleFrameDoneNotifyFunc frame_func,
288                                    ModuleAnimationDoneNotifyFunc anim_done_func,
289                                    gpointer user_data)
290 {
291         JpegProgContext *context;
292         my_source_mgr   *src;
293
294         context = g_new0 (JpegProgContext, 1);
295         context->prepared_func = prepared_func;
296         context->updated_func  = updated_func;
297         context->user_data = user_data;
298         context->pixbuf = NULL;
299         context->got_header = FALSE;
300         context->did_prescan = FALSE;
301         context->src_initialized = FALSE;
302
303         /* create libjpeg structures */
304         jpeg_create_decompress (&context->cinfo);
305
306         context->cinfo.src = (struct jpeg_source_mgr *) g_new0 (my_source_mgr, 1);
307         src = (my_src_ptr) context->cinfo.src;
308
309         context->cinfo.err = jpeg_std_error (&context->jerr.pub);
310         context->jerr.pub.error_exit = fatal_error_handler;
311
312         src = (my_src_ptr) context->cinfo.src;
313         src->pub.init_source = init_source;
314         src->pub.fill_input_buffer = fill_input_buffer;
315         src->pub.skip_input_data = skip_input_data;
316         src->pub.resync_to_restart = jpeg_resync_to_restart;
317         src->pub.term_source = term_source;
318         src->pub.bytes_in_buffer = 0;
319         src->pub.next_input_byte = NULL;
320
321         return (gpointer) context;
322 }
323
324 /*
325  * context - returned from image_begin_load
326  *
327  * free context, unref gdk_pixbuf
328  */
329 void
330 gdk_pixbuf__jpeg_image_stop_load (gpointer data)
331 {
332         JpegProgContext *context = (JpegProgContext *) data;
333
334         g_return_if_fail (context != NULL);
335
336         if (context->pixbuf)
337                 gdk_pixbuf_unref (context->pixbuf);
338
339         /* if we have an error? */
340         if (sigsetjmp (context->jerr.setjmp_buffer, 1)) {
341                 jpeg_destroy_decompress (&context->cinfo);
342         } else {
343                 jpeg_finish_decompress(&context->cinfo);
344                 jpeg_destroy_decompress(&context->cinfo);
345         }
346
347         if (context->cinfo.src) {
348                 my_src_ptr src = (my_src_ptr) context->cinfo.src;
349                 
350                 g_free (src);
351         }
352
353         g_free (context);
354 }
355
356
357
358
359 /*
360  * context - from image_begin_load
361  * buf - new image data
362  * size - length of new image data
363  *
364  * append image data onto inrecrementally built output image
365  */
366 gboolean
367 gdk_pixbuf__jpeg_image_load_increment (gpointer data, guchar *buf, guint size)
368 {
369         JpegProgContext *context = (JpegProgContext *)data;
370         struct jpeg_decompress_struct *cinfo;
371         my_src_ptr  src;
372         guint       num_left, num_copy;
373         guint       last_bytes_left;
374         guint       spinguard;
375         gboolean    first;
376         guchar *bufhd;
377
378         g_return_val_if_fail (context != NULL, FALSE);
379         g_return_val_if_fail (buf != NULL, FALSE);
380
381         src = (my_src_ptr) context->cinfo.src;
382
383         cinfo = &context->cinfo;
384
385         /* XXXXXXX (drmike) - loop(s) below need to be recoded now I
386          *                    have a grasp of what the flow needs to be!
387          */
388
389         /* check for fatal error */
390         if (sigsetjmp (context->jerr.setjmp_buffer, 1)) {
391                 return FALSE;
392         }
393
394         /* skip over data if requested, handle unsigned int sizes cleanly */
395         /* only can happen if we've already called jpeg_get_header once   */
396         if (context->src_initialized && src->skip_next) {
397                 if (src->skip_next > size) {
398                         src->skip_next -= size;
399                         return TRUE;
400                 } else {
401                         num_left = size - src->skip_next;
402                         bufhd = buf + src->skip_next;
403                         src->skip_next = 0;
404                 }
405         } else {
406                 num_left = size;
407                 bufhd = buf;
408         }
409
410         if (num_left == 0)
411                 return TRUE;
412
413         last_bytes_left = 0;
414         spinguard = 0;
415         first = TRUE;
416         while (TRUE) {
417
418                 /* handle any data from caller we haven't processed yet */
419                 if (num_left > 0) {
420                         if(src->pub.bytes_in_buffer && 
421                            src->pub.next_input_byte != src->buffer)
422                                 memmove(src->buffer, src->pub.next_input_byte,
423                                         src->pub.bytes_in_buffer);
424
425
426                         num_copy = MIN (JPEG_PROG_BUF_SIZE - src->pub.bytes_in_buffer,
427                                         num_left);
428
429 /*                      if (num_copy == 0) 
430                                 g_error ("Buffer overflow!");
431 */
432                         memcpy(src->buffer + src->pub.bytes_in_buffer, bufhd,num_copy);
433                         src->pub.next_input_byte = src->buffer;
434                         src->pub.bytes_in_buffer += num_copy;
435                         bufhd += num_copy;
436                         num_left -= num_copy;
437                 } else {
438                 /* did anything change from last pass, if not return */
439                         if (first) {
440                                 last_bytes_left = src->pub.bytes_in_buffer;
441                                 first = FALSE;
442                         } else if (src->pub.bytes_in_buffer == last_bytes_left)
443                                 spinguard++;
444                         else
445                                 last_bytes_left = src->pub.bytes_in_buffer;
446                 }
447
448                 /* should not go through twice and not pull bytes out of buf */
449                 if (spinguard > 2)
450                         return TRUE;
451
452                 /* try to load jpeg header */
453                 if (!context->got_header) {
454                         int rc;
455
456                         rc = jpeg_read_header (cinfo, TRUE);
457                         context->src_initialized = TRUE;
458
459                         if (rc == JPEG_SUSPENDED)
460                                 continue;
461
462                         context->got_header = TRUE;
463
464 #if 0
465                         if (jpeg_has_multiple_scans (cinfo)) {
466                                 g_print ("io-jpeg.c: Does not currently "
467                                          "support progressive jpeg files.\n");
468                                 return FALSE;
469                         }
470 #endif
471                         context->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, 
472                                                          FALSE,
473                                                          8, 
474                                                          cinfo->image_width,
475                                                          cinfo->image_height);
476
477                         if (context->pixbuf == NULL) {
478                                 /* Failed to allocate memory */
479                                 g_error ("Couldn't allocate gdkpixbuf");
480                         }
481
482                         /* Use pixbuf buffer to store decompressed data */
483                         context->dptr = context->pixbuf->pixels;
484
485                         /* Notify the client that we are ready to go */
486                         (* context->prepared_func) (context->pixbuf,
487                                                     context->user_data);
488
489                 } else if (!context->did_prescan) {
490                         int rc;
491
492                         /* start decompression */
493                         rc = jpeg_start_decompress (cinfo);
494                         cinfo->do_fancy_upsampling = FALSE;
495                         cinfo->do_block_smoothing = FALSE;
496
497                         if (rc == JPEG_SUSPENDED)
498                                 continue;
499
500                         context->did_prescan = TRUE;
501                 } else {
502
503                         /* we're decompressing so feed jpeg lib scanlines */
504                         guchar *lines[4];
505                         guchar **lptr;
506                         guchar *rowptr;
507                         gint   nlines, i;
508                         gint   start_scanline;
509
510                         /* keep going until we've done all scanlines */
511                         while (cinfo->output_scanline < cinfo->output_height) {
512                                 start_scanline = cinfo->output_scanline;
513                                 lptr = lines;
514                                 rowptr = context->dptr;
515                                 for (i=0; i < cinfo->rec_outbuf_height; i++) {
516                                         *lptr++ = rowptr;
517                                         rowptr += context->pixbuf->rowstride;
518                                 }
519
520                                 nlines = jpeg_read_scanlines (cinfo, lines,
521                                                               cinfo->rec_outbuf_height);
522                                 if (nlines == 0)
523                                         break;
524
525                                 /* handle gray */
526                                 if (cinfo->output_components == 1)
527                                         explode_gray_into_buf (cinfo, lines);
528
529                                 context->dptr += nlines * context->pixbuf->rowstride;
530
531                                 /* send updated signal */
532                                 (* context->updated_func) (context->pixbuf,
533                                                            0, 
534                                                            cinfo->output_scanline-1,
535                                                            cinfo->image_width, 
536                                                            nlines,
537                                                            context->user_data);
538
539 #undef DEBUG_JPEG_PROGRESSIVE
540 #ifdef DEBUG_JPEG_PROGRESSIVE
541                                 
542                                 if (start_scanline != cinfo->output_scanline)
543                                         g_print("jpeg: Input pass=%2d, next input scanline=%3d,"
544                                                 " emitted %3d - %3d\n",
545                                                 cinfo->input_scan_number, cinfo->input_iMCU_row * 16,
546                                                 start_scanline, cinfo->output_scanline - 1);
547                                 
548                                 
549                                 
550                                 g_print ("Scanline %d of %d - ", 
551                                          cinfo->output_scanline,
552                                          cinfo->output_height);
553 /*                      g_print ("rec_height %d -", cinfo->rec_outbuf_height); */
554                                 g_print ("Processed %d lines - bytes left = %d\n",
555                                          nlines, cinfo->src->bytes_in_buffer);
556 #endif
557                         }
558                         /* did entire image */
559                         if (cinfo->output_scanline >= cinfo->output_height)
560                                 return TRUE;
561                         else
562                                 continue;
563                 }
564         }
565
566         return TRUE;
567 }
568
569 gboolean
570 gdk_pixbuf__jpeg_image_save (FILE          *f, 
571                              GdkPixbuf     *pixbuf, 
572                              gchar        **keys,
573                              gchar        **values,
574                              GError       **error)
575 {
576         /* FIXME error handling is broken */
577         
578        struct jpeg_compress_struct cinfo;
579        guchar *buf = NULL;
580        guchar *ptr;
581        guchar *pixels = NULL;
582        JSAMPROW *jbuf;
583        int y = 0;
584        int quality = 75; /* default; must be between 0 and 100 */
585        int i, j;
586        int w, h = 0;
587        int rowstride = 0;
588        struct error_handler_data jerr;
589
590        if (keys && *keys) {
591                gchar **kiter = keys;
592                gchar **viter = values;
593
594                while (*kiter) {
595                        if (strcmp (*kiter, "quality") == 0) {
596                                char *endptr = NULL;
597                                quality = strtol (*viter, &endptr, 10);
598
599                                if (endptr == *viter) {
600                                        g_set_error (error,
601                                                     GDK_PIXBUF_ERROR,
602                                                     GDK_PIXBUF_ERROR_BAD_OPTION_VALUE,
603                                                     _("JPEG quality must be a value between 0 and 100; value '%s' could not be parsed."),
604                                                     *viter);
605
606                                        return FALSE;
607                                }
608                                
609                                if (quality < 0 ||
610                                    quality > 100) {
611                                        /* This is a user-visible error;
612                                         * lets people skip the range-checking
613                                         * in their app.
614                                         */
615                                        g_set_error (error,
616                                                     GDK_PIXBUF_ERROR,
617                                                     GDK_PIXBUF_ERROR_BAD_OPTION_VALUE,
618                                                     _("JPEG quality must be a value between 0 and 100; value '%d' is not allowed."),
619                                                     quality);
620
621                                        return FALSE;
622                                }
623                        } else {
624                                g_warning ("Bad option name '%s' passed to JPEG saver",
625                                           *kiter);
626                                return FALSE;
627                        }
628                
629                        ++kiter;
630                        ++viter;
631                }
632        }
633        
634        rowstride = gdk_pixbuf_get_rowstride (pixbuf);
635
636        w = gdk_pixbuf_get_width (pixbuf);
637        h = gdk_pixbuf_get_height (pixbuf);
638
639        /* no image data? abort */
640        pixels = gdk_pixbuf_get_pixels (pixbuf);
641        g_return_val_if_fail (pixels != NULL, FALSE);
642
643        /* allocate a small buffer to convert image data */
644        buf = malloc (w * 3 * sizeof (guchar));
645        g_return_val_if_fail (buf != NULL, FALSE);
646
647        /* set up error handling */
648        jerr.pub.error_exit = fatal_error_handler;
649
650        cinfo.err = jpeg_std_error (&(jerr.pub));
651        if (sigsetjmp (jerr.setjmp_buffer, 1)) {
652                jpeg_destroy_compress (&cinfo);
653                free (buf);
654                return FALSE;
655        }
656
657        /* setup compress params */
658        jpeg_create_compress (&cinfo);
659        jpeg_stdio_dest (&cinfo, f);
660        cinfo.image_width      = w;
661        cinfo.image_height     = h;
662        cinfo.input_components = 3; 
663        cinfo.in_color_space   = JCS_RGB;
664
665        /* set up jepg compression parameters */
666        jpeg_set_defaults (&cinfo);
667        jpeg_set_quality (&cinfo, quality, TRUE);
668        jpeg_start_compress (&cinfo, TRUE);
669        /* get the start pointer */
670        ptr = pixels;
671        /* go one scanline at a time... and save */
672        i = 0;
673        while (cinfo.next_scanline < cinfo.image_height) {
674                /* convert scanline from ARGB to RGB packed */
675                for (j = 0; j < w; j++)
676                        memcpy (&(buf[j*3]), &(ptr[i*rowstride + j*3]), 3);
677
678                /* write scanline */
679                jbuf = (JSAMPROW *)(&buf);
680                jpeg_write_scanlines (&cinfo, jbuf, 1);
681                i++;
682                y++;
683
684        }
685        
686        /* finish off */
687        jpeg_finish_compress (&cinfo);   
688        free (buf);
689        return TRUE;
690 }