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

Revision 12912, 47.8 kB (checked in by robert, 3 years ago)

Added support for using GL_UNPACK_ROW_LENGTH in conjunction with texture's + osg::Image via new RowLength?
parameter in osg::Image. To support this Image::setData(..) now has a new optional rowLength parameter which
defaults to 0, which provides the original behaviour, Image::setRowLength(int) and int Image::getRowLength() are also provided.

With the introduction of RowLength? support in osg::Image it is now possible to create a sub image where
the t size of the image are smaller than the row length, useful for when you have a large image on the CPU
and which to use a small portion of it on the GPU. However, when these sub images are created the data
within the image is no longer contiguous so data access can no longer assume that all the data is in
one block. The new method Image::isDataContiguous() enables the user to check whether the data is contiguous,
and if not one can either access the data row by row using Image::data(column,row,image) accessor, or use the
new Image::DataIterator? for stepping through each block on memory assocatied with the image.

To support the possibility of non contiguous osg::Image usage of image objects has had to be updated to
check DataContiguous? and handle the case or use access via the DataIerator? or by row by row. To achieve
this a relatively large number of files has had to be modified, in particular the texture classes and
image plugins that doing writing.

  • 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        if (!osg_image.isDataContiguous())
1203        {
1204            return WriteResult::FILE_NOT_HANDLED;
1205        }
1206
1207        WriteResult ret_val = WriteResult::ERROR_IN_WRITING_FILE;
1208
1209        CGImageDestinationRef cg_dest_ref = CreateCGImageDestinationFromDataStream(fout, the_options);
1210        if (NULL == cg_dest_ref) return WriteResult::ERROR_IN_WRITING_FILE;
1211       
1212        CGImageRef cg_image_ref = CreateCGImageFromOSGData(osg_image);
1213        if(NULL == cg_image_ref)
1214        {
1215            CFRelease(cg_dest_ref);
1216            return WriteResult::ERROR_IN_WRITING_FILE;
1217        }
1218       
1219        CGImageDestinationAddImage(cg_dest_ref, cg_image_ref, NULL);
1220        if(CGImageDestinationFinalize(cg_dest_ref))
1221        {
1222            ret_val = WriteResult::FILE_SAVED;
1223        }
1224        else
1225        {
1226            ret_val = WriteResult::ERROR_IN_WRITING_FILE;
1227        }
1228       
1229        CFRelease(cg_image_ref);
1230        CFRelease(cg_dest_ref);
1231       
1232        return WriteResult::FILE_SAVED;   
1233    }
1234       
1235    virtual WriteResult writeImage(const osg::Image& osg_image, std::ostream& fout, const osgDB::ReaderWriter::Options* the_options) const
1236    {
1237        WriteResult write_result = writeImageStream(osg_image, fout, the_options);
1238        return write_result;
1239    }
1240
1241    WriteResult writeImageFile(const osg::Image& osg_image, const std::string& full_file_name, const osgDB::ReaderWriter::Options* the_options) const
1242    {
1243        if (!osg_image.isDataContiguous())
1244        {
1245            return WriteResult::FILE_NOT_HANDLED;
1246        }
1247
1248        WriteResult ret_val = WriteResult::ERROR_IN_WRITING_FILE;
1249        WriteResult ret_val = WriteResult::ERROR_IN_WRITING_FILE;
1250        // Call ImageIO to load the image.
1251        CGImageDestinationRef cg_dest_ref = CreateCGImageDestinationFromFile(full_file_name.c_str(), the_options);
1252        if (NULL == cg_dest_ref) return WriteResult::ERROR_IN_WRITING_FILE;
1253
1254        CGImageRef cg_image_ref = CreateCGImageFromOSGData(osg_image);
1255        if(NULL == cg_image_ref)
1256        {
1257            CFRelease(cg_dest_ref);
1258            return WriteResult::ERROR_IN_WRITING_FILE;
1259        }
1260
1261        CGImageDestinationAddImage(cg_dest_ref, cg_image_ref, NULL);
1262        if(CGImageDestinationFinalize(cg_dest_ref))
1263        {
1264            ret_val = WriteResult::FILE_SAVED;
1265        }
1266        else
1267        {
1268            ret_val = WriteResult::ERROR_IN_WRITING_FILE;
1269        }
1270       
1271        CFRelease(cg_image_ref);
1272        CFRelease(cg_dest_ref);
1273       
1274        return WriteResult::FILE_SAVED;
1275    }
1276
1277    virtual WriteResult writeImage(const osg::Image& osg_image, const std::string& file_name, const osgDB::ReaderWriter::Options* the_options) const
1278    {
1279        std::string ext = osgDB::getFileExtension(file_name);
1280        if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;
1281
1282        if (!osg_image.isDataContiguous())
1283        {
1284            return WriteResult::FILE_NOT_HANDLED;
1285        }
1286
1287        WriteResult ret_val = WriteResult::ERROR_IN_WRITING_FILE;
1288#if 1
1289        // FIXME: Something may need to provide a proper writable location for the files.
1290        std::string full_file_name;
1291        full_file_name = file_name;
1292        return writeImageFile(osg_image, full_file_name, the_options);
1293#else
1294        // Only here to help test ostream backend. The file version is better because
1295        // the filenname.extension could potentially be used by ImageIO to hint what the format type is.
1296        osgDB::ofstream fout(file_name.c_str(), std::ios::out | std::ios::binary);
1297        if(!fout) return WriteResult::ERROR_IN_WRITING_FILE;
1298        return writeImage(osg_image, fout, the_options);
1299#endif       
1300    }
1301
1302};
1303
1304// now register with Registry to instantiate the above
1305// reader/writer.
1306REGISTER_OSGPLUGIN(imageio, ReaderWriterImageIO)
Note: See TracBrowser for help on using the browser.