1380 lines
36 KiB
C++
Executable File
1380 lines
36 KiB
C++
Executable File
//-----------------------------------------------------------------------------
|
|
// Torque Game Engine
|
|
// Copyright (C) GarageGames.com, Inc.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "gui/core/guiControl.h"
|
|
|
|
// This control currently disabled until streaming is
|
|
// added back into the audio library
|
|
|
|
#if defined(TORQUE_OS_LINUX) || defined(TORQUE_OS_OPENBSD)
|
|
#if DEDICATED
|
|
#define ENABLE_AVI_GUI 0
|
|
#define ENABLE_MPG_GUI 0
|
|
#else
|
|
#define ENABLE_AVI_GUI 0
|
|
#define ENABLE_MPG_GUI 0 // was 1
|
|
#endif
|
|
#else
|
|
/* Windows */
|
|
#define ENABLE_AVI_GUI 0
|
|
#define ENABLE_MPG_GUI 0
|
|
#endif
|
|
|
|
#if ENABLE_AVI_GUI || ENABLE_MPG_GUI
|
|
#include "audio/audio.h"
|
|
#if ENABLE_AVI_GUI
|
|
#include <windows.h>
|
|
#include <vfw.h>
|
|
#endif
|
|
#if ENABLE_MPG_GUI
|
|
#include "smpeg.h"
|
|
#endif
|
|
#endif /* Either AVI or MPG GUI */
|
|
|
|
|
|
#include "gui/game/guiAviBitmapCtrl.h"
|
|
#include "console/consoleTypes.h"
|
|
#include "dgl/dgl.h"
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
#if !ENABLE_AVI_GUI || !ENABLE_MPG_GUI
|
|
// Version for Loki which will compile (and do nothing)-
|
|
IMPLEMENT_CONOBJECT(GuiAviBitmapCtrl);
|
|
GuiAviBitmapCtrl::GuiAviBitmapCtrl()
|
|
{
|
|
mDone = true;
|
|
}
|
|
GuiAviBitmapCtrl::~GuiAviBitmapCtrl()
|
|
{
|
|
}
|
|
|
|
void GuiAviBitmapCtrl::initPersistFields()
|
|
{
|
|
Parent::initPersistFields();
|
|
addGroup("Misc");
|
|
addField("done", TypeBool, Offset(mDone, GuiAviBitmapCtrl));
|
|
endGroup("Misc");
|
|
}
|
|
#else
|
|
|
|
// Code common to both AVI and MPG players
|
|
|
|
#define ALIGNULONG(bytes) ((((bytes) + 3) / 4) * 4)
|
|
|
|
// Error codes
|
|
#define MOVERR_OK 0
|
|
#define MOVERR_NOVIDEOSTREAM -1
|
|
#define MOVERR_PLAYING -4
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
ConsoleMethod( GuiAviBitmapCtrl, setFilename, void, 3, 3, "(string filename)")
|
|
{
|
|
object->setFilename(argv[2]);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
ConsoleMethod( GuiAviBitmapCtrl, play, void, 2, 2, "Start playback.")
|
|
{
|
|
object->movieStart();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
ConsoleMethod( GuiAviBitmapCtrl, stop, void, 2, 2, "Stop playback.")
|
|
{
|
|
object->movieStop();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Audio Operations
|
|
|
|
bool GuiAviBitmapCtrl::sndOpen()
|
|
{
|
|
mAudioLatency = 0;
|
|
|
|
#if 0
|
|
// streaming disabled in this build
|
|
|
|
// Open up the audio. Taking the easy way and using two separate streams.
|
|
// Don't treat as error if it doesn't open-
|
|
dSprintf(fileBuffer, sizeof(fileBuffer), "%s", mWavFilename);
|
|
Audio::Description desc;
|
|
desc.mIs3D = false;
|
|
desc.mVolume = 1.0f;
|
|
desc.mIsLooping = false;
|
|
desc.mType = Audio::MusicAudioType;
|
|
mWavHandle = alxCreateSource(&desc, fileBuffer, 0);
|
|
#endif
|
|
|
|
mWavHandle = NULL_AUDIOHANDLE;
|
|
// alxGetContexti(ALC_BUFFER_LATENCY, &mAudioLatency);
|
|
|
|
return (mWavHandle != NULL_AUDIOHANDLE);
|
|
}
|
|
|
|
void GuiAviBitmapCtrl::sndStart()
|
|
{
|
|
if (mWavHandle != NULL_AUDIOHANDLE)
|
|
alxPlay(mWavHandle);
|
|
}
|
|
|
|
void GuiAviBitmapCtrl::sndStop()
|
|
{
|
|
if (mWavHandle != NULL_AUDIOHANDLE)
|
|
{
|
|
alxStop(mWavHandle);
|
|
mWavHandle = NULL_AUDIOHANDLE;
|
|
}
|
|
}
|
|
|
|
|
|
#if ENABLE_AVI_GUI
|
|
|
|
#define FOURCC_iv50 mmioFOURCC('i','v','5','0')
|
|
#define FOURCC_IV50 mmioFOURCC('I','V','5','0')
|
|
|
|
// Error codes
|
|
#define VIDSERR_OK 0
|
|
#define VIDSERR_NOVIDEO -20
|
|
#define VIDSERR_VCM -21
|
|
#define VIDSERR_NOTSTARTED -23
|
|
#define VIDSERR_READ -24
|
|
|
|
#define VCMERR_OK 0
|
|
#define VCMERR_INTERNAL -1
|
|
|
|
//----------------------------------------------------------------------------
|
|
IMPLEMENT_CONOBJECT(GuiAviBitmapCtrl);
|
|
|
|
GuiAviBitmapCtrl::GuiAviBitmapCtrl()
|
|
{
|
|
mAviFilename = StringTable->insert("");
|
|
mTextureHandles = NULL;
|
|
|
|
mPFile = NULL;
|
|
mPAviVideo = NULL;
|
|
mBPlaying = false;
|
|
mDone = false;
|
|
mLetterBox = false;
|
|
mTimePlayStart = -1;
|
|
mPVBuf = NULL;
|
|
mPBuf = NULL;
|
|
mPBiSrc = mPBiDst = NULL;
|
|
mHic = NULL;
|
|
mFccHandler = 0;
|
|
mSwapRB = true;
|
|
mSpeed = 1.0;
|
|
mWavHandle = NULL_AUDIOHANDLE;
|
|
|
|
AVIFileInit();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
GuiAviBitmapCtrl::~GuiAviBitmapCtrl()
|
|
{
|
|
vcmClose();
|
|
AVIFileExit();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// AVI File Operations
|
|
|
|
// Open a file
|
|
//
|
|
S32 GuiAviBitmapCtrl::fileOpen()
|
|
{
|
|
S32 rval;
|
|
if (!dStrcmp(mAviFilename,""))
|
|
return MOVERR_NOVIDEOSTREAM;
|
|
|
|
rval = AVIFileOpen(&mPFile, mAviFilename, OF_SHARE_DENY_WRITE, 0);
|
|
if (rval)
|
|
{
|
|
fileClose();
|
|
return rval;
|
|
}
|
|
|
|
return MOVERR_OK;
|
|
}
|
|
|
|
// Close a file
|
|
//
|
|
S32 GuiAviBitmapCtrl::fileClose ()
|
|
{
|
|
if (mPFile)
|
|
{
|
|
AVIFileRelease(mPFile);
|
|
mPFile = NULL;
|
|
}
|
|
return MOVERR_OK;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Movie Operations
|
|
|
|
// A movie must have a video stream
|
|
// Return codes should be properly done but for now, 0 is no error
|
|
// while !0 is error unless otherwise noted
|
|
|
|
// Open a movie (video stream) from an open file
|
|
//
|
|
S32 GuiAviBitmapCtrl::movieOpen()
|
|
{
|
|
S32 rval;
|
|
|
|
// Open first video stream found
|
|
// Note that we don't handle multiple video streams
|
|
rval = AVIFileGetStream(mPFile, &mPAviVideo, streamtypeVIDEO, 0);
|
|
if (rval == AVIERR_NODATA)
|
|
{
|
|
mPAviVideo = NULL;
|
|
|
|
return MOVERR_NOVIDEOSTREAM;
|
|
}
|
|
|
|
// Get video stream info
|
|
AVISTREAMINFO avis;
|
|
|
|
AVIStreamInfo(mPAviVideo, &avis, sizeof(avis));
|
|
|
|
mFrate = F32(avis.dwRate)/F32(avis.dwScale);
|
|
|
|
// Open vids
|
|
rval = vidsVideoOpen();
|
|
if (rval)
|
|
return rval;
|
|
|
|
return MOVERR_OK;
|
|
}
|
|
|
|
// Close movie stream
|
|
//
|
|
S32 GuiAviBitmapCtrl::movieClose()
|
|
{
|
|
// Make sure movie is stopped
|
|
movieStop();
|
|
|
|
// Release streams
|
|
if (mPAviVideo)
|
|
AVIStreamRelease(mPAviVideo);
|
|
|
|
mPAviVideo = 0;
|
|
|
|
return MOVERR_OK;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Video Operations
|
|
|
|
// Open and init VIDS
|
|
//
|
|
S32 GuiAviBitmapCtrl::vidsVideoOpen()
|
|
{
|
|
if (!mPAviVideo)
|
|
return VIDSERR_NOVIDEO;
|
|
|
|
mTimePlayStart = -1;
|
|
|
|
// Get stream info
|
|
AVISTREAMINFO avis;
|
|
|
|
AVIStreamInfo(mPAviVideo, &avis, sizeof(avis));
|
|
|
|
mVidsFirst = mVidsCurrent = avis.dwStart;
|
|
mVidsLast = avis.dwStart + avis.dwLength - 1;
|
|
|
|
mVidsPrevious = mVidsCurrent-1;
|
|
|
|
// This was not initialized and causing problems in RTEST-
|
|
mVidsPrevKey = -100000;
|
|
|
|
// Read first frame to get source info
|
|
S32 cb, rval;
|
|
BITMAPINFOHEADER biFormat;
|
|
|
|
AVIStreamFormatSize(mPAviVideo, 0, (long *) &cb);
|
|
if (cb != sizeof(BITMAPINFOHEADER))
|
|
return -1;
|
|
rval = AVIStreamReadFormat(mPAviVideo, 0, &biFormat, (long *) &cb);
|
|
rval = rval;
|
|
|
|
// Open VCM for this instance
|
|
if (vcmOpen(avis.fccHandler, &biFormat))
|
|
{
|
|
vidsVideoClose();
|
|
|
|
return VIDSERR_VCM;
|
|
}
|
|
|
|
// Create temporary buffer
|
|
mCBVBuf = biFormat.biHeight * ALIGNULONG(biFormat.biWidth)
|
|
* biFormat.biBitCount/8;
|
|
mPVBuf = (unsigned char *) dMalloc(mCBVBuf);
|
|
AssertFatal(mPVBuf, "Out of memory");
|
|
dMemset(mPVBuf, 0, mCBVBuf);
|
|
|
|
// Reset internal skip count
|
|
mPlayFSkipped = 0;
|
|
|
|
return VIDSERR_OK;
|
|
}
|
|
|
|
// Close VIDS
|
|
//
|
|
S32 GuiAviBitmapCtrl::vidsVideoClose()
|
|
{
|
|
if (mPVBuf)
|
|
{
|
|
dFree(mPVBuf);
|
|
mPVBuf = NULL;
|
|
}
|
|
|
|
vcmClose();
|
|
|
|
return VIDSERR_OK;
|
|
}
|
|
|
|
// Start video, note that all decode/draw is done in vidsDraw()
|
|
//
|
|
// If fStart < 0, then start on current frame
|
|
//
|
|
S32 GuiAviBitmapCtrl::vidsVideoStart()
|
|
{
|
|
// Begin VCM
|
|
if (vcmBegin())
|
|
{
|
|
vidsVideoStop();
|
|
|
|
return VIDSERR_VCM;
|
|
}
|
|
|
|
// Pass seperate start message to VCM only when playing
|
|
if (mBPlaying)
|
|
vcmDrawStart();
|
|
|
|
// Get start time
|
|
mTimePlayStart = getMilliseconds();
|
|
mTimePlayStartPos = AVIStreamSampleToTime(mPAviVideo, mVidsCurrent);
|
|
|
|
// Init play stats
|
|
mPlayFPrev = mVidsCurrent;
|
|
|
|
return VIDSERR_OK;
|
|
}
|
|
|
|
// Stop video
|
|
//
|
|
S32 GuiAviBitmapCtrl::vidsVideoStop()
|
|
{
|
|
vcmDrawStop();
|
|
vcmEnd();
|
|
|
|
mTimePlayStart = -1;
|
|
|
|
return VIDSERR_OK;
|
|
}
|
|
|
|
// Draw current frame of video
|
|
//
|
|
S32 GuiAviBitmapCtrl::vidsVideoDraw()
|
|
{
|
|
// Check if started
|
|
if (mTimePlayStart < 0)
|
|
return VIDSERR_NOTSTARTED;
|
|
|
|
if (mBPlaying)
|
|
{
|
|
S32 lTime = mTimePlayStartPos + (getMilliseconds() - mTimePlayStart);
|
|
|
|
mVidsCurrent = vidsTimeToSample(lTime);
|
|
|
|
if (mVidsCurrent > mVidsLast)
|
|
mVidsCurrent = mVidsLast;
|
|
|
|
if (mVidsCurrent == mVidsPrevious)
|
|
// Going too fast! Should actually return a ms
|
|
// count so calling app can Sleep() if desired.
|
|
return VIDSERR_OK;
|
|
}
|
|
else
|
|
{
|
|
if (mVidsCurrent > mVidsLast)
|
|
mVidsCurrent = mVidsLast;
|
|
|
|
if (mVidsCurrent == mVidsPrevious)
|
|
vidsResetDraw();
|
|
}
|
|
|
|
if (!vidsSync())
|
|
return VIDSERR_OK; // don't draw this frame
|
|
|
|
S32 rval = AVIStreamRead(mPAviVideo,
|
|
mVidsCurrent,
|
|
1,
|
|
mPVBuf,
|
|
mCBVBuf,
|
|
NULL,
|
|
NULL);
|
|
if (rval)
|
|
return VIDSERR_READ;
|
|
|
|
if (vcmDraw())
|
|
return VIDSERR_VCM;
|
|
|
|
mVidsPrevious = mVidsCurrent;
|
|
|
|
if (mBPlaying)
|
|
{
|
|
mPlayFSkipped += mVidsCurrent-mPlayFPrev-1;
|
|
mPlayFPrev = mVidsCurrent;
|
|
|
|
if (mVidsCurrent == mVidsLast)
|
|
{
|
|
mVidsCurrent = -1;
|
|
|
|
return movieStop();
|
|
}
|
|
}
|
|
|
|
return VIDSERR_OK;
|
|
}
|
|
|
|
// Convert ms to sample (frame)
|
|
//
|
|
S32 GuiAviBitmapCtrl::vidsTimeToSample(S32 lTime)
|
|
{
|
|
S32 lSamp = AVIStreamTimeToSample(mPAviVideo, lTime);
|
|
|
|
return lSamp;
|
|
}
|
|
|
|
// TRUE if frame is KEY, if frame < 0 then check current frame
|
|
//
|
|
bool GuiAviBitmapCtrl::vidsIsKey(S32 frame /* = -1 */)
|
|
{
|
|
if (!mPAviVideo)
|
|
return false;
|
|
|
|
if (frame < 0)
|
|
frame = mVidsCurrent;
|
|
|
|
return AVIStreamIsKeyFrame(mPAviVideo, frame);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Internal video routines
|
|
|
|
// Synchronization and Keyframe Management:
|
|
// pretty simple plan, don't do anything too fancy.
|
|
//
|
|
bool GuiAviBitmapCtrl::vidsSync()
|
|
{
|
|
#define dist(x,y) ((x)-(y))
|
|
#define ABS_(x) (x<0 ? (-(x)) : (x))
|
|
|
|
if (mVidsCurrent < mVidsPrevious) // seeked back - reset draw
|
|
mVidsPrevious = -1;
|
|
|
|
if (dist(mVidsCurrent, mVidsPrevious) == 1)
|
|
{
|
|
// normal situation
|
|
// fall thru and draw
|
|
}
|
|
else
|
|
{
|
|
// SKIPPED
|
|
if (AVIStreamIsKeyFrame(mPAviVideo, mVidsCurrent))
|
|
{
|
|
// we are on KF boundry just reset and start here
|
|
mVidsPrevKey = mVidsCurrent;
|
|
mVidsNextKey = AVIStreamNextKeyFrame(mPAviVideo, mVidsCurrent);
|
|
// fall thru and draw
|
|
}
|
|
else
|
|
{
|
|
if (dist(mVidsCurrent, mVidsPrevious) == 2)
|
|
{
|
|
// one frame off - just draw
|
|
vidsCatchup();
|
|
// fall thru and draw
|
|
}
|
|
else
|
|
{
|
|
// We are greater than one frame off:
|
|
// if we went past a K frame, update K frame info then:
|
|
// if we are closer to previous frame than catchup and draw
|
|
// if we are closer to next KEY frame than don't draw
|
|
if ((mVidsNextKey < mVidsCurrent) || (mVidsPrevKey > mVidsCurrent)) // seeked past previous key frame
|
|
{
|
|
// went past a K frame
|
|
mVidsPrevKey = AVIStreamPrevKeyFrame (mPAviVideo, mVidsCurrent);
|
|
mVidsNextKey = AVIStreamNextKeyFrame (mPAviVideo, mVidsCurrent);
|
|
}
|
|
|
|
if (ABS_(dist(mVidsCurrent, mVidsPrevKey)) <= ABS_(dist(mVidsCurrent, mVidsNextKey)))
|
|
vidsCatchup();
|
|
// fall thru and draw
|
|
else
|
|
if (mBPlaying)
|
|
return false; // m_vidsPrev NOT updated
|
|
else
|
|
vidsCatchup(); // if not playing than we want to
|
|
// draw the current frame
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Readies to draw (but doesn't draw) m_vidsCurrent.
|
|
// We just ICDECOMPRESS_HURRYUP frames from m_vidsPrevious or
|
|
// m_vidsPrevKey, whichever is closer.
|
|
// Updates m_vidsPrevious.
|
|
//
|
|
void GuiAviBitmapCtrl::vidsCatchup()
|
|
{
|
|
if (mVidsPrevious < mVidsPrevKey)
|
|
mVidsPrevious = mVidsPrevKey-1;
|
|
|
|
S32 catchup = mVidsPrevious+1;
|
|
|
|
while (catchup < mVidsCurrent)
|
|
{
|
|
S32 rval = AVIStreamRead(mPAviVideo,
|
|
catchup,
|
|
1,
|
|
mPVBuf,
|
|
mCBVBuf,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (rval)
|
|
break;
|
|
|
|
if (!mBPlaying )
|
|
vcmDrawIn();
|
|
else
|
|
vcmDrawIn(ICDECOMPRESS_HURRYUP);
|
|
|
|
mVidsPrevious = catchup++;
|
|
}
|
|
}
|
|
|
|
// Note that between vcmOpen() and vcmClose(), the source information does not
|
|
// change. If it does, open a new one.
|
|
//
|
|
S32 GuiAviBitmapCtrl::vcmOpen (FOURCC fccHandler, BITMAPINFOHEADER * pbiSrc)
|
|
{
|
|
if (fccHandler == 0)
|
|
mFccHandler = pbiSrc->biCompression;
|
|
else
|
|
mFccHandler = fccHandler;
|
|
|
|
if (mFccHandler == FOURCC_IV50)
|
|
mFccHandler = FOURCC_iv50;
|
|
|
|
// Open codec
|
|
mHic = ICLocate(ICTYPE_VIDEO, fccHandler, pbiSrc, 0, ICMODE_DECOMPRESS);
|
|
if (!mHic) return AVIERR_NOCOMPRESSOR;
|
|
|
|
delete [] mPBiSrc;
|
|
mPBiSrc = (BITMAPINFOHEADER *) new char [sizeof (BITMAPINFOHEADER) +
|
|
256 * sizeof (RGBQUAD)];
|
|
|
|
delete [] mPBiDst;
|
|
mPBiDst = (BITMAPINFOHEADER *) new char [sizeof (BITMAPINFOHEADER) +
|
|
256 * sizeof (RGBQUAD)];
|
|
AssertFatal(mPBiSrc && mPBiDst, "Out of memory");
|
|
|
|
dMemcpy(mPBiSrc, pbiSrc, sizeof(BITMAPINFOHEADER));
|
|
|
|
// Initialize destination bitmap header
|
|
dMemcpy (mPBiDst, mPBiSrc, sizeof(BITMAPINFOHEADER));
|
|
// Default destination bitmap header
|
|
mPBiDst->biBitCount = 24;
|
|
mPBiDst->biCompression = BI_RGB;
|
|
mPBiDst->biSizeImage = mPBiDst->biHeight * ALIGNULONG(mPBiDst->biWidth)*mPBiDst->biBitCount/8;
|
|
|
|
// Create temporary buffer
|
|
mCBuf = mPBiDst->biSizeImage;
|
|
mPBuf = (U8 *) dMalloc(mCBuf);
|
|
AssertFatal(mPBuf, "Out of memory");
|
|
dMemset(mPBuf, 0, mCBuf);
|
|
|
|
return VCMERR_OK;
|
|
}
|
|
|
|
S32 GuiAviBitmapCtrl::vcmClose()
|
|
{
|
|
if (mPBiSrc) delete [] mPBiSrc;
|
|
if (mPBiDst) delete [] mPBiDst;
|
|
mPBiSrc = mPBiDst = NULL;
|
|
|
|
if (mPBuf)
|
|
{
|
|
dFree(mPBuf);
|
|
mPBuf = NULL;
|
|
}
|
|
|
|
if (mHic)
|
|
{
|
|
ICClose(mHic);
|
|
mHic = NULL;
|
|
}
|
|
|
|
mFccHandler = 0;
|
|
|
|
return VCMERR_OK;
|
|
}
|
|
|
|
// vcmBegin() and vcmEnd() (de)initializes a series of vcmDraw()'s
|
|
// The user must end and restart a sequence when the destination
|
|
// parameters change.
|
|
// Note that if the source information changes vcmOpen()/vcmClose()
|
|
// must be used (since fcc might be different).
|
|
// fInit initializes the sequence (do the first time and when resetting
|
|
// parameters
|
|
//
|
|
S32 GuiAviBitmapCtrl::vcmBegin()
|
|
{
|
|
S32 rval;
|
|
|
|
if (!mHic)
|
|
return VCMERR_INTERNAL;
|
|
|
|
rval = ICDecompressExQuery(mHic, 0,
|
|
mPBiSrc, NULL, 0, 0, mBitmapWidth, mBitmapHeight,
|
|
mPBiDst, NULL, 0, 0, mBitmapWidth, mBitmapHeight);
|
|
|
|
if (rval) return rval;
|
|
|
|
rval = ICDecompressExBegin(mHic, 0,
|
|
mPBiSrc, NULL, 0, 0, mBitmapWidth, mBitmapHeight,
|
|
mPBiDst, NULL, 0, 0, mBitmapWidth, mBitmapHeight);
|
|
|
|
if (rval) return rval;
|
|
|
|
return AVIERR_OK;
|
|
}
|
|
|
|
S32 GuiAviBitmapCtrl::vcmEnd()
|
|
{
|
|
return VCMERR_OK;
|
|
}
|
|
|
|
// vcmDrawStart/vcmDrawStop are not absolutely necessary but some codecs
|
|
// use them to do timing (to tell when playing real time)
|
|
//
|
|
S32 GuiAviBitmapCtrl::vcmDrawStart()
|
|
{
|
|
// Send ICM_DRAW_BEGIN.
|
|
// this is only for telling the codec what our frame rate is - zero out all other members.
|
|
ICDrawBegin(mHic,
|
|
0, 0, 0, 0,
|
|
0, 0, 0, 0,
|
|
NULL,
|
|
0, 0, 0, 0,
|
|
(DWORD) (1.0/mFrate * 1000.0 * 1000.0), // dwRate
|
|
(DWORD) (1000*1000)); // dwScale
|
|
|
|
// Send ICM_DRAW_START.
|
|
ICDrawStart(mHic);
|
|
|
|
return VCMERR_OK;
|
|
}
|
|
|
|
S32 GuiAviBitmapCtrl::vcmDrawStop()
|
|
{
|
|
// Send ICM_DRAW_STOP
|
|
ICDrawStop(mHic);
|
|
|
|
// Send ICM_DRAW_END
|
|
ICDrawEnd(mHic);
|
|
|
|
return VCMERR_OK;
|
|
}
|
|
|
|
S32 GuiAviBitmapCtrl::vcmDraw(U64 dwICflags)
|
|
{
|
|
S32 rval;
|
|
|
|
rval = ICDecompressEx(mHic, dwICflags,
|
|
mPBiSrc, mPVBuf, 0, 0, mBitmapWidth, mBitmapHeight,
|
|
mPBiDst, mPBuf, 0, 0, mBitmapWidth, mBitmapHeight);
|
|
|
|
if (rval)
|
|
// Normal in case of ICM_HURRYUP flag (rval = 1)
|
|
return rval;
|
|
|
|
for (U32 j = 0; j < mHeightCount; j++)
|
|
{
|
|
U32 y = j * 256;
|
|
U32 height = getMin(mBitmapHeight - y, U32(256));
|
|
|
|
for (U32 i = 0; i < mWidthCount; i++)
|
|
{
|
|
U32 index = j * mWidthCount + i;
|
|
U32 x = i * 256;
|
|
U32 width = getMin(mBitmapWidth - x, U32(256));
|
|
GBitmap *bmp = mTextureHandles[index].getBitmap();
|
|
|
|
for (U32 lp = 0; lp < height; lp++)
|
|
{
|
|
const U8 *src = &mPBuf[3*((mBitmapHeight-(y+lp+1))*mBitmapAlignedWidth + x)];
|
|
U8 *dest = bmp->getAddress(0, lp);
|
|
|
|
// counting on the artist to switch the R & B channels so we don't have to in runtime
|
|
if (!mSwapRB)
|
|
dMemcpy(dest, src, width*3);
|
|
else
|
|
for (U32 k = 0; k < width; ++k)
|
|
{
|
|
*dest++ = src[2];
|
|
*dest++ = src[1];
|
|
*dest++ = src[0];
|
|
src += 3;
|
|
}
|
|
}
|
|
|
|
mTextureHandles[index].refresh();
|
|
}
|
|
}
|
|
|
|
return VCMERR_OK;
|
|
}
|
|
|
|
S32 GuiAviBitmapCtrl::vcmDrawIn(U64 dwICflags)
|
|
{
|
|
// If we are not displaying frames, IVI still writes to the buffer
|
|
S32 rval = ICDecompressEx(mHic, dwICflags,
|
|
mPBiSrc, mPVBuf, 0, 0, mBitmapWidth, mBitmapHeight,
|
|
mPBiDst, mPBuf, 0, 0, mBitmapWidth, mBitmapHeight);
|
|
|
|
if (rval)
|
|
// Normal in case of ICM_HURRYUP flag (rval = 1)
|
|
return rval;
|
|
|
|
return VCMERR_OK;
|
|
}
|
|
|
|
void GuiAviBitmapCtrl::initPersistFields()
|
|
{
|
|
Parent::initPersistFields();
|
|
|
|
addGroup("Media");
|
|
addField("aviFilename", TypeFilename, Offset(mAviFilename, GuiAviBitmapCtrl));
|
|
addField("wavFilename", TypeFilename, Offset(mWavFilename, GuiAviBitmapCtrl));
|
|
endGroup("Media");
|
|
|
|
addGroup("Misc");
|
|
addField("swapRB", TypeBool, Offset(mSwapRB, GuiAviBitmapCtrl));
|
|
addField("done", TypeBool, Offset(mDone, GuiAviBitmapCtrl));
|
|
addField("letterBox", TypeBool, Offset(mLetterBox, GuiAviBitmapCtrl));
|
|
addField("speed", TypeF32, Offset(mSpeed, GuiAviBitmapCtrl));
|
|
endGroup("Misc");
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void GuiAviBitmapCtrl::setFilename(const char *filename)
|
|
{
|
|
bool awake = mAwake;
|
|
|
|
if (awake)
|
|
onSleep();
|
|
|
|
mAviFilename = StringTable->insert(filename);
|
|
|
|
if (awake)
|
|
onWake();
|
|
}
|
|
|
|
// Start a movie, i.e. begin play from stopped state
|
|
// or restart from paused state
|
|
//
|
|
S32 GuiAviBitmapCtrl::movieStart()
|
|
{
|
|
if (!mPAviVideo)
|
|
return MOVERR_NOVIDEOSTREAM;
|
|
|
|
// Check if starting without stopping
|
|
if (mBPlaying)
|
|
return MOVERR_PLAYING;
|
|
|
|
mBPlaying = true;
|
|
|
|
sndStart();
|
|
|
|
// Start video, note only one state var, play or stop
|
|
vidsVideoStart();
|
|
setUpdate();
|
|
|
|
return MOVERR_OK;
|
|
}
|
|
|
|
// Stop playing a movie
|
|
//
|
|
S32 GuiAviBitmapCtrl::movieStop()
|
|
{
|
|
mBPlaying = false;
|
|
vidsVideoStop();
|
|
|
|
sndStop();
|
|
|
|
// notify the script
|
|
// Con::executef(this,1,"movieStopped");
|
|
|
|
mDone = true;
|
|
|
|
return MOVERR_OK;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool GuiAviBitmapCtrl::onWake()
|
|
{
|
|
if (!Parent::onWake()) return false;
|
|
|
|
if (fileOpen() || movieOpen())
|
|
{
|
|
mDone = true;
|
|
|
|
// we return TRUE here, or else the damn thing gets deleted and that's
|
|
// just plain bad.
|
|
|
|
return true;
|
|
}
|
|
|
|
sndOpen();
|
|
|
|
mBitmapWidth = mPBiSrc->biWidth;
|
|
mBitmapAlignedWidth = ALIGNULONG(mBitmapWidth);
|
|
mBitmapHeight = mPBiSrc->biHeight;
|
|
mWidthCount = mBitmapWidth / 256;
|
|
mHeightCount = mBitmapHeight / 256;
|
|
if (mBitmapWidth % 256)
|
|
mWidthCount++;
|
|
if (mBitmapHeight % 256)
|
|
mHeightCount++;
|
|
mNumTextures = mWidthCount * mHeightCount;
|
|
mTextureHandles = new TextureHandle[mNumTextures];
|
|
for (U32 j = 0; j < mHeightCount; j++)
|
|
{
|
|
U32 y = j * 256;
|
|
U32 height = getMin(mBitmapWidth - y, U32(256));
|
|
|
|
for (U32 i = 0; i < mWidthCount; i++)
|
|
{
|
|
char nameBuffer[64];
|
|
U32 index = j * mWidthCount + i;
|
|
|
|
dSprintf(nameBuffer, sizeof(nameBuffer), "%s_#%d_#%d", mAviFilename, i, j);
|
|
mTextureHandles[index] = TextureHandle(nameBuffer, RegisteredTexture, true);
|
|
if (!bool(mTextureHandles[index]))
|
|
{
|
|
U32 x = i * 256;
|
|
U32 width = getMin(mBitmapWidth - x, U32(256));
|
|
|
|
const GBitmap *bmp = new GBitmap(width, height, false, GBitmap::RGB);
|
|
|
|
mTextureHandles[index] = TextureHandle(nameBuffer, bmp, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void GuiAviBitmapCtrl::onSleep()
|
|
{
|
|
movieClose();
|
|
fileClose();
|
|
|
|
if (mTextureHandles)
|
|
{
|
|
delete [] mTextureHandles;
|
|
mTextureHandles = NULL;
|
|
}
|
|
|
|
Parent::onSleep();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void GuiAviBitmapCtrl::onMouseDown(const GuiEvent&)
|
|
{
|
|
// end the movie NOW!
|
|
movieStop();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool GuiAviBitmapCtrl::onKeyDown(const GuiEvent&)
|
|
{
|
|
// end the movie NOW!
|
|
movieStop();
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Playing with speed for debugging (glitch shows up when skipping occurs).
|
|
|
|
S32 GuiAviBitmapCtrl::getMilliseconds()
|
|
{
|
|
F32 ms;
|
|
|
|
if (mSpeed > 0)
|
|
{
|
|
ms = Platform::getRealMilliseconds();
|
|
ms *= mSpeed;
|
|
}
|
|
else
|
|
{
|
|
// Try to force the glitch (negative speed)-
|
|
static F32 deterministicClock = 0.0f;
|
|
ms = deterministicClock;
|
|
deterministicClock -= mSpeed;
|
|
}
|
|
|
|
return S32(ms);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void GuiAviBitmapCtrl::onRender(Point2I offset, const RectI &updateRect)
|
|
{
|
|
vidsVideoDraw();
|
|
|
|
if (mTextureHandles)
|
|
{
|
|
RectI displayRect(mBounds);
|
|
S32 verticalDisplace = 0;
|
|
|
|
if (mLetterBox)
|
|
{
|
|
// Our supplied picture is 3/4 height 640x360 for 640x480 res. But let's allow
|
|
// for a regular full-window version since it might come in handy elsewhere (so
|
|
// letterBox is public flag on the GUI).
|
|
verticalDisplace = (mBounds.extent.y >> 3);
|
|
displayRect.extent.y = (displayRect.extent.y * 3 >> 2);
|
|
displayRect.point.y += verticalDisplace;
|
|
RectI upperRect(offset.x, offset.y, displayRect.extent.x, verticalDisplace + 1);
|
|
dglDrawRectFill(upperRect, mProfile->mFillColorHL);
|
|
}
|
|
|
|
// Scale into the letterbox-
|
|
F32 widthScale = F32(displayRect.extent.x) / F32(mBitmapWidth);
|
|
F32 heightScale = F32(displayRect.extent.y) / F32(mBitmapHeight);
|
|
|
|
offset.y += verticalDisplace;
|
|
|
|
dglSetBitmapModulation(ColorF(1,1,1));
|
|
for (U32 i = 0; i < mWidthCount; i++)
|
|
{
|
|
for (U32 j = 0; j < mHeightCount; j++)
|
|
{
|
|
TextureHandle t = mTextureHandles[j * mWidthCount + i];
|
|
RectI stretchRegion;
|
|
|
|
stretchRegion.point.x = i * 256 * widthScale + offset.x;
|
|
stretchRegion.point.y = j * 256 * heightScale + offset.y;
|
|
stretchRegion.extent.x = (i * 256 + t.getWidth()) * widthScale + offset.x - stretchRegion.point.x;
|
|
stretchRegion.extent.y = (j * 256 + t.getHeight()) * heightScale + offset.y - stretchRegion.point.y;
|
|
dglDrawBitmapStretch(t, stretchRegion);
|
|
}
|
|
}
|
|
|
|
if (mLetterBox)
|
|
{
|
|
// For some reason the above loop draws white below, and this rect fill has to
|
|
// come after - got to look at that math... Also don't know why we need the
|
|
// extra width & height here...
|
|
RectI lowerRect(offset.x, mBounds.extent.y - verticalDisplace - 1,
|
|
mBounds.extent.x + 2, verticalDisplace + 2);
|
|
dglDrawRectFill(lowerRect, mProfile->mFillColorHL);
|
|
}
|
|
|
|
renderChildControls(offset, updateRect);
|
|
|
|
if (mBPlaying)
|
|
setUpdate();
|
|
}
|
|
else
|
|
Parent::onRender(offset, updateRect);
|
|
}
|
|
|
|
#endif /* ENABLE_AVI_GUI */
|
|
|
|
|
|
#if ENABLE_MPG_GUI
|
|
|
|
//----------------------------------------------------------------------------
|
|
IMPLEMENT_CONOBJECT(GuiAviBitmapCtrl);
|
|
|
|
GuiAviBitmapCtrl::GuiAviBitmapCtrl()
|
|
{
|
|
mAviFilename = StringTable->insert("");
|
|
mTextureHandles = NULL;
|
|
|
|
mMPEG = NULL;
|
|
mBPlaying = false;
|
|
mDone = false;
|
|
mLetterBox = false;
|
|
mSurface = NULL;
|
|
mPBuf = NULL;
|
|
mDecodeLock = NULL;
|
|
mWavHandle = NULL_AUDIOHANDLE;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
GuiAviBitmapCtrl::~GuiAviBitmapCtrl()
|
|
{
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// MPEG File Operations
|
|
|
|
// Open a file
|
|
//
|
|
S32 GuiAviBitmapCtrl::fileOpen()
|
|
{
|
|
char fileBuffer[256];
|
|
|
|
if (!dStrcmp(mAviFilename,""))
|
|
return MOVERR_NOVIDEOSTREAM;
|
|
|
|
// FixMe: Should convert this to have a dir attribute...
|
|
dSprintf(fileBuffer, sizeof(fileBuffer), "FixMe/textures/%s", mAviFilename);
|
|
|
|
// Convert filename from .avi to .mpg
|
|
char *ext;
|
|
ext = dStrstr(static_cast<const char *>(fileBuffer), ".avi");
|
|
if (!ext)
|
|
ext = dStrstr(static_cast<const char *>(fileBuffer), ".AVI");
|
|
if (ext)
|
|
dStrcpy(ext, ".mpg");
|
|
|
|
mMPEG = SMPEG_new(fileBuffer, &mInfo, 0);
|
|
if (!mMPEG || (SMPEG_status(mMPEG) == SMPEG_ERROR))
|
|
{
|
|
fileClose();
|
|
return MOVERR_NOVIDEOSTREAM;
|
|
}
|
|
return MOVERR_OK;
|
|
}
|
|
|
|
// Close a file
|
|
//
|
|
S32 GuiAviBitmapCtrl::fileClose ()
|
|
{
|
|
if (mMPEG)
|
|
{
|
|
SMPEG_delete(mMPEG);
|
|
mMPEG = NULL;
|
|
}
|
|
return MOVERR_OK;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Movie Operations
|
|
|
|
// A movie must have a video stream
|
|
// Return codes should be properly done but for now, 0 is no error
|
|
// while !0 is error unless otherwise noted
|
|
|
|
// Open a movie (video stream) from an open file
|
|
//
|
|
S32 GuiAviBitmapCtrl::movieOpen()
|
|
{
|
|
// If the file was opened successfully, it's an MPEG video
|
|
return MOVERR_OK;
|
|
}
|
|
|
|
// Close movie stream
|
|
//
|
|
S32 GuiAviBitmapCtrl::movieClose()
|
|
{
|
|
// Make sure movie is stopped
|
|
movieStop();
|
|
|
|
return MOVERR_OK;
|
|
}
|
|
|
|
|
|
void GuiAviBitmapCtrl::initPersistFields()
|
|
{
|
|
Parent::initPersistFields();
|
|
|
|
addGroup("Media");
|
|
addField("aviFilename", TypeFilename, Offset(mAviFilename, GuiAviBitmapCtrl));
|
|
addField("wavFilename", TypeFilename, Offset(mWavFilename, GuiAviBitmapCtrl));
|
|
endGroup("Media");
|
|
|
|
addGroup("Misc");
|
|
addField("done", TypeBool, Offset(mDone, GuiAviBitmapCtrl));
|
|
addField("letterBox", TypeBool, Offset(mLetterBox, GuiAviBitmapCtrl));
|
|
endGroup("Misc");
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void GuiAviBitmapCtrl::setFilename(const char *filename)
|
|
{
|
|
bool awake = mAwake;
|
|
|
|
if (awake)
|
|
onSleep();
|
|
|
|
mAviFilename = StringTable->insert(filename);
|
|
|
|
if (awake)
|
|
onWake();
|
|
}
|
|
|
|
// Start a movie, i.e. begin play from stopped state
|
|
// or restart from paused state
|
|
//
|
|
S32 GuiAviBitmapCtrl::movieStart()
|
|
{
|
|
if (!mMPEG)
|
|
return MOVERR_NOVIDEOSTREAM;
|
|
|
|
// Check if starting without stopping
|
|
if (mBPlaying)
|
|
return MOVERR_PLAYING;
|
|
|
|
mBPlaying = true;
|
|
|
|
sndStart();
|
|
|
|
// Start video, note only one state var, play or stop
|
|
SMPEG_play(mMPEG);
|
|
|
|
return MOVERR_OK;
|
|
}
|
|
|
|
// Stop playing a movie
|
|
//
|
|
S32 GuiAviBitmapCtrl::movieStop()
|
|
{
|
|
mBPlaying = false;
|
|
|
|
if (mMPEG)
|
|
{
|
|
SMPEG_stop(mMPEG);
|
|
}
|
|
sndStop();
|
|
|
|
// notify the script
|
|
// Con::executef(this,1,"movieStopped");
|
|
|
|
mDone = true;
|
|
|
|
return MOVERR_OK;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool GuiAviBitmapCtrl::onWake()
|
|
{
|
|
if (!Parent::onWake()) return false;
|
|
|
|
if (fileOpen() || movieOpen())
|
|
{
|
|
mDone = true;
|
|
// Never return false from onWake, the object will be freed, but
|
|
// not removed from the gui framework, so the game crashes later.
|
|
return true;
|
|
}
|
|
|
|
sndOpen();
|
|
|
|
mBitmapWidth = mInfo.width;
|
|
mBitmapAlignedWidth = ALIGNULONG(mBitmapWidth);
|
|
AssertFatal(mBitmapAlignedWidth == mBitmapWidth, "Unaligned MPEG data");
|
|
mBitmapHeight = mInfo.height;
|
|
mWidthCount = mBitmapWidth / 256;
|
|
mHeightCount = mBitmapHeight / 256;
|
|
if (mBitmapWidth % 256)
|
|
mWidthCount++;
|
|
if (mBitmapHeight % 256)
|
|
mHeightCount++;
|
|
mNumTextures = mWidthCount * mHeightCount;
|
|
mTextureHandles = new TextureHandle[mNumTextures];
|
|
for (U32 j = 0; j < mHeightCount; j++)
|
|
{
|
|
U32 y = j * 256;
|
|
U32 height = getMin(mBitmapWidth - y, U32(256));
|
|
|
|
for (U32 i = 0; i < mWidthCount; i++)
|
|
{
|
|
char nameBuffer[64];
|
|
U32 index = j * mWidthCount + i;
|
|
|
|
dSprintf(nameBuffer, sizeof(nameBuffer), "%s_#%d_#%d", mAviFilename, i, j);
|
|
mTextureHandles[index] = TextureHandle(nameBuffer, RegisteredTexture, true);
|
|
if (!bool(mTextureHandles[index]))
|
|
{
|
|
U32 x = i * 256;
|
|
U32 width = getMin(mBitmapWidth - x, U32(256));
|
|
|
|
const GBitmap *bmp = new GBitmap(width, height, false, GBitmap::RGB);
|
|
|
|
mTextureHandles[index] = TextureHandle(nameBuffer, bmp, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Allocate the SDL surface for the YUV decoding
|
|
// It shore wood be nice if SDL could decode YUV to GL textures...
|
|
mPBuf = (U8 *) dMalloc(mBitmapWidth*3*mBitmapHeight);
|
|
AssertFatal(mPBuf, "Out of memory for buffer.");
|
|
mSurface = SDL_CreateRGBSurfaceFrom(mPBuf,
|
|
mBitmapWidth, mBitmapHeight, 24,
|
|
mBitmapWidth*3,
|
|
0x000000FF, 0x0000FF00, 0x00FF0000, 0);
|
|
AssertFatal(mSurface, "Out of memory for a surface!");
|
|
|
|
// Target the decoding to our surface
|
|
mDecodeLock = SDL_CreateMutex();
|
|
AssertFatal(mDecodeLock, "Out of memory for a decode lock!");
|
|
SMPEG_setdisplay(mMPEG, mSurface, mDecodeLock, NULL);
|
|
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void GuiAviBitmapCtrl::onSleep()
|
|
{
|
|
movieClose();
|
|
fileClose();
|
|
|
|
if (mSurface)
|
|
{
|
|
SDL_FreeSurface(mSurface);
|
|
mSurface = NULL;
|
|
}
|
|
|
|
if (mPBuf)
|
|
{
|
|
dFree(mPBuf);
|
|
mPBuf = NULL;
|
|
}
|
|
|
|
if (mTextureHandles)
|
|
{
|
|
delete [] mTextureHandles;
|
|
mTextureHandles = NULL;
|
|
}
|
|
|
|
Parent::onSleep();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void GuiAviBitmapCtrl::onMouseDown(const GuiEvent&)
|
|
{
|
|
// end the movie NOW!
|
|
movieStop();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool GuiAviBitmapCtrl::onKeyDown(const GuiEvent&)
|
|
{
|
|
// end the movie NOW!
|
|
movieStop();
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void GuiAviBitmapCtrl::onRender(Point2I offset, const RectI &updateRect)
|
|
{
|
|
if (mTextureHandles)
|
|
{
|
|
RectI displayRect(mBounds);
|
|
S32 verticalDisplace = 0;
|
|
|
|
// Get the converted RGB data from SMPEG
|
|
SDL_LockMutex(mDecodeLock);
|
|
for (U32 j = 0; j < mHeightCount; j++)
|
|
{
|
|
U32 y = j * 256;
|
|
U32 height = getMin(mBitmapHeight - y, U32(256));
|
|
|
|
for (U32 i = 0; i < mWidthCount; i++)
|
|
{
|
|
U32 index = j * mWidthCount + i;
|
|
U32 x = i * 256;
|
|
U32 width = getMin(mBitmapWidth - x, U32(256));
|
|
GBitmap *bmp = mTextureHandles[index].getBitmap();
|
|
|
|
for (U32 lp = 0; lp < height; lp++)
|
|
{
|
|
const U8 *src = &mPBuf[3*((y+lp)*mBitmapAlignedWidth + x)];
|
|
U8 *dest = bmp->getAddress(0, lp);
|
|
|
|
dMemcpy(dest, src, width*3);
|
|
}
|
|
mTextureHandles[index].refresh();
|
|
}
|
|
}
|
|
SDL_UnlockMutex(mDecodeLock);
|
|
|
|
if (mLetterBox)
|
|
{
|
|
// Our supplied picture is 3/4 height 640x360 for 640x480 res. But let's allow
|
|
// for a regular full-window version since it might come in handy elsewhere (so
|
|
// letterBox is public flag on the GUI).
|
|
verticalDisplace = (mBounds.extent.y >> 3);
|
|
displayRect.extent.y = (displayRect.extent.y * 3 >> 2);
|
|
displayRect.point.y += verticalDisplace;
|
|
RectI upperRect(offset.x, offset.y, displayRect.extent.x, verticalDisplace + 1);
|
|
dglDrawRectFill(upperRect, mProfile->mFillColorHL);
|
|
}
|
|
|
|
// Scale into the letterbox-
|
|
F32 widthScale = F32(displayRect.extent.x) / F32(mBitmapWidth);
|
|
F32 heightScale = F32(displayRect.extent.y) / F32(mBitmapHeight);
|
|
|
|
offset.y += verticalDisplace;
|
|
|
|
dglSetBitmapModulation(ColorF(1,1,1));
|
|
for (U32 i = 0; i < mWidthCount; i++)
|
|
{
|
|
for (U32 j = 0; j < mHeightCount; j++)
|
|
{
|
|
TextureHandle t = mTextureHandles[j * mWidthCount + i];
|
|
RectI stretchRegion;
|
|
|
|
stretchRegion.point.x = i * 256 * widthScale + offset.x;
|
|
stretchRegion.point.y = j * 256 * heightScale + offset.y;
|
|
stretchRegion.extent.x = (i * 256 + t.getWidth()) * widthScale + offset.x - stretchRegion.point.x;
|
|
stretchRegion.extent.y = (j * 256 + t.getHeight()) * heightScale + offset.y - stretchRegion.point.y;
|
|
dglDrawBitmapStretch(t, stretchRegion);
|
|
}
|
|
}
|
|
|
|
if (mLetterBox)
|
|
{
|
|
// For some reason the above loop draws white below, and this rect fill has to
|
|
// come after - got to look at that math... Also don't know why we need the
|
|
// extra width & height here...
|
|
RectI lowerRect(offset.x, mBounds.extent.y - verticalDisplace - 1,
|
|
mBounds.extent.x + 2, verticalDisplace + 2);
|
|
dglDrawRectFill(lowerRect, mProfile->mFillColorHL);
|
|
}
|
|
|
|
renderChildControls(offset, updateRect);
|
|
|
|
mBPlaying = (SMPEG_status(mMPEG) == SMPEG_PLAYING);
|
|
if (mBPlaying)
|
|
setUpdate();
|
|
else
|
|
movieStop();
|
|
}
|
|
else
|
|
Parent::onRender(offset, updateRect);
|
|
}
|
|
|
|
#endif /* ENABLE_MPG_GUI */
|
|
|
|
#endif /* Enabled Movie GUI */
|