aboutsummaryrefslogtreecommitdiff
path: root/engines/director/frame.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/director/frame.cpp')
-rw-r--r--engines/director/frame.cpp760
1 files changed, 760 insertions, 0 deletions
diff --git a/engines/director/frame.cpp b/engines/director/frame.cpp
new file mode 100644
index 0000000000..abb880a6cf
--- /dev/null
+++ b/engines/director/frame.cpp
@@ -0,0 +1,760 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program 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 program 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/system.h"
+#include "graphics/macgui/macwindowmanager.h"
+#include "image/bmp.h"
+
+#include "director/director.h"
+#include "director/frame.h"
+#include "director/images.h"
+#include "director/score.h"
+#include "director/sprite.h"
+
+namespace Director {
+
+Frame::Frame(DirectorEngine *vm) {
+ _vm = vm;
+ _transDuration = 0;
+ _transType = kTransNone;
+ _transArea = 0;
+ _transChunkSize = 0;
+ _tempo = 0;
+
+ _sound1 = 0;
+ _sound2 = 0;
+ _soundType1 = 0;
+ _soundType2 = 0;
+
+ _actionId = 0;
+ _skipFrameFlag = 0;
+ _blend = 0;
+
+ _sprites.resize(CHANNEL_COUNT);
+
+ for (uint16 i = 0; i < _sprites.size(); i++) {
+ Sprite *sp = new Sprite();
+ _sprites[i] = sp;
+ }
+}
+
+Frame::Frame(const Frame &frame) {
+ _vm = frame._vm;
+ _actionId = frame._actionId;
+ _transArea = frame._transArea;
+ _transDuration = frame._transDuration;
+ _transType = frame._transType;
+ _transChunkSize = frame._transChunkSize;
+ _tempo = frame._tempo;
+ _sound1 = frame._sound1;
+ _sound2 = frame._sound2;
+ _soundType1 = frame._soundType1;
+ _soundType2 = frame._soundType2;
+ _skipFrameFlag = frame._skipFrameFlag;
+ _blend = frame._blend;
+ _palette = new PaletteInfo();
+
+ _sprites.resize(CHANNEL_COUNT);
+
+ for (uint16 i = 0; i < CHANNEL_COUNT; i++) {
+ _sprites[i] = new Sprite(*frame._sprites[i]);
+ }
+}
+
+Frame::~Frame() {
+ delete _palette;
+}
+
+void Frame::readChannel(Common::SeekableSubReadStreamEndian &stream, uint16 offset, uint16 size) {
+ if (offset >= 32) {
+ if (size <= 16)
+ readSprite(stream, offset, size);
+ else {
+ //read > 1 sprites channel
+ while (size > 16) {
+ byte spritePosition = (offset - 32) / 16;
+ uint16 nextStart = (spritePosition + 1) * 16 + 32;
+ uint16 needSize = nextStart - offset;
+ readSprite(stream, offset, needSize);
+ offset += needSize;
+ size -= needSize;
+ }
+ readSprite(stream, offset, size);
+ }
+ } else {
+ readMainChannels(stream, offset, size);
+ }
+}
+
+void Frame::readMainChannels(Common::SeekableSubReadStreamEndian &stream, uint16 offset, uint16 size) {
+ uint16 finishPosition = offset + size;
+
+ while (offset < finishPosition) {
+ switch(offset) {
+ case kScriptIdPosition:
+ _actionId = stream.readByte();
+ offset++;
+ break;
+ case kSoundType1Position:
+ _soundType1 = stream.readByte();
+ offset++;
+ break;
+ case kTransFlagsPosition: {
+ uint8 transFlags = stream.readByte();
+ if (transFlags & 0x80)
+ _transArea = 1;
+ else
+ _transArea = 0;
+ _transDuration = transFlags & 0x7f;
+ offset++;
+ }
+ break;
+ case kTransChunkSizePosition:
+ _transChunkSize = stream.readByte();
+ offset++;
+ break;
+ case kTempoPosition:
+ _tempo = stream.readByte();
+ offset++;
+ break;
+ case kTransTypePosition:
+ _transType = static_cast<TransitionType>(stream.readByte());
+ offset++;
+ break;
+ case kSound1Position:
+ _sound1 = stream.readUint16();
+ offset+=2;
+ break;
+ case kSkipFrameFlagsPosition:
+ _skipFrameFlag = stream.readByte();
+ offset++;
+ break;
+ case kBlendPosition:
+ _blend = stream.readByte();
+ offset++;
+ break;
+ case kSound2Position:
+ _sound2 = stream.readUint16();
+ offset += 2;
+ break;
+ case kSound2TypePosition:
+ _soundType2 = stream.readByte();
+ offset += 1;
+ break;
+ case kPaletePosition:
+ if (stream.readUint16())
+ readPaletteInfo(stream);
+ offset += 16;
+ break;
+ default:
+ offset++;
+ stream.readByte();
+ debug("Field Position %d, Finish Position %d", offset, finishPosition);
+ break;
+ }
+ }
+}
+
+void Frame::readPaletteInfo(Common::SeekableSubReadStreamEndian &stream) {
+ _palette->firstColor = stream.readByte();
+ _palette->lastColor = stream.readByte();
+ _palette->flags = stream.readByte();
+ _palette->speed = stream.readByte();
+ _palette->frameCount = stream.readUint16();
+ stream.skip(8); //unknown
+}
+
+void Frame::readSprite(Common::SeekableSubReadStreamEndian &stream, uint16 offset, uint16 size) {
+ uint16 spritePosition = (offset - 32) / 16;
+ uint16 spriteStart = spritePosition * 16 + 32;
+
+ uint16 fieldPosition = offset - spriteStart;
+ uint16 finishPosition = fieldPosition + size;
+
+ Sprite &sprite = *_sprites[spritePosition];
+
+ while (fieldPosition < finishPosition) {
+ switch (fieldPosition) {
+ case kSpritePositionUnk1:
+ /*byte x1 = */ stream.readByte();
+ fieldPosition++;
+ break;
+ case kSpritePositionEnabled:
+ sprite._enabled = (stream.readByte() != 0);
+ fieldPosition++;
+ break;
+ case kSpritePositionUnk2:
+ /*byte x2 = */ stream.readUint16();
+ fieldPosition += 2;
+ break;
+ case kSpritePositionFlags:
+ sprite._flags = stream.readUint16();
+ sprite._ink = static_cast<InkType>(sprite._flags & 0x3f);
+
+ if (sprite._flags & 0x40)
+ sprite._trails = 1;
+ else
+ sprite._trails = 0;
+
+ fieldPosition += 2;
+ break;
+ case kSpritePositionCastId:
+ sprite._castId = stream.readUint16();
+ fieldPosition += 2;
+ break;
+ case kSpritePositionY:
+ sprite._startPoint.y = stream.readUint16();
+ fieldPosition += 2;
+ break;
+ case kSpritePositionX:
+ sprite._startPoint.x = stream.readUint16();
+ fieldPosition += 2;
+ break;
+ case kSpritePositionWidth:
+ sprite._width = stream.readUint16();
+ fieldPosition += 2;
+ break;
+ case kSpritePositionHeight:
+ sprite._height = stream.readUint16();
+ fieldPosition += 2;
+ break;
+ default:
+ //end cycle, go to next sprite channel
+ readSprite(stream, spriteStart + 16, finishPosition - fieldPosition);
+ fieldPosition = finishPosition;
+ break;
+ }
+ }
+}
+
+void Frame::prepareFrame(Score *score) {
+ renderSprites(*score->_surface, false);
+ renderSprites(*score->_trailSurface, true);
+
+ if (_transType != 0)
+ //TODO Handle changing area case
+ playTransition(score);
+
+ if (_sound1 != 0 || _sound2 != 0) {
+ playSoundChannel();
+ }
+
+ g_system->copyRectToScreen(score->_surface->getPixels(), score->_surface->pitch, 0, 0, score->_surface->getBounds().width(), score->_surface->getBounds().height());
+}
+
+void Frame::playSoundChannel() {
+ debug(0, "Sound2 %d", _sound2);
+ debug(0, "Sound1 %d", _sound1);
+}
+
+void Frame::playTransition(Score *score) {
+ uint16 duration = _transDuration * 250; // _transDuration in 1/4 of sec
+ duration = (duration == 0 ? 250 : duration); // director support transition duration = 0, but animation play like value = 1, idk.
+
+ if (_transChunkSize == 0)
+ _transChunkSize = 1; //equal 1 step
+
+ uint16 stepDuration = duration / _transChunkSize;
+ uint16 steps = duration / stepDuration;
+
+ switch (_transType) {
+ case kTransCoverDown:
+ {
+ uint16 stepSize = score->_movieRect.height() / steps;
+ Common::Rect r = score->_movieRect;
+
+ for (uint16 i = 1; i < steps; i++) {
+ r.setHeight(stepSize * i);
+
+ g_system->delayMillis(stepDuration);
+ score->processEvents();
+
+ g_system->copyRectToScreen(score->_surface->getPixels(), score->_surface->pitch, 0, 0, r.width(), r.height());
+ g_system->updateScreen();
+ }
+ }
+ break;
+ case kTransCoverUp:
+ {
+ uint16 stepSize = score->_movieRect.height() / steps;
+ Common::Rect r = score->_movieRect;
+
+ for (uint16 i = 1; i < steps; i++) {
+ r.setHeight(stepSize * i);
+
+ g_system->delayMillis(stepDuration);
+ score->processEvents();
+
+ g_system->copyRectToScreen(score->_surface->getPixels(), score->_surface->pitch, 0, score->_movieRect.height() - stepSize * i, r.width(), r.height());
+ g_system->updateScreen();
+ }
+ }
+ break;
+ case kTransCoverRight: {
+ uint16 stepSize = score->_movieRect.width() / steps;
+ Common::Rect r = score->_movieRect;
+
+ for (uint16 i = 1; i < steps; i++) {
+ r.setWidth(stepSize * i);
+
+ g_system->delayMillis(stepDuration);
+ score->processEvents();
+
+ g_system->copyRectToScreen(score->_surface->getPixels(), score->_surface->pitch, 0, 0, r.width(), r.height());
+ g_system->updateScreen();
+ }
+ }
+ break;
+ case kTransCoverLeft: {
+ uint16 stepSize = score->_movieRect.width() / steps;
+ Common::Rect r = score->_movieRect;
+
+ for (uint16 i = 1; i < steps; i++) {
+ r.setWidth(stepSize * i);
+
+ g_system->delayMillis(stepDuration);
+ score->processEvents();
+
+ g_system->copyRectToScreen(score->_surface->getPixels(), score->_surface->pitch, score->_movieRect.width() - stepSize * i, 0, r.width(), r.height());
+ g_system->updateScreen();
+ }
+ }
+ break;
+ case kTransCoverUpLeft: {
+ uint16 stepSize = score->_movieRect.width() / steps;
+ Common::Rect r = score->_movieRect;
+
+ for (uint16 i = 1; i < steps; i++) {
+ r.setWidth(stepSize * i);
+ r.setHeight(stepSize * i);
+
+ g_system->delayMillis(stepDuration);
+ score->processEvents();
+
+ g_system->copyRectToScreen(score->_surface->getPixels(), score->_surface->pitch, score->_movieRect.width() - stepSize * i, score->_movieRect.height() - stepSize * i, r.width(), r.height());
+ g_system->updateScreen();
+ }
+ }
+ break;
+ case kTransCoverUpRight: {
+ uint16 stepSize = score->_movieRect.width() / steps;
+ Common::Rect r = score->_movieRect;
+
+ for (uint16 i = 1; i < steps; i++) {
+ r.setWidth(stepSize * i);
+ r.setHeight(stepSize * i);
+
+ g_system->delayMillis(stepDuration);
+ score->processEvents();
+
+ g_system->copyRectToScreen(score->_surface->getPixels(), score->_surface->pitch, 0, score->_movieRect.height() - stepSize * i, r.width(), r.height());
+ g_system->updateScreen();
+ }
+ }
+ break;
+ case kTransCoverDownLeft: {
+ uint16 stepSize = score->_movieRect.width() / steps;
+ Common::Rect r = score->_movieRect;
+
+ for (uint16 i = 1; i < steps; i++) {
+ r.setWidth(stepSize * i);
+ r.setHeight(stepSize * i);
+
+ g_system->delayMillis(stepDuration);
+ score->processEvents();
+
+ g_system->copyRectToScreen(score->_surface->getPixels(), score->_surface->pitch, score->_movieRect.width() - stepSize * i, 0, r.width(), r.height());
+ g_system->updateScreen();
+ }
+ }
+ break;
+ case kTransCoverDownRight: {
+ uint16 stepSize = score->_movieRect.width() / steps;
+ Common::Rect r = score->_movieRect;
+
+ for (uint16 i = 1; i < steps; i++) {
+ r.setWidth(stepSize * i);
+ r.setHeight(stepSize * i);
+
+ g_system->delayMillis(stepDuration);
+ score->processEvents();
+
+ g_system->copyRectToScreen(score->_surface->getPixels(), score->_surface->pitch, 0, 0, r.width(), r.height());
+ g_system->updateScreen();
+ }
+ }
+ break;
+ default:
+ warning("Unhandled transition type %d %d %d", _transType, duration, _transChunkSize);
+ break;
+
+ }
+}
+
+void Frame::renderSprites(Graphics::ManagedSurface &surface, bool renderTrail) {
+ for (uint16 i = 0; i < CHANNEL_COUNT; i++) {
+ if (_sprites[i]->_enabled) {
+ if ((_sprites[i]->_trails == 0 && renderTrail) || (_sprites[i]->_trails == 1 && !renderTrail))
+ continue;
+
+ Cast *cast;
+ if (!_vm->_currentScore->_casts.contains(_sprites[i]->_castId)) {
+ if (!_vm->getSharedCasts()->contains(_sprites[i]->_castId)) {
+ warning("Cast id %d not found", _sprites[i]->_castId);
+ continue;
+ } else {
+ cast = _vm->getSharedCasts()->getVal(_sprites[i]->_castId);
+ }
+ } else {
+ cast = _vm->_currentScore->_casts[_sprites[i]->_castId];
+ }
+
+ if (cast->type == kCastText) {
+ renderText(surface, i);
+ continue;
+ }
+
+ Image::ImageDecoder *img = getImageFrom(_sprites[i]->_castId, _sprites[i]->_width, _sprites[i]->_height);
+
+ if (!img) {
+ warning("Image with id %d not found", _sprites[i]->_castId);
+ continue;
+ }
+
+ if (!img->getSurface()) {
+ //TODO
+ //BMPDecoder doesnt cover all BITD resources (not all have first two bytes 'BM')
+ //Some BITD's first two bytes 0x6 0x0
+ warning("Can not load image %d", _sprites[i]->_castId);
+ continue;
+ }
+
+ uint32 regX = static_cast<BitmapCast *>(_sprites[i]->_cast)->regX;
+ uint32 regY = static_cast<BitmapCast *>(_sprites[i]->_cast)->regY;
+ uint32 rectLeft = static_cast<BitmapCast *>(_sprites[i]->_cast)->initialRect.left;
+ uint32 rectTop = static_cast<BitmapCast *>(_sprites[i]->_cast)->initialRect.top;
+
+ int x = _sprites[i]->_startPoint.x - regX + rectLeft;
+ int y = _sprites[i]->_startPoint.y - regY + rectTop;
+ int height = _sprites[i]->_height;
+ int width = _sprites[i]->_width;
+
+ Common::Rect drawRect = Common::Rect(x, y, x + width, y + height);
+ _drawRects.push_back(drawRect);
+
+ switch (_sprites[i]->_ink) {
+ case kInkTypeCopy:
+ surface.blitFrom(*img->getSurface(), Common::Point(x, y));
+ break;
+ case kInkTypeBackgndTrans:
+ drawBackgndTransSprite(surface, *img->getSurface(), drawRect);
+ break;
+ case kInkTypeMatte:
+ drawMatteSprite(surface, *img->getSurface(), drawRect);
+ break;
+ case kInkTypeGhost:
+ drawGhostSprite(surface, *img->getSurface(), drawRect);
+ break;
+ case kInkTypeReverse:
+ drawReverseSprite(surface, *img->getSurface(), drawRect);
+ break;
+ default:
+ warning("Unhandled ink type %d", _sprites[i]->_ink);
+ surface.blitFrom(*img->getSurface(), Common::Point(x, y));
+ break;
+ }
+ }
+ }
+}
+
+void Frame::renderButton(Graphics::ManagedSurface &surface, uint16 spriteId) {
+ renderText(surface, spriteId);
+
+ uint16 castID = _sprites[spriteId]->_castId;
+ ButtonCast *button = static_cast<ButtonCast *>(_vm->_currentScore->_casts[castID]);
+
+ uint32 rectLeft = button->initialRect.left;
+ uint32 rectTop = button->initialRect.top;
+
+ int x = _sprites[spriteId]->_startPoint.x + rectLeft;
+ int y = _sprites[spriteId]->_startPoint.y + rectTop;
+ int height = _sprites[spriteId]->_height;
+ int width = _sprites[spriteId]->_width;
+
+ switch (button->buttonType) {
+ case kTypeCheckBox:
+ //Magic numbers: checkbox square need to move left about 5px from text and 12px side size (d4)
+ surface.frameRect(Common::Rect(x - 17, y, x + 12, y + 12), 0);
+ break;
+ case kTypeButton:
+ surface.frameRect(Common::Rect(x, y, x + width, y + height), 0);
+ break;
+ case kTypeRadio:
+ warning("STUB: renderButton: kTypeRadio");
+ break;
+ }
+}
+
+Image::ImageDecoder *Frame::getImageFrom(uint16 spriteId, int w, int h) {
+ uint16 imgId = spriteId + 1024;
+ Image::ImageDecoder *img = NULL;
+
+ if (_vm->_currentScore->getArchive()->hasResource(MKTAG('D', 'I', 'B', ' '), imgId)) {
+ img = new DIBDecoder();
+ img->loadStream(*_vm->_currentScore->getArchive()->getResource(MKTAG('D', 'I', 'B', ' '), imgId));
+ return img;
+ }
+
+ if (_vm->getSharedDIB() != NULL && _vm->getSharedDIB()->contains(imgId)) {
+ img = new DIBDecoder();
+ img->loadStream(*_vm->getSharedDIB()->getVal(imgId));
+ return img;
+ }
+
+ if (_vm->_currentScore->getArchive()->hasResource(MKTAG('B', 'I', 'T', 'D'), imgId)) {
+ if (_vm->getVersion() < 4) {
+ img = new BITDDecoder(w, h);
+ } else {
+ img = new Image::BitmapDecoder();
+ }
+
+ if (debugChannelSet(8, kDebugLoading)) {
+ Common::SeekableReadStream *s = _vm->_currentScore->getArchive()->getResource(MKTAG('B', 'I', 'T', 'D'), imgId);
+ byte buf[1024];
+ int n = s->read(buf, 1024);
+ Common::hexdump(buf, n);
+ }
+
+ img->loadStream(*_vm->_currentScore->getArchive()->getResource(MKTAG('B', 'I', 'T', 'D'), imgId));
+ return img;
+ }
+
+ if (_vm->getSharedBMP() != NULL && _vm->getSharedBMP()->contains(imgId)) {
+ img = new Image::BitmapDecoder();
+ img->loadStream(*_vm->getSharedBMP()->getVal(imgId));
+ return img;
+ }
+
+ warning("Image %d not found", spriteId);
+ return img;
+}
+
+
+void Frame::renderText(Graphics::ManagedSurface &surface, uint16 spriteID) {
+ uint16 castID = _sprites[spriteID]->_castId;
+
+ TextCast *textCast = static_cast<TextCast *>(_vm->_currentScore->_casts[castID]);
+ Common::SeekableSubReadStreamEndian *textStream;
+
+ if (_vm->_currentScore->_movieArchive->hasResource(MKTAG('S','T','X','T'), castID + 1024)) {
+ textStream = _vm->_currentScore->_movieArchive->getResource(MKTAG('S','T','X','T'), castID + 1024);
+ } else {
+ textStream = _vm->getSharedSTXT()->getVal(spriteID + 1024);
+ }
+ /*uint32 unk1 = */ textStream->readUint32();
+ uint32 strLen = textStream->readUint32();
+ /*uin32 dataLen = */ textStream->readUint32();
+ Common::String text;
+
+ for (uint32 i = 0; i < strLen; i++) {
+ byte ch = textStream->readByte();
+ if (ch == 0x0d) {
+ ch = '\n';
+ }
+ text += ch;
+ }
+
+ uint32 rectLeft = static_cast<TextCast *>(_sprites[spriteID]->_cast)->initialRect.left;
+ uint32 rectTop = static_cast<TextCast *>(_sprites[spriteID]->_cast)->initialRect.top;
+
+ int x = _sprites[spriteID]->_startPoint.x + rectLeft;
+ int y = _sprites[spriteID]->_startPoint.y + rectTop;
+ int height = _sprites[spriteID]->_height;
+ int width = _sprites[spriteID]->_width;
+
+ const char *fontName;
+
+ if (_vm->_currentScore->_fontMap.contains(textCast->fontId)) {
+ fontName = _vm->_currentScore->_fontMap[textCast->fontId].c_str();
+ } else if ((fontName = _vm->_wm->getFontName(textCast->fontId, textCast->fontSize)) == NULL) {
+ warning("Unknown font id %d, falling back to default", textCast->fontId);
+ fontName = _vm->_wm->getFontName(0, 12);
+ }
+
+ const Graphics::Font *font = _vm->_wm->getFont(fontName, Graphics::FontManager::kBigGUIFont);
+
+ font->drawString(&surface, text, x, y, width, 0);
+
+ if (textCast->borderSize != kSizeNone) {
+ uint16 size = textCast->borderSize;
+
+ //Indent from borders, measured in d4
+ x -= 1;
+ y -= 4;
+
+ height += 4;
+ width += 1;
+
+ while (size) {
+ surface.frameRect(Common::Rect(x, y, x + height, y + width), 0);
+ x--;
+ y--;
+ height += 2;
+ width += 2;
+ size--;
+ }
+ }
+
+ if (textCast->gutterSize != kSizeNone) {
+ x -= 1;
+ y -= 4;
+
+ height += 4;
+ width += 1;
+ uint16 size = textCast->gutterSize;
+
+ surface.frameRect(Common::Rect(x, y, x + height, y + width), 0);
+
+ while (size) {
+ surface.drawLine(x + width, y, x + width, y + height, 0);
+ surface.drawLine(x, y + height, x + width, y + height, 0);
+ x++;
+ y++;
+ size--;
+ }
+ }
+}
+
+void Frame::drawBackgndTransSprite(Graphics::ManagedSurface &target, const Graphics::Surface &sprite, Common::Rect &drawRect) {
+ uint8 skipColor = _vm->getPaletteColorCount() - 1; //FIXME is it always white (last entry in pallette) ?
+
+ for (int ii = 0; ii < sprite.h; ii++) {
+ const byte *src = (const byte *)sprite.getBasePtr(0, ii);
+ byte *dst = (byte *)target.getBasePtr(drawRect.left, drawRect.top + ii);
+
+ for (int j = 0; j < drawRect.width(); j++) {
+ if (*src != skipColor)
+ *dst = *src;
+
+ src++;
+ dst++;
+ }
+ }
+}
+
+void Frame::drawGhostSprite(Graphics::ManagedSurface &target, const Graphics::Surface &sprite, Common::Rect &drawRect) {
+ uint8 skipColor = _vm->getPaletteColorCount() - 1;
+ for (int ii = 0; ii < sprite.h; ii++) {
+ const byte *src = (const byte *)sprite.getBasePtr(0, ii);
+ byte *dst = (byte *)target.getBasePtr(drawRect.left, drawRect.top + ii);
+
+ for (int j = 0; j < drawRect.width(); j++) {
+ if ((getSpriteIDFromPos(Common::Point(drawRect.left + j, drawRect.top + ii)) != 0) && (*src != skipColor))
+ *dst = (_vm->getPaletteColorCount() - 1) - *src; //Oposite color
+
+ src++;
+ dst++;
+ }
+ }
+}
+
+void Frame::drawReverseSprite(Graphics::ManagedSurface &target, const Graphics::Surface &sprite, Common::Rect &drawRect) {
+ uint8 skipColor = _vm->getPaletteColorCount() - 1;
+ for (int ii = 0; ii < sprite.h; ii++) {
+ const byte *src = (const byte *)sprite.getBasePtr(0, ii);
+ byte *dst = (byte *)target.getBasePtr(drawRect.left, drawRect.top + ii);
+
+ for (int j = 0; j < drawRect.width(); j++) {
+ if ((getSpriteIDFromPos(Common::Point(drawRect.left + j, drawRect.top + ii)) != 0))
+ *dst = (_vm->getPaletteColorCount() - 1) - *src;
+ else if (*src != skipColor)
+ *dst = *src;
+ src++;
+ dst++;
+ }
+ }
+}
+
+void Frame::drawMatteSprite(Graphics::ManagedSurface &target, const Graphics::Surface &sprite, Common::Rect &drawRect) {
+ //Like background trans, but all white pixels NOT ENCLOSED by coloured pixels are transparent
+ Graphics::Surface tmp;
+ tmp.copyFrom(sprite);
+
+ // Searching white color in the corners
+ int whiteColor = -1;
+
+ for (int corner = 0; corner < 4; corner++) {
+ int x = (corner & 0x1) ? tmp.w - 1 : 0;
+ int y = (corner & 0x2) ? tmp.h - 1 : 0;
+
+ byte color = *(byte *)tmp.getBasePtr(x, y);
+
+ if (_vm->getPalette()[color * 3 + 0] == 0xff &&
+ _vm->getPalette()[color * 3 + 1] == 0xff &&
+ _vm->getPalette()[color * 3 + 2] == 0xff) {
+ whiteColor = color;
+ break;
+ }
+ }
+
+ if (whiteColor == -1) {
+ warning("No white color for Matte image");
+ whiteColor = *(byte *)tmp.getBasePtr(0, 0);
+ }
+
+ Graphics::FloodFill ff(&tmp, whiteColor, 0, true);
+
+ for (int yy = 0; yy < tmp.h; yy++) {
+ ff.addSeed(0, yy);
+ ff.addSeed(tmp.w - 1, yy);
+ }
+
+ for (int xx = 0; xx < tmp.w; xx++) {
+ ff.addSeed(xx, 0);
+ ff.addSeed(xx, tmp.h - 1);
+ }
+ ff.fillMask();
+
+ for (int yy = 0; yy < tmp.h; yy++) {
+ const byte *src = (const byte *)tmp.getBasePtr(0, yy);
+ const byte *mask = (const byte *)ff.getMask()->getBasePtr(0, yy);
+ byte *dst = (byte *)target.getBasePtr(drawRect.left, drawRect.top + yy);
+
+ for (int xx = 0; xx < drawRect.width(); xx++, src++, dst++, mask++)
+ if (*mask == 0)
+ *dst = *src;
+ }
+
+ tmp.free();
+}
+
+uint16 Frame::getSpriteIDFromPos(Common::Point pos) {
+ //Find first from top to bottom
+ for (uint16 i = _drawRects.size() - 1; i > 0; i--) {
+ if (_drawRects[i].contains(pos))
+ return i;
+ }
+
+ return 0;
+}
+
+} //End of namespace Director