IMG_UIImage.m
author Sam Lantinga <slouken@libsdl.org>
Sun, 04 Jan 2009 02:43:39 +0000
changeset 188 1318044935ad
parent 187 3405ab551300
child 195 6df7a974044f
permissions -rw-r--r--
Date: Sat, 3 Jan 2009 18:11:52 -0800
From: "E. Wing"
Subject: Re: [SDL] Submission: ImageIO backend for SDL_Image on Mac OS X

Ooops. I just caught a bug in the decoupled file loaders.

For the load file routines, the code failed to fallback to the
built-in SDL_image loaders if ImageIO failed to handle the image type.
This meant images like PCX would fail to load.
     1 /*
     2  *  IMG_ImageIO.c
     3  *  SDL_image
     4  *
     5  *  Created by Eric Wing on 1/2/09.
     6  *  Copyright 2009 __MyCompanyName__. All rights reserved.
     7  *
     8  */
     9 #include "SDL_image.h"
    10 #import <UIKit/UIKit.h>
    11 #import <Foundation/Foundation.h>
    12 
    13 
    14 // Once we have our image, we need to get it into an SDL_Surface
    15 // (Copied straight from the ImageIO backend.)
    16 static SDL_Surface* Create_SDL_Surface_From_CGImage(CGImageRef image_ref)
    17 {
    18 	/* This code is adapted from Apple's Documentation found here:
    19 	 * http://developer.apple.com/documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/index.html
    20 	 * Listing 9-4††Using a Quartz image as a texture source.
    21 	 * Unfortunately, this guide doesn't show what to do about
    22 	 * non-RGBA image formats so I'm making the rest up.
    23 	 * All this code should be scrutinized.
    24 	 */
    25 	
    26 	size_t the_width = CGImageGetWidth(image_ref);
    27 	size_t the_height = CGImageGetHeight(image_ref);
    28 	CGRect the_rect = {{0, 0}, {the_width, the_height}};
    29 	
    30 	size_t bits_per_pixel = CGImageGetBitsPerPixel(image_ref);
    31 	size_t bytes_per_row = CGImageGetBytesPerRow(image_ref);
    32 	//	size_t bits_per_component = CGImageGetBitsPerComponent(image_ref);
    33 	size_t bits_per_component = 8;
    34 	
    35 //	CGImageAlphaInfo alpha_info = CGImageGetAlphaInfo(image_ref);
    36 	
    37 
    38 	SDL_Surface* sdl_surface = NULL;
    39 	Uint32 Rmask;
    40 	Uint32 Gmask;
    41 	Uint32 Bmask;
    42 	Uint32 Amask;
    43 
    44 	
    45 	CGContextRef bitmap_context = NULL;
    46 	
    47 	CGColorSpaceRef color_space = NULL;
    48 	CGBitmapInfo bitmap_info = CGImageGetBitmapInfo(image_ref);
    49 
    50 	
    51 	switch(bits_per_pixel)
    52 	{
    53 		case 8:
    54 		{
    55 			bytes_per_row = the_width*4;
    56 			//				color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
    57 			color_space = CGColorSpaceCreateDeviceRGB();
    58 			//				bitmap_info = kCGImageAlphaPremultipliedFirst;
    59 #if __BIG_ENDIAN__
    60 			bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Big; /* XRGB Big Endian */
    61 #else
    62 			bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little; /* XRGB Little Endian */
    63 #endif
    64 
    65 			Rmask = 0x00FF0000;
    66 			Gmask = 0x0000FF00;
    67 			Bmask = 0x000000FF;
    68 			Amask = 0x00000000;
    69 
    70 			sdl_surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
    71 											   the_width, the_height, 32, Rmask, Gmask, Bmask, Amask);
    72 
    73 
    74 			
    75 			break;
    76 		}
    77 		case 15:
    78 		case 16:
    79 		{
    80 			bytes_per_row = the_width*4;
    81 
    82 			color_space = CGColorSpaceCreateDeviceRGB();
    83 
    84 #if __BIG_ENDIAN__
    85 			bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Big; /* XRGB Big Endian */
    86 #else
    87 			bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little; /* XRGB Little Endian */
    88 #endif
    89 			Rmask = 0x00FF0000;
    90 			Gmask = 0x0000FF00;
    91 			Bmask = 0x000000FF;
    92 			Amask = 0x00000000;
    93 
    94 
    95 			sdl_surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
    96 											   the_width, the_height, 32, Rmask, Gmask, Bmask, Amask);
    97 
    98 			break;
    99 		}
   100 		case 24:
   101 		{
   102 			bytes_per_row = the_width*4;
   103 			//			color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
   104 			color_space = CGColorSpaceCreateDeviceRGB();
   105 			//			bitmap_info = kCGImageAlphaNone;
   106 #if __BIG_ENDIAN__
   107 			bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Big; /* XRGB Big Endian */
   108 #else
   109 			bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little; /* XRGB Little Endian */
   110 #endif
   111 			Rmask = 0x00FF0000;
   112 			Gmask = 0x0000FF00;
   113 			Bmask = 0x000000FF;
   114 			Amask = 0x00000000;
   115 
   116 			sdl_surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
   117 											   the_width, the_height, 32, Rmask, Gmask, Bmask, Amask);
   118 
   119 			break;
   120 		}
   121 		case 32:
   122 		{
   123 						
   124 			bytes_per_row = the_width*4;
   125 			//			color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
   126 			color_space = CGColorSpaceCreateDeviceRGB();
   127 			//			bitmap_info = kCGImageAlphaPremultipliedFirst;
   128 #if __BIG_ENDIAN__
   129 			bitmap_info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Big; /* XRGB Big Endian */
   130 #else
   131 			bitmap_info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little; /* XRGB Little Endian */
   132 #endif 
   133 			Amask = 0xFF000000;
   134 			Rmask = 0x00FF0000;
   135 			Gmask = 0x0000FF00;
   136 			Bmask = 0x000000FF;
   137 
   138 			sdl_surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
   139 											   the_width, the_height, 32, Rmask, Gmask, Bmask, Amask);
   140 			break;
   141 		}
   142 		default:
   143 		{
   144             sdl_surface = NULL;
   145             break;
   146 		}
   147 			
   148 	}
   149 
   150 	if(NULL == sdl_surface)
   151 	{
   152 		if(color_space != NULL)
   153 		{
   154 			CGColorSpaceRelease(color_space);			
   155 		}
   156 		return NULL;
   157 	}
   158 
   159 
   160 	// Sets up a context to be drawn to with sdl_surface->pixels as the area to be drawn to
   161 	bitmap_context = CGBitmapContextCreate(
   162 														sdl_surface->pixels,
   163 														the_width,
   164 														the_height,
   165 														bits_per_component,
   166 														bytes_per_row,
   167 														color_space,
   168 														bitmap_info
   169 														);
   170 	
   171 	// Draws the image into the context's image_data
   172 	CGContextDrawImage(bitmap_context, the_rect, image_ref);
   173 	
   174 	CGContextRelease(bitmap_context);
   175 	CGColorSpaceRelease(color_space);
   176 	
   177 	return sdl_surface;
   178 	
   179 	
   180 	
   181 }
   182 
   183 static SDL_Surface* LoadImageFromRWops(SDL_RWops* rw_ops, CFStringRef uti_string_hint)
   184 {
   185 	NSAutoreleasePool* autorelease_pool = [[NSAutoreleasePool alloc] init];
   186 	SDL_Surface* sdl_surface;
   187 	UIImage* ui_image;
   188 
   189 	CGImageRef image_ref = NULL;
   190 	int bytes_read = 0;
   191 	// I don't know what a good size is. 
   192 	// Max recommended texture size is 1024x1024 on iPhone so maybe base it on that?
   193 	const int block_size = 1024*4;
   194 	char temp_buffer[block_size];
   195 	
   196 	NSMutableData* ns_data = [[NSMutableData alloc] initWithCapacity:1024*1024*4];
   197 
   198 	
   199 	do
   200 	{
   201 		bytes_read = SDL_RWread(rw_ops, temp_buffer, 1, block_size);
   202 		[ns_data appendBytes:temp_buffer length:bytes_read];
   203 	} while(bytes_read > 0);
   204 
   205 	if(NULL == image_ref)
   206 	{
   207 		return NULL;
   208 	}
   209 
   210 	ui_image = [[UIImage alloc] initWithData:ns_data];
   211 	
   212 	sdl_surface = Create_SDL_Surface_From_CGImage([ui_image CGImage]);
   213 
   214 	[ui_image release];
   215 	[ns_data release];
   216 
   217 	[autorelease_pool drain];
   218 
   219 	return sdl_surface;
   220 }
   221 
   222 static SDL_Surface* LoadImageFromFile(const char *file)
   223 {
   224 	NSAutoreleasePool* autorelease_pool = [[NSAutoreleasePool alloc] init];
   225 	SDL_Surface* sdl_surface = NULL;
   226 	UIImage* ui_image;
   227 	NSString* ns_string;
   228 	
   229 	ns_string = [[NSString alloc] initWithUTF8String:file];
   230 	ui_image = [[UIImage alloc] initWithContentsOfFile:ns_string];
   231 	
   232 	sdl_surface = Create_SDL_Surface_From_CGImage([ui_image CGImage]);
   233 	
   234 	[ui_image release];
   235 	[ns_string release];
   236 	
   237 	[autorelease_pool drain];
   238 	
   239 	return sdl_surface;
   240 }
   241 
   242 
   243 /* Since UIImage doesn't really support streams well, we should optimize for the file case. */
   244 SDL_Surface *IMG_Load(const char *file)
   245 {
   246 	SDL_Surface* sdl_surface = NULL;
   247 
   248 	sdl_surface = LoadImageFromFile(file);
   249 	if(NULL == sdl_surface)
   250 	{
   251 		// Either the file doesn't exist or ImageIO doesn't understand the format.
   252 		// For the latter case, fallback to the native SDL_image handlers.
   253 
   254 		SDL_RWops *src = SDL_RWFromFile(file, "rb");
   255 		char *ext = strrchr(file, '.');
   256 		if(ext) {
   257 			ext++;
   258 		}
   259 		if(!src) {
   260 			/* The error message has been set in SDL_RWFromFile */
   261 			return NULL;
   262 		}
   263 		sdl_surface = IMG_LoadTyped_RW(src, 1, ext);
   264 	}
   265 	return sdl_surface;
   266 }
   267 
   268 
   269 
   270 /* Copied straight from other files so I don't have to alter them. */
   271 int IMG_isBMP(SDL_RWops *src)
   272 {
   273 	int start;
   274 	int is_BMP;
   275 	char magic[2];
   276 	
   277 	if ( !src )
   278 		return 0;
   279 	start = SDL_RWtell(src);
   280 	is_BMP = 0;
   281 	if ( SDL_RWread(src, magic, sizeof(magic), 1) ) {
   282 		if ( strncmp(magic, "BM", 2) == 0 ) {
   283 			is_BMP = 1;
   284 		}
   285 	}
   286 	SDL_RWseek(src, start, SEEK_SET);
   287 	return(is_BMP);
   288 }
   289 
   290 int IMG_isGIF(SDL_RWops *src)
   291 {
   292 	int start;
   293 	int is_GIF;
   294 	char magic[6];
   295 	
   296 	if ( !src )
   297 		return 0;
   298 	start = SDL_RWtell(src);
   299 	is_GIF = 0;
   300 	if ( SDL_RWread(src, magic, sizeof(magic), 1) ) {
   301 		if ( (strncmp(magic, "GIF", 3) == 0) &&
   302 			((memcmp(magic + 3, "87a", 3) == 0) ||
   303 			 (memcmp(magic + 3, "89a", 3) == 0)) ) {
   304 			is_GIF = 1;
   305 		}
   306 	}
   307 	SDL_RWseek(src, start, SEEK_SET);
   308 	return(is_GIF);
   309 }
   310 
   311 int IMG_isJPG(SDL_RWops *src)
   312 {
   313 	int start;
   314 	int is_JPG;
   315 	int in_scan;
   316 	Uint8 magic[4];
   317 	
   318 	/* This detection code is by Steaphan Greene <stea@cs.binghamton.edu> */
   319 	/* Blame me, not Sam, if this doesn't work right. */
   320 	/* And don't forget to report the problem to the the sdl list too! */
   321 	
   322 	if ( !src )
   323 		return 0;
   324 	start = SDL_RWtell(src);
   325 	is_JPG = 0;
   326 	in_scan = 0;
   327 	if ( SDL_RWread(src, magic, 2, 1) ) {
   328 		if ( (magic[0] == 0xFF) && (magic[1] == 0xD8) ) {
   329 			is_JPG = 1;
   330 			while (is_JPG == 1) {
   331 				if(SDL_RWread(src, magic, 1, 2) != 2) {
   332 					is_JPG = 0;
   333 				} else if( (magic[0] != 0xFF) && (in_scan == 0) ) {
   334 					is_JPG = 0;
   335 				} else if( (magic[0] != 0xFF) || (magic[1] == 0xFF) ) {
   336 					/* Extra padding in JPEG (legal) */
   337 					/* or this is data and we are scanning */
   338 					SDL_RWseek(src, -1, SEEK_CUR);
   339 				} else if(magic[1] == 0xD9) {
   340 					/* Got to end of good JPEG */
   341 					break;
   342 				} else if( (in_scan == 1) && (magic[1] == 0x00) ) {
   343 					/* This is an encoded 0xFF within the data */
   344 				} else if( (magic[1] >= 0xD0) && (magic[1] < 0xD9) ) {
   345 					/* These have nothing else */
   346 				} else if(SDL_RWread(src, magic+2, 1, 2) != 2) {
   347 					is_JPG = 0;
   348 				} else {
   349 					/* Yes, it's big-endian */
   350 					Uint32 start;
   351 					Uint32 size;
   352 					Uint32 end;
   353 					start = SDL_RWtell(src);
   354 					size = (magic[2] << 8) + magic[3];
   355 					end = SDL_RWseek(src, size-2, SEEK_CUR);
   356 					if ( end != start + size - 2 ) is_JPG = 0;
   357 					if ( magic[1] == 0xDA ) {
   358 						/* Now comes the actual JPEG meat */
   359 #ifdef	FAST_IS_JPEG
   360 						/* Ok, I'm convinced.  It is a JPEG. */
   361 						break;
   362 #else
   363 						/* I'm not convinced.  Prove it! */
   364 						in_scan = 1;
   365 #endif
   366 					}
   367 				}
   368 			}
   369 		}
   370 	}
   371 	SDL_RWseek(src, start, SEEK_SET);
   372 	return(is_JPG);
   373 }
   374 
   375 int IMG_isPNG(SDL_RWops *src)
   376 {
   377 	int start;
   378 	int is_PNG;
   379 	Uint8 magic[4];
   380 	
   381 	if ( !src )
   382 		return 0;
   383 	start = SDL_RWtell(src);
   384 	is_PNG = 0;
   385 	if ( SDL_RWread(src, magic, 1, sizeof(magic)) == sizeof(magic) ) {
   386 		if ( magic[0] == 0x89 &&
   387 			magic[1] == 'P' &&
   388 			magic[2] == 'N' &&
   389 			magic[3] == 'G' ) {
   390 			is_PNG = 1;
   391 		}
   392 	}
   393 	SDL_RWseek(src, start, SEEK_SET);
   394 	return(is_PNG);
   395 }
   396 
   397 int IMG_isTIF(SDL_RWops* src)
   398 {
   399 	int start;
   400 	int is_TIF;
   401 	Uint8 magic[4];
   402 	
   403 	if ( !src )
   404 		return 0;
   405 	start = SDL_RWtell(src);
   406 	is_TIF = 0;
   407 	if ( SDL_RWread(src, magic, 1, sizeof(magic)) == sizeof(magic) ) {
   408 		if ( (magic[0] == 'I' &&
   409 			  magic[1] == 'I' &&
   410 		      magic[2] == 0x2a &&
   411 			  magic[3] == 0x00) ||
   412 			(magic[0] == 'M' &&
   413 			 magic[1] == 'M' &&
   414 			 magic[2] == 0x00 &&
   415 			 magic[3] == 0x2a) ) {
   416 			is_TIF = 1;
   417 		}
   418 	}
   419 	SDL_RWseek(src, start, SEEK_SET);
   420 	return(is_TIF);
   421 }
   422 
   423 
   424 SDL_Surface* IMG_LoadBMP_RW(SDL_RWops *src)
   425 {
   426 	return LoadImageFromRWops(src, kUTTypeBMP);
   427 }
   428 SDL_Surface* IMG_LoadGIF_RW(SDL_RWops *src)
   429 {
   430 	return LoadImageFromRWops(src, kUTTypeGIF);
   431 }
   432 SDL_Surface* IMG_LoadJPG_RW(SDL_RWops *src)
   433 {
   434 	return LoadImageFromRWops(src, kUTTypeJPEG);
   435 }
   436 SDL_Surface* IMG_LoadPNG_RW(SDL_RWops *src)
   437 {
   438 	return LoadImageFromRWops(src, kUTTypePNG);
   439 }
   440 SDL_Surface* IMG_LoadTIF_RW(SDL_RWops *src)
   441 {
   442 	return LoadImageFromRWops(src, kUTTypeTIFF);
   443 }
   444