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

Revision 10623, 40.0 kB (checked in by robert, 5 years ago)

From Bob Kuehne, "imageio plugin fix, mac os x : remove double-qualified ctor - illegal syntax under gcc 4.2.1"

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