IMG_ImageIO.m
author Sam Lantinga <slouken@libsdl.org>
Tue, 12 Dec 2017 22:18:18 -0800
changeset 550 b5bcb9cb9f6d
parent 530 181ef57f85b4
permissions -rw-r--r--
Fixed iOS build warnings
     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     CGDataProviderRelease(data_provider);
   113     return source_ref;
   114 }
   115 
   116 /* Create a CGImageSourceRef from a file. */
   117 /* Remember to CFRelease the created source when done. */
   118 static CGImageSourceRef CreateCGImageSourceFromFile(const char* the_path)
   119 {
   120     CFURLRef the_url = NULL;
   121     CGImageSourceRef source_ref = NULL;
   122     CFStringRef cf_string = NULL;
   123 
   124     /* Create a CFString from a C string */
   125     cf_string = CFStringCreateWithCString(NULL, the_path, kCFStringEncodingUTF8);
   126     if (!cf_string) {
   127         return NULL;
   128     }
   129 
   130     /* Create a CFURL from a CFString */
   131     the_url = CFURLCreateWithFileSystemPath(NULL, cf_string, kCFURLPOSIXPathStyle, false);
   132 
   133     /* Don't need the CFString any more (error or not) */
   134     CFRelease(cf_string);
   135 
   136     if(!the_url)
   137     {
   138         return NULL;
   139     }
   140 
   141 
   142     source_ref = CGImageSourceCreateWithURL(the_url, NULL);
   143     /* Don't need the URL any more (error or not) */
   144     CFRelease(the_url);
   145 
   146     return source_ref;
   147 }
   148 
   149 static CGImageRef CreateCGImageFromCGImageSource(CGImageSourceRef image_source)
   150 {
   151     CGImageRef image_ref = NULL;
   152 
   153     if(NULL == image_source)
   154     {
   155         return NULL;
   156     }
   157 
   158     // Get the first item in the image source (some image formats may
   159     // contain multiple items).
   160     image_ref = CGImageSourceCreateImageAtIndex(image_source, 0, NULL);
   161     if(NULL == image_ref)
   162     {
   163         IMG_SetError("CGImageSourceCreateImageAtIndex() failed");
   164     }
   165     return image_ref;
   166 }
   167 
   168 static CFDictionaryRef CreateHintDictionary(CFStringRef uti_string_hint)
   169 {
   170     CFDictionaryRef hint_dictionary = NULL;
   171 
   172     if(uti_string_hint != NULL)
   173     {
   174         // Do a bunch of work to setup a CFDictionary containing the jpeg compression properties.
   175         CFStringRef the_keys[1];
   176         CFStringRef the_values[1];
   177 
   178         the_keys[0] = kCGImageSourceTypeIdentifierHint;
   179         the_values[0] = uti_string_hint;
   180 
   181         // kCFTypeDictionaryKeyCallBacks or kCFCopyStringDictionaryKeyCallBacks?
   182         hint_dictionary = CFDictionaryCreate(NULL, (const void**)&the_keys, (const void**)&the_values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
   183     }
   184     return hint_dictionary;
   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_RGB(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, (int)w, (int)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 static SDL_Surface* Create_SDL_Surface_From_CGImage_Index(CGImageRef image_ref)
   284 {
   285     size_t w = CGImageGetWidth(image_ref);
   286     size_t h = CGImageGetHeight(image_ref);
   287     size_t bits_per_pixel = CGImageGetBitsPerPixel(image_ref);
   288     size_t bytes_per_row = CGImageGetBytesPerRow(image_ref);
   289 
   290     SDL_Surface* surface;
   291     SDL_Palette* palette;
   292     CGColorSpaceRef color_space = CGImageGetColorSpace(image_ref);
   293     CGColorSpaceRef base_color_space = CGColorSpaceGetBaseColorSpace(color_space);
   294     size_t num_components = CGColorSpaceGetNumberOfComponents(base_color_space);
   295     size_t num_entries = CGColorSpaceGetColorTableCount(color_space);
   296     uint8_t *entry, entries[num_components * num_entries];
   297 
   298     /* What do we do if it's not RGB? */
   299     if (num_components != 3) {
   300         SDL_SetError("Unknown colorspace components %lu", num_components);
   301         return NULL;
   302     }
   303     if (bits_per_pixel != 8) {
   304         SDL_SetError("Unknown bits_per_pixel %lu", bits_per_pixel);
   305         return NULL;
   306     }
   307 
   308     CGColorSpaceGetColorTable(color_space, entries);
   309     surface = SDL_CreateRGBSurface(SDL_SWSURFACE, (int)w, (int)h, bits_per_pixel, 0, 0, 0, 0);
   310     if (surface) {
   311         uint8_t* pixels = (uint8_t*)surface->pixels;
   312         CGDataProviderRef provider = CGImageGetDataProvider(image_ref);
   313         NSData* data = (id)CGDataProviderCopyData(provider);
   314         [data autorelease];
   315         const uint8_t* bytes = [data bytes];
   316         size_t i;
   317 
   318         palette = surface->format->palette;
   319         for (i = 0, entry = entries; i < num_entries; ++i) {
   320             palette->colors[i].r = entry[0];
   321             palette->colors[i].g = entry[1];
   322             palette->colors[i].b = entry[2];
   323             entry += num_components;
   324         }
   325 
   326         for (i = 0; i < h; ++i) {
   327             SDL_memcpy(pixels, bytes, w);
   328             pixels += surface->pitch;
   329             bytes += bytes_per_row;
   330         }
   331     }
   332     return surface;
   333 }
   334 static SDL_Surface* Create_SDL_Surface_From_CGImage(CGImageRef image_ref)
   335 {
   336     CGColorSpaceRef color_space = CGImageGetColorSpace(image_ref);
   337     if (CGColorSpaceGetModel(color_space) == kCGColorSpaceModelIndexed) {
   338         return Create_SDL_Surface_From_CGImage_Index(image_ref);
   339     } else {
   340         return Create_SDL_Surface_From_CGImage_RGB(image_ref);
   341     }
   342 }
   343 
   344 
   345 #pragma mark -
   346 #pragma mark IMG_Init stubs
   347 #if !defined(ALLOW_UIIMAGE_FALLBACK) && ((TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1))
   348 static int Internal_checkImageIOisAvailable() {
   349     // just check if we are running on ios 4 or more, else throw exception
   350     if ([UIImage instancesRespondToSelector:@selector(initWithCGImage:scale:orientation:)])
   351         return 0;
   352     [NSException raise:@"UIImage fallback not enabled at compile time"
   353                 format:@"ImageIO is not available on your platform, please recompile SDL_Image with ALLOW_UIIMAGE_FALLBACK."];
   354     return -1;
   355 }
   356 #endif
   357 
   358 int IMG_InitJPG()
   359 {
   360     return 0;
   361 }
   362 
   363 void IMG_QuitJPG()
   364 {
   365 }
   366 
   367 int IMG_InitPNG()
   368 {
   369     return 0;
   370 }
   371 
   372 void IMG_QuitPNG()
   373 {
   374 }
   375 
   376 int IMG_InitTIF()
   377 {
   378     return 0;
   379 }
   380 
   381 void IMG_QuitTIF()
   382 {
   383 }
   384 
   385 #pragma mark -
   386 #pragma mark Get type of image
   387 static int Internal_isType_UIImage (SDL_RWops *rw_ops, CFStringRef uti_string_to_test)
   388 {
   389     int is_type = 0;
   390 
   391 #if defined(ALLOW_UIIMAGE_FALLBACK) && ((TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1))
   392     Sint64 start = SDL_RWtell(rw_ops);
   393     if ((0 == CFStringCompare(uti_string_to_test, kUTTypeICO, 0)) ||
   394         (0 == CFStringCompare(uti_string_to_test, CFSTR("com.microsoft.cur"), 0))) {
   395 
   396         // The Win32 ICO file header (14 bytes)
   397         Uint16 bfReserved;
   398         Uint16 bfType;
   399         Uint16 bfCount;
   400         int type = (0 == CFStringCompare(uti_string_to_test, kUTTypeICO, 0)) ? 1 : 2;
   401 
   402         bfReserved = SDL_ReadLE16(rw_ops);
   403         bfType = SDL_ReadLE16(rw_ops);
   404         bfCount = SDL_ReadLE16(rw_ops);
   405         if ((bfReserved == 0) && (bfType == type) && (bfCount != 0))
   406             is_type = 1;
   407     } else if (0 == CFStringCompare(uti_string_to_test, kUTTypeBMP, 0)) {
   408         char magic[2];
   409 
   410         if ( SDL_RWread(rw_ops, magic, sizeof(magic), 1) ) {
   411             if ( strncmp(magic, "BM", 2) == 0 ) {
   412                 is_type = 1;
   413             }
   414         }
   415     } else if (0 == CFStringCompare(uti_string_to_test, kUTTypeGIF, 0)) {
   416         char magic[6];
   417 
   418         if ( SDL_RWread(rw_ops, magic, sizeof(magic), 1) ) {
   419             if ( (strncmp(magic, "GIF", 3) == 0) &&
   420                 ((memcmp(magic + 3, "87a", 3) == 0) ||
   421                  (memcmp(magic + 3, "89a", 3) == 0)) ) {
   422                     is_type = 1;
   423                 }
   424         }
   425     } else if (0 == CFStringCompare(uti_string_to_test, kUTTypeJPEG, 0)) {
   426         int in_scan = 0;
   427         Uint8 magic[4];
   428 
   429         // This detection code is by Steaphan Greene <stea@cs.binghamton.edu>
   430         // Blame me, not Sam, if this doesn't work right. */
   431         // And don't forget to report the problem to the the sdl list too! */
   432 
   433         if ( SDL_RWread(rw_ops, magic, 2, 1) ) {
   434             if ( (magic[0] == 0xFF) && (magic[1] == 0xD8) ) {
   435                 is_type = 1;
   436                 while (is_type == 1) {
   437                     if(SDL_RWread(rw_ops, magic, 1, 2) != 2) {
   438                         is_type = 0;
   439                     } else if( (magic[0] != 0xFF) && (in_scan == 0) ) {
   440                         is_type = 0;
   441                     } else if( (magic[0] != 0xFF) || (magic[1] == 0xFF) ) {
   442                         /* Extra padding in JPEG (legal) */
   443                         /* or this is data and we are scanning */
   444                         SDL_RWseek(rw_ops, -1, SEEK_CUR);
   445                     } else if(magic[1] == 0xD9) {
   446                         /* Got to end of good JPEG */
   447                         break;
   448                     } else if( (in_scan == 1) && (magic[1] == 0x00) ) {
   449                         /* This is an encoded 0xFF within the data */
   450                     } else if( (magic[1] >= 0xD0) && (magic[1] < 0xD9) ) {
   451                         /* These have nothing else */
   452                     } else if(SDL_RWread(rw_ops, magic+2, 1, 2) != 2) {
   453                         is_type = 0;
   454                     } else {
   455                         /* Yes, it's big-endian */
   456                         Uint32 start;
   457                         Uint32 size;
   458                         Uint32 end;
   459                         start = SDL_RWtell(rw_ops);
   460                         size = (magic[2] << 8) + magic[3];
   461                         end = SDL_RWseek(rw_ops, size-2, SEEK_CUR);
   462                         if ( end != start + size - 2 ) is_type = 0;
   463                         if ( magic[1] == 0xDA ) {
   464                             /* Now comes the actual JPEG meat */
   465 #ifdef  FAST_IS_JPEG
   466                             /* Ok, I'm convinced.  It is a JPEG. */
   467                             break;
   468 #else
   469                             /* I'm not convinced.  Prove it! */
   470                             in_scan = 1;
   471 #endif
   472                         }
   473                     }
   474                 }
   475             }
   476         }
   477     } else if (0 == CFStringCompare(uti_string_to_test, kUTTypePNG, 0)) {
   478         Uint8 magic[4];
   479 
   480         if ( SDL_RWread(rw_ops, magic, 1, sizeof(magic)) == sizeof(magic) ) {
   481             if ( magic[0] == 0x89 &&
   482                 magic[1] == 'P' &&
   483                 magic[2] == 'N' &&
   484                 magic[3] == 'G' ) {
   485                 is_type = 1;
   486             }
   487         }
   488     } else if (0 == CFStringCompare(uti_string_to_test, CFSTR("com.truevision.tga-image"), 0)) {
   489         //TODO: fill me!
   490     } else if (0 == CFStringCompare(uti_string_to_test, kUTTypeTIFF, 0)) {
   491         Uint8 magic[4];
   492 
   493         if ( SDL_RWread(rw_ops, magic, 1, sizeof(magic)) == sizeof(magic) ) {
   494             if ( (magic[0] == 'I' &&
   495                   magic[1] == 'I' &&
   496                   magic[2] == 0x2a &&
   497                   magic[3] == 0x00) ||
   498                 (magic[0] == 'M' &&
   499                  magic[1] == 'M' &&
   500                  magic[2] == 0x00 &&
   501                  magic[3] == 0x2a) ) {
   502                     is_type = 1;
   503                 }
   504         }
   505     }
   506 
   507     // reset the file pointer
   508     SDL_RWseek(rw_ops, start, SEEK_SET);
   509 
   510 #endif  /* #if defined(ALLOW_UIIMAGE_FALLBACK) && ((TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)) */
   511     return is_type;
   512 }
   513 
   514 static int Internal_isType_ImageIO (SDL_RWops *rw_ops, CFStringRef uti_string_to_test)
   515 {
   516     int is_type = 0;
   517 
   518     Sint64 start = SDL_RWtell(rw_ops);
   519     CFDictionaryRef hint_dictionary = CreateHintDictionary(uti_string_to_test);
   520     CGImageSourceRef image_source = CreateCGImageSourceFromRWops(rw_ops, hint_dictionary);
   521 
   522     if (hint_dictionary != NULL) {
   523         CFRelease(hint_dictionary);
   524     }
   525 
   526     if (NULL == image_source) {
   527         // reset the file pointer
   528         SDL_RWseek(rw_ops, start, SEEK_SET);
   529         return 0;
   530     }
   531 
   532     // This will get the UTI of the container, not the image itself.
   533     // Under most cases, this won't be a problem.
   534     // But if a person passes an icon file which contains a bmp,
   535     // the format will be of the icon file.
   536     // But I think the main SDL_image codebase has this same problem so I'm not going to worry about it.
   537     CFStringRef uti_type = CGImageSourceGetType(image_source);
   538     //  CFShow(uti_type);
   539 
   540     // Unsure if we really want conformance or equality
   541     is_type = (int)UTTypeConformsTo(uti_string_to_test, uti_type);
   542 
   543     CFRelease(image_source);
   544 
   545     // reset the file pointer
   546     SDL_RWseek(rw_ops, start, SEEK_SET);
   547     return is_type;
   548 }
   549 
   550 static int Internal_isType (SDL_RWops *rw_ops, CFStringRef uti_string_to_test)
   551 {
   552     if (rw_ops == NULL)
   553         return 0;
   554 
   555 #if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)
   556     if (USE_UIIMAGE_BACKEND())
   557         return Internal_isType_UIImage(rw_ops, uti_string_to_test);
   558     else
   559 #endif
   560         return Internal_isType_ImageIO(rw_ops, uti_string_to_test);
   561 }
   562 
   563 #ifdef BMP_USES_IMAGEIO
   564 
   565 int IMG_isCUR(SDL_RWops *src)
   566 {
   567     /* FIXME: Is this a supported type? */
   568     return Internal_isType(src, CFSTR("com.microsoft.cur"));
   569 }
   570 
   571 int IMG_isICO(SDL_RWops *src)
   572 {
   573     return Internal_isType(src, kUTTypeICO);
   574 }
   575 
   576 int IMG_isBMP(SDL_RWops *src)
   577 {
   578     return Internal_isType(src, kUTTypeBMP);
   579 }
   580 
   581 #endif /* BMP_USES_IMAGEIO */
   582 
   583 int IMG_isGIF(SDL_RWops *src)
   584 {
   585     return Internal_isType(src, kUTTypeGIF);
   586 }
   587 
   588 // Note: JPEG 2000 is kUTTypeJPEG2000
   589 int IMG_isJPG(SDL_RWops *src)
   590 {
   591     return Internal_isType(src, kUTTypeJPEG);
   592 }
   593 
   594 int IMG_isPNG(SDL_RWops *src)
   595 {
   596     return Internal_isType(src, kUTTypePNG);
   597 }
   598 
   599 // This isn't a public API function. Apple seems to be able to identify tga's.
   600 int IMG_isTGA(SDL_RWops *src)
   601 {
   602     return Internal_isType(src, CFSTR("com.truevision.tga-image"));
   603 }
   604 
   605 int IMG_isTIF(SDL_RWops *src)
   606 {
   607     return Internal_isType(src, kUTTypeTIFF);
   608 }
   609 
   610 #pragma mark -
   611 #pragma mark Load image engine
   612 static SDL_Surface *LoadImageFromRWops_UIImage (SDL_RWops* rw_ops, CFStringRef uti_string_hint)
   613 {
   614     SDL_Surface *sdl_surface = NULL;
   615 
   616 #if defined(ALLOW_UIIMAGE_FALLBACK) && ((TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1))
   617     NSAutoreleasePool* autorelease_pool = [[NSAutoreleasePool alloc] init];
   618     UIImage *ui_image;
   619     int bytes_read = 0;
   620     // I don't know what a good size is.
   621     // Max recommended texture size is 1024x1024 on iPhone so maybe base it on that?
   622     const int block_size = 1024*4;
   623     char temp_buffer[block_size];
   624 
   625     NSMutableData* ns_data = [[NSMutableData alloc] initWithCapacity:1024*1024*4];
   626     do {
   627         bytes_read = SDL_RWread(rw_ops, temp_buffer, 1, block_size);
   628         [ns_data appendBytes:temp_buffer length:bytes_read];
   629     } while (bytes_read > 0);
   630 
   631     ui_image = [[UIImage alloc] initWithData:ns_data];
   632     if (ui_image != nil)
   633         sdl_surface = Create_SDL_Surface_From_CGImage([ui_image CGImage]);
   634     [ui_image release];
   635     [ns_data release];
   636     [autorelease_pool drain];
   637 
   638 #endif  /* #if defined(ALLOW_UIIMAGE_FALLBACK) && ((TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)) */
   639     return sdl_surface;
   640 }
   641 
   642 static SDL_Surface *LoadImageFromRWops_ImageIO (SDL_RWops *rw_ops, CFStringRef uti_string_hint)
   643 {
   644     CFDictionaryRef hint_dictionary = CreateHintDictionary(uti_string_hint);
   645     CGImageSourceRef image_source = CreateCGImageSourceFromRWops(rw_ops, hint_dictionary);
   646 
   647     if (hint_dictionary != NULL)
   648         CFRelease(hint_dictionary);
   649 
   650     if (NULL == image_source)
   651         return NULL;
   652 
   653     CGImageRef image_ref = CreateCGImageFromCGImageSource(image_source);
   654     CFRelease(image_source);
   655 
   656     if (NULL == image_ref)
   657         return NULL;
   658     SDL_Surface *sdl_surface = Create_SDL_Surface_From_CGImage(image_ref);
   659     CFRelease(image_ref);
   660 
   661     return sdl_surface;
   662 }
   663 
   664 static SDL_Surface *LoadImageFromRWops (SDL_RWops *rw_ops, CFStringRef uti_string_hint)
   665 {
   666 #if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)
   667     if (USE_UIIMAGE_BACKEND())
   668         return LoadImageFromRWops_UIImage(rw_ops, uti_string_hint);
   669     else
   670 #endif
   671         return LoadImageFromRWops_ImageIO(rw_ops, uti_string_hint);
   672 }
   673 
   674 static SDL_Surface* LoadImageFromFile_UIImage (const char *file)
   675 {
   676     SDL_Surface *sdl_surface = NULL;
   677 
   678 #if defined(ALLOW_UIIMAGE_FALLBACK) && ((TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1))
   679     NSAutoreleasePool* autorelease_pool = [[NSAutoreleasePool alloc] init];
   680     NSString *ns_string = [[NSString alloc] initWithUTF8String:file];
   681     UIImage *ui_image = [[UIImage alloc] initWithContentsOfFile:ns_string];
   682     if (ui_image != nil)
   683         sdl_surface = Create_SDL_Surface_From_CGImage([ui_image CGImage]);
   684     [ui_image release];
   685     [ns_string release];
   686     [autorelease_pool drain];
   687 
   688 #endif  /* #if defined(ALLOW_UIIMAGE_FALLBACK) && ((TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)) */
   689     return sdl_surface;
   690 }
   691 
   692 static SDL_Surface* LoadImageFromFile_ImageIO (const char *file)
   693 {
   694     CGImageSourceRef image_source = NULL;
   695 
   696     image_source = CreateCGImageSourceFromFile(file);
   697 
   698     if(NULL == image_source)
   699         return NULL;
   700 
   701     CGImageRef image_ref = CreateCGImageFromCGImageSource(image_source);
   702     CFRelease(image_source);
   703 
   704     if (NULL == image_ref)
   705         return NULL;
   706     SDL_Surface *sdl_surface = Create_SDL_Surface_From_CGImage(image_ref);
   707     CFRelease(image_ref);
   708     return sdl_surface;
   709 }
   710 
   711 static SDL_Surface* LoadImageFromFile (const char *file)
   712 {
   713 #if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)
   714     if (USE_UIIMAGE_BACKEND())
   715         return LoadImageFromFile_UIImage(file);
   716     else
   717 #endif
   718         return LoadImageFromFile_ImageIO(file);
   719 }
   720 
   721 #ifdef BMP_USES_IMAGEIO
   722 
   723 SDL_Surface* IMG_LoadCUR_RW (SDL_RWops *src)
   724 {
   725     /* FIXME: Is this a supported type? */
   726     return LoadImageFromRWops(src, CFSTR("com.microsoft.cur"));
   727 }
   728 
   729 SDL_Surface* IMG_LoadICO_RW (SDL_RWops *src)
   730 {
   731     return LoadImageFromRWops(src, kUTTypeICO);
   732 }
   733 
   734 SDL_Surface* IMG_LoadBMP_RW (SDL_RWops *src)
   735 {
   736     return LoadImageFromRWops(src, kUTTypeBMP);
   737 }
   738 
   739 #endif /* BMP_USES_IMAGEIO */
   740 
   741 SDL_Surface* IMG_LoadGIF_RW (SDL_RWops *src)
   742 {
   743     return LoadImageFromRWops (src, kUTTypeGIF);
   744 }
   745 
   746 SDL_Surface* IMG_LoadJPG_RW (SDL_RWops *src)
   747 {
   748     return LoadImageFromRWops (src, kUTTypeJPEG);
   749 }
   750 
   751 SDL_Surface* IMG_LoadPNG_RW (SDL_RWops *src)
   752 {
   753     return LoadImageFromRWops (src, kUTTypePNG);
   754 }
   755 
   756 SDL_Surface* IMG_LoadTGA_RW (SDL_RWops *src)
   757 {
   758     return LoadImageFromRWops(src, CFSTR("com.truevision.tga-image"));
   759 }
   760 
   761 SDL_Surface* IMG_LoadTIF_RW (SDL_RWops *src)
   762 {
   763     return LoadImageFromRWops(src, kUTTypeTIFF);
   764 }
   765 
   766 // Since UIImage doesn't really support streams well, we should optimize for the file case.
   767 // Apple provides both stream and file loading functions in ImageIO.
   768 // Potentially, Apple can optimize for either case.
   769 SDL_Surface* IMG_Load (const char *file)
   770 {
   771     SDL_Surface* sdl_surface = NULL;
   772 
   773     sdl_surface = LoadImageFromFile(file);
   774     if(NULL == sdl_surface)
   775     {
   776         // Either the file doesn't exist or ImageIO doesn't understand the format.
   777         // For the latter case, fallback to the native SDL_image handlers.
   778         SDL_RWops *src = SDL_RWFromFile(file, "rb");
   779         char *ext = strrchr(file, '.');
   780         if (ext) {
   781             ext++;
   782         }
   783         if (!src) {
   784             /* The error message has been set in SDL_RWFromFile */
   785             return NULL;
   786         }
   787         sdl_surface = IMG_LoadTyped_RW(src, 1, ext);
   788     }
   789     return sdl_surface;
   790 }
   791 
   792 #endif /* defined(__APPLE__) && !defined(SDL_IMAGE_USE_COMMON_BACKEND) */