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

Revision 10855, 47.5 kB (checked in by robert, 4 years ago)

From Tatsuhiro Nishioka, "> The workaround/solution was to add a block of code at the end of the

loader to un-premultiply the alpha (now in the codebase).

Applying the code brightens the semi-transparent portion, but the black edges are still there (same on both osgviewer and FlightGear?).
Therefore I believe that the alpha channel is completely ignored (on png, gif, tiff, etc...). I tweaked and tweaked and finally got a workaround.

Please commit the enclosed file to fix these issues.

My workaround is a bit tricky (and some lines are even weird for me), but it resolves the black edges.
These workarounds also work on GIF, TIFF, TGA, and PSD as long as I've tested so far.

Please read this for more info on this issue:
http://macflightgear.sourceforge.net/home/development-notes/devnote-dec-02-2009
http://macflightgear.sourceforge.net/home/development-notes/devnote-dec-03-2009

I'm very happy if some of you guys find a better means of solving the black edges.

"

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 24:
315        {
316            internal_format = GL_RGBA8;
317            pixel_format = GL_BGRA_EXT;
318            data_type = GL_UNSIGNED_INT_8_8_8_8_REV;
319            bytes_per_row = the_width*4;
320//            color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
321            color_space = CGColorSpaceCreateDeviceRGB();
322//            bitmap_info = kCGImageAlphaNone;
[9880]323#if __BIG_ENDIAN__
[10854]324            bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Big; /* XRGB Big Endian */
[9880]325#else
[10854]326            bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little; /* XRGB Little Endian */
[9880]327#endif       
[10854]328            break;
329        }
[10855]330        //
331        // Tatsuhiro Nishioka
332        // 16 bpp grayscale (8 bit white and 8 bit alpha) causes invalid argument combination
333        // in CGBitmapContextCreate.
334        // I guess it is safer to handle 16 bit grayscale image as 32-bit RGBA image.
335        // It works at least on FlightGear
336        //
337        case 16:
[10854]338        case 32:
339        {
[10855]340
[10854]341            internal_format = GL_RGBA8;
342            pixel_format = GL_BGRA_EXT;
343            data_type = GL_UNSIGNED_INT_8_8_8_8_REV;
344           
345            bytes_per_row = the_width*4;
346//            color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
347            color_space = CGColorSpaceCreateDeviceRGB();
348//            bitmap_info = kCGImageAlphaPremultipliedFirst;
[10855]349
[9880]350#if __BIG_ENDIAN__
[10854]351            bitmap_info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Big; /* XRGB Big Endian */
[9880]352#else
[10854]353            bitmap_info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little; /* XRGB Little Endian */
[9880]354#endif 
[10854]355            break;
356        }
357        default:
358        {
359            // osg::notify(osg::WARN) << "Unknown file type in " << fileName.c_str() << " with " << origDepth << std::endl;
[9880]360            return NULL;
361            break;
[10854]362        }
[9880]363
[10854]364    }
365   
366    // Sets up a context to be drawn to with image_data as the area to be drawn to
367    CGContextRef bitmap_context = CGBitmapContextCreate(
368        image_data,
369        the_width,
370        the_height,
371        bits_per_component,
372        bytes_per_row,
373        color_space,
374        bitmap_info
375    );
376   
377    // Draws the image into the context's image_data
378    CGContextDrawImage(bitmap_context, the_rect, image_ref);
[9880]379
[10854]380    CGContextRelease(bitmap_context);
[9880]381
[10855]382    //
383    // Reverse the premultiplied alpha for avoiding unexpected darker edges
384    // by Tatsuhiro Nishioka (based on SDL's workaround on the similar issue)
385    // http://bugzilla.libsdl.org/show_bug.cgi?id=868
386    //
387    if (bits_per_pixel > 8 && (bitmap_info & kCGBitmapAlphaInfoMask) == kCGImageAlphaPremultipliedFirst) {
388        int i, j;
389        GLubyte *pixels = (GLubyte *)image_data;
390        for (i = the_height * the_width; i--; ) {
391            GLuint *value = (GLuint *)pixels;
392#if __BIG_ENDIAN__
393            //
394            // swap endian of each pixel for avoiding weird colors on ppc macs
395            // by Tatsuhiro Nishioka
396            // FIXME: I've tried many combinations of pixel_format, internal_format, and data_type
397            // but none worked well. Therefore I tried endian swapping, which seems working with gif,png,tiff,tga,and psd.
398            // (for grayscaled tga and non-power-of-two tga, I can't guarantee since test images (made with Gimp)
399            // get corrupted on Preview.app ...
400            *value = ((*value) >> 24) | (((*value) << 8) & 0x00FF0000) | (((*value) >> 8) & 0x0000FF00) | ((*value) << 24);
401#endif
402            GLubyte alpha = pixels[3];
403            if (alpha) {
404                for (j = 0; j < 3; ++j) {
405                    pixels[j] = (pixels[j] * 255) / alpha;
406                }
407            }
408            pixels += 4;
409        }
410    }
411
412    //
413    // Workaround for ignored alpha channel
414    // by Tatsuhiro Nishioka
415    // FIXME: specifying GL_UNSIGNED_INT_8_8_8_8_REV or GL_UNSIGNED_INT_8_8_8_8 ignores the alpha channel.
416    // changing it to GL_UNSIGNED_BYTE seems working, but I'm not sure if this is a right way.
417    //
418    data_type = GL_UNSIGNED_BYTE;
[10854]419    osg::Image* osg_image = new osg::Image;
420   
421    osg_image->setImage(
422        the_width,
423        the_height,
424        1,
425        internal_format,
426        pixel_format,
427        data_type,
428        (unsigned char*)image_data,
429        osg::Image::USE_MALLOC_FREE // Assumption: osg_image takes ownership of image_data and will free
430    );
[9880]431
[10854]432    osg_image->flipVertical();
433    return osg_image;
[9880]434
435
436
437}
438/**************************************************************
439***** End Support functions for reading (stream and file) *****
440**************************************************************/
441
442
443/**************************************************************
444***** Begin Support functions for writing (stream and file)****
445**************************************************************/
446
447/* Create a CGImageRef from osg::Image.
448 * Code adapted from
449 * http://developer.apple.com/samplecode/OpenGLScreenSnapshot/listing2.html
450 */
451CGImageRef CreateCGImageFromOSGData(const osg::Image& osg_image)
452{
[10854]453    size_t image_width = osg_image.s();
454    size_t image_height = osg_image.t();
455    /* From Apple's header for CGBitmapContextCreate()
456     * Each row of the bitmap consists of `bytesPerRow' bytes, which must be at
457     * least `(width * bitsPerComponent * number of components + 7)/8' bytes.
458     */
459    size_t target_bytes_per_row;
460     
461    CGColorSpaceRef color_space;
462    CGBitmapInfo bitmap_info;
463    /* From what I can figure out so far...
464     * We need to create a CGContext connected to the data we want to save
465     * and then call CGBitmapContextCreateImage() on that context to get
466     * a CGImageRef.
467     * However, OS X only allows 4-component image formats (e.g. RGBA) and not
468     * just RGB for the RGB-based CGContext. So for a 24-bit image coming in,
469     * we need to expand the data to 32-bit.
470     * The easiest and fastest way to do that is through the vImage framework
471     * which is part of the Accelerate framework.
472     * Also, the osg::Image data coming in is inverted from what we want, so
473     * we need to invert the image too. Since the osg::Image is const,
474     * we don't want to touch the data, so again we turn to the vImage framework
475     * and invert the data.
476     */
477    vImage_Buffer vimage_buffer_in =
478    {
479        (void*)osg_image.data(), // need to override const, but we don't modify the data so it's safe
480        image_height,
481        image_width,
482        osg_image.getRowSizeInBytes()
483    };
[9880]484
[10854]485    void* out_image_data;
486    vImage_Buffer vimage_buffer_out =
487    {
488        NULL, // will fill-in in switch
489        image_height,
490        image_width,
491        0 // will fill-in in switch
492    };   
493    vImage_Error vimage_error_flag;
[9880]494
[10854]495    // FIXME: Do I want to use format, type, or internalFormat?
496    switch(osg_image.getPixelFormat())
497    {
498        case GL_LUMINANCE:
499        {
500            bitmap_info = kCGImageAlphaNone;
501            target_bytes_per_row = (image_width * 8 + 7)/8;
502            color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
503            if(NULL == color_space)
504            {
505                return NULL;
506            }
507           
508            //    out_image_data = calloc(target_bytes_per_row, image_height);
509            out_image_data = malloc(target_bytes_per_row * image_height);           
510            if(NULL == out_image_data)
511            {
512                osg::notify(osg::WARN) << "In CreateCGImageFromOSGData, malloc failed" << std::endl;
513                CGColorSpaceRelease(color_space);
514                return NULL;
515            }
[9880]516
[10854]517            vimage_buffer_out.data = out_image_data;
518            vimage_buffer_out.rowBytes = target_bytes_per_row;
[9880]519
[10854]520            // Now invert the image
521            vimage_error_flag = vImageVerticalReflect_Planar8(
522                &vimage_buffer_in, // since the osg_image is const...
523                &vimage_buffer_out, // don't reuse the buffer
524                kvImageNoFlags
525            );
526            if(vimage_error_flag != kvImageNoError)
527            {
528                osg::notify(osg::WARN) << "In CreateCGImageFromOSGData for GL_LUMINANCE, vImageVerticalReflect_Planar8 failed with vImage Error Code: " << vimage_error_flag << std::endl;
529                free(out_image_data);
530                CGColorSpaceRelease(color_space);
531                return NULL;
532            }           
[9880]533
534
[10854]535            break;
536        }
537        case GL_ALPHA:
538        {
539            bitmap_info = kCGImageAlphaOnly;
540            target_bytes_per_row = (image_width * 8 + 7)/8;
541            // According to:
542            // http://developer.apple.com/qa/qa2001/qa1037.html
543            // colorSpace=NULL is for alpha only
544            color_space = NULL;
545           
546            //    out_image_data = calloc(target_bytes_per_row, image_height);
547            out_image_data = malloc(target_bytes_per_row * image_height);           
548            if(NULL == out_image_data)
549            {
550                osg::notify(osg::WARN) << "In CreateCGImageFromOSGData, malloc failed" << std::endl;
551                return NULL;
552            }
[9880]553
[10854]554            vimage_buffer_out.data = out_image_data;
555            vimage_buffer_out.rowBytes = target_bytes_per_row;
[9880]556
[10854]557            // Now invert the image
558            vimage_error_flag = vImageVerticalReflect_Planar8(
559                &vimage_buffer_in, // since the osg_image is const...
560                &vimage_buffer_out, // don't reuse the buffer
561                kvImageNoFlags
562            );
563            if(vimage_error_flag != kvImageNoError)
564            {
565                osg::notify(osg::WARN) << "In CreateCGImageFromOSGData for GL_ALPHA, vImageVerticalReflect_Planar8 failed with vImage Error Code: " << vimage_error_flag << std::endl;
566                free(out_image_data);
567                return NULL;
568            }           
[9880]569
570
[10854]571            break;
572        }
[9880]573/*
[10854]574        case GL_LUMINANCE_ALPHA:
575        {
576            // I don't know if we can support this.
577            // The qa1037 doesn't show both gray+alpha.
578            break;
579        }
[9880]580*/
[10854]581        case GL_RGB:
582        {
583            bitmap_info = kCGImageAlphaNoneSkipFirst;
584            target_bytes_per_row = (image_width * 8 * 4 + 7)/8;
585            color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
586            if(NULL == color_space)
587            {
588                osg::notify(osg::WARN) << "In CreateCGImageFromOSGData, CGColorSpaceCreateWithName failed" << std::endl;
589                return NULL;
590            }
591           
592            //    out_image_data = calloc(target_bytes_per_row, image_height);
593            out_image_data = malloc(target_bytes_per_row * image_height);           
594            if(NULL == out_image_data)
595            {
596                osg::notify(osg::WARN) << "In CreateCGImageFromOSGData, malloc failed" << std::endl;
597                CGColorSpaceRelease(color_space);
598                return NULL;
599            }
[9880]600
[10854]601            // Use vImage to get an RGB buffer into ARGB.
602            vimage_buffer_out.data = out_image_data;
603            vimage_buffer_out.rowBytes = target_bytes_per_row;
604            vimage_error_flag = vImageConvert_RGB888toARGB8888(
605                &vimage_buffer_in,
606                NULL, // we don't have a buffer containing alpha values
607                255, // The alpha value we want given to all pixels since we don't have a buffer
608                &vimage_buffer_out,
609                0, // premultiply?
610                kvImageNoFlags // Only responds to kvImageDoNotTile, but I think we want tiling/threading
611            );
612            if(vimage_error_flag != kvImageNoError)
613            {
614                osg::notify(osg::WARN) << "In CreateCGImageFromOSGData, vImageConvert_RGB888toARGB8888 failed with vImage Error Code: " << vimage_error_flag << std::endl;
615                free(out_image_data);
616                CGColorSpaceRelease(color_space);
617                return NULL;
618            }
619            // Now invert the image
620            vimage_error_flag = vImageVerticalReflect_ARGB8888(
621                &vimage_buffer_out,
622                &vimage_buffer_out, // reuse the same 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           
633            break;
634        }
635        case GL_RGBA:
636        {
637            bitmap_info = kCGImageAlphaPremultipliedLast;
638            target_bytes_per_row = osg_image.getRowSizeInBytes();
639            color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
640            if(NULL == color_space)
641            {
642                osg::notify(osg::WARN) << "In CreateCGImageFromOSGData, CGColorSpaceCreateWithName failed" << std::endl;
643                return NULL;
644            }
645            //    out_image_data = calloc(target_bytes_per_row, image_height);
646            out_image_data = malloc(target_bytes_per_row * image_height);           
647            if(NULL == out_image_data)
648            {
649                osg::notify(osg::WARN) << "In CreateCGImageFromOSGData, malloc failed" << std::endl;
650                CGColorSpaceRelease(color_space);
651                return NULL;
652            }
653            vimage_buffer_out.data = out_image_data;
654            vimage_buffer_out.rowBytes = target_bytes_per_row;
655            // Invert the image
656            vimage_error_flag = vImageVerticalReflect_ARGB8888(
657                &vimage_buffer_in, // since the osg_image is const...
658                &vimage_buffer_out, // don't reuse the buffer
659                kvImageNoFlags
660            );
661            if(vimage_error_flag != kvImageNoError)
662            {
663                osg::notify(osg::WARN) << "In CreateCGImageFromOSGData, vImageAffineWarp_ARGB8888 failed with vImage Error Code: " << vimage_error_flag << std::endl;
664                free(out_image_data);
665                CGColorSpaceRelease(color_space);
666                return NULL;
667            }           
668            break;
669        }
670        case GL_BGRA:
671        {
672            if(GL_UNSIGNED_INT_8_8_8_8_REV == osg_image.getDataType())
673            {
[9880]674#if __BIG_ENDIAN__
[10854]675                bitmap_info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Big; /* XRGB Big Endian */
[9880]676#else
[10854]677                bitmap_info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little; /* XRGB Little Endian */
[9880]678#endif 
[10854]679            }
680            else
681            {
682                // FIXME: Don't know how to handle this case
683                bitmap_info = kCGImageAlphaPremultipliedLast;
684            }
[9880]685
[10854]686            target_bytes_per_row = osg_image.getRowSizeInBytes();
687            color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
688            if(NULL == color_space)
689            {
690                osg::notify(osg::WARN) << "In CreateCGImageFromOSGData, CGColorSpaceCreateWithName failed" << std::endl;
691                return NULL;
692            }
693            //    out_image_data = calloc(target_bytes_per_row, image_height);
694            out_image_data = malloc(target_bytes_per_row * image_height);           
695            if(NULL == out_image_data)
696            {
697                osg::notify(osg::WARN) << "In CreateCGImageFromOSGData, malloc failed" << std::endl;
698                CGColorSpaceRelease(color_space);
699                return NULL;
700            }
701            vimage_buffer_out.data = out_image_data;
702            vimage_buffer_out.rowBytes = target_bytes_per_row;
703            // Invert the image
704            vimage_error_flag = vImageVerticalReflect_ARGB8888(
705                                                               &vimage_buffer_in, // since the osg_image is const...
706                                                               &vimage_buffer_out, // don't reuse the buffer
707                                                               kvImageNoFlags
708                                                               );
709            if(vimage_error_flag != kvImageNoError)
710            {
711                osg::notify(osg::WARN) << "In CreateCGImageFromOSGData, vImageAffineWarp_ARGB8888 failed with vImage Error Code: " << vimage_error_flag << std::endl;
712                free(out_image_data);
713                CGColorSpaceRelease(color_space);
714                return NULL;
715            }           
716            break;
717        }
718        // FIXME: Handle other cases.
719        // Use vImagePermuteChannels_ARGB8888 to swizzle bytes
720        default:
721        {
722            osg::notify(osg::WARN) << "In CreateCGImageFromOSGData: Sorry support for this format is not implemented." << std::endl;
723            return NULL;
724            break;
725        }
726    }
[9880]727
[10854]728    CGContextRef bitmap_context = CGBitmapContextCreate(
729        vimage_buffer_out.data,
730        vimage_buffer_out.width,
731        vimage_buffer_out.height,
732        8,
733        vimage_buffer_out.rowBytes,
734        color_space,
735        bitmap_info
736    );
737    /* Done with color space */
738    CGColorSpaceRelease(color_space);
[9880]739
[10854]740    if(NULL == bitmap_context)
741    {
742        free(out_image_data);
743        return NULL;
744    }
745   
[9880]746
[10854]747    /* Make an image out of our bitmap; does a cheap vm_copy of the bitmap */
748    CGImageRef image_ref = CGBitmapContextCreateImage(bitmap_context);
[9880]749
[10854]750    /* Done with data */
751    free(out_image_data);
[9880]752
[10854]753    /* Done with bitmap_context */
754    CGContextRelease(bitmap_context);
[9880]755
[10854]756    return image_ref;
[9880]757}
758
759
760/* Create a CGImageDestinationRef from a file. */
761/* Remember to CFRelease when done. */
762CGImageDestinationRef CreateCGImageDestinationFromFile(const char* the_path,  const osgDB::ReaderWriter::Options* the_options)
763{
764    CFURLRef the_url = NULL;
[10854]765    CFStringRef cf_string = NULL;
766    CFStringRef uti_type = NULL;
[9880]767    CGImageDestinationRef dest_ref = NULL;
[10854]768    bool found_png_option = false;
769    bool found_jpeg_option = false;
770    float compression_quality = 1.0f;
[9880]771
[10854]772    /* Create a CFString from a C string */
773    cf_string = CFStringCreateWithCString(
774        NULL,
775        the_path,
776        kCFStringEncodingUTF8
777    );
778    if(!cf_string)
779    {
780        return NULL;
781    }
782   
783    /* Create a CFURL from a CFString */
[9880]784    the_url = CFURLCreateWithFileSystemPath(
[10854]785        NULL,
786        cf_string,
787        kCFURLPOSIXPathStyle,
788        false
789    );
790   
791    /* Don't need the CFString any more (error or not) */
792    CFRelease(cf_string);
793   
794    if(!the_url)
795    {
796        return NULL;
797    }
798   
799    if(the_options)
800    {
801        std::istringstream iss(the_options->getOptionString());
802        std::string opt;
803        while (iss >> opt)
804        {
805            // Not handled: The user could do something stupid and specify both PNG and JPEG options.
806           
807            if(opt=="PNG_COMPRESSION")
808            {
809                found_png_option = true;
810                // I don't see an option to set PNG compression levels in the API so this info is unused.
811                int level;
812                iss >> level;
813                               
814            }
815            else if(opt=="JPEG_QUALITY")
816            {
817                found_jpeg_option = true;
818                // Chances are that people are specifying values in libjpeg ranges and not ImageIO ranges.
819                // ImageIO is normalized between 0.0 to 1.0 where 1.0 is lossless and 0 is max compression.
820                // I am uncertain what libjpeg's range is. I'm guessing 0-100.
821                int quality;
822                iss >> quality;
823                compression_quality = (float)quality/100.0f;
824            }
825        }
826    }
827   
[9880]828
[10854]829    CFStringRef path_extension = CFURLCopyPathExtension(the_url);
830    if(NULL == path_extension)
831    {
832        if(found_jpeg_option)
833        {
834            uti_type = UTTypeCreatePreferredIdentifierForTag(
835                 kUTTagClassFilenameExtension,
836                 CFSTR("jpg"),
837                 kUTTypeImage // "public.image"
838            );
839        }
840        else
841        {
842            uti_type = UTTypeCreatePreferredIdentifierForTag(
843                 kUTTagClassFilenameExtension,
844                 CFSTR("png"),
845                 kUTTypeImage // "public.image"
846            );
847        }
848    }
849    else
850    {
851        uti_type = UTTypeCreatePreferredIdentifierForTag(
852            kUTTagClassFilenameExtension,
853            path_extension,
854            kUTTypeImage // "public.image"
855        );
856        CFRelease(path_extension);
857    }
[9880]858
[10854]859                                                     
860    dest_ref =  CGImageDestinationCreateWithURL(
861        the_url,
862        uti_type,
863        1, // image file will contain only one image
864        NULL
865    );
[9880]866
[10854]867   
868    CFRelease(uti_type);
869    CFRelease(the_url);
[9880]870
871
[10854]872    // Not handled: The user could do something stupid and specify both PNG and JPEG options.
873    if(found_jpeg_option)
874    {
875        // Do a bunch of work to setup a CFDictionary containing the jpeg compression properties.
876        CFStringRef the_keys[1];
877        CFNumberRef the_values[1];
878        CFDictionaryRef the_dict;
879               
880        the_keys[0] = kCGImageDestinationLossyCompressionQuality;
881        the_values[0] = CFNumberCreate(
882            NULL,
883            kCFNumberFloat32Type,
884            &compression_quality
885        );
886       
887        the_dict = CFDictionaryCreate(NULL, (const void**)&the_keys, (const void**)&the_values, 1, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
888        CFRelease(the_values[0]);
[9880]889
[10854]890        // Now that we have the dict, actually set the property.
891        CGImageDestinationSetProperties(dest_ref, the_dict);
892       
893        CFRelease(the_dict);
894    }
[9880]895
[10854]896    return dest_ref;
[9880]897}
898
899
900/* Create a CGImageDestinationRef from a file. */
901/* Remember to CFRelease when done. */
902CGImageDestinationRef CreateCGImageDestinationFromDataStream(std::ostream& fout,  const osgDB::ReaderWriter::Options* the_options)
903{
[10854]904    CFStringRef uti_type = NULL;
[9880]905    CGImageDestinationRef dest_ref = NULL;
[10854]906    bool found_png_option = false;
907    bool found_jpeg_option = false;
908    float compression_quality = 1.0f;
909   
910    CGDataConsumerCallbacks consumer_callbacks =
911    {
912        MyConsumerPutBytesCallback,
913        MyConsumerReleaseInfoCallback
914    };
915   
916    CGDataConsumerRef data_consumer = CGDataConsumerCreate(&fout, &consumer_callbacks);
[9880]917
[10854]918    if(the_options)
919    {
920        std::istringstream iss(the_options->getOptionString());
921        std::string opt;
922        while (iss >> opt)
923        {
924            // Not handled: The user could do something stupid and specify both PNG and JPEG options.
925           
926            if(opt=="PNG_COMPRESSION")
927            {
928                found_png_option = true;
929                // I don't see an option to set PNG compression levels in the API so this info is unused.
930                int level;
931                iss >> level;
932               
933            }
934            else if(opt=="JPEG_QUALITY")
935            {
936                found_jpeg_option = true;
937                // Chances are that people are specifying values in libjpeg ranges and not ImageIO ranges.
938                // ImageIO is normalized between 0.0 to 1.0 where 1.0 is lossless and 0 is max compression.
939                // I am uncertain what libjpeg's range is. I'm guessing 0-100.
940                int quality;
941                iss >> quality;
942                compression_quality = (float)quality/100.0f;
943            }
944        }
945    }
946   
[9880]947
[10854]948    if(found_jpeg_option)
949    {
950        uti_type = UTTypeCreatePreferredIdentifierForTag(
951            kUTTagClassFilenameExtension,
952            CFSTR("jpg"),
953            kUTTypeImage // "public.image"
954        );
955    }
956    else // default to png
957    {
958        uti_type = UTTypeCreatePreferredIdentifierForTag(
959            kUTTagClassFilenameExtension,
960            CFSTR("png"),
961            kUTTypeImage // "public.image"
962        );
963    }
964   
[9880]965
[10854]966    // If we had a way of hinting at what the data type is, we could
967    // pass this hint in the second parameter.
968    dest_ref = CGImageDestinationCreateWithDataConsumer(
969        data_consumer,
970        uti_type,
971        1, // image file will contain only one image
972        NULL
973    );
974   
975    CGDataConsumerRelease(data_consumer);
976    CFRelease(uti_type);
977   
978   
979    // Not handled: The user could do something stupid and specify both PNG and JPEG options.
980    if(found_jpeg_option)
981    {
982        // Do a bunch of work to setup a CFDictionary containing the jpeg compression properties.
983        CFStringRef the_keys[1];
984        CFNumberRef the_values[1];
985        CFDictionaryRef the_dict;
986       
987        the_keys[0] = kCGImageDestinationLossyCompressionQuality;
988        the_values[0] = CFNumberCreate(
989                                       NULL,
990                                       kCFNumberFloat32Type,
991                                       &compression_quality
992                                       );
993       
994        the_dict = CFDictionaryCreate(NULL, (const void**)&the_keys, (const void**)&the_values, 1, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
995        CFRelease(the_values[0]);
996       
997        // Now that we have the dict, actually set the property.
998        CGImageDestinationSetProperties(dest_ref, the_dict);
999       
1000        CFRelease(the_dict);
1001    }
1002   
1003    return dest_ref;
[9880]1004}
1005/**************************************************************
1006***** End Support functions for writing (stream and file) *****
1007**************************************************************/
1008
1009
1010
1011class ReaderWriterImageIO : public osgDB::ReaderWriter
1012
1013{
1014public:
[10623]1015    ReaderWriterImageIO()
[9880]1016    {
1017
1018         supportsExtension("jpg",   "jpg image file");
1019         supportsExtension("jpeg""jpeg image file");
1020         supportsExtension("jpe",   "jpe image file");
1021         supportsExtension("jp2",   "jp2 image file");
1022         supportsExtension("tiff""tiff image file");
1023         supportsExtension("tif",   "tif image file");               
1024         supportsExtension("gif",   "gif image file");
1025         supportsExtension("png",   "png image file");
1026         supportsExtension("pict""pict image file");
1027         supportsExtension("pct",   "pct image file");
1028         supportsExtension("pic",   "pic image file");
1029         supportsExtension("bmp",   "bmp image file");
1030         supportsExtension("BMPf""BMPf image file");
1031         supportsExtension("ico",   "ico image file");
1032         supportsExtension("icns""icns image file");
1033         supportsExtension("tga",   "tga image file");
1034         supportsExtension("targa", "targa image file");
1035         supportsExtension("psd",   "psd image file");
[10854]1036         
[9880]1037         supportsExtension("pdf",   "pdf image file");
1038         supportsExtension("eps",   "eps image file");
1039         supportsExtension("epi",   "epi image file");
1040         supportsExtension("epsf""epsf image file");
1041         supportsExtension("epsi""epsi image file");
1042         supportsExtension("ps",    "postscript image file");
[10854]1043         
[9880]1044         supportsExtension("dng",   "dng image file");
1045         supportsExtension("cr2",   "cr2 image file");
1046         supportsExtension("crw",   "crw image file");
1047         supportsExtension("fpx",   "fpx image file");
1048         supportsExtension("fpxi""fpxi image file");
1049         supportsExtension("raf",   "raf image file");
1050         supportsExtension("dcr",   "dcr image file");
1051         supportsExtension("ptng""ptng image file");
1052         supportsExtension("pnt",   "pnt image file");
1053         supportsExtension("mac",   "mac image file");
1054         supportsExtension("mrw",   "mrw image file");
1055         supportsExtension("nef",   "nef image file");
1056         supportsExtension("orf",   "orf image file");
1057         supportsExtension("exr",   "exr image file");
1058         supportsExtension("qti",   "qti image file");
1059         supportsExtension("qtif""qtif image file");
1060         supportsExtension("hdr",   "hdr image file");
1061         supportsExtension("sgi",   "sgi image file");
1062         supportsExtension("srf",   "srf image file");
1063         supportsExtension("cur",   "cur image file");
1064         supportsExtension("xbm",   "xbm image file");
1065
1066         supportsExtension("raw",   "raw image file");
1067    }
1068
[10854]1069    virtual const char* className() const { return "Mac OS X ImageIO based Image Reader/Writer"; }
[9880]1070
1071
1072   virtual bool acceptsExtension(const std::string& extension) const
1073   {
[10854]1074       // ImageIO speaks in UTIs.
1075       // http://developer.apple.com/graphicsimaging/workingwithimageio.html
1076       // The Cocoa drawing guide lists these and says to use the
1077       // imageFileTypes class method of NSImage to get a complete
1078       // list of extensions. But remember ImageIO may support more formats
1079       // than Cocoa.
1080       // http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaDrawingGuide/Images/chapter_7_section_3.html
1081       // Apple's UTI guide:
1082       // http://developer.apple.com/documentation/Carbon/Conceptual/understanding_utis/utilist/chapter_4_section_1.html
[9880]1083      return
1084         osgDB::equalCaseInsensitive(extension,"jpg") ||
1085         osgDB::equalCaseInsensitive(extension,"jpeg") ||
1086         osgDB::equalCaseInsensitive(extension,"jpe") ||
1087         osgDB::equalCaseInsensitive(extension,"jp2") ||
1088         osgDB::equalCaseInsensitive(extension,"tiff") ||
1089         osgDB::equalCaseInsensitive(extension,"tif") ||               
1090         osgDB::equalCaseInsensitive(extension,"gif") ||
1091         osgDB::equalCaseInsensitive(extension,"png") ||
1092         osgDB::equalCaseInsensitive(extension,"pict") ||
1093         osgDB::equalCaseInsensitive(extension,"pct") ||
1094         osgDB::equalCaseInsensitive(extension,"pic") ||
1095         osgDB::equalCaseInsensitive(extension,"bmp") ||
1096         osgDB::equalCaseInsensitive(extension,"BMPf") ||
1097         osgDB::equalCaseInsensitive(extension,"ico") ||
1098         osgDB::equalCaseInsensitive(extension,"icns") ||
1099         osgDB::equalCaseInsensitive(extension,"tga") ||
1100         osgDB::equalCaseInsensitive(extension,"targa") ||
1101         osgDB::equalCaseInsensitive(extension,"psd") ||
[10854]1102         
[9880]1103         osgDB::equalCaseInsensitive(extension,"pdf") ||
1104         osgDB::equalCaseInsensitive(extension,"eps") ||
1105         osgDB::equalCaseInsensitive(extension,"epi") ||
1106         osgDB::equalCaseInsensitive(extension,"epsf") ||
1107         osgDB::equalCaseInsensitive(extension,"epsi") ||
1108         osgDB::equalCaseInsensitive(extension,"ps") ||
[10854]1109         
[9880]1110         osgDB::equalCaseInsensitive(extension,"dng") ||
1111         osgDB::equalCaseInsensitive(extension,"cr2") ||
1112         osgDB::equalCaseInsensitive(extension,"crw") ||
1113         osgDB::equalCaseInsensitive(extension,"fpx") ||
1114         osgDB::equalCaseInsensitive(extension,"fpxi") ||
1115         osgDB::equalCaseInsensitive(extension,"raf") ||
1116         osgDB::equalCaseInsensitive(extension,"dcr") ||
1117         osgDB::equalCaseInsensitive(extension,"ptng") ||
1118         osgDB::equalCaseInsensitive(extension,"pnt") ||
1119         osgDB::equalCaseInsensitive(extension,"mac") ||
1120         osgDB::equalCaseInsensitive(extension,"mrw") ||
1121         osgDB::equalCaseInsensitive(extension,"nef") ||
1122         osgDB::equalCaseInsensitive(extension,"orf") ||
1123         osgDB::equalCaseInsensitive(extension,"exr") ||
1124         osgDB::equalCaseInsensitive(extension,"qti") ||
1125         osgDB::equalCaseInsensitive(extension,"qtif") ||
1126         osgDB::equalCaseInsensitive(extension,"hdr") ||
1127         osgDB::equalCaseInsensitive(extension,"sgi") ||
1128         osgDB::equalCaseInsensitive(extension,"srf") ||
1129         osgDB::equalCaseInsensitive(extension,"cur") ||
1130         osgDB::equalCaseInsensitive(extension,"xbm") ||
1131
1132         osgDB::equalCaseInsensitive(extension,"raw");
1133   }
1134
1135
[10854]1136   
1137    ReadResult readImageStream(std::istream& fin) const
1138    {
1139        // Call ImageIO to load the image.
1140        CGImageRef cg_image_ref = CreateCGImageFromDataStream(fin);
1141        if (NULL == cg_image_ref) return ReadResult::FILE_NOT_FOUND;
[9880]1142
[10854]1143        // Create an osg::Image from the CGImageRef.
1144        osg::Image* osg_image = CreateOSGImageFromCGImage(cg_image_ref);
1145   
1146        CFRelease(cg_image_ref);
1147        return osg_image;
1148    }
[9880]1149
[10854]1150    virtual ReadResult readImage(std::istream& fin, const osgDB::ReaderWriter::Options* the_options = NULL) const
1151    {
1152        ReadResult read_result = readImageStream(fin);
1153        return read_result;
1154    }
1155   
1156    ReadResult readImageFile(const std::string& file_name) const
1157    {
1158        osg::notify(osg::INFO) << "imageio readImageFile: " << file_name << std::endl;
[9880]1159       
1160        // Call ImageIO to load the image.
[10854]1161        CGImageRef cg_image_ref = CreateCGImageFromFile(file_name.c_str());
1162        if (NULL == cg_image_ref) return ReadResult::FILE_NOT_FOUND;
[9880]1163
[10854]1164        // Create an osg::Image from the CGImageRef.
1165        osg::Image* osg_image = CreateOSGImageFromCGImage(cg_image_ref);
1166   
1167        CFRelease(cg_image_ref);
[9880]1168
[10854]1169        return osg_image;
1170    }
[9880]1171
[10854]1172    virtual ReadResult readImage(const std::string& file_name, const osgDB::ReaderWriter::Options* the_options) const
1173    {
1174        std::string ext = osgDB::getLowerCaseFileExtension(file_name);
1175        if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
[9880]1176
[10854]1177        std::string full_file_name = osgDB::findDataFile( file_name, the_options );
1178        if (full_file_name.empty()) return ReadResult::FILE_NOT_FOUND;
[9880]1179
1180#if 1
[10854]1181        ReadResult read_result = readImageFile(full_file_name);
[9880]1182#else
[10854]1183        // Only here to help test istream backend. The file version is better because
1184        // the filenname.extension could potentially be used by ImageIO to hint what the format type is.
1185        std::ifstream istream(full_file_name.c_str(), std::ios::in | std::ios::binary);
1186        if(!istream) return ReadResult::FILE_NOT_HANDLED;
1187        ReadResult read_result = readImage(istream);
[9880]1188#endif
1189
[10854]1190        if(read_result.validImage())
1191        {
1192            read_result.getImage()->setFileName(full_file_name);
1193        }
1194        return read_result;
1195    }
[9880]1196
1197
[10854]1198    WriteResult writeImageStream(const osg::Image& osg_image, std::ostream& fout, const osgDB::ReaderWriter::Options* the_options) const
1199    {
1200        WriteResult ret_val = WriteResult::ERROR_IN_WRITING_FILE;
[9880]1201
[10854]1202        CGImageDestinationRef cg_dest_ref = CreateCGImageDestinationFromDataStream(fout, the_options);
1203        if (NULL == cg_dest_ref) return WriteResult::ERROR_IN_WRITING_FILE;
1204       
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        }
1211       
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    }
1227       
1228    virtual WriteResult writeImage(const osg::Image& osg_image, std::ostream& fout, const osgDB::ReaderWriter::Options* the_options) const
1229    {
1230        WriteResult write_result = writeImageStream(osg_image, fout, the_options);
1231        return write_result;
1232    }
[9880]1233
[10854]1234    WriteResult writeImageFile(const osg::Image& osg_image, const std::string& full_file_name, const osgDB::ReaderWriter::Options* the_options) const
1235    {
1236        WriteResult ret_val = WriteResult::ERROR_IN_WRITING_FILE;
1237        // Call ImageIO to load the image.
1238        CGImageDestinationRef cg_dest_ref = CreateCGImageDestinationFromFile(full_file_name.c_str(), the_options);
1239        if (NULL == cg_dest_ref) return WriteResult::ERROR_IN_WRITING_FILE;
[9880]1240
[10854]1241        CGImageRef cg_image_ref = CreateCGImageFromOSGData(osg_image);
1242        if(NULL == cg_image_ref)
1243        {
1244            CFRelease(cg_dest_ref);
1245            return WriteResult::ERROR_IN_WRITING_FILE;
1246        }
[9880]1247
[10854]1248        CGImageDestinationAddImage(cg_dest_ref, cg_image_ref, NULL);
1249        if(CGImageDestinationFinalize(cg_dest_ref))
1250        {
1251            ret_val = WriteResult::FILE_SAVED;
1252        }
1253        else
1254        {
1255            ret_val = WriteResult::ERROR_IN_WRITING_FILE;
1256        }
1257       
1258        CFRelease(cg_image_ref);
1259        CFRelease(cg_dest_ref);
1260       
1261        return WriteResult::FILE_SAVED;
1262    }
[9880]1263
[10854]1264    virtual WriteResult writeImage(const osg::Image& osg_image, const std::string& file_name, const osgDB::ReaderWriter::Options* the_options) const
1265    {
1266        std::string ext = osgDB::getFileExtension(file_name);
1267        if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;
[9880]1268
1269#if 1
[10854]1270        // FIXME: Something may need to provide a proper writable location for the files.
1271        std::string full_file_name;
1272        full_file_name = file_name;
1273        return writeImageFile(osg_image, full_file_name, the_options);
[9880]1274#else
[10854]1275        // Only here to help test ostream backend. The file version is better because
1276        // the filenname.extension could potentially be used by ImageIO to hint what the format type is.
1277        std::ofstream fout(file_name.c_str(), std::ios::out | std::ios::binary);
1278        if(!fout) return WriteResult::ERROR_IN_WRITING_FILE;
1279        return writeImage(osg_image, fout, the_options);
1280#endif       
1281    }
[9880]1282
1283};
1284
1285// now register with Registry to instantiate the above
1286// reader/writer.
[10401]1287REGISTER_OSGPLUGIN(imagio, ReaderWriterImageIO)
[9880]1288
1289
1290
Note: See TracBrowser for help on using the browser.