]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/libsdl-image/contrib/IMG_ImageIO.c
74d6e7b55dd85af0b1c7899199e306a425c3b06c
[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 #include "SDL_image.h"
10
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>
15
16 /**************************************************************
17  ***** Begin Callback functions for block reading *************
18  **************************************************************/
19
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)
23 {
24         return (size_t)SDL_RWread((struct SDL_RWops *)rwops_userdata, quartz_buffer, 1, the_count);
25 }
26
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)
30 {
31         // What should I put here? 
32         // I think the user and SDL_RWops controls closing, so I don't do anything.
33 }
34
35 static void MyProviderRewindCallback(void* rwops_userdata)
36 {
37         SDL_RWseek((struct SDL_RWops *)rwops_userdata, 0, RW_SEEK_SET);
38 }
39
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)
42 {
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);     
47 }
48 #else // CGDataProviderCreate was deprecated in 10.5
49 static void MyProviderSkipBytesCallback(void* rwops_userdata, size_t the_count)
50 {
51         SDL_RWseek((struct SDL_RWops *)rwops_userdata, the_count, RW_SEEK_CUR);
52 }
53 #endif
54
55
56 /**************************************************************
57  ***** End Callback functions for block reading ***************
58  **************************************************************/
59
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)
63 {
64         CGImageSourceRef source_ref;
65
66         // Similar to SDL_RWops, Apple has their own callbacks for dealing with data streams.
67         
68 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 // CGDataProviderCreateSequential was introduced in 10.5; CGDataProviderCreate is deprecated
69         CGDataProviderSequentialCallbacks provider_callbacks =
70         {
71         0,
72                 MyProviderGetBytesCallback,
73                 MyProviderSkipForwardBytesCallback,
74                 MyProviderRewindCallback,
75                 MyProviderReleaseInfoCallback
76         };
77         
78         CGDataProviderRef data_provider = CGDataProviderCreateSequential(rw_ops, &provider_callbacks);
79         
80         
81 #else // CGDataProviderCreate was deprecated in 10.5
82         
83         CGDataProviderCallbacks provider_callbacks =
84         {
85                 MyProviderGetBytesCallback,
86                 MyProviderSkipBytesCallback,
87                 MyProviderRewindCallback,
88                 MyProviderReleaseInfoCallback
89         };
90         
91         CGDataProviderRef data_provider = CGDataProviderCreate(rw_ops, &provider_callbacks);
92 #endif
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);
96         return source_ref;
97 }
98
99
100 /* Create a CGImageSourceRef from a file. */
101 /* Remember to CFRelease the created source when done. */
102 static CGImageSourceRef CreateCGImageSourceFromFile(const char* the_path)
103 {
104     CFURLRef the_url = NULL;
105     CGImageSourceRef source_ref = NULL;
106         CFStringRef cf_string = NULL;
107         
108         /* Create a CFString from a C string */
109         cf_string = CFStringCreateWithCString(
110                                                                                   NULL,
111                                                                                   the_path,
112                                                                                   kCFStringEncodingUTF8
113                                                                                   );
114         if(!cf_string)
115         {
116                 return NULL;
117         }
118         
119         /* Create a CFURL from a CFString */
120     the_url = CFURLCreateWithFileSystemPath(
121                                                                                         NULL, 
122                                                                                         cf_string,
123                                                                                         kCFURLPOSIXPathStyle,
124                                                                                         false
125                                                                                         );
126         
127         /* Don't need the CFString any more (error or not) */
128         CFRelease(cf_string);
129         
130         if(!the_url)
131         {
132                 return NULL;
133         }
134         
135         
136     source_ref = CGImageSourceCreateWithURL(the_url, NULL);
137         /* Don't need the URL any more (error or not) */
138         CFRelease(the_url);
139         
140         return source_ref;
141 }
142
143
144
145 static CGImageRef CreateCGImageFromCGImageSource(CGImageSourceRef image_source)
146 {
147         CGImageRef image_ref = NULL;
148         
149     if(NULL == image_source)
150         {
151                 return NULL;
152         }
153         
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);
157         return image_ref;
158 }
159
160 static CFDictionaryRef CreateHintDictionary(CFStringRef uti_string_hint)
161 {
162         CFDictionaryRef hint_dictionary = NULL;
163
164         if(uti_string_hint != NULL)
165         {
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];
169                 
170                 the_keys[0] = kCGImageSourceTypeIdentifierHint;
171                 the_values[0] = uti_string_hint;
172                 
173                 // kCFTypeDictionaryKeyCallBacks or kCFCopyStringDictionaryKeyCallBacks?
174                 hint_dictionary = CFDictionaryCreate(NULL, (const void**)&the_keys, (const void**)&the_values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
175         }
176         return hint_dictionary;
177 }
178
179
180
181
182 static int Internal_isType(SDL_RWops* rw_ops, CFStringRef uti_string_to_test)
183 {
184         CGImageSourceRef image_source;
185         CFStringRef uti_type;
186         Boolean is_type;
187         
188         CFDictionaryRef hint_dictionary = NULL;
189         
190         hint_dictionary = CreateHintDictionary(uti_string_to_test);     
191         image_source = CreateCGImageSourceFromRWops(rw_ops, hint_dictionary);
192         
193         if(hint_dictionary != NULL)
194         {
195                 CFRelease(hint_dictionary);             
196         }
197         
198         if(NULL == image_source)
199         {
200                 return 0;
201         }
202         
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);
209         //      CFShow(uti_type);
210         
211         // Unsure if we really want conformance or equality
212         is_type = UTTypeConformsTo(uti_string_to_test, uti_type);
213         
214         CFRelease(image_source);
215         
216         return (int)is_type;
217 }
218
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)
221 {
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.
228          */
229         
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}};
233         
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;
238         
239 //      CGImageAlphaInfo alpha_info = CGImageGetAlphaInfo(image_ref);
240         
241
242         SDL_Surface* sdl_surface = NULL;
243         Uint32 Rmask;
244         Uint32 Gmask;
245         Uint32 Bmask;
246         Uint32 Amask;
247
248         
249         CGContextRef bitmap_context = NULL;
250         
251         CGColorSpaceRef color_space = NULL;
252         CGBitmapInfo bitmap_info = CGImageGetBitmapInfo(image_ref);
253
254         
255         switch(bits_per_pixel)
256         {
257                 case 8:
258                 {
259                         bytes_per_row = the_width*4;
260                         //                              color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
261                         color_space = CGColorSpaceCreateDeviceRGB();
262                         //                              bitmap_info = kCGImageAlphaPremultipliedFirst;
263 #if __BIG_ENDIAN__
264                         bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Big; /* XRGB Big Endian */
265 #else
266                         bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little; /* XRGB Little Endian */
267 #endif
268
269                         Rmask = 0x00FF0000;
270                         Gmask = 0x0000FF00;
271                         Bmask = 0x000000FF;
272                         Amask = 0x00000000;
273
274                         sdl_surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
275                                                                                            the_width, the_height, 32, Rmask, Gmask, Bmask, Amask);
276
277
278                         
279                         break;
280                 }
281                 case 15:
282                 case 16:
283                 {
284                         bytes_per_row = the_width*4;
285
286                         color_space = CGColorSpaceCreateDeviceRGB();
287
288 #if __BIG_ENDIAN__
289                         bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Big; /* XRGB Big Endian */
290 #else
291                         bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little; /* XRGB Little Endian */
292 #endif
293                         Rmask = 0x00FF0000;
294                         Gmask = 0x0000FF00;
295                         Bmask = 0x000000FF;
296                         Amask = 0x00000000;
297
298
299                         sdl_surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
300                                                                                            the_width, the_height, 32, Rmask, Gmask, Bmask, Amask);
301
302                         break;
303                 }
304                 case 24:
305                 {
306                         bytes_per_row = the_width*4;
307                         //                      color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
308                         color_space = CGColorSpaceCreateDeviceRGB();
309                         //                      bitmap_info = kCGImageAlphaNone;
310 #if __BIG_ENDIAN__
311                         bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Big; /* XRGB Big Endian */
312 #else
313                         bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little; /* XRGB Little Endian */
314 #endif
315                         Rmask = 0x00FF0000;
316                         Gmask = 0x0000FF00;
317                         Bmask = 0x000000FF;
318                         Amask = 0x00000000;
319
320                         sdl_surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
321                                                                                            the_width, the_height, 32, Rmask, Gmask, Bmask, Amask);
322
323                         break;
324                 }
325                 case 32:
326                 {
327                                                 
328                         bytes_per_row = the_width*4;
329                         //                      color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
330                         color_space = CGColorSpaceCreateDeviceRGB();
331                         //                      bitmap_info = kCGImageAlphaPremultipliedFirst;
332 #if __BIG_ENDIAN__
333                         bitmap_info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Big; /* XRGB Big Endian */
334 #else
335                         bitmap_info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little; /* XRGB Little Endian */
336 #endif 
337
338                         Amask = 0xFF000000;
339                         Rmask = 0x00FF0000;
340                         Gmask = 0x0000FF00;
341                         Bmask = 0x000000FF;
342
343                         sdl_surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
344                                                                                            the_width, the_height, 32, Rmask, Gmask, Bmask, Amask);
345                         break;
346                 }
347                 default:
348                 {
349             sdl_surface = NULL;
350             break;
351                 }
352                         
353         }
354
355         if(NULL == sdl_surface)
356         {
357                 if(color_space != NULL)
358                 {
359                         CGColorSpaceRelease(color_space);                       
360                 }
361                 return NULL;
362         }
363
364
365         // Sets up a context to be drawn to with sdl_surface->pixels as the area to be drawn to
366         bitmap_context = CGBitmapContextCreate(
367                                                                                                                 sdl_surface->pixels,
368                                                                                                                 the_width,
369                                                                                                                 the_height,
370                                                                                                                 bits_per_component,
371                                                                                                                 bytes_per_row,
372                                                                                                                 color_space,
373                                                                                                                 bitmap_info
374                                                                                                                 );
375         
376         // Draws the image into the context's image_data
377         CGContextDrawImage(bitmap_context, the_rect, image_ref);
378         
379         CGContextRelease(bitmap_context);
380         CGColorSpaceRelease(color_space);
381         
382         return sdl_surface;
383         
384         
385         
386 }
387
388
389 static SDL_Surface* LoadImageFromRWops(SDL_RWops* rw_ops, CFStringRef uti_string_hint)
390 {
391         SDL_Surface* sdl_surface;
392         CGImageSourceRef image_source;
393         CGImageRef image_ref = NULL;
394         CFDictionaryRef hint_dictionary = NULL;
395
396         hint_dictionary = CreateHintDictionary(uti_string_hint);
397         image_source = CreateCGImageSourceFromRWops(rw_ops, hint_dictionary);
398
399         if(hint_dictionary != NULL)
400         {
401                 CFRelease(hint_dictionary);             
402         }
403         
404         if(NULL == image_source)
405         {
406                 return NULL;
407         }
408         
409         image_ref = CreateCGImageFromCGImageSource(image_source);
410         CFRelease(image_source);
411
412         if(NULL == image_ref)
413         {
414                 return NULL;
415         }
416         
417         sdl_surface = Create_SDL_Surface_From_CGImage(image_ref);
418         CFRelease(image_ref);
419         return sdl_surface;
420         
421 }
422
423
424
425 static SDL_Surface* LoadImageFromFile(const char* file)
426 {
427         SDL_Surface* sdl_surface = NULL;
428         CGImageSourceRef image_source = NULL;
429         CGImageRef image_ref = NULL;
430         
431         // First ImageIO
432         image_source = CreateCGImageSourceFromFile(file);
433         
434         if(NULL == image_source)
435         {
436                 return NULL;
437         }
438         
439         image_ref = CreateCGImageFromCGImageSource(image_source);
440         CFRelease(image_source);
441         
442         if(NULL == image_ref)
443         {
444                 return NULL;
445         }
446         
447         sdl_surface = Create_SDL_Surface_From_CGImage(image_ref);
448         CFRelease(image_ref);
449         return sdl_surface;     
450 }
451
452
453 int IMG_isCUR(SDL_RWops *src)
454 {
455         /* FIXME: Is this a supported type? */
456         return Internal_isType(src, CFSTR("com.microsoft.cur"));
457 }
458
459 int IMG_isICO(SDL_RWops *src)
460 {
461         return Internal_isType(src, kUTTypeICO);
462 }
463
464 int IMG_isBMP(SDL_RWops *src)
465 {
466         return Internal_isType(src, kUTTypeBMP);
467 }
468
469 int IMG_isGIF(SDL_RWops *src)
470 {
471         return Internal_isType(src, kUTTypeGIF);
472 }
473
474 // Note: JPEG 2000 is kUTTypeJPEG2000
475 int IMG_isJPG(SDL_RWops *src)
476 {
477         return Internal_isType(src, kUTTypeJPEG);
478 }
479
480 int IMG_isPNG(SDL_RWops *src)
481 {
482         return Internal_isType(src, kUTTypePNG);
483 }
484
485 // This isn't a public API function. Apple seems to be able to identify tga's.
486 int IMG_isTGA(SDL_RWops *src)
487 {
488         return Internal_isType(src, CFSTR("com.truevision.tga-image"));
489 }
490
491 int IMG_isTIF(SDL_RWops *src)
492 {
493         return Internal_isType(src, kUTTypeTIFF);
494 }
495
496 SDL_Surface* IMG_LoadCUR_RW(SDL_RWops *src)
497 {
498         /* FIXME: Is this a supported type? */
499         return LoadImageFromRWops(src, CFSTR("com.microsoft.cur"));
500 }
501 SDL_Surface* IMG_LoadICO_RW(SDL_RWops *src)
502 {
503         return LoadImageFromRWops(src, kUTTypeICO);
504 }
505 SDL_Surface* IMG_LoadBMP_RW(SDL_RWops *src)
506 {
507         return LoadImageFromRWops(src, kUTTypeBMP);
508 }
509 SDL_Surface* IMG_LoadGIF_RW(SDL_RWops *src)
510 {
511         return LoadImageFromRWops(src, kUTTypeGIF);
512 }
513 SDL_Surface* IMG_LoadJPG_RW(SDL_RWops *src)
514 {
515         return LoadImageFromRWops(src, kUTTypeJPEG);
516 }
517 SDL_Surface* IMG_LoadPNG_RW(SDL_RWops *src)
518 {
519         return LoadImageFromRWops(src, kUTTypePNG);
520 }
521 SDL_Surface* IMG_LoadTGA_RW(SDL_RWops *src)
522 {
523         return LoadImageFromRWops(src, CFSTR("com.truevision.tga-image"));
524 }
525 SDL_Surface* IMG_LoadTIF_RW(SDL_RWops *src)
526 {
527         return LoadImageFromRWops(src, kUTTypeTIFF);
528 }
529
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)
533 {
534         SDL_Surface* sdl_surface = NULL;
535
536         sdl_surface = LoadImageFromFile(file);
537         if(NULL == sdl_surface)
538         {
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, '.');
543                 if(ext) {
544                         ext++;
545                 }
546                 if(!src) {
547                         /* The error message has been set in SDL_RWFromFile */
548                         return NULL;
549                 }
550                 sdl_surface = IMG_LoadTyped_RW(src, 1, ext);
551         }
552         return sdl_surface;
553 }
554