]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/libsdl-image/contrib/IMG_jpg.c
update
[l4.git] / l4 / pkg / libsdl-image / contrib / IMG_jpg.c
1 /*
2     SDL_image:  An example image loading library for use with SDL
3     Copyright (C) 1997-2009 Sam Lantinga
4
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Lesser General Public
7     License as published by the Free Software Foundation; either
8     version 2.1 of the License, or (at your option) any later version.
9
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13     Lesser General Public License for more details.
14
15     You should have received a copy of the GNU Lesser General Public
16     License along with this library; if not, write to the Free Software
17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19     Sam Lantinga
20     slouken@libsdl.org
21 */
22
23 #if !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND)
24
25 /* This is a JPEG image file loading framework */
26
27 #include <stdio.h>
28 #include <string.h>
29 #include <setjmp.h>
30
31 #include "SDL_image.h"
32
33 #ifdef LOAD_JPG
34
35 #include <jpeglib.h>
36
37 /* Define this for fast loading and not as good image quality */
38 /*#define FAST_JPEG*/
39
40 /* Define this for quicker (but less perfect) JPEG identification */
41 #define FAST_IS_JPEG
42
43 static struct {
44         int loaded;
45         void *handle;
46         void (*jpeg_calc_output_dimensions) (j_decompress_ptr cinfo);
47         void (*jpeg_CreateDecompress) (j_decompress_ptr cinfo, int version, size_t structsize);
48         void (*jpeg_destroy_decompress) (j_decompress_ptr cinfo);
49         boolean (*jpeg_finish_decompress) (j_decompress_ptr cinfo);
50         int (*jpeg_read_header) (j_decompress_ptr cinfo, boolean require_image);
51         JDIMENSION (*jpeg_read_scanlines) (j_decompress_ptr cinfo, JSAMPARRAY scanlines, JDIMENSION max_lines);
52         boolean (*jpeg_resync_to_restart) (j_decompress_ptr cinfo, int desired);
53         boolean (*jpeg_start_decompress) (j_decompress_ptr cinfo);
54         struct jpeg_error_mgr * (*jpeg_std_error) (struct jpeg_error_mgr * err);
55 } lib;
56
57 #ifdef LOAD_JPG_DYNAMIC
58 int IMG_InitJPG()
59 {
60         if ( lib.loaded == 0 ) {
61                 lib.handle = SDL_LoadObject(LOAD_JPG_DYNAMIC);
62                 if ( lib.handle == NULL ) {
63                         return -1;
64                 }
65                 lib.jpeg_calc_output_dimensions =
66                         (void (*) (j_decompress_ptr))
67                         SDL_LoadFunction(lib.handle, "jpeg_calc_output_dimensions");
68                 if ( lib.jpeg_calc_output_dimensions == NULL ) {
69                         SDL_UnloadObject(lib.handle);
70                         return -1;
71                 }
72                 lib.jpeg_CreateDecompress = 
73                         (void (*) (j_decompress_ptr, int, size_t))
74                         SDL_LoadFunction(lib.handle, "jpeg_CreateDecompress");
75                 if ( lib.jpeg_CreateDecompress == NULL ) {
76                         SDL_UnloadObject(lib.handle);
77                         return -1;
78                 }
79                 lib.jpeg_destroy_decompress = 
80                         (void (*) (j_decompress_ptr))
81                         SDL_LoadFunction(lib.handle, "jpeg_destroy_decompress");
82                 if ( lib.jpeg_destroy_decompress == NULL ) {
83                         SDL_UnloadObject(lib.handle);
84                         return -1;
85                 }
86                 lib.jpeg_finish_decompress = 
87                         (boolean (*) (j_decompress_ptr))
88                         SDL_LoadFunction(lib.handle, "jpeg_finish_decompress");
89                 if ( lib.jpeg_finish_decompress == NULL ) {
90                         SDL_UnloadObject(lib.handle);
91                         return -1;
92                 }
93                 lib.jpeg_read_header = 
94                         (int (*) (j_decompress_ptr, boolean))
95                         SDL_LoadFunction(lib.handle, "jpeg_read_header");
96                 if ( lib.jpeg_read_header == NULL ) {
97                         SDL_UnloadObject(lib.handle);
98                         return -1;
99                 }
100                 lib.jpeg_read_scanlines = 
101                         (JDIMENSION (*) (j_decompress_ptr, JSAMPARRAY, JDIMENSION))
102                         SDL_LoadFunction(lib.handle, "jpeg_read_scanlines");
103                 if ( lib.jpeg_read_scanlines == NULL ) {
104                         SDL_UnloadObject(lib.handle);
105                         return -1;
106                 }
107                 lib.jpeg_resync_to_restart = 
108                         (boolean (*) (j_decompress_ptr, int))
109                         SDL_LoadFunction(lib.handle, "jpeg_resync_to_restart");
110                 if ( lib.jpeg_resync_to_restart == NULL ) {
111                         SDL_UnloadObject(lib.handle);
112                         return -1;
113                 }
114                 lib.jpeg_start_decompress = 
115                         (boolean (*) (j_decompress_ptr))
116                         SDL_LoadFunction(lib.handle, "jpeg_start_decompress");
117                 if ( lib.jpeg_start_decompress == NULL ) {
118                         SDL_UnloadObject(lib.handle);
119                         return -1;
120                 }
121                 lib.jpeg_std_error = 
122                         (struct jpeg_error_mgr * (*) (struct jpeg_error_mgr *))
123                         SDL_LoadFunction(lib.handle, "jpeg_std_error");
124                 if ( lib.jpeg_std_error == NULL ) {
125                         SDL_UnloadObject(lib.handle);
126                         return -1;
127                 }
128         }
129         ++lib.loaded;
130
131         return 0;
132 }
133 void IMG_QuitJPG()
134 {
135         if ( lib.loaded == 0 ) {
136                 return;
137         }
138         if ( lib.loaded == 1 ) {
139                 SDL_UnloadObject(lib.handle);
140         }
141         --lib.loaded;
142 }
143 #else
144 int IMG_InitJPG()
145 {
146         if ( lib.loaded == 0 ) {
147                 lib.jpeg_calc_output_dimensions = jpeg_calc_output_dimensions;
148                 lib.jpeg_CreateDecompress = jpeg_CreateDecompress;
149                 lib.jpeg_destroy_decompress = jpeg_destroy_decompress;
150                 lib.jpeg_finish_decompress = jpeg_finish_decompress;
151                 lib.jpeg_read_header = jpeg_read_header;
152                 lib.jpeg_read_scanlines = jpeg_read_scanlines;
153                 lib.jpeg_resync_to_restart = jpeg_resync_to_restart;
154                 lib.jpeg_start_decompress = jpeg_start_decompress;
155                 lib.jpeg_std_error = jpeg_std_error;
156         }
157         ++lib.loaded;
158
159         return 0;
160 }
161 void IMG_QuitJPG()
162 {
163         if ( lib.loaded == 0 ) {
164                 return;
165         }
166         if ( lib.loaded == 1 ) {
167         }
168         --lib.loaded;
169 }
170 #endif /* LOAD_JPG_DYNAMIC */
171
172 /* See if an image is contained in a data source */
173 int IMG_isJPG(SDL_RWops *src)
174 {
175         int start;
176         int is_JPG;
177         int in_scan;
178         Uint8 magic[4];
179
180         /* This detection code is by Steaphan Greene <stea@cs.binghamton.edu> */
181         /* Blame me, not Sam, if this doesn't work right. */
182         /* And don't forget to report the problem to the the sdl list too! */
183
184         if ( !src )
185                 return 0;
186         start = SDL_RWtell(src);
187         is_JPG = 0;
188         in_scan = 0;
189         if ( SDL_RWread(src, magic, 2, 1) ) {
190                 if ( (magic[0] == 0xFF) && (magic[1] == 0xD8) ) {
191                         is_JPG = 1;
192                         while (is_JPG == 1) {
193                                 if(SDL_RWread(src, magic, 1, 2) != 2) {
194                                         is_JPG = 0;
195                                 } else if( (magic[0] != 0xFF) && (in_scan == 0) ) {
196                                         is_JPG = 0;
197                                 } else if( (magic[0] != 0xFF) || (magic[1] == 0xFF) ) {
198                                         /* Extra padding in JPEG (legal) */
199                                         /* or this is data and we are scanning */
200                                         SDL_RWseek(src, -1, RW_SEEK_CUR);
201                                 } else if(magic[1] == 0xD9) {
202                                         /* Got to end of good JPEG */
203                                         break;
204                                 } else if( (in_scan == 1) && (magic[1] == 0x00) ) {
205                                         /* This is an encoded 0xFF within the data */
206                                 } else if( (magic[1] >= 0xD0) && (magic[1] < 0xD9) ) {
207                                         /* These have nothing else */
208                                 } else if(SDL_RWread(src, magic+2, 1, 2) != 2) {
209                                         is_JPG = 0;
210                                 } else {
211                                         /* Yes, it's big-endian */
212                                         Uint32 start;
213                                         Uint32 size;
214                                         Uint32 end;
215                                         start = SDL_RWtell(src);
216                                         size = (magic[2] << 8) + magic[3];
217                                         end = SDL_RWseek(src, size-2, RW_SEEK_CUR);
218                                         if ( end != start + size - 2 ) is_JPG = 0;
219                                         if ( magic[1] == 0xDA ) {
220                                                 /* Now comes the actual JPEG meat */
221 #ifdef  FAST_IS_JPEG
222                                                 /* Ok, I'm convinced.  It is a JPEG. */
223                                                 break;
224 #else
225                                                 /* I'm not convinced.  Prove it! */
226                                                 in_scan = 1;
227 #endif
228                                         }
229                                 }
230                         }
231                 }
232         }
233         SDL_RWseek(src, start, RW_SEEK_SET);
234         return(is_JPG);
235 }
236
237 #define INPUT_BUFFER_SIZE       4096
238 typedef struct {
239         struct jpeg_source_mgr pub;
240
241         SDL_RWops *ctx;
242         Uint8 buffer[INPUT_BUFFER_SIZE];
243 } my_source_mgr;
244
245 /*
246  * Initialize source --- called by jpeg_read_header
247  * before any data is actually read.
248  */
249 static void init_source (j_decompress_ptr cinfo)
250 {
251         /* We don't actually need to do anything */
252         return;
253 }
254
255 /*
256  * Fill the input buffer --- called whenever buffer is emptied.
257  */
258 static int fill_input_buffer (j_decompress_ptr cinfo)
259 {
260         my_source_mgr * src = (my_source_mgr *) cinfo->src;
261         int nbytes;
262
263         nbytes = SDL_RWread(src->ctx, src->buffer, 1, INPUT_BUFFER_SIZE);
264         if (nbytes <= 0) {
265                 /* Insert a fake EOI marker */
266                 src->buffer[0] = (Uint8) 0xFF;
267                 src->buffer[1] = (Uint8) JPEG_EOI;
268                 nbytes = 2;
269         }
270         src->pub.next_input_byte = src->buffer;
271         src->pub.bytes_in_buffer = nbytes;
272
273         return TRUE;
274 }
275
276
277 /*
278  * Skip data --- used to skip over a potentially large amount of
279  * uninteresting data (such as an APPn marker).
280  *
281  * Writers of suspendable-input applications must note that skip_input_data
282  * is not granted the right to give a suspension return.  If the skip extends
283  * beyond the data currently in the buffer, the buffer can be marked empty so
284  * that the next read will cause a fill_input_buffer call that can suspend.
285  * Arranging for additional bytes to be discarded before reloading the input
286  * buffer is the application writer's problem.
287  */
288 static void skip_input_data (j_decompress_ptr cinfo, long num_bytes)
289 {
290         my_source_mgr * src = (my_source_mgr *) cinfo->src;
291
292         /* Just a dumb implementation for now.  Could use fseek() except
293          * it doesn't work on pipes.  Not clear that being smart is worth
294          * any trouble anyway --- large skips are infrequent.
295          */
296         if (num_bytes > 0) {
297                 while (num_bytes > (long) src->pub.bytes_in_buffer) {
298                         num_bytes -= (long) src->pub.bytes_in_buffer;
299                         (void) src->pub.fill_input_buffer(cinfo);
300                         /* note we assume that fill_input_buffer will never
301                          * return FALSE, so suspension need not be handled.
302                          */
303                 }
304                 src->pub.next_input_byte += (size_t) num_bytes;
305                 src->pub.bytes_in_buffer -= (size_t) num_bytes;
306         }
307 }
308
309 /*
310  * Terminate source --- called by jpeg_finish_decompress
311  * after all data has been read.
312  */
313 static void term_source (j_decompress_ptr cinfo)
314 {
315         /* We don't actually need to do anything */
316         return;
317 }
318
319 /*
320  * Prepare for input from a stdio stream.
321  * The caller must have already opened the stream, and is responsible
322  * for closing it after finishing decompression.
323  */
324 static void jpeg_SDL_RW_src (j_decompress_ptr cinfo, SDL_RWops *ctx)
325 {
326   my_source_mgr *src;
327
328   /* The source object and input buffer are made permanent so that a series
329    * of JPEG images can be read from the same file by calling jpeg_stdio_src
330    * only before the first one.  (If we discarded the buffer at the end of
331    * one image, we'd likely lose the start of the next one.)
332    * This makes it unsafe to use this manager and a different source
333    * manager serially with the same JPEG object.  Caveat programmer.
334    */
335   if (cinfo->src == NULL) {     /* first time for this JPEG object? */
336     cinfo->src = (struct jpeg_source_mgr *)
337       (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
338                                   sizeof(my_source_mgr));
339     src = (my_source_mgr *) cinfo->src;
340   }
341
342   src = (my_source_mgr *) cinfo->src;
343   src->pub.init_source = init_source;
344   src->pub.fill_input_buffer = fill_input_buffer;
345   src->pub.skip_input_data = skip_input_data;
346   src->pub.resync_to_restart = lib.jpeg_resync_to_restart; /* use default method */
347   src->pub.term_source = term_source;
348   src->ctx = ctx;
349   src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
350   src->pub.next_input_byte = NULL; /* until buffer loaded */
351 }
352
353 struct my_error_mgr {
354         struct jpeg_error_mgr errmgr;
355         jmp_buf escape;
356 };
357
358 static void my_error_exit(j_common_ptr cinfo)
359 {
360         struct my_error_mgr *err = (struct my_error_mgr *)cinfo->err;
361         longjmp(err->escape, 1);
362 }
363
364 static void output_no_message(j_common_ptr cinfo)
365 {
366         /* do nothing */
367 }
368
369 /* Load a JPEG type image from an SDL datasource */
370 SDL_Surface *IMG_LoadJPG_RW(SDL_RWops *src)
371 {
372         int start;
373         struct jpeg_decompress_struct cinfo;
374         JSAMPROW rowptr[1];
375         SDL_Surface *volatile surface = NULL;
376         struct my_error_mgr jerr;
377
378         if ( !src ) {
379                 /* The error message has been set in SDL_RWFromFile */
380                 return NULL;
381         }
382         start = SDL_RWtell(src);
383
384         if ( !IMG_Init(IMG_INIT_JPG) ) {
385                 return NULL;
386         }
387
388         /* Create a decompression structure and load the JPEG header */
389         cinfo.err = lib.jpeg_std_error(&jerr.errmgr);
390         jerr.errmgr.error_exit = my_error_exit;
391         jerr.errmgr.output_message = output_no_message;
392         if(setjmp(jerr.escape)) {
393                 /* If we get here, libjpeg found an error */
394                 lib.jpeg_destroy_decompress(&cinfo);
395                 if ( surface != NULL ) {
396                         SDL_FreeSurface(surface);
397                 }
398                 SDL_RWseek(src, start, RW_SEEK_SET);
399                 IMG_SetError("JPEG loading error");
400                 return NULL;
401         }
402
403         lib.jpeg_create_decompress(&cinfo);
404         jpeg_SDL_RW_src(&cinfo, src);
405         lib.jpeg_read_header(&cinfo, TRUE);
406
407         if(cinfo.num_components == 4) {
408                 /* Set 32-bit Raw output */
409                 cinfo.out_color_space = JCS_CMYK;
410                 cinfo.quantize_colors = FALSE;
411                 lib.jpeg_calc_output_dimensions(&cinfo);
412
413                 /* Allocate an output surface to hold the image */
414                 surface = SDL_AllocSurface(SDL_SWSURFACE,
415                         cinfo.output_width, cinfo.output_height, 32,
416 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
417                                    0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
418 #else
419                                    0x0000FF00, 0x00FF0000, 0xFF000000, 0x000000FF);
420 #endif
421         } else {
422                 /* Set 24-bit RGB output */
423                 cinfo.out_color_space = JCS_RGB;
424                 cinfo.quantize_colors = FALSE;
425 #ifdef FAST_JPEG
426                 cinfo.scale_num   = 1;
427                 cinfo.scale_denom = 1;
428                 cinfo.dct_method = JDCT_FASTEST;
429                 cinfo.do_fancy_upsampling = FALSE;
430 #endif
431                 lib.jpeg_calc_output_dimensions(&cinfo);
432
433                 /* Allocate an output surface to hold the image */
434                 surface = SDL_AllocSurface(SDL_SWSURFACE,
435                         cinfo.output_width, cinfo.output_height, 24,
436 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
437                                    0x0000FF, 0x00FF00, 0xFF0000,
438 #else
439                                    0xFF0000, 0x00FF00, 0x0000FF,
440 #endif
441                                    0);
442         }
443
444         if ( surface == NULL ) {
445                 lib.jpeg_destroy_decompress(&cinfo);
446                 SDL_RWseek(src, start, RW_SEEK_SET);
447                 IMG_SetError("Out of memory");
448                 return NULL;
449         }
450
451         /* Decompress the image */
452         lib.jpeg_start_decompress(&cinfo);
453         while ( cinfo.output_scanline < cinfo.output_height ) {
454                 rowptr[0] = (JSAMPROW)(Uint8 *)surface->pixels +
455                                     cinfo.output_scanline * surface->pitch;
456                 lib.jpeg_read_scanlines(&cinfo, rowptr, (JDIMENSION) 1);
457         }
458         lib.jpeg_finish_decompress(&cinfo);
459         lib.jpeg_destroy_decompress(&cinfo);
460
461         return(surface);
462 }
463
464 #else
465
466 int IMG_InitJPG()
467 {
468         IMG_SetError("JPEG images are not supported");
469         return(-1);
470 }
471
472 void IMG_QuitJPG()
473 {
474 }
475
476 /* See if an image is contained in a data source */
477 int IMG_isJPG(SDL_RWops *src)
478 {
479         return(0);
480 }
481
482 /* Load a JPEG type image from an SDL datasource */
483 SDL_Surface *IMG_LoadJPG_RW(SDL_RWops *src)
484 {
485         return(NULL);
486 }
487
488 #endif /* LOAD_JPG */
489
490 #endif /* !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND) */