1/* 2 * IMG_ImageIO.c 3 * SDL_image 4 * 5 * Created by Eric Wing on 1/2/09. 6 * Copyright 2009 __MyCompanyName__. All rights reserved. 7 * 8 */ 9#include "SDL_image.h" 10#import <UIKit/UIKit.h> 11#import <MobileCoreServices/MobileCoreServices.h> // for UTCoreTypes.h 12 13// Once we have our image, we need to get it into an SDL_Surface 14// (Copied straight from the ImageIO backend.) 15static SDL_Surface* Create_SDL_Surface_From_CGImage(CGImageRef image_ref) 16{ 17 /* This code is adapted from Apple's Documentation found here: 18 * http://developer.apple.com/documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/index.html 19 * Listing 9-4††Using a Quartz image as a texture source. 20 * Unfortunately, this guide doesn't show what to do about 21 * non-RGBA image formats so I'm making the rest up. 22 * All this code should be scrutinized. 23 */ 24 25 size_t w = CGImageGetWidth(image_ref); 26 size_t h = CGImageGetHeight(image_ref); 27 CGRect rect = {{0, 0}, {w, h}}; 28 29 CGImageAlphaInfo alpha = CGImageGetAlphaInfo(image_ref); 30 //size_t bits_per_pixel = CGImageGetBitsPerPixel(image_ref); 31 size_t bits_per_component = 8; 32 33 SDL_Surface* surface; 34 Uint32 Amask; 35 Uint32 Rmask; 36 Uint32 Gmask; 37 Uint32 Bmask; 38 39 CGContextRef bitmap_context; 40 CGBitmapInfo bitmap_info; 41 CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB(); 42 43 if (alpha == kCGImageAlphaNone || 44 alpha == kCGImageAlphaNoneSkipFirst || 45 alpha == kCGImageAlphaNoneSkipLast) { 46 bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host; /* XRGB */ 47 Amask = 0x00000000; 48 } else { 49 /* kCGImageAlphaFirst isn't supported */ 50 //bitmap_info = kCGImageAlphaFirst | kCGBitmapByteOrder32Host; /* ARGB */ 51 bitmap_info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host; /* ARGB */ 52 Amask = 0xFF000000; 53 } 54 55 Rmask = 0x00FF0000; 56 Gmask = 0x0000FF00; 57 Bmask = 0x000000FF; 58 59 surface = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, Rmask, Gmask, Bmask, Amask); 60 if (surface) 61 { 62 // Sets up a context to be drawn to with surface->pixels as the area to be drawn to 63 bitmap_context = CGBitmapContextCreate( 64 surface->pixels, 65 surface->w, 66 surface->h, 67 bits_per_component, 68 surface->pitch, 69 color_space, 70 bitmap_info 71 ); 72 73 // Draws the image into the context's image_data 74 CGContextDrawImage(bitmap_context, rect, image_ref); 75 76 CGContextRelease(bitmap_context); 77 78 // FIXME: Reverse the premultiplied alpha 79 if ((bitmap_info & kCGBitmapAlphaInfoMask) == kCGImageAlphaPremultipliedFirst) { 80 int i, j; 81 Uint8 *p = (Uint8 *)surface->pixels; 82 for (i = surface->h * surface->pitch/4; i--; ) { 83#if __LITTLE_ENDIAN__ 84 Uint8 A = p[3]; 85 if (A) { 86 for (j = 0; j < 3; ++j) { 87 p[j] = (p[j] * 255) / A; 88 } 89 } 90#else 91 Uint8 A = p[0]; 92 if (A) { 93 for (j = 1; j < 4; ++j) { 94 p[j] = (p[j] * 255) / A; 95 } 96 } 97#endif /* ENDIAN */ 98 p += 4; 99 } 100 } 101 } 102 103 if (color_space) 104 { 105 CGColorSpaceRelease(color_space); 106 } 107 108 return surface; 109} 110 111static SDL_Surface* LoadImageFromRWops(SDL_RWops* rw_ops, CFStringRef uti_string_hint) 112{ 113 NSAutoreleasePool* autorelease_pool = [[NSAutoreleasePool alloc] init]; 114 SDL_Surface* sdl_surface; 115 UIImage* ui_image; 116 117 int bytes_read = 0; 118 // I don't know what a good size is. 119 // Max recommended texture size is 1024x1024 on iPhone so maybe base it on that? 120 const int block_size = 1024*4; 121 char temp_buffer[block_size]; 122 123 NSMutableData* ns_data = [[NSMutableData alloc] initWithCapacity:1024*1024*4]; 124 125 126 do 127 { 128 bytes_read = SDL_RWread(rw_ops, temp_buffer, 1, block_size); 129 [ns_data appendBytes:temp_buffer length:bytes_read]; 130 } while(bytes_read > 0); 131 132 ui_image = [[UIImage alloc] initWithData:ns_data]; 133 134 sdl_surface = Create_SDL_Surface_From_CGImage([ui_image CGImage]); 135 136 [ui_image release]; 137 [ns_data release]; 138 139 [autorelease_pool drain]; 140 141 return sdl_surface; 142} 143 144static SDL_Surface* LoadImageFromFile(const char *file) 145{ 146 NSAutoreleasePool* autorelease_pool = [[NSAutoreleasePool alloc] init]; 147 SDL_Surface* sdl_surface = NULL; 148 UIImage* ui_image; 149 NSString* ns_string; 150 151 ns_string = [[NSString alloc] initWithUTF8String:file]; 152 ui_image = [[UIImage alloc] initWithContentsOfFile:ns_string]; 153 if(ui_image != NULL) 154 { 155 sdl_surface = Create_SDL_Surface_From_CGImage([ui_image CGImage]); 156 } 157 158 [ui_image release]; 159 [ns_string release]; 160 161 [autorelease_pool drain]; 162 163 return sdl_surface; 164} 165 166 167/* Since UIImage doesn't really support streams well, we should optimize for the file case. */ 168SDL_Surface *IMG_Load(const char *file) 169{ 170 SDL_Surface* sdl_surface = NULL; 171 172 sdl_surface = LoadImageFromFile(file); 173 if(NULL == sdl_surface) 174 { 175 // Either the file doesn't exist or ImageIO doesn't understand the format. 176 // For the latter case, fallback to the native SDL_image handlers. 177 178 SDL_RWops *src = SDL_RWFromFile(file, "rb"); 179 char *ext = strrchr(file, '.'); 180 if(ext) { 181 ext++; 182 } 183 if(!src) { 184 /* The error message has been set in SDL_RWFromFile */ 185 return NULL; 186 } 187 sdl_surface = IMG_LoadTyped_RW(src, 1, ext); 188 } 189 return sdl_surface; 190} 191 192 193int IMG_InitJPG() 194{ 195 return 0; 196} 197 198void IMG_QuitJPG() 199{ 200} 201 202int IMG_InitPNG() 203{ 204 return 0; 205} 206 207void IMG_QuitPNG() 208{ 209} 210 211int IMG_InitTIF() 212{ 213 return 0; 214} 215 216void IMG_QuitTIF() 217{ 218} 219 220/* Copied straight from other files so I don't have to alter them. */ 221static int IMG_isICOCUR(SDL_RWops *src, int type) 222{ 223 int start; 224 int is_ICOCUR; 225 226 /* The Win32 ICO file header (14 bytes) */ 227 Uint16 bfReserved; 228 Uint16 bfType; 229 Uint16 bfCount; 230 231 if ( !src ) 232 return 0; 233 start = SDL_RWtell(src); 234 is_ICOCUR = 0; 235 bfReserved = SDL_ReadLE16(src); 236 bfType = SDL_ReadLE16(src); 237 bfCount = SDL_ReadLE16(src); 238 if ((bfReserved == 0) && (bfType == type) && (bfCount != 0)) 239 is_ICOCUR = 1; 240 SDL_RWseek(src, start, SEEK_SET); 241 242 return (is_ICOCUR); 243} 244 245int IMG_isICO(SDL_RWops *src) 246{ 247 return IMG_isICOCUR(src, 1); 248} 249 250int IMG_isCUR(SDL_RWops *src) 251{ 252 return IMG_isICOCUR(src, 2); 253} 254 255int IMG_isBMP(SDL_RWops *src) 256{ 257 int start; 258 int is_BMP; 259 char magic[2]; 260 261 if ( !src ) 262 return 0; 263 start = SDL_RWtell(src); 264 is_BMP = 0; 265 if ( SDL_RWread(src, magic, sizeof(magic), 1) ) { 266 if ( strncmp(magic, "BM", 2) == 0 ) { 267 is_BMP = 1; 268 } 269 } 270 SDL_RWseek(src, start, SEEK_SET); 271 return(is_BMP); 272} 273 274int IMG_isGIF(SDL_RWops *src) 275{ 276 int start; 277 int is_GIF; 278 char magic[6]; 279 280 if ( !src ) 281 return 0; 282 start = SDL_RWtell(src); 283 is_GIF = 0; 284 if ( SDL_RWread(src, magic, sizeof(magic), 1) ) { 285 if ( (strncmp(magic, "GIF", 3) == 0) && 286 ((memcmp(magic + 3, "87a", 3) == 0) || 287 (memcmp(magic + 3, "89a", 3) == 0)) ) { 288 is_GIF = 1; 289 } 290 } 291 SDL_RWseek(src, start, SEEK_SET); 292 return(is_GIF); 293} 294 295int IMG_isJPG(SDL_RWops *src) 296{ 297 int start; 298 int is_JPG; 299 int in_scan; 300 Uint8 magic[4]; 301 302 /* This detection code is by Steaphan Greene <stea@cs.binghamton.edu> */ 303 /* Blame me, not Sam, if this doesn't work right. */ 304 /* And don't forget to report the problem to the the sdl list too! */ 305 306 if ( !src ) 307 return 0; 308 start = SDL_RWtell(src); 309 is_JPG = 0; 310 in_scan = 0; 311 if ( SDL_RWread(src, magic, 2, 1) ) { 312 if ( (magic[0] == 0xFF) && (magic[1] == 0xD8) ) { 313 is_JPG = 1; 314 while (is_JPG == 1) { 315 if(SDL_RWread(src, magic, 1, 2) != 2) { 316 is_JPG = 0; 317 } else if( (magic[0] != 0xFF) && (in_scan == 0) ) { 318 is_JPG = 0; 319 } else if( (magic[0] != 0xFF) || (magic[1] == 0xFF) ) { 320 /* Extra padding in JPEG (legal) */ 321 /* or this is data and we are scanning */ 322 SDL_RWseek(src, -1, SEEK_CUR); 323 } else if(magic[1] == 0xD9) { 324 /* Got to end of good JPEG */ 325 break; 326 } else if( (in_scan == 1) && (magic[1] == 0x00) ) { 327 /* This is an encoded 0xFF within the data */ 328 } else if( (magic[1] >= 0xD0) && (magic[1] < 0xD9) ) { 329 /* These have nothing else */ 330 } else if(SDL_RWread(src, magic+2, 1, 2) != 2) { 331 is_JPG = 0; 332 } else { 333 /* Yes, it's big-endian */ 334 Uint32 start; 335 Uint32 size; 336 Uint32 end; 337 start = SDL_RWtell(src); 338 size = (magic[2] << 8) + magic[3]; 339 end = SDL_RWseek(src, size-2, SEEK_CUR); 340 if ( end != start + size - 2 ) is_JPG = 0; 341 if ( magic[1] == 0xDA ) { 342 /* Now comes the actual JPEG meat */ 343#ifdef FAST_IS_JPEG 344 /* Ok, I'm convinced. It is a JPEG. */ 345 break; 346#else 347 /* I'm not convinced. Prove it! */ 348 in_scan = 1; 349#endif 350 } 351 } 352 } 353 } 354 } 355 SDL_RWseek(src, start, SEEK_SET); 356 return(is_JPG); 357} 358 359int IMG_isPNG(SDL_RWops *src) 360{ 361 int start; 362 int is_PNG; 363 Uint8 magic[4]; 364 365 if ( !src ) 366 return 0; 367 start = SDL_RWtell(src); 368 is_PNG = 0; 369 if ( SDL_RWread(src, magic, 1, sizeof(magic)) == sizeof(magic) ) { 370 if ( magic[0] == 0x89 && 371 magic[1] == 'P' && 372 magic[2] == 'N' && 373 magic[3] == 'G' ) { 374 is_PNG = 1; 375 } 376 } 377 SDL_RWseek(src, start, SEEK_SET); 378 return(is_PNG); 379} 380 381int IMG_isTIF(SDL_RWops* src) 382{ 383 int start; 384 int is_TIF; 385 Uint8 magic[4]; 386 387 if ( !src ) 388 return 0; 389 start = SDL_RWtell(src); 390 is_TIF = 0; 391 if ( SDL_RWread(src, magic, 1, sizeof(magic)) == sizeof(magic) ) { 392 if ( (magic[0] == 'I' && 393 magic[1] == 'I' && 394 magic[2] == 0x2a && 395 magic[3] == 0x00) || 396 (magic[0] == 'M' && 397 magic[1] == 'M' && 398 magic[2] == 0x00 && 399 magic[3] == 0x2a) ) { 400 is_TIF = 1; 401 } 402 } 403 SDL_RWseek(src, start, SEEK_SET); 404 return(is_TIF); 405} 406 407SDL_Surface* IMG_LoadCUR_RW(SDL_RWops *src) 408{ 409 /* FIXME: Is this a supported type? */ 410 return LoadImageFromRWops(src, CFSTR("com.microsoft.cur")); 411} 412SDL_Surface* IMG_LoadICO_RW(SDL_RWops *src) 413{ 414 return LoadImageFromRWops(src, kUTTypeICO); 415} 416SDL_Surface* IMG_LoadBMP_RW(SDL_RWops *src) 417{ 418 return LoadImageFromRWops(src, kUTTypeBMP); 419} 420SDL_Surface* IMG_LoadGIF_RW(SDL_RWops *src) 421{ 422 return LoadImageFromRWops(src, kUTTypeGIF); 423} 424SDL_Surface* IMG_LoadJPG_RW(SDL_RWops *src) 425{ 426 return LoadImageFromRWops(src, kUTTypeJPEG); 427} 428SDL_Surface* IMG_LoadPNG_RW(SDL_RWops *src) 429{ 430 return LoadImageFromRWops(src, kUTTypePNG); 431} 432SDL_Surface* IMG_LoadTGA_RW(SDL_RWops *src) 433{ 434 return LoadImageFromRWops(src, CFSTR("com.truevision.tga-image")); 435} 436SDL_Surface* IMG_LoadTIF_RW(SDL_RWops *src) 437{ 438 return LoadImageFromRWops(src, kUTTypeTIFF); 439} 440 441