From a1f1f5114c11e5e6583efd8efcfc8095eaf07239 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Thu, 17 Apr 2008 15:30:35 +0000 Subject: [PATCH] From Stephan Huber, "attached you'll find a refactored and improved quicktime-plugin. I moved all code related to the image-loading, swizzling etc into an extra class. The quicktime plugin supports now reading and writing images from/to streams and the code is less cluttered than previous versions. I removed QTtexture.h/.cpp and added QTImportExport.h/.cpp. I updated the CMake-files, I hope they are alright. I used the submitted code in my own apps since two months or so and it seems pretty stable, but as always the migration to the osg-quicktime plugin may have introduced new bugs, so perfect for developer release :)" --- src/osgPlugins/quicktime/CMakeLists.txt | 4 +- src/osgPlugins/quicktime/QTImportExport.cpp | 526 ++++++++++++++++++++ src/osgPlugins/quicktime/QTImportExport.h | 65 +++ src/osgPlugins/quicktime/QTtexture.cpp | 396 --------------- src/osgPlugins/quicktime/QTtexture.h | 19 - src/osgPlugins/quicktime/ReaderWriterQT.cpp | 424 +++++----------- 6 files changed, 706 insertions(+), 728 deletions(-) create mode 100644 src/osgPlugins/quicktime/QTImportExport.cpp create mode 100644 src/osgPlugins/quicktime/QTImportExport.h delete mode 100644 src/osgPlugins/quicktime/QTtexture.cpp delete mode 100644 src/osgPlugins/quicktime/QTtexture.h diff --git a/src/osgPlugins/quicktime/CMakeLists.txt b/src/osgPlugins/quicktime/CMakeLists.txt index 89fe64324..cf180faf5 100644 --- a/src/osgPlugins/quicktime/CMakeLists.txt +++ b/src/osgPlugins/quicktime/CMakeLists.txt @@ -9,7 +9,7 @@ SET(TARGET_SRC MovieData.cpp QTUtils.cpp QTLiveUtils.cpp - QTtexture.cpp + QTImportExport.cpp QuicktimeImageStream.cpp QuicktimeLiveImageStream.cpp ReaderWriterQT.cpp @@ -19,7 +19,7 @@ SET(TARGET_H MovieData.h QTUtils.h QTLiveUtils.h - QTtexture.h + QTImportExport.h QuicktimeImageStream.h QuicktimeLiveImageStream.h ) diff --git a/src/osgPlugins/quicktime/QTImportExport.cpp b/src/osgPlugins/quicktime/QTImportExport.cpp new file mode 100644 index 000000000..13b5e68cf --- /dev/null +++ b/src/osgPlugins/quicktime/QTImportExport.cpp @@ -0,0 +1,526 @@ +/* + * QTImportExport.cpp + * cefix + * + * Created by Stephan Huber on 07.02.08. + * Copyright 2008 __MyCompanyName__. All rights reserved. + * + */ +#include +#include +#include "QTImportExport.h" +#include "QTUtils.h" + +#include + + +/** small exception class bundling a error-message */ +class QTImportExportException : public std::exception { + + public: + QTImportExportException(int err, const std::string& msg) : std::exception(), _err(err), _msg(msg) {} + + virtual const char* what() { return _msg.c_str(); } + int getErrorCode() { return _err; } + + virtual ~QTImportExportException() throw () {} + + private: + int _err; + std::string _msg; +}; + +QuicktimeImportExport::QuicktimeImportExport() +: _error(0), + _lastError("") +{ + initQuicktime(); +} + + +// ---------------------------------------------------------------------------------------------------------- +// flipImage +// ---------------------------------------------------------------------------------------------------------- + +void QuicktimeImportExport::flipImage(unsigned char* pixels, int bytesPerPixel, unsigned int width, unsigned height) +{ + // Flip the image + unsigned imageSize = width * height * bytesPerPixel; + char *tBuffer = new char [imageSize]; + unsigned int rowBytes = width * bytesPerPixel; + unsigned int i,j; + for (i = 0, j = imageSize - rowBytes; i < imageSize; i += rowBytes, j -= rowBytes) + memcpy( &tBuffer[j], &pixels[i], (size_t)rowBytes ); + + memcpy(pixels, tBuffer, (size_t)imageSize); + delete[] tBuffer; +} + + + + +// ---------------------------------------------------------------------------------------------------------- +// prepareBufferForOSG +// ---------------------------------------------------------------------------------------------------------- + +unsigned char* QuicktimeImportExport::pepareBufferForOSG(unsigned char * buffer, int bytesPerPixel, unsigned int width, unsigned int height) +{ + unsigned char *pixels = new unsigned char [height * width * 4]; + unsigned char *dstp = pixels; + unsigned char *srcp = buffer; + unsigned int i, j; + + int roffset, goffset, boffset, aoffset; + aoffset = -1; + int sourceStep; + + switch (bytesPerPixel) { + case 1: + sourceStep = 1; + roffset = goffset = boffset = 0; + break; + case 3: + sourceStep = 3; + roffset = 0; + goffset = 1; + boffset = 2; + break; + case 4: + sourceStep = 4; + aoffset = 1; + roffset = 2; + goffset = 3; + boffset = 0; + break; + + } + + for (i = 0; i < height; ++i ) + { + for (j = 0; j < width; ++j ) + { + dstp[0] = (aoffset < 0) ? 0 : srcp[aoffset]; + dstp[1] = srcp[roffset]; + dstp[2] = srcp[goffset]; + dstp[3] = srcp[boffset]; + srcp+=sourceStep; + dstp+=4; + + } + } + + flipImage(pixels, bytesPerPixel, width, height); + return pixels; + +} + + + +// ---------------------------------------------------------------------------------------------------------- +// prepareBufferForQuicktime +// ---------------------------------------------------------------------------------------------------------- + +unsigned char* QuicktimeImportExport::prepareBufferForQuicktime(unsigned char* buffer, GLenum pixelFormat, int bytesPerPixel, unsigned int width, unsigned int height) +{ + unsigned char *pixels = new unsigned char [height * width * 4]; + unsigned char *dstp = pixels; + unsigned char *srcp = buffer; + unsigned int i, j; + + int roffset, goffset, boffset, aoffset; + aoffset = -1; + int sourceStep; + + switch (bytesPerPixel) { + case 1: + sourceStep = 1; + roffset = goffset = boffset = 0; + break; + case 3: + sourceStep = 3; + roffset = 0; + goffset = 1; + boffset = 2; + break; + case 4: + sourceStep = 4; + switch (pixelFormat) { + case GL_RGBA: + aoffset = 3; + roffset = 0; + goffset = 1; + boffset = 2; + break; + + case GL_BGRA_EXT: + aoffset = 0; + roffset = 1; + goffset = 2; + boffset = 3; + break; + } + } + + + for (i = 0; i < height; ++i ) + { + for (j = 0; j < width; ++j ) + { + dstp[0] = (aoffset < 0) ? 0 : srcp[aoffset]; + dstp[1] = srcp[roffset]; + dstp[2] = srcp[goffset]; + dstp[3] = srcp[boffset]; + srcp+=sourceStep; + dstp+=4; + + } + } + + flipImage(pixels, 4, width, height); + + return pixels; + +} + +// ---------------------------------------------------------------------------------------------------------- +// readFromStream +// ---------------------------------------------------------------------------------------------------------- + +osg::Image* QuicktimeImportExport::readFromStream(std::istream & inStream, const std::string& fileTypeHint, long sizeHint) +{ + char* content = NULL; + long length = 0; + if (sizeHint != 0) + { + length = sizeHint; + content = new char[length]; + inStream.read (content,length); + } + else + { + int readBytes(0), newBytes(0); + + char buffer[10240]; + + while (!inStream.eof()) { + inStream.read(buffer, 10240); + newBytes = inStream.gcount(); + if (newBytes > 0) { + char* newcontent = new char[readBytes + newBytes]; + + if (readBytes > 0) + memcpy(newcontent, content, readBytes); + + memcpy(&newcontent[readBytes], &buffer, newBytes); + readBytes += newBytes; + if (content) delete[] content; + content = newcontent; + } + } + length = readBytes; + } + + osg::Image* img = doImport(reinterpret_cast(content), length, fileTypeHint); + + if (content) delete[] content; + return img; + } + + +Handle getPtrDataRef(unsigned char *data, unsigned int size, const std::string &filename) +{ + // Load Data Reference + Handle dataRef; + Handle fileNameHandle; + PointerDataRefRecord ptrDataRefRec; + ComponentInstance dataRefHandler; + unsigned char pstr[255]; + + ptrDataRefRec.data = data; + ptrDataRefRec.dataLength = size; + + /*err = */PtrToHand(&ptrDataRefRec, &dataRef, sizeof(PointerDataRefRecord)); + + // Open a Data Handler for the Data Reference + /*err = */OpenADataHandler(dataRef, PointerDataHandlerSubType, NULL, + (OSType)0, NULL, kDataHCanRead, &dataRefHandler); + + // Convert From CString in filename to a PascalString in pstr + if (filename.length() > 255) { + CopyCStringToPascal(filename.c_str(), pstr); + //hmm...not good, pascal string limit is 255! + //do some error handling maybe?! + } + + // Add filename extension + /*err = */PtrToHand(pstr, &fileNameHandle, filename.length() + 1); + /*err = */DataHSetDataRefExtension(dataRefHandler, fileNameHandle, + kDataRefExtensionFileName); + DisposeHandle(fileNameHandle); + + // Release old handler which does not have the extensions + DisposeHandle(dataRef); + + // Grab the SAFE_NEW version of the data ref from the data handler + /*err = */ DataHGetDataRef(dataRefHandler, &dataRef); + + CloseComponent(dataRefHandler); + + return dataRef; +} + + +osg::Image* QuicktimeImportExport::doImport(unsigned char* data, unsigned int sizeData, const std::string& fileTypeHint) +{ + GWorldPtr gworld = 0; + OSType pixelFormat; + int rowStride; + GraphicsImportComponent gicomp = 0; + Rect rectImage; + GDHandle origDevice = 0; + CGrafPtr origPort = 0; + ImageDescriptionHandle desc = 0; + int depth = 32; + unsigned int xsize, ysize; + unsigned char* imageData; + + // Data Handle for file data ( & load data from file ) + Handle dataRef = getPtrDataRef(data, sizeData, fileTypeHint); + + try { + OSErr err = noErr; + + // GraphicsImporter - Get Importer for our filetype + GetGraphicsImporterForDataRef(dataRef, 'ptr ', &gicomp); + + // GWorld - Get Texture Info + err = GraphicsImportGetNaturalBounds(gicomp, &rectImage); + if (err != noErr) { + throw QTImportExportException(err, "GraphicsImportGetNaturalBounds failed"); + + } + xsize = (unsigned int)(rectImage.right - rectImage.left); + ysize = (unsigned int)(rectImage.bottom - rectImage.top); + + // ImageDescription - Get Image Description + err = GraphicsImportGetImageDescription(gicomp, &desc); + if (err != noErr) { + throw QTImportExportException(err, "GraphicsImportGetImageDescription failed"); + } + + // ImageDescription - Get Bit Depth + HLock(reinterpret_cast(desc)); + + + // GWorld - Pixel Format stuff + pixelFormat = k32ARGBPixelFormat; // Make sure its forced...NOTE: i'm pretty sure this cannot be RGBA! + + // GWorld - Row stride + rowStride = xsize * 4; // (width * depth_bpp / 8) + + // GWorld - Allocate output buffer + imageData = new unsigned char[rowStride * ysize]; + + // GWorld - Actually Create IT! + QTNewGWorldFromPtr(&gworld, pixelFormat, &rectImage, 0, 0, 0, imageData, rowStride); + if (!gworld) { + throw QTImportExportException(-1, "QTNewGWorldFromPtr failed"); + } + + // Save old Graphics Device and Graphics Port to reset to later + GetGWorld (&origPort, &origDevice); + + // GraphicsImporter - Set Destination GWorld (our buffer) + err = GraphicsImportSetGWorld(gicomp, gworld, 0); + if (err != noErr) { + throw QTImportExportException(err, "GraphicsImportSetGWorld failed"); + } + + // GraphicsImporter - Set Quality Level + err = GraphicsImportSetQuality(gicomp, codecLosslessQuality); + if (err != noErr) { + throw QTImportExportException(err, "GraphicsImportSetQuality failed"); + } + + // Lock pixels so that we can draw to our memory texture + if (!GetGWorldPixMap(gworld) || !LockPixels(GetGWorldPixMap(gworld))) { + throw QTImportExportException(0, "GetGWorldPixMap failed"); + } + + + //*** Draw GWorld into our Memory Texture! + GraphicsImportDraw(gicomp); + + // Clean up + UnlockPixels(GetGWorldPixMap(gworld)); + SetGWorld(origPort, origDevice); // set graphics port to offscreen (we don't need it now) + DisposeGWorld(gworld); + CloseComponent(gicomp); + DisposeHandle(reinterpret_cast(desc)); + DisposeHandle(dataRef); + } + catch (QTImportExportException e) + { + setError(e.what()); + + if (gworld) { + UnlockPixels(GetGWorldPixMap(gworld)); + SetGWorld(origPort, origDevice); // set graphics port to offscreen (we don't need it now) + DisposeGWorld(gworld); + } + if (gicomp) + CloseComponent(gicomp); + if (desc) + DisposeHandle(reinterpret_cast(desc)); + + if (imageData) + delete[] imageData; + if (dataRef) + DisposeHandle(dataRef); + + return NULL; + } + + + + unsigned int glpixelFormat; + + switch(depth >> 3) { + case 3 : + glpixelFormat = GL_RGB; + break; + case 4 : + glpixelFormat = GL_RGBA; + break; + default : + delete imageData; + setError("unknown pixelformat"); + return NULL; + break; + } + + unsigned char* swizzled = pepareBufferForOSG(imageData, depth >> 3, xsize, ysize); + + delete[] imageData; + + osg::Image* image = new osg::Image(); + image->setFileName(fileTypeHint.c_str()); + image->setImage(xsize,ysize,1, + depth >> 3, + glpixelFormat, + GL_UNSIGNED_BYTE, + swizzled, + osg::Image::USE_NEW_DELETE ); + + + return image; +} + + + void QuicktimeImportExport::writeToStream(std::ostream& outStream, osg::Image* image, const std::string& fileTypeHint) + { + + std::string ext = osgDB::getFileExtension(fileTypeHint); + //Build map of extension <-> osFileTypes + static std::map extmap; + if (extmap.size() == 0) { + extmap["jpg"] = kQTFileTypeJPEG; + extmap["jpeg"] = kQTFileTypeJPEG; + extmap["bmp"] = kQTFileTypeBMP; + extmap["tif"] = kQTFileTypeTIFF; + extmap["tiff"] = kQTFileTypeTIFF; + extmap["png"] = kQTFileTypePNG; + extmap["gif"] = kQTFileTypeGIF; + extmap["psd"] = kQTFileTypePhotoShop; + extmap["sgi"] = kQTFileTypeSGIImage; + extmap["rgb"] = kQTFileTypeSGIImage; + extmap["rgba"] = kQTFileTypeSGIImage; + } + + + std::map::iterator cur = extmap.find(ext); + + // can not handle this type of file, perhaps a movie? + if (cur == extmap.end()) + return; + + unsigned int numBytes = image->computeNumComponents(image->getPixelFormat()); + unsigned char* pixels = prepareBufferForQuicktime( + image->data(), + image->getPixelFormat(), + numBytes, + image->s(), + image->t() + ); + + + OSType desiredType = cur->second; + GraphicsExportComponent geComp = NULL; + GWorldPtr gw = 0; + Handle dataHandle; + dataHandle = NewHandle(0); + + try { + OSErr err = OpenADefaultComponent(GraphicsExporterComponentType, desiredType, &geComp); + Rect bounds = {0,0, image->t(), image->s()}; + + err = QTNewGWorldFromPtr(&gw, k32ARGBPixelFormat, &bounds, 0,0,0, pixels, image->s()*4); + if (err != noErr) { + throw QTImportExportException(err, "could not create gworld for type " + ext); + } + + err = GraphicsExportSetInputGWorld(geComp, gw); + if (err != noErr) { + throw QTImportExportException(err, "could not set input gworld for type " + ext); + } + + err = GraphicsExportSetOutputHandle( geComp, dataHandle); + if (err != noErr) { + throw QTImportExportException(err, "could not set output file for type " + ext); + } + + // Set the compression quality (needed for JPEG, not necessarily for other formats) + if (desiredType == kQTFileTypeJPEG) { + err = GraphicsExportSetCompressionQuality(geComp, codecLosslessQuality); + if (err != noErr) { + throw QTImportExportException(err, "could not set compression for type " + ext); + } + } + + if(4 == numBytes) + { + err = GraphicsExportSetDepth( geComp, k32ARGBPixelFormat ); // depth + } + // else k24RGBPixelFormat??? + + // do the export + err = GraphicsExportDoExport(geComp, NULL); + if (err != noErr) { + throw QTImportExportException(err, "could not do the export for type " + ext); + } + + if (geComp != NULL) + CloseComponent(geComp); + + if (gw) DisposeGWorld (gw); + if (pixels) free(pixels); + + outStream.write(*dataHandle, GetHandleSize(dataHandle)); + DisposeHandle(dataHandle); + } + + + catch (QTImportExportException e) + { + setError(e.what()); + + if (geComp != NULL) CloseComponent(geComp); + if (gw != NULL) DisposeGWorld (gw); + if (pixels) free(pixels); + + DisposeHandle(dataHandle); + + } + +} + diff --git a/src/osgPlugins/quicktime/QTImportExport.h b/src/osgPlugins/quicktime/QTImportExport.h new file mode 100644 index 000000000..8c089db19 --- /dev/null +++ b/src/osgPlugins/quicktime/QTImportExport.h @@ -0,0 +1,65 @@ +/* + * QTImportExport.h + * cefix + * + * Created by Stephan Huber on 07.02.08. + * Copyright 2008 StephanMaximilianHuber, digitalmind. All rights reserved. + * + */ + +#include + +#ifndef QT_IMPORT_EXPORT_HEADER +#define QT_IMPORT_EXPORT_HEADER + +/** small helper class handling the im- and export of image data via quicktime */ +class QuicktimeImportExport { + + public: + /** ctor */ + QuicktimeImportExport(); + + /** readFromString tries to read a chunk of bytes and interpret it as an image. + * @param istream the input stream + * @param fileTypeHint you can speed up the conversion by providing a filename with extension, so quicktime has not to guess the image's type + * @param sizeHint useful, if you are streaming data, if you provide a sizeHint ony sizeHint bytes are read from the stream + */ + osg::Image* readFromStream(std::istream & inStream, const std::string& fileTypeHint, long sizeHint = 0); + + /** writes an osg::Image to a stream, using fileTypeHint as a hint whar format you want to write. */ + void writeToStream(std::ostream& outStream, osg::Image* image, const std::string& fileTypeHint) ; + + /** get the last error-message */ + const std::string getLastErrorString() { return _lastError; } + + /** return true if no error occured */ + bool success() { return (_error == false); } + + protected: + + /** flips an image */ + void flipImage(unsigned char* buffer, int bytesPerPixel, unsigned int width, unsigned height); + + /** do some swizzling, so osg can use the image */ + unsigned char* pepareBufferForOSG(unsigned char * buffer, int bytesPerPixel, unsigned int width, unsigned height); + + /** do some swizzling, so quicktime can use the image */ + unsigned char* prepareBufferForQuicktime(unsigned char* buffer, GLenum pixelFormat, int bytesPerPixel, unsigned int width, unsigned int height) ; + + /** sets an error-msg */ + void setError(const std::string& msg) { _lastError = msg; _error = true; } + + /** do the import */ + osg::Image* doImport(unsigned char* buffer, unsigned int dataSize, const std::string& fileTypeHint); + + + private: + bool _error; + std::string _lastError; + + +}; + + + +#endif \ No newline at end of file diff --git a/src/osgPlugins/quicktime/QTtexture.cpp b/src/osgPlugins/quicktime/QTtexture.cpp deleted file mode 100644 index 2774ca515..000000000 --- a/src/osgPlugins/quicktime/QTtexture.cpp +++ /dev/null @@ -1,396 +0,0 @@ -/* - * QTtexture.c - * Cocoa rostrumMIP - - * - * Created by philatki on Thu Nov 29 2001. - * Copyright (c) 2001 __MyCompanyName__. All rights reserved. - * - */ - -/* -PORTIONS OF THIS CODE ARE COPYRIGHT APPLE COMPUTER - - - Copyright: Copyright © 2001 Apple Computer, Inc., All Rights Reserved - - Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. - ("Apple") in consideration of your agreement to the following terms, and your - use, installation, modification or redistribution of this Apple software - constitutes acceptance of these terms. If you do not agree with these terms, - please do not use, install, modify or redistribute this Apple software. - - In consideration of your agreement to abide by the following terms, and subject - to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs - copyrights in this original Apple software (the "Apple Software"), to use, - reproduce, modify and redistribute the Apple Software, with or without - modifications, in source and/or binary forms; provided that if you redistribute - the Apple Software in its entirety and without modifications, you must retain - this notice and the following text and disclaimers in all such redistributions of - the Apple Software. Neither the name, trademarks, service marks or logos of - Apple Computer, Inc. may be used to endorse or promote products derived from the - Apple Software without specific prior written permission from Apple. Except as - expressly stated in this notice, no other rights or licenses, express or implied, - are granted by Apple herein, including but not limited to any patent rights that - may be infringed by your derivative works or by other works in which the Apple - Software may be incorporated. - - The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO - WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED - WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR - PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN - COMBINATION WITH YOUR PRODUCTS. - - IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE - GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION - OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT - (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN - ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifdef __APPLE__ -#include -#include // for image loading and decompression -#include // for file type support -#else -#include // for image loading and decompression -#include // for file type support -#endif - -#include // for OpenGL API -#include "QTUtils.h" -#include "QTtexture.h" - -#include -#include -#include - -// ================================== - -enum // how to scale image to power of two on read if scaling -{ - kNone = 1, - kNearest, // find nearest power of 2 - kNearestLess, // nearest power of 2 which is less than or equal image dimension - KNearestGreater, // nearest power of 2 which is greater than or equal image dimension - k32, // use this size specifically - k64, - k128, - k256, - k512, - k1024, - k2048, - k4096, - k8192 -}; - -// Default values for images loading -short gTextureScale = k1024; // for non-tiled images the type of texture scaling to do -short gMaxTextureSize = 4096; // maximum texture size to use for application -Boolean gfTileTextures = true; // are multiple tiled textures used to display image? -Boolean gfOverlapTextures = true; // do tiled textures overlapped to create correct filtering between tiles? (only applies if using tiled textures) -Boolean gfClientTextures = false; // 10.1+ only: texture from client memory -Boolean gfAGPTextures = false; // 10.1+ only: texture from AGP memory without loading to GPU can be set after inmage loaded -Boolean gfNPOTTextures = false; // 10.1+ only: use Non-Power Of Two (NPOT) textures - -// --------------------------------- - -static long GetScaledTextureDimFromImageDim (long imageDimension, short scaling); -static unsigned char * LoadBufferFromImageFile (FSSpec fsspecImage, short imageScale, - long * pOrigImageWidth, long * pOrigImageHeight, long *pOrigDepth, - long * pBufferWidth, long * pBufferHeight, long * pBufferDepth); - -// --------------------------------- - -// based on scaling determine the texture dimension which fits the image dimension passe in -// kNone: no scaling just use image dimension (will not guarentee support power for 2 textures) -// kNearest: find nearest power of 2 texture -// kNearestLess: find nearest power of 2 texture which is less than image dimension -// kNearestGreater: find nearest power of 2 texture which is greater than image dimension -// k32 - k1024: use this specific texture size - -static long GetScaledTextureDimFromImageDim (long imageDimension, short scaling) -{ - switch (scaling) - { - case kNone: // no scaling - return imageDimension; - break; - case kNearest: // scale to nearest power of two - { - // find power of 2 greater - long i = 0, texDim = 1, imageTemp = imageDimension; - while (imageTemp >>= 1) // while the dimension still has bits of data shift right (losing a bit at a time) - i++; // count shifts (i.e., powers of two) - texDim = texDim << i; // shift our one bit representation left the like amount (i.e., 2 ^ i) - if (texDim >= gMaxTextureSize) // if we are too big or at max size - return gMaxTextureSize; // then must use max texture size - // are we closer to greater pow 2 or closer to higher pow 2? - // compare to the power of two that is double of initial guess - else if (((texDim << 1) - imageDimension) < (imageDimension - texDim)) - return (texDim << 1); // if it is closer away then return double guess - else - return texDim; // otherwise orginal guess is closer so return this - } - break; - case kNearestLess: - { - // find power of 2 lower - long i = 0, texDim = 1; - while (imageDimension >>= 1) // while the dimension still has bits of data shift right (losing a bit at a time) - i++; // count shifts (i.e., powers of two) - texDim = texDim << i; // shift our one bit representation left the like amount (i.e., 2 ^ i) - return texDim; // returns the maxium power of two that is less than or equal the texture dimension - } - break; - case KNearestGreater: - { - // find power of 2 greater - long i = 0, texDim = 1; - while (imageDimension >>= 1) // while the dimension still has bits of data shift right (losing a bit at a time) - i++; // count shifts (i.e., powers of two) - texDim = texDim << (i + 1); // shift our one bit representation left the like amount (i.e., 2 ^ (i + 1)) - return texDim; // returns the minimum power of two that is greater than or equal the texture dimension - } - break; - case k32: // return hard values for texture dimension - return 32; - break; - case k64: - return 64; - break; - case k128: - return 128; - break; - case k256: - return 256; - break; - case k512: - return 512; - break; - case k1024: - return 1024; - break; - case k2048: - return 2048; - break; - case k4096: - return 8192; - break; - case k8192: - return 8192; - break; - } - return 0; -} - -static char errMess[256]; - -char *QTfailureMessage(void) { return errMess; } - -static unsigned char * LoadBufferFromImageFile ( FSSpec fsspecImage, - short imageScale, - long *pOrigImageWidth, long *pOrigImageHeight, long *pOrigImageDepth, - long *pBufferWidth, long *pBufferHeight, long *pBufferDepth) -{ - unsigned char * pImageBuffer = NULL; - GWorldPtr pGWorld = NULL; - OSType pixelFormat; - long rowStride; // length, in bytes, of a pixel row in the image - GraphicsImportComponent giComp; // componenet for importing image - Rect rectImage; // rectangle of source image - ImageDescriptionHandle hImageDesc; // handle to image description used to get image depth - MatrixRecord matrix; - GDHandle origDevice; // save field for current graphics device - CGrafPtr origPort; // save field for current graphics port - OSStatus err = noErr; // err return value - - // zero output params - *pOrigImageWidth = 0; - *pOrigImageHeight = 0; - *pOrigImageDepth = 0; - *pBufferWidth = 0; - *pBufferHeight = 0; - *pBufferDepth = 0; - - // get imorter for the image tyoe in file - GetGraphicsImporterForFile (&fsspecImage, &giComp); - if (err != noErr) { // if we have an error - sprintf ( errMess, "couldnt find importer\n"); - return NULL; // go away - } - - // Create GWorld - err = GraphicsImportGetNaturalBounds (giComp, &rectImage); // get the image bounds - if (err != noErr) { - sprintf ( errMess, "failed to GraphicsImportGetNaturalBounds"); - - return NULL; // go away if error - } - // create a handle for the image description - hImageDesc = (ImageDescriptionHandle) NewHandle (sizeof (ImageDescriptionHandle)); - HLock ((Handle) hImageDesc); // lock said handle - err = GraphicsImportGetImageDescription (giComp, &hImageDesc); // retrieve the image description - if (err != noErr) { - sprintf ( errMess, "failed to GraphicsImportGetImageDescription"); - - return NULL; // go away if error - } - *pOrigImageWidth = (long) (rectImage.right - rectImage.left); // find width from right side - left side bounds - *pOrigImageHeight = (long) (rectImage.bottom - rectImage.top); // same for height - - // we will use a 24-bit rgb texture or a 32-bit rgba - if ((**hImageDesc).depth == 32) *pOrigImageDepth=4; - else *pOrigImageDepth=3; - - *pBufferDepth = 32; // we will use a 32 bbit texture (this includes 24 bit images) - pixelFormat = k32ARGBPixelFormat; - - bool doScaling = false; - if (doScaling) - { - int scalefac; - // note - we want texels to stay square, so - if ((*pOrigImageWidth) > (*pOrigImageHeight)) - { - *pBufferWidth = GetScaledTextureDimFromImageDim ( *pOrigImageWidth, imageScale ) ; - *pBufferHeight=*pBufferWidth; - scalefac = X2Fix ((float) (*pBufferWidth) / (float) *pOrigImageWidth); - } - else - { - *pBufferHeight = GetScaledTextureDimFromImageDim (*pOrigImageHeight, imageScale ); - *pBufferWidth = *pBufferHeight; - scalefac = X2Fix ((float) (*pBufferHeight) / (float) *pOrigImageHeight); - } - } - else - { - // NOTE: scaling of the image removed, this is already done inside osg::Image - *pBufferWidth = *pOrigImageWidth; - *pBufferHeight= *pOrigImageHeight; - } - - SetRect (&rectImage, 0, 0, (short) *pBufferWidth, (short) *pBufferHeight); // l, t, r. b set image rectangle for creation of GWorld - rowStride = *pBufferWidth * *pBufferDepth >> 3; // set stride in bytes width of image * pixel depth in bytes - - const long len = rowStride * *pBufferHeight; - - pImageBuffer = new unsigned char [ len ]; // build new buffer exact size of image (stride * height) - - // pImageBuffer = (unsigned char *) NewPtrClear (rowStride * *pBufferHeight); // build new buffer exact size of image (stride * height) - - - if (NULL == pImageBuffer) - { - sprintf ( errMess, "failed to allocate image buffer"); - CloseComponent(giComp); // dump component - return NULL; // if we failed to allocate buffer - } - // create a new gworld using our unpadded buffer, ensure we set the pixel type correctly for the expected image bpp - QTNewGWorldFromPtr (&pGWorld, pixelFormat, &rectImage, NULL, NULL, 0, pImageBuffer, rowStride); - if (NULL == pGWorld) - { - sprintf ( errMess, "failed to create GWorld"); - // DisposePtr ((Ptr) pImageBuffer); // dump image buffer - delete [] pImageBuffer; - pImageBuffer = NULL; - CloseComponent(giComp); - return NULL; // if we failed to create gworld - } - - GetGWorld (&origPort, &origDevice); // save onscreen graphics port - - // decompress (draw) to gworld and thus fill buffer - SetIdentityMatrix (&matrix); // set transform matrix to identity (basically pass through) - - TranslateMatrix ( &matrix, -X2Fix(0.5f * *pOrigImageWidth), -X2Fix(0.5f * *pOrigImageHeight)); - ScaleMatrix (&matrix, X2Fix(1.0), X2Fix(-1.0), X2Fix (0.0), X2Fix (0.0)); - TranslateMatrix ( &matrix, X2Fix(0.5f * *pOrigImageWidth), X2Fix(0.5f * *pOrigImageHeight)); - - err = GraphicsImportSetMatrix(giComp, &matrix); // set our matrix as the importer matrix - - if (err == noErr) - err = GraphicsImportSetGWorld (giComp, pGWorld, NULL); // set the destination of the importer component - if (err == noErr) - err = GraphicsImportSetQuality (giComp, codecLosslessQuality); // we want lossless decompression - if ((err == noErr) && GetGWorldPixMap (pGWorld) && LockPixels (GetGWorldPixMap (pGWorld))) - GraphicsImportDraw (giComp); // if everything looks good draw image to locked pixmap - else - { - sprintf ( errMess, "failed to Set Matrix or GWorld or Quality or GetPixMap"); - - DisposeGWorld (pGWorld); // dump gworld - pGWorld = NULL; - // DisposePtr ((Ptr) pImageBuffer); // dump image buffer - delete [] pImageBuffer; - pImageBuffer = NULL; - CloseComponent(giComp); // dump component - return NULL; - } - - UnlockPixels (GetGWorldPixMap (pGWorld)); // unlock pixels - CloseComponent(giComp); // dump component - SetGWorld(origPort, origDevice); // set current graphics port to offscreen - // done with gworld and image since they are loaded to a texture - // DisposeGWorld (pGWorld); // do not need gworld - // pGWorld = NULL; - - return pImageBuffer; -} - -// new implementation of darwinPathToFSSpec -// the old code fails for me under os x 10.1.5 -// the code below is from an example of apple, modified to fit our needs -// the example can be found at -// - -FSSpec *darwinPathToFSSpec (char *fname ) { - - FSSpec *fs; - OSStatus result; -#if defined( __APPLE__ ) - FSRef ref; - - result = FSPathMakeRef( (UInt8*)fname, &ref, false); // fname is not a directory - if (result!=0) return NULL; - - /* and then convert the FSRef to an FSSpec */ - fs = (FSSpec *) malloc(sizeof(FSSpec)); - result = FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, NULL, fs, NULL); - if (result==0) return fs; // success - - // failed: - free(fs); - return NULL; -#else - // windows implementation to get a fsspec - fs = (FSSpec *) malloc(sizeof(FSSpec)); - result = NativePathNameToFSSpec(fname, fs, 0 /* flags */); - - if (0 == result) - return fs; - free(fs); - return NULL; -#endif -} - - -unsigned char* -LoadBufferFromDarwinPath ( const char *fname, long *origWidth, long *origHeight, long *origDepth, - long *buffWidth, long *buffHeight, - long *buffDepth) -{ - FSSpec *fs; - - fs=darwinPathToFSSpec ( const_cast( fname ) ); - - if (fs == NULL) { - sprintf ( errMess, "error creating path from fsspec" ); - return NULL; - } - else - return LoadBufferFromImageFile ( *fs, kNone, origWidth,origHeight,origDepth,buffWidth,buffHeight,buffDepth); -} diff --git a/src/osgPlugins/quicktime/QTtexture.h b/src/osgPlugins/quicktime/QTtexture.h deleted file mode 100644 index ad3481c9a..000000000 --- a/src/osgPlugins/quicktime/QTtexture.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef __QTTEXTURE_H__ -#define __QTTEXTURE_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -unsigned char* LoadBufferFromDarwinPath ( const char *fname, long *origWidth, - long *origHeight, long *origDepth, - long *buffWidth, long *buffHeight, long *buffDepth); - -char* QTfailureMessage(void); -FSSpec *darwinPathToFSSpec (char *fname ); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/osgPlugins/quicktime/ReaderWriterQT.cpp b/src/osgPlugins/quicktime/ReaderWriterQT.cpp index 9dce88e44..e4258eab3 100644 --- a/src/osgPlugins/quicktime/ReaderWriterQT.cpp +++ b/src/osgPlugins/quicktime/ReaderWriterQT.cpp @@ -14,6 +14,8 @@ #include #include #include +#include + #ifndef __APPLE__ #include "Components.h" @@ -27,7 +29,7 @@ #endif #include "QTUtils.h" #include "QTLiveUtils.h" -#include "QTtexture.h" +#include "QTImportExport.h" #include "QuicktimeImageStream.h" #include "QuicktimeLiveImageStream.h" @@ -169,7 +171,7 @@ public: osg::notify(osg::ALWAYS) << std::endl; osg::notify(osg::ALWAYS) << "Video Component/Input IDs follow: " << std::endl; osg::notify(osg::ALWAYS) << std::endl; - for (int device_input = 0; device_input < video_device_list.size(); ++device_input) + for (unsigned int device_input = 0; device_input < video_device_list.size(); ++device_input) { OSG_SGDevicePair device_pair = video_device_list[device_input]; osg::notify(osg::ALWAYS) << device_pair.first.c_str() << " " << device_pair.second.c_str() << std::endl; @@ -183,7 +185,7 @@ public: osg::notify(osg::ALWAYS) << std::endl; osg::notify(osg::ALWAYS) << "Audio Component/Input IDs follow: " << std::endl; osg::notify(osg::ALWAYS) << std::endl; - for (int device_input = 0; device_input < audio_device_list.size(); ++device_input) + for (unsigned int device_input = 0; device_input < audio_device_list.size(); ++device_input) { OSG_SGDevicePair device_pair = audio_device_list[device_input]; osg::notify(osg::ALWAYS) << device_pair.first.c_str() << " " << device_pair.second.c_str() << std::endl; @@ -246,332 +248,132 @@ public: return moov; } - long origWidth, origHeight,buffWidth,buffHeight,buffDepth,origDepth; + QuicktimeImportExport importer; - // NOTE - implememntation means that this will always return 32 bits, so it is hard to work out if - // an image was monochrome. So it will waste texture memory unless it gets a monochrome hint. + std::ifstream is; + is.open (fileName.c_str(), std::ios::binary | std::ios::in ); + is.seekg (0, std::ios::end); + long length = is.tellg(); + is.seekg (0, std::ios::beg); - unsigned char *pixels = LoadBufferFromDarwinPath ( fileName.c_str(), &origWidth,&origHeight,&origDepth, - &buffWidth,&buffHeight, - &buffDepth); + osg::ref_ptr image = importer.readFromStream(is, fileName, length); + is.close(); + if (!importer.success() || (image == NULL)) { + osg::notify(osg::WARN) << "Error reading file " << file << " : " << importer.getLastErrorString() << std::endl; + return ReadResult::ERROR_IN_READING_FILE; + } - // IMPORTANT - - // origDepth in BYTES, buffDepth in BITS - if (pixels == 0) - { - osg::notify(osg::WARN) << "LoadBufferFromDarwinPath failed " << fileName.c_str() << QTfailureMessage() << std::endl; - return 0; - } + _qtExitObserver.addMedia(image.get()); - unsigned int pixelFormat; - - switch(origDepth) - { - case 1 : - pixelFormat = GL_RGB; - break; - case 2 : - pixelFormat = GL_LUMINANCE_ALPHA; - break; - case 3 : - pixelFormat = GL_RGB; - break; - case 4 : - pixelFormat = GL_RGBA; - break; - default : - osg::notify(osg::WARN) << "Unknown file type in " << fileName.c_str() << " with " << origDepth << std::endl; - pixelFormat = (GLenum)-1; - return 0; - break; - } - - { - unsigned char *srcp=pixels, *dstp=pixels; - - int i, j; - - // swizzle entire image in-place - unsigned char r, g, b, a; - for (i=0; igetOptionString()); + std::string opt; + while (iss >> opt) { - - /* - since 8-bit tgas will get expanded into colour, have to use RGB code for 8-bit images - - case 1 : - for (j=0; j image = importer.readFromStream(is, filename, sizeHint); + + if (!importer.success() || (image == NULL)) { + osg::notify(osg::WARN) << "Error reading from stream " << importer.getLastErrorString() << std::endl; + return ReadResult::ERROR_IN_READING_FILE; + } + _qtExitObserver.addMedia(image.get()); + return image.release(); + + } + virtual WriteResult writeImage(const osg::Image &img,const std::string& fileName, const osgDB::ReaderWriter::Options*) const + { + std::string ext = osgDB::getFileExtension(fileName); + if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED; + initQuicktime(); - Image* image = new Image(); - image->setFileName(fileName.c_str()); - image->setImage(buffWidth,buffHeight,1, - buffDepth >> 3, - pixelFormat, - GL_UNSIGNED_BYTE, - pixels, - osg::Image::USE_NEW_DELETE ); + //Buidl map of extension <-> osFileTypes + std::map extmap; - notify(DEBUG_INFO) << "image read ok "<("jpg", kQTFileTypeJPEG)); + extmap.insert(std::pair("jpeg", kQTFileTypeJPEG)); + extmap.insert(std::pair("bmp", kQTFileTypeBMP)); + extmap.insert(std::pair("tif", kQTFileTypeTIFF)); + extmap.insert(std::pair("tiff", kQTFileTypeTIFF)); + extmap.insert(std::pair("png", kQTFileTypePNG)); + extmap.insert(std::pair("gif", kQTFileTypeGIF)); + extmap.insert(std::pair("psd", kQTFileTypePhotoShop)); + extmap.insert(std::pair("sgi", kQTFileTypeSGIImage)); + extmap.insert(std::pair("rgb", kQTFileTypeSGIImage)); + extmap.insert(std::pair("rgba", kQTFileTypeSGIImage)); - // add the media to the observer for proper clean up on exit - _qtExitObserver.addMedia(image); + std::map::iterator cur = extmap.find(ext); - return image; - } - - virtual WriteResult writeImage(const osg::Image &img,const std::string& fileName, const osgDB::ReaderWriter::Options*) const - { - std::string ext = osgDB::getFileExtension(fileName); - if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED; - - initQuicktime(); - - //Buidl map of extension <-> osFileTypes - std::map extmap; - - extmap.insert(std::pair("jpg", kQTFileTypeJPEG)); - extmap.insert(std::pair("jpeg", kQTFileTypeJPEG)); - extmap.insert(std::pair("bmp", kQTFileTypeBMP)); - extmap.insert(std::pair("tif", kQTFileTypeTIFF)); - extmap.insert(std::pair("tiff", kQTFileTypeTIFF)); - extmap.insert(std::pair("png", kQTFileTypePNG)); - extmap.insert(std::pair("gif", kQTFileTypeGIF)); - extmap.insert(std::pair("psd", kQTFileTypePhotoShop)); - // extmap.insert(std::pair("tga", kQTFileTypeTargaImage)); - extmap.insert(std::pair("sgi", kQTFileTypeSGIImage)); - extmap.insert(std::pair("rgb", kQTFileTypeSGIImage)); - extmap.insert(std::pair("rgba", kQTFileTypeSGIImage)); - - std::map::iterator cur = extmap.find(ext); - - // can not handle this type of file, perhaps a movie? - if (cur == extmap.end()) + // can not handle this type of file, perhaps a movie? + if (cur == extmap.end()) return WriteResult::FILE_NOT_HANDLED; - OSType desiredType = cur->second; - GraphicsExportComponent geComp = NULL; + std::ofstream os(fileName.c_str(), std::ios::binary | std::ios::trunc | std::ios::out); + if(os.good()) + { + QuicktimeImportExport exporter; + exporter.writeToStream(os, const_cast(&img), fileName); + + if (exporter.success()) + return WriteResult::FILE_SAVED; + } - OSErr err = OpenADefaultComponent(GraphicsExporterComponentType, desiredType, &geComp); - - if (err != noErr) { - osg::notify(osg::WARN) << "ReaderWriterQT: could not open Graphics epxorter for type " << ext << ", Err: " << err << std::endl; - return WriteResult::FILE_NOT_HANDLED; - } - - GWorldPtr gw = NULL; - - // we are converting the images back to 32bit, it seems, that quicktime can't handle others - - unsigned long desiredPixelFormat = k32ARGBPixelFormat; - - - - // we need to swizzle the colours again :) - unsigned int numBytes = img.computeNumComponents(img.getPixelFormat()); - - unsigned int buffWidth = img.s(); - unsigned int buffHeight = img.t(); - char * pixels = (char*) malloc(buffHeight * buffWidth * 4); - - - - const unsigned char *srcp = img.data(); - char *dstp=pixels; - unsigned int i, j; - for (i=0; i(fileName.c_str()) ); - if (fileSpec == NULL) { - osg::notify(osg::WARN) << "ReaderWriterQT: could not get FSSpec" << std::endl; - throw err; - } - - err = GraphicsExportSetInputGWorld(geComp, gw); - if (err != noErr) { - osg::notify(osg::WARN) << "ReaderWriterQT: could not set input gworld for type " << ext << ", Err: " << err << std::endl; - throw err; - } - - err = GraphicsExportSetOutputFile(geComp, fileSpec); - if (err != noErr) { - osg::notify(osg::WARN) << "ReaderWriterQT: could not set output file for type " << ext << ", Err: " << err << std::endl; - throw err; - } - - // Set the compression quality (needed for JPEG, not necessarily for other formats) - if (desiredType == kQTFileTypeJPEG) { - err = GraphicsExportSetCompressionQuality(geComp, codecLosslessQuality); - if (err != noErr) { - osg::notify(osg::WARN) << "ReaderWriterQT: could not set compression for type " << ext << ", Err: " << err << std::endl; - throw err; + return WriteResult::ERROR_IN_WRITING_FILE; + } + + virtual WriteResult writeImage (const osg::Image& img, std::ostream& os, const Options* options=NULL) const + { + std::string filename = "file.jpg"; // use jpeg if not otherwise specified + + if (options) { + std::istringstream iss(options->getOptionString()); + std::string opt; + while (iss >> opt) + { + int index = opt.find( "=" ); + if( opt.substr( 0, index ) == "filename" || + opt.substr( 0, index ) == "FILENAME" ) + { + filename = opt.substr( index+1 ); + } } - } - - if(32 == numBytes) - { - err = GraphicsExportSetDepth( geComp, - k32ARGBPixelFormat ); // depth - } - // else k24RGBPixelFormat??? - - // do the export - err = GraphicsExportDoExport(geComp, NULL); - if (err != noErr) { - osg::notify(osg::WARN) << "ReaderWriterQT: could not save file for type " << ext << ", Err: " << err << std::endl; - throw err; - } - - if (geComp != NULL) - CloseComponent(geComp); - - DisposeGWorld (gw); - if (fileSpec != NULL ) free(fileSpec); - if (pixels) free(pixels); - - return WriteResult::FILE_SAVED; - } + } + + QuicktimeImportExport exporter; + exporter.writeToStream(os, const_cast(&img), filename); + + if (exporter.success()) + return WriteResult::FILE_SAVED; + + return WriteResult::ERROR_IN_WRITING_FILE; + } - catch (...) { - - if (geComp != NULL) CloseComponent(geComp); - if (gw != NULL) DisposeGWorld (gw); - if (fileSpec != NULL ) free(fileSpec); - if (pixels) free(pixels); - - return WriteResult::ERROR_IN_WRITING_FILE; - } - - } - - - mutable QuicktimeExitObserver _qtExitObserver; + mutable QuicktimeExitObserver _qtExitObserver; }; // now register with Registry to instantiate the above