]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/io-jpeg.c
Change the order of the update func to make it more compatible with the
[~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 Library 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  * Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library 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.h"
51 #include "gdk-pixbuf-io.h"
52
53 \f
54
55 /* we are a "source manager" as far as libjpeg is concerned */
56 #define JPEG_PROG_BUF_SIZE 4096
57
58 typedef struct {
59         struct jpeg_source_mgr pub;   /* public fields */
60
61         JOCTET buffer[JPEG_PROG_BUF_SIZE];              /* start of buffer */
62         long  skip_next;              /* number of bytes to skip next read */
63
64 } my_source_mgr;
65
66 typedef my_source_mgr * my_src_ptr;
67
68 /* error handler data */
69 struct error_handler_data {
70         struct jpeg_error_mgr pub;
71         sigjmp_buf setjmp_buffer;
72 };
73
74 /* progressive loader context */
75 typedef struct {
76         ModuleUpdatedNotifyFunc  updated_func;
77         ModulePreparedNotifyFunc prepared_func;
78         gpointer                 user_data;
79         
80         GdkPixbuf                *pixbuf;
81         guchar                   *dptr;   /* current position in pixbuf */
82
83         gboolean                 did_prescan;  /* are we in image data yet? */
84         gboolean                 got_header;  /* have we loaded jpeg header? */
85         gboolean                 src_initialized;/* TRUE when jpeg lib initialized */
86         struct jpeg_decompress_struct cinfo;
87         struct error_handler_data     jerr;
88 } JpegProgContext;
89
90 GdkPixbuf *image_load (FILE *f);
91 gpointer image_begin_load (ModulePreparedNotifyFunc func, 
92                            ModuleUpdatedNotifyFunc func2,
93                            ModuleFrameDoneNotifyFunc func3,
94                            ModuleAnimationDoneNotifyFunc func4,
95                            gpointer user_data);
96 void image_stop_load (gpointer context);
97 gboolean image_load_increment(gpointer context, guchar *buf, guint size);
98
99
100 static void
101 fatal_error_handler (j_common_ptr cinfo)
102 {
103         /* FIXME:
104          * We should somehow signal what error occurred to the caller so the
105          * caller can handle the error message */
106         struct error_handler_data *errmgr;
107
108         errmgr = (struct error_handler_data *) cinfo->err;
109         cinfo->err->output_message (cinfo);
110         siglongjmp (errmgr->setjmp_buffer, 1);
111         return;
112 }
113
114 /* Destroy notification function for the libart pixbuf */
115 static void
116 free_buffer (gpointer user_data, gpointer data)
117 {
118         free (data);
119 }
120
121
122 /* explode gray image data from jpeg library into rgb components in pixbuf */
123 static void
124 explode_gray_into_buf (struct jpeg_decompress_struct *cinfo,
125                        guchar **lines) 
126 {
127         gint i, j;
128         guint w;
129
130         g_return_if_fail (cinfo != NULL);
131         g_return_if_fail (cinfo->output_components == 1);
132
133         /* Expand grey->colour.  Expand from the end of the
134          * memory down, so we can use the same buffer.
135          */
136         w = cinfo->image_width;
137         for (i = cinfo->rec_outbuf_height - 1; i >= 0; i--) {
138                 guchar *from, *to;
139                 
140                 from = lines[i] + w - 1;
141                 to = lines[i] + (w - 1) * 3;
142                 for (j = w - 1; j >= 0; j--) {
143                         to[0] = from[0];
144                         to[1] = from[0];
145                         to[2] = from[0];
146                         to -= 3;
147                         from--;
148                 }
149         }
150 }
151
152 /* Shared library entry point */
153 GdkPixbuf *
154 image_load (FILE *f)
155 {
156         gint w, h, i;
157         guchar *pixels = NULL;
158         guchar *dptr;
159         guchar *lines[4]; /* Used to expand rows, via rec_outbuf_height, 
160                            * from the header file: 
161                            * " Usually rec_outbuf_height will be 1 or 2, 
162                            * at most 4."
163                            */
164         guchar **lptr;
165         struct jpeg_decompress_struct cinfo;
166         struct error_handler_data jerr;
167
168         /* setup error handler */
169         cinfo.err = jpeg_std_error (&jerr.pub);
170         jerr.pub.error_exit = fatal_error_handler;
171
172         if (sigsetjmp (jerr.setjmp_buffer, 1)) {
173                 /* Whoops there was a jpeg error */
174                 if (pixels)
175                         free (pixels);
176
177                 jpeg_destroy_decompress (&cinfo);
178                 return NULL;
179         }
180
181         /* load header, setup */
182         jpeg_create_decompress (&cinfo);
183         jpeg_stdio_src (&cinfo, f);
184         jpeg_read_header (&cinfo, TRUE);
185         jpeg_start_decompress (&cinfo);
186         cinfo.do_fancy_upsampling = FALSE;
187         cinfo.do_block_smoothing = FALSE;
188
189         w = cinfo.output_width;
190         h = cinfo.output_height;
191
192         pixels = g_malloc (h * w * 3);
193         if (!pixels) {
194                 jpeg_destroy_decompress (&cinfo);
195                 return NULL;
196         }
197
198         dptr = pixels;
199
200         /* decompress all the lines, a few at a time */
201
202         while (cinfo.output_scanline < cinfo.output_height) {
203                 lptr = lines;
204                 for (i = 0; i < cinfo.rec_outbuf_height; i++) {
205                         *lptr++ = dptr;
206                         dptr += w * 3;
207                 }
208
209                 jpeg_read_scanlines (&cinfo, lines, cinfo.rec_outbuf_height);
210
211                 if (cinfo.output_components == 1)
212                         explode_gray_into_buf (&cinfo, lines);
213         }
214
215         jpeg_finish_decompress (&cinfo);
216         jpeg_destroy_decompress (&cinfo);
217
218         return gdk_pixbuf_new_from_data (pixels, ART_PIX_RGB, FALSE,
219                                          w, h, w * 3,
220                                          free_buffer, NULL);
221 }
222
223
224 /**** Progressive image loading handling *****/
225
226 /* these routines required because we are acting as a source manager for */
227 /* libjpeg. */
228 static void
229 init_source (j_decompress_ptr cinfo)
230 {
231         my_src_ptr src = (my_src_ptr) cinfo->src;
232
233         src->skip_next = 0;
234 }
235
236
237 static void
238 term_source (j_decompress_ptr cinfo)
239 {
240         /* XXXX - probably should scream something has happened */
241 }
242
243
244 /* for progressive loading (called "I/O Suspension" by libjpeg docs) */
245 /* we do nothing except return "FALSE"                               */
246 static boolean
247 fill_input_buffer (j_decompress_ptr cinfo)
248 {
249         return FALSE;
250 }
251
252
253 static void
254 skip_input_data (j_decompress_ptr cinfo, long num_bytes)
255 {
256         my_src_ptr src = (my_src_ptr) cinfo->src;
257         long   num_can_do;
258
259         /* move as far as we can into current buffer */
260         /* then set skip_next to catch the rest      */
261         if (num_bytes > 0) {
262                 num_can_do = MIN (src->pub.bytes_in_buffer, num_bytes);
263                 src->pub.next_input_byte += (size_t) num_can_do;
264                 src->pub.bytes_in_buffer -= (size_t) num_can_do;
265
266                 src->skip_next = num_bytes - num_can_do;
267         }
268 }
269
270  
271 /* 
272  * func - called when we have pixmap created (but no image data)
273  * user_data - passed as arg 1 to func
274  * return context (opaque to user)
275  */
276
277 gpointer
278 image_begin_load (ModulePreparedNotifyFunc prepared_func, 
279                   ModuleUpdatedNotifyFunc  updated_func,
280                   ModuleFrameDoneNotifyFunc frame_func,
281                   ModuleAnimationDoneNotifyFunc anim_done_func,
282                   gpointer user_data)
283 {
284         JpegProgContext *context;
285         my_source_mgr   *src;
286
287         context = g_new0 (JpegProgContext, 1);
288         context->prepared_func = prepared_func;
289         context->updated_func  = updated_func;
290         context->user_data = user_data;
291         context->pixbuf = NULL;
292         context->got_header = FALSE;
293         context->did_prescan = FALSE;
294         context->src_initialized = FALSE;
295
296         /* create libjpeg structures */
297         jpeg_create_decompress (&context->cinfo);
298
299         context->cinfo.src = (struct jpeg_source_mgr *) g_new0 (my_source_mgr, 1);
300         src = (my_src_ptr) context->cinfo.src;
301
302         context->cinfo.err = jpeg_std_error (&context->jerr.pub);
303
304         src = (my_src_ptr) context->cinfo.src;
305         src->pub.init_source = init_source;
306         src->pub.fill_input_buffer = fill_input_buffer;
307         src->pub.skip_input_data = skip_input_data;
308         src->pub.resync_to_restart = jpeg_resync_to_restart;
309         src->pub.term_source = term_source;
310         src->pub.bytes_in_buffer = 0;
311         src->pub.next_input_byte = NULL;
312
313         return (gpointer) context;
314 }
315
316 /*
317  * context - returned from image_begin_load
318  *
319  * free context, unref gdk_pixbuf
320  */
321 void
322 image_stop_load (gpointer data)
323 {
324         JpegProgContext *context = (JpegProgContext *) data;
325
326         g_return_if_fail (context != NULL);
327
328         if (context->pixbuf)
329                 gdk_pixbuf_unref (context->pixbuf);
330
331         jpeg_finish_decompress(&context->cinfo);
332         jpeg_destroy_decompress(&context->cinfo);
333
334         if (context->cinfo.src) {
335                 my_src_ptr src = (my_src_ptr) context->cinfo.src;
336                 
337                 g_free (src);
338         }
339
340         g_free (context);
341 }
342
343
344
345
346 /*
347  * context - from image_begin_load
348  * buf - new image data
349  * size - length of new image data
350  *
351  * append image data onto inrecrementally built output image
352  */
353 gboolean
354 image_load_increment (gpointer data, guchar *buf, guint size)
355 {
356         JpegProgContext *context = (JpegProgContext *)data;
357         struct jpeg_decompress_struct *cinfo;
358         my_src_ptr  src;
359         guint       num_left, num_copy;
360         guint       last_bytes_left;
361         guint       spinguard;
362         gboolean    first;
363         guchar *bufhd;
364
365         g_return_val_if_fail (context != NULL, FALSE);
366         g_return_val_if_fail (buf != NULL, FALSE);
367
368         src = (my_src_ptr) context->cinfo.src;
369
370         cinfo = &context->cinfo;
371
372         /* XXXXXXX (drmike) - loop(s) below need to be recoded now I
373          *                    have a grasp of what the flow needs to be!
374          */
375
376
377         /* skip over data if requested, handle unsigned int sizes cleanly */
378         /* only can happen if we've already called jpeg_get_header once   */
379         if (context->src_initialized && src->skip_next) {
380                 if (src->skip_next > size) {
381                         src->skip_next -= size;
382                         return TRUE;
383                 } else {
384                         num_left = size - src->skip_next;
385                         bufhd = buf + src->skip_next;
386                         src->skip_next = 0;
387                 }
388         } else {
389                 num_left = size;
390                 bufhd = buf;
391         }
392
393         if (num_left == 0)
394                 return TRUE;
395
396         last_bytes_left = 0;
397         spinguard = 0;
398         first = TRUE;
399         while (TRUE) {
400
401                 /* handle any data from caller we haven't processed yet */
402                 if (num_left > 0) {
403                         if(src->pub.bytes_in_buffer && 
404                            src->pub.next_input_byte != src->buffer)
405                                 memmove(src->buffer, src->pub.next_input_byte,
406                                         src->pub.bytes_in_buffer);
407
408
409                         num_copy = MIN (JPEG_PROG_BUF_SIZE - src->pub.bytes_in_buffer,
410                                         num_left);
411
412 /*                      if (num_copy == 0) 
413                                 g_error ("Buffer overflow!");
414 */
415                         memcpy(src->buffer + src->pub.bytes_in_buffer, bufhd,num_copy);
416                         src->pub.next_input_byte = src->buffer;
417                         src->pub.bytes_in_buffer += num_copy;
418                         bufhd += num_copy;
419                         num_left -= num_copy;
420                 } else {
421                 /* did anything change from last pass, if not return */
422                         if (first) {
423                                 last_bytes_left = src->pub.bytes_in_buffer;
424                                 first = FALSE;
425                         } else if (src->pub.bytes_in_buffer == last_bytes_left)
426                                 spinguard++;
427                         else
428                                 last_bytes_left = src->pub.bytes_in_buffer;
429                 }
430
431                 /* should not go through twice and not pull bytes out of buf */
432                 if (spinguard > 2)
433                         return TRUE;
434
435                 /* try to load jpeg header */
436                 if (!context->got_header) {
437                         int rc;
438
439                         rc = jpeg_read_header (cinfo, TRUE);
440                         context->src_initialized = TRUE;
441
442                         if (rc == JPEG_SUSPENDED)
443                                 continue;
444
445                         context->got_header = TRUE;
446
447 #if 0
448                         if (jpeg_has_multiple_scans (cinfo)) {
449                                 g_print ("io-jpeg.c: Does not currently "
450                                          "support progressive jpeg files.\n");
451                                 return FALSE;
452                         }
453 #endif
454                         context->pixbuf = gdk_pixbuf_new(ART_PIX_RGB, 
455                                                          /*have_alpha*/ FALSE,
456                                                          8, 
457                                                          cinfo->image_width,
458                                                          cinfo->image_height);
459
460                         if (context->pixbuf == NULL) {
461                                 /* Failed to allocate memory */
462                                 g_error ("Couldn't allocate gdkpixbuf");
463                         }
464
465                         /* Use pixbuf buffer to store decompressed data */
466                         context->dptr = context->pixbuf->art_pixbuf->pixels;
467
468                         /* Notify the client that we are ready to go */
469                         (* context->prepared_func) (context->pixbuf,
470                                                     context->user_data);
471
472                 } else if (!context->did_prescan) {
473                         int rc;
474
475                         /* start decompression */
476                         rc = jpeg_start_decompress (cinfo);
477                         cinfo->do_fancy_upsampling = FALSE;
478                         cinfo->do_block_smoothing = FALSE;
479
480                         if (rc == JPEG_SUSPENDED)
481                                 continue;
482
483                         context->did_prescan = TRUE;
484                 } else {
485
486                         /* we're decompressing so feed jpeg lib scanlines */
487                         guchar *lines[4];
488                         guchar **lptr;
489                         guchar *rowptr;
490                         gint   nlines, i;
491                         gint   start_scanline;
492
493                         /* keep going until we've done all scanlines */
494                         while (cinfo->output_scanline < cinfo->output_height) {
495                                 start_scanline = cinfo->output_scanline;
496                                 lptr = lines;
497                                 rowptr = context->dptr;
498                                 for (i=0; i < cinfo->rec_outbuf_height; i++) {
499                                         *lptr++ = rowptr;
500                                         rowptr += context->pixbuf->art_pixbuf->rowstride;
501                                 }
502
503                                 nlines = jpeg_read_scanlines (cinfo, lines,
504                                                               cinfo->rec_outbuf_height);
505                                 if (nlines == 0)
506                                         break;
507
508                                 /* handle gray */
509                                 if (cinfo->output_components == 1)
510                                         explode_gray_into_buf (cinfo, lines);
511
512                                 context->dptr += nlines * context->pixbuf->art_pixbuf->rowstride;
513
514                                 /* send updated signal */
515                                 (* context->updated_func) (context->pixbuf,
516                                                            0, 
517                                                            cinfo->output_scanline-1,
518                                                            cinfo->image_width, 
519                                                            nlines,
520                                                            context->user_data);
521
522 #undef DEBUG_JPEG_PROGRESSIVE
523 #ifdef DEBUG_JPEG_PROGRESSIVE
524                                 
525                                 if (start_scanline != cinfo->output_scanline)
526                                         g_print("jpeg: Input pass=%2d, next input scanline=%3d,"
527                                                 " emitted %3d - %3d\n",
528                                                 cinfo->input_scan_number, cinfo->input_iMCU_row * 16,
529                                                 start_scanline, cinfo->output_scanline - 1);
530                                 
531                                 
532                                 
533                                 g_print ("Scanline %d of %d - ", 
534                                          cinfo->output_scanline,
535                                          cinfo->output_height);
536 /*                      g_print ("rec_height %d -", cinfo->rec_outbuf_height); */
537                                 g_print ("Processed %d lines - bytes left = %d\n",
538                                          nlines, cinfo->src->bytes_in_buffer);
539 #endif
540                         }
541                         /* did entire image */
542                         if (cinfo->output_scanline >= cinfo->output_height)
543                                 return TRUE;
544                         else
545                                 continue;
546                 }
547         }
548
549         return TRUE;
550 }