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

Revision 12789, 47.3 kB (checked in by robert, 3 years ago)

From Paul Idstein, warning fixes for OSX 10.7

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