#include #include #include #include #include #include #include // for istream #include // for ios:: #import #import #import #import /************************************************************** ***** Begin Callback functions for istream block reading ***** **************************************************************/ // This callback reads some bytes from an istream and copies it // to a Quartz buffer (supplied by Apple framework). size_t MyProviderGetBytesCallback(void* istream_userdata, void* quartz_buffer, size_t the_count) { std::istream* the_istream = (std::istream*)istream_userdata; the_istream->read((char*)quartz_buffer, the_count); return the_istream->gcount(); // return the actual number of bytes read } // This callback is triggered when the data provider is released // so you can clean up any resources. void MyProviderReleaseInfoCallback(void* istream_userdata) { // What should I put here? Do I need to close the istream? // The png and tga don't seem to. // std::istream* the_istream = (std::istream*)istream_userdata; } void MyProviderRewindCallback(void* istream_userdata) { std::istream* the_istream = (std::istream*)istream_userdata; the_istream->seekg(0, std::ios::beg); } off_t MyProviderSkipForwardBytesCallback(void* istream_userdata, off_t the_count) { std::istream* the_istream = (std::istream*)istream_userdata; off_t start_position = the_istream->tellg(); the_istream->seekg(the_count, std::ios::cur); off_t end_position = the_istream->tellg(); return (end_position - start_position); } /************************************************************** ***** End Callback functions for istream block reading ******** **************************************************************/ /************************************************************** ***** Begin Callback functions for ostream block writing ****** **************************************************************/ size_t MyConsumerPutBytesCallback(void* ostream_userdata, const void* quartz_buffer, size_t the_count) { std::ostream* the_ostream = (std::ostream*)ostream_userdata; the_ostream->write((char*)quartz_buffer, the_count); // Don't know how to get number of bytes actually written, so // just returning the_count. return the_count; } void MyConsumerReleaseInfoCallback(void* ostream_userdata) { std::ostream* the_ostream = (std::ostream*)ostream_userdata; the_ostream->flush(); } /************************************************************** ***** End Callback functions for ostream block writing ******** **************************************************************/ /************************************************************** ***** Begin Support functions for reading (stream and file) *** **************************************************************/ /* Create a CGImageSourceRef from raw data */ CGImageRef CreateCGImageFromDataStream(std::istream& fin) { CGImageRef image_ref = NULL; CGImageSourceRef source_ref; /* The easy way would be to use CGImageSourceCreateWithData, * but this presumes you have a known fixed-length buffer of data. * The istream makes this harder to know, so we use the ProviderCallbacks APIs CFDataRef the_cf_data = CFDataCreateWithBytesNoCopy( kCFAllocatorDefault, (const UInt8*)the_data, CFIndex length, kCFAllocatorNull // do not free data buffer, must do it yourself ); source_ref = CGImageSourceCreateWithData(the_cf_data, NULL); */ CGDataProviderSequentialCallbacks provider_callbacks = { 0, MyProviderGetBytesCallback, MyProviderSkipForwardBytesCallback, MyProviderRewindCallback, MyProviderReleaseInfoCallback }; CGDataProviderRef data_provider = CGDataProviderCreateSequential(&fin, &provider_callbacks); // If we had a way of hinting at what the data type is, we could // pass this hint in the second parameter. source_ref = CGImageSourceCreateWithDataProvider(data_provider, NULL); CGDataProviderRelease(data_provider); if(!source_ref) { return NULL; } image_ref = CGImageSourceCreateImageAtIndex(source_ref, 0, NULL); /* Don't need the SourceRef any more (error or not) */ CFRelease(source_ref); return image_ref; } static NSString* toNSString(const std::string& text, NSStringEncoding nsse) { NSString* nstr = nil; if (!text.empty()) { nstr = [NSString stringWithCString:text.c_str() encoding:nsse]; //nstr = [NSString stringWithUTF8String:text.c_str()];// encoding:nsse] } if (nstr == nil) { nstr = @""; } return nstr; } // std::string to NSString with the UTF8 encoding static NSString* toNSString(const std::string& text) { return toNSString(text, NSUTF8StringEncoding); } // //really basic image io for IOS // osg::Image* ReadCoreGraphicsImageFromFile(std::string file) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; //chop the extension off //std::string strExt = osgDB::getFileExtension(file); //std::string strPath = osgDB::getFilePath(file); //std::string strName = osgDB::getStrippedName(file); //std::string strFile = strPath + "/" + strName; //NSString* path = [NSString stringWithCString:strName.c_str() encoding:NSUTF8StringEncoding]; //NSString* ext = [NSString stringWithCString:strExt.c_str() encoding:NSUTF8StringEncoding]; //CGImageRef textureImage = [UIImage imageNamed:path].CGImage; //CGImageRef textureImage = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:path ofType:ext]].CGImage; NSString* path = [NSString stringWithCString:file.c_str() encoding:NSUTF8StringEncoding]; //NSLog(@"imageio: About to open %@.\n", path); UIImage *img = [UIImage imageWithContentsOfFile:path]; if (!img) { NSLog(@"imageio: failed to load UIImage image '%@'.\n", path); [pool release]; return NULL; } CGImageRef textureImage = img.CGImage; if (!textureImage) { NSLog(@"imageio: failed to create CGImageRef.\n"); [pool release]; return NULL; } size_t texWidth = CGImageGetWidth(textureImage); size_t texHeight = CGImageGetHeight(textureImage); GLubyte *textureData = (GLubyte *)malloc(texWidth * texHeight * 4); if (!textureData) { NSLog(@"imageio: out of memory.\n"); [pool release]; return NULL; } CGColorSpaceRef csref = CGColorSpaceCreateDeviceRGB(); if (!csref) { NSLog(@"imageio: failed to create CGColorSpaceRef.\n"); free(textureData); [pool release]; return NULL; } CGContextRef textureContext = CGBitmapContextCreate(textureData, texWidth, texHeight, 8, texWidth * 4, csref, kCGImageAlphaPremultipliedLast); CGColorSpaceRelease(csref); if (!textureContext) { NSLog(@"imageio: failed to create CGContextRef.\n"); free(textureData); [pool release]; return NULL; } //copy into texturedata CGContextDrawImage(textureContext, CGRectMake(0.0f, 0.0f, (float)texWidth, (float)texHeight), textureImage); CGContextRelease(textureContext); //create the osg image int s = texWidth; int t = texHeight; osg::Image* image = new osg::Image(); image->setImage(s, t, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, textureData, osg::Image::USE_MALLOC_FREE); //flip vertical image->flipVertical(); // // Reverse the premultiplied alpha for avoiding unexpected darker edges // by Tatsuhiro Nishioka (based on SDL's workaround on the similar issue) // http://bugzilla.libsdl.org/show_bug.cgi?id=868 // int i, j; GLubyte *pixels = (GLubyte *)image->data(); for (i = image->t() * image->s(); i--; ) { GLubyte alpha = pixels[3]; if (alpha && (alpha < 255)) { for (j = 0; j < 3; ++j) { pixels[j] = (static_cast(pixels[j]) * 255) / alpha; } } pixels += 4; } [pool release]; return image; } osg::Image* CreateOSGImageFromCGImage(CGImageRef textureImage) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; if (textureImage == nil) { [pool release]; NSLog(@"imageio: failed to load CGImageRef image"); return NULL; } size_t texWidth = CGImageGetWidth(textureImage); size_t texHeight = CGImageGetHeight(textureImage); GLubyte *textureData = (GLubyte *)malloc(texWidth * texHeight * 4); if (!textureData) { NSLog(@"imageio: out of memory.\n"); [pool release]; return NULL; } CGColorSpaceRef csref = CGColorSpaceCreateDeviceRGB(); if (!csref) { NSLog(@"imageio: failed to create CGColorSpaceRef.\n"); free(textureData); [pool release]; return NULL; } CGContextRef textureContext = CGBitmapContextCreate(textureData, texWidth, texHeight, 8, texWidth * 4, csref, kCGImageAlphaPremultipliedLast); CGColorSpaceRelease(csref); if (!textureContext) { NSLog(@"imageio: failed to create CGContextRef.\n"); free(textureData); [pool release]; return NULL; } //copy into texturedata CGContextDrawImage(textureContext, CGRectMake(0.0f, 0.0f, (float)texWidth, (float)texHeight), textureImage); CGContextFlush(textureContext); CGContextRelease(textureContext); //create the osg image int s = texWidth; int t = texHeight; osg::Image* image = new osg::Image(); image->setImage(s, t, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, textureData, osg::Image::USE_MALLOC_FREE); //flip vertical image->flipVertical(); // // Reverse the premultiplied alpha for avoiding unexpected darker edges // by Tatsuhiro Nishioka (based on SDL's workaround on the similar issue) // http://bugzilla.libsdl.org/show_bug.cgi?id=868 // int i, j; GLubyte *pixels = (GLubyte *)image->data(); for (i = image->t() * image->s(); i--; ) { GLubyte alpha = pixels[3]; if (alpha && (alpha < 255)) { for (j = 0; j < 3; ++j) { pixels[j] = (static_cast(pixels[j]) * 255) / alpha; } } pixels += 4; } [pool release]; return image; } class ReaderWriterImageIO : public osgDB::ReaderWriter { public: ReaderWriterImageIO() { supportsExtension("jpg", "jpg image file"); supportsExtension("jpeg", "jpeg image file"); supportsExtension("jpe", "jpe image file"); supportsExtension("jp2", "jp2 image file"); supportsExtension("tiff", "tiff image file"); supportsExtension("tif", "tif image file"); supportsExtension("gif", "gif image file"); supportsExtension("png", "png image file"); supportsExtension("pict", "pict image file"); supportsExtension("pct", "pct image file"); supportsExtension("pic", "pic image file"); supportsExtension("bmp", "bmp image file"); supportsExtension("BMPf", "BMPf image file"); supportsExtension("ico", "ico image file"); supportsExtension("icns", "icns image file"); supportsExtension("tga", "tga image file"); supportsExtension("targa", "targa image file"); supportsExtension("psd", "psd image file"); supportsExtension("pdf", "pdf image file"); supportsExtension("eps", "eps image file"); supportsExtension("epi", "epi image file"); supportsExtension("epsf", "epsf image file"); supportsExtension("epsi", "epsi image file"); supportsExtension("ps", "postscript image file"); supportsExtension("dng", "dng image file"); supportsExtension("cr2", "cr2 image file"); supportsExtension("crw", "crw image file"); supportsExtension("fpx", "fpx image file"); supportsExtension("fpxi", "fpxi image file"); supportsExtension("raf", "raf image file"); supportsExtension("dcr", "dcr image file"); supportsExtension("ptng", "ptng image file"); supportsExtension("pnt", "pnt image file"); supportsExtension("mac", "mac image file"); supportsExtension("mrw", "mrw image file"); supportsExtension("nef", "nef image file"); supportsExtension("orf", "orf image file"); supportsExtension("exr", "exr image file"); supportsExtension("qti", "qti image file"); supportsExtension("qtif", "qtif image file"); supportsExtension("hdr", "hdr image file"); supportsExtension("sgi", "sgi image file"); supportsExtension("srf", "srf image file"); supportsExtension("cur", "cur image file"); supportsExtension("xbm", "xbm image file"); supportsExtension("raw", "raw image file"); } virtual const char* className() const { return "Mac OS X ImageIO based Image Reader/Writer"; } virtual bool acceptsExtension(const std::string& extension) const { // ImageIO speaks in UTIs. // http://developer.apple.com/graphicsimaging/workingwithimageio.html // The Cocoa drawing guide lists these and says to use the // imageFileTypes class method of NSImage to get a complete // list of extensions. But remember ImageIO may support more formats // than Cocoa. // http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaDrawingGuide/Images/chapter_7_section_3.html // Apple's UTI guide: // http://developer.apple.com/documentation/Carbon/Conceptual/understanding_utis/utilist/chapter_4_section_1.html return osgDB::equalCaseInsensitive(extension,"jpg") || osgDB::equalCaseInsensitive(extension,"jpeg") || osgDB::equalCaseInsensitive(extension,"jpe") || osgDB::equalCaseInsensitive(extension,"jp2") || osgDB::equalCaseInsensitive(extension,"tiff") || osgDB::equalCaseInsensitive(extension,"tif") || osgDB::equalCaseInsensitive(extension,"gif") || osgDB::equalCaseInsensitive(extension,"png") || osgDB::equalCaseInsensitive(extension,"pict") || osgDB::equalCaseInsensitive(extension,"pct") || osgDB::equalCaseInsensitive(extension,"pic") || osgDB::equalCaseInsensitive(extension,"bmp") || osgDB::equalCaseInsensitive(extension,"BMPf") || osgDB::equalCaseInsensitive(extension,"ico") || osgDB::equalCaseInsensitive(extension,"icns") || osgDB::equalCaseInsensitive(extension,"tga") || osgDB::equalCaseInsensitive(extension,"targa") || osgDB::equalCaseInsensitive(extension,"psd") || osgDB::equalCaseInsensitive(extension,"pdf") || osgDB::equalCaseInsensitive(extension,"eps") || osgDB::equalCaseInsensitive(extension,"epi") || osgDB::equalCaseInsensitive(extension,"epsf") || osgDB::equalCaseInsensitive(extension,"epsi") || osgDB::equalCaseInsensitive(extension,"ps") || osgDB::equalCaseInsensitive(extension,"dng") || osgDB::equalCaseInsensitive(extension,"cr2") || osgDB::equalCaseInsensitive(extension,"crw") || osgDB::equalCaseInsensitive(extension,"fpx") || osgDB::equalCaseInsensitive(extension,"fpxi") || osgDB::equalCaseInsensitive(extension,"raf") || osgDB::equalCaseInsensitive(extension,"dcr") || osgDB::equalCaseInsensitive(extension,"ptng") || osgDB::equalCaseInsensitive(extension,"pnt") || osgDB::equalCaseInsensitive(extension,"mac") || osgDB::equalCaseInsensitive(extension,"mrw") || osgDB::equalCaseInsensitive(extension,"nef") || osgDB::equalCaseInsensitive(extension,"orf") || osgDB::equalCaseInsensitive(extension,"exr") || osgDB::equalCaseInsensitive(extension,"qti") || osgDB::equalCaseInsensitive(extension,"qtif") || osgDB::equalCaseInsensitive(extension,"hdr") || osgDB::equalCaseInsensitive(extension,"sgi") || osgDB::equalCaseInsensitive(extension,"srf") || osgDB::equalCaseInsensitive(extension,"cur") || osgDB::equalCaseInsensitive(extension,"xbm") || osgDB::equalCaseInsensitive(extension,"raw"); } ReadResult readImageStream(std::istream& fin) const { // Call ImageIO to load the image. CGImageRef cg_image_ref = CreateCGImageFromDataStream(fin); if (NULL == cg_image_ref) return ReadResult::FILE_NOT_FOUND; // Create an osg::Image from the CGImageRef. osg::Image* osg_image = CreateOSGImageFromCGImage(cg_image_ref); CFRelease(cg_image_ref); return osg_image; } virtual ReadResult readImage(std::istream& fin, const osgDB::ReaderWriter::Options* the_options = NULL) const { ReadResult read_result = readImageStream(fin); return read_result; } ReadResult readImageFile(const std::string& file_name) const { //osg::notify(osg::INFO) << "imageio readImageFile: " << file_name << std::endl; // Create an osg::Image from the CGImageRef. osg::Image* osg_image = ReadCoreGraphicsImageFromFile(file_name); return osg_image; } virtual ReadResult readImage(const std::string& file_name, const osgDB::ReaderWriter::Options* the_options) const { std::string ext = osgDB::getLowerCaseFileExtension(file_name); if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED; std::string full_file_name = osgDB::findDataFile( file_name, the_options ); if (full_file_name.empty()) return ReadResult::FILE_NOT_FOUND; #if 1 ReadResult read_result = readImageFile(full_file_name); #else // Only here to help test istream backend. The file version is better because // the filenname.extension could potentially be used by ImageIO to hint what the format type is. std::ifstream istream(full_file_name.c_str(), std::ios::in | std::ios::binary); if(!istream) return ReadResult::FILE_NOT_HANDLED; ReadResult read_result = readImage(istream); #endif if(read_result.validImage()) { read_result.getImage()->setFileName(full_file_name); } return read_result; } WriteResult writeImageStream(const osg::Image& osg_image, std::ostream& fout, const osgDB::ReaderWriter::Options* the_options) const { WriteResult ret_val = WriteResult::ERROR_IN_WRITING_FILE; return WriteResult::FILE_SAVED; } virtual WriteResult writeImage(const osg::Image& osg_image, std::ostream& fout, const osgDB::ReaderWriter::Options* the_options) const { WriteResult write_result = writeImageStream(osg_image, fout, the_options); return write_result; } WriteResult writeImageFile(const osg::Image& osg_image, const std::string& full_file_name, const osgDB::ReaderWriter::Options* the_options) const { WriteResult ret_val = WriteResult::ERROR_IN_WRITING_FILE; return WriteResult::FILE_SAVED; } virtual WriteResult writeImage(const osg::Image& osg_image, const std::string& file_name, const osgDB::ReaderWriter::Options* the_options) const { std::string ext = osgDB::getFileExtension(file_name); if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED; #if 1 // FIXME: Something may need to provide a proper writable location for the files. std::string full_file_name; full_file_name = file_name; return writeImageFile(osg_image, full_file_name, the_options); #else // Only here to help test ostream backend. The file version is better because // the filenname.extension could potentially be used by ImageIO to hint what the format type is. std::ofstream fout(file_name.c_str(), std::ios::out | std::ios::binary); if(!fout) return WriteResult::ERROR_IN_WRITING_FILE; return writeImage(osg_image, fout, the_options); #endif } }; // now register with Registry to instantiate the above // reader/writer. REGISTER_OSGPLUGIN(imageio, ReaderWriterImageIO)