From Stephan Huber and Riccardo Corsi, port of QuickTime plugin to Windows

This commit is contained in:
Robert Osfield
2006-12-05 16:29:11 +00:00
parent 905857acd7
commit 875668b84d
9 changed files with 941 additions and 642 deletions

View File

@@ -6,21 +6,17 @@
* Copyright (c) 2004 __MyCompanyName__. All rights reserved.
*
*/
#include <OpenGL/gl.h>
#include <OpenGL/glext.h>
#include <osg/GL>
#include <osg/Endian>
#include <osgDB/FileNameUtils>
#include "MovieData.h"
#include "QTUtils.h"
using namespace osgQuicktime;
namespace osg {
MovieData::MovieData() : _movie(NULL), _gw(NULL), _fError(false)
MovieData::MovieData() : _pointer(NULL), _movie(NULL), _gw(NULL), _fError(false)
{
}
@@ -28,6 +24,7 @@ MovieData::MovieData() : _movie(NULL), _gw(NULL), _fError(false)
MovieData::~MovieData()
{
if (_pointer) free(_pointer);
if (_gw) DisposeGWorld(_gw);
if (_movie) DisposeMovie(_movie);
}
@@ -35,10 +32,13 @@ MovieData::~MovieData()
void MovieData::load(osg::Image* image, std::string filename, float startTime)
void MovieData::load(osg::Image* image, std::string afilename, float startTime)
{
Rect bounds;
std::string filename = afilename;
if (!osgDB::isFileNameNativeStyle(filename))
filename = osgDB::convertFileNameToNativeStyle(filename);
osg::notify(osg::INFO) << "MovieData :: opening movie '" << filename << "'" << std::endl;
OSStatus err = MakeMovieFromPath(filename.c_str(),&_movie);
@@ -75,6 +75,9 @@ void MovieData::load(osg::Image* image, std::string filename, float startTime)
UpdateMovie(_movie);
SetMovieRate(_movie,0);
SetMovieActive(_movie, true);
UpdateMovie(_movie);
MoviesTask(_movie,0);
}
}
@@ -88,30 +91,30 @@ void MovieData::_initImage(osg::Image* image)
{
void* buffer;
char* pointer;
_textureWidth = ((_movieWidth + 7) >> 3) << 3;
_textureHeight = _movieHeight;
// some magic alignment...
pointer = (char*)malloc(4 * _textureWidth * _textureHeight + 32);
_pointer = (char*)malloc(4 * _textureWidth * _textureHeight + 32);
if (pointer == NULL) {
if (_pointer == NULL) {
osg::notify(osg::FATAL) << "MovieData: " << "Can't allocate texture buffer" << std::endl;
_fError= true;
}
buffer = (void*)(((unsigned long)(pointer + 31) >> 5) << 5);
buffer = (void*)(((unsigned long)(_pointer + 31) >> 5) << 5);
GLenum internalFormat = (getCpuByteOrder()==osg::BigEndian)?
GL_UNSIGNED_INT_8_8_8_8_REV :
GL_UNSIGNED_INT_8_8_8_8;
image->setImage(_textureWidth,_textureHeight,0,
image->setImage(_textureWidth,_textureHeight,1,
(GLint) GL_RGBA8,
(GLenum)GL_BGRA_EXT,
internalFormat,
(unsigned char*) buffer,osg::Image::USE_MALLOC_FREE,4);
internalformat,
(unsigned char*) buffer,osg::Image::NO_DELETE,4);
}
@@ -162,6 +165,36 @@ void MovieData::_initGWorldStuff(osg::Image * image) {
}
void MovieData::setMovieTime(float atime) {
float time = (atime > getMovieDuration()) ? getMovieDuration() : atime;
TimeValue t = (TimeValue) (time * _timescale);
SetMovieTimeValue(_movie,t);
_checkMovieError("setMovieTime failed");
UpdateMovie(_movie);
MoviesTask(_movie,0);
}
void MovieData::setMovieRate(float rate) {
// osg::notify(osg::ALWAYS) << "new movierate: " << rate << " current: " << getMovieRate() << std::endl;
_movieRate = rate;
if (rate != 0) {
PrerollMovie(_movie, GetMovieTime(_movie,NULL), X2Fix(rate));
_checkMovieError("PrerollMovie failed");
}
SetMovieRate(_movie, X2Fix(rate));
_checkMovieError("setMovieRate failed");
MoviesTask(_movie, 0);
_checkMovieError("MoviesTask failed");
UpdateMovie(_movie);
_checkMovieError("UpdateMovie failed");
}
} // namespace

View File

@@ -13,35 +13,90 @@
#include <osg/Notify>
#include <osg/Image>
#include <string>
#include <QuickTime/QuickTime.h>
namespace osg {
#include "QTUtils.h"
/**
* the class MovieData encapsulates all quicktime-related stuff, so it doesn't polute the namespaces
* it handles all calls to quicktime etc... It is mainly used by the QuicktimeImageStream, it is
* rarely needed in other contexts
*/
class MovieData {
public:
/** default constructor */
MovieData();
/** default destructor */
~MovieData();
/**
* loads a movie, start it playing at startTime, use Image for the storage
* @param image the image, to use as storage
* @param fileName the movie to open
* @param startTime the starttime to begin with
*/
void load(osg::Image* image, std::string fileName, float startTime = 0.0f);
float getMovieDuration() { return GetMovieDuration(_movie)/(float)_timescale;}
float getMovieTime() {return GetMovieTime(_movie,NULL)/(float)_timescale; }
Movie &getMovie() { return _movie; }
/** @return the duration for this movie in seconds */
inline float getMovieDuration() { return GetMovieDuration(_movie)/(float)_timescale;}
/** @return the current position for this movie in seconds */
inline float getMovieTime() {return GetMovieTime(_movie,NULL)/(float)_timescale; }
/** stes the movietime */
void setMovieTime(float atime);
/** @return the Movie-handle, to use it with other quicktime-calls */
inline Movie &getMovie() { return _movie; }
/** @return the current movieRate */
inline float getMovieRate() { return Fix2X(GetMovieRate(_movie)); }
/** @return returns the cached movierate, may differ to the real movierate */
inline float getCachedMovieRate() { return _movieRate; }
/** sets the MovieRate for this movie */
void setMovieRate(float rate);
/** sets the volume for the soundtrack of this movie */
void setVolume(float volume) { SetMovieVolume(_movie,(short)(volume*255));}
float getVolume() { return GetMovieVolume(_movie) / 255.0f; }
void setAudioBalance(float f) {
Float32 balance = f;
SetMovieAudioBalance(_movie, balance, 0);
}
float getAudioBalance() {
Float32 balance;
float f;
GetMovieAudioBalance(_movie, &balance, 0);
f = balance;
return f;
}
protected:
char* _pointer;
Movie _movie;
GWorldPtr _gw;
unsigned int _movieWidth, _movieHeight, _textureWidth, _textureHeight;
float _timescale;
bool _fError;
float _movieRate;
/** inits the image for storage */
void _initImage(osg::Image* image);
/** inits the gWorld, where the movie gets drawn into */
void _initGWorldStuff(osg::Image * image);
/** inits the texture */
void _initTexture();
/** checks for an movie-error */
inline void _checkMovieError(std::string msg) {
if (GetMoviesError()) {
_fError = true;
@@ -52,6 +107,6 @@ namespace osg {
};
} // namespace
#endif

View File

@@ -6,33 +6,75 @@
* Copyright (c) 2002 digital mind. All rights reserved.
*
*/
#include <osg/ref_ptr>
#include <osg/Referenced>
#include <osg/Notify>
#include <QuickTime/QuickTime.h>
#include <Carbon/Carbon.h>
#include "QTUtils.h"
#include "osgDB/Registry"
using namespace std;
namespace osgQuicktime {
class QuicktimeInitializer : public osg::Referenced {
public:
QuicktimeInitializer() :osg::Referenced() {
static bool s_fQuicktimeInited = 0;
if (!s_fQuicktimeInited) {
#ifndef __APPLE__
InitializeQTML(0);
#endif
OSErr err = EnterMovies();
if (err!=0)
osg::notify(osg::FATAL) << "Error while initializing quicktime: " << err << endl;
else
osg::notify(osg::DEBUG_INFO) << "Quicktime initialized successfully" << endl;
registerQTReader();
s_fQuicktimeInited = true;
}
}
~QuicktimeInitializer() {
#ifndef __APPLE__
ExitMovies();
#endif
//osg::notify(osg::DEBUG_INFO) << "Quicktime deinitialized successfully" << endl;
}
protected:
void registerQTReader() {
osgDB::Registry* r = osgDB::Registry::instance();
r->addFileExtensionAlias("jpg", "qt");
r->addFileExtensionAlias("jpe", "qt");
r->addFileExtensionAlias("jpeg", "qt");
r->addFileExtensionAlias("tif", "qt");
r->addFileExtensionAlias("tiff", "qt");
r->addFileExtensionAlias("gif", "qt");
r->addFileExtensionAlias("png", "qt");
r->addFileExtensionAlias("psd", "qt");
r->addFileExtensionAlias("tga", "qt");
r->addFileExtensionAlias("mov", "qt");
r->addFileExtensionAlias("avi", "qt");
r->addFileExtensionAlias("mpg", "qt");
r->addFileExtensionAlias("mpv", "qt");
r->addFileExtensionAlias("dv", "qt");
r->addFileExtensionAlias("mp4", "qt");
r->addFileExtensionAlias("m4v", "qt");
}
};
void initQuicktime(bool erase) {
static osg::ref_ptr<QuicktimeInitializer> s_qt_init = new QuicktimeInitializer();
if (erase)
s_qt_init = NULL;
}
void initQuicktime() {
static bool s_fQuicktimeInited = 0;
OSErr err;
if (!s_fQuicktimeInited) {
err = EnterMovies();
if (err!=0)
osg::notify(osg::FATAL) << "Error while initializing quicktime: " << err << endl;
else
osg::notify(osg::DEBUG_INFO) << "Quicktime initialized successfully" << endl;
s_fQuicktimeInited = true;
}
else
osg::notify(osg::DEBUG_INFO) << "Quicktime already initialized ..." << endl;
void exitQuicktime() {
initQuicktime(true);
}
@@ -43,6 +85,7 @@ namespace osgQuicktime {
// wandelt einen Posix-Pfad in ein FSSpec um.
// ---------------------------------------------------------------------------
OSStatus MakeFSSpecFromPath(const char* path, FSSpec* spec) {
#ifdef __APPLE__
OSStatus err;
FSRef fsref;
Boolean isdir;
@@ -58,17 +101,23 @@ namespace osgQuicktime {
// Ditto
err = FSGetCatalogInfo(&fsref, kFSCatInfoNone, NULL, NULL, spec, NULL);
return err;
#else
return -1;
#endif
}
// ---------------------------------------------------------------------------
// MakeMovieFromPath
// erzeugt movie-objekt aus Pfad
// ---------------------------------------------------------------------------
OSStatus MakeMovieFromPath(const char* path, Movie* movie) {
OSStatus err;
FSSpec spec;
short resref;
#ifdef __APPLE__
MakeFSSpecFromPath(path, &spec);
#else
err = NativePathNameToFSSpec((char*)path, &spec, 0 /* flags */);
#endif
err = OpenMovieFile(&spec, &resref, fsRdPerm);
if (err!=0) return err;
err = NewMovieFromFile(movie, resref, NULL, NULL, 0, NULL);
@@ -78,4 +127,3 @@ namespace osgQuicktime {
} // namespace

View File

@@ -7,14 +7,56 @@
*
*/
#include <Carbon/Carbon.h>
#include <QuickTime/QuickTime.h>
#ifndef QTUTILS_HEADER_
#define QTUTILS_HEADER_
namespace osgQuicktime {
#ifdef __APPLE__
#include <Quicktime/Quicktime.h>
#include <Carbon/Carbon.h>
#else
#include <QTML.h>
#include <Movies.h>
#include <Quickdraw.h>
#include <QDOffscreen.h>
#include <QuicktimeComponents.h>
#include <FixMath.h>
#include <CGBitmapContext.h>
#include <CGImage.h>
#include <CGColorSpace.h>
#include <ImageCompression.h>
void initQuicktime();
extern "C" {
/** legacy function for Windows */
inline void GetPortBounds(GWorldPtr gw, Rect* rect) {
(*rect) = (gw->portRect);
}
/** legacy function for Windows */
inline PixMapHandle GetPortPixMap (CGrafPtr port) {
return port->portPixMap;
}
}
OSStatus MakeFSSpecFromPath(const char* path, FSSpec* spec);
OSStatus MakeMovieFromPath(const char* path, Movie* movie);
#define SetRect MacSetRect
#define OffsetRect MacOffsetRect
}
#endif
/**
* inits Quicktime, if erase = true, the Quicktime-stuff gets cleaned, call it before your app exits
* @param erase true, if you want to cleanup quicktime-related stuff
*/
void initQuicktime(bool erase = false);
/** cleans up all quicktime-related stuff */
void exitQuicktime();
/** constructs an FSSpec out of an path */
OSStatus MakeFSSpecFromPath(const char* path, FSSpec* spec);
/** opens a movie from a path */
OSStatus MakeMovieFromPath(const char* path, Movie* movie);
#endif

View File

@@ -50,18 +50,23 @@ PORTIONS OF THIS CODE ARE COPYRIGHT APPLE COMPUTER -
*
*/
#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 <OpenGL/gl.h> // for OpenGL API
#include <OpenGL/glu.h> // for OpenGL API
#include <OpenGL/glext.h> // for OpenGL extension support
#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
@@ -346,15 +351,10 @@ FSSpec *darwinPathToFSSpec (char *fname ) {
FSSpec *fs;
OSStatus result;
#if defined( __APPLE__ )
FSRef ref;
/* convert the POSIX path to an FSRef */
#if defined( __APPLE__ ) && ( __GNUC__ > 3 )
result = FSPathMakeRef( (UInt8*)fname, &ref, false); // fname is not a directory
#else
result = FSPathMakeRef(fname, &ref, false); // fname is not a directory
#endif
result = FSPathMakeRef( (UInt8*)fname, &ref, false); // fname is not a directory
if (result!=0) return NULL;
/* and then convert the FSRef to an FSSpec */
@@ -365,6 +365,16 @@ FSSpec *darwinPathToFSSpec (char *fname ) {
// 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
}

View File

@@ -6,9 +6,9 @@ extern "C" {
#endif
unsigned char* LoadBufferFromDarwinPath ( const char *fname, long *origWidth,
long *origHeight, long *origDepth,
long *buffWidth, long *buffHeight, long *buffDepth);
long *origHeight, long *origDepth,
long *buffWidth, long *buffHeight, long *buffDepth);
char* QTfailureMessage(void);
FSSpec *darwinPathToFSSpec (char *fname );

View File

@@ -1,27 +1,27 @@
// -*-c++-*-
/*
* Copyright (C) 2004 Stephan Huber http://digitalmind.de
*
*
* The Open Scene Graph (OSG) is a cross platform C++/OpenGL library for
* real-time rendering of large 3D photo-realistic models.
* The OSG homepage is http://www.openscenegraph.org/
*
* This software is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
* Copyright (C) 2004 Stephan Huber http://digitalmind.de
*
*
* The Open Scene Graph (OSG) is a cross platform C++/OpenGL library for
* real-time rendering of large 3D photo-realistic models.
* The OSG homepage is http://www.openscenegraph.org/
*
* This software is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "QuicktimeImageStream.h"
#include <osg/Notify>
@@ -34,201 +34,254 @@
#include "MovieData.h"
using namespace osg;
#define IDLE_TIMEOUT 150000L
#define ERR_MSG(no,msg) osg::notify(osg::WARN) << "QT-ImageStream: " << msg << " failed with error " << no << std::endl;
static OpenThreads::Mutex* s_qtMutex = new OpenThreads::Mutex;
int QuicktimeImageStream::_qtInstanceCount = 0;
// Constructor: setup and start thread
QuicktimeImageStream::QuicktimeImageStream(std::string fileName) : ImageStream()
{
// ricky
if(_qtInstanceCount == 0)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(*s_qtMutex);
osg::notify(osg::NOTICE) << "quicktime Init" << std::endl;
initQuicktime();
}
++ _qtInstanceCount;
// end ricky
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(*s_qtMutex);
osgQuicktime::initQuicktime();
}
_len = 0;
_data = new MovieData();
for (int i = 0; i < NUM_CMD_INDEX; i++)
_cmd[i] = THREAD_IDLE;
_wrIndex = _rdIndex = 0;
_len = 0;
_data = new MovieData();
load(fileName);
for (int i = 0; i < NUM_CMD_INDEX; i++)
_cmd[i] = THREAD_IDLE;
_wrIndex = _rdIndex = 0;
if (!fileName.empty())
setFileName(fileName);
load(fileName);
if (!fileName.empty())
setFileName(fileName);
// ricky
_status = ImageStream::PAUSED;
}
// Deconstructor: stop and terminate thread
QuicktimeImageStream::~QuicktimeImageStream()
{
if( isRunning() )
{
quit(true);
}
if( isRunning() )
{
quit(true);
}
// ricky
-- _qtInstanceCount;
if(_qtInstanceCount == 0)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(*s_qtMutex);
osg::notify(osg::NOTICE) << "quicktime Exit" << std::endl;
exitQuicktime();
}
// end ricky
// clean up quicktime movies.
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(*s_qtMutex);
delete _data;
}
// clean up quicktime movies.
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(*s_qtMutex);
delete _data;
}
}
// Set command
void QuicktimeImageStream::setCmd(ThreadCommand cmd)
void QuicktimeImageStream::setCmd(ThreadCommand cmd, float rate)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
_cmd[_wrIndex] = cmd;
_wrIndex = (_wrIndex + 1) % NUM_CMD_INDEX;
_cmd[_wrIndex] = cmd;
_rates[_wrIndex] = rate;
_wrIndex = (_wrIndex + 1) % NUM_CMD_INDEX;
}
// Get command
QuicktimeImageStream::ThreadCommand QuicktimeImageStream::getCmd()
{
ThreadCommand cmd = THREAD_IDLE;
ThreadCommand cmd = THREAD_IDLE;
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
if (_rdIndex != _wrIndex) {
cmd = _cmd[_rdIndex];
_rdIndex = (_rdIndex + 1) % NUM_CMD_INDEX;
}
if (_rdIndex != _wrIndex) {
cmd = _cmd[_rdIndex];
_currentRate = _rates[_rdIndex];
_rdIndex = (_rdIndex + 1) % NUM_CMD_INDEX;
}
return cmd;
return cmd;
}
void QuicktimeImageStream::load(std::string fileName)
{
osg::notify(osg::DEBUG_INFO) << "QT-ImageStream: loading quicktime movie from " << fileName << std::endl;
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(*s_qtMutex);
osg::notify(osg::DEBUG_INFO) << "QT-ImageStream: loading quicktime movie from " << fileName << std::endl;
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(*s_qtMutex);
_data->load(this, fileName);
_len = _data->getMovieDuration();
_current = 0;
_data->load(this, fileName);
_len = _data->getMovieDuration();
}
void QuicktimeImageStream::quit(bool wiatForThreadToExit)
{
osg::notify(osg::DEBUG_INFO)<<"Sending quit"<<this<<std::endl;
setCmd(THREAD_QUIT);
osg::notify(osg::DEBUG_INFO)<<"Sending quit"<<this<<std::endl;
setCmd(THREAD_QUIT);
if (wiatForThreadToExit)
{
while(isRunning())
{
osg::notify(osg::DEBUG_INFO)<<"Waiting for QuicktimeImageStream to quit"<<this<<std::endl;
OpenThreads::Thread::YieldCurrentThread();
}
osg::notify(osg::DEBUG_INFO)<<"QuicktimeImageStream has quit"<<this<<std::endl;
}
if (wiatForThreadToExit)
{
while(isRunning())
{
osg::notify(osg::DEBUG_INFO)<<"Waiting for QuicktimeImageStream to quit"<<this<<std::endl;
OpenThreads::Thread::YieldCurrentThread();
}
osg::notify(osg::DEBUG_INFO)<<"QuicktimeImageStream has quit"<<this<<std::endl;
}
}
void QuicktimeImageStream::run()
{
bool playing = false;
bool done = false;
//OSErr err;
// err = EnterMoviesOnThread(0);
// ERR_MSG(err,"EnterMoviesOnThread");
while (!done) {
// osg::notify(osg::ALWAYS) << "movietime: " << _data->getMovieTime() << " state " << getCmd() << std::endl;
// Handle commands
ThreadCommand cmd = getCmd();
bool playing = false;
bool done = false;
float currentTime=0.0f;
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(*s_qtMutex);
/*
OSErr err;
err = EnterMoviesOnThread(0);
ERR_MSG(err,"EnterMoviesOnThread");
err = AttachMovieToCurrentThread(_data->getMovie());
*/
while (!done) {
if (cmd != THREAD_IDLE) {
switch (cmd) {
ThreadCommand cmd = getCmd();
osg::notify(osg::DEBUG_INFO) << "movietime: " << _data->getMovieTime() << " rate: " << _data->getMovieRate() << " state " << cmd << " playing: " << playing << " done " << done << " " << _wrIndex << "/" << _rdIndex << std::endl;
// Handle commands
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(*s_qtMutex);
if (cmd != THREAD_IDLE) {
osg::notify(osg::DEBUG_INFO) << "new cmd: " << cmd << std::endl;
switch (cmd) {
case THREAD_START: // Start or continue stream
StartMovie(_data->getMovie());
playing = true;
break;
_data->setMovieRate(1.0f);
playing = true;
break;
case THREAD_STOP:
SetMovieRate(_data->getMovie(),0);
osg::notify(INFO) << "QT-ImageStream: stop at "<< std::endl;
playing = false;
break;
_data->setMovieRate(0);
osg::notify(osg::INFO) << "QT-ImageStream: stop at "<< std::endl;
playing = false;
break;
case THREAD_REWIND:
SetMovieRate(_data->getMovie(),0);
GoToBeginningOfMovie(_data->getMovie());
break;
SetMovieRate(_data->getMovie(),0);
GoToBeginningOfMovie(_data->getMovie());
break;
case THREAD_FORWARD:
SetMovieRate(_data->getMovie(),0);
GoToEndOfMovie(_data->getMovie());
break;
case THREAD_SEEK:
_data->setMovieTime(_currentRate);
playing = true;
break;
case THREAD_SETRATE:
_data->setMovieRate(_currentRate);
playing = (_currentRate != 0.0f);
break;
case THREAD_CLOSE:
SetMovieRate(_data->getMovie(),0);
break;
_data->setMovieRate(0);
break;
case THREAD_QUIT: // TODO
SetMovieRate(_data->getMovie(),0);
osg::notify(INFO) << "QT-ImageStream: quit" << std::endl;
//playing = false;
done = true;
break;
_data->setMovieRate(0);
osg::notify(osg::INFO) << "QT-ImageStream: quit" << std::endl;
//playing = false;
done = true;
break;
default:
osg::notify(osg::WARN) << "QT-ImageStream: Unknown command " << cmd << std::endl;
break;
}
osg::notify(osg::WARN) << "QT-ImageStream: Unknown command " << cmd << std::endl;
break;
}
}
MoviesTask(_data->getMovie(),0);
MoviesTask(_data->getMovie(),0);
_current = _data->getMovieTime();
}
currentTime = _data->getMovieTime();
}
if (_lastUpdate!= currentTime)
{
if (_lastUpdate!= _current)
{
// force the texture to update the image
dirty();
// update internal time and take care of looping
_lastUpdate = _current;
dirty();
_lastUpdate = currentTime;
if (currentTime>=_data->getMovieDuration())
{
if (getLoopingMode()==LOOPING)
{
rewind();
play();
}
else
{
pause();
}
if (getLoopingMode() == LOOPING)
{
// loopen wir rueckwaerts?
if ((_current <= 0.0f) && (_data->getCachedMovieRate() < 0.0f)) {
forward();
setMovieRate(_data->getCachedMovieRate());
}
// loppen wir vorw<72>rts?
else if ((_current >= _len) && (_data->getCachedMovieRate() > 0.0f))
{
rewind();
setMovieRate(_data->getCachedMovieRate());
}
}
else
{ // NO LOOPING
//ricky
if(_current >= _len)
{
pause();
rewind();
}
// orig
//pause();
}
if (playing)
{
// TODO
}
else if (!done)
{
OpenThreads::Thread::microSleep(IDLE_TIMEOUT);
}
}
// err = ExitMoviesOnThread();
//ERR_MSG(err,"ExitMoviesOnThread");
}
}
if (playing)
{
OpenThreads::Thread::microSleep(16000);
}
else if (!done)
{
OpenThreads::Thread::microSleep(IDLE_TIMEOUT);
}
}
/*
err = DetachMovieFromCurrentThread(_data->getMovie());
err = ExitMoviesOnThread();
ERR_MSG(err,"ExitMoviesOnThread");
*/
}

View File

@@ -32,78 +32,118 @@
#define NUM_CMD_INDEX 20
namespace osg {
class MovieData;
/**
* Quicktime Image Stream class.
*/
class QuicktimeImageStream : public osg::ImageStream, public OpenThreads::Thread
{
public:
QuicktimeImageStream(std::string fileName = "");
virtual Object* clone() const { return new QuicktimeImageStream; }
virtual bool isSameKindAs(const Object* obj) const {
return dynamic_cast<const QuicktimeImageStream*>(obj) != NULL;
}
virtual const char* className() const { return "QuicktimeImageStream"; }
/// Start or continue stream.
virtual void play()
{
if (!isRunning()) start();
setCmd(THREAD_START);
}
/// Pause stream at current position.
virtual void pause() { setCmd(THREAD_STOP); }
/// Rewind stream to beginning.
virtual void rewind() { setCmd(THREAD_REWIND); }
virtual void quit(bool wiatForThreadToExit);
/// Get total length in seconds.
inline float getLength() const { return _len; }
void load(std::string fileName);
virtual void run();
protected:
virtual ~QuicktimeImageStream();
private:
float _lastUpdate;
float _len;
MovieData* _data;
enum ThreadCommand {
THREAD_IDLE = 0,
THREAD_START,
THREAD_STOP,
THREAD_REWIND,
THREAD_CLOSE,
THREAD_QUIT
};
ThreadCommand _cmd[NUM_CMD_INDEX];
int _wrIndex, _rdIndex;
OpenThreads::Mutex _mutex;
/// Set command.
void setCmd(ThreadCommand cmd);
/// Get command.
ThreadCommand getCmd();
};
class MovieData;
/**
* Quicktime Image Stream class. streams a quicktime movie into an image
*/
class QuicktimeImageStream : public osg::ImageStream, public OpenThreads::Thread
{
public:
/** Constructor
* @param fileName movie to open */
QuicktimeImageStream(std::string fileName = "");
virtual Object* clone() const { return new QuicktimeImageStream; }
virtual bool isSameKindAs(const Object* obj) const {
return dynamic_cast<const QuicktimeImageStream*>(obj) != NULL;
}
virtual const char* className() const { return "QuicktimeImageStream"; }
/// Start or continue stream.
virtual void play()
{
if (!isRunning()) start();
setCmd(THREAD_START);
// ricky
_status = ImageStream::PLAYING;
}
/// sets the movierate of this movie
void setMovieRate(float rate) {
if (!isRunning()) start();
setCmd(THREAD_SETRATE,rate);
}
/// Pause stream at current position.
virtual void pause()
{
setCmd(THREAD_STOP);
// ricky
_status = ImageStream::PAUSED;
}
/// Rewind stream to beginning.
virtual void rewind() { setCmd(THREAD_REWIND); }
/// forward stream to the end
virtual void forward() { setCmd(THREAD_FORWARD); }
/// stop playing
virtual void quit(bool wiatForThreadToExit);
/// Get total length in seconds.
inline float getLength() const { return _len; }
/// jumps to a specific position
void jumpTo(float pos) {
setCmd(THREAD_SEEK, pos);
}
/// returns the current playing position
inline float getCurrentTime() const { return _current; }
/// @return the current moviedata-object
MovieData* getMovieData() { return _data; }
/// loads a movie from fileName
void load(std::string fileName);
/// starts the thread
virtual void run();
protected:
/// destructor
virtual ~QuicktimeImageStream();
private:
float _lastUpdate;
float _len;
float _current;
float _currentRate;
MovieData* _data;
enum ThreadCommand {
THREAD_IDLE = 0,
THREAD_START,
THREAD_STOP,
THREAD_REWIND,
THREAD_FORWARD,
THREAD_SEEK,
THREAD_SETRATE,
THREAD_CLOSE,
THREAD_QUIT
};
ThreadCommand _cmd[NUM_CMD_INDEX];
float _rates[NUM_CMD_INDEX];
int _wrIndex, _rdIndex;
OpenThreads::Mutex _mutex;
/// Set command.
void setCmd(ThreadCommand cmd, float rate = 0.0f);
/// Get command.
ThreadCommand getCmd();
// ricky
static int _qtInstanceCount;
};
} // namespace
#endif

View File

@@ -16,8 +16,7 @@
#ifndef SEEK_SET
# define SEEK_SET 0
#endif
#include <Carbon/Carbon.h>
#include <QuickTime/QuickTime.h>
#include "QTUtils.h"
#include "QTtexture.h"
#include "QuicktimeImageStream.h"
@@ -28,375 +27,394 @@ using namespace osg;
class ReaderWriterQT : public osgDB::ReaderWriter
{
public:
virtual const char* className() const { return "Default Quicktime Image Reader/Writer"; }
virtual bool acceptsMovieExtension(const std::string& extension) const
{
return osgDB::equalCaseInsensitive(extension,"mov") ||
osgDB::equalCaseInsensitive(extension,"mpg") ||
osgDB::equalCaseInsensitive(extension,"mpv") ||
osgDB::equalCaseInsensitive(extension,"mp4") ||
osgDB::equalCaseInsensitive(extension,"m4v") ||
osgDB::equalCaseInsensitive(extension,"dv");
}
virtual bool acceptsExtension(const std::string& extension) const
{
// this should be the only image importer required on the Mac
// dont know what else it supports, but these will do
return osgDB::equalCaseInsensitive(extension,"rgb") ||
osgDB::equalCaseInsensitive(extension,"rgba") ||
osgDB::equalCaseInsensitive(extension,"jpg") ||
osgDB::equalCaseInsensitive(extension,"jpeg") ||
osgDB::equalCaseInsensitive(extension,"tif") ||
osgDB::equalCaseInsensitive(extension,"tiff") ||
osgDB::equalCaseInsensitive(extension,"gif") ||
osgDB::equalCaseInsensitive(extension,"png") ||
osgDB::equalCaseInsensitive(extension,"pict") ||
osgDB::equalCaseInsensitive(extension,"pct") ||
osgDB::equalCaseInsensitive(extension,"tga") ||
osgDB::equalCaseInsensitive(extension,"psd") ||
acceptsMovieExtension(extension);
}
public:
virtual const char* className() const { return "Default Quicktime Image Reader/Writer"; }
virtual ReadResult readImage(const std::string& file, const osgDB::ReaderWriter::Options*) const
{
std::string ext = osgDB::getLowerCaseFileExtension(file);
if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
virtual bool acceptsMovieExtension(const std::string& extension) const
{
return osgDB::equalCaseInsensitive(extension,"mov") ||
osgDB::equalCaseInsensitive(extension,"mpg") ||
osgDB::equalCaseInsensitive(extension,"mpv") ||
osgDB::equalCaseInsensitive(extension,"mp4") ||
osgDB::equalCaseInsensitive(extension,"m4v") ||
osgDB::equalCaseInsensitive(extension,"dv") ||
osgDB::equalCaseInsensitive(extension,"avi") ||
osgDB::equalCaseInsensitive(extension,"swf");
}
std::string fileName = osgDB::findDataFile( file );
if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
// if the file is a movie file then load as an ImageStream.
if (acceptsMovieExtension(ext))
virtual bool acceptsExtension(const std::string& extension) const
{
// this should be the only image importer required on the Mac
// dont know what else it supports, but these will do
return
/*osgDB::equalCaseInsensitive(extension,"rgb") ||
osgDB::equalCaseInsensitive(extension,"rgba") ||
osgDB::equalCaseInsensitive(extension,"jpg") ||
osgDB::equalCaseInsensitive(extension,"jpeg") ||
osgDB::equalCaseInsensitive(extension,"tif") ||
osgDB::equalCaseInsensitive(extension,"tiff") ||
osgDB::equalCaseInsensitive(extension,"gif") ||
osgDB::equalCaseInsensitive(extension,"png") ||
osgDB::equalCaseInsensitive(extension,"pict") ||
osgDB::equalCaseInsensitive(extension,"pct") ||
osgDB::equalCaseInsensitive(extension,"tga") ||
osgDB::equalCaseInsensitive(extension,"psd") ||
*/
acceptsMovieExtension(extension);
}
virtual ReadResult readImage(const std::string& file, const osgDB::ReaderWriter::Options*) const
{
// ricky
// If using only the QuicktimeImageStream class for the movies, you can comment
// out this line, as the init function is called inside the QTImageStream
// ctor.
// It is left here for compatibility with the original plugin, to handle
// regular 2D images as well.
initQuicktime();
std::string ext = osgDB::getLowerCaseFileExtension(file);
if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
std::string fileName = osgDB::findDataFile( file );
if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
// if the file is a movie file then load as an ImageStream.
if (acceptsMovieExtension(ext))
{
// note from Robert Osfield when integrating, we should probably have so
// error handling mechanism here. Possibly move the load from
// the constructor to a seperate load method, and have a valid
// state on the ImageStream... will integrated as is right now
// to get things off the ground.
QuicktimeImageStream* moov = new QuicktimeImageStream(fileName);
// moov->play();
return moov;
}
long origWidth, origHeight,buffWidth,buffHeight,buffDepth,origDepth;
// 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.
unsigned char *pixels = LoadBufferFromDarwinPath ( fileName.c_str(), &origWidth,&origHeight,&origDepth,
&buffWidth,&buffHeight,
&buffDepth);
// IMPORTANT -
// origDepth in BYTES, buffDepth in BITS
if (pixels == 0)
{
osg::notify(osg::WARN) << "LoadBufferFromDarwinPath failed " << fileName.c_str() << QTfailureMessage() << std::endl;
return 0;
}
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)
{
// note from Robert Osfield when integrating, we should probably have so
// error handling mechanism here. Possibly move the load from
// the constructor to a seperate load method, and have a valid
// state on the ImageStream... will integrated as is right now
// to get things off the ground.
osg::QuicktimeImageStream* moov = new osg::QuicktimeImageStream(fileName);
// moov->play();
return moov;
}
long origWidth, origHeight,buffWidth,buffHeight,buffDepth,origDepth;
// 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.
unsigned char *pixels = LoadBufferFromDarwinPath ( fileName.c_str(), &origWidth,&origHeight,&origDepth,
&buffWidth,&buffHeight,
&buffDepth);
// IMPORTANT -
// origDepth in BYTES, buffDepth in BITS
if (pixels == 0) {
osg::notify(osg::WARN) << "LoadBufferFromDarwinPath failed " << fileName.c_str() << QTfailureMessage() << std::endl;
return 0;
}
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) {
/*
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;
}
}
}
/*
since 8-bit tgas will get expanded into colour, have to use RGB code for 8-bit images
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 );
notify(INFO) << "image read ok "<<buffWidth<<" "<<buffHeight<<std::endl;
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;
//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())
return WriteResult::FILE_NOT_HANDLED;
OSType desiredType = cur->second;
GraphicsExportComponent geComp = NULL;
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;
OSErr err = OpenADefaultComponent(GraphicsExporterComponentType, desiredType, &geComp);
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;
}
}
}
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 );
notify(DEBUG_INFO) << "image read ok "<<buffWidth<<" "<<buffHeight<<std::endl;
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())
return WriteResult::FILE_NOT_HANDLED;
OSType desiredType = cur->second;
GraphicsExportComponent geComp = NULL;
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 open Graphics epxorter for type " << ext << ", Err: " << err << std::endl;
return WriteResult::FILE_NOT_HANDLED;
osg::notify(osg::WARN) << "ReaderWriterQT: could not set compression for type " << ext << ", Err: " << err << std::endl;
throw err;
}
CGContextRef cg_context = 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] = 0xFFFFFFFF;
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]=0xFFFFFFFF;
dstp[1]=srcp[0];
dstp[2]=srcp[1];
dstp[3]=srcp[2];
if(32 == numBytes)
{
err = GraphicsExportSetDepth( geComp,
k32ARGBPixelFormat ); // depth
}
// else k24RGBPixelFormat???
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 );
// 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;
}
memcpy(pixels, tBuffer, (size_t)imageSize);
free(tBuffer);
FSSpec* fileSpec = NULL;
if (geComp != NULL)
CloseComponent(geComp);
try {
// I'm not sure what the bitsPerComponet should be. For RGB and RGBA in 24 and 32 bit pixel formats, it's 8.
// Since the code above put everything in ARGB, I guess I can hardcode everything.
cg_context = CGBitmapContextCreate(pixels, img.s(), img.t(), 8, buffWidth*4,
CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB),
kCGImageAlphaPremultipliedFirst // The code above put everything in ARGB
);
if (cg_context == NULL) {
osg::notify(osg::WARN) << "ReaderWriterQT: could not create gworld for type " << ext << ", Err: " << err << std::endl;
// This seems really messed up, but throwing Sin16's seems really messed up in general.
throw 0;
}
DisposeGWorld (gw);
if (fileSpec != NULL ) free(fileSpec);
if (pixels) free(pixels);
return WriteResult::FILE_SAVED;
}
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;
}
}
// 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 = GraphicsExportSetInputCGBitmapContext(geComp, cg_context);
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;
}
}
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);
CGContextRelease(cg_context);
if (fileSpec != NULL ) free(fileSpec);
if (pixels) free(pixels);
return WriteResult::FILE_SAVED;
}
catch (...) {
if (geComp != NULL) CloseComponent(geComp);
if (cg_context != NULL) CGContextRelease(cg_context);
if (fileSpec != NULL ) free(fileSpec);
if (pixels) free(pixels);
return WriteResult::ERROR_IN_WRITING_FILE;
}
}
};