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