5 * Created by Eric Wing on 1/1/09.
6 * Copyright 2009 __MyCompanyName__. All rights reserved.
10 #if defined(__APPLE__) && !defined(SDL_IMAGE_USE_COMMON_BACKEND)
12 #include "SDL_image.h"
14 // For ImageIO framework and also LaunchServices framework (for UTIs)
15 #include <ApplicationServices/ApplicationServices.h>
16 // Used because CGDataProviderCreate became deprecated in 10.5
17 #include <AvailabilityMacros.h>
19 /**************************************************************
20 ***** Begin Callback functions for block reading *************
21 **************************************************************/
23 // This callback reads some bytes from an SDL_rwops and copies it
24 // to a Quartz buffer (supplied by Apple framework).
25 static size_t MyProviderGetBytesCallback(void* rwops_userdata, void* quartz_buffer, size_t the_count)
27 return (size_t)SDL_RWread((struct SDL_RWops *)rwops_userdata, quartz_buffer, 1, the_count);
30 // This callback is triggered when the data provider is released
31 // so you can clean up any resources.
32 static void MyProviderReleaseInfoCallback(void* rwops_userdata)
34 // What should I put here?
35 // I think the user and SDL_RWops controls closing, so I don't do anything.
38 static void MyProviderRewindCallback(void* rwops_userdata)
40 SDL_RWseek((struct SDL_RWops *)rwops_userdata, 0, RW_SEEK_SET);
43 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 // CGDataProviderCreateSequential was introduced in 10.5; CGDataProviderCreate is deprecated
44 off_t MyProviderSkipForwardBytesCallback(void* rwops_userdata, off_t the_count)
46 off_t start_position = SDL_RWtell((struct SDL_RWops *)rwops_userdata);
47 SDL_RWseek((struct SDL_RWops *)rwops_userdata, the_count, RW_SEEK_CUR);
48 off_t end_position = SDL_RWtell((struct SDL_RWops *)rwops_userdata);
49 return (end_position - start_position);
51 #else // CGDataProviderCreate was deprecated in 10.5
52 static void MyProviderSkipBytesCallback(void* rwops_userdata, size_t the_count)
54 SDL_RWseek((struct SDL_RWops *)rwops_userdata, the_count, RW_SEEK_CUR);
59 /**************************************************************
60 ***** End Callback functions for block reading ***************
61 **************************************************************/
63 // This creates a CGImageSourceRef which is a handle to an image that can be used to examine information
64 // about the image or load the actual image data.
65 static CGImageSourceRef CreateCGImageSourceFromRWops(SDL_RWops* rw_ops, CFDictionaryRef hints_and_options)
67 CGImageSourceRef source_ref;
69 // Similar to SDL_RWops, Apple has their own callbacks for dealing with data streams.
71 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 // CGDataProviderCreateSequential was introduced in 10.5; CGDataProviderCreate is deprecated
72 CGDataProviderSequentialCallbacks provider_callbacks =
75 MyProviderGetBytesCallback,
76 MyProviderSkipForwardBytesCallback,
77 MyProviderRewindCallback,
78 MyProviderReleaseInfoCallback
81 CGDataProviderRef data_provider = CGDataProviderCreateSequential(rw_ops, &provider_callbacks);
84 #else // CGDataProviderCreate was deprecated in 10.5
86 CGDataProviderCallbacks provider_callbacks =
88 MyProviderGetBytesCallback,
89 MyProviderSkipBytesCallback,
90 MyProviderRewindCallback,
91 MyProviderReleaseInfoCallback
94 CGDataProviderRef data_provider = CGDataProviderCreate(rw_ops, &provider_callbacks);
96 // Get the CGImageSourceRef.
97 // The dictionary can be NULL or contain hints to help ImageIO figure out the image type.
98 source_ref = CGImageSourceCreateWithDataProvider(data_provider, hints_and_options);
103 /* Create a CGImageSourceRef from a file. */
104 /* Remember to CFRelease the created source when done. */
105 static CGImageSourceRef CreateCGImageSourceFromFile(const char* the_path)
107 CFURLRef the_url = NULL;
108 CGImageSourceRef source_ref = NULL;
109 CFStringRef cf_string = NULL;
111 /* Create a CFString from a C string */
112 cf_string = CFStringCreateWithCString(
115 kCFStringEncodingUTF8
122 /* Create a CFURL from a CFString */
123 the_url = CFURLCreateWithFileSystemPath(
126 kCFURLPOSIXPathStyle,
130 /* Don't need the CFString any more (error or not) */
131 CFRelease(cf_string);
139 source_ref = CGImageSourceCreateWithURL(the_url, NULL);
140 /* Don't need the URL any more (error or not) */
148 static CGImageRef CreateCGImageFromCGImageSource(CGImageSourceRef image_source)
150 CGImageRef image_ref = NULL;
152 if(NULL == image_source)
157 // Get the first item in the image source (some image formats may
158 // contain multiple items).
159 image_ref = CGImageSourceCreateImageAtIndex(image_source, 0, NULL);
160 if(NULL == image_ref)
162 IMG_SetError("CGImageSourceCreateImageAtIndex() failed");
167 static CFDictionaryRef CreateHintDictionary(CFStringRef uti_string_hint)
169 CFDictionaryRef hint_dictionary = NULL;
171 if(uti_string_hint != NULL)
173 // Do a bunch of work to setup a CFDictionary containing the jpeg compression properties.
174 CFStringRef the_keys[1];
175 CFStringRef the_values[1];
177 the_keys[0] = kCGImageSourceTypeIdentifierHint;
178 the_values[0] = uti_string_hint;
180 // kCFTypeDictionaryKeyCallBacks or kCFCopyStringDictionaryKeyCallBacks?
181 hint_dictionary = CFDictionaryCreate(NULL, (const void**)&the_keys, (const void**)&the_values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
183 return hint_dictionary;
189 static int Internal_isType(SDL_RWops* rw_ops, CFStringRef uti_string_to_test)
191 CGImageSourceRef image_source;
192 CFStringRef uti_type;
195 CFDictionaryRef hint_dictionary = NULL;
197 hint_dictionary = CreateHintDictionary(uti_string_to_test);
198 image_source = CreateCGImageSourceFromRWops(rw_ops, hint_dictionary);
200 if(hint_dictionary != NULL)
202 CFRelease(hint_dictionary);
205 if(NULL == image_source)
210 // This will get the UTI of the container, not the image itself.
211 // Under most cases, this won't be a problem.
212 // But if a person passes an icon file which contains a bmp,
213 // the format will be of the icon file.
214 // But I think the main SDL_image codebase has this same problem so I'm not going to worry about it.
215 uti_type = CGImageSourceGetType(image_source);
218 // Unsure if we really want conformance or equality
219 is_type = UTTypeConformsTo(uti_string_to_test, uti_type);
221 CFRelease(image_source);
226 // Once we have our image, we need to get it into an SDL_Surface
227 static SDL_Surface* Create_SDL_Surface_From_CGImage(CGImageRef image_ref)
229 /* This code is adapted from Apple's Documentation found here:
230 * http://developer.apple.com/documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/index.html
231 * Listing 9-4††Using a Quartz image as a texture source.
232 * Unfortunately, this guide doesn't show what to do about
233 * non-RGBA image formats so I'm making the rest up.
234 * All this code should be scrutinized.
237 size_t w = CGImageGetWidth(image_ref);
238 size_t h = CGImageGetHeight(image_ref);
239 CGRect rect = {{0, 0}, {w, h}};
241 CGImageAlphaInfo alpha = CGImageGetAlphaInfo(image_ref);
242 //size_t bits_per_pixel = CGImageGetBitsPerPixel(image_ref);
243 size_t bits_per_component = 8;
245 SDL_Surface* surface;
251 CGContextRef bitmap_context;
252 CGBitmapInfo bitmap_info;
253 CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();
255 if (alpha == kCGImageAlphaNone ||
256 alpha == kCGImageAlphaNoneSkipFirst ||
257 alpha == kCGImageAlphaNoneSkipLast) {
258 bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host; /* XRGB */
261 /* kCGImageAlphaFirst isn't supported */
262 //bitmap_info = kCGImageAlphaFirst | kCGBitmapByteOrder32Host; /* ARGB */
263 bitmap_info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host; /* ARGB */
271 surface = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, Rmask, Gmask, Bmask, Amask);
274 // Sets up a context to be drawn to with surface->pixels as the area to be drawn to
275 bitmap_context = CGBitmapContextCreate(
285 // Draws the image into the context's image_data
286 CGContextDrawImage(bitmap_context, rect, image_ref);
288 CGContextRelease(bitmap_context);
290 // FIXME: Reverse the premultiplied alpha
291 if ((bitmap_info & kCGBitmapAlphaInfoMask) == kCGImageAlphaPremultipliedFirst) {
293 Uint8 *p = (Uint8 *)surface->pixels;
294 for (i = surface->h * surface->pitch/4; i--; ) {
295 #if __LITTLE_ENDIAN__
298 for (j = 0; j < 3; ++j) {
299 p[j] = (p[j] * 255) / A;
305 for (j = 1; j < 4; ++j) {
306 p[j] = (p[j] * 255) / A;
317 CGColorSpaceRelease(color_space);
324 static SDL_Surface* LoadImageFromRWops(SDL_RWops* rw_ops, CFStringRef uti_string_hint)
326 SDL_Surface* sdl_surface;
327 CGImageSourceRef image_source;
328 CGImageRef image_ref = NULL;
329 CFDictionaryRef hint_dictionary = NULL;
331 hint_dictionary = CreateHintDictionary(uti_string_hint);
332 image_source = CreateCGImageSourceFromRWops(rw_ops, hint_dictionary);
334 if(hint_dictionary != NULL)
336 CFRelease(hint_dictionary);
339 if(NULL == image_source)
344 image_ref = CreateCGImageFromCGImageSource(image_source);
345 CFRelease(image_source);
347 if(NULL == image_ref)
352 sdl_surface = Create_SDL_Surface_From_CGImage(image_ref);
353 CFRelease(image_ref);
360 static SDL_Surface* LoadImageFromFile(const char* file)
362 SDL_Surface* sdl_surface = NULL;
363 CGImageSourceRef image_source = NULL;
364 CGImageRef image_ref = NULL;
367 image_source = CreateCGImageSourceFromFile(file);
369 if(NULL == image_source)
374 image_ref = CreateCGImageFromCGImageSource(image_source);
375 CFRelease(image_source);
377 if(NULL == image_ref)
382 sdl_surface = Create_SDL_Surface_From_CGImage(image_ref);
383 CFRelease(image_ref);
414 int IMG_isCUR(SDL_RWops *src)
416 /* FIXME: Is this a supported type? */
417 return Internal_isType(src, CFSTR("com.microsoft.cur"));
420 int IMG_isICO(SDL_RWops *src)
422 return Internal_isType(src, kUTTypeICO);
425 int IMG_isBMP(SDL_RWops *src)
427 return Internal_isType(src, kUTTypeBMP);
430 int IMG_isGIF(SDL_RWops *src)
432 return Internal_isType(src, kUTTypeGIF);
435 // Note: JPEG 2000 is kUTTypeJPEG2000
436 int IMG_isJPG(SDL_RWops *src)
438 return Internal_isType(src, kUTTypeJPEG);
441 int IMG_isPNG(SDL_RWops *src)
443 return Internal_isType(src, kUTTypePNG);
446 // This isn't a public API function. Apple seems to be able to identify tga's.
447 int IMG_isTGA(SDL_RWops *src)
449 return Internal_isType(src, CFSTR("com.truevision.tga-image"));
452 int IMG_isTIF(SDL_RWops *src)
454 return Internal_isType(src, kUTTypeTIFF);
457 SDL_Surface* IMG_LoadCUR_RW(SDL_RWops *src)
459 /* FIXME: Is this a supported type? */
460 return LoadImageFromRWops(src, CFSTR("com.microsoft.cur"));
462 SDL_Surface* IMG_LoadICO_RW(SDL_RWops *src)
464 return LoadImageFromRWops(src, kUTTypeICO);
466 SDL_Surface* IMG_LoadBMP_RW(SDL_RWops *src)
468 return LoadImageFromRWops(src, kUTTypeBMP);
470 SDL_Surface* IMG_LoadGIF_RW(SDL_RWops *src)
472 return LoadImageFromRWops(src, kUTTypeGIF);
474 SDL_Surface* IMG_LoadJPG_RW(SDL_RWops *src)
476 return LoadImageFromRWops(src, kUTTypeJPEG);
478 SDL_Surface* IMG_LoadPNG_RW(SDL_RWops *src)
480 return LoadImageFromRWops(src, kUTTypePNG);
482 SDL_Surface* IMG_LoadTGA_RW(SDL_RWops *src)
484 return LoadImageFromRWops(src, CFSTR("com.truevision.tga-image"));
486 SDL_Surface* IMG_LoadTIF_RW(SDL_RWops *src)
488 return LoadImageFromRWops(src, kUTTypeTIFF);
491 // Apple provides both stream and file loading functions in ImageIO.
492 // Potentially, Apple can optimize for either case.
493 SDL_Surface* IMG_Load(const char *file)
495 SDL_Surface* sdl_surface = NULL;
497 sdl_surface = LoadImageFromFile(file);
498 if(NULL == sdl_surface)
500 // Either the file doesn't exist or ImageIO doesn't understand the format.
501 // For the latter case, fallback to the native SDL_image handlers.
502 SDL_RWops *src = SDL_RWFromFile(file, "rb");
503 char *ext = strrchr(file, '.');
508 /* The error message has been set in SDL_RWFromFile */
511 sdl_surface = IMG_LoadTyped_RW(src, 1, ext);
516 #endif /* defined(__APPLE__) && !defined(SDL_IMAGE_USE_COMMON_BACKEND) */