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 // 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>
18
19 /**************************************************************
20 ***** Begin Callback functions for block reading *************
21 **************************************************************/
22
23 // This callback reads some bytes from an SDL_rwops and copies it
24 // to a Quartz buffer (supplied by Apple framework).
MyProviderGetBytesCallback(void * rwops_userdata,void * quartz_buffer,size_t the_count)25 static size_t MyProviderGetBytesCallback(void* rwops_userdata, void* quartz_buffer, size_t the_count)
26 {
27 return (size_t)SDL_RWread((struct SDL_RWops *)rwops_userdata, quartz_buffer, 1, the_count);
28 }
29
30 // This callback is triggered when the data provider is released
31 // so you can clean up any resources.
MyProviderReleaseInfoCallback(void * rwops_userdata)32 static void MyProviderReleaseInfoCallback(void* rwops_userdata)
33 {
34 // What should I put here?
35 // I think the user and SDL_RWops controls closing, so I don't do anything.
36 }
37
MyProviderRewindCallback(void * rwops_userdata)38 static void MyProviderRewindCallback(void* rwops_userdata)
39 {
40 SDL_RWseek((struct SDL_RWops *)rwops_userdata, 0, RW_SEEK_SET);
41 }
42
43 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 // CGDataProviderCreateSequential was introduced in 10.5; CGDataProviderCreate is deprecated
MyProviderSkipForwardBytesCallback(void * rwops_userdata,off_t the_count)44 off_t MyProviderSkipForwardBytesCallback(void* rwops_userdata, off_t the_count)
45 {
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);
50 }
51 #else // CGDataProviderCreate was deprecated in 10.5
MyProviderSkipBytesCallback(void * rwops_userdata,size_t the_count)52 static void MyProviderSkipBytesCallback(void* rwops_userdata, size_t the_count)
53 {
54 SDL_RWseek((struct SDL_RWops *)rwops_userdata, the_count, RW_SEEK_CUR);
55 }
56 #endif
57
58
59 /**************************************************************
60 ***** End Callback functions for block reading ***************
61 **************************************************************/
62
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.
CreateCGImageSourceFromRWops(SDL_RWops * rw_ops,CFDictionaryRef hints_and_options)65 static CGImageSourceRef CreateCGImageSourceFromRWops(SDL_RWops* rw_ops, CFDictionaryRef hints_and_options)
66 {
67 CGImageSourceRef source_ref;
68
69 // Similar to SDL_RWops, Apple has their own callbacks for dealing with data streams.
70
71 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 // CGDataProviderCreateSequential was introduced in 10.5; CGDataProviderCreate is deprecated
72 CGDataProviderSequentialCallbacks provider_callbacks =
73 {
74 0,
75 MyProviderGetBytesCallback,
76 MyProviderSkipForwardBytesCallback,
77 MyProviderRewindCallback,
78 MyProviderReleaseInfoCallback
79 };
80
81 CGDataProviderRef data_provider = CGDataProviderCreateSequential(rw_ops, &provider_callbacks);
82
83
84 #else // CGDataProviderCreate was deprecated in 10.5
85
86 CGDataProviderCallbacks provider_callbacks =
87 {
88 MyProviderGetBytesCallback,
89 MyProviderSkipBytesCallback,
90 MyProviderRewindCallback,
91 MyProviderReleaseInfoCallback
92 };
93
94 CGDataProviderRef data_provider = CGDataProviderCreate(rw_ops, &provider_callbacks);
95 #endif
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);
99 return source_ref;
100 }
101
102
103 /* Create a CGImageSourceRef from a file. */
104 /* Remember to CFRelease the created source when done. */
CreateCGImageSourceFromFile(const char * the_path)105 static CGImageSourceRef CreateCGImageSourceFromFile(const char* the_path)
106 {
107 CFURLRef the_url = NULL;
108 CGImageSourceRef source_ref = NULL;
109 CFStringRef cf_string = NULL;
110
111 /* Create a CFString from a C string */
112 cf_string = CFStringCreateWithCString(
113 NULL,
114 the_path,
115 kCFStringEncodingUTF8
116 );
117 if(!cf_string)
118 {
119 return NULL;
120 }
121
122 /* Create a CFURL from a CFString */
123 the_url = CFURLCreateWithFileSystemPath(
124 NULL,
125 cf_string,
126 kCFURLPOSIXPathStyle,
127 false
128 );
129
130 /* Don't need the CFString any more (error or not) */
131 CFRelease(cf_string);
132
133 if(!the_url)
134 {
135 return NULL;
136 }
137
138
139 source_ref = CGImageSourceCreateWithURL(the_url, NULL);
140 /* Don't need the URL any more (error or not) */
141 CFRelease(the_url);
142
143 return source_ref;
144 }
145
146
147
CreateCGImageFromCGImageSource(CGImageSourceRef image_source)148 static CGImageRef CreateCGImageFromCGImageSource(CGImageSourceRef image_source)
149 {
150 CGImageRef image_ref = NULL;
151
152 if(NULL == image_source)
153 {
154 return NULL;
155 }
156
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 return image_ref;
161 }
162
CreateHintDictionary(CFStringRef uti_string_hint)163 static CFDictionaryRef CreateHintDictionary(CFStringRef uti_string_hint)
164 {
165 CFDictionaryRef hint_dictionary = NULL;
166
167 if(uti_string_hint != NULL)
168 {
169 // Do a bunch of work to setup a CFDictionary containing the jpeg compression properties.
170 CFStringRef the_keys[1];
171 CFStringRef the_values[1];
172
173 the_keys[0] = kCGImageSourceTypeIdentifierHint;
174 the_values[0] = uti_string_hint;
175
176 // kCFTypeDictionaryKeyCallBacks or kCFCopyStringDictionaryKeyCallBacks?
177 hint_dictionary = CFDictionaryCreate(NULL, (const void**)&the_keys, (const void**)&the_values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
178 }
179 return hint_dictionary;
180 }
181
182
183
184
Internal_isType(SDL_RWops * rw_ops,CFStringRef uti_string_to_test)185 static int Internal_isType(SDL_RWops* rw_ops, CFStringRef uti_string_to_test)
186 {
187 CGImageSourceRef image_source;
188 CFStringRef uti_type;
189 Boolean is_type;
190
191 CFDictionaryRef hint_dictionary = NULL;
192
193 hint_dictionary = CreateHintDictionary(uti_string_to_test);
194 image_source = CreateCGImageSourceFromRWops(rw_ops, hint_dictionary);
195
196 if(hint_dictionary != NULL)
197 {
198 CFRelease(hint_dictionary);
199 }
200
201 if(NULL == image_source)
202 {
203 return 0;
204 }
205
206 // This will get the UTI of the container, not the image itself.
207 // Under most cases, this won't be a problem.
208 // But if a person passes an icon file which contains a bmp,
209 // the format will be of the icon file.
210 // But I think the main SDL_image codebase has this same problem so I'm not going to worry about it.
211 uti_type = CGImageSourceGetType(image_source);
212 // CFShow(uti_type);
213
214 // Unsure if we really want conformance or equality
215 is_type = UTTypeConformsTo(uti_string_to_test, uti_type);
216
217 CFRelease(image_source);
218
219 return (int)is_type;
220 }
221
222 // Once we have our image, we need to get it into an SDL_Surface
Create_SDL_Surface_From_CGImage(CGImageRef image_ref)223 static SDL_Surface* Create_SDL_Surface_From_CGImage(CGImageRef image_ref)
224 {
225 /* This code is adapted from Apple's Documentation found here:
226 * http://developer.apple.com/documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/index.html
227 * Listing 9-4††Using a Quartz image as a texture source.
228 * Unfortunately, this guide doesn't show what to do about
229 * non-RGBA image formats so I'm making the rest up.
230 * All this code should be scrutinized.
231 */
232
233 size_t w = CGImageGetWidth(image_ref);
234 size_t h = CGImageGetHeight(image_ref);
235 CGRect rect = {{0, 0}, {w, h}};
236
237 CGImageAlphaInfo alpha = CGImageGetAlphaInfo(image_ref);
238 //size_t bits_per_pixel = CGImageGetBitsPerPixel(image_ref);
239 size_t bits_per_component = 8;
240
241 SDL_Surface* surface;
242 Uint32 Amask;
243 Uint32 Rmask;
244 Uint32 Gmask;
245 Uint32 Bmask;
246
247 CGContextRef bitmap_context;
248 CGBitmapInfo bitmap_info;
249 CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();
250
251 if (alpha == kCGImageAlphaNone ||
252 alpha == kCGImageAlphaNoneSkipFirst ||
253 alpha == kCGImageAlphaNoneSkipLast) {
254 bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host; /* XRGB */
255 Amask = 0x00000000;
256 } else {
257 /* kCGImageAlphaFirst isn't supported */
258 //bitmap_info = kCGImageAlphaFirst | kCGBitmapByteOrder32Host; /* ARGB */
259 bitmap_info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host; /* ARGB */
260 Amask = 0xFF000000;
261 }
262
263 Rmask = 0x00FF0000;
264 Gmask = 0x0000FF00;
265 Bmask = 0x000000FF;
266
267 surface = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, Rmask, Gmask, Bmask, Amask);
268 if (surface)
269 {
270 // Sets up a context to be drawn to with surface->pixels as the area to be drawn to
271 bitmap_context = CGBitmapContextCreate(
272 surface->pixels,
273 surface->w,
274 surface->h,
275 bits_per_component,
276 surface->pitch,
277 color_space,
278 bitmap_info
279 );
280
281 // Draws the image into the context's image_data
282 CGContextDrawImage(bitmap_context, rect, image_ref);
283
284 CGContextRelease(bitmap_context);
285
286 // FIXME: Reverse the premultiplied alpha
287 if ((bitmap_info & kCGBitmapAlphaInfoMask) == kCGImageAlphaPremultipliedFirst) {
288 int i, j;
289 Uint8 *p = (Uint8 *)surface->pixels;
290 for (i = surface->h * surface->pitch/4; i--; ) {
291 #if __LITTLE_ENDIAN__
292 Uint8 A = p[3];
293 if (A) {
294 for (j = 0; j < 3; ++j) {
295 p[j] = (p[j] * 255) / A;
296 }
297 }
298 #else
299 Uint8 A = p[0];
300 if (A) {
301 for (j = 1; j < 4; ++j) {
302 p[j] = (p[j] * 255) / A;
303 }
304 }
305 #endif /* ENDIAN */
306 p += 4;
307 }
308 }
309 }
310
311 if (color_space)
312 {
313 CGColorSpaceRelease(color_space);
314 }
315
316 return surface;
317 }
318
319
LoadImageFromRWops(SDL_RWops * rw_ops,CFStringRef uti_string_hint)320 static SDL_Surface* LoadImageFromRWops(SDL_RWops* rw_ops, CFStringRef uti_string_hint)
321 {
322 SDL_Surface* sdl_surface;
323 CGImageSourceRef image_source;
324 CGImageRef image_ref = NULL;
325 CFDictionaryRef hint_dictionary = NULL;
326
327 hint_dictionary = CreateHintDictionary(uti_string_hint);
328 image_source = CreateCGImageSourceFromRWops(rw_ops, hint_dictionary);
329
330 if(hint_dictionary != NULL)
331 {
332 CFRelease(hint_dictionary);
333 }
334
335 if(NULL == image_source)
336 {
337 return NULL;
338 }
339
340 image_ref = CreateCGImageFromCGImageSource(image_source);
341 CFRelease(image_source);
342
343 if(NULL == image_ref)
344 {
345 return NULL;
346 }
347
348 sdl_surface = Create_SDL_Surface_From_CGImage(image_ref);
349 CFRelease(image_ref);
350 return sdl_surface;
351
352 }
353
354
355
LoadImageFromFile(const char * file)356 static SDL_Surface* LoadImageFromFile(const char* file)
357 {
358 SDL_Surface* sdl_surface = NULL;
359 CGImageSourceRef image_source = NULL;
360 CGImageRef image_ref = NULL;
361
362 // First ImageIO
363 image_source = CreateCGImageSourceFromFile(file);
364
365 if(NULL == image_source)
366 {
367 return NULL;
368 }
369
370 image_ref = CreateCGImageFromCGImageSource(image_source);
371 CFRelease(image_source);
372
373 if(NULL == image_ref)
374 {
375 return NULL;
376 }
377
378 sdl_surface = Create_SDL_Surface_From_CGImage(image_ref);
379 CFRelease(image_ref);
380 return sdl_surface;
381 }
382
IMG_InitJPG()383 int IMG_InitJPG()
384 {
385 return 0;
386 }
387
IMG_QuitJPG()388 void IMG_QuitJPG()
389 {
390 }
391
IMG_InitPNG()392 int IMG_InitPNG()
393 {
394 return 0;
395 }
396
IMG_QuitPNG()397 void IMG_QuitPNG()
398 {
399 }
400
IMG_InitTIF()401 int IMG_InitTIF()
402 {
403 return 0;
404 }
405
IMG_QuitTIF()406 void IMG_QuitTIF()
407 {
408 }
409
IMG_isCUR(SDL_RWops * src)410 int IMG_isCUR(SDL_RWops *src)
411 {
412 /* FIXME: Is this a supported type? */
413 return Internal_isType(src, CFSTR("com.microsoft.cur"));
414 }
415
IMG_isICO(SDL_RWops * src)416 int IMG_isICO(SDL_RWops *src)
417 {
418 return Internal_isType(src, kUTTypeICO);
419 }
420
IMG_isBMP(SDL_RWops * src)421 int IMG_isBMP(SDL_RWops *src)
422 {
423 return Internal_isType(src, kUTTypeBMP);
424 }
425
IMG_isGIF(SDL_RWops * src)426 int IMG_isGIF(SDL_RWops *src)
427 {
428 return Internal_isType(src, kUTTypeGIF);
429 }
430
431 // Note: JPEG 2000 is kUTTypeJPEG2000
IMG_isJPG(SDL_RWops * src)432 int IMG_isJPG(SDL_RWops *src)
433 {
434 return Internal_isType(src, kUTTypeJPEG);
435 }
436
IMG_isPNG(SDL_RWops * src)437 int IMG_isPNG(SDL_RWops *src)
438 {
439 return Internal_isType(src, kUTTypePNG);
440 }
441
442 // This isn't a public API function. Apple seems to be able to identify tga's.
IMG_isTGA(SDL_RWops * src)443 int IMG_isTGA(SDL_RWops *src)
444 {
445 return Internal_isType(src, CFSTR("com.truevision.tga-image"));
446 }
447
IMG_isTIF(SDL_RWops * src)448 int IMG_isTIF(SDL_RWops *src)
449 {
450 return Internal_isType(src, kUTTypeTIFF);
451 }
452
IMG_LoadCUR_RW(SDL_RWops * src)453 SDL_Surface* IMG_LoadCUR_RW(SDL_RWops *src)
454 {
455 /* FIXME: Is this a supported type? */
456 return LoadImageFromRWops(src, CFSTR("com.microsoft.cur"));
457 }
IMG_LoadICO_RW(SDL_RWops * src)458 SDL_Surface* IMG_LoadICO_RW(SDL_RWops *src)
459 {
460 return LoadImageFromRWops(src, kUTTypeICO);
461 }
IMG_LoadBMP_RW(SDL_RWops * src)462 SDL_Surface* IMG_LoadBMP_RW(SDL_RWops *src)
463 {
464 return LoadImageFromRWops(src, kUTTypeBMP);
465 }
IMG_LoadGIF_RW(SDL_RWops * src)466 SDL_Surface* IMG_LoadGIF_RW(SDL_RWops *src)
467 {
468 return LoadImageFromRWops(src, kUTTypeGIF);
469 }
IMG_LoadJPG_RW(SDL_RWops * src)470 SDL_Surface* IMG_LoadJPG_RW(SDL_RWops *src)
471 {
472 return LoadImageFromRWops(src, kUTTypeJPEG);
473 }
IMG_LoadPNG_RW(SDL_RWops * src)474 SDL_Surface* IMG_LoadPNG_RW(SDL_RWops *src)
475 {
476 return LoadImageFromRWops(src, kUTTypePNG);
477 }
IMG_LoadTGA_RW(SDL_RWops * src)478 SDL_Surface* IMG_LoadTGA_RW(SDL_RWops *src)
479 {
480 return LoadImageFromRWops(src, CFSTR("com.truevision.tga-image"));
481 }
IMG_LoadTIF_RW(SDL_RWops * src)482 SDL_Surface* IMG_LoadTIF_RW(SDL_RWops *src)
483 {
484 return LoadImageFromRWops(src, kUTTypeTIFF);
485 }
486
487 // Apple provides both stream and file loading functions in ImageIO.
488 // Potentially, Apple can optimize for either case.
IMG_Load(const char * file)489 SDL_Surface* IMG_Load(const char *file)
490 {
491 SDL_Surface* sdl_surface = NULL;
492
493 sdl_surface = LoadImageFromFile(file);
494 if(NULL == sdl_surface)
495 {
496 // Either the file doesn't exist or ImageIO doesn't understand the format.
497 // For the latter case, fallback to the native SDL_image handlers.
498 SDL_RWops *src = SDL_RWFromFile(file, "rb");
499 char *ext = strrchr(file, '.');
500 if(ext) {
501 ext++;
502 }
503 if(!src) {
504 /* The error message has been set in SDL_RWFromFile */
505 return NULL;
506 }
507 sdl_surface = IMG_LoadTyped_RW(src, 1, ext);
508 }
509 return sdl_surface;
510 }
511
512 #endif /* defined(__APPLE__) && !defined(SDL_IMAGE_USE_COMMON_BACKEND) */
513