IMG_ImageIO.c
author Eric Wing <ewing . public |-at-| gmail . com>
Mon, 31 Jan 2011 00:44:54 -0800
changeset 270 98f97f83e203
parent 254 ea63b44f5428
permissions -rw-r--r--
Initial changes to support ImageIO on iOS.
     1 /*
     2  *  IMG_ImageIO.c
     3  *  SDL_image
     4  *
     5  *  Created by Eric Wing on 1/1/09.
     6  *  Copyright 2009 __MyCompanyName__. All rights reserved.
     7  *
     8  */
     9 
    10 #if defined(__APPLE__) && !defined(SDL_IMAGE_USE_COMMON_BACKEND)
    11 
    12 #include "SDL_image.h"
    13 
    14 // Used because CGDataProviderCreate became deprecated in 10.5
    15 #include <AvailabilityMacros.h>
    16 #include <TargetConditionals.h>
    17 
    18 #if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)
    19 #import <MobileCoreServices/MobileCoreServices.h> // for UTCoreTypes.h
    20 #import <ImageIO/ImageIO.h>
    21 #else
    22 // For ImageIO framework and also LaunchServices framework (for UTIs)
    23 #include <ApplicationServices/ApplicationServices.h>
    24 #endif
    25 
    26 /**************************************************************
    27  ***** Begin Callback functions for block reading *************
    28  **************************************************************/
    29 
    30 // This callback reads some bytes from an SDL_rwops and copies it
    31 // to a Quartz buffer (supplied by Apple framework).
    32 static size_t MyProviderGetBytesCallback(void* rwops_userdata, void* quartz_buffer, size_t the_count)
    33 {
    34 	return (size_t)SDL_RWread((struct SDL_RWops *)rwops_userdata, quartz_buffer, 1, the_count);
    35 }
    36 
    37 // This callback is triggered when the data provider is released
    38 // so you can clean up any resources.
    39 static void MyProviderReleaseInfoCallback(void* rwops_userdata)
    40 {
    41 	// What should I put here? 
    42 	// I think the user and SDL_RWops controls closing, so I don't do anything.
    43 }
    44 
    45 static void MyProviderRewindCallback(void* rwops_userdata)
    46 {
    47 	SDL_RWseek((struct SDL_RWops *)rwops_userdata, 0, RW_SEEK_SET);
    48 }
    49 
    50 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 // CGDataProviderCreateSequential was introduced in 10.5; CGDataProviderCreate is deprecated
    51 off_t MyProviderSkipForwardBytesCallback(void* rwops_userdata, off_t the_count)
    52 {
    53 	off_t start_position = SDL_RWtell((struct SDL_RWops *)rwops_userdata);
    54 	SDL_RWseek((struct SDL_RWops *)rwops_userdata, the_count, RW_SEEK_CUR);
    55     off_t end_position = SDL_RWtell((struct SDL_RWops *)rwops_userdata);
    56     return (end_position - start_position);	
    57 }
    58 #else // CGDataProviderCreate was deprecated in 10.5
    59 static void MyProviderSkipBytesCallback(void* rwops_userdata, size_t the_count)
    60 {
    61 	SDL_RWseek((struct SDL_RWops *)rwops_userdata, the_count, RW_SEEK_CUR);
    62 }
    63 #endif
    64 
    65 
    66 /**************************************************************
    67  ***** End Callback functions for block reading ***************
    68  **************************************************************/
    69 
    70 // This creates a CGImageSourceRef which is a handle to an image that can be used to examine information
    71 // about the image or load the actual image data.
    72 static CGImageSourceRef CreateCGImageSourceFromRWops(SDL_RWops* rw_ops, CFDictionaryRef hints_and_options)
    73 {
    74 	CGImageSourceRef source_ref;
    75 
    76 	// Similar to SDL_RWops, Apple has their own callbacks for dealing with data streams.
    77 	
    78 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 // CGDataProviderCreateSequential was introduced in 10.5; CGDataProviderCreate is deprecated
    79 	CGDataProviderSequentialCallbacks provider_callbacks =
    80 	{
    81         0,
    82 		MyProviderGetBytesCallback,
    83 		MyProviderSkipForwardBytesCallback,
    84 		MyProviderRewindCallback,
    85 		MyProviderReleaseInfoCallback
    86 	};
    87 	
    88 	CGDataProviderRef data_provider = CGDataProviderCreateSequential(rw_ops, &provider_callbacks);
    89 	
    90 	
    91 #else // CGDataProviderCreate was deprecated in 10.5
    92 	
    93 	CGDataProviderCallbacks provider_callbacks =
    94 	{
    95 		MyProviderGetBytesCallback,
    96 		MyProviderSkipBytesCallback,
    97 		MyProviderRewindCallback,
    98 		MyProviderReleaseInfoCallback
    99 	};
   100 	
   101 	CGDataProviderRef data_provider = CGDataProviderCreate(rw_ops, &provider_callbacks);
   102 #endif
   103 	// Get the CGImageSourceRef.
   104 	// The dictionary can be NULL or contain hints to help ImageIO figure out the image type.
   105 	source_ref = CGImageSourceCreateWithDataProvider(data_provider, hints_and_options);
   106 	return source_ref;
   107 }
   108 
   109 
   110 /* Create a CGImageSourceRef from a file. */
   111 /* Remember to CFRelease the created source when done. */
   112 static CGImageSourceRef CreateCGImageSourceFromFile(const char* the_path)
   113 {
   114     CFURLRef the_url = NULL;
   115     CGImageSourceRef source_ref = NULL;
   116 	CFStringRef cf_string = NULL;
   117 	
   118 	/* Create a CFString from a C string */
   119 	cf_string = CFStringCreateWithCString(
   120 										  NULL,
   121 										  the_path,
   122 										  kCFStringEncodingUTF8
   123 										  );
   124 	if(!cf_string)
   125 	{
   126 		return NULL;
   127 	}
   128 	
   129 	/* Create a CFURL from a CFString */
   130     the_url = CFURLCreateWithFileSystemPath(
   131 											NULL, 
   132 											cf_string,
   133 											kCFURLPOSIXPathStyle,
   134 											false
   135 											);
   136 	
   137 	/* Don't need the CFString any more (error or not) */
   138 	CFRelease(cf_string);
   139 	
   140 	if(!the_url)
   141 	{
   142 		return NULL;
   143 	}
   144 	
   145 	
   146     source_ref = CGImageSourceCreateWithURL(the_url, NULL);
   147 	/* Don't need the URL any more (error or not) */
   148 	CFRelease(the_url);
   149 	
   150 	return source_ref;
   151 }
   152 
   153 
   154 
   155 static CGImageRef CreateCGImageFromCGImageSource(CGImageSourceRef image_source)
   156 {
   157 	CGImageRef image_ref = NULL;
   158 	
   159 	if(NULL == image_source)
   160 	{
   161 		return NULL;
   162 	}
   163 	
   164 	// Get the first item in the image source (some image formats may
   165 	// contain multiple items).
   166 	image_ref = CGImageSourceCreateImageAtIndex(image_source, 0, NULL);
   167 	if(NULL == image_ref)
   168 	{
   169 		IMG_SetError("CGImageSourceCreateImageAtIndex() failed");
   170 	}
   171 	return image_ref;
   172 }
   173 
   174 static CFDictionaryRef CreateHintDictionary(CFStringRef uti_string_hint)
   175 {
   176 	CFDictionaryRef hint_dictionary = NULL;
   177 
   178 	if(uti_string_hint != NULL)
   179 	{
   180 		// Do a bunch of work to setup a CFDictionary containing the jpeg compression properties.
   181 		CFStringRef the_keys[1];
   182 		CFStringRef the_values[1];
   183 		
   184 		the_keys[0] = kCGImageSourceTypeIdentifierHint;
   185 		the_values[0] = uti_string_hint;
   186 		
   187 		// kCFTypeDictionaryKeyCallBacks or kCFCopyStringDictionaryKeyCallBacks?
   188 		hint_dictionary = CFDictionaryCreate(NULL, (const void**)&the_keys, (const void**)&the_values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
   189 	}
   190 	return hint_dictionary;
   191 }
   192 
   193 
   194 
   195 
   196 static int Internal_isType(SDL_RWops* rw_ops, CFStringRef uti_string_to_test)
   197 {
   198 	CGImageSourceRef image_source;
   199 	CFStringRef uti_type;
   200 	Boolean is_type;
   201 	
   202 	CFDictionaryRef hint_dictionary = NULL;
   203 	
   204 	hint_dictionary = CreateHintDictionary(uti_string_to_test);	
   205 	image_source = CreateCGImageSourceFromRWops(rw_ops, hint_dictionary);
   206 	
   207 	if(hint_dictionary != NULL)
   208 	{
   209 		CFRelease(hint_dictionary);		
   210 	}
   211 	
   212 	if(NULL == image_source)
   213 	{
   214 		return 0;
   215 	}
   216 	
   217 	// This will get the UTI of the container, not the image itself.
   218 	// Under most cases, this won't be a problem.
   219 	// But if a person passes an icon file which contains a bmp,
   220 	// the format will be of the icon file.
   221 	// But I think the main SDL_image codebase has this same problem so I'm not going to worry about it.	
   222 	uti_type = CGImageSourceGetType(image_source);
   223 	//	CFShow(uti_type);
   224 	
   225 	// Unsure if we really want conformance or equality
   226 	is_type = UTTypeConformsTo(uti_string_to_test, uti_type);
   227 	
   228 	CFRelease(image_source);
   229 	
   230 	return (int)is_type;
   231 }
   232 
   233 // Once we have our image, we need to get it into an SDL_Surface
   234 static SDL_Surface* Create_SDL_Surface_From_CGImage(CGImageRef image_ref)
   235 {
   236 	/* This code is adapted from Apple's Documentation found here:
   237 	 * http://developer.apple.com/documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/index.html
   238 	 * Listing 9-4††Using a Quartz image as a texture source.
   239 	 * Unfortunately, this guide doesn't show what to do about
   240 	 * non-RGBA image formats so I'm making the rest up.
   241 	 * All this code should be scrutinized.
   242 	 */
   243 
   244 	size_t w = CGImageGetWidth(image_ref);
   245 	size_t h = CGImageGetHeight(image_ref);
   246 	CGRect rect = {{0, 0}, {w, h}};
   247 
   248 	CGImageAlphaInfo alpha = CGImageGetAlphaInfo(image_ref);
   249 	//size_t bits_per_pixel = CGImageGetBitsPerPixel(image_ref);
   250 	size_t bits_per_component = 8;
   251 
   252 	SDL_Surface* surface;
   253 	Uint32 Amask;
   254 	Uint32 Rmask;
   255 	Uint32 Gmask;
   256 	Uint32 Bmask;
   257 
   258 	CGContextRef bitmap_context;
   259 	CGBitmapInfo bitmap_info;
   260 	CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();
   261 
   262 	if (alpha == kCGImageAlphaNone ||
   263 	    alpha == kCGImageAlphaNoneSkipFirst ||
   264 	    alpha == kCGImageAlphaNoneSkipLast) {
   265 		bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host; /* XRGB */
   266 		Amask = 0x00000000;
   267 	} else {
   268 		/* kCGImageAlphaFirst isn't supported */
   269 		//bitmap_info = kCGImageAlphaFirst | kCGBitmapByteOrder32Host; /* ARGB */
   270 		bitmap_info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host; /* ARGB */
   271 		Amask = 0xFF000000;
   272 	}
   273 
   274 	Rmask = 0x00FF0000;
   275 	Gmask = 0x0000FF00;
   276 	Bmask = 0x000000FF;
   277 
   278 	surface = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, Rmask, Gmask, Bmask, Amask);
   279 	if (surface)
   280 	{
   281 		// Sets up a context to be drawn to with surface->pixels as the area to be drawn to
   282 		bitmap_context = CGBitmapContextCreate(
   283 															surface->pixels,
   284 															surface->w,
   285 															surface->h,
   286 															bits_per_component,
   287 															surface->pitch,
   288 															color_space,
   289 															bitmap_info
   290 															);
   291 
   292 		// Draws the image into the context's image_data
   293 		CGContextDrawImage(bitmap_context, rect, image_ref);
   294 
   295 		CGContextRelease(bitmap_context);
   296 
   297 		// FIXME: Reverse the premultiplied alpha
   298 		if ((bitmap_info & kCGBitmapAlphaInfoMask) == kCGImageAlphaPremultipliedFirst) {
   299 			int i, j;
   300 			Uint8 *p = (Uint8 *)surface->pixels;
   301 			for (i = surface->h * surface->pitch/4; i--; ) {
   302 #if __LITTLE_ENDIAN__
   303 				Uint8 A = p[3];
   304 				if (A) {
   305 					for (j = 0; j < 3; ++j) {
   306 						p[j] = (p[j] * 255) / A;
   307 					}
   308 				}
   309 #else
   310 				Uint8 A = p[0];
   311 				if (A) {
   312 					for (j = 1; j < 4; ++j) {
   313 						p[j] = (p[j] * 255) / A;
   314 					}
   315 				}
   316 #endif /* ENDIAN */
   317 				p += 4;
   318 			}
   319 		}
   320 	}
   321 
   322 	if (color_space)
   323 	{
   324 		CGColorSpaceRelease(color_space);			
   325 	}
   326 
   327 	return surface;
   328 }
   329 
   330 
   331 static SDL_Surface* LoadImageFromRWops(SDL_RWops* rw_ops, CFStringRef uti_string_hint)
   332 {
   333 	SDL_Surface* sdl_surface;
   334 	CGImageSourceRef image_source;
   335 	CGImageRef image_ref = NULL;
   336 	CFDictionaryRef hint_dictionary = NULL;
   337 
   338 	hint_dictionary = CreateHintDictionary(uti_string_hint);
   339 	image_source = CreateCGImageSourceFromRWops(rw_ops, hint_dictionary);
   340 
   341 	if(hint_dictionary != NULL)
   342 	{
   343 		CFRelease(hint_dictionary);		
   344 	}
   345 	
   346 	if(NULL == image_source)
   347 	{
   348 		return NULL;
   349 	}
   350 	
   351 	image_ref = CreateCGImageFromCGImageSource(image_source);
   352 	CFRelease(image_source);
   353 
   354 	if(NULL == image_ref)
   355 	{
   356 		return NULL;
   357 	}
   358 	
   359 	sdl_surface = Create_SDL_Surface_From_CGImage(image_ref);
   360 	CFRelease(image_ref);
   361 	return sdl_surface;
   362 	
   363 }
   364 
   365 
   366 
   367 static SDL_Surface* LoadImageFromFile(const char* file)
   368 {
   369 	SDL_Surface* sdl_surface = NULL;
   370 	CGImageSourceRef image_source = NULL;
   371 	CGImageRef image_ref = NULL;
   372 	
   373 	// First ImageIO
   374 	image_source = CreateCGImageSourceFromFile(file);
   375 	
   376 	if(NULL == image_source)
   377 	{
   378 		return NULL;
   379 	}
   380 	
   381 	image_ref = CreateCGImageFromCGImageSource(image_source);
   382 	CFRelease(image_source);
   383 	
   384 	if(NULL == image_ref)
   385 	{
   386 		return NULL;
   387 	}
   388 	
   389 	sdl_surface = Create_SDL_Surface_From_CGImage(image_ref);
   390 	CFRelease(image_ref);
   391 	return sdl_surface;	
   392 }
   393 
   394 int IMG_InitJPG()
   395 {
   396 	return 0;
   397 }
   398 
   399 void IMG_QuitJPG()
   400 {
   401 }
   402 
   403 int IMG_InitPNG()
   404 {
   405 	return 0;
   406 }
   407 
   408 void IMG_QuitPNG()
   409 {
   410 }
   411 
   412 int IMG_InitTIF()
   413 {
   414 	return 0;
   415 }
   416 
   417 void IMG_QuitTIF()
   418 {
   419 }
   420 
   421 int IMG_isCUR(SDL_RWops *src)
   422 {
   423 	/* FIXME: Is this a supported type? */
   424 	return Internal_isType(src, CFSTR("com.microsoft.cur"));
   425 }
   426 
   427 int IMG_isICO(SDL_RWops *src)
   428 {
   429 	return Internal_isType(src, kUTTypeICO);
   430 }
   431 
   432 int IMG_isBMP(SDL_RWops *src)
   433 {
   434 	return Internal_isType(src, kUTTypeBMP);
   435 }
   436 
   437 int IMG_isGIF(SDL_RWops *src)
   438 {
   439 	return Internal_isType(src, kUTTypeGIF);
   440 }
   441 
   442 // Note: JPEG 2000 is kUTTypeJPEG2000
   443 int IMG_isJPG(SDL_RWops *src)
   444 {
   445 	return Internal_isType(src, kUTTypeJPEG);
   446 }
   447 
   448 int IMG_isPNG(SDL_RWops *src)
   449 {
   450 	return Internal_isType(src, kUTTypePNG);
   451 }
   452 
   453 // This isn't a public API function. Apple seems to be able to identify tga's.
   454 int IMG_isTGA(SDL_RWops *src)
   455 {
   456 	return Internal_isType(src, CFSTR("com.truevision.tga-image"));
   457 }
   458 
   459 int IMG_isTIF(SDL_RWops *src)
   460 {
   461 	return Internal_isType(src, kUTTypeTIFF);
   462 }
   463 
   464 SDL_Surface* IMG_LoadCUR_RW(SDL_RWops *src)
   465 {
   466 	/* FIXME: Is this a supported type? */
   467 	return LoadImageFromRWops(src, CFSTR("com.microsoft.cur"));
   468 }
   469 SDL_Surface* IMG_LoadICO_RW(SDL_RWops *src)
   470 {
   471 	return LoadImageFromRWops(src, kUTTypeICO);
   472 }
   473 SDL_Surface* IMG_LoadBMP_RW(SDL_RWops *src)
   474 {
   475 	return LoadImageFromRWops(src, kUTTypeBMP);
   476 }
   477 SDL_Surface* IMG_LoadGIF_RW(SDL_RWops *src)
   478 {
   479 	return LoadImageFromRWops(src, kUTTypeGIF);
   480 }
   481 SDL_Surface* IMG_LoadJPG_RW(SDL_RWops *src)
   482 {
   483 	return LoadImageFromRWops(src, kUTTypeJPEG);
   484 }
   485 SDL_Surface* IMG_LoadPNG_RW(SDL_RWops *src)
   486 {
   487 	return LoadImageFromRWops(src, kUTTypePNG);
   488 }
   489 SDL_Surface* IMG_LoadTGA_RW(SDL_RWops *src)
   490 {
   491 	return LoadImageFromRWops(src, CFSTR("com.truevision.tga-image"));
   492 }
   493 SDL_Surface* IMG_LoadTIF_RW(SDL_RWops *src)
   494 {
   495 	return LoadImageFromRWops(src, kUTTypeTIFF);
   496 }
   497 
   498 // Apple provides both stream and file loading functions in ImageIO.
   499 // Potentially, Apple can optimize for either case.
   500 SDL_Surface* IMG_Load(const char *file)
   501 {
   502 	SDL_Surface* sdl_surface = NULL;
   503 
   504 	sdl_surface = LoadImageFromFile(file);
   505 	if(NULL == sdl_surface)
   506 	{
   507 		// Either the file doesn't exist or ImageIO doesn't understand the format.
   508 		// For the latter case, fallback to the native SDL_image handlers.
   509 		SDL_RWops *src = SDL_RWFromFile(file, "rb");
   510 		char *ext = strrchr(file, '.');
   511 		if(ext) {
   512 			ext++;
   513 		}
   514 		if(!src) {
   515 			/* The error message has been set in SDL_RWFromFile */
   516 			return NULL;
   517 		}
   518 		sdl_surface = IMG_LoadTyped_RW(src, 1, ext);
   519 	}
   520 	return sdl_surface;
   521 }
   522 
   523 #endif /* defined(__APPLE__) && !defined(SDL_IMAGE_USE_COMMON_BACKEND) */