IMG_ImageIO.m
author Sam Lantinga <slouken@libsdl.org>
Fri, 30 Dec 2011 16:14:00 -0500
changeset 279 e2fa9ff13b5d
parent 270 IMG_ImageIO.c@98f97f83e203
child 283 ec183abd5996
permissions -rw-r--r--
SDL_image uses ImageIO for ios4 and osx and UIImage on ios3

Vittorio Giovara

>> The second one is a feature request to make it so that when the
>> ImageIO framework is not present the library would fallback to the
>> UIImage backend; although it would perhaps make backporting a little
>> difficult I am sure that there is still a lot of software targeting
>> pre-ios 4 that could make use of it.
>
> It's possible, and not entirely unreasonable, but iOS moves so fast
> that I haven't felt it really merited the extra effort to do so. My
> experience is that there isn't a lot of interest in 3.x. I have a
> 3.1.3 device and there are actually very few apps (that I would be
> interested in getting) that support 3.x any more and I have to be very
> careful to no upgrade my existing apps because the new versions don't
> work on my device any more. I think the percentage of devices 3.x is
> less than 10% now. Testing old devices is another difficulty. And if
> you need to start weak linking frameworks in the Xcode project, you go
> into areas most non Apple developers don't know about.
>
> -Eric

I see your point, iOS is a much faster platform than OSX and a lot of
applications are dropping support for 3.* devices, however I feel that
as SDL is a compatibility layer between architectures it should be as
compatible as possible, until major api change or build system make it
impossible/unbearable to keep it updated.
Having said that I am proposing a solution that should keep SDL_image
compatible with both the newer ImageIO for ios4 and osx and allow us
to use uiimage on ios3: what I am linking is a merge of
IMG_UIImage.m and IMG_ImageIO.c and the small changes that need to be
applied to the related xcode project files).
     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 #include <Foundation/Foundation.h>
    18 
    19 #if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)
    20 #ifdef ALLOW_UIIMAGE_FALLBACK
    21 #define USE_UIIMAGE_BACKEND() ([UIImage instancesRespondToSelector:@selector(initWithCGImage:scale:orientation:)] == NO)
    22 #else
    23 #define USE_UIIMAGE_BACKEND() (Internal_checkImageIOisAvailable())
    24 #endif
    25 #import <MobileCoreServices/MobileCoreServices.h> // for UTCoreTypes.h
    26 #import <ImageIO/ImageIO.h>
    27 #import <UIKit/UIImage.h>
    28 #else
    29 // For ImageIO framework and also LaunchServices framework (for UTIs)
    30 #include <ApplicationServices/ApplicationServices.h>
    31 #endif
    32 
    33 /**************************************************************
    34  ***** Begin Callback functions for block reading *************
    35  **************************************************************/
    36 
    37 // This callback reads some bytes from an SDL_rwops and copies it
    38 // to a Quartz buffer (supplied by Apple framework).
    39 static size_t MyProviderGetBytesCallback(void* rwops_userdata, void* quartz_buffer, size_t the_count)
    40 {
    41     return (size_t)SDL_RWread((struct SDL_RWops *)rwops_userdata, quartz_buffer, 1, the_count);
    42 }
    43 
    44 // This callback is triggered when the data provider is released
    45 // so you can clean up any resources.
    46 static void MyProviderReleaseInfoCallback(void* rwops_userdata)
    47 {
    48     // What should I put here? 
    49     // I think the user and SDL_RWops controls closing, so I don't do anything.
    50 }
    51 
    52 static void MyProviderRewindCallback(void* rwops_userdata)
    53 {
    54     SDL_RWseek((struct SDL_RWops *)rwops_userdata, 0, RW_SEEK_SET);
    55 }
    56 
    57 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 // CGDataProviderCreateSequential was introduced in 10.5; CGDataProviderCreate is deprecated
    58 off_t MyProviderSkipForwardBytesCallback(void* rwops_userdata, off_t the_count)
    59 {
    60     off_t start_position = SDL_RWtell((struct SDL_RWops *)rwops_userdata);
    61     SDL_RWseek((struct SDL_RWops *)rwops_userdata, the_count, RW_SEEK_CUR);
    62     off_t end_position = SDL_RWtell((struct SDL_RWops *)rwops_userdata);
    63     return (end_position - start_position);	
    64 }
    65 #else // CGDataProviderCreate was deprecated in 10.5
    66 static void MyProviderSkipBytesCallback(void* rwops_userdata, size_t the_count)
    67 {
    68     SDL_RWseek((struct SDL_RWops *)rwops_userdata, the_count, RW_SEEK_CUR);
    69 }
    70 #endif
    71 
    72 /**************************************************************
    73  ***** End Callback functions for block reading ***************
    74  **************************************************************/
    75 
    76 // This creates a CGImageSourceRef which is a handle to an image that can be used to examine information
    77 // about the image or load the actual image data.
    78 static CGImageSourceRef CreateCGImageSourceFromRWops(SDL_RWops* rw_ops, CFDictionaryRef hints_and_options)
    79 {
    80     CGImageSourceRef source_ref;
    81     
    82     // Similar to SDL_RWops, Apple has their own callbacks for dealing with data streams.
    83     
    84 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 // CGDataProviderCreateSequential was introduced in 10.5; CGDataProviderCreate is deprecated
    85     CGDataProviderSequentialCallbacks provider_callbacks =
    86     {
    87         0,
    88         MyProviderGetBytesCallback,
    89         MyProviderSkipForwardBytesCallback,
    90         MyProviderRewindCallback,
    91         MyProviderReleaseInfoCallback
    92     };
    93     
    94     CGDataProviderRef data_provider = CGDataProviderCreateSequential(rw_ops, &provider_callbacks);
    95     
    96     
    97 #else // CGDataProviderCreate was deprecated in 10.5
    98     
    99     CGDataProviderCallbacks provider_callbacks =
   100     {
   101         MyProviderGetBytesCallback,
   102         MyProviderSkipBytesCallback,
   103         MyProviderRewindCallback,
   104         MyProviderReleaseInfoCallback
   105     };
   106     
   107     CGDataProviderRef data_provider = CGDataProviderCreate(rw_ops, &provider_callbacks);
   108 #endif
   109     // Get the CGImageSourceRef.
   110     // The dictionary can be NULL or contain hints to help ImageIO figure out the image type.
   111     source_ref = CGImageSourceCreateWithDataProvider(data_provider, hints_and_options);
   112     return source_ref;
   113 }
   114 
   115 /* Create a CGImageSourceRef from a file. */
   116 /* Remember to CFRelease the created source when done. */
   117 static CGImageSourceRef CreateCGImageSourceFromFile(const char* the_path)
   118 {
   119     CFURLRef the_url = NULL;
   120     CGImageSourceRef source_ref = NULL;
   121     CFStringRef cf_string = NULL;
   122     
   123     /* Create a CFString from a C string */
   124     cf_string = CFStringCreateWithCString(NULL, the_path, kCFStringEncodingUTF8);
   125     if (!cf_string) {
   126         return NULL;
   127     }
   128     
   129     /* Create a CFURL from a CFString */
   130     the_url = CFURLCreateWithFileSystemPath(NULL, cf_string, kCFURLPOSIXPathStyle, false);
   131     
   132     /* Don't need the CFString any more (error or not) */
   133     CFRelease(cf_string);
   134     
   135     if(!the_url)
   136     {
   137         return NULL;
   138     }
   139     
   140     
   141     source_ref = CGImageSourceCreateWithURL(the_url, NULL);
   142     /* Don't need the URL any more (error or not) */
   143     CFRelease(the_url);
   144     
   145     return source_ref;
   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     if(NULL == image_ref)
   161     {
   162         IMG_SetError("CGImageSourceCreateImageAtIndex() failed");
   163     }
   164     return image_ref;
   165 }
   166 
   167 static CFDictionaryRef CreateHintDictionary(CFStringRef uti_string_hint)
   168 {
   169     CFDictionaryRef hint_dictionary = NULL;
   170     
   171     if(uti_string_hint != NULL)
   172     {
   173         // Do a bunch of work to setup a CFDictionary containing the jpeg compression properties.
   174         CFStringRef the_keys[1];
   175         CFStringRef the_values[1];
   176         
   177         the_keys[0] = kCGImageSourceTypeIdentifierHint;
   178         the_values[0] = uti_string_hint;
   179         
   180         // kCFTypeDictionaryKeyCallBacks or kCFCopyStringDictionaryKeyCallBacks?
   181         hint_dictionary = CFDictionaryCreate(NULL, (const void**)&the_keys, (const void**)&the_values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
   182     }
   183     return hint_dictionary;
   184 }
   185 
   186 
   187 // Once we have our image, we need to get it into an SDL_Surface
   188 static SDL_Surface* Create_SDL_Surface_From_CGImage(CGImageRef image_ref)
   189 {
   190     /* This code is adapted from Apple's Documentation found here:
   191      * http://developer.apple.com/documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/index.html
   192      * Listing 9-4††Using a Quartz image as a texture source.
   193      * Unfortunately, this guide doesn't show what to do about
   194      * non-RGBA image formats so I'm making the rest up.
   195      * All this code should be scrutinized.
   196      */
   197     
   198     size_t w = CGImageGetWidth(image_ref);
   199     size_t h = CGImageGetHeight(image_ref);
   200     CGRect rect = {{0, 0}, {w, h}};
   201     
   202     CGImageAlphaInfo alpha = CGImageGetAlphaInfo(image_ref);
   203     //size_t bits_per_pixel = CGImageGetBitsPerPixel(image_ref);
   204     size_t bits_per_component = 8;
   205     
   206     SDL_Surface* surface;
   207     Uint32 Amask;
   208     Uint32 Rmask;
   209     Uint32 Gmask;
   210     Uint32 Bmask;
   211     
   212     CGContextRef bitmap_context;
   213     CGBitmapInfo bitmap_info;
   214     CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();
   215     
   216     if (alpha == kCGImageAlphaNone ||
   217         alpha == kCGImageAlphaNoneSkipFirst ||
   218         alpha == kCGImageAlphaNoneSkipLast) {
   219         bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host; /* XRGB */
   220         Amask = 0x00000000;
   221     } else {
   222         /* kCGImageAlphaFirst isn't supported */
   223         //bitmap_info = kCGImageAlphaFirst | kCGBitmapByteOrder32Host; /* ARGB */
   224         bitmap_info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host; /* ARGB */
   225         Amask = 0xFF000000;
   226     }
   227     
   228     Rmask = 0x00FF0000;
   229     Gmask = 0x0000FF00;
   230     Bmask = 0x000000FF;
   231     
   232     surface = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, Rmask, Gmask, Bmask, Amask);
   233     if (surface)
   234     {
   235         // Sets up a context to be drawn to with surface->pixels as the area to be drawn to
   236         bitmap_context = CGBitmapContextCreate(
   237                                                surface->pixels,
   238                                                surface->w,
   239                                                surface->h,
   240                                                bits_per_component,
   241                                                surface->pitch,
   242                                                color_space,
   243                                                bitmap_info
   244                                                );
   245         
   246         // Draws the image into the context's image_data
   247         CGContextDrawImage(bitmap_context, rect, image_ref);
   248         
   249         CGContextRelease(bitmap_context);
   250         
   251         // FIXME: Reverse the premultiplied alpha
   252         if ((bitmap_info & kCGBitmapAlphaInfoMask) == kCGImageAlphaPremultipliedFirst) {
   253             int i, j;
   254             Uint8 *p = (Uint8 *)surface->pixels;
   255             for (i = surface->h * surface->pitch/4; i--; ) {
   256 #if __LITTLE_ENDIAN__
   257                 Uint8 A = p[3];
   258                 if (A) {
   259                     for (j = 0; j < 3; ++j) {
   260                         p[j] = (p[j] * 255) / A;
   261                     }
   262                 }
   263 #else
   264                 Uint8 A = p[0];
   265                 if (A) {
   266                     for (j = 1; j < 4; ++j) {
   267                         p[j] = (p[j] * 255) / A;
   268                     }
   269                 }
   270 #endif /* ENDIAN */
   271                 p += 4;
   272             }
   273         }
   274     }
   275     
   276     if (color_space)
   277     {
   278         CGColorSpaceRelease(color_space);			
   279     }
   280     
   281     return surface;
   282 }
   283 
   284 
   285 #pragma mark -
   286 #pragma mark IMG_Init stubs
   287 #if !defined(ALLOW_UIIMAGE_FALLBACK) && ((TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1))
   288 static int Internal_checkImageIOisAvailable() {
   289     // just check if we are running on ios 4 or more, else throw exception
   290     if ([UIImage instancesRespondToSelector:@selector(initWithCGImage:scale:orientation:)])
   291         return 0;
   292     [NSException raise:@"UIImage fallback not enabled at compile time"
   293                 format:@"ImageIO is not available on your platform, please recompile SDL_Image with ALLOW_UIIMAGE_FALLBACK."];
   294     return -1;
   295 }
   296 #endif
   297 
   298 int IMG_InitJPG()
   299 {
   300     return 0;
   301 }
   302 
   303 void IMG_QuitJPG()
   304 {
   305 }
   306 
   307 int IMG_InitPNG()
   308 {
   309     return 0;
   310 }
   311 
   312 void IMG_QuitPNG()
   313 {
   314 }
   315 
   316 int IMG_InitTIF()
   317 {
   318     return 0;
   319 }
   320 
   321 void IMG_QuitTIF()
   322 {
   323 }
   324 
   325 #pragma mark -
   326 #pragma mark Get type of image
   327 static int Internal_isType_UIImage (SDL_RWops *rw_ops, CFStringRef uti_string_to_test)
   328 {
   329     int is_type = 0;
   330     
   331 #if defined(ALLOW_UIIMAGE_FALLBACK) && ((TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1))
   332     int start = SDL_RWtell(rw_ops);
   333     if ((0 == CFStringCompare(uti_string_to_test, kUTTypeICO, 0)) ||
   334         (0 == CFStringCompare(uti_string_to_test, CFSTR("com.microsoft.cur"), 0))) {
   335         
   336         // The Win32 ICO file header (14 bytes)
   337         Uint16 bfReserved;
   338         Uint16 bfType;
   339         Uint16 bfCount;
   340         int type = (0 == CFStringCompare(uti_string_to_test, kUTTypeICO, 0)) ? 1 : 2;
   341         
   342         bfReserved = SDL_ReadLE16(rw_ops);
   343         bfType = SDL_ReadLE16(rw_ops);
   344         bfCount = SDL_ReadLE16(rw_ops);
   345         if ((bfReserved == 0) && (bfType == type) && (bfCount != 0)) 
   346             is_type = 1;
   347     } else if (0 == CFStringCompare(uti_string_to_test, kUTTypeBMP, 0)) {
   348         char magic[2];
   349         
   350         if ( SDL_RWread(rw_ops, magic, sizeof(magic), 1) ) {
   351             if ( strncmp(magic, "BM", 2) == 0 ) {
   352                 is_type = 1;
   353             }
   354         }
   355     } else if (0 == CFStringCompare(uti_string_to_test, kUTTypeGIF, 0)) {
   356         char magic[6];
   357         
   358         if ( SDL_RWread(rw_ops, magic, sizeof(magic), 1) ) {
   359             if ( (strncmp(magic, "GIF", 3) == 0) &&
   360                 ((memcmp(magic + 3, "87a", 3) == 0) ||
   361                  (memcmp(magic + 3, "89a", 3) == 0)) ) {
   362                     is_type = 1;
   363                 }
   364         }
   365     } else if (0 == CFStringCompare(uti_string_to_test, kUTTypeJPEG, 0)) {
   366         int in_scan = 0;
   367         Uint8 magic[4];
   368         
   369         // This detection code is by Steaphan Greene <stea@cs.binghamton.edu>
   370         // Blame me, not Sam, if this doesn't work right. */
   371         // And don't forget to report the problem to the the sdl list too! */
   372         
   373         if ( SDL_RWread(rw_ops, magic, 2, 1) ) {
   374             if ( (magic[0] == 0xFF) && (magic[1] == 0xD8) ) {
   375                 is_type = 1;
   376                 while (is_type == 1) {
   377                     if(SDL_RWread(rw_ops, magic, 1, 2) != 2) {
   378                         is_type = 0;
   379                     } else if( (magic[0] != 0xFF) && (in_scan == 0) ) {
   380                         is_type = 0;
   381                     } else if( (magic[0] != 0xFF) || (magic[1] == 0xFF) ) {
   382                         /* Extra padding in JPEG (legal) */
   383                         /* or this is data and we are scanning */
   384                         SDL_RWseek(rw_ops, -1, SEEK_CUR);
   385                     } else if(magic[1] == 0xD9) {
   386                         /* Got to end of good JPEG */
   387                         break;
   388                     } else if( (in_scan == 1) && (magic[1] == 0x00) ) {
   389                         /* This is an encoded 0xFF within the data */
   390                     } else if( (magic[1] >= 0xD0) && (magic[1] < 0xD9) ) {
   391                         /* These have nothing else */
   392                     } else if(SDL_RWread(rw_ops, magic+2, 1, 2) != 2) {
   393                         is_type = 0;
   394                     } else {
   395                         /* Yes, it's big-endian */
   396                         Uint32 start;
   397                         Uint32 size;
   398                         Uint32 end;
   399                         start = SDL_RWtell(rw_ops);
   400                         size = (magic[2] << 8) + magic[3];
   401                         end = SDL_RWseek(rw_ops, size-2, SEEK_CUR);
   402                         if ( end != start + size - 2 ) is_type = 0;
   403                         if ( magic[1] == 0xDA ) {
   404                             /* Now comes the actual JPEG meat */
   405 #ifdef	FAST_IS_JPEG
   406                             /* Ok, I'm convinced.  It is a JPEG. */
   407                             break;
   408 #else
   409                             /* I'm not convinced.  Prove it! */
   410                             in_scan = 1;
   411 #endif
   412                         }
   413                     }
   414                 }
   415             }
   416         }
   417     } else if (0 == CFStringCompare(uti_string_to_test, kUTTypePNG, 0)) {
   418         Uint8 magic[4];
   419         
   420         if ( SDL_RWread(rw_ops, magic, 1, sizeof(magic)) == sizeof(magic) ) {
   421             if ( magic[0] == 0x89 &&
   422                 magic[1] == 'P' &&
   423                 magic[2] == 'N' &&
   424                 magic[3] == 'G' ) {
   425                 is_type = 1;
   426             }
   427         }
   428     } else if (0 == CFStringCompare(uti_string_to_test, CFSTR("com.truevision.tga-image"), 0)) {
   429         //TODO: fill me!
   430     } else if (0 == CFStringCompare(uti_string_to_test, kUTTypeTIFF, 0)) {
   431         Uint8 magic[4];
   432         
   433         if ( SDL_RWread(rw_ops, magic, 1, sizeof(magic)) == sizeof(magic) ) {
   434             if ( (magic[0] == 'I' &&
   435                   magic[1] == 'I' &&
   436                   magic[2] == 0x2a &&
   437                   magic[3] == 0x00) ||
   438                 (magic[0] == 'M' &&
   439                  magic[1] == 'M' &&
   440                  magic[2] == 0x00 &&
   441                  magic[3] == 0x2a) ) {
   442                     is_type = 1;
   443                 }
   444         }
   445     }
   446     
   447     // reset the file descption pointer
   448     SDL_RWseek(rw_ops, start, SEEK_SET);
   449 
   450 #endif  /* #if defined(ALLOW_UIIMAGE_FALLBACK) && ((TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)) */
   451     return is_type;
   452 }
   453 
   454 static int Internal_isType_ImageIO (SDL_RWops *rw_ops, CFStringRef uti_string_to_test)
   455 {
   456     int is_type = 0;
   457     
   458     CFDictionaryRef hint_dictionary = CreateHintDictionary(uti_string_to_test);	
   459     CGImageSourceRef image_source = CreateCGImageSourceFromRWops(rw_ops, hint_dictionary);
   460     
   461     if (hint_dictionary != NULL) {
   462         CFRelease(hint_dictionary);		
   463     }
   464     
   465     if (NULL == image_source) {
   466         return 0;
   467     }
   468     
   469     // This will get the UTI of the container, not the image itself.
   470     // Under most cases, this won't be a problem.
   471     // But if a person passes an icon file which contains a bmp,
   472     // the format will be of the icon file.
   473     // But I think the main SDL_image codebase has this same problem so I'm not going to worry about it.	
   474     CFStringRef uti_type = CGImageSourceGetType(image_source);
   475     //	CFShow(uti_type);
   476     
   477     // Unsure if we really want conformance or equality
   478     is_type = (int)UTTypeConformsTo(uti_string_to_test, uti_type);
   479     
   480     CFRelease(image_source);
   481     return is_type;
   482 }
   483 
   484 static int Internal_isType (SDL_RWops *rw_ops, CFStringRef uti_string_to_test)
   485 {
   486     if (rw_ops == NULL)
   487         return 0;
   488     
   489 #if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)
   490     if (USE_UIIMAGE_BACKEND())
   491         return Internal_isType_UIImage(rw_ops, uti_string_to_test);
   492     else
   493 #endif
   494         return Internal_isType_ImageIO(rw_ops, uti_string_to_test);
   495 }
   496 
   497 int IMG_isCUR(SDL_RWops *src)
   498 {
   499     /* FIXME: Is this a supported type? */
   500     return Internal_isType(src, CFSTR("com.microsoft.cur"));
   501 }
   502 
   503 int IMG_isICO(SDL_RWops *src)
   504 {
   505     return Internal_isType(src, kUTTypeICO);
   506 }
   507 
   508 int IMG_isBMP(SDL_RWops *src)
   509 {
   510     return Internal_isType(src, kUTTypeBMP);
   511 }
   512 
   513 int IMG_isGIF(SDL_RWops *src)
   514 {
   515     return Internal_isType(src, kUTTypeGIF);
   516 }
   517 
   518 // Note: JPEG 2000 is kUTTypeJPEG2000
   519 int IMG_isJPG(SDL_RWops *src)
   520 {
   521     return Internal_isType(src, kUTTypeJPEG);
   522 }
   523 
   524 int IMG_isPNG(SDL_RWops *src)
   525 {
   526     return Internal_isType(src, kUTTypePNG);
   527 }
   528 
   529 // This isn't a public API function. Apple seems to be able to identify tga's.
   530 int IMG_isTGA(SDL_RWops *src)
   531 {
   532     return Internal_isType(src, CFSTR("com.truevision.tga-image"));
   533 }
   534 
   535 int IMG_isTIF(SDL_RWops *src)
   536 {
   537     return Internal_isType(src, kUTTypeTIFF);
   538 }
   539 
   540 #pragma mark -
   541 #pragma mark Load image engine
   542 static SDL_Surface *LoadImageFromRWops_UIImage (SDL_RWops* rw_ops, CFStringRef uti_string_hint)
   543 {
   544     SDL_Surface *sdl_surface = NULL;
   545 
   546 #if defined(ALLOW_UIIMAGE_FALLBACK) && ((TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1))
   547     NSAutoreleasePool* autorelease_pool = [[NSAutoreleasePool alloc] init];
   548     UIImage *ui_image;
   549     int bytes_read = 0;
   550     // I don't know what a good size is. 
   551     // Max recommended texture size is 1024x1024 on iPhone so maybe base it on that?
   552     const int block_size = 1024*4;
   553     char temp_buffer[block_size];
   554     
   555     NSMutableData* ns_data = [[NSMutableData alloc] initWithCapacity:1024*1024*4];
   556     do {
   557         bytes_read = SDL_RWread(rw_ops, temp_buffer, 1, block_size);
   558         [ns_data appendBytes:temp_buffer length:bytes_read];
   559     } while (bytes_read > 0);
   560     
   561     ui_image = [[UIImage alloc] initWithData:ns_data];
   562     if (ui_image != nil)
   563         sdl_surface = Create_SDL_Surface_From_CGImage([ui_image CGImage]);
   564     [ui_image release];
   565     [ns_data release];          
   566     [autorelease_pool drain];
   567 
   568 #endif  /* #if defined(ALLOW_UIIMAGE_FALLBACK) && ((TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)) */
   569     return sdl_surface;
   570 }
   571 
   572 static SDL_Surface *LoadImageFromRWops_ImageIO (SDL_RWops *rw_ops, CFStringRef uti_string_hint)
   573 {
   574     CFDictionaryRef hint_dictionary = CreateHintDictionary(uti_string_hint);
   575     CGImageSourceRef image_source = CreateCGImageSourceFromRWops(rw_ops, hint_dictionary);
   576 
   577     if (hint_dictionary != NULL)
   578         CFRelease(hint_dictionary);		
   579 
   580     if (NULL == image_source)
   581         return NULL;
   582     
   583     CGImageRef image_ref = CreateCGImageFromCGImageSource(image_source);
   584     CFRelease(image_source);
   585 
   586     if (NULL == image_ref)
   587         return NULL;
   588     SDL_Surface *sdl_surface = Create_SDL_Surface_From_CGImage(image_ref);
   589     CFRelease(image_ref);
   590 
   591     return sdl_surface;
   592 }
   593 
   594 static SDL_Surface *LoadImageFromRWops (SDL_RWops *rw_ops, CFStringRef uti_string_hint)
   595 {
   596 #if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)
   597     if (USE_UIIMAGE_BACKEND()) 
   598         return LoadImageFromRWops_UIImage(rw_ops, uti_string_hint);
   599     else
   600 #endif
   601         return LoadImageFromRWops_ImageIO(rw_ops, uti_string_hint);
   602 }
   603 
   604 static SDL_Surface* LoadImageFromFile_UIImage (const char *file)
   605 {
   606     SDL_Surface *sdl_surface = NULL;
   607 
   608 #if defined(ALLOW_UIIMAGE_FALLBACK) && ((TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1))
   609     NSAutoreleasePool* autorelease_pool = [[NSAutoreleasePool alloc] init];
   610     NSString *ns_string = [[NSString alloc] initWithUTF8String:file];
   611     UIImage *ui_image = [[UIImage alloc] initWithContentsOfFile:ns_string];
   612     if (ui_image != nil)
   613         sdl_surface = Create_SDL_Surface_From_CGImage([ui_image CGImage]);
   614     [ui_image release];
   615     [ns_string release];
   616     [autorelease_pool drain];
   617 
   618 #endif  /* #if defined(ALLOW_UIIMAGE_FALLBACK) && ((TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)) */
   619     return sdl_surface;	
   620 }
   621 
   622 static SDL_Surface* LoadImageFromFile_ImageIO (const char *file)
   623 {
   624     CGImageSourceRef image_source = NULL;
   625 
   626     image_source = CreateCGImageSourceFromFile(file);
   627 
   628     if(NULL == image_source)
   629         return NULL;
   630     
   631     CGImageRef image_ref = CreateCGImageFromCGImageSource(image_source);
   632     CFRelease(image_source);
   633 
   634     if (NULL == image_ref)
   635         return NULL;
   636     SDL_Surface *sdl_surface = Create_SDL_Surface_From_CGImage(image_ref);
   637     CFRelease(image_ref);
   638     return sdl_surface;	
   639 }
   640 
   641 static SDL_Surface* LoadImageFromFile (const char *file)
   642 {
   643 #if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)
   644     if (USE_UIIMAGE_BACKEND())
   645         return LoadImageFromFile_UIImage(file);
   646     else
   647 #endif
   648         return LoadImageFromFile_ImageIO(file);
   649 }
   650 
   651 SDL_Surface* IMG_LoadCUR_RW (SDL_RWops *src)
   652 {
   653     /* FIXME: Is this a supported type? */
   654     return LoadImageFromRWops(src, CFSTR("com.microsoft.cur"));
   655 }
   656 
   657 SDL_Surface* IMG_LoadICO_RW (SDL_RWops *src)
   658 {
   659     return LoadImageFromRWops(src, kUTTypeICO);
   660 }
   661 
   662 SDL_Surface* IMG_LoadBMP_RW (SDL_RWops *src)
   663 {
   664     return LoadImageFromRWops(src, kUTTypeBMP);
   665 }
   666 
   667 SDL_Surface* IMG_LoadGIF_RW (SDL_RWops *src)
   668 {
   669     return LoadImageFromRWops (src, kUTTypeGIF);
   670 }
   671 
   672 SDL_Surface* IMG_LoadJPG_RW (SDL_RWops *src)
   673 {
   674     return LoadImageFromRWops (src, kUTTypeJPEG);
   675 }
   676 
   677 SDL_Surface* IMG_LoadPNG_RW (SDL_RWops *src)
   678 {
   679     return LoadImageFromRWops (src, kUTTypePNG);
   680 }
   681 
   682 SDL_Surface* IMG_LoadTGA_RW (SDL_RWops *src)
   683 {
   684     return LoadImageFromRWops(src, CFSTR("com.truevision.tga-image"));
   685 }
   686 
   687 SDL_Surface* IMG_LoadTIF_RW (SDL_RWops *src)
   688 {
   689     return LoadImageFromRWops(src, kUTTypeTIFF);
   690 }
   691 
   692 // Since UIImage doesn't really support streams well, we should optimize for the file case.
   693 // Apple provides both stream and file loading functions in ImageIO.
   694 // Potentially, Apple can optimize for either case.
   695 SDL_Surface* IMG_Load (const char *file)
   696 {
   697     SDL_Surface* sdl_surface = NULL;
   698     
   699     sdl_surface = LoadImageFromFile(file);
   700     if(NULL == sdl_surface)
   701     {
   702         // Either the file doesn't exist or ImageIO doesn't understand the format.
   703         // For the latter case, fallback to the native SDL_image handlers.
   704         SDL_RWops *src = SDL_RWFromFile(file, "rb");
   705         char *ext = strrchr(file, '.');
   706         if (ext) {
   707             ext++;
   708         }
   709         if (!src) {
   710             /* The error message has been set in SDL_RWFromFile */
   711             return NULL;
   712         }
   713         sdl_surface = IMG_LoadTyped_RW(src, 1, ext);
   714     }
   715     return sdl_surface;
   716 }
   717 
   718 #endif /* defined(__APPLE__) && !defined(SDL_IMAGE_USE_COMMON_BACKEND) */