IMG_jpg.c
author Dimitris Zenios <dimitris.zenios@gmail.com>
Sun, 12 Jun 2011 20:14:22 +0300
changeset 276 a5d4daece235
parent 273 b1c1ec3a8d49
child 280 ec4ae96c100c
permissions -rw-r--r--
Fixes a bug where Mingw defines JPEG_TRUE JPEG_FALSE AND JPEG_boolean in libjpeg-8

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