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