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). 39static 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. 46static 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 52static 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 58off_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 66static 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. 78static 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. */ 118static 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 149static 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 168static 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 188static 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} 283static 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} 334static 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)) 348static 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 358int IMG_InitJPG() 359{ 360 return 0; 361} 362 363void IMG_QuitJPG() 364{ 365} 366 367int IMG_InitPNG() 368{ 369 return 0; 370} 371 372void IMG_QuitPNG() 373{ 374} 375 376int IMG_InitTIF() 377{ 378 return 0; 379} 380 381void IMG_QuitTIF() 382{ 383} 384 385#pragma mark - 386#pragma mark Get type of image 387static 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 514static 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 550static 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 565int IMG_isCUR(SDL_RWops *src) 566{ 567 /* FIXME: Is this a supported type? */ 568 return Internal_isType(src, CFSTR("com.microsoft.cur")); 569} 570 571int IMG_isICO(SDL_RWops *src) 572{ 573 return Internal_isType(src, kUTTypeICO); 574} 575 576int IMG_isBMP(SDL_RWops *src) 577{ 578 return Internal_isType(src, kUTTypeBMP); 579} 580 581#endif /* BMP_USES_IMAGEIO */ 582 583int IMG_isGIF(SDL_RWops *src) 584{ 585 return Internal_isType(src, kUTTypeGIF); 586} 587 588// Note: JPEG 2000 is kUTTypeJPEG2000 589int IMG_isJPG(SDL_RWops *src) 590{ 591 return Internal_isType(src, kUTTypeJPEG); 592} 593 594int 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. 600int IMG_isTGA(SDL_RWops *src) 601{ 602 return Internal_isType(src, CFSTR("com.truevision.tga-image")); 603} 604 605int IMG_isTIF(SDL_RWops *src) 606{ 607 return Internal_isType(src, kUTTypeTIFF); 608} 609 610#pragma mark - 611#pragma mark Load image engine 612static 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 642static 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 664static 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 674static 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 692static 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 711static 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 723SDL_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 729SDL_Surface* IMG_LoadICO_RW (SDL_RWops *src) 730{ 731 return LoadImageFromRWops(src, kUTTypeICO); 732} 733 734SDL_Surface* IMG_LoadBMP_RW (SDL_RWops *src) 735{ 736 return LoadImageFromRWops(src, kUTTypeBMP); 737} 738 739#endif /* BMP_USES_IMAGEIO */ 740 741SDL_Surface* IMG_LoadGIF_RW (SDL_RWops *src) 742{ 743 return LoadImageFromRWops (src, kUTTypeGIF); 744} 745 746SDL_Surface* IMG_LoadJPG_RW (SDL_RWops *src) 747{ 748 return LoadImageFromRWops (src, kUTTypeJPEG); 749} 750 751SDL_Surface* IMG_LoadPNG_RW (SDL_RWops *src) 752{ 753 return LoadImageFromRWops (src, kUTTypePNG); 754} 755 756SDL_Surface* IMG_LoadTGA_RW (SDL_RWops *src) 757{ 758 return LoadImageFromRWops(src, CFSTR("com.truevision.tga-image")); 759} 760 761SDL_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. 769SDL_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) */ 793