root/OpenSceneGraph/trunk/src/osgPlugins/imageio/ReaderWriterImageIO_IOS.cpp @ 13041

Revision 13041, 20.8 kB (checked in by robert, 3 years ago)

Ran script to remove trailing spaces and tabs

  • Property svn:eol-style set to native
Line 
1#include <osg/GL>
2#include <osg/Notify>
3#include <osg/Image>
4
5#include <osgDB/Registry>
6#include <osgDB/FileNameUtils>
7#include <osgDB/FileUtils>
8
9#include <sstream> // for istream
10#include <iostream> // for ios::
11
12#import <UIKit/UIImage.h>
13#import <CoreGraphics/CoreGraphics.h>
14#import <Foundation/Foundation.h>
15#import <ImageIO/CGImageSource.h>
16
17/**************************************************************
18 ***** Begin Callback functions for istream block reading *****
19 **************************************************************/
20
21// This callback reads some bytes from an istream and copies it
22// to a Quartz buffer (supplied by Apple framework).
23size_t MyProviderGetBytesCallback(void* istream_userdata, void* quartz_buffer, size_t the_count)
24{
25    std::istream* the_istream = (std::istream*)istream_userdata;
26    the_istream->read((char*)quartz_buffer, the_count);
27    return the_istream->gcount(); // return the actual number of bytes read
28}
29
30// This callback is triggered when the data provider is released
31// so you can clean up any resources.
32void MyProviderReleaseInfoCallback(void* istream_userdata)
33{
34    // What should I put here? Do I need to close the istream?
35    // The png and tga don't seem to.
36    //    std::istream* the_istream = (std::istream*)istream_userdata;
37}
38
39void MyProviderRewindCallback(void* istream_userdata)
40{
41    std::istream* the_istream = (std::istream*)istream_userdata;
42    the_istream->seekg(0, std::ios::beg);
43}
44
45off_t MyProviderSkipForwardBytesCallback(void* istream_userdata, off_t the_count)
46{
47    std::istream* the_istream = (std::istream*)istream_userdata;
48    off_t start_position = the_istream->tellg();
49    the_istream->seekg(the_count, std::ios::cur);
50    off_t end_position = the_istream->tellg();
51    return (end_position - start_position);
52}
53
54/**************************************************************
55 ***** End Callback functions for istream block reading ********
56 **************************************************************/
57
58
59/**************************************************************
60 ***** Begin Callback functions for ostream block writing ******
61 **************************************************************/
62size_t MyConsumerPutBytesCallback(void* ostream_userdata, const void* quartz_buffer, size_t the_count)
63{
64    std::ostream* the_ostream = (std::ostream*)ostream_userdata;
65    the_ostream->write((char*)quartz_buffer, the_count);
66    // Don't know how to get number of bytes actually written, so
67    // just returning the_count.
68    return the_count;
69}
70
71void MyConsumerReleaseInfoCallback(void* ostream_userdata)
72{
73    std::ostream* the_ostream = (std::ostream*)ostream_userdata;
74    the_ostream->flush();
75}
76/**************************************************************
77 ***** End Callback functions for ostream block writing ********
78 **************************************************************/
79
80
81/**************************************************************
82 ***** Begin Support functions for reading (stream and file) ***
83 **************************************************************/
84
85/* Create a CGImageSourceRef from raw data */
86CGImageRef CreateCGImageFromDataStream(std::istream& fin)
87{
88    CGImageRef image_ref = NULL;
89    CGImageSourceRef source_ref;
90    /* The easy way would be to use CGImageSourceCreateWithData,
91     * but this presumes you have a known fixed-length buffer of data.
92     * The istream makes this harder to know, so we use the ProviderCallbacks APIs
93     CFDataRef the_cf_data = CFDataCreateWithBytesNoCopy(
94     kCFAllocatorDefault,
95     (const UInt8*)the_data,
96     CFIndex length,
97     kCFAllocatorNull // do not free data buffer, must do it yourself
98     );
99     source_ref = CGImageSourceCreateWithData(the_cf_data, NULL);
100     */
101
102    CGDataProviderSequentialCallbacks provider_callbacks =
103    {
104        0,
105        MyProviderGetBytesCallback,
106        MyProviderSkipForwardBytesCallback,
107        MyProviderRewindCallback,
108        MyProviderReleaseInfoCallback
109    };
110
111    CGDataProviderRef data_provider = CGDataProviderCreateSequential(&fin, &provider_callbacks);
112
113    // If we had a way of hinting at what the data type is, we could
114    // pass this hint in the second parameter.
115    source_ref = CGImageSourceCreateWithDataProvider(data_provider, NULL);
116
117    CGDataProviderRelease(data_provider);
118
119
120    if(!source_ref)
121    {
122        return NULL;
123    }
124
125    image_ref = CGImageSourceCreateImageAtIndex(source_ref, 0, NULL);
126
127    /* Don't need the SourceRef any more (error or not) */
128    CFRelease(source_ref);
129
130    return image_ref;
131}
132
133static NSString* toNSString(const std::string& text, NSStringEncoding nsse)
134{
135    NSString*  nstr = nil;
136
137    if (!text.empty())
138    {
139        nstr = [NSString stringWithCString:text.c_str() encoding:nsse];
140        //nstr = [NSString stringWithUTF8String:text.c_str()];// encoding:nsse]
141    }
142
143    if (nstr == nil)
144    {
145        nstr = @"";
146    }
147
148    return nstr;
149}
150
151// std::string to NSString with the UTF8 encoding
152
153static NSString* toNSString(const std::string& text)
154{
155    return toNSString(text, NSUTF8StringEncoding);
156}
157
158//
159//really basic image io for IOS
160//
161osg::Image* ReadCoreGraphicsImageFromFile(std::string file)
162{
163    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
164    //chop the extension off
165    //std::string strExt = osgDB::getFileExtension(file);
166    //std::string strPath = osgDB::getFilePath(file);
167    //std::string strName = osgDB::getStrippedName(file);
168    //std::string strFile = strPath + "/" + strName;
169
170    //NSString* path = [NSString stringWithCString:strName.c_str() encoding:NSUTF8StringEncoding];
171    //NSString* ext = [NSString stringWithCString:strExt.c_str() encoding:NSUTF8StringEncoding];
172
173    //CGImageRef textureImage = [UIImage imageNamed:path].CGImage;
174    //CGImageRef textureImage = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:path ofType:ext]].CGImage;
175    NSString* path = [NSString stringWithCString:file.c_str() encoding:NSUTF8StringEncoding];
176    //NSLog(@"imageio: About to open %@.\n", path);
177    UIImage *img = [UIImage imageWithContentsOfFile:path];
178    if (!img) {
179        NSLog(@"imageio: failed to load UIImage image '%@'.\n", path);
180        [pool release];
181        return NULL;
182    }
183    CGImageRef textureImage = img.CGImage;
184    if (!textureImage) {
185        NSLog(@"imageio: failed to create CGImageRef.\n");
186        [pool release];
187        return NULL;
188    }
189
190    size_t texWidth = CGImageGetWidth(textureImage);
191    size_t texHeight = CGImageGetHeight(textureImage);
192    GLubyte *textureData = (GLubyte *)malloc(texWidth * texHeight * 4);
193    if (!textureData) {
194        NSLog(@"imageio: out of memory.\n");
195        [pool release];
196        return NULL;
197    }
198
199    CGColorSpaceRef csref = CGColorSpaceCreateDeviceRGB();
200    if (!csref) {
201        NSLog(@"imageio: failed to create CGColorSpaceRef.\n");
202        free(textureData);
203        [pool release];
204        return NULL;
205    }
206
207    CGContextRef textureContext = CGBitmapContextCreate(textureData,
208                                                        texWidth, texHeight,
209                                                        8, texWidth * 4,
210                                                        csref,
211                                                        kCGImageAlphaPremultipliedLast);
212    CGColorSpaceRelease(csref);
213    if (!textureContext) {
214        NSLog(@"imageio: failed to create CGContextRef.\n");
215        free(textureData);
216        [pool release];
217        return NULL;
218    }
219
220    //copy into texturedata
221    CGContextDrawImage(textureContext,
222                       CGRectMake(0.0f, 0.0f, (float)texWidth, (float)texHeight),
223                       textureImage);
224    CGContextRelease(textureContext);
225
226    //create the osg image
227    int s = texWidth;
228    int t = texHeight;
229    osg::Image* image = new osg::Image();
230    image->setImage(s, t, 1,
231                    GL_RGBA,
232                    GL_RGBA,
233                    GL_UNSIGNED_BYTE,
234                    textureData,
235                    osg::Image::USE_MALLOC_FREE);
236
237    //flip vertical
238    image->flipVertical();
239
240    //
241    // Reverse the premultiplied alpha for avoiding unexpected darker edges
242    // by Tatsuhiro Nishioka (based on SDL's workaround on the similar issue)
243    // http://bugzilla.libsdl.org/show_bug.cgi?id=868
244    //
245    int i, j;
246    GLubyte *pixels = (GLubyte *)image->data();
247    for (i = image->t() * image->s(); i--; ) {
248
249        GLubyte alpha = pixels[3];
250        if (alpha && (alpha < 255)) {
251            for (j = 0; j < 3; ++j) {
252                pixels[j] = (static_cast<int>(pixels[j]) * 255) / alpha;
253            }
254        }
255        pixels += 4;
256    }
257
258    [pool release];
259    return image;
260}
261
262osg::Image* CreateOSGImageFromCGImage(CGImageRef textureImage)
263{
264    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
265    if (textureImage == nil) {
266        [pool release];
267        NSLog(@"imageio: failed to load CGImageRef image");
268        return NULL;
269    }
270
271    size_t texWidth = CGImageGetWidth(textureImage);
272    size_t texHeight = CGImageGetHeight(textureImage);
273
274    GLubyte *textureData = (GLubyte *)malloc(texWidth * texHeight * 4);
275    if (!textureData) {
276        NSLog(@"imageio: out of memory.\n");
277        [pool release];
278        return NULL;
279    }
280
281    CGColorSpaceRef csref = CGColorSpaceCreateDeviceRGB();
282    if (!csref) {
283        NSLog(@"imageio: failed to create CGColorSpaceRef.\n");
284        free(textureData);
285        [pool release];
286        return NULL;
287    }
288
289    CGContextRef textureContext = CGBitmapContextCreate(textureData,
290                                                        texWidth, texHeight,
291                                                        8, texWidth * 4,
292                                                        csref,
293                                                        kCGImageAlphaPremultipliedLast);
294    CGColorSpaceRelease(csref);
295    if (!textureContext) {
296        NSLog(@"imageio: failed to create CGContextRef.\n");
297        free(textureData);
298        [pool release];
299        return NULL;
300    }
301
302    //copy into texturedata
303    CGContextDrawImage(textureContext,
304                       CGRectMake(0.0f, 0.0f, (float)texWidth, (float)texHeight),
305                       textureImage);
306    CGContextFlush(textureContext);
307    CGContextRelease(textureContext);
308
309
310    //create the osg image
311    int s = texWidth;
312    int t = texHeight;
313    osg::Image* image = new osg::Image();
314    image->setImage(s, t, 1,
315                    GL_RGBA,
316                    GL_RGBA,
317                    GL_UNSIGNED_BYTE,
318                    textureData,
319                    osg::Image::USE_MALLOC_FREE);
320
321    //flip vertical
322    image->flipVertical();
323
324    //
325    // Reverse the premultiplied alpha for avoiding unexpected darker edges
326    // by Tatsuhiro Nishioka (based on SDL's workaround on the similar issue)
327    // http://bugzilla.libsdl.org/show_bug.cgi?id=868
328    //
329
330
331    int i, j;
332    GLubyte *pixels = (GLubyte *)image->data();
333    for (i = image->t() * image->s(); i--; ) {
334
335        GLubyte alpha = pixels[3];
336        if (alpha && (alpha < 255)) {
337            for (j = 0; j < 3; ++j) {
338                pixels[j] = (static_cast<int>(pixels[j]) * 255) / alpha;
339            }
340        }
341        pixels += 4;
342    }
343
344    [pool release];
345    return image;
346}
347
348class ReaderWriterImageIO : public osgDB::ReaderWriter
349
350{
351public:
352    ReaderWriterImageIO()
353    {
354
355        supportsExtension("jpg",   "jpg image file");
356        supportsExtension("jpeg""jpeg image file");
357        supportsExtension("jpe",   "jpe image file");
358        supportsExtension("jp2",   "jp2 image file");
359        supportsExtension("tiff""tiff image file");
360        supportsExtension("tif",   "tif image file");
361        supportsExtension("gif",   "gif image file");
362        supportsExtension("png",   "png image file");
363        supportsExtension("pict""pict image file");
364        supportsExtension("pct",   "pct image file");
365        supportsExtension("pic",   "pic image file");
366        supportsExtension("bmp",   "bmp image file");
367        supportsExtension("BMPf""BMPf image file");
368        supportsExtension("ico",   "ico image file");
369        supportsExtension("icns""icns image file");
370        supportsExtension("tga",   "tga image file");
371        supportsExtension("targa", "targa image file");
372        supportsExtension("psd",   "psd image file");
373
374        supportsExtension("pdf",   "pdf image file");
375        supportsExtension("eps",   "eps image file");
376        supportsExtension("epi",   "epi image file");
377        supportsExtension("epsf""epsf image file");
378        supportsExtension("epsi""epsi image file");
379        supportsExtension("ps",    "postscript image file");
380
381        supportsExtension("dng",   "dng image file");
382        supportsExtension("cr2",   "cr2 image file");
383        supportsExtension("crw",   "crw image file");
384        supportsExtension("fpx",   "fpx image file");
385        supportsExtension("fpxi""fpxi image file");
386        supportsExtension("raf",   "raf image file");
387        supportsExtension("dcr",   "dcr image file");
388        supportsExtension("ptng""ptng image file");
389        supportsExtension("pnt",   "pnt image file");
390        supportsExtension("mac",   "mac image file");
391        supportsExtension("mrw",   "mrw image file");
392        supportsExtension("nef",   "nef image file");
393        supportsExtension("orf",   "orf image file");
394        supportsExtension("exr",   "exr image file");
395        supportsExtension("qti",   "qti image file");
396        supportsExtension("qtif""qtif image file");
397        supportsExtension("hdr",   "hdr image file");
398        supportsExtension("sgi",   "sgi image file");
399        supportsExtension("srf",   "srf image file");
400        supportsExtension("cur",   "cur image file");
401        supportsExtension("xbm",   "xbm image file");
402
403        supportsExtension("raw",   "raw image file");
404    }
405
406    virtual const char* className() const { return "Mac OS X ImageIO based Image Reader/Writer"; }
407
408
409    virtual bool acceptsExtension(const std::string& extension) const
410    {
411        // ImageIO speaks in UTIs.
412        // http://developer.apple.com/graphicsimaging/workingwithimageio.html
413        // The Cocoa drawing guide lists these and says to use the
414        // imageFileTypes class method of NSImage to get a complete
415        // list of extensions. But remember ImageIO may support more formats
416        // than Cocoa.
417        // http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaDrawingGuide/Images/chapter_7_section_3.html
418        // Apple's UTI guide:
419        // http://developer.apple.com/documentation/Carbon/Conceptual/understanding_utis/utilist/chapter_4_section_1.html
420        return
421         osgDB::equalCaseInsensitive(extension,"jpg") ||
422         osgDB::equalCaseInsensitive(extension,"jpeg") ||
423         osgDB::equalCaseInsensitive(extension,"jpe") ||
424         osgDB::equalCaseInsensitive(extension,"jp2") ||
425         osgDB::equalCaseInsensitive(extension,"tiff") ||
426         osgDB::equalCaseInsensitive(extension,"tif") ||
427         osgDB::equalCaseInsensitive(extension,"gif") ||
428         osgDB::equalCaseInsensitive(extension,"png") ||
429         osgDB::equalCaseInsensitive(extension,"pict") ||
430         osgDB::equalCaseInsensitive(extension,"pct") ||
431         osgDB::equalCaseInsensitive(extension,"pic") ||
432         osgDB::equalCaseInsensitive(extension,"bmp") ||
433         osgDB::equalCaseInsensitive(extension,"BMPf") ||
434         osgDB::equalCaseInsensitive(extension,"ico") ||
435         osgDB::equalCaseInsensitive(extension,"icns") ||
436         osgDB::equalCaseInsensitive(extension,"tga") ||
437         osgDB::equalCaseInsensitive(extension,"targa") ||
438         osgDB::equalCaseInsensitive(extension,"psd") ||
439
440         osgDB::equalCaseInsensitive(extension,"pdf") ||
441         osgDB::equalCaseInsensitive(extension,"eps") ||
442         osgDB::equalCaseInsensitive(extension,"epi") ||
443         osgDB::equalCaseInsensitive(extension,"epsf") ||
444         osgDB::equalCaseInsensitive(extension,"epsi") ||
445         osgDB::equalCaseInsensitive(extension,"ps") ||
446
447         osgDB::equalCaseInsensitive(extension,"dng") ||
448         osgDB::equalCaseInsensitive(extension,"cr2") ||
449         osgDB::equalCaseInsensitive(extension,"crw") ||
450         osgDB::equalCaseInsensitive(extension,"fpx") ||
451         osgDB::equalCaseInsensitive(extension,"fpxi") ||
452         osgDB::equalCaseInsensitive(extension,"raf") ||
453         osgDB::equalCaseInsensitive(extension,"dcr") ||
454         osgDB::equalCaseInsensitive(extension,"ptng") ||
455         osgDB::equalCaseInsensitive(extension,"pnt") ||
456         osgDB::equalCaseInsensitive(extension,"mac") ||
457         osgDB::equalCaseInsensitive(extension,"mrw") ||
458         osgDB::equalCaseInsensitive(extension,"nef") ||
459         osgDB::equalCaseInsensitive(extension,"orf") ||
460         osgDB::equalCaseInsensitive(extension,"exr") ||
461         osgDB::equalCaseInsensitive(extension,"qti") ||
462         osgDB::equalCaseInsensitive(extension,"qtif") ||
463         osgDB::equalCaseInsensitive(extension,"hdr") ||
464         osgDB::equalCaseInsensitive(extension,"sgi") ||
465         osgDB::equalCaseInsensitive(extension,"srf") ||
466         osgDB::equalCaseInsensitive(extension,"cur") ||
467         osgDB::equalCaseInsensitive(extension,"xbm") ||
468
469         osgDB::equalCaseInsensitive(extension,"raw");
470    }
471
472
473
474    ReadResult readImageStream(std::istream& fin) const
475    {
476        // Call ImageIO to load the image.
477        CGImageRef cg_image_ref = CreateCGImageFromDataStream(fin);
478        if (NULL == cg_image_ref) return ReadResult::FILE_NOT_FOUND;
479
480        // Create an osg::Image from the CGImageRef.
481        osg::Image* osg_image = CreateOSGImageFromCGImage(cg_image_ref);
482
483        CFRelease(cg_image_ref);
484        return osg_image;
485    }
486
487    virtual ReadResult readImage(std::istream& fin, const osgDB::ReaderWriter::Options* the_options = NULL) const
488    {
489        ReadResult read_result = readImageStream(fin);
490        return read_result;
491    }
492
493    ReadResult readImageFile(const std::string& file_name) const
494    {
495        //osg::notify(osg::INFO) << "imageio readImageFile: " << file_name << std::endl;
496
497        // Create an osg::Image from the CGImageRef.
498        osg::Image* osg_image = ReadCoreGraphicsImageFromFile(file_name);
499
500        return osg_image;
501    }
502
503    virtual ReadResult readImage(const std::string& file_name, const osgDB::ReaderWriter::Options* the_options) const
504    {
505        std::string ext = osgDB::getLowerCaseFileExtension(file_name);
506        if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
507
508        std::string full_file_name = osgDB::findDataFile( file_name, the_options );
509        if (full_file_name.empty()) return ReadResult::FILE_NOT_FOUND;
510
511#if 1
512        ReadResult read_result = readImageFile(full_file_name);
513#else
514        // Only here to help test istream backend. The file version is better because
515        // the filenname.extension could potentially be used by ImageIO to hint what the format type is.
516        std::ifstream istream(full_file_name.c_str(), std::ios::in | std::ios::binary);
517        if(!istream) return ReadResult::FILE_NOT_HANDLED;
518        ReadResult read_result = readImage(istream);
519#endif
520
521        if(read_result.validImage())
522        {
523            read_result.getImage()->setFileName(full_file_name);
524        }
525        return read_result;
526    }
527
528
529    WriteResult writeImageStream(const osg::Image& osg_image, std::ostream& fout, const osgDB::ReaderWriter::Options* the_options) const
530    {
531        WriteResult ret_val = WriteResult::ERROR_IN_WRITING_FILE;
532
533        return WriteResult::FILE_SAVED;
534    }
535
536    virtual WriteResult writeImage(const osg::Image& osg_image, std::ostream& fout, const osgDB::ReaderWriter::Options* the_options) const
537    {
538        WriteResult write_result = writeImageStream(osg_image, fout, the_options);
539        return write_result;
540    }
541
542    WriteResult writeImageFile(const osg::Image& osg_image, const std::string& full_file_name, const osgDB::ReaderWriter::Options* the_options) const
543    {
544        WriteResult ret_val = WriteResult::ERROR_IN_WRITING_FILE;
545
546        return WriteResult::FILE_SAVED;
547    }
548
549    virtual WriteResult writeImage(const osg::Image& osg_image, const std::string& file_name, const osgDB::ReaderWriter::Options* the_options) const
550    {
551        std::string ext = osgDB::getFileExtension(file_name);
552        if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;
553
554#if 1
555        // FIXME: Something may need to provide a proper writable location for the files.
556        std::string full_file_name;
557        full_file_name = file_name;
558        return writeImageFile(osg_image, full_file_name, the_options);
559#else
560        // Only here to help test ostream backend. The file version is better because
561        // the filenname.extension could potentially be used by ImageIO to hint what the format type is.
562        std::ofstream fout(file_name.c_str(), std::ios::out | std::ios::binary);
563        if(!fout) return WriteResult::ERROR_IN_WRITING_FILE;
564        return writeImage(osg_image, fout, the_options);
565#endif
566    }
567
568};
569
570// now register with Registry to instantiate the above
571// reader/writer.
572REGISTER_OSGPLUGIN(imageio, ReaderWriterImageIO)
Note: See TracBrowser for help on using the browser.