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 :)"
This commit is contained in:
@@ -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
|
||||
)
|
||||
|
||||
526
src/osgPlugins/quicktime/QTImportExport.cpp
Normal file
526
src/osgPlugins/quicktime/QTImportExport.cpp
Normal file
@@ -0,0 +1,526 @@
|
||||
/*
|
||||
* QTImportExport.cpp
|
||||
* cefix
|
||||
*
|
||||
* Created by Stephan Huber on 07.02.08.
|
||||
* Copyright 2008 __MyCompanyName__. All rights reserved.
|
||||
*
|
||||
*/
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include "QTImportExport.h"
|
||||
#include "QTUtils.h"
|
||||
|
||||
#include <osgDB/FileNameUtils>
|
||||
|
||||
|
||||
/** 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<unsigned char*>(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<char **>(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<char **>(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<char **>(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<std::string, OSType> 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<std::string, OSType>::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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
65
src/osgPlugins/quicktime/QTImportExport.h
Normal file
65
src/osgPlugins/quicktime/QTImportExport.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* QTImportExport.h
|
||||
* cefix
|
||||
*
|
||||
* Created by Stephan Huber on 07.02.08.
|
||||
* Copyright 2008 StephanMaximilianHuber, digitalmind. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osg/Image>
|
||||
|
||||
#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
|
||||
@@ -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 <20> 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<6C>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 <Carbon/Carbon.h>
|
||||
#include <QuickTime/ImageCompression.h> // for image loading and decompression
|
||||
#include <QuickTime/QuickTimeComponents.h> // for file type support
|
||||
#else
|
||||
#include <ImageCompression.h> // for image loading and decompression
|
||||
#include <QuickTimeComponents.h> // for file type support
|
||||
#endif
|
||||
|
||||
#include <osg/GL> // for OpenGL API
|
||||
#include "QTUtils.h"
|
||||
#include "QTtexture.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// ==================================
|
||||
|
||||
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
|
||||
// <http://developer.apple.com/samplecode/Sample_Code/Files/MoreFilesX.htm>
|
||||
|
||||
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<char*>( fname ) );
|
||||
|
||||
if (fs == NULL) {
|
||||
sprintf ( errMess, "error creating path from fsspec" );
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
return LoadBufferFromImageFile ( *fs, kNone, origWidth,origHeight,origDepth,buffWidth,buffHeight,buffDepth);
|
||||
}
|
||||
@@ -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
|
||||
@@ -14,6 +14,8 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
#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<osg::Image> 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; i<buffHeight; i++ ) {
|
||||
|
||||
switch (origDepth)
|
||||
return image.release();
|
||||
}
|
||||
|
||||
virtual ReadResult readImage (std::istream& is, const osgDB::ReaderWriter::Options* options=NULL) const
|
||||
{
|
||||
std::string filename = "";
|
||||
long sizeHint(0);
|
||||
// check options for a file-type-hint
|
||||
if (options) {
|
||||
std::istringstream iss(options->getOptionString());
|
||||
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<buffWidth; j++ ) {
|
||||
dstp[0]=srcp[2];
|
||||
srcp+=4;
|
||||
dstp++;
|
||||
}
|
||||
break;
|
||||
*/
|
||||
case 2 :
|
||||
for (j=0; j<buffWidth; j++ ) {
|
||||
dstp[1]=srcp[0];
|
||||
dstp[0]=srcp[2];
|
||||
srcp+=4;
|
||||
dstp+=2;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1 :
|
||||
case 3 :
|
||||
for (j=0; j<buffWidth; j++ ) {
|
||||
dstp[0]=srcp[1];
|
||||
dstp[1]=srcp[2];
|
||||
dstp[2]=srcp[3];
|
||||
srcp+=4;
|
||||
dstp+=3;
|
||||
}
|
||||
break;
|
||||
case 4 :
|
||||
for (j=0; j<buffWidth; j++ ) {
|
||||
r=srcp[1];
|
||||
g=srcp[2];
|
||||
b=srcp[3];
|
||||
a=srcp[0];
|
||||
|
||||
dstp[0]=r;
|
||||
dstp[1]=g;
|
||||
dstp[2]=b;
|
||||
dstp[3]=a;
|
||||
|
||||
srcp+=4;
|
||||
dstp+=4;
|
||||
}
|
||||
break;
|
||||
default :
|
||||
// osg::notify(osg::WARN) << "ERROR IN RETURNED PIXEL DEPTH, CANNOT COPE" << std::endl;
|
||||
return 0;
|
||||
break;
|
||||
int index = opt.find( "=" );
|
||||
if( opt.substr( 0, index ) == "filename" ||
|
||||
opt.substr( 0, index ) == "FILENAME" )
|
||||
{
|
||||
filename = opt.substr( index+1 );
|
||||
} else if( opt.substr( 0, index ) == "size" ||
|
||||
opt.substr( 0, index ) == "SIZE" )
|
||||
{
|
||||
std::string sizestr = opt.substr( index+1 );
|
||||
sizeHint = atol(sizestr.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QuicktimeImportExport importer;
|
||||
osg::ref_ptr<osg::Image> 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<std::string, OSType> extmap;
|
||||
|
||||
notify(DEBUG_INFO) << "image read ok "<<buffWidth<<" "<<buffHeight<<std::endl;
|
||||
extmap.insert(std::pair<std::string, OSType>("jpg", kQTFileTypeJPEG));
|
||||
extmap.insert(std::pair<std::string, OSType>("jpeg", kQTFileTypeJPEG));
|
||||
extmap.insert(std::pair<std::string, OSType>("bmp", kQTFileTypeBMP));
|
||||
extmap.insert(std::pair<std::string, OSType>("tif", kQTFileTypeTIFF));
|
||||
extmap.insert(std::pair<std::string, OSType>("tiff", kQTFileTypeTIFF));
|
||||
extmap.insert(std::pair<std::string, OSType>("png", kQTFileTypePNG));
|
||||
extmap.insert(std::pair<std::string, OSType>("gif", kQTFileTypeGIF));
|
||||
extmap.insert(std::pair<std::string, OSType>("psd", kQTFileTypePhotoShop));
|
||||
extmap.insert(std::pair<std::string, OSType>("sgi", kQTFileTypeSGIImage));
|
||||
extmap.insert(std::pair<std::string, OSType>("rgb", kQTFileTypeSGIImage));
|
||||
extmap.insert(std::pair<std::string, OSType>("rgba", kQTFileTypeSGIImage));
|
||||
|
||||
// add the media to the observer for proper clean up on exit
|
||||
_qtExitObserver.addMedia(image);
|
||||
std::map<std::string, OSType>::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<std::string, OSType> extmap;
|
||||
|
||||
extmap.insert(std::pair<std::string, OSType>("jpg", kQTFileTypeJPEG));
|
||||
extmap.insert(std::pair<std::string, OSType>("jpeg", kQTFileTypeJPEG));
|
||||
extmap.insert(std::pair<std::string, OSType>("bmp", kQTFileTypeBMP));
|
||||
extmap.insert(std::pair<std::string, OSType>("tif", kQTFileTypeTIFF));
|
||||
extmap.insert(std::pair<std::string, OSType>("tiff", kQTFileTypeTIFF));
|
||||
extmap.insert(std::pair<std::string, OSType>("png", kQTFileTypePNG));
|
||||
extmap.insert(std::pair<std::string, OSType>("gif", kQTFileTypeGIF));
|
||||
extmap.insert(std::pair<std::string, OSType>("psd", kQTFileTypePhotoShop));
|
||||
// extmap.insert(std::pair<std::string, OSType>("tga", kQTFileTypeTargaImage));
|
||||
extmap.insert(std::pair<std::string, OSType>("sgi", kQTFileTypeSGIImage));
|
||||
extmap.insert(std::pair<std::string, OSType>("rgb", kQTFileTypeSGIImage));
|
||||
extmap.insert(std::pair<std::string, OSType>("rgba", kQTFileTypeSGIImage));
|
||||
|
||||
std::map<std::string, OSType>::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<osg::Image*>(&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<buffHeight; i++ ) {
|
||||
|
||||
switch (numBytes) {
|
||||
case 1 :
|
||||
dstp[0] = 0;
|
||||
dstp[1] = srcp[0];
|
||||
dstp[2] = srcp[0];
|
||||
dstp[3] = srcp[0];
|
||||
srcp+=1;
|
||||
dstp+=4;
|
||||
|
||||
break;
|
||||
case 3 :
|
||||
for (j=0; j<buffWidth; j++ ) {
|
||||
dstp[0]=0;
|
||||
dstp[1]=srcp[0];
|
||||
dstp[2]=srcp[1];
|
||||
dstp[3]=srcp[2];
|
||||
|
||||
srcp+=3;
|
||||
dstp+=4;
|
||||
}
|
||||
break;
|
||||
|
||||
case 4 :
|
||||
for (j=0; j<buffWidth; j++ ) {
|
||||
// shift from RGBA to ARGB
|
||||
dstp[0]=srcp[3];
|
||||
dstp[1]=srcp[0];
|
||||
dstp[2]=srcp[1];
|
||||
dstp[3]=srcp[2];
|
||||
|
||||
srcp+=4;
|
||||
dstp+=4;
|
||||
}
|
||||
break;
|
||||
|
||||
default :
|
||||
// osg::notify(osg::WARN) << "ERROR IN RETURNED PIXEL DEPTH, CANNOT COPE" << std::endl;
|
||||
return WriteResult::ERROR_IN_WRITING_FILE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Flip the image
|
||||
unsigned imageSize = buffWidth*buffHeight*4;
|
||||
char *tBuffer = (char*)malloc((size_t)imageSize);
|
||||
unsigned int rowBytes = buffWidth * 4;
|
||||
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);
|
||||
free(tBuffer);
|
||||
|
||||
FSSpec* fileSpec = NULL;
|
||||
|
||||
try {
|
||||
Rect bounds;
|
||||
SetRect(&bounds, 0,0, img.s(), img.t());
|
||||
|
||||
err = QTNewGWorldFromPtr(&gw, desiredPixelFormat, &bounds, 0,0,0, pixels, buffWidth*4);
|
||||
if (err != noErr) {
|
||||
osg::notify(osg::WARN) << "ReaderWriterQT: could not create gworld for type " << ext << ", Err: " << err << std::endl;
|
||||
throw err;
|
||||
}
|
||||
|
||||
// create a dummy file at location
|
||||
FILE *fp = fopen(fileName.c_str(), "wb");
|
||||
if (!fp) {
|
||||
osg::notify(osg::WARN) << "ReaderWriterQT: could not create file!" << std::endl;
|
||||
throw err;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
// get an FSSpec to the file, so quicktime can handle the file.
|
||||
fileSpec = darwinPathToFSSpec( const_cast<char*>(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<osg::Image*>(&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
|
||||
|
||||
Reference in New Issue
Block a user