Files
OpenSceneGraph/src/osgPlugins/quicktime/QTImportExport.cpp

528 lines
16 KiB
C++

/*
* 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) {
//hmm...not good, pascal string limit is 255!
//do some error handling maybe?!
throw QTImportExportException(0, "filename length limit exceeded");
}
CopyCStringToPascal(filename.c_str(), pstr);
// 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 = 0;
// 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 bytesPerPixel = depth / 8;
unsigned int glpixelFormat;
switch(bytesPerPixel) {
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, bytesPerPixel, xsize, ysize);
delete[] imageData;
osg::Image* image = new osg::Image();
image->setFileName(fileTypeHint.c_str());
image->setImage(xsize,ysize,1,
bytesPerPixel,
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);
}
}