]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/libsdl-image/contrib/IMG_ImageIO.c
update
[l4.git] / l4 / pkg / libsdl-image / contrib / IMG_ImageIO.c
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).
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.
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
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
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
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.
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. */
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
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         if(NULL == image_ref)
161         {
162                 IMG_SetError("CGImageSourceCreateImageAtIndex() failed");
163         }
164         return image_ref;
165 }
166
167 static CFDictionaryRef CreateHintDictionary(CFStringRef uti_string_hint)
168 {
169         CFDictionaryRef hint_dictionary = NULL;
170
171         if(uti_string_hint != NULL)
172         {
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];
176                 
177                 the_keys[0] = kCGImageSourceTypeIdentifierHint;
178                 the_values[0] = uti_string_hint;
179                 
180                 // kCFTypeDictionaryKeyCallBacks or kCFCopyStringDictionaryKeyCallBacks?
181                 hint_dictionary = CFDictionaryCreate(NULL, (const void**)&the_keys, (const void**)&the_values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
182         }
183         return hint_dictionary;
184 }
185
186
187
188
189 static int Internal_isType(SDL_RWops* rw_ops, CFStringRef uti_string_to_test)
190 {
191         CGImageSourceRef image_source;
192         CFStringRef uti_type;
193         Boolean is_type;
194         
195         CFDictionaryRef hint_dictionary = NULL;
196         
197         hint_dictionary = CreateHintDictionary(uti_string_to_test);     
198         image_source = CreateCGImageSourceFromRWops(rw_ops, hint_dictionary);
199         
200         if(hint_dictionary != NULL)
201         {
202                 CFRelease(hint_dictionary);             
203         }
204         
205         if(NULL == image_source)
206         {
207                 return 0;
208         }
209         
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);
216         //      CFShow(uti_type);
217         
218         // Unsure if we really want conformance or equality
219         is_type = UTTypeConformsTo(uti_string_to_test, uti_type);
220         
221         CFRelease(image_source);
222         
223         return (int)is_type;
224 }
225
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)
228 {
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.
235          */
236
237         size_t w = CGImageGetWidth(image_ref);
238         size_t h = CGImageGetHeight(image_ref);
239         CGRect rect = {{0, 0}, {w, h}};
240
241         CGImageAlphaInfo alpha = CGImageGetAlphaInfo(image_ref);
242         //size_t bits_per_pixel = CGImageGetBitsPerPixel(image_ref);
243         size_t bits_per_component = 8;
244
245         SDL_Surface* surface;
246         Uint32 Amask;
247         Uint32 Rmask;
248         Uint32 Gmask;
249         Uint32 Bmask;
250
251         CGContextRef bitmap_context;
252         CGBitmapInfo bitmap_info;
253         CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();
254
255         if (alpha == kCGImageAlphaNone ||
256             alpha == kCGImageAlphaNoneSkipFirst ||
257             alpha == kCGImageAlphaNoneSkipLast) {
258                 bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host; /* XRGB */
259                 Amask = 0x00000000;
260         } else {
261                 /* kCGImageAlphaFirst isn't supported */
262                 //bitmap_info = kCGImageAlphaFirst | kCGBitmapByteOrder32Host; /* ARGB */
263                 bitmap_info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host; /* ARGB */
264                 Amask = 0xFF000000;
265         }
266
267         Rmask = 0x00FF0000;
268         Gmask = 0x0000FF00;
269         Bmask = 0x000000FF;
270
271         surface = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, Rmask, Gmask, Bmask, Amask);
272         if (surface)
273         {
274                 // Sets up a context to be drawn to with surface->pixels as the area to be drawn to
275                 bitmap_context = CGBitmapContextCreate(
276                                                                                                                         surface->pixels,
277                                                                                                                         surface->w,
278                                                                                                                         surface->h,
279                                                                                                                         bits_per_component,
280                                                                                                                         surface->pitch,
281                                                                                                                         color_space,
282                                                                                                                         bitmap_info
283                                                                                                                         );
284
285                 // Draws the image into the context's image_data
286                 CGContextDrawImage(bitmap_context, rect, image_ref);
287
288                 CGContextRelease(bitmap_context);
289
290                 // FIXME: Reverse the premultiplied alpha
291                 if ((bitmap_info & kCGBitmapAlphaInfoMask) == kCGImageAlphaPremultipliedFirst) {
292                         int i, j;
293                         Uint8 *p = (Uint8 *)surface->pixels;
294                         for (i = surface->h * surface->pitch/4; i--; ) {
295 #if __LITTLE_ENDIAN__
296                                 Uint8 A = p[3];
297                                 if (A) {
298                                         for (j = 0; j < 3; ++j) {
299                                                 p[j] = (p[j] * 255) / A;
300                                         }
301                                 }
302 #else
303                                 Uint8 A = p[0];
304                                 if (A) {
305                                         for (j = 1; j < 4; ++j) {
306                                                 p[j] = (p[j] * 255) / A;
307                                         }
308                                 }
309 #endif /* ENDIAN */
310                                 p += 4;
311                         }
312                 }
313         }
314
315         if (color_space)
316         {
317                 CGColorSpaceRelease(color_space);                       
318         }
319
320         return surface;
321 }
322
323
324 static SDL_Surface* LoadImageFromRWops(SDL_RWops* rw_ops, CFStringRef uti_string_hint)
325 {
326         SDL_Surface* sdl_surface;
327         CGImageSourceRef image_source;
328         CGImageRef image_ref = NULL;
329         CFDictionaryRef hint_dictionary = NULL;
330
331         hint_dictionary = CreateHintDictionary(uti_string_hint);
332         image_source = CreateCGImageSourceFromRWops(rw_ops, hint_dictionary);
333
334         if(hint_dictionary != NULL)
335         {
336                 CFRelease(hint_dictionary);             
337         }
338         
339         if(NULL == image_source)
340         {
341                 return NULL;
342         }
343         
344         image_ref = CreateCGImageFromCGImageSource(image_source);
345         CFRelease(image_source);
346
347         if(NULL == image_ref)
348         {
349                 return NULL;
350         }
351         
352         sdl_surface = Create_SDL_Surface_From_CGImage(image_ref);
353         CFRelease(image_ref);
354         return sdl_surface;
355         
356 }
357
358
359
360 static SDL_Surface* LoadImageFromFile(const char* file)
361 {
362         SDL_Surface* sdl_surface = NULL;
363         CGImageSourceRef image_source = NULL;
364         CGImageRef image_ref = NULL;
365         
366         // First ImageIO
367         image_source = CreateCGImageSourceFromFile(file);
368         
369         if(NULL == image_source)
370         {
371                 return NULL;
372         }
373         
374         image_ref = CreateCGImageFromCGImageSource(image_source);
375         CFRelease(image_source);
376         
377         if(NULL == image_ref)
378         {
379                 return NULL;
380         }
381         
382         sdl_surface = Create_SDL_Surface_From_CGImage(image_ref);
383         CFRelease(image_ref);
384         return sdl_surface;     
385 }
386
387 int IMG_InitJPG()
388 {
389         return 0;
390 }
391
392 void IMG_QuitJPG()
393 {
394 }
395
396 int IMG_InitPNG()
397 {
398         return 0;
399 }
400
401 void IMG_QuitPNG()
402 {
403 }
404
405 int IMG_InitTIF()
406 {
407         return 0;
408 }
409
410 void IMG_QuitTIF()
411 {
412 }
413
414 int IMG_isCUR(SDL_RWops *src)
415 {
416         /* FIXME: Is this a supported type? */
417         return Internal_isType(src, CFSTR("com.microsoft.cur"));
418 }
419
420 int IMG_isICO(SDL_RWops *src)
421 {
422         return Internal_isType(src, kUTTypeICO);
423 }
424
425 int IMG_isBMP(SDL_RWops *src)
426 {
427         return Internal_isType(src, kUTTypeBMP);
428 }
429
430 int IMG_isGIF(SDL_RWops *src)
431 {
432         return Internal_isType(src, kUTTypeGIF);
433 }
434
435 // Note: JPEG 2000 is kUTTypeJPEG2000
436 int IMG_isJPG(SDL_RWops *src)
437 {
438         return Internal_isType(src, kUTTypeJPEG);
439 }
440
441 int IMG_isPNG(SDL_RWops *src)
442 {
443         return Internal_isType(src, kUTTypePNG);
444 }
445
446 // This isn't a public API function. Apple seems to be able to identify tga's.
447 int IMG_isTGA(SDL_RWops *src)
448 {
449         return Internal_isType(src, CFSTR("com.truevision.tga-image"));
450 }
451
452 int IMG_isTIF(SDL_RWops *src)
453 {
454         return Internal_isType(src, kUTTypeTIFF);
455 }
456
457 SDL_Surface* IMG_LoadCUR_RW(SDL_RWops *src)
458 {
459         /* FIXME: Is this a supported type? */
460         return LoadImageFromRWops(src, CFSTR("com.microsoft.cur"));
461 }
462 SDL_Surface* IMG_LoadICO_RW(SDL_RWops *src)
463 {
464         return LoadImageFromRWops(src, kUTTypeICO);
465 }
466 SDL_Surface* IMG_LoadBMP_RW(SDL_RWops *src)
467 {
468         return LoadImageFromRWops(src, kUTTypeBMP);
469 }
470 SDL_Surface* IMG_LoadGIF_RW(SDL_RWops *src)
471 {
472         return LoadImageFromRWops(src, kUTTypeGIF);
473 }
474 SDL_Surface* IMG_LoadJPG_RW(SDL_RWops *src)
475 {
476         return LoadImageFromRWops(src, kUTTypeJPEG);
477 }
478 SDL_Surface* IMG_LoadPNG_RW(SDL_RWops *src)
479 {
480         return LoadImageFromRWops(src, kUTTypePNG);
481 }
482 SDL_Surface* IMG_LoadTGA_RW(SDL_RWops *src)
483 {
484         return LoadImageFromRWops(src, CFSTR("com.truevision.tga-image"));
485 }
486 SDL_Surface* IMG_LoadTIF_RW(SDL_RWops *src)
487 {
488         return LoadImageFromRWops(src, kUTTypeTIFF);
489 }
490
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)
494 {
495         SDL_Surface* sdl_surface = NULL;
496
497         sdl_surface = LoadImageFromFile(file);
498         if(NULL == sdl_surface)
499         {
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, '.');
504                 if(ext) {
505                         ext++;
506                 }
507                 if(!src) {
508                         /* The error message has been set in SDL_RWFromFile */
509                         return NULL;
510                 }
511                 sdl_surface = IMG_LoadTyped_RW(src, 1, ext);
512         }
513         return sdl_surface;
514 }
515
516 #endif /* defined(__APPLE__) && !defined(SDL_IMAGE_USE_COMMON_BACKEND) */