root/OpenSceneGraph/trunk/src/osgPlugins/imageio/ReaderWriterImageIO.cpp @ 10854

Revision 10854, 45.9 kB (checked in by robert, 5 years ago)

Converted tabs to four space

RevLine 
[9880]1// Copyright Eric Wing
2// This plugin is the bridge to OS X's ImageIO framework
3// which provides access to all of Apple's supported image types.
4// This plugin plus the QTKit plugin obsoletes the old QuickTime plugin.
5// This requires 10.4+. (The old QuickTime plugin will not support 64-bit.)
6
7
8// Needs testing, especially in:
9// 8-bits per pixel (256 color vs GL_ALPHA, and what about GL_LUMINANCE)?
10// 16-bits per pixel (is GL_LUMINANCE_ALPHA a safe assumption?)
11// Non-power-of-two textures (especially odd sizes)
12// istream code path
13// ostream code path
14// write image, especially GL_LUMINANCE and GL_ALPHA paths and image formats other than PNG/JPEG
15
16// Enhancements needed:
17// Way to provide image type hint to ImageIO calls (via CFDictionary),
18// probably especially important for istream which lacks extension information.
19// Is there information we can use in the OSG options parameter?
20
21// For ImageIO framework and also LaunchServices framework (for UTIs)
22#include <ApplicationServices/ApplicationServices.h>
23// For the vImage framework (part of the Accerlate framework)
24#include <Accelerate/Accelerate.h>
25
26// Used because CGDataProviderCreate became deprecated in 10.5
27#include <AvailabilityMacros.h>
28
29#include <osg/GL>
30#include <osg/Notify>
31#include <osg/Image>
32
33#include <osgDB/Registry>
34#include <osgDB/FileNameUtils>
35#include <osgDB/FileUtils>
36
37#include <sstream> // for istream
38#include <iostream> // for ios::
39
40
41/**************************************************************
42 ***** Begin Callback functions for istream block reading *****
43 **************************************************************/
44 
45// This callback reads some bytes from an istream and copies it
46// to a Quartz buffer (supplied by Apple framework).
47size_t MyProviderGetBytesCallback(void* istream_userdata, void* quartz_buffer, size_t the_count)
48{
[10854]49    std::istream* the_istream = (std::istream*)istream_userdata;
50    the_istream->read((char*)quartz_buffer, the_count);
51    return the_istream->gcount(); // return the actual number of bytes read
[9880]52}
53
54// This callback is triggered when the data provider is released
55// so you can clean up any resources.
56void MyProviderReleaseInfoCallback(void* istream_userdata)
57{
[10854]58    // What should I put here? Do I need to close the istream?
59    // The png and tga don't seem to.
60//    std::istream* the_istream = (std::istream*)istream_userdata;
[9880]61}
62
63void MyProviderRewindCallback(void* istream_userdata)
64{
[10854]65    std::istream* the_istream = (std::istream*)istream_userdata;
66    the_istream->seekg(0, std::ios::beg);
[9880]67}
68
69#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 // CGDataProviderCreateSequential was introduced in 10.5; CGDataProviderCreate is deprecated
70off_t MyProviderSkipForwardBytesCallback(void* istream_userdata, off_t the_count)
71{
[10854]72    std::istream* the_istream = (std::istream*)istream_userdata;
[9880]73    off_t start_position = the_istream->tellg();
[10854]74    the_istream->seekg(the_count, std::ios::cur);
[9880]75    off_t end_position = the_istream->tellg();
76    return (end_position - start_position);
77}
78#else // CGDataProviderCreate was deprecated in 10.5
79void MyProviderSkipBytesCallback(void* istream_userdata, size_t the_count)
80{
[10854]81    std::istream* the_istream = (std::istream*)istream_userdata;
82    the_istream->seekg(the_count, std::ios::cur);
[9880]83}
84#endif
85
86/**************************************************************
87***** End Callback functions for istream block reading ********
88**************************************************************/
89
90
91/**************************************************************
92***** Begin Callback functions for ostream block writing ******
93**************************************************************/
94size_t MyConsumerPutBytesCallback(void* ostream_userdata, const void* quartz_buffer, size_t the_count)
95{
[10854]96    std::ostream* the_ostream = (std::ostream*)ostream_userdata;
97    the_ostream->write((char*)quartz_buffer, the_count);
98    // Don't know how to get number of bytes actually written, so
99    // just returning the_count.
100    return the_count;
[9880]101}
102
103void MyConsumerReleaseInfoCallback(void* ostream_userdata)
104{
[10854]105    std::ostream* the_ostream = (std::ostream*)ostream_userdata;
106    the_ostream->flush();
[9880]107}
108/**************************************************************
109***** End Callback functions for ostream block writing ********
110**************************************************************/
111
112
113/**************************************************************
114***** Begin Support functions for reading (stream and file) ***
115**************************************************************/
116
117/* Create a CGImageSourceRef from raw data */
118CGImageRef CreateCGImageFromDataStream(std::istream& fin)
119{
120    CGImageRef image_ref = NULL;
121    CGImageSourceRef source_ref;
[10854]122    /* The easy way would be to use CGImageSourceCreateWithData,
123     * but this presumes you have a known fixed-length buffer of data.
124     * The istream makes this harder to know, so we use the ProviderCallbacks APIs
125    CFDataRef the_cf_data = CFDataCreateWithBytesNoCopy(
126        kCFAllocatorDefault,
127        (const UInt8*)the_data,
128        CFIndex length,
129        kCFAllocatorNull // do not free data buffer, must do it yourself
130    );
[9880]131    source_ref = CGImageSourceCreateWithData(the_cf_data, NULL);
132*/
133
134#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 // CGDataProviderCreateSequential was introduced in 10.5; CGDataProviderCreate is deprecated
[10854]135    CGDataProviderSequentialCallbacks provider_callbacks =
136    {
[9880]137        0,
[10854]138        MyProviderGetBytesCallback,
139        MyProviderSkipForwardBytesCallback,
140        MyProviderRewindCallback,
141        MyProviderReleaseInfoCallback
142    };
143   
144    CGDataProviderRef data_provider = CGDataProviderCreateSequential(&fin, &provider_callbacks);
[9880]145
146
147#else // CGDataProviderCreate was deprecated in 10.5
148
[10854]149    CGDataProviderCallbacks provider_callbacks =
150    {
151        MyProviderGetBytesCallback,
152        MyProviderSkipBytesCallback,
153        MyProviderRewindCallback,
154        MyProviderReleaseInfoCallback
155    };
156   
157    CGDataProviderRef data_provider = CGDataProviderCreate(&fin, &provider_callbacks);
[9880]158#endif
[10854]159    // If we had a way of hinting at what the data type is, we could
160    // pass this hint in the second parameter.
161    source_ref = CGImageSourceCreateWithDataProvider(data_provider, NULL);
[9880]162
[10854]163    CGDataProviderRelease(data_provider);
[9880]164
165
166    if(!source_ref)
[10854]167    {
168        return NULL;
169    }
[9880]170
[10854]171    image_ref = CGImageSourceCreateImageAtIndex(source_ref, 0, NULL);
172   
173    /* Don't need the SourceRef any more (error or not) */
174    CFRelease(source_ref);
[9880]175
[10854]176    return image_ref;
[9880]177}
178
179
180/* Create a CGImageSourceRef from a file. */
181/* Remember to CFRelease the created image when done. */
182CGImageRef CreateCGImageFromFile(const char* the_path)
183{
184    CFURLRef the_url = NULL;
185    CGImageRef image_ref = NULL;
186    CGImageSourceRef source_ref = NULL;
[10854]187    CFStringRef cf_string = NULL;
[9880]188
[10854]189    /* Create a CFString from a C string */
190    cf_string = CFStringCreateWithCString(
191        NULL,
192        the_path,
193        kCFStringEncodingUTF8
194    );
195    if(!cf_string)
196    {
197        osg::notify(osg::WARN) << "CreateCGImageFromFile :: could not create CCFSTring" << std::endl;
[9880]198        return NULL;
[10854]199    }
[9880]200 
[10854]201    /* Create a CFURL from a CFString */
[9880]202    the_url = CFURLCreateWithFileSystemPath(
[10854]203        NULL,
204        cf_string,
205        kCFURLPOSIXPathStyle,
206        false
207    );
[9880]208
[10854]209    /* Don't need the CFString any more (error or not) */
210    CFRelease(cf_string);
211   
212    if(!the_url)
213    {
214        osg::notify(osg::WARN) << "CreateCGImageFromFile :: could not create CFUrl" << std::endl;
[9880]215        return NULL;
[10854]216    }
[9880]217
[10854]218   
[9880]219    source_ref = CGImageSourceCreateWithURL(the_url, NULL);
[10854]220    /* Don't need the URL any more (error or not) */
221    CFRelease(the_url);
[9880]222
223    if(!source_ref)
[10854]224    {
225        osg::notify(osg::WARN) << "CreateCGImageFromFile :: could not create ImageSource" << std::endl;
[9880]226        return NULL;
227    }
228
[10854]229    // Get the first item in the image source (some image formats may
230    // contain multiple items).
231    image_ref = CGImageSourceCreateImageAtIndex(source_ref, 0, NULL);
232    if (!image_ref) {
[9880]233        osg::notify(osg::WARN) << "CreateCGImageFromFile :: could not get Image" << std::endl;
234    }
235   
[10854]236    /* Don't need the SourceRef any more (error or not) */
237    CFRelease(source_ref);
[9880]238
[10854]239    return image_ref;
[9880]240}
241
242/* Once we have our image (CGImageRef), we need to get it into an osg::Image */
243osg::Image* CreateOSGImageFromCGImage(CGImageRef image_ref)
244{
[10854]245    /* This code is adapted from Apple's Documentation found here:
246     * http://developer.apple.com/documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/index.html
247     * Listing 9-4††Using a Quartz image as a texture source.
248     * Unfortunately, this guide doesn't show what to do about
249     * non-RGBA image formats so I'm making the rest up
250     * (and it's probably all wrong).
251     */
[9880]252
[10854]253    size_t the_width = CGImageGetWidth(image_ref);
254    size_t the_height = CGImageGetHeight(image_ref);
255    CGRect the_rect = {{0, 0}, {the_width, the_height}};
[9880]256
[10854]257    size_t bits_per_pixel = CGImageGetBitsPerPixel(image_ref);
258    size_t bytes_per_row = CGImageGetBytesPerRow(image_ref);
259//    size_t bits_per_component = CGImageGetBitsPerComponent(image_ref);
260    size_t bits_per_component = 8;
261   
262    CGImageAlphaInfo alpha_info = CGImageGetAlphaInfo(image_ref);
263   
264    GLint internal_format;
265    GLenum pixel_format;
266    GLenum data_type;
267   
268    void* image_data = calloc(the_width * 4, the_height);
[9880]269
[10854]270    CGColorSpaceRef color_space;
271    CGBitmapInfo bitmap_info = CGImageGetBitmapInfo(image_ref);
[9880]272
[10854]273    switch(bits_per_pixel)
274    {
275        // Drat, if 8-bit, how do you distinguish
276        // between a 256 color GIF, a LUMINANCE map
277        // or an ALPHA map?
278        case 8:
279        {
280            // I probably did the formats all wrong for this case,
281            // especially the ALPHA case.
282            if(kCGImageAlphaNone == alpha_info)
283            {
284                /*
285                 internal_format = GL_LUMINANCE;
286                 pixel_format = GL_LUMINANCE;
287                 */
288                internal_format = GL_RGBA8;
289                pixel_format = GL_BGRA_EXT;
290                data_type = GL_UNSIGNED_INT_8_8_8_8_REV;
291               
292                bytes_per_row = the_width*4;
293//                color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
294                color_space = CGColorSpaceCreateDeviceRGB();
295//                bitmap_info = kCGImageAlphaPremultipliedFirst;
[9880]296#if __BIG_ENDIAN__
[10854]297                bitmap_info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Big; /* XRGB Big Endian */
[9880]298#else
[10854]299                bitmap_info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little; /* XRGB Little Endian */
[9880]300#endif 
[10854]301            }
302            else
303            {
304                internal_format = GL_ALPHA;
305                pixel_format = GL_ALPHA;
306                data_type = GL_UNSIGNED_BYTE;
307                //            bytes_per_row = the_width;
308//                color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
309                color_space = CGColorSpaceCreateDeviceGray();
310            }
[9880]311
[10854]312            break;
313        }
314        case 16:
315        {
316            // FIXME: Is this really a reasonable assumption: GL_LUMINANCE_ALPHA?
317            internal_format = GL_LUMINANCE_ALPHA;
318            pixel_format = GL_LUMINANCE_ALPHA;
319            data_type = GL_UNSIGNED_BYTE;
320//            color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
321            color_space = CGColorSpaceCreateDeviceGray();
322            break;
323        }
324        case 24:
325        {
326            internal_format = GL_RGBA8;
327            pixel_format = GL_BGRA_EXT;
328            data_type = GL_UNSIGNED_INT_8_8_8_8_REV;
329            bytes_per_row = the_width*4;
330//            color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
331            color_space = CGColorSpaceCreateDeviceRGB();
332//            bitmap_info = kCGImageAlphaNone;
[9880]333#if __BIG_ENDIAN__
[10854]334            bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Big; /* XRGB Big Endian */
[9880]335#else
[10854]336            bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little; /* XRGB Little Endian */
[9880]337#endif       
[10854]338            break;
339        }
340        case 32:
341        {
342           
343            internal_format = GL_RGBA8;
344            pixel_format = GL_BGRA_EXT;
345            data_type = GL_UNSIGNED_INT_8_8_8_8_REV;
346           
347            bytes_per_row = the_width*4;
348//            color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
349            color_space = CGColorSpaceCreateDeviceRGB();
350//            bitmap_info = kCGImageAlphaPremultipliedFirst;
[9880]351#if __BIG_ENDIAN__
[10854]352            bitmap_info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Big; /* XRGB Big Endian */
[9880]353#else
[10854]354            bitmap_info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little; /* XRGB Little Endian */
[9880]355#endif 
[10854]356            break;
357        }
358        default:
359        {
360            // osg::notify(osg::WARN) << "Unknown file type in " << fileName.c_str() << " with " << origDepth << std::endl;
[9880]361            return NULL;
362            break;
[10854]363        }
[9880]364
[10854]365    }
366   
367    // Sets up a context to be drawn to with image_data as the area to be drawn to
368    CGContextRef bitmap_context = CGBitmapContextCreate(
369        image_data,
370        the_width,
371        the_height,
372        bits_per_component,
373        bytes_per_row,
374        color_space,
375        bitmap_info
376    );
377   
378    // Draws the image into the context's image_data
379    CGContextDrawImage(bitmap_context, the_rect, image_ref);
[9880]380
[10854]381    CGContextRelease(bitmap_context);
[9880]382
[10854]383    osg::Image* osg_image = new osg::Image;
384   
385    osg_image->setImage(
386        the_width,
387        the_height,
388        1,
389        internal_format,
390        pixel_format,
391        data_type,
392        (unsigned char*)image_data,
393        osg::Image::USE_MALLOC_FREE // Assumption: osg_image takes ownership of image_data and will free
394    );
[9880]395
[10854]396    osg_image->flipVertical();
397    return osg_image;
[9880]398
399
400
401}
402/**************************************************************
403***** End Support functions for reading (stream and file) *****
404**************************************************************/
405
406
407/**************************************************************
408***** Begin Support functions for writing (stream and file)****
409**************************************************************/
410
411/* Create a CGImageRef from osg::Image.
412 * Code adapted from
413 * http://developer.apple.com/samplecode/OpenGLScreenSnapshot/listing2.html
414 */
415CGImageRef CreateCGImageFromOSGData(const osg::Image& osg_image)
416{
[10854]417    size_t image_width = osg_image.s();
418    size_t image_height = osg_image.t();
419    /* From Apple's header for CGBitmapContextCreate()
420     * Each row of the bitmap consists of `bytesPerRow' bytes, which must be at
421     * least `(width * bitsPerComponent * number of components + 7)/8' bytes.
422     */
423    size_t target_bytes_per_row;
424     
425    CGColorSpaceRef color_space;
426    CGBitmapInfo bitmap_info;
427    /* From what I can figure out so far...
428     * We need to create a CGContext connected to the data we want to save
429     * and then call CGBitmapContextCreateImage() on that context to get
430     * a CGImageRef.
431     * However, OS X only allows 4-component image formats (e.g. RGBA) and not
432     * just RGB for the RGB-based CGContext. So for a 24-bit image coming in,
433     * we need to expand the data to 32-bit.
434     * The easiest and fastest way to do that is through the vImage framework
435     * which is part of the Accelerate framework.
436     * Also, the osg::Image data coming in is inverted from what we want, so
437     * we need to invert the image too. Since the osg::Image is const,
438     * we don't want to touch the data, so again we turn to the vImage framework
439     * and invert the data.
440     */
441    vImage_Buffer vimage_buffer_in =
442    {
443        (void*)osg_image.data(), // need to override const, but we don't modify the data so it's safe
444        image_height,
445        image_width,
446        osg_image.getRowSizeInBytes()
447    };
[9880]448
[10854]449    void* out_image_data;
450    vImage_Buffer vimage_buffer_out =
451    {
452        NULL, // will fill-in in switch
453        image_height,
454        image_width,
455        0 // will fill-in in switch
456    };   
457    vImage_Error vimage_error_flag;
[9880]458
[10854]459    // FIXME: Do I want to use format, type, or internalFormat?
460    switch(osg_image.getPixelFormat())
461    {
462        case GL_LUMINANCE:
463        {
464            bitmap_info = kCGImageAlphaNone;
465            target_bytes_per_row = (image_width * 8 + 7)/8;
466            color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
467            if(NULL == color_space)
468            {
469                return NULL;
470            }
471           
472            //    out_image_data = calloc(target_bytes_per_row, image_height);
473            out_image_data = malloc(target_bytes_per_row * image_height);           
474            if(NULL == out_image_data)
475            {
476                osg::notify(osg::WARN) << "In CreateCGImageFromOSGData, malloc failed" << std::endl;
477                CGColorSpaceRelease(color_space);
478                return NULL;
479            }
[9880]480
[10854]481            vimage_buffer_out.data = out_image_data;
482            vimage_buffer_out.rowBytes = target_bytes_per_row;
[9880]483
[10854]484            // Now invert the image
485            vimage_error_flag = vImageVerticalReflect_Planar8(
486                &vimage_buffer_in, // since the osg_image is const...
487                &vimage_buffer_out, // don't reuse the buffer
488                kvImageNoFlags
489            );
490            if(vimage_error_flag != kvImageNoError)
491            {
492                osg::notify(osg::WARN) << "In CreateCGImageFromOSGData for GL_LUMINANCE, vImageVerticalReflect_Planar8 failed with vImage Error Code: " << vimage_error_flag << std::endl;
493                free(out_image_data);
494                CGColorSpaceRelease(color_space);
495                return NULL;
496            }           
[9880]497
498
[10854]499            break;
500        }
501        case GL_ALPHA:
502        {
503            bitmap_info = kCGImageAlphaOnly;
504            target_bytes_per_row = (image_width * 8 + 7)/8;
505            // According to:
506            // http://developer.apple.com/qa/qa2001/qa1037.html
507            // colorSpace=NULL is for alpha only
508            color_space = NULL;
509           
510            //    out_image_data = calloc(target_bytes_per_row, image_height);
511            out_image_data = malloc(target_bytes_per_row * image_height);           
512            if(NULL == out_image_data)
513            {
514                osg::notify(osg::WARN) << "In CreateCGImageFromOSGData, malloc failed" << std::endl;
515                return NULL;
516            }
[9880]517
[10854]518            vimage_buffer_out.data = out_image_data;
519            vimage_buffer_out.rowBytes = target_bytes_per_row;
[9880]520
[10854]521            // Now invert the image
522            vimage_error_flag = vImageVerticalReflect_Planar8(
523                &vimage_buffer_in, // since the osg_image is const...
524                &vimage_buffer_out, // don't reuse the buffer
525                kvImageNoFlags
526            );
527            if(vimage_error_flag != kvImageNoError)
528            {
529                osg::notify(osg::WARN) << "In CreateCGImageFromOSGData for GL_ALPHA, vImageVerticalReflect_Planar8 failed with vImage Error Code: " << vimage_error_flag << std::endl;
530                free(out_image_data);
531                return NULL;
532            }           
[9880]533
534
[10854]535            break;
536        }
[9880]537/*
[10854]538        case GL_LUMINANCE_ALPHA:
539        {
540            // I don't know if we can support this.
541            // The qa1037 doesn't show both gray+alpha.
542            break;
543        }
[9880]544*/
[10854]545        case GL_RGB:
546        {
547            bitmap_info = kCGImageAlphaNoneSkipFirst;
548            target_bytes_per_row = (image_width * 8 * 4 + 7)/8;
549            color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
550            if(NULL == color_space)
551            {
552                osg::notify(osg::WARN) << "In CreateCGImageFromOSGData, CGColorSpaceCreateWithName failed" << std::endl;
553                return NULL;
554            }
555           
556            //    out_image_data = calloc(target_bytes_per_row, image_height);
557            out_image_data = malloc(target_bytes_per_row * image_height);           
558            if(NULL == out_image_data)
559            {
560                osg::notify(osg::WARN) << "In CreateCGImageFromOSGData, malloc failed" << std::endl;
561                CGColorSpaceRelease(color_space);
562                return NULL;
563            }
[9880]564
[10854]565            // Use vImage to get an RGB buffer into ARGB.
566            vimage_buffer_out.data = out_image_data;
567            vimage_buffer_out.rowBytes = target_bytes_per_row;
568            vimage_error_flag = vImageConvert_RGB888toARGB8888(
569                &vimage_buffer_in,
570                NULL, // we don't have a buffer containing alpha values
571                255, // The alpha value we want given to all pixels since we don't have a buffer
572                &vimage_buffer_out,
573                0, // premultiply?
574                kvImageNoFlags // Only responds to kvImageDoNotTile, but I think we want tiling/threading
575            );
576            if(vimage_error_flag != kvImageNoError)
577            {
578                osg::notify(osg::WARN) << "In CreateCGImageFromOSGData, vImageConvert_RGB888toARGB8888 failed with vImage Error Code: " << vimage_error_flag << std::endl;
579                free(out_image_data);
580                CGColorSpaceRelease(color_space);
581                return NULL;
582            }
583            // Now invert the image
584            vimage_error_flag = vImageVerticalReflect_ARGB8888(
585                &vimage_buffer_out,
586                &vimage_buffer_out, // reuse the same buffer
587                kvImageNoFlags
588            );
589            if(vimage_error_flag != kvImageNoError)
590            {
591                osg::notify(osg::WARN) << "In CreateCGImageFromOSGData, vImageAffineWarp_ARGB8888 failed with vImage Error Code: " << vimage_error_flag << std::endl;
592                free(out_image_data);
593                CGColorSpaceRelease(color_space);
594                return NULL;
595            }           
596           
597            break;
598        }
599        case GL_RGBA:
600        {
601            bitmap_info = kCGImageAlphaPremultipliedLast;
602            target_bytes_per_row = osg_image.getRowSizeInBytes();
603            color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
604            if(NULL == color_space)
605            {
606                osg::notify(osg::WARN) << "In CreateCGImageFromOSGData, CGColorSpaceCreateWithName failed" << std::endl;
607                return NULL;
608            }
609            //    out_image_data = calloc(target_bytes_per_row, image_height);
610            out_image_data = malloc(target_bytes_per_row * image_height);           
611            if(NULL == out_image_data)
612            {
613                osg::notify(osg::WARN) << "In CreateCGImageFromOSGData, malloc failed" << std::endl;
614                CGColorSpaceRelease(color_space);
615                return NULL;
616            }
617            vimage_buffer_out.data = out_image_data;
618            vimage_buffer_out.rowBytes = target_bytes_per_row;
619            // Invert the image
620            vimage_error_flag = vImageVerticalReflect_ARGB8888(
621                &vimage_buffer_in, // since the osg_image is const...
622                &vimage_buffer_out, // don't reuse the buffer
623                kvImageNoFlags
624            );
625            if(vimage_error_flag != kvImageNoError)
626            {
627                osg::notify(osg::WARN) << "In CreateCGImageFromOSGData, vImageAffineWarp_ARGB8888 failed with vImage Error Code: " << vimage_error_flag << std::endl;
628                free(out_image_data);
629                CGColorSpaceRelease(color_space);
630                return NULL;
631            }           
632            break;
633        }
634        case GL_BGRA:
635        {
636            if(GL_UNSIGNED_INT_8_8_8_8_REV == osg_image.getDataType())
637            {
[9880]638#if __BIG_ENDIAN__
[10854]639                bitmap_info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Big; /* XRGB Big Endian */
[9880]640#else
[10854]641                bitmap_info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little; /* XRGB Little Endian */
[9880]642#endif 
[10854]643            }
644            else
645            {
646                // FIXME: Don't know how to handle this case
647                bitmap_info = kCGImageAlphaPremultipliedLast;
648            }
[9880]649
[10854]650            target_bytes_per_row = osg_image.getRowSizeInBytes();
651            color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
652            if(NULL == color_space)
653            {
654                osg::notify(osg::WARN) << "In CreateCGImageFromOSGData, CGColorSpaceCreateWithName failed" << std::endl;
655                return NULL;
656            }
657            //    out_image_data = calloc(target_bytes_per_row, image_height);
658            out_image_data = malloc(target_bytes_per_row * image_height);           
659            if(NULL == out_image_data)
660            {
661                osg::notify(osg::WARN) << "In CreateCGImageFromOSGData, malloc failed" << std::endl;
662                CGColorSpaceRelease(color_space);
663                return NULL;
664            }
665            vimage_buffer_out.data = out_image_data;
666            vimage_buffer_out.rowBytes = target_bytes_per_row;
667            // Invert the image
668            vimage_error_flag = vImageVerticalReflect_ARGB8888(
669                                                               &vimage_buffer_in, // since the osg_image is const...
670                                                               &vimage_buffer_out, // don't reuse the buffer
671                                                               kvImageNoFlags
672                                                               );
673            if(vimage_error_flag != kvImageNoError)
674            {
675                osg::notify(osg::WARN) << "In CreateCGImageFromOSGData, vImageAffineWarp_ARGB8888 failed with vImage Error Code: " << vimage_error_flag << std::endl;
676                free(out_image_data);
677                CGColorSpaceRelease(color_space);
678                return NULL;
679            }           
680            break;
681        }
682        // FIXME: Handle other cases.
683        // Use vImagePermuteChannels_ARGB8888 to swizzle bytes
684        default:
685        {
686            osg::notify(osg::WARN) << "In CreateCGImageFromOSGData: Sorry support for this format is not implemented." << std::endl;
687            return NULL;
688            break;
689        }
690    }
[9880]691
[10854]692    CGContextRef bitmap_context = CGBitmapContextCreate(
693        vimage_buffer_out.data,
694        vimage_buffer_out.width,
695        vimage_buffer_out.height,
696        8,
697        vimage_buffer_out.rowBytes,
698        color_space,
699        bitmap_info
700    );
701    /* Done with color space */
702    CGColorSpaceRelease(color_space);
[9880]703
[10854]704    if(NULL == bitmap_context)
705    {
706        free(out_image_data);
707        return NULL;
708    }
709   
[9880]710
[10854]711    /* Make an image out of our bitmap; does a cheap vm_copy of the bitmap */
712    CGImageRef image_ref = CGBitmapContextCreateImage(bitmap_context);
[9880]713
[10854]714    /* Done with data */
715    free(out_image_data);
[9880]716
[10854]717    /* Done with bitmap_context */
718    CGContextRelease(bitmap_context);
[9880]719
[10854]720    return image_ref;
[9880]721}
722
723
724/* Create a CGImageDestinationRef from a file. */
725/* Remember to CFRelease when done. */
726CGImageDestinationRef CreateCGImageDestinationFromFile(const char* the_path,  const osgDB::ReaderWriter::Options* the_options)
727{
728    CFURLRef the_url = NULL;
[10854]729    CFStringRef cf_string = NULL;
730    CFStringRef uti_type = NULL;
[9880]731    CGImageDestinationRef dest_ref = NULL;
[10854]732    bool found_png_option = false;
733    bool found_jpeg_option = false;
734    float compression_quality = 1.0f;
[9880]735
[10854]736    /* Create a CFString from a C string */
737    cf_string = CFStringCreateWithCString(
738        NULL,
739        the_path,
740        kCFStringEncodingUTF8
741    );
742    if(!cf_string)
743    {
744        return NULL;
745    }
746   
747    /* Create a CFURL from a CFString */
[9880]748    the_url = CFURLCreateWithFileSystemPath(
[10854]749        NULL,
750        cf_string,
751        kCFURLPOSIXPathStyle,
752        false
753    );
754   
755    /* Don't need the CFString any more (error or not) */
756    CFRelease(cf_string);
757   
758    if(!the_url)
759    {
760        return NULL;
761    }
762   
763    if(the_options)
764    {
765        std::istringstream iss(the_options->getOptionString());
766        std::string opt;
767        while (iss >> opt)
768        {
769            // Not handled: The user could do something stupid and specify both PNG and JPEG options.
770           
771            if(opt=="PNG_COMPRESSION")
772            {
773                found_png_option = true;
774                // I don't see an option to set PNG compression levels in the API so this info is unused.
775                int level;
776                iss >> level;
777                               
778            }
779            else if(opt=="JPEG_QUALITY")
780            {
781                found_jpeg_option = true;
782                // Chances are that people are specifying values in libjpeg ranges and not ImageIO ranges.
783                // ImageIO is normalized between 0.0 to 1.0 where 1.0 is lossless and 0 is max compression.
784                // I am uncertain what libjpeg's range is. I'm guessing 0-100.
785                int quality;
786                iss >> quality;
787                compression_quality = (float)quality/100.0f;
788            }
789        }
790    }
791   
[9880]792
[10854]793    CFStringRef path_extension = CFURLCopyPathExtension(the_url);
794    if(NULL == path_extension)
795    {
796        if(found_jpeg_option)
797        {
798            uti_type = UTTypeCreatePreferredIdentifierForTag(
799                 kUTTagClassFilenameExtension,
800                 CFSTR("jpg"),
801                 kUTTypeImage // "public.image"
802            );
803        }
804        else
805        {
806            uti_type = UTTypeCreatePreferredIdentifierForTag(
807                 kUTTagClassFilenameExtension,
808                 CFSTR("png"),
809                 kUTTypeImage // "public.image"
810            );
811        }
812    }
813    else
814    {
815        uti_type = UTTypeCreatePreferredIdentifierForTag(
816            kUTTagClassFilenameExtension,
817            path_extension,
818            kUTTypeImage // "public.image"
819        );
820        CFRelease(path_extension);
821    }
[9880]822
[10854]823                                                     
824    dest_ref =  CGImageDestinationCreateWithURL(
825        the_url,
826        uti_type,
827        1, // image file will contain only one image
828        NULL
829    );
[9880]830
[10854]831   
832    CFRelease(uti_type);
833    CFRelease(the_url);
[9880]834
835
[10854]836    // Not handled: The user could do something stupid and specify both PNG and JPEG options.
837    if(found_jpeg_option)
838    {
839        // Do a bunch of work to setup a CFDictionary containing the jpeg compression properties.
840        CFStringRef the_keys[1];
841        CFNumberRef the_values[1];
842        CFDictionaryRef the_dict;
843               
844        the_keys[0] = kCGImageDestinationLossyCompressionQuality;
845        the_values[0] = CFNumberCreate(
846            NULL,
847            kCFNumberFloat32Type,
848            &compression_quality
849        );
850       
851        the_dict = CFDictionaryCreate(NULL, (const void**)&the_keys, (const void**)&the_values, 1, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
852        CFRelease(the_values[0]);
[9880]853
[10854]854        // Now that we have the dict, actually set the property.
855        CGImageDestinationSetProperties(dest_ref, the_dict);
856       
857        CFRelease(the_dict);
858    }
[9880]859
[10854]860    return dest_ref;
[9880]861}
862
863
864/* Create a CGImageDestinationRef from a file. */
865/* Remember to CFRelease when done. */
866CGImageDestinationRef CreateCGImageDestinationFromDataStream(std::ostream& fout,  const osgDB::ReaderWriter::Options* the_options)
867{
[10854]868    CFStringRef uti_type = NULL;
[9880]869    CGImageDestinationRef dest_ref = NULL;
[10854]870    bool found_png_option = false;
871    bool found_jpeg_option = false;
872    float compression_quality = 1.0f;
873   
874    CGDataConsumerCallbacks consumer_callbacks =
875    {
876        MyConsumerPutBytesCallback,
877        MyConsumerReleaseInfoCallback
878    };
879   
880    CGDataConsumerRef data_consumer = CGDataConsumerCreate(&fout, &consumer_callbacks);
[9880]881
[10854]882    if(the_options)
883    {
884        std::istringstream iss(the_options->getOptionString());
885        std::string opt;
886        while (iss >> opt)
887        {
888            // Not handled: The user could do something stupid and specify both PNG and JPEG options.
889           
890            if(opt=="PNG_COMPRESSION")
891            {
892                found_png_option = true;
893                // I don't see an option to set PNG compression levels in the API so this info is unused.
894                int level;
895                iss >> level;
896               
897            }
898            else if(opt=="JPEG_QUALITY")
899            {
900                found_jpeg_option = true;
901                // Chances are that people are specifying values in libjpeg ranges and not ImageIO ranges.
902                // ImageIO is normalized between 0.0 to 1.0 where 1.0 is lossless and 0 is max compression.
903                // I am uncertain what libjpeg's range is. I'm guessing 0-100.
904                int quality;
905                iss >> quality;
906                compression_quality = (float)quality/100.0f;
907            }
908        }
909    }
910   
[9880]911
[10854]912    if(found_jpeg_option)
913    {
914        uti_type = UTTypeCreatePreferredIdentifierForTag(
915            kUTTagClassFilenameExtension,
916            CFSTR("jpg"),
917            kUTTypeImage // "public.image"
918        );
919    }
920    else // default to png
921    {
922        uti_type = UTTypeCreatePreferredIdentifierForTag(
923            kUTTagClassFilenameExtension,
924            CFSTR("png"),
925            kUTTypeImage // "public.image"
926        );
927    }
928   
[9880]929
[10854]930    // If we had a way of hinting at what the data type is, we could
931    // pass this hint in the second parameter.
932    dest_ref = CGImageDestinationCreateWithDataConsumer(
933        data_consumer,
934        uti_type,
935        1, // image file will contain only one image
936        NULL
937    );
938   
939    CGDataConsumerRelease(data_consumer);
940    CFRelease(uti_type);
941   
942   
943    // Not handled: The user could do something stupid and specify both PNG and JPEG options.
944    if(found_jpeg_option)
945    {
946        // Do a bunch of work to setup a CFDictionary containing the jpeg compression properties.
947        CFStringRef the_keys[1];
948        CFNumberRef the_values[1];
949        CFDictionaryRef the_dict;
950       
951        the_keys[0] = kCGImageDestinationLossyCompressionQuality;
952        the_values[0] = CFNumberCreate(
953                                       NULL,
954                                       kCFNumberFloat32Type,
955                                       &compression_quality
956                                       );
957       
958        the_dict = CFDictionaryCreate(NULL, (const void**)&the_keys, (const void**)&the_values, 1, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
959        CFRelease(the_values[0]);
960       
961        // Now that we have the dict, actually set the property.
962        CGImageDestinationSetProperties(dest_ref, the_dict);
963       
964        CFRelease(the_dict);
965    }
966   
967    return dest_ref;
[9880]968}
969/**************************************************************
970***** End Support functions for writing (stream and file) *****
971**************************************************************/
972
973
974
975class ReaderWriterImageIO : public osgDB::ReaderWriter
976
977{
978public:
[10623]979    ReaderWriterImageIO()
[9880]980    {
981
982         supportsExtension("jpg",   "jpg image file");
983         supportsExtension("jpeg""jpeg image file");
984         supportsExtension("jpe",   "jpe image file");
985         supportsExtension("jp2",   "jp2 image file");
986         supportsExtension("tiff""tiff image file");
987         supportsExtension("tif",   "tif image file");               
988         supportsExtension("gif",   "gif image file");
989         supportsExtension("png",   "png image file");
990         supportsExtension("pict""pict image file");
991         supportsExtension("pct",   "pct image file");
992         supportsExtension("pic",   "pic image file");
993         supportsExtension("bmp",   "bmp image file");
994         supportsExtension("BMPf""BMPf image file");
995         supportsExtension("ico",   "ico image file");
996         supportsExtension("icns""icns image file");
997         supportsExtension("tga",   "tga image file");
998         supportsExtension("targa", "targa image file");
999         supportsExtension("psd",   "psd image file");
[10854]1000         
[9880]1001         supportsExtension("pdf",   "pdf image file");
1002         supportsExtension("eps",   "eps image file");
1003         supportsExtension("epi",   "epi image file");
1004         supportsExtension("epsf""epsf image file");
1005         supportsExtension("epsi""epsi image file");
1006         supportsExtension("ps",    "postscript image file");
[10854]1007         
[9880]1008         supportsExtension("dng",   "dng image file");
1009         supportsExtension("cr2",   "cr2 image file");
1010         supportsExtension("crw",   "crw image file");
1011         supportsExtension("fpx",   "fpx image file");
1012         supportsExtension("fpxi""fpxi image file");
1013         supportsExtension("raf",   "raf image file");
1014         supportsExtension("dcr",   "dcr image file");
1015         supportsExtension("ptng""ptng image file");
1016         supportsExtension("pnt",   "pnt image file");
1017         supportsExtension("mac",   "mac image file");
1018         supportsExtension("mrw",   "mrw image file");
1019         supportsExtension("nef",   "nef image file");
1020         supportsExtension("orf",   "orf image file");
1021         supportsExtension("exr",   "exr image file");
1022         supportsExtension("qti",   "qti image file");
1023         supportsExtension("qtif""qtif image file");
1024         supportsExtension("hdr",   "hdr image file");
1025         supportsExtension("sgi",   "sgi image file");
1026         supportsExtension("srf",   "srf image file");
1027         supportsExtension("cur",   "cur image file");
1028         supportsExtension("xbm",   "xbm image file");
1029
1030         supportsExtension("raw",   "raw image file");
1031    }
1032
[10854]1033    virtual const char* className() const { return "Mac OS X ImageIO based Image Reader/Writer"; }
[9880]1034
1035
1036   virtual bool acceptsExtension(const std::string& extension) const
1037   {
[10854]1038       // ImageIO speaks in UTIs.
1039       // http://developer.apple.com/graphicsimaging/workingwithimageio.html
1040       // The Cocoa drawing guide lists these and says to use the
1041       // imageFileTypes class method of NSImage to get a complete
1042       // list of extensions. But remember ImageIO may support more formats
1043       // than Cocoa.
1044       // http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaDrawingGuide/Images/chapter_7_section_3.html
1045       // Apple's UTI guide:
1046       // http://developer.apple.com/documentation/Carbon/Conceptual/understanding_utis/utilist/chapter_4_section_1.html
[9880]1047      return
1048         osgDB::equalCaseInsensitive(extension,"jpg") ||
1049         osgDB::equalCaseInsensitive(extension,"jpeg") ||
1050         osgDB::equalCaseInsensitive(extension,"jpe") ||
1051         osgDB::equalCaseInsensitive(extension,"jp2") ||
1052         osgDB::equalCaseInsensitive(extension,"tiff") ||
1053         osgDB::equalCaseInsensitive(extension,"tif") ||               
1054         osgDB::equalCaseInsensitive(extension,"gif") ||
1055         osgDB::equalCaseInsensitive(extension,"png") ||
1056         osgDB::equalCaseInsensitive(extension,"pict") ||
1057         osgDB::equalCaseInsensitive(extension,"pct") ||
1058         osgDB::equalCaseInsensitive(extension,"pic") ||
1059         osgDB::equalCaseInsensitive(extension,"bmp") ||
1060         osgDB::equalCaseInsensitive(extension,"BMPf") ||
1061         osgDB::equalCaseInsensitive(extension,"ico") ||
1062         osgDB::equalCaseInsensitive(extension,"icns") ||
1063         osgDB::equalCaseInsensitive(extension,"tga") ||
1064         osgDB::equalCaseInsensitive(extension,"targa") ||
1065         osgDB::equalCaseInsensitive(extension,"psd") ||
[10854]1066         
[9880]1067         osgDB::equalCaseInsensitive(extension,"pdf") ||
1068         osgDB::equalCaseInsensitive(extension,"eps") ||
1069         osgDB::equalCaseInsensitive(extension,"epi") ||
1070         osgDB::equalCaseInsensitive(extension,"epsf") ||
1071         osgDB::equalCaseInsensitive(extension,"epsi") ||
1072         osgDB::equalCaseInsensitive(extension,"ps") ||
[10854]1073         
[9880]1074         osgDB::equalCaseInsensitive(extension,"dng") ||
1075         osgDB::equalCaseInsensitive(extension,"cr2") ||
1076         osgDB::equalCaseInsensitive(extension,"crw") ||
1077         osgDB::equalCaseInsensitive(extension,"fpx") ||
1078         osgDB::equalCaseInsensitive(extension,"fpxi") ||
1079         osgDB::equalCaseInsensitive(extension,"raf") ||
1080         osgDB::equalCaseInsensitive(extension,"dcr") ||
1081         osgDB::equalCaseInsensitive(extension,"ptng") ||
1082         osgDB::equalCaseInsensitive(extension,"pnt") ||
1083         osgDB::equalCaseInsensitive(extension,"mac") ||
1084         osgDB::equalCaseInsensitive(extension,"mrw") ||
1085         osgDB::equalCaseInsensitive(extension,"nef") ||
1086         osgDB::equalCaseInsensitive(extension,"orf") ||
1087         osgDB::equalCaseInsensitive(extension,"exr") ||
1088         osgDB::equalCaseInsensitive(extension,"qti") ||
1089         osgDB::equalCaseInsensitive(extension,"qtif") ||
1090         osgDB::equalCaseInsensitive(extension,"hdr") ||
1091         osgDB::equalCaseInsensitive(extension,"sgi") ||
1092         osgDB::equalCaseInsensitive(extension,"srf") ||
1093         osgDB::equalCaseInsensitive(extension,"cur") ||
1094         osgDB::equalCaseInsensitive(extension,"xbm") ||
1095
1096         osgDB::equalCaseInsensitive(extension,"raw");
1097   }
1098
1099
[10854]1100   
1101    ReadResult readImageStream(std::istream& fin) const
1102    {
1103        // Call ImageIO to load the image.
1104        CGImageRef cg_image_ref = CreateCGImageFromDataStream(fin);
1105        if (NULL == cg_image_ref) return ReadResult::FILE_NOT_FOUND;
[9880]1106
[10854]1107        // Create an osg::Image from the CGImageRef.
1108        osg::Image* osg_image = CreateOSGImageFromCGImage(cg_image_ref);
1109   
1110        CFRelease(cg_image_ref);
1111        return osg_image;
1112    }
[9880]1113
[10854]1114    virtual ReadResult readImage(std::istream& fin, const osgDB::ReaderWriter::Options* the_options = NULL) const
1115    {
1116        ReadResult read_result = readImageStream(fin);
1117        return read_result;
1118    }
1119   
1120    ReadResult readImageFile(const std::string& file_name) const
1121    {
1122        osg::notify(osg::INFO) << "imageio readImageFile: " << file_name << std::endl;
[9880]1123       
1124        // Call ImageIO to load the image.
[10854]1125        CGImageRef cg_image_ref = CreateCGImageFromFile(file_name.c_str());
1126        if (NULL == cg_image_ref) return ReadResult::FILE_NOT_FOUND;
[9880]1127
[10854]1128        // Create an osg::Image from the CGImageRef.
1129        osg::Image* osg_image = CreateOSGImageFromCGImage(cg_image_ref);
1130   
1131        CFRelease(cg_image_ref);
[9880]1132
[10854]1133        return osg_image;
1134    }
[9880]1135
[10854]1136    virtual ReadResult readImage(const std::string& file_name, const osgDB::ReaderWriter::Options* the_options) const
1137    {
1138        std::string ext = osgDB::getLowerCaseFileExtension(file_name);
1139        if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
[9880]1140
[10854]1141        std::string full_file_name = osgDB::findDataFile( file_name, the_options );
1142        if (full_file_name.empty()) return ReadResult::FILE_NOT_FOUND;
[9880]1143
1144#if 1
[10854]1145        ReadResult read_result = readImageFile(full_file_name);
[9880]1146#else
[10854]1147        // Only here to help test istream backend. The file version is better because
1148        // the filenname.extension could potentially be used by ImageIO to hint what the format type is.
1149        std::ifstream istream(full_file_name.c_str(), std::ios::in | std::ios::binary);
1150        if(!istream) return ReadResult::FILE_NOT_HANDLED;
1151        ReadResult read_result = readImage(istream);
[9880]1152#endif
1153
[10854]1154        if(read_result.validImage())
1155        {
1156            read_result.getImage()->setFileName(full_file_name);
1157        }
1158        return read_result;
1159    }
[9880]1160
1161
[10854]1162    WriteResult writeImageStream(const osg::Image& osg_image, std::ostream& fout, const osgDB::ReaderWriter::Options* the_options) const
1163    {
1164        WriteResult ret_val = WriteResult::ERROR_IN_WRITING_FILE;
[9880]1165
[10854]1166        CGImageDestinationRef cg_dest_ref = CreateCGImageDestinationFromDataStream(fout, the_options);
1167        if (NULL == cg_dest_ref) return WriteResult::ERROR_IN_WRITING_FILE;
1168       
1169        CGImageRef cg_image_ref = CreateCGImageFromOSGData(osg_image);
1170        if(NULL == cg_image_ref)
1171        {
1172            CFRelease(cg_dest_ref);
1173            return WriteResult::ERROR_IN_WRITING_FILE;
1174        }
1175       
1176        CGImageDestinationAddImage(cg_dest_ref, cg_image_ref, NULL);
1177        if(CGImageDestinationFinalize(cg_dest_ref))
1178        {
1179            ret_val = WriteResult::FILE_SAVED;
1180        }
1181        else
1182        {
1183            ret_val = WriteResult::ERROR_IN_WRITING_FILE;
1184        }
1185       
1186        CFRelease(cg_image_ref);
1187        CFRelease(cg_dest_ref);
1188       
1189        return WriteResult::FILE_SAVED;   
1190    }
1191       
1192    virtual WriteResult writeImage(const osg::Image& osg_image, std::ostream& fout, const osgDB::ReaderWriter::Options* the_options) const
1193    {
1194        WriteResult write_result = writeImageStream(osg_image, fout, the_options);
1195        return write_result;
1196    }
[9880]1197
[10854]1198    WriteResult writeImageFile(const osg::Image& osg_image, const std::string& full_file_name, const osgDB::ReaderWriter::Options* the_options) const
1199    {
1200        WriteResult ret_val = WriteResult::ERROR_IN_WRITING_FILE;
1201        // Call ImageIO to load the image.
1202        CGImageDestinationRef cg_dest_ref = CreateCGImageDestinationFromFile(full_file_name.c_str(), the_options);
1203        if (NULL == cg_dest_ref) return WriteResult::ERROR_IN_WRITING_FILE;
[9880]1204
[10854]1205        CGImageRef cg_image_ref = CreateCGImageFromOSGData(osg_image);
1206        if(NULL == cg_image_ref)
1207        {
1208            CFRelease(cg_dest_ref);
1209            return WriteResult::ERROR_IN_WRITING_FILE;
1210        }
[9880]1211
[10854]1212        CGImageDestinationAddImage(cg_dest_ref, cg_image_ref, NULL);
1213        if(CGImageDestinationFinalize(cg_dest_ref))
1214        {
1215            ret_val = WriteResult::FILE_SAVED;
1216        }
1217        else
1218        {
1219            ret_val = WriteResult::ERROR_IN_WRITING_FILE;
1220        }
1221       
1222        CFRelease(cg_image_ref);
1223        CFRelease(cg_dest_ref);
1224       
1225        return WriteResult::FILE_SAVED;
1226    }
[9880]1227
[10854]1228    virtual WriteResult writeImage(const osg::Image& osg_image, const std::string& file_name, const osgDB::ReaderWriter::Options* the_options) const
1229    {
1230        std::string ext = osgDB::getFileExtension(file_name);
1231        if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;
[9880]1232
1233#if 1
[10854]1234        // FIXME: Something may need to provide a proper writable location for the files.
1235        std::string full_file_name;
1236        full_file_name = file_name;
1237        return writeImageFile(osg_image, full_file_name, the_options);
[9880]1238#else
[10854]1239        // Only here to help test ostream backend. The file version is better because
1240        // the filenname.extension could potentially be used by ImageIO to hint what the format type is.
1241        std::ofstream fout(file_name.c_str(), std::ios::out | std::ios::binary);
1242        if(!fout) return WriteResult::ERROR_IN_WRITING_FILE;
1243        return writeImage(osg_image, fout, the_options);
1244#endif       
1245    }
[9880]1246
1247};
1248
1249// now register with Registry to instantiate the above
1250// reader/writer.
[10401]1251REGISTER_OSGPLUGIN(imagio, ReaderWriterImageIO)
[9880]1252
1253
1254
Note: See TracBrowser for help on using the browser.