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