5 * Created by Eric Wing on 1/1/09.
6 * Copyright 2009 __MyCompanyName__. All rights reserved.
11 // For ImageIO framework and also LaunchServices framework (for UTIs)
12 #include <ApplicationServices/ApplicationServices.h>
13 // Used because CGDataProviderCreate became deprecated in 10.5
14 #include <AvailabilityMacros.h>
16 /**************************************************************
17 ***** Begin Callback functions for block reading *************
18 **************************************************************/
20 // This callback reads some bytes from an SDL_rwops and copies it
21 // to a Quartz buffer (supplied by Apple framework).
22 static size_t MyProviderGetBytesCallback(void* rwops_userdata, void* quartz_buffer, size_t the_count)
24 return (size_t)SDL_RWread((struct SDL_RWops *)rwops_userdata, quartz_buffer, 1, the_count);
27 // This callback is triggered when the data provider is released
28 // so you can clean up any resources.
29 static void MyProviderReleaseInfoCallback(void* rwops_userdata)
31 // What should I put here?
32 // I think the user and SDL_RWops controls closing, so I don't do anything.
35 static void MyProviderRewindCallback(void* rwops_userdata)
37 SDL_RWseek((struct SDL_RWops *)rwops_userdata, 0, RW_SEEK_SET);
40 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 // CGDataProviderCreateSequential was introduced in 10.5; CGDataProviderCreate is deprecated
41 off_t MyProviderSkipForwardBytesCallback(void* rwops_userdata, off_t the_count)
43 off_t start_position = SDL_RWtell((struct SDL_RWops *)rwops_userdata);
44 SDL_RWseek((struct SDL_RWops *)rwops_userdata, the_count, RW_SEEK_CUR);
45 off_t end_position = SDL_RWtell((struct SDL_RWops *)rwops_userdata);
46 return (end_position - start_position);
48 #else // CGDataProviderCreate was deprecated in 10.5
49 static void MyProviderSkipBytesCallback(void* rwops_userdata, size_t the_count)
51 SDL_RWseek((struct SDL_RWops *)rwops_userdata, the_count, RW_SEEK_CUR);
56 /**************************************************************
57 ***** End Callback functions for block reading ***************
58 **************************************************************/
60 // This creates a CGImageSourceRef which is a handle to an image that can be used to examine information
61 // about the image or load the actual image data.
62 static CGImageSourceRef CreateCGImageSourceFromRWops(SDL_RWops* rw_ops, CFDictionaryRef hints_and_options)
64 CGImageSourceRef source_ref;
66 // Similar to SDL_RWops, Apple has their own callbacks for dealing with data streams.
68 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 // CGDataProviderCreateSequential was introduced in 10.5; CGDataProviderCreate is deprecated
69 CGDataProviderSequentialCallbacks provider_callbacks =
72 MyProviderGetBytesCallback,
73 MyProviderSkipForwardBytesCallback,
74 MyProviderRewindCallback,
75 MyProviderReleaseInfoCallback
78 CGDataProviderRef data_provider = CGDataProviderCreateSequential(rw_ops, &provider_callbacks);
81 #else // CGDataProviderCreate was deprecated in 10.5
83 CGDataProviderCallbacks provider_callbacks =
85 MyProviderGetBytesCallback,
86 MyProviderSkipBytesCallback,
87 MyProviderRewindCallback,
88 MyProviderReleaseInfoCallback
91 CGDataProviderRef data_provider = CGDataProviderCreate(rw_ops, &provider_callbacks);
93 // Get the CGImageSourceRef.
94 // The dictionary can be NULL or contain hints to help ImageIO figure out the image type.
95 source_ref = CGImageSourceCreateWithDataProvider(data_provider, hints_and_options);
100 /* Create a CGImageSourceRef from a file. */
101 /* Remember to CFRelease the created source when done. */
102 static CGImageSourceRef CreateCGImageSourceFromFile(const char* the_path)
104 CFURLRef the_url = NULL;
105 CGImageSourceRef source_ref = NULL;
106 CFStringRef cf_string = NULL;
108 /* Create a CFString from a C string */
109 cf_string = CFStringCreateWithCString(
112 kCFStringEncodingUTF8
119 /* Create a CFURL from a CFString */
120 the_url = CFURLCreateWithFileSystemPath(
123 kCFURLPOSIXPathStyle,
127 /* Don't need the CFString any more (error or not) */
128 CFRelease(cf_string);
136 source_ref = CGImageSourceCreateWithURL(the_url, NULL);
137 /* Don't need the URL any more (error or not) */
145 static CGImageRef CreateCGImageFromCGImageSource(CGImageSourceRef image_source)
147 CGImageRef image_ref = NULL;
149 if(NULL == image_source)
154 // Get the first item in the image source (some image formats may
155 // contain multiple items).
156 image_ref = CGImageSourceCreateImageAtIndex(image_source, 0, NULL);
160 static CFDictionaryRef CreateHintDictionary(CFStringRef uti_string_hint)
162 CFDictionaryRef hint_dictionary = NULL;
164 if(uti_string_hint != NULL)
166 // Do a bunch of work to setup a CFDictionary containing the jpeg compression properties.
167 CFStringRef the_keys[1];
168 CFStringRef the_values[1];
170 the_keys[0] = kCGImageSourceTypeIdentifierHint;
171 the_values[0] = uti_string_hint;
173 // kCFTypeDictionaryKeyCallBacks or kCFCopyStringDictionaryKeyCallBacks?
174 hint_dictionary = CFDictionaryCreate(NULL, (const void**)&the_keys, (const void**)&the_values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
176 return hint_dictionary;
182 static int Internal_isType(SDL_RWops* rw_ops, CFStringRef uti_string_to_test)
184 CGImageSourceRef image_source;
185 CFStringRef uti_type;
188 CFDictionaryRef hint_dictionary = NULL;
190 hint_dictionary = CreateHintDictionary(uti_string_to_test);
191 image_source = CreateCGImageSourceFromRWops(rw_ops, hint_dictionary);
193 if(hint_dictionary != NULL)
195 CFRelease(hint_dictionary);
198 if(NULL == image_source)
203 // This will get the UTI of the container, not the image itself.
204 // Under most cases, this won't be a problem.
205 // But if a person passes an icon file which contains a bmp,
206 // the format will be of the icon file.
207 // But I think the main SDL_image codebase has this same problem so I'm not going to worry about it.
208 uti_type = CGImageSourceGetType(image_source);
211 // Unsure if we really want conformance or equality
212 is_type = UTTypeConformsTo(uti_string_to_test, uti_type);
214 CFRelease(image_source);
219 // Once we have our image, we need to get it into an SDL_Surface
220 static SDL_Surface* Create_SDL_Surface_From_CGImage(CGImageRef image_ref)
222 /* This code is adapted from Apple's Documentation found here:
223 * http://developer.apple.com/documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/index.html
224 * Listing 9-4††Using a Quartz image as a texture source.
225 * Unfortunately, this guide doesn't show what to do about
226 * non-RGBA image formats so I'm making the rest up.
227 * All this code should be scrutinized.
230 size_t the_width = CGImageGetWidth(image_ref);
231 size_t the_height = CGImageGetHeight(image_ref);
232 CGRect the_rect = {{0, 0}, {the_width, the_height}};
234 size_t bits_per_pixel = CGImageGetBitsPerPixel(image_ref);
235 size_t bytes_per_row = CGImageGetBytesPerRow(image_ref);
236 // size_t bits_per_component = CGImageGetBitsPerComponent(image_ref);
237 size_t bits_per_component = 8;
239 // CGImageAlphaInfo alpha_info = CGImageGetAlphaInfo(image_ref);
242 SDL_Surface* sdl_surface = NULL;
249 CGContextRef bitmap_context = NULL;
251 CGColorSpaceRef color_space = NULL;
252 CGBitmapInfo bitmap_info = CGImageGetBitmapInfo(image_ref);
255 switch(bits_per_pixel)
259 bytes_per_row = the_width*4;
260 // color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
261 color_space = CGColorSpaceCreateDeviceRGB();
262 // bitmap_info = kCGImageAlphaPremultipliedFirst;
264 bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Big; /* XRGB Big Endian */
266 bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little; /* XRGB Little Endian */
274 sdl_surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
275 the_width, the_height, 32, Rmask, Gmask, Bmask, Amask);
284 bytes_per_row = the_width*4;
286 color_space = CGColorSpaceCreateDeviceRGB();
289 bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Big; /* XRGB Big Endian */
291 bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little; /* XRGB Little Endian */
299 sdl_surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
300 the_width, the_height, 32, Rmask, Gmask, Bmask, Amask);
306 bytes_per_row = the_width*4;
307 // color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
308 color_space = CGColorSpaceCreateDeviceRGB();
309 // bitmap_info = kCGImageAlphaNone;
311 bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Big; /* XRGB Big Endian */
313 bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little; /* XRGB Little Endian */
320 sdl_surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
321 the_width, the_height, 32, Rmask, Gmask, Bmask, Amask);
328 bytes_per_row = the_width*4;
329 // color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
330 color_space = CGColorSpaceCreateDeviceRGB();
331 // bitmap_info = kCGImageAlphaPremultipliedFirst;
333 bitmap_info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Big; /* XRGB Big Endian */
335 bitmap_info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little; /* XRGB Little Endian */
343 sdl_surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
344 the_width, the_height, 32, Rmask, Gmask, Bmask, Amask);
355 if(NULL == sdl_surface)
357 if(color_space != NULL)
359 CGColorSpaceRelease(color_space);
365 // Sets up a context to be drawn to with sdl_surface->pixels as the area to be drawn to
366 bitmap_context = CGBitmapContextCreate(
376 // Draws the image into the context's image_data
377 CGContextDrawImage(bitmap_context, the_rect, image_ref);
379 CGContextRelease(bitmap_context);
380 CGColorSpaceRelease(color_space);
389 static SDL_Surface* LoadImageFromRWops(SDL_RWops* rw_ops, CFStringRef uti_string_hint)
391 SDL_Surface* sdl_surface;
392 CGImageSourceRef image_source;
393 CGImageRef image_ref = NULL;
394 CFDictionaryRef hint_dictionary = NULL;
396 hint_dictionary = CreateHintDictionary(uti_string_hint);
397 image_source = CreateCGImageSourceFromRWops(rw_ops, hint_dictionary);
399 if(hint_dictionary != NULL)
401 CFRelease(hint_dictionary);
404 if(NULL == image_source)
409 image_ref = CreateCGImageFromCGImageSource(image_source);
410 CFRelease(image_source);
412 if(NULL == image_ref)
417 sdl_surface = Create_SDL_Surface_From_CGImage(image_ref);
418 CFRelease(image_ref);
425 static SDL_Surface* LoadImageFromFile(const char* file)
427 SDL_Surface* sdl_surface = NULL;
428 CGImageSourceRef image_source = NULL;
429 CGImageRef image_ref = NULL;
432 image_source = CreateCGImageSourceFromFile(file);
434 if(NULL == image_source)
439 image_ref = CreateCGImageFromCGImageSource(image_source);
440 CFRelease(image_source);
442 if(NULL == image_ref)
447 sdl_surface = Create_SDL_Surface_From_CGImage(image_ref);
448 CFRelease(image_ref);
453 int IMG_isCUR(SDL_RWops *src)
455 /* FIXME: Is this a supported type? */
456 return Internal_isType(src, CFSTR("com.microsoft.cur"));
459 int IMG_isICO(SDL_RWops *src)
461 return Internal_isType(src, kUTTypeICO);
464 int IMG_isBMP(SDL_RWops *src)
466 return Internal_isType(src, kUTTypeBMP);
469 int IMG_isGIF(SDL_RWops *src)
471 return Internal_isType(src, kUTTypeGIF);
474 // Note: JPEG 2000 is kUTTypeJPEG2000
475 int IMG_isJPG(SDL_RWops *src)
477 return Internal_isType(src, kUTTypeJPEG);
480 int IMG_isPNG(SDL_RWops *src)
482 return Internal_isType(src, kUTTypePNG);
485 // This isn't a public API function. Apple seems to be able to identify tga's.
486 int IMG_isTGA(SDL_RWops *src)
488 return Internal_isType(src, CFSTR("com.truevision.tga-image"));
491 int IMG_isTIF(SDL_RWops *src)
493 return Internal_isType(src, kUTTypeTIFF);
496 SDL_Surface* IMG_LoadCUR_RW(SDL_RWops *src)
498 /* FIXME: Is this a supported type? */
499 return LoadImageFromRWops(src, CFSTR("com.microsoft.cur"));
501 SDL_Surface* IMG_LoadICO_RW(SDL_RWops *src)
503 return LoadImageFromRWops(src, kUTTypeICO);
505 SDL_Surface* IMG_LoadBMP_RW(SDL_RWops *src)
507 return LoadImageFromRWops(src, kUTTypeBMP);
509 SDL_Surface* IMG_LoadGIF_RW(SDL_RWops *src)
511 return LoadImageFromRWops(src, kUTTypeGIF);
513 SDL_Surface* IMG_LoadJPG_RW(SDL_RWops *src)
515 return LoadImageFromRWops(src, kUTTypeJPEG);
517 SDL_Surface* IMG_LoadPNG_RW(SDL_RWops *src)
519 return LoadImageFromRWops(src, kUTTypePNG);
521 SDL_Surface* IMG_LoadTGA_RW(SDL_RWops *src)
523 return LoadImageFromRWops(src, CFSTR("com.truevision.tga-image"));
525 SDL_Surface* IMG_LoadTIF_RW(SDL_RWops *src)
527 return LoadImageFromRWops(src, kUTTypeTIFF);
530 // Apple provides both stream and file loading functions in ImageIO.
531 // Potentially, Apple can optimize for either case.
532 SDL_Surface* IMG_Load(const char *file)
534 SDL_Surface* sdl_surface = NULL;
536 sdl_surface = LoadImageFromFile(file);
537 if(NULL == sdl_surface)
539 // Either the file doesn't exist or ImageIO doesn't understand the format.
540 // For the latter case, fallback to the native SDL_image handlers.
541 SDL_RWops *src = SDL_RWFromFile(file, "rb");
542 char *ext = strrchr(file, '.');
547 /* The error message has been set in SDL_RWFromFile */
550 sdl_surface = IMG_LoadTyped_RW(src, 1, ext);