IMG_ImageIO.m
author Ozkan Sezer <sezeroz@gmail.com>
Tue, 16 Oct 2018 20:02:02 +0300
branchSDL-1.2
changeset 607 1a1189c2978f
parent 308 ea460af4ceff
permissions -rw-r--r--
backports of multiple bug fixes from 2.0:

3999783e (r340): bug 1413 - Fix image corruption when using ImageIO framework
5bf0f0d6 (r343), 326a6025 (r361): fixes from VS code analysis and code review
2742fe58 (r355), 1e7a55d7 (r356). dd40be56 (r358): support for webp on big endian systems
ce8091ca (r365): bug 1801 - typo in the xcf decoder, condition is always false
35beff02 (r369): bug 1831 - Memory leak issue in SDL_image-1.2.12/IMG_xpm.c file
1700d607 (r415): bug 1991 - XCF and LBM image loading [only the memory leak parts.]
e108e122 (r419): bug 2010 - Memory leaks in do_layer_surface function in IMG_xcf.c
7a360f7d (r436): bug 2295 - Memory leak in IMG_LoadWEBP_RW
ee17b8eb (r443): bug 2454 - Crash when loading some XPM files
bca82f1c (r476): proper fix for Bugzilla #2965.
fd721465 (r490): crash if some initialization succeeded and some didn't
915de300 (r492): bug 3474 - IMG_tif leaks memory on errors
b6f8fbe5 (r493): bug 3475 - Remove unnecessary loop from IMG_tif.c
d3e819a0 (r499): bug 2318 - h->cm_map resource getting leak in read_xcf_header function
1e32e1f4 (r503): bug 3008 - Compiler warnings: "warning: initialization discards 'const'
318484db (r513): security vulnerability in XCF image loader
181ef57f (r530): failing to reset the file pointer when detecting file types with ImageIO
16772bbb (r555): lbm: use correct variable to check color planes.
97f7f01e (r556): lbm: Fail to load images with unsupported/bogus color depth.
bfa08dc0 (r557): lbm: Don't overflow static colormap buffer.
a1e9b624 (r558): ico: reject obviously incorrect image sizes.
37445f61 (r559): bmp: don't overflow palette buffer with bogus biClrUsed values.
7df1580f (r560): xcf: deal with bogus data in rle tile decoding.
45e750f9 (r563): gif: report error on bogus LWZ data, instead of overflowing a buffer.
2938fc80 (r567): pcx: don't overflow buffer if bytes-per-line is less than image width.
c5f9cbb5 (r568): xcf: Prevent infinite loop and/or buffer overflow on bogus data.
fb643e37 (r569): xcf: check for some potential integer overflows.
170d7d32 (r585): potential buffer overflow on corrupt or maliciously-crafted XCF file.
19beb4a1 (r586): Don't get into infinite loops on truncated GIF files.
32a18ca0 (r587): Don't get into infinite loops on truncated PNM files.
8b4ee1d7 (r590): memory leak in IMG_xcf.c
90a531f2 (r591): PNM: Improve checks when loading a file
31263a04 (r592): XCF: check if there's sufficient data in the stream before allocating
cec9b759 (r593): More error checking, and null terminate strings...
     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 
   215 	/* This sets up a color space that results in identical values
   216 	 * as the image data itself, which is the same as the standalone
   217 	 * libpng loader.
   218 	 * Thanks to Allegro. :)
   219 	 */
   220 	CGFloat whitePoint[3] = { 0.950, 1.000, 1.089 };
   221 	CGFloat blackPoint[3] = { 0.000, 0.000, 0.000 };
   222 	CGFloat gamma[3] = { 2.2, 2.2, 2.2 };
   223 	CGFloat matrix[9] = {
   224 		0.412, 0.213, 0.019,
   225 		0.358, 0.715, 0.119,
   226 		0.180, 0.072, 0.950
   227 	};
   228 	CGColorSpaceRef color_space =
   229 		CGColorSpaceCreateCalibratedRGB(
   230 									whitePoint, blackPoint, gamma, matrix
   231 									);   
   232 	
   233     if (alpha == kCGImageAlphaNone ||
   234         alpha == kCGImageAlphaNoneSkipFirst ||
   235         alpha == kCGImageAlphaNoneSkipLast) {
   236         bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host; /* XRGB */
   237         Amask = 0x00000000;
   238     } else {
   239         /* kCGImageAlphaFirst isn't supported */
   240         //bitmap_info = kCGImageAlphaFirst | kCGBitmapByteOrder32Host; /* ARGB */
   241         bitmap_info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host; /* ARGB */
   242         Amask = 0xFF000000;
   243     }
   244     
   245     Rmask = 0x00FF0000;
   246     Gmask = 0x0000FF00;
   247     Bmask = 0x000000FF;
   248     
   249     surface = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, Rmask, Gmask, Bmask, Amask);
   250     if (surface)
   251     {
   252         // Sets up a context to be drawn to with surface->pixels as the area to be drawn to
   253         bitmap_context = CGBitmapContextCreate(
   254                                                surface->pixels,
   255                                                surface->w,
   256                                                surface->h,
   257                                                bits_per_component,
   258                                                surface->pitch,
   259                                                color_space,
   260                                                bitmap_info
   261                                                );
   262         
   263         // Draws the image into the context's image_data
   264         CGContextDrawImage(bitmap_context, rect, image_ref);
   265         
   266         CGContextRelease(bitmap_context);
   267         
   268         // FIXME: Reverse the premultiplied alpha
   269         if ((bitmap_info & kCGBitmapAlphaInfoMask) == kCGImageAlphaPremultipliedFirst) {
   270             int i, j;
   271             Uint8 *p = (Uint8 *)surface->pixels;
   272             for (i = surface->h * surface->pitch/4; i--; ) {
   273 #if __LITTLE_ENDIAN__
   274                 Uint8 A = p[3];
   275                 if (A) {
   276                     for (j = 0; j < 3; ++j) {
   277                         p[j] = (p[j] * 255) / A;
   278                     }
   279                 }
   280 #else
   281                 Uint8 A = p[0];
   282                 if (A) {
   283                     for (j = 1; j < 4; ++j) {
   284                         p[j] = (p[j] * 255) / A;
   285                     }
   286                 }
   287 #endif /* ENDIAN */
   288                 p += 4;
   289             }
   290         }
   291     }
   292     
   293     if (color_space)
   294     {
   295         CGColorSpaceRelease(color_space);			
   296     }
   297     
   298     return surface;
   299 }
   300 static SDL_Surface* Create_SDL_Surface_From_CGImage_Index(CGImageRef image_ref)
   301 {
   302     size_t w = CGImageGetWidth(image_ref);
   303     size_t h = CGImageGetHeight(image_ref);
   304     size_t bits_per_pixel = CGImageGetBitsPerPixel(image_ref);
   305     size_t bytes_per_row = CGImageGetBytesPerRow(image_ref);
   306 
   307     SDL_Surface* surface;
   308     SDL_Palette* palette;
   309 	CGColorSpaceRef color_space = CGImageGetColorSpace(image_ref);
   310     CGColorSpaceRef base_color_space = CGColorSpaceGetBaseColorSpace(color_space);
   311     size_t num_components = CGColorSpaceGetNumberOfComponents(base_color_space);
   312     size_t num_entries = CGColorSpaceGetColorTableCount(color_space);
   313     uint8_t *entry, entries[num_components * num_entries];
   314 
   315     /* What do we do if it's not RGB? */
   316     if (num_components != 3) {
   317         SDL_SetError("Unknown colorspace components %lu", num_components);
   318         return NULL;
   319     }
   320     if (bits_per_pixel != 8) {
   321         SDL_SetError("Unknown bits_per_pixel %lu", bits_per_pixel);
   322         return NULL;
   323     }
   324 
   325     CGColorSpaceGetColorTable(color_space, entries);
   326     surface = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, bits_per_pixel, 0, 0, 0, 0);
   327     if (surface) {
   328         uint8_t* pixels = (uint8_t*)surface->pixels;
   329         CGDataProviderRef provider = CGImageGetDataProvider(image_ref);
   330         NSData* data = (id)CGDataProviderCopyData(provider);
   331         [data autorelease];
   332         const uint8_t* bytes = [data bytes];
   333         size_t i;
   334 
   335         palette = surface->format->palette;
   336         for (i = 0, entry = entries; i < num_entries; ++i) {
   337             palette->colors[i].r = entry[0];
   338             palette->colors[i].g = entry[1];
   339             palette->colors[i].b = entry[2];
   340             entry += num_components;
   341         }
   342 
   343         for (i = 0; i < h; ++i) {
   344             SDL_memcpy(pixels, bytes, w);
   345             pixels += surface->pitch;
   346             bytes += bytes_per_row;
   347         }
   348     }
   349     return surface;
   350 }
   351 static SDL_Surface* Create_SDL_Surface_From_CGImage(CGImageRef image_ref)
   352 {
   353 	CGColorSpaceRef color_space = CGImageGetColorSpace(image_ref);
   354     if (CGColorSpaceGetModel(color_space) == kCGColorSpaceModelIndexed) {
   355         return Create_SDL_Surface_From_CGImage_Index(image_ref);
   356     } else {
   357         return Create_SDL_Surface_From_CGImage_RGB(image_ref);
   358     }
   359 }
   360 
   361 
   362 #pragma mark -
   363 #pragma mark IMG_Init stubs
   364 #if !defined(ALLOW_UIIMAGE_FALLBACK) && ((TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1))
   365 static int Internal_checkImageIOisAvailable() {
   366     // just check if we are running on ios 4 or more, else throw exception
   367     if ([UIImage instancesRespondToSelector:@selector(initWithCGImage:scale:orientation:)])
   368         return 0;
   369     [NSException raise:@"UIImage fallback not enabled at compile time"
   370                 format:@"ImageIO is not available on your platform, please recompile SDL_Image with ALLOW_UIIMAGE_FALLBACK."];
   371     return -1;
   372 }
   373 #endif
   374 
   375 int IMG_InitJPG()
   376 {
   377     return 0;
   378 }
   379 
   380 void IMG_QuitJPG()
   381 {
   382 }
   383 
   384 int IMG_InitPNG()
   385 {
   386     return 0;
   387 }
   388 
   389 void IMG_QuitPNG()
   390 {
   391 }
   392 
   393 int IMG_InitTIF()
   394 {
   395     return 0;
   396 }
   397 
   398 void IMG_QuitTIF()
   399 {
   400 }
   401 
   402 #pragma mark -
   403 #pragma mark Get type of image
   404 static int Internal_isType_UIImage (SDL_RWops *rw_ops, CFStringRef uti_string_to_test)
   405 {
   406     int is_type = 0;
   407     
   408 #if defined(ALLOW_UIIMAGE_FALLBACK) && ((TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1))
   409     int start = SDL_RWtell(rw_ops);
   410     if ((0 == CFStringCompare(uti_string_to_test, kUTTypeICO, 0)) ||
   411         (0 == CFStringCompare(uti_string_to_test, CFSTR("com.microsoft.cur"), 0))) {
   412         
   413         // The Win32 ICO file header (14 bytes)
   414         Uint16 bfReserved;
   415         Uint16 bfType;
   416         Uint16 bfCount;
   417         int type = (0 == CFStringCompare(uti_string_to_test, kUTTypeICO, 0)) ? 1 : 2;
   418         
   419         bfReserved = SDL_ReadLE16(rw_ops);
   420         bfType = SDL_ReadLE16(rw_ops);
   421         bfCount = SDL_ReadLE16(rw_ops);
   422         if ((bfReserved == 0) && (bfType == type) && (bfCount != 0)) 
   423             is_type = 1;
   424     } else if (0 == CFStringCompare(uti_string_to_test, kUTTypeBMP, 0)) {
   425         char magic[2];
   426         
   427         if ( SDL_RWread(rw_ops, magic, sizeof(magic), 1) ) {
   428             if ( strncmp(magic, "BM", 2) == 0 ) {
   429                 is_type = 1;
   430             }
   431         }
   432     } else if (0 == CFStringCompare(uti_string_to_test, kUTTypeGIF, 0)) {
   433         char magic[6];
   434         
   435         if ( SDL_RWread(rw_ops, magic, sizeof(magic), 1) ) {
   436             if ( (strncmp(magic, "GIF", 3) == 0) &&
   437                 ((memcmp(magic + 3, "87a", 3) == 0) ||
   438                  (memcmp(magic + 3, "89a", 3) == 0)) ) {
   439                     is_type = 1;
   440                 }
   441         }
   442     } else if (0 == CFStringCompare(uti_string_to_test, kUTTypeJPEG, 0)) {
   443         int in_scan = 0;
   444         Uint8 magic[4];
   445         
   446         // This detection code is by Steaphan Greene <stea@cs.binghamton.edu>
   447         // Blame me, not Sam, if this doesn't work right. */
   448         // And don't forget to report the problem to the the sdl list too! */
   449         
   450         if ( SDL_RWread(rw_ops, magic, 2, 1) ) {
   451             if ( (magic[0] == 0xFF) && (magic[1] == 0xD8) ) {
   452                 is_type = 1;
   453                 while (is_type == 1) {
   454                     if(SDL_RWread(rw_ops, magic, 1, 2) != 2) {
   455                         is_type = 0;
   456                     } else if( (magic[0] != 0xFF) && (in_scan == 0) ) {
   457                         is_type = 0;
   458                     } else if( (magic[0] != 0xFF) || (magic[1] == 0xFF) ) {
   459                         /* Extra padding in JPEG (legal) */
   460                         /* or this is data and we are scanning */
   461                         SDL_RWseek(rw_ops, -1, SEEK_CUR);
   462                     } else if(magic[1] == 0xD9) {
   463                         /* Got to end of good JPEG */
   464                         break;
   465                     } else if( (in_scan == 1) && (magic[1] == 0x00) ) {
   466                         /* This is an encoded 0xFF within the data */
   467                     } else if( (magic[1] >= 0xD0) && (magic[1] < 0xD9) ) {
   468                         /* These have nothing else */
   469                     } else if(SDL_RWread(rw_ops, magic+2, 1, 2) != 2) {
   470                         is_type = 0;
   471                     } else {
   472                         /* Yes, it's big-endian */
   473                         Uint32 start;
   474                         Uint32 size;
   475                         Uint32 end;
   476                         start = SDL_RWtell(rw_ops);
   477                         size = (magic[2] << 8) + magic[3];
   478                         end = SDL_RWseek(rw_ops, size-2, SEEK_CUR);
   479                         if ( end != start + size - 2 ) is_type = 0;
   480                         if ( magic[1] == 0xDA ) {
   481                             /* Now comes the actual JPEG meat */
   482 #ifdef	FAST_IS_JPEG
   483                             /* Ok, I'm convinced.  It is a JPEG. */
   484                             break;
   485 #else
   486                             /* I'm not convinced.  Prove it! */
   487                             in_scan = 1;
   488 #endif
   489                         }
   490                     }
   491                 }
   492             }
   493         }
   494     } else if (0 == CFStringCompare(uti_string_to_test, kUTTypePNG, 0)) {
   495         Uint8 magic[4];
   496         
   497         if ( SDL_RWread(rw_ops, magic, 1, sizeof(magic)) == sizeof(magic) ) {
   498             if ( magic[0] == 0x89 &&
   499                 magic[1] == 'P' &&
   500                 magic[2] == 'N' &&
   501                 magic[3] == 'G' ) {
   502                 is_type = 1;
   503             }
   504         }
   505     } else if (0 == CFStringCompare(uti_string_to_test, CFSTR("com.truevision.tga-image"), 0)) {
   506         //TODO: fill me!
   507     } else if (0 == CFStringCompare(uti_string_to_test, kUTTypeTIFF, 0)) {
   508         Uint8 magic[4];
   509         
   510         if ( SDL_RWread(rw_ops, magic, 1, sizeof(magic)) == sizeof(magic) ) {
   511             if ( (magic[0] == 'I' &&
   512                   magic[1] == 'I' &&
   513                   magic[2] == 0x2a &&
   514                   magic[3] == 0x00) ||
   515                 (magic[0] == 'M' &&
   516                  magic[1] == 'M' &&
   517                  magic[2] == 0x00 &&
   518                  magic[3] == 0x2a) ) {
   519                     is_type = 1;
   520                 }
   521         }
   522     }
   523     
   524     // reset the file pointer
   525     SDL_RWseek(rw_ops, start, SEEK_SET);
   526 
   527 #endif  /* #if defined(ALLOW_UIIMAGE_FALLBACK) && ((TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)) */
   528     return is_type;
   529 }
   530 
   531 static int Internal_isType_ImageIO (SDL_RWops *rw_ops, CFStringRef uti_string_to_test)
   532 {
   533     int is_type = 0;
   534     
   535     Sint32 start = SDL_RWtell(rw_ops);
   536     CFDictionaryRef hint_dictionary = CreateHintDictionary(uti_string_to_test);	
   537     CGImageSourceRef image_source = CreateCGImageSourceFromRWops(rw_ops, hint_dictionary);
   538     
   539     if (hint_dictionary != NULL) {
   540         CFRelease(hint_dictionary);		
   541     }
   542     
   543     if (NULL == image_source) {
   544         // reset the file pointer
   545         SDL_RWseek(rw_ops, start, SEEK_SET);
   546         return 0;
   547     }
   548     
   549     // This will get the UTI of the container, not the image itself.
   550     // Under most cases, this won't be a problem.
   551     // But if a person passes an icon file which contains a bmp,
   552     // the format will be of the icon file.
   553     // But I think the main SDL_image codebase has this same problem so I'm not going to worry about it.	
   554     CFStringRef uti_type = CGImageSourceGetType(image_source);
   555     //	CFShow(uti_type);
   556     
   557     // Unsure if we really want conformance or equality
   558     is_type = (int)UTTypeConformsTo(uti_string_to_test, uti_type);
   559     
   560     CFRelease(image_source);
   561 
   562     // reset the file pointer
   563     SDL_RWseek(rw_ops, start, SEEK_SET);
   564     return is_type;
   565 }
   566 
   567 static int Internal_isType (SDL_RWops *rw_ops, CFStringRef uti_string_to_test)
   568 {
   569     if (rw_ops == NULL)
   570         return 0;
   571     
   572 #if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)
   573     if (USE_UIIMAGE_BACKEND())
   574         return Internal_isType_UIImage(rw_ops, uti_string_to_test);
   575     else
   576 #endif
   577         return Internal_isType_ImageIO(rw_ops, uti_string_to_test);
   578 }
   579 
   580 int IMG_isCUR(SDL_RWops *src)
   581 {
   582     /* FIXME: Is this a supported type? */
   583     return Internal_isType(src, CFSTR("com.microsoft.cur"));
   584 }
   585 
   586 int IMG_isICO(SDL_RWops *src)
   587 {
   588     return Internal_isType(src, kUTTypeICO);
   589 }
   590 
   591 int IMG_isBMP(SDL_RWops *src)
   592 {
   593     return Internal_isType(src, kUTTypeBMP);
   594 }
   595 
   596 int IMG_isGIF(SDL_RWops *src)
   597 {
   598     return Internal_isType(src, kUTTypeGIF);
   599 }
   600 
   601 // Note: JPEG 2000 is kUTTypeJPEG2000
   602 int IMG_isJPG(SDL_RWops *src)
   603 {
   604     return Internal_isType(src, kUTTypeJPEG);
   605 }
   606 
   607 int IMG_isPNG(SDL_RWops *src)
   608 {
   609     return Internal_isType(src, kUTTypePNG);
   610 }
   611 
   612 // This isn't a public API function. Apple seems to be able to identify tga's.
   613 int IMG_isTGA(SDL_RWops *src)
   614 {
   615     return Internal_isType(src, CFSTR("com.truevision.tga-image"));
   616 }
   617 
   618 int IMG_isTIF(SDL_RWops *src)
   619 {
   620     return Internal_isType(src, kUTTypeTIFF);
   621 }
   622 
   623 #pragma mark -
   624 #pragma mark Load image engine
   625 static SDL_Surface *LoadImageFromRWops_UIImage (SDL_RWops* rw_ops, CFStringRef uti_string_hint)
   626 {
   627     SDL_Surface *sdl_surface = NULL;
   628 
   629 #if defined(ALLOW_UIIMAGE_FALLBACK) && ((TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1))
   630     NSAutoreleasePool* autorelease_pool = [[NSAutoreleasePool alloc] init];
   631     UIImage *ui_image;
   632     int bytes_read = 0;
   633     // I don't know what a good size is. 
   634     // Max recommended texture size is 1024x1024 on iPhone so maybe base it on that?
   635     const int block_size = 1024*4;
   636     char temp_buffer[block_size];
   637     
   638     NSMutableData* ns_data = [[NSMutableData alloc] initWithCapacity:1024*1024*4];
   639     do {
   640         bytes_read = SDL_RWread(rw_ops, temp_buffer, 1, block_size);
   641         [ns_data appendBytes:temp_buffer length:bytes_read];
   642     } while (bytes_read > 0);
   643     
   644     ui_image = [[UIImage alloc] initWithData:ns_data];
   645     if (ui_image != nil)
   646         sdl_surface = Create_SDL_Surface_From_CGImage([ui_image CGImage]);
   647     [ui_image release];
   648     [ns_data release];          
   649     [autorelease_pool drain];
   650 
   651 #endif  /* #if defined(ALLOW_UIIMAGE_FALLBACK) && ((TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)) */
   652     return sdl_surface;
   653 }
   654 
   655 static SDL_Surface *LoadImageFromRWops_ImageIO (SDL_RWops *rw_ops, CFStringRef uti_string_hint)
   656 {
   657     CFDictionaryRef hint_dictionary = CreateHintDictionary(uti_string_hint);
   658     CGImageSourceRef image_source = CreateCGImageSourceFromRWops(rw_ops, hint_dictionary);
   659 
   660     if (hint_dictionary != NULL)
   661         CFRelease(hint_dictionary);		
   662 
   663     if (NULL == image_source)
   664         return NULL;
   665     
   666     CGImageRef image_ref = CreateCGImageFromCGImageSource(image_source);
   667     CFRelease(image_source);
   668 
   669     if (NULL == image_ref)
   670         return NULL;
   671     SDL_Surface *sdl_surface = Create_SDL_Surface_From_CGImage(image_ref);
   672     CFRelease(image_ref);
   673 
   674     return sdl_surface;
   675 }
   676 
   677 static SDL_Surface *LoadImageFromRWops (SDL_RWops *rw_ops, CFStringRef uti_string_hint)
   678 {
   679 #if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)
   680     if (USE_UIIMAGE_BACKEND()) 
   681         return LoadImageFromRWops_UIImage(rw_ops, uti_string_hint);
   682     else
   683 #endif
   684         return LoadImageFromRWops_ImageIO(rw_ops, uti_string_hint);
   685 }
   686 
   687 static SDL_Surface* LoadImageFromFile_UIImage (const char *file)
   688 {
   689     SDL_Surface *sdl_surface = NULL;
   690 
   691 #if defined(ALLOW_UIIMAGE_FALLBACK) && ((TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1))
   692     NSAutoreleasePool* autorelease_pool = [[NSAutoreleasePool alloc] init];
   693     NSString *ns_string = [[NSString alloc] initWithUTF8String:file];
   694     UIImage *ui_image = [[UIImage alloc] initWithContentsOfFile:ns_string];
   695     if (ui_image != nil)
   696         sdl_surface = Create_SDL_Surface_From_CGImage([ui_image CGImage]);
   697     [ui_image release];
   698     [ns_string release];
   699     [autorelease_pool drain];
   700 
   701 #endif  /* #if defined(ALLOW_UIIMAGE_FALLBACK) && ((TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)) */
   702     return sdl_surface;	
   703 }
   704 
   705 static SDL_Surface* LoadImageFromFile_ImageIO (const char *file)
   706 {
   707     CGImageSourceRef image_source = NULL;
   708 
   709     image_source = CreateCGImageSourceFromFile(file);
   710 
   711     if(NULL == image_source)
   712         return NULL;
   713     
   714     CGImageRef image_ref = CreateCGImageFromCGImageSource(image_source);
   715     CFRelease(image_source);
   716 
   717     if (NULL == image_ref)
   718         return NULL;
   719     SDL_Surface *sdl_surface = Create_SDL_Surface_From_CGImage(image_ref);
   720     CFRelease(image_ref);
   721     return sdl_surface;	
   722 }
   723 
   724 static SDL_Surface* LoadImageFromFile (const char *file)
   725 {
   726 #if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)
   727     if (USE_UIIMAGE_BACKEND())
   728         return LoadImageFromFile_UIImage(file);
   729     else
   730 #endif
   731         return LoadImageFromFile_ImageIO(file);
   732 }
   733 
   734 SDL_Surface* IMG_LoadCUR_RW (SDL_RWops *src)
   735 {
   736     /* FIXME: Is this a supported type? */
   737     return LoadImageFromRWops(src, CFSTR("com.microsoft.cur"));
   738 }
   739 
   740 SDL_Surface* IMG_LoadICO_RW (SDL_RWops *src)
   741 {
   742     return LoadImageFromRWops(src, kUTTypeICO);
   743 }
   744 
   745 SDL_Surface* IMG_LoadBMP_RW (SDL_RWops *src)
   746 {
   747     return LoadImageFromRWops(src, kUTTypeBMP);
   748 }
   749 
   750 SDL_Surface* IMG_LoadGIF_RW (SDL_RWops *src)
   751 {
   752     return LoadImageFromRWops (src, kUTTypeGIF);
   753 }
   754 
   755 SDL_Surface* IMG_LoadJPG_RW (SDL_RWops *src)
   756 {
   757     return LoadImageFromRWops (src, kUTTypeJPEG);
   758 }
   759 
   760 SDL_Surface* IMG_LoadPNG_RW (SDL_RWops *src)
   761 {
   762     return LoadImageFromRWops (src, kUTTypePNG);
   763 }
   764 
   765 SDL_Surface* IMG_LoadTGA_RW (SDL_RWops *src)
   766 {
   767     return LoadImageFromRWops(src, CFSTR("com.truevision.tga-image"));
   768 }
   769 
   770 SDL_Surface* IMG_LoadTIF_RW (SDL_RWops *src)
   771 {
   772     return LoadImageFromRWops(src, kUTTypeTIFF);
   773 }
   774 
   775 // Since UIImage doesn't really support streams well, we should optimize for the file case.
   776 // Apple provides both stream and file loading functions in ImageIO.
   777 // Potentially, Apple can optimize for either case.
   778 SDL_Surface* IMG_Load (const char *file)
   779 {
   780     SDL_Surface* sdl_surface = NULL;
   781     
   782     sdl_surface = LoadImageFromFile(file);
   783     if(NULL == sdl_surface)
   784     {
   785         // Either the file doesn't exist or ImageIO doesn't understand the format.
   786         // For the latter case, fallback to the native SDL_image handlers.
   787         SDL_RWops *src = SDL_RWFromFile(file, "rb");
   788         char *ext = strrchr(file, '.');
   789         if (ext) {
   790             ext++;
   791         }
   792         if (!src) {
   793             /* The error message has been set in SDL_RWFromFile */
   794             return NULL;
   795         }
   796         sdl_surface = IMG_LoadTyped_RW(src, 1, ext);
   797     }
   798     return sdl_surface;
   799 }
   800 
   801 #endif /* defined(__APPLE__) && !defined(SDL_IMAGE_USE_COMMON_BACKEND) */