New comit of SDL2
[supertux.git] / src / SDL2 / IMG_ImageIO.m
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 descption 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     CFDictionaryRef hint_dictionary = CreateHintDictionary(uti_string_to_test);
536     CGImageSourceRef image_source = CreateCGImageSourceFromRWops(rw_ops, hint_dictionary);
537
538     if (hint_dictionary != NULL) {
539         CFRelease(hint_dictionary);
540     }
541
542     if (NULL == image_source) {
543         return 0;
544     }
545
546     // This will get the UTI of the container, not the image itself.
547     // Under most cases, this won't be a problem.
548     // But if a person passes an icon file which contains a bmp,
549     // the format will be of the icon file.
550     // But I think the main SDL_image codebase has this same problem so I'm not going to worry about it.
551     CFStringRef uti_type = CGImageSourceGetType(image_source);
552     //  CFShow(uti_type);
553
554     // Unsure if we really want conformance or equality
555     is_type = (int)UTTypeConformsTo(uti_string_to_test, uti_type);
556
557     CFRelease(image_source);
558     return is_type;
559 }
560
561 static int Internal_isType (SDL_RWops *rw_ops, CFStringRef uti_string_to_test)
562 {
563     if (rw_ops == NULL)
564         return 0;
565
566 #if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)
567     if (USE_UIIMAGE_BACKEND())
568         return Internal_isType_UIImage(rw_ops, uti_string_to_test);
569     else
570 #endif
571         return Internal_isType_ImageIO(rw_ops, uti_string_to_test);
572 }
573
574 #ifdef BMP_USES_IMAGEIO
575
576 int IMG_isCUR(SDL_RWops *src)
577 {
578     /* FIXME: Is this a supported type? */
579     return Internal_isType(src, CFSTR("com.microsoft.cur"));
580 }
581
582 int IMG_isICO(SDL_RWops *src)
583 {
584     return Internal_isType(src, kUTTypeICO);
585 }
586
587 int IMG_isBMP(SDL_RWops *src)
588 {
589     return Internal_isType(src, kUTTypeBMP);
590 }
591
592 #endif /* BMP_USES_IMAGEIO */
593
594 int IMG_isGIF(SDL_RWops *src)
595 {
596     return Internal_isType(src, kUTTypeGIF);
597 }
598
599 // Note: JPEG 2000 is kUTTypeJPEG2000
600 int IMG_isJPG(SDL_RWops *src)
601 {
602     return Internal_isType(src, kUTTypeJPEG);
603 }
604
605 int IMG_isPNG(SDL_RWops *src)
606 {
607     return Internal_isType(src, kUTTypePNG);
608 }
609
610 // This isn't a public API function. Apple seems to be able to identify tga's.
611 int IMG_isTGA(SDL_RWops *src)
612 {
613     return Internal_isType(src, CFSTR("com.truevision.tga-image"));
614 }
615
616 int IMG_isTIF(SDL_RWops *src)
617 {
618     return Internal_isType(src, kUTTypeTIFF);
619 }
620
621 #pragma mark -
622 #pragma mark Load image engine
623 static SDL_Surface *LoadImageFromRWops_UIImage (SDL_RWops* rw_ops, CFStringRef uti_string_hint)
624 {
625     SDL_Surface *sdl_surface = NULL;
626
627 #if defined(ALLOW_UIIMAGE_FALLBACK) && ((TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1))
628     NSAutoreleasePool* autorelease_pool = [[NSAutoreleasePool alloc] init];
629     UIImage *ui_image;
630     int bytes_read = 0;
631     // I don't know what a good size is.
632     // Max recommended texture size is 1024x1024 on iPhone so maybe base it on that?
633     const int block_size = 1024*4;
634     char temp_buffer[block_size];
635
636     NSMutableData* ns_data = [[NSMutableData alloc] initWithCapacity:1024*1024*4];
637     do {
638         bytes_read = SDL_RWread(rw_ops, temp_buffer, 1, block_size);
639         [ns_data appendBytes:temp_buffer length:bytes_read];
640     } while (bytes_read > 0);
641
642     ui_image = [[UIImage alloc] initWithData:ns_data];
643     if (ui_image != nil)
644         sdl_surface = Create_SDL_Surface_From_CGImage([ui_image CGImage]);
645     [ui_image release];
646     [ns_data release];
647     [autorelease_pool drain];
648
649 #endif  /* #if defined(ALLOW_UIIMAGE_FALLBACK) && ((TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)) */
650     return sdl_surface;
651 }
652
653 static SDL_Surface *LoadImageFromRWops_ImageIO (SDL_RWops *rw_ops, CFStringRef uti_string_hint)
654 {
655     CFDictionaryRef hint_dictionary = CreateHintDictionary(uti_string_hint);
656     CGImageSourceRef image_source = CreateCGImageSourceFromRWops(rw_ops, hint_dictionary);
657
658     if (hint_dictionary != NULL)
659         CFRelease(hint_dictionary);
660
661     if (NULL == image_source)
662         return NULL;
663
664     CGImageRef image_ref = CreateCGImageFromCGImageSource(image_source);
665     CFRelease(image_source);
666
667     if (NULL == image_ref)
668         return NULL;
669     SDL_Surface *sdl_surface = Create_SDL_Surface_From_CGImage(image_ref);
670     CFRelease(image_ref);
671
672     return sdl_surface;
673 }
674
675 static SDL_Surface *LoadImageFromRWops (SDL_RWops *rw_ops, CFStringRef uti_string_hint)
676 {
677 #if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)
678     if (USE_UIIMAGE_BACKEND())
679         return LoadImageFromRWops_UIImage(rw_ops, uti_string_hint);
680     else
681 #endif
682         return LoadImageFromRWops_ImageIO(rw_ops, uti_string_hint);
683 }
684
685 static SDL_Surface* LoadImageFromFile_UIImage (const char *file)
686 {
687     SDL_Surface *sdl_surface = NULL;
688
689 #if defined(ALLOW_UIIMAGE_FALLBACK) && ((TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1))
690     NSAutoreleasePool* autorelease_pool = [[NSAutoreleasePool alloc] init];
691     NSString *ns_string = [[NSString alloc] initWithUTF8String:file];
692     UIImage *ui_image = [[UIImage alloc] initWithContentsOfFile:ns_string];
693     if (ui_image != nil)
694         sdl_surface = Create_SDL_Surface_From_CGImage([ui_image CGImage]);
695     [ui_image release];
696     [ns_string release];
697     [autorelease_pool drain];
698
699 #endif  /* #if defined(ALLOW_UIIMAGE_FALLBACK) && ((TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)) */
700     return sdl_surface;
701 }
702
703 static SDL_Surface* LoadImageFromFile_ImageIO (const char *file)
704 {
705     CGImageSourceRef image_source = NULL;
706
707     image_source = CreateCGImageSourceFromFile(file);
708
709     if(NULL == image_source)
710         return NULL;
711
712     CGImageRef image_ref = CreateCGImageFromCGImageSource(image_source);
713     CFRelease(image_source);
714
715     if (NULL == image_ref)
716         return NULL;
717     SDL_Surface *sdl_surface = Create_SDL_Surface_From_CGImage(image_ref);
718     CFRelease(image_ref);
719     return sdl_surface;
720 }
721
722 static SDL_Surface* LoadImageFromFile (const char *file)
723 {
724 #if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)
725     if (USE_UIIMAGE_BACKEND())
726         return LoadImageFromFile_UIImage(file);
727     else
728 #endif
729         return LoadImageFromFile_ImageIO(file);
730 }
731
732 #ifdef BMP_USES_IMAGEIO
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 #endif /* BMP_USES_IMAGEIO */
751
752 SDL_Surface* IMG_LoadGIF_RW (SDL_RWops *src)
753 {
754     return LoadImageFromRWops (src, kUTTypeGIF);
755 }
756
757 SDL_Surface* IMG_LoadJPG_RW (SDL_RWops *src)
758 {
759     return LoadImageFromRWops (src, kUTTypeJPEG);
760 }
761
762 SDL_Surface* IMG_LoadPNG_RW (SDL_RWops *src)
763 {
764     return LoadImageFromRWops (src, kUTTypePNG);
765 }
766
767 SDL_Surface* IMG_LoadTGA_RW (SDL_RWops *src)
768 {
769     return LoadImageFromRWops(src, CFSTR("com.truevision.tga-image"));
770 }
771
772 SDL_Surface* IMG_LoadTIF_RW (SDL_RWops *src)
773 {
774     return LoadImageFromRWops(src, kUTTypeTIFF);
775 }
776
777 // Since UIImage doesn't really support streams well, we should optimize for the file case.
778 // Apple provides both stream and file loading functions in ImageIO.
779 // Potentially, Apple can optimize for either case.
780 SDL_Surface* IMG_Load (const char *file)
781 {
782     SDL_Surface* sdl_surface = NULL;
783
784     sdl_surface = LoadImageFromFile(file);
785     if(NULL == sdl_surface)
786     {
787         // Either the file doesn't exist or ImageIO doesn't understand the format.
788         // For the latter case, fallback to the native SDL_image handlers.
789         SDL_RWops *src = SDL_RWFromFile(file, "rb");
790         char *ext = strrchr(file, '.');
791         if (ext) {
792             ext++;
793         }
794         if (!src) {
795             /* The error message has been set in SDL_RWFromFile */
796             return NULL;
797         }
798         sdl_surface = IMG_LoadTyped_RW(src, 1, ext);
799     }
800     return sdl_surface;
801 }
802
803 #endif /* defined(__APPLE__) && !defined(SDL_IMAGE_USE_COMMON_BACKEND) */