diff --git a/engines/director/events.cpp b/engines/director/events.cpp
index 05909b5656f..6af7ef8ed40 100644
--- a/engines/director/events.cpp
+++ b/engines/director/events.cpp
@@ -62,7 +62,6 @@ void DirectorEngine::processEvents(bool bufferLingoEvents) {
warning("processEvents: request to access frame %d of %d", sc->getCurrentFrame(), sc->_frames.size() - 1);
return;
}
- Frame *currentFrame = sc->_frames[sc->getCurrentFrame()];
uint16 spriteId = 0;
Common::Point pos;
@@ -82,7 +81,7 @@ void DirectorEngine::processEvents(bool bufferLingoEvents) {
sc->_lastRollTime = sc->_lastEventTime;
if (_draggingSprite) {
- Sprite *draggedSprite = currentFrame->_sprites[_draggingSpriteId];
+ Sprite *draggedSprite = sc->_sprites[_draggingSpriteId];
if (draggedSprite->_moveable) {
pos = g_system->getEventManager()->getMousePos();
Common::Point delta = pos - _draggingSpritePos;
@@ -101,7 +100,7 @@ void DirectorEngine::processEvents(bool bufferLingoEvents) {
// D3 doesn't have both mouse up and down.
// But we still want to know if the mouse is down for press effects.
- spriteId = currentFrame->getSpriteIDFromPos(pos);
+ spriteId = sc->getSpriteIDFromPos(pos);
sc->_currentMouseDownSpriteId = spriteId;
sc->_mouseIsDown = true;
@@ -111,7 +110,7 @@ void DirectorEngine::processEvents(bool bufferLingoEvents) {
debugC(3, kDebugEvents, "event: Button Down @(%d, %d), sprite id: %d", pos.x, pos.y, spriteId);
_lingo->registerEvent(kEventMouseDown);
- if (currentFrame->_sprites[spriteId]->_moveable)
+ if (sc->_sprites[spriteId]->_moveable)
g_director->setDraggedSprite(spriteId);
break;
@@ -119,7 +118,7 @@ void DirectorEngine::processEvents(bool bufferLingoEvents) {
case Common::EVENT_LBUTTONUP:
pos = g_system->getEventManager()->getMousePos();
- spriteId = currentFrame->getSpriteIDFromPos(pos);
+ spriteId = sc->getSpriteIDFromPos(pos);
debugC(3, kDebugEvents, "event: Button Up @(%d, %d), sprite id: %d", pos.x, pos.y, spriteId);
diff --git a/engines/director/frame.cpp b/engines/director/frame.cpp
index f506fd00e9c..782a39f64b3 100644
--- a/engines/director/frame.cpp
+++ b/engines/director/frame.cpp
@@ -23,13 +23,9 @@
#include "common/system.h"
#include "common/substream.h"
-#include "graphics/macgui/macfontmanager.h"
-#include "graphics/macgui/macwindowmanager.h"
-#include "graphics/macgui/maceditabletext.h"
#include "graphics/primitives.h"
#include "director/director.h"
-#include "director/cachedmactext.h"
#include "director/cast.h"
#include "director/frame.h"
#include "director/score.h"
@@ -525,662 +521,4 @@ void Frame::readSprite(Common::SeekableSubReadStreamEndian &stream, uint16 offse
}
-void Frame::prepareFrame(Score *score, bool updateStageOnly) {
- renderSprites(*score->_surface, false);
- renderSprites(*score->_trailSurface, true);
-
- if (!updateStageOnly) {
- score->renderZoomBox();
-
- _vm->_wm->draw();
-
- if (_transType != 0)
- // TODO Handle changing area case
- playTransition(score);
-
- if (_sound1 != 0 || _sound2 != 0) {
- playSoundChannel();
- }
-
- if (_vm->getCurrentScore()->haveZoomBox())
- score->_backSurface->copyFrom(*score->_surface);
- }
-
- g_system->copyRectToScreen(score->_surface->getPixels(), score->_surface->pitch, 0, 0, score->_surface->getBounds().width(), score->_surface->getBounds().height());
-}
-
-void Frame::playSoundChannel() {
- debug(0, "STUB: playSoundChannel(), Sound1 %d Sound2 %d", _sound1, _sound2);
-}
-
-void Frame::renderSprites(Graphics::ManagedSurface &surface, bool renderTrail) {
- for (uint16 i = 0; i <= _numChannels; i++) {
- if (!_sprites[i]->_enabled)
- continue;
-
- if ((_sprites[i]->_trails == 0 && renderTrail) || (_sprites[i]->_trails == 1 && !renderTrail))
- continue;
-
- CastType castType = _sprites[i]->_castType;
- if (castType == kCastTypeNull)
- continue;
-
- // this needs precedence to be hit first... D3 does something really tricky with cast IDs for shapes.
- // I don't like this implementation 100% as the 'cast' above might not actually hit a member and be null?
- debugC(1, kDebugImages, "Frame::renderSprites(): Channel: %d castType: %d", i, castType);
-
- _sprites[i]->_currentBbox = _sprites[i]->_dirtyBbox;
-
- if (castType == kCastShape) {
- renderShape(surface, i);
- } else if (castType == kCastText || castType == kCastRTE) {
- renderText(surface, i, NULL);
- } else if (castType == kCastButton) {
- renderButton(surface, i);
- } else {
- if (!_sprites[i]->_cast || _sprites[i]->_cast->_type != kCastBitmap) {
- warning("Frame::renderSprites(): No cast ID for sprite %d", i);
- continue;
- }
- if (_sprites[i]->_cast->_surface == nullptr) {
- warning("Frame::renderSprites(): No cast surface for sprite %d", i);
- continue;
- }
-
- renderBitmap(surface, i);
- }
- }
-}
-
-void Frame::renderShape(Graphics::ManagedSurface &surface, uint16 spriteId) {
- Sprite *sp = _sprites[spriteId];
-
- InkType ink = sp->_ink;
- byte spriteType = sp->_spriteType;
- byte foreColor = sp->_foreColor;
- byte backColor = sp->_backColor;
- int lineSize = sp->_thickness & 0x3;
-
- if (_vm->getVersion() >= 3 && spriteType == kCastMemberSprite) {
- if (!sp->_cast) {
- warning("Frame::renderShape(): kCastMemberSprite has no cast defined");
- return;
- }
- switch (sp->_cast->_type) {
- case kCastShape:
- {
- ShapeCast *sc = (ShapeCast *)sp->_cast;
- switch (sc->_shapeType) {
- case kShapeRectangle:
- spriteType = sc->_fillType ? kRectangleSprite : kOutlinedRectangleSprite;
- break;
- case kShapeRoundRect:
- spriteType = sc->_fillType ? kRoundedRectangleSprite : kOutlinedRoundedRectangleSprite;
- break;
- case kShapeOval:
- spriteType = sc->_fillType ? kOvalSprite : kOutlinedOvalSprite;
- break;
- case kShapeLine:
- spriteType = sc->_lineDirection == 6 ? kLineBottomTopSprite : kLineTopBottomSprite;
- break;
- default:
- break;
- }
- if (_vm->getVersion() > 3) {
- foreColor = sc->_fgCol;
- backColor = sc->_bgCol;
- lineSize = sc->_lineThickness;
- ink = sc->_ink;
- }
- // shapes should be rendered with transparency by default
- if (ink == kInkTypeCopy) {
- ink = kInkTypeTransparent;
- }
- }
- break;
- default:
- warning("Frame::renderShape(): Unhandled cast type: %d", sp->_cast->_type);
- break;
- }
- }
-
- // for outlined shapes, line thickness of 1 means invisible.
- lineSize -= 1;
-
- Common::Rect shapeRect = sp->_currentBbox;
-
- Graphics::ManagedSurface tmpSurface, maskSurface;
- tmpSurface.create(shapeRect.width(), shapeRect.height(), Graphics::PixelFormat::createFormatCLUT8());
- tmpSurface.clear(backColor);
-
- maskSurface.create(shapeRect.width(), shapeRect.height(), Graphics::PixelFormat::createFormatCLUT8());
- maskSurface.clear(0);
-
- // Draw fill
- Common::Rect fillRect((int)shapeRect.width(), (int)shapeRect.height());
- Graphics::MacPlotData plotFill(&tmpSurface, &maskSurface, &_vm->getPatterns(), sp->getPattern(), -shapeRect.left, -shapeRect.top, 1, backColor);
- switch (spriteType) {
- case kRectangleSprite:
- Graphics::drawFilledRect(fillRect, foreColor, Graphics::macDrawPixel, &plotFill);
- break;
- case kRoundedRectangleSprite:
- Graphics::drawRoundRect(fillRect, 12, foreColor, true, Graphics::macDrawPixel, &plotFill);
- break;
- case kOvalSprite:
- Graphics::drawEllipse(fillRect.left, fillRect.top, fillRect.right, fillRect.bottom, foreColor, true, Graphics::macDrawPixel, &plotFill);
- break;
- case kCastMemberSprite: // Face kit D3
- Graphics::drawFilledRect(fillRect, foreColor, Graphics::macDrawPixel, &plotFill);
- break;
- default:
- break;
- }
-
- // Draw stroke
- Common::Rect strokeRect(MAX((int)shapeRect.width() - lineSize, 0), MAX((int)shapeRect.height() - lineSize, 0));
- Graphics::MacPlotData plotStroke(&tmpSurface, &maskSurface, &_vm->getPatterns(), 1, -shapeRect.left, -shapeRect.top, lineSize, backColor);
- switch (spriteType) {
- case kLineTopBottomSprite:
- Graphics::drawLine(strokeRect.left, strokeRect.top, strokeRect.right, strokeRect.bottom, foreColor, Graphics::macDrawPixel, &plotStroke);
- break;
- case kLineBottomTopSprite:
- Graphics::drawLine(strokeRect.left, strokeRect.bottom, strokeRect.right, strokeRect.top, foreColor, Graphics::macDrawPixel, &plotStroke);
- break;
- case kRectangleSprite:
- // fall through
- case kOutlinedRectangleSprite: // this is actually a mouse-over shape? I don't think it's a real button.
- Graphics::drawRect(strokeRect, foreColor, Graphics::macDrawPixel, &plotStroke);
- //tmpSurface.fillRect(Common::Rect(shapeRect.width(), shapeRect.height()), (_vm->getCurrentScore()->_currentMouseDownSpriteId == spriteId ? 0 : 0xff));
- break;
- case kRoundedRectangleSprite:
- // fall through
- case kOutlinedRoundedRectangleSprite:
- Graphics::drawRoundRect(strokeRect, 12, foreColor, false, Graphics::macDrawPixel, &plotStroke);
- break;
- case kOvalSprite:
- // fall through
- case kOutlinedOvalSprite:
- Graphics::drawEllipse(strokeRect.left, strokeRect.top, strokeRect.right, strokeRect.bottom, foreColor, false, Graphics::macDrawPixel, &plotStroke);
- break;
- default:
- break;
- }
-
- inkBasedBlit(surface, &maskSurface, tmpSurface, ink, shapeRect, spriteId);
-}
-
-void Frame::renderButton(Graphics::ManagedSurface &surface, uint16 spriteId) {
- uint16 castId = _sprites[spriteId]->_castId;
-
- // This may not be a button cast. It could be a textcast with the channel forcing it
- // to be a checkbox or radio button!
- Cast *member = _vm->getCastMember(castId);
- if (!member) {
- warning("renderButton: unknown cast id %d", castId);
- } else if (member->_type != kCastButton) {
- warning("renderButton: cast id %d not of type kCastButton", castId);
- return;
- }
- ButtonCast *button = (ButtonCast *)member;
-
- // Sometimes, at least in the D3 Workshop Examples, these buttons are just TextCast.
- // If they are, then we just want to use the spriteType as the button type.
- // If they are full-bown Cast members, then use the actual cast member type.
- int buttonType = _sprites[spriteId]->_spriteType;
- if (buttonType == kCastMemberSprite) {
- switch (button->_buttonType) {
- case kTypeCheckBox:
- buttonType = kCheckboxSprite;
- break;
- case kTypeButton:
- buttonType = kButtonSprite;
- break;
- case kTypeRadio:
- buttonType = kRadioButtonSprite;
- break;
- }
- }
-
- bool invert = spriteId == _vm->getCurrentScore()->_currentMouseDownSpriteId;
-
- // TODO: review all cases to confirm if we should use text height.
- // height = textRect.height();
-
- Common::Rect _rect = _sprites[spriteId]->_currentBbox;
- int16 x = _rect.left;
- int16 y = _rect.top;
-
- Common::Rect textRect(0, 0, _rect.width(), _rect.height());
-
- // WORKAROUND, HACK
- // Because we're not drawing text with transparency
- // We swap drawing depending on whether the button is
- // inverted or not, to prevent destroying the border
- if (!invert)
- renderText(surface, spriteId, &textRect);
-
- Graphics::MacPlotData plotStroke(&surface, nullptr, &_vm->getPatterns(), 1, 0, 0, 1, 0);
-
- switch (buttonType) {
- case kCheckboxSprite:
- surface.frameRect(_rect, 0);
- break;
- case kButtonSprite: {
- Graphics::MacPlotData pd(&surface, nullptr, &_vm->getMacWindowManager()->getPatterns(), Graphics::MacGUIConstants::kPatternSolid, 0, 0, 1, invert ? Graphics::kColorBlack : Graphics::kColorWhite);
-
- Graphics::drawRoundRect(_rect, 4, 0, invert, Graphics::macDrawPixel, &pd);
- }
- break;
- case kRadioButtonSprite:
- Graphics::drawEllipse(x, y + 2, x + 11, y + 13, 0, false, Graphics::macDrawPixel, &plotStroke);
- break;
- default:
- warning("renderButton: Unknown buttonType");
- break;
- }
-
- if (invert)
- renderText(surface, spriteId, &textRect);
-}
-
-void Frame::renderText(Graphics::ManagedSurface &surface, uint16 spriteId, Common::Rect *textRect) {
- TextCast *textCast = (TextCast*)_sprites[spriteId]->_cast;
- if (textCast == nullptr) {
- warning("Frame::renderText(): TextCast #%d is a nullptr", spriteId);
- return;
- }
-
- Score *score = _vm->getCurrentScore();
- Sprite *sprite = _sprites[spriteId];
-
- Common::Rect bbox = sprite->_currentBbox;
- int width = bbox.width();
- int height = bbox.height();
- int x = bbox.left;
- int y = bbox.top;
-
- if (_vm->getCurrentScore()->_fontMap.contains(textCast->_fontId)) {
- // We need to make sure that the Shared Cast fonts have been loaded in?
- // might need a mapping table here of our own.
- // textCast->fontId = _vm->_wm->_fontMan->getFontIdByName(_vm->getCurrentScore()->_fontMap[textCast->fontId]);
- }
-
- if (width == 0 || height == 0) {
- warning("Frame::renderText(): Requested to draw on an empty surface: %d x %d", width, height);
- return;
- }
-
- if (sprite->_editable) {
- if (!textCast->_widget) {
- warning("Creating MacEditableText with '%s'", toPrintable(textCast->_ftext).c_str());
- textCast->_widget = new Graphics::MacEditableText(score->_window, x, y, width, height, g_director->_wm, textCast->_ftext, new Graphics::MacFont(), 0, 255, width);
- warning("Finished creating MacEditableText");
- }
-
- textCast->_widget->draw();
-
- InkType ink = sprite->_ink;
-
- if (spriteId == score->_currentMouseDownSpriteId)
- ink = kInkTypeReverse;
-
- inkBasedBlit(surface, nullptr, textCast->_widget->getSurface()->rawSurface(), ink, Common::Rect(x, y, x + width, y + height), spriteId);
-
- return;
- }
-
- debugC(3, kDebugText, "renderText: sprite: %d x: %d y: %d w: %d h: %d fontId: '%d' text: '%s'", spriteId, x, y, width, height, textCast->_fontId, Common::toPrintable(textCast->_ftext).c_str());
-
- uint16 boxShadow = (uint16)textCast->_boxShadow;
- uint16 borderSize = (uint16)textCast->_borderSize;
- if (textRect != NULL)
- borderSize = 0;
- uint16 padding = (uint16)textCast->_gutterSize;
- uint16 textShadow = (uint16)textCast->_textShadow;
-
- //uint32 rectLeft = textCast->initialRect.left;
- //uint32 rectTop = textCast->initialRect.top;
-
- textCast->_cachedMacText->clip(width);
- const Graphics::ManagedSurface *textSurface = textCast->_cachedMacText->getSurface();
-
- if (!textSurface)
- return;
-
- height = textSurface->h;
- if (textRect != NULL) {
- // TODO: this offset could be due to incorrect fonts loaded!
- textRect->bottom = height + textCast->_cachedMacText->getLineCount();
- }
-
- uint16 textX = 0, textY = 0;
-
- if (textRect == NULL) {
- if (borderSize > 0) {
- if (_vm->getVersion() <= 3) {
- height += (borderSize * 2);
- textX += (borderSize + 2);
- } else {
- height += borderSize;
- textX += (borderSize + 1);
- }
- textY += borderSize;
- } else {
- x += 1;
- }
-
- if (padding > 0) {
- width += padding * 2;
- height += padding;
- textY += padding / 2;
- }
-
- if (textCast->_textAlign == kTextAlignRight)
- textX -= 1;
-
- if (textShadow > 0)
- textX--;
- } else {
- x++;
- if (width % 2 != 0)
- x++;
-
- if (sprite->_spriteType != kCastMemberSprite) {
- y += 2;
- switch (sprite->_spriteType) {
- case kCheckboxSprite:
- textX += 16;
- break;
- case kRadioButtonSprite:
- textX += 17;
- break;
- default:
- break;
- }
- } else {
- ButtonType buttonType = ((ButtonCast*)textCast)->_buttonType;
- switch (buttonType) {
- case kTypeCheckBox:
- width += 4;
- textX += 16;
- break;
- case kTypeRadio:
- width += 4;
- textX += 17;
- break;
- case kTypeButton:
- width += 4;
- y += 2;
- break;
- default:
- warning("Frame::renderText(): Expected button but got unexpected button type: %d", buttonType);
- y += 2;
- break;
- }
- }
- }
-
- switch (textCast->_textAlign) {
- case kTextAlignLeft:
- default:
- break;
- case kTextAlignCenter:
- textX = (width / 2) - (textSurface->w / 2) + (padding / 2) + borderSize;
- break;
- case kTextAlignRight:
- textX = width - (textSurface->w + 1) + (borderSize * 2) - (textShadow * 2) - (padding);
- break;
- }
-
- Graphics::ManagedSurface textWithFeatures(width + (borderSize * 2) + boxShadow + textShadow, height + borderSize + boxShadow + textShadow);
- textWithFeatures.fillRect(Common::Rect(textWithFeatures.w, textWithFeatures.h), score->getStageColor());
-
- if (textRect == NULL && boxShadow > 0) {
- textWithFeatures.fillRect(Common::Rect(boxShadow, boxShadow, textWithFeatures.w + boxShadow, textWithFeatures.h), 0);
- }
-
- if (textRect == NULL && borderSize != kSizeNone) {
- for (int bb = 0; bb < borderSize; bb++) {
- Common::Rect borderRect(bb, bb, textWithFeatures.w - bb - boxShadow - textShadow, textWithFeatures.h - bb - boxShadow - textShadow);
- textWithFeatures.fillRect(borderRect, 0xff);
- textWithFeatures.frameRect(borderRect, 0);
- }
- }
-
- if (textShadow > 0)
- textWithFeatures.transBlitFrom(textSurface->rawSurface(), Common::Point(textX + textShadow, textY + textShadow), 0xff);
-
- textWithFeatures.transBlitFrom(textSurface->rawSurface(), Common::Point(textX, textY), 0xff);
-
- InkType ink = sprite->_ink;
-
- if (spriteId == score->_currentMouseDownSpriteId)
- ink = kInkTypeReverse;
-
- inkBasedBlit(surface, nullptr, textWithFeatures, ink, Common::Rect(x, y, x + width, y + height), spriteId);
-}
-
-void Frame::renderBitmap(Graphics::ManagedSurface &surface, uint16 spriteId) {
- InkType ink;
- Sprite *sprite = _sprites[spriteId];
-
- if (spriteId == _vm->getCurrentScore()->_currentMouseDownSpriteId)
- ink = kInkTypeReverse;
- else
- ink = sprite->_ink;
-
- BitmapCast *bc = (BitmapCast *)sprite->_cast;
- Common::Rect drawRect = sprite->_currentBbox;
-
- inkBasedBlit(surface, nullptr, *(bc->_surface), ink, drawRect, spriteId);
-}
-
-void Frame::inkBasedBlit(Graphics::ManagedSurface &targetSurface, const Graphics::ManagedSurface *maskSurface, const Graphics::Surface &spriteSurface, InkType ink, Common::Rect drawRect, uint spriteId) {
- // drawRect could be bigger than the spriteSurface. Clip it
- Common::Rect t(spriteSurface.w, spriteSurface.h);
- t.moveTo(drawRect.left, drawRect.top);
- drawRect.clip(t);
-
- switch (ink) {
- case kInkTypeCopy:
- if (maskSurface)
- targetSurface.transBlitFrom(spriteSurface, Common::Point(drawRect.left, drawRect.top), *maskSurface);
- else
- targetSurface.blitFrom(spriteSurface, Common::Point(drawRect.left, drawRect.top));
- break;
- case kInkTypeTransparent:
- // FIXME: is it always white (last entry in pallette)?
- targetSurface.transBlitFrom(spriteSurface, Common::Point(drawRect.left, drawRect.top), _vm->getPaletteColorCount() - 1);
- break;
- case kInkTypeBackgndTrans:
- drawBackgndTransSprite(targetSurface, spriteSurface, drawRect, spriteId);
- break;
- case kInkTypeMatte:
- drawMatteSprite(targetSurface, spriteSurface, drawRect);
- break;
- case kInkTypeGhost:
- drawGhostSprite(targetSurface, spriteSurface, drawRect);
- break;
- case kInkTypeReverse:
- drawReverseSprite(targetSurface, spriteSurface, drawRect, spriteId);
- break;
- default:
- warning("Frame::inkBasedBlit(): Unhandled ink type %d", ink);
- targetSurface.blitFrom(spriteSurface, Common::Point(drawRect.left, drawRect.top));
- break;
- }
-}
-
-void Frame::drawBackgndTransSprite(Graphics::ManagedSurface &target, const Graphics::Surface &sprite, Common::Rect &drawRect, int spriteId) {
- byte skipColor = _sprites[spriteId]->_backColor;
- Common::Rect srcRect(sprite.w, sprite.h);
-
- if (!target.clip(srcRect, drawRect))
- return; // Out of screen
-
- for (int ii = 0; ii < srcRect.height(); ii++) {
- const byte *src = (const byte *)sprite.getBasePtr(srcRect.left, srcRect.top + ii);
- byte *dst = (byte *)target.getBasePtr(drawRect.left, drawRect.top + ii);
-
- for (int j = 0; j < srcRect.width(); j++) {
- if (*src != skipColor)
- *dst = *src;
-
- src++;
- dst++;
- }
- }
-}
-
-void Frame::drawGhostSprite(Graphics::ManagedSurface &target, const Graphics::Surface &sprite, Common::Rect &drawRect) {
- Common::Rect srcRect(sprite.w, sprite.h);
-
- if (!target.clip(srcRect, drawRect))
- return; // Out of screen
-
- uint8 skipColor = _vm->getPaletteColorCount() - 1;
- for (int ii = 0; ii < srcRect.height(); ii++) {
- const byte *src = (const byte *)sprite.getBasePtr(srcRect.left, srcRect.top + ii);
- byte *dst = (byte *)target.getBasePtr(drawRect.left, drawRect.top + ii);
-
- for (int j = 0; j < srcRect.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, uint16 spriteId) {
- Common::Rect srcRect(sprite.w, sprite.h);
-
- if (!target.clip(srcRect, drawRect))
- return; // Out of screen
-
- uint8 skipColor = _vm->getPaletteColorCount() - 1;
- for (int ii = 0; ii < srcRect.height(); ii++) {
- const byte *src = (const byte *)sprite.getBasePtr(srcRect.left, srcRect.top + ii);
- byte *dst = (byte *)target.getBasePtr(drawRect.left, drawRect.top + ii);
- byte srcColor = *src;
-
- for (int j = 0; j < srcRect.width(); j++) {
- if (_sprites[spriteId]->_cast->_type == kCastShape)
- srcColor = 0x0;
- else
- srcColor = *src;
- uint16 targetSprite = getSpriteIDFromPos(Common::Point(drawRect.left + j, drawRect.top + ii));
- if ((targetSprite != 0)) {
- // TODO: This entire reverse colour attempt needs a lot more testing on
- // a lot more colour depths.
- if (srcColor != skipColor) {
- if (_sprites[targetSprite]->_cast->_type != kCastBitmap) {
- if (*dst == 0 || *dst == 255) {
- *dst = _vm->transformColor(*dst);
- } else if (srcColor == 255 || srcColor == 0) {
- *dst = _vm->transformColor(*dst - 40);
- } else {
- *dst = _vm->transformColor(*src - 40);
- }
- } else {
- if (*dst == 0 && _vm->getVersion() == 3 &&
- ((BitmapCast*)_sprites[spriteId]->_cast)->_bitsPerPixel > 1) {
- *dst = _vm->transformColor(*src - 40);
- } else {
- *dst ^= _vm->transformColor(srcColor);
- }
- }
- }
- } else if (srcColor != skipColor) {
- *dst = _vm->transformColor(srcColor);
- }
- 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);
- Common::Rect srcRect(sprite.w, sprite.h);
-
- if (!target.clip(srcRect, drawRect))
- return; // Out of screen
-
- // Searching white color in the corners
- int whiteColor = -1;
-
- for (int y = 0; y < tmp.h; y++) {
- for (int x = 0; x < tmp.w; x++) {
- 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) {
- debugC(1, kDebugImages, "Frame::drawMatteSprite(): No white color for Matte image");
-
- for (int yy = 0; yy < srcRect.height(); yy++) {
- const byte *src = (const byte *)tmp.getBasePtr(srcRect.left, srcRect.top + yy);
- byte *dst = (byte *)target.getBasePtr(drawRect.left, drawRect.top + yy);
-
- for (int xx = 0; xx < drawRect.width(); xx++, src++, dst++)
- *dst = *src;
- }
- } else {
- 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 < srcRect.height(); yy++) {
- const byte *src = (const byte *)tmp.getBasePtr(srcRect.left, srcRect.top + yy);
- const byte *mask = (const byte *)ff.getMask()->getBasePtr(srcRect.left, srcRect.top + yy);
- byte *dst = (byte *)target.getBasePtr(drawRect.left, drawRect.top + yy);
-
- for (int xx = 0; xx < srcRect.width(); xx++, src++, dst++, mask++)
- if (*mask == 0)
- *dst = *src;
- }
- }
-
- tmp.free();
-}
-
-uint16 Frame::getSpriteIDFromPos(Common::Point pos) {
- for (int i = _sprites.size() - 1; i >= 0; i--)
- if (_sprites[i]->_currentBbox.contains(pos))
- return i;
-
- return 0;
-}
-
-bool Frame::checkSpriteIntersection(uint16 spriteId, Common::Point pos) {
- if (_sprites[spriteId]->_currentBbox.contains(pos))
- return true;
-
- return false;
-}
-
-Common::Rect *Frame::getSpriteRect(uint16 spriteId) {
- return &_sprites[spriteId]->_currentBbox;
-}
-
} // End of namespace Director
diff --git a/engines/director/frame.h b/engines/director/frame.h
index 5ab89bd330d..684488841d3 100644
--- a/engines/director/frame.h
+++ b/engines/director/frame.h
@@ -72,31 +72,19 @@ public:
~Frame();
void readChannels(Common::ReadStreamEndian *stream);
void readChannel(Common::SeekableSubReadStreamEndian &stream, uint16 offset, uint16 size);
- void prepareFrame(Score *score, bool updateStageOnly = false);
- uint16 getSpriteIDFromPos(Common::Point pos);
- bool checkSpriteIntersection(uint16 spriteId, Common::Point pos);
- Common::Rect *getSpriteRect(uint16 spriteId);
void executeImmediateScripts();
-private:
void playTransition(Score *score);
- void playSoundChannel();
- void renderSprites(Graphics::ManagedSurface &surface, bool renderTrail);
- void renderText(Graphics::ManagedSurface &surface, uint16 spriteId, Common::Rect *textSize);
- void renderShape(Graphics::ManagedSurface &surface, uint16 spriteId);
- void renderButton(Graphics::ManagedSurface &surface, uint16 spriteId);
- void renderBitmap(Graphics::ManagedSurface &surface, uint16 spriteId);
+
+private:
+
void readPaletteInfo(Common::SeekableSubReadStreamEndian &stream);
void readSprite(Common::SeekableSubReadStreamEndian &stream, uint16 offset, uint16 size);
void readMainChannels(Common::SeekableSubReadStreamEndian &stream, uint16 offset, uint16 size);
Image::ImageDecoder *getImageFrom(uint16 spriteId);
Common::String readTextStream(Common::SeekableSubReadStreamEndian *textStream, TextCast *textCast);
- void drawBackgndTransSprite(Graphics::ManagedSurface &target, const Graphics::Surface &sprite, Common::Rect &drawRect, int spriteId);
- void drawMatteSprite(Graphics::ManagedSurface &target, const Graphics::Surface &sprite, Common::Rect &drawRect);
- void drawGhostSprite(Graphics::ManagedSurface &target, const Graphics::Surface &sprite, Common::Rect &drawRect);
- void drawReverseSprite(Graphics::ManagedSurface &target, const Graphics::Surface &sprite, Common::Rect &drawRect, uint16 spriteId);
- void inkBasedBlit(Graphics::ManagedSurface &targetSurface, const Graphics::ManagedSurface *maskSurface, const Graphics::Surface &spriteSurface, InkType ink, Common::Rect drawRect, uint spriteId);
+
public:
int _numChannels;
diff --git a/engines/director/lingo/lingo-builtins.cpp b/engines/director/lingo/lingo-builtins.cpp
index f0ef57eaa0e..17877d00fb6 100644
--- a/engines/director/lingo/lingo-builtins.cpp
+++ b/engines/director/lingo/lingo-builtins.cpp
@@ -1715,21 +1715,21 @@ void LB::b_rollOver(int nargs) {
Datum res(0);
int arg = d.asInt();
- if (!g_director->getCurrentScore()) {
+ Score *score = g_director->getCurrentScore();
+
+ if (!score) {
warning("b_rollOver: Reference to an empty score");
return;
}
- Frame *frame = g_director->getCurrentScore()->_frames[g_director->getCurrentScore()->getCurrentFrame()];
-
- if (arg >= (int32) frame->_sprites.size()) {
+ if (arg >= (int32) score->_sprites.size()) {
g_lingo->push(res);
return;
}
Common::Point pos = g_system->getEventManager()->getMousePos();
- if (frame->checkSpriteIntersection(arg, pos))
+ if (score->checkSpriteIntersection(arg, pos))
res.u.i = 1; // TRUE
g_lingo->push(res);
@@ -1775,9 +1775,8 @@ void LB::b_zoomBox(int nargs) {
Score *score = g_director->getCurrentScore();
uint16 curFrame = score->getCurrentFrame();
- Frame *frame = score->_frames[curFrame];
- Common::Rect *startRect = frame->getSpriteRect(startSprite);
+ Common::Rect *startRect = score->getSpriteRect(startSprite);
if (!startRect) {
warning("b_zoomBox: unknown start sprite #%d", startSprite);
return;
@@ -1785,10 +1784,10 @@ void LB::b_zoomBox(int nargs) {
// Looks for endSprite in the current frame, otherwise
// Looks for endSprite in the next frame
- Common::Rect *endRect = frame->getSpriteRect(endSprite);
+ Common::Rect *endRect = score->getSpriteRect(endSprite);
if (!endRect) {
if ((uint)curFrame + 1 < score->_frames.size())
- score->_frames[curFrame + 1]->getSpriteRect(endSprite);
+ endRect = &score->_frames[curFrame + 1]->_sprites[endSprite]->_currentBbox;
}
if (!endRect) {
@@ -1818,11 +1817,7 @@ void LB::b_updateStage(int nargs) {
return;
}
- uint16 curFrame = score->getCurrentFrame();
- Frame *frame = score->_frames[curFrame];
-
- frame->prepareFrame(score, true);
- g_director->processEvents(true);
+ score->renderFrame( score->getCurrentFrame(), false, true);
}
diff --git a/engines/director/lingo/lingo-the.cpp b/engines/director/lingo/lingo-the.cpp
index 5459c5f44d4..0383d7057d2 100644
--- a/engines/director/lingo/lingo-the.cpp
+++ b/engines/director/lingo/lingo-the.cpp
@@ -427,10 +427,9 @@ Datum Lingo::getTheEntity(int entity, Datum &id, int field) {
{
Common::Point pos = g_system->getEventManager()->getMousePos();
Score *sc = _vm->getCurrentScore();
- Frame *currentFrame = sc->_frames[sc->getCurrentFrame()];
- uint16 spriteId = currentFrame->getSpriteIDFromPos(pos);
+ uint16 spriteId = sc->getSpriteIDFromPos(pos);
d.type = INT;
- d.u.i = currentFrame->_sprites[spriteId]->_castId;
+ d.u.i = sc->_sprites[spriteId]->_castId;
if (d.u.i == 0)
d.u.i = -1;
}
@@ -735,6 +734,10 @@ void Lingo::setTheSprite(Datum &id1, int field, Datum &d) {
break;
case kThePuppet:
sprite->_puppet = d.asInt();
+ if (!d.u.i) {
+ sprite->_currentPoint = sprite->_startPoint;
+ sprite->_dirtyBbox = sprite->_startBbox;
+ }
break;
case kTheStartTime:
sprite->_startTime = d.asInt();
diff --git a/engines/director/score.cpp b/engines/director/score.cpp
index b04454bc209..70258152df7 100644
--- a/engines/director/score.cpp
+++ b/engines/director/score.cpp
@@ -31,6 +31,8 @@
#include "graphics/primitives.h"
#include "graphics/macgui/macfontmanager.h"
#include "graphics/macgui/macwindowmanager.h"
+#include "graphics/macgui/maceditabletext.h"
+#include "director/cachedmactext.h"
#include "image/bmp.h"
#include "director/director.h"
@@ -69,7 +71,7 @@ const char *scriptType2str(ScriptType scr) {
Score::Score(DirectorEngine *vm) {
_vm = vm;
_surface = nullptr;
- _trailSurface = nullptr;
+ _maskSurface = nullptr;
_backSurface = nullptr;
_backSurface2 = nullptr;
_lingo = _vm->getLingo();
@@ -454,8 +456,8 @@ void Score::loadSpriteSounds(bool isSharedCast) {
Score::~Score() {
- if (_trailSurface && _trailSurface->w)
- _trailSurface->free();
+ if (_maskSurface && _maskSurface->w)
+ _maskSurface->free();
if (_backSurface && _backSurface->w)
_backSurface->free();
@@ -465,7 +467,7 @@ Score::~Score() {
delete _backSurface;
delete _backSurface2;
- delete _trailSurface;
+ delete _maskSurface;
if (_window)
_vm->_wm->removeWindow(_window);
@@ -1706,14 +1708,16 @@ void Score::startLoop() {
_window->resize(_movieRect.width(), _movieRect.height());
_surface = _window->getWindowSurface();
- _trailSurface = new Graphics::ManagedSurface;
+ _maskSurface = new Graphics::ManagedSurface;
_backSurface = new Graphics::ManagedSurface;
_backSurface2 = new Graphics::ManagedSurface;
- _trailSurface->create(_movieRect.width(), _movieRect.height());
+ _maskSurface->create(_movieRect.width(), _movieRect.height());
_backSurface->create(_movieRect.width(), _movieRect.height());
_backSurface2->create(_movieRect.width(), _movieRect.height());
+ _sprites.resize(_frames[0]->_sprites.size());
+
if (_vm->_backSurface.w > 0) {
// Persist screen between the movies
// TODO: this is a workaround until the rendering pipeline is reworked
@@ -1728,7 +1732,7 @@ void Score::startLoop() {
_vm->_wm->setScreen(_surface);
- _trailSurface->clear(_stageColor);
+ _surface->clear(_stageColor);
_currentFrame = 0;
_stopPlay = false;
@@ -1736,7 +1740,8 @@ void Score::startLoop() {
_lingo->processEvent(kEventStartMovie);
- _frames[_currentFrame]->prepareFrame(this);
+ _sprites = _frames[_currentFrame]->_sprites;
+ renderFrame(_currentFrame, true);
while (!_stopPlay) {
if (_currentFrame >= _frames.size()) {
@@ -1818,8 +1823,8 @@ void Score::update() {
_vm->_newMovieStarted = false;
- _surface->clear(_stageColor);
- _surface->copyFrom(*_trailSurface);
+ // _surface->clear(_stageColor);
+ // _surface->copyFrom(*_trailSurface);
_lingo->executeImmediateScripts(_frames[_currentFrame]);
@@ -1830,7 +1835,7 @@ void Score::update() {
// TODO: Director 6 step: send prepareFrame event to all sprites and the script channel in upcoming frame
}
- _frames[_currentFrame]->prepareFrame(this);
+ renderFrame(_currentFrame);
// Stage is drawn between the prepareFrame and enterFrame events (Lingo in a Nutshell, p.100)
// Enter and exit from previous frame (Director 4)
@@ -1880,19 +1885,724 @@ void Score::update() {
_framesRan++;
}
+void Score::renderFrame(uint16 frameId, bool forceUpdate, bool updateStageOnly) {
+ _maskSurface->clear(0);
+
+ Frame *currentFrame = _frames[frameId];
+
+ for (uint16 i = 0; i < _sprites.size(); i++) {
+ Sprite *currentSprite = _sprites[i];
+ Sprite *nextSprite;
+
+ if (currentSprite->_puppet)
+ nextSprite = currentSprite;
+ else
+ nextSprite = currentFrame->_sprites[i];
+
+ bool needsUpdate = (currentSprite->_currentBbox != nextSprite->_currentBbox || currentSprite->_currentBbox != currentSprite->_dirtyBbox);
+
+ if (needsUpdate || forceUpdate)
+ unrenderSprite(i);
+
+ _sprites[i] = nextSprite;
+ }
+
+ for (uint i = 0; i < _sprites.size(); i++)
+ renderSprite(i);
+
+ if (!updateStageOnly) {
+ renderZoomBox();
+
+ _vm->_wm->draw();
+
+ if (currentFrame->_transType != 0)
+ // TODO Handle changing area case
+ currentFrame->playTransition(this);
+
+ if (currentFrame->_sound1 != 0 || currentFrame->_sound2 != 0) {
+ playSoundChannel(frameId);
+ }
+
+ if (_vm->getCurrentScore()->haveZoomBox())
+ _backSurface->copyFrom(*_surface);
+ }
+
+ g_system->copyRectToScreen(_surface->getPixels(), _surface->pitch, 0, 0, _surface->getBounds().width(), _surface->getBounds().height());
+}
+
+void Score::unrenderSprite(uint16 spriteId) {
+ Sprite *currentSprite = _sprites[spriteId];
+
+ if (!currentSprite->_trails) {
+ _maskSurface->fillRect(currentSprite->_currentBbox, 1);
+ _surface->fillRect(currentSprite->_currentBbox, _stageColor);
+ }
+
+ currentSprite->_currentBbox = currentSprite->_dirtyBbox;
+}
+
+void Score::renderSprite(uint16 id) {
+ Sprite *sprite = _sprites[id];
+
+ if (!sprite)
+ return;
+
+ CastType castType = sprite->_castType;
+
+ _maskSurface->fillRect(sprite->_currentBbox, 1);
+
+ if (castType == kCastTypeNull)
+ return;
+
+ debugC(1, kDebugImages, "Score::renderFrame(): channel: %d, castType: %d", id, castType);
+ // this needs precedence to be hit first... D3 does something really tricky
+ // with cast IDs for shapes. I don't like this implementation 100% as the
+ // 'cast' above might not actually hit a member and be null?
+ if (castType == kCastShape) {
+ renderShape(id);
+ } else if (castType == kCastText || castType == kCastRTE) {
+ renderText(id, NULL);
+ } else if (castType == kCastButton) {
+ renderButton(id);
+ } else {
+ if (!sprite->_cast || sprite->_cast->_type != kCastBitmap) {
+ warning("Score::renderFrame(): No cast ID for sprite %d", id);
+ return;
+ }
+ if (sprite->_cast->_surface == nullptr) {
+ warning("Score::renderFrame(): No cast surface for sprite %d", id);
+ return;
+ }
+
+ renderBitmap(id);
+ }
+}
+
+void Score::renderShape(uint16 spriteId) {
+ Sprite *sp = _sprites[spriteId];
+
+ InkType ink = sp->_ink;
+ byte spriteType = sp->_spriteType;
+ byte foreColor = sp->_foreColor;
+ byte backColor = sp->_backColor;
+ int lineSize = sp->_thickness & 0x3;
+
+ if (_vm->getVersion() >= 3 && spriteType == kCastMemberSprite) {
+ if (!sp->_cast) {
+ warning("Frame::renderShape(): kCastMemberSprite has no cast defined");
+ return;
+ }
+ switch (sp->_cast->_type) {
+ case kCastShape:
+ {
+ ShapeCast *sc = (ShapeCast *)sp->_cast;
+ switch (sc->_shapeType) {
+ case kShapeRectangle:
+ spriteType = sc->_fillType ? kRectangleSprite : kOutlinedRectangleSprite;
+ break;
+ case kShapeRoundRect:
+ spriteType = sc->_fillType ? kRoundedRectangleSprite : kOutlinedRoundedRectangleSprite;
+ break;
+ case kShapeOval:
+ spriteType = sc->_fillType ? kOvalSprite : kOutlinedOvalSprite;
+ break;
+ case kShapeLine:
+ spriteType = sc->_lineDirection == 6 ? kLineBottomTopSprite : kLineTopBottomSprite;
+ break;
+ default:
+ break;
+ }
+ if (_vm->getVersion() > 3) {
+ foreColor = sc->_fgCol;
+ backColor = sc->_bgCol;
+ lineSize = sc->_lineThickness;
+ ink = sc->_ink;
+ }
+ // shapes should be rendered with transparency by default
+ if (ink == kInkTypeCopy) {
+ ink = kInkTypeTransparent;
+ }
+ }
+ break;
+ default:
+ warning("Frame::renderShape(): Unhandled cast type: %d", sp->_cast->_type);
+ break;
+ }
+ }
+
+ // for outlined shapes, line thickness of 1 means invisible.
+ lineSize -= 1;
+
+ Common::Rect shapeRect = sp->_currentBbox;
+
+ Graphics::ManagedSurface tmpSurface, maskSurface;
+ tmpSurface.create(shapeRect.width(), shapeRect.height(), Graphics::PixelFormat::createFormatCLUT8());
+ tmpSurface.clear(backColor);
+
+ maskSurface.create(shapeRect.width(), shapeRect.height(), Graphics::PixelFormat::createFormatCLUT8());
+ maskSurface.clear(0);
+
+ // Draw fill
+ Common::Rect fillRect((int)shapeRect.width(), (int)shapeRect.height());
+ Graphics::MacPlotData plotFill(&tmpSurface, &maskSurface, &_vm->getPatterns(), sp->getPattern(), -shapeRect.left, -shapeRect.top, 1, backColor);
+ switch (spriteType) {
+ case kRectangleSprite:
+ Graphics::drawFilledRect(fillRect, foreColor, Graphics::macDrawPixel, &plotFill);
+ break;
+ case kRoundedRectangleSprite:
+ Graphics::drawRoundRect(fillRect, 12, foreColor, true, Graphics::macDrawPixel, &plotFill);
+ break;
+ case kOvalSprite:
+ Graphics::drawEllipse(fillRect.left, fillRect.top, fillRect.right, fillRect.bottom, foreColor, true, Graphics::macDrawPixel, &plotFill);
+ break;
+ case kCastMemberSprite: // Face kit D3
+ Graphics::drawFilledRect(fillRect, foreColor, Graphics::macDrawPixel, &plotFill);
+ break;
+ default:
+ break;
+ }
+
+ // Draw stroke
+ Common::Rect strokeRect(MAX((int)shapeRect.width() - lineSize, 0), MAX((int)shapeRect.height() - lineSize, 0));
+ Graphics::MacPlotData plotStroke(&tmpSurface, &maskSurface, &_vm->getPatterns(), 1, -shapeRect.left, -shapeRect.top, lineSize, backColor);
+ switch (spriteType) {
+ case kLineTopBottomSprite:
+ Graphics::drawLine(strokeRect.left, strokeRect.top, strokeRect.right, strokeRect.bottom, foreColor, Graphics::macDrawPixel, &plotStroke);
+ break;
+ case kLineBottomTopSprite:
+ Graphics::drawLine(strokeRect.left, strokeRect.bottom, strokeRect.right, strokeRect.top, foreColor, Graphics::macDrawPixel, &plotStroke);
+ break;
+ case kRectangleSprite:
+ // fall through
+ case kOutlinedRectangleSprite: // this is actually a mouse-over shape? I don't think it's a real button.
+ Graphics::drawRect(strokeRect, foreColor, Graphics::macDrawPixel, &plotStroke);
+ //tmpSurface.fillRect(Common::Rect(shapeRect.width(), shapeRect.height()), (_vm->getCurrentScore()->_currentMouseDownSpriteId == spriteId ? 0 : 0xff));
+ break;
+ case kRoundedRectangleSprite:
+ // fall through
+ case kOutlinedRoundedRectangleSprite:
+ Graphics::drawRoundRect(strokeRect, 12, foreColor, false, Graphics::macDrawPixel, &plotStroke);
+ break;
+ case kOvalSprite:
+ // fall through
+ case kOutlinedOvalSprite:
+ Graphics::drawEllipse(strokeRect.left, strokeRect.top, strokeRect.right, strokeRect.bottom, foreColor, false, Graphics::macDrawPixel, &plotStroke);
+ break;
+ default:
+ break;
+ }
+
+ inkBasedBlit(&maskSurface, tmpSurface, ink, shapeRect, spriteId);
+}
+
+
+void Score::renderButton(uint16 spriteId) {
+ uint16 castId = _sprites[spriteId]->_castId;
+
+ // This may not be a button cast. It could be a textcast with the channel forcing it
+ // to be a checkbox or radio button!
+ Cast *member = _vm->getCastMember(castId);
+ if (!member) {
+ warning("renderButton: unknown cast id %d", castId);
+ } else if (member->_type != kCastButton) {
+ warning("renderButton: cast id %d not of type kCastButton", castId);
+ return;
+ }
+ ButtonCast *button = (ButtonCast *)member;
+
+ // Sometimes, at least in the D3 Workshop Examples, these buttons are just TextCast.
+ // If they are, then we just want to use the spriteType as the button type.
+ // If they are full-bown Cast members, then use the actual cast member type.
+ int buttonType = _sprites[spriteId]->_spriteType;
+ if (buttonType == kCastMemberSprite) {
+ switch (button->_buttonType) {
+ case kTypeCheckBox:
+ buttonType = kCheckboxSprite;
+ break;
+ case kTypeButton:
+ buttonType = kButtonSprite;
+ break;
+ case kTypeRadio:
+ buttonType = kRadioButtonSprite;
+ break;
+ }
+ }
+
+ bool invert = spriteId == _vm->getCurrentScore()->_currentMouseDownSpriteId;
+
+ // TODO: review all cases to confirm if we should use text height.
+ // height = textRect.height();
+
+ Common::Rect _rect = _sprites[spriteId]->_currentBbox;
+ int16 x = _rect.left;
+ int16 y = _rect.top;
+
+ Common::Rect textRect(0, 0, _rect.width(), _rect.height());
+
+ // WORKAROUND, HACK
+ // Because we're not drawing text with transparency
+ // We swap drawing depending on whether the button is
+ // inverted or not, to prevent destroying the border
+ if (!invert)
+ renderText(spriteId, &textRect);
+
+ Graphics::MacPlotData plotStroke(_surface, nullptr, &_vm->getPatterns(), 1, 0, 0, 1, 0);
+
+ switch (buttonType) {
+ case kCheckboxSprite:
+ _surface->frameRect(_rect, 0);
+ break;
+ case kButtonSprite: {
+ Graphics::MacPlotData pd(_surface, nullptr, &_vm->getMacWindowManager()->getPatterns(), Graphics::MacGUIConstants::kPatternSolid, 0, 0, 1, invert ? Graphics::kColorBlack : Graphics::kColorWhite);
+
+ Graphics::drawRoundRect(_rect, 4, 0, invert, Graphics::macDrawPixel, &pd);
+ }
+ break;
+ case kRadioButtonSprite:
+ Graphics::drawEllipse(x, y + 2, x + 11, y + 13, 0, false, Graphics::macDrawPixel, &plotStroke);
+ break;
+ default:
+ warning("renderButton: Unknown buttonType");
+ break;
+ }
+
+ if (invert)
+ renderText(spriteId, &textRect);
+}
+
+void Score::renderText(uint16 spriteId, Common::Rect *textRect) {
+ TextCast *textCast = (TextCast*)_sprites[spriteId]->_cast;
+ if (textCast == nullptr) {
+ warning("Frame::renderText(): TextCast #%d is a nullptr", spriteId);
+ return;
+ }
+
+ Score *score = _vm->getCurrentScore();
+ Sprite *sprite = _sprites[spriteId];
+
+ Common::Rect bbox = sprite->_currentBbox;
+ int width = bbox.width();
+ int height = bbox.height();
+ int x = bbox.left;
+ int y = bbox.top;
+
+ if (_vm->getCurrentScore()->_fontMap.contains(textCast->_fontId)) {
+ // We need to make sure that the Shared Cast fonts have been loaded in?
+ // might need a mapping table here of our own.
+ // textCast->fontId = _vm->_wm->_fontMan->getFontIdByName(_vm->getCurrentScore()->_fontMap[textCast->fontId]);
+ }
+
+ if (width == 0 || height == 0) {
+ warning("Frame::renderText(): Requested to draw on an empty surface: %d x %d", width, height);
+ return;
+ }
+
+ if (sprite->_editable) {
+ if (!textCast->_widget) {
+ warning("Creating MacEditableText with '%s'", toPrintable(textCast->_ftext).c_str());
+ textCast->_widget = new Graphics::MacEditableText(score->_window, x, y, width, height, g_director->_wm, textCast->_ftext, new Graphics::MacFont(), 0, 255, width);
+ warning("Finished creating MacEditableText");
+ }
+
+ textCast->_widget->draw();
+
+ InkType ink = sprite->_ink;
+
+ if (spriteId == score->_currentMouseDownSpriteId)
+ ink = kInkTypeReverse;
+
+ inkBasedBlit(nullptr, textCast->_widget->getSurface()->rawSurface(), ink, Common::Rect(x, y, x + width, y + height), spriteId);
+
+ return;
+ }
+
+ debugC(3, kDebugText, "renderText: sprite: %d x: %d y: %d w: %d h: %d fontId: '%d' text: '%s'", spriteId, x, y, width, height, textCast->_fontId, Common::toPrintable(textCast->_ftext).c_str());
+
+ uint16 boxShadow = (uint16)textCast->_boxShadow;
+ uint16 borderSize = (uint16)textCast->_borderSize;
+ if (textRect != NULL)
+ borderSize = 0;
+ uint16 padding = (uint16)textCast->_gutterSize;
+ uint16 textShadow = (uint16)textCast->_textShadow;
+
+ //uint32 rectLeft = textCast->initialRect.left;
+ //uint32 rectTop = textCast->initialRect.top;
+
+ textCast->_cachedMacText->clip(width);
+ const Graphics::ManagedSurface *textSurface = textCast->_cachedMacText->getSurface();
+
+ if (!textSurface)
+ return;
+
+ height = textSurface->h;
+ if (textRect != NULL) {
+ // TODO: this offset could be due to incorrect fonts loaded!
+ textRect->bottom = height + textCast->_cachedMacText->getLineCount();
+ }
+
+ uint16 textX = 0, textY = 0;
+
+ if (textRect == NULL) {
+ if (borderSize > 0) {
+ if (_vm->getVersion() <= 3) {
+ height += (borderSize * 2);
+ textX += (borderSize + 2);
+ } else {
+ height += borderSize;
+ textX += (borderSize + 1);
+ }
+ textY += borderSize;
+ } else {
+ x += 1;
+ }
+
+ if (padding > 0) {
+ width += padding * 2;
+ height += padding;
+ textY += padding / 2;
+ }
+
+ if (textCast->_textAlign == kTextAlignRight)
+ textX -= 1;
+
+ if (textShadow > 0)
+ textX--;
+ } else {
+ x++;
+ if (width % 2 != 0)
+ x++;
+
+ if (sprite->_spriteType != kCastMemberSprite) {
+ y += 2;
+ switch (sprite->_spriteType) {
+ case kCheckboxSprite:
+ textX += 16;
+ break;
+ case kRadioButtonSprite:
+ textX += 17;
+ break;
+ default:
+ break;
+ }
+ } else {
+ ButtonType buttonType = ((ButtonCast*)textCast)->_buttonType;
+ switch (buttonType) {
+ case kTypeCheckBox:
+ width += 4;
+ textX += 16;
+ break;
+ case kTypeRadio:
+ width += 4;
+ textX += 17;
+ break;
+ case kTypeButton:
+ width += 4;
+ y += 2;
+ break;
+ default:
+ warning("Frame::renderText(): Expected button but got unexpected button type: %d", buttonType);
+ y += 2;
+ break;
+ }
+ }
+ }
+
+ switch (textCast->_textAlign) {
+ case kTextAlignLeft:
+ default:
+ break;
+ case kTextAlignCenter:
+ textX = (width / 2) - (textSurface->w / 2) + (padding / 2) + borderSize;
+ break;
+ case kTextAlignRight:
+ textX = width - (textSurface->w + 1) + (borderSize * 2) - (textShadow * 2) - (padding);
+ break;
+ }
+
+ Graphics::ManagedSurface textWithFeatures(width + (borderSize * 2) + boxShadow + textShadow, height + borderSize + boxShadow + textShadow);
+ textWithFeatures.fillRect(Common::Rect(textWithFeatures.w, textWithFeatures.h), score->getStageColor());
+
+ if (textRect == NULL && boxShadow > 0) {
+ textWithFeatures.fillRect(Common::Rect(boxShadow, boxShadow, textWithFeatures.w + boxShadow, textWithFeatures.h), 0);
+ }
+
+ if (textRect == NULL && borderSize != kSizeNone) {
+ for (int bb = 0; bb < borderSize; bb++) {
+ Common::Rect borderRect(bb, bb, textWithFeatures.w - bb - boxShadow - textShadow, textWithFeatures.h - bb - boxShadow - textShadow);
+ textWithFeatures.fillRect(borderRect, 0xff);
+ textWithFeatures.frameRect(borderRect, 0);
+ }
+ }
+
+ if (textShadow > 0)
+ textWithFeatures.transBlitFrom(textSurface->rawSurface(), Common::Point(textX + textShadow, textY + textShadow), 0xff);
+
+ textWithFeatures.transBlitFrom(textSurface->rawSurface(), Common::Point(textX, textY), 0xff);
+
+ InkType ink = sprite->_ink;
+
+ if (spriteId == score->_currentMouseDownSpriteId)
+ ink = kInkTypeReverse;
+
+ inkBasedBlit(nullptr, textWithFeatures, ink, Common::Rect(x, y, x + width, y + height), spriteId);
+}
+
+void Score::renderBitmap(uint16 spriteId) {
+ InkType ink;
+ Sprite *sprite = _sprites[spriteId];
+
+ if (spriteId == _vm->getCurrentScore()->_currentMouseDownSpriteId)
+ ink = kInkTypeReverse;
+ else
+ ink = sprite->_ink;
+
+ BitmapCast *bc = (BitmapCast *)sprite->_cast;
+ Common::Rect drawRect = sprite->_currentBbox;
+
+ inkBasedBlit(nullptr, *(bc->_surface), ink, drawRect, spriteId);
+}
+
+void Score::inkBasedBlit(Graphics::ManagedSurface *maskSurface, const Graphics::Surface &spriteSurface, InkType ink, Common::Rect drawRect, uint spriteId) {
+ // drawRect could be bigger than the spriteSurface. Clip it
+ Common::Rect t(spriteSurface.w, spriteSurface.h);
+ t.moveTo(drawRect.left, drawRect.top);
+ bool nullMask = false;
+
+ // combine the given mask with the maskSurface
+ if (!maskSurface) {
+ nullMask = true;
+ maskSurface = new Graphics::ManagedSurface;
+ maskSurface->create(spriteSurface.w, spriteSurface.h, Graphics::PixelFormat::createFormatCLUT8());
+ maskSurface->clear(0);
+ }
+
+ maskSurface->blitFrom(*_maskSurface, drawRect, Common::Point(0, 0));
+
+ drawRect.clip(t);
+
+ switch (ink) {
+ case kInkTypeCopy:
+ if (maskSurface)
+ _surface->transBlitFrom(spriteSurface, Common::Point(drawRect.left, drawRect.top), *maskSurface);
+ else
+ _surface->blitFrom(spriteSurface, Common::Point(drawRect.left, drawRect.top));
+ break;
+ case kInkTypeTransparent:
+ // FIXME: is it always white (last entry in pallette)?
+ _surface->transBlitFrom(spriteSurface, Common::Point(drawRect.left, drawRect.top), _vm->getPaletteColorCount() - 1);
+ break;
+ case kInkTypeBackgndTrans:
+ drawBackgndTransSprite(spriteSurface, drawRect, spriteId);
+ break;
+ case kInkTypeMatte:
+ drawMatteSprite(spriteSurface, drawRect);
+ break;
+ case kInkTypeGhost:
+ drawGhostSprite(spriteSurface, drawRect);
+ break;
+ case kInkTypeReverse:
+ drawReverseSprite(spriteSurface, drawRect, spriteId);
+ break;
+ default:
+ warning("Frame::inkBasedBlit(): Unhandled ink type %d", ink);
+ _surface->blitFrom(spriteSurface, Common::Point(drawRect.left, drawRect.top));
+ break;
+ }
+
+ if (nullMask)
+ delete maskSurface;
+}
+
+void Score::drawBackgndTransSprite(const Graphics::Surface &sprite, Common::Rect &drawRect, int spriteId) {
+ byte skipColor = _sprites[spriteId]->_backColor;
+ Common::Rect srcRect(sprite.w, sprite.h);
+
+ if (!_surface->clip(srcRect, drawRect))
+ return; // Out of screen
+
+ for (int ii = 0; ii < srcRect.height(); ii++) {
+ const byte *src = (const byte *)sprite.getBasePtr(srcRect.left, srcRect.top + ii);
+ byte *dst = (byte *)_surface->getBasePtr(drawRect.left, drawRect.top + ii);
+
+ for (int j = 0; j < srcRect.width(); j++) {
+ if (*src != skipColor)
+ *dst = *src;
+
+ src++;
+ dst++;
+ }
+ }
+}
+
+void Score::drawGhostSprite(const Graphics::Surface &sprite, Common::Rect &drawRect) {
+ Common::Rect srcRect(sprite.w, sprite.h);
+
+ if (!_surface->clip(srcRect, drawRect))
+ return; // Out of screen
+
+ uint8 skipColor = _vm->getPaletteColorCount() - 1;
+ for (int ii = 0; ii < srcRect.height(); ii++) {
+ const byte *src = (const byte *)sprite.getBasePtr(srcRect.left, srcRect.top + ii);
+ byte *dst = (byte *)_surface->getBasePtr(drawRect.left, drawRect.top + ii);
+
+ for (int j = 0; j < srcRect.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 Score::drawReverseSprite(const Graphics::Surface &sprite, Common::Rect &drawRect, uint16 spriteId) {
+ Common::Rect srcRect(sprite.w, sprite.h);
+
+ if (!_surface->clip(srcRect, drawRect))
+ return; // Out of screen
+
+ uint8 skipColor = _vm->getPaletteColorCount() - 1;
+ for (int ii = 0; ii < srcRect.height(); ii++) {
+ const byte *src = (const byte *)sprite.getBasePtr(srcRect.left, srcRect.top + ii);
+ byte *dst = (byte *)_surface->getBasePtr(drawRect.left, drawRect.top + ii);
+ byte srcColor = *src;
+
+ for (int j = 0; j < srcRect.width(); j++) {
+ if (_sprites[spriteId]->_cast->_type == kCastShape)
+ srcColor = 0x0;
+ else
+ srcColor = *src;
+ uint16 targetSprite = getSpriteIDFromPos(Common::Point(drawRect.left + j, drawRect.top + ii));
+ if ((targetSprite != 0)) {
+ // TODO: This entire reverse colour attempt needs a lot more testing on
+ // a lot more colour depths.
+ if (srcColor != skipColor) {
+ if (_sprites[targetSprite]->_cast->_type != kCastBitmap) {
+ if (*dst == 0 || *dst == 255) {
+ *dst = _vm->transformColor(*dst);
+ } else if (srcColor == 255 || srcColor == 0) {
+ *dst = _vm->transformColor(*dst - 40);
+ } else {
+ *dst = _vm->transformColor(*src - 40);
+ }
+ } else {
+ if (*dst == 0 && _vm->getVersion() == 3 &&
+ ((BitmapCast*)_sprites[spriteId]->_cast)->_bitsPerPixel > 1) {
+ *dst = _vm->transformColor(*src - 40);
+ } else {
+ *dst ^= _vm->transformColor(srcColor);
+ }
+ }
+ }
+ } else if (srcColor != skipColor) {
+ *dst = _vm->transformColor(srcColor);
+ }
+ src++;
+ dst++;
+ }
+ }
+}
+
+void Score::drawMatteSprite(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);
+ Common::Rect srcRect(sprite.w, sprite.h);
+
+ if (!_surface->clip(srcRect, drawRect))
+ return; // Out of screen
+
+ // Searching white color in the corners
+ int whiteColor = -1;
+
+ for (int y = 0; y < tmp.h; y++) {
+ for (int x = 0; x < tmp.w; x++) {
+ 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) {
+ debugC(1, kDebugImages, "Frame::drawMatteSprite(): No white color for Matte image");
+
+ for (int yy = 0; yy < srcRect.height(); yy++) {
+ const byte *src = (const byte *)tmp.getBasePtr(srcRect.left, srcRect.top + yy);
+ byte *dst = (byte *)_surface->getBasePtr(drawRect.left, drawRect.top + yy);
+
+ for (int xx = 0; xx < drawRect.width(); xx++, src++, dst++)
+ *dst = *src;
+ }
+ } else {
+ 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 < srcRect.height(); yy++) {
+ const byte *src = (const byte *)tmp.getBasePtr(srcRect.left, srcRect.top + yy);
+ const byte *mask = (const byte *)ff.getMask()->getBasePtr(srcRect.left, srcRect.top + yy);
+ byte *dst = (byte *)_surface->getBasePtr(drawRect.left, drawRect.top + yy);
+
+ for (int xx = 0; xx < srcRect.width(); xx++, src++, dst++, mask++)
+ if (*mask == 0)
+ *dst = *src;
+ }
+ }
+
+ tmp.free();
+}
+
+uint16 Score::getSpriteIDFromPos(Common::Point pos) {
+ for (int i = _sprites.size() - 1; i >= 0; i--)
+ if (_sprites[i]->_currentBbox.contains(pos))
+ return i;
+
+ return 0;
+}
+
+bool Score::checkSpriteIntersection(uint16 spriteId, Common::Point pos) {
+ if (_sprites[spriteId]->_currentBbox.contains(pos))
+ return true;
+
+ return false;
+}
+
+Common::Rect *Score::getSpriteRect(uint16 spriteId) {
+ return &_sprites[spriteId]->_currentBbox;
+}
+
Sprite *Score::getSpriteById(uint16 id) {
- if (_currentFrame >= _frames.size() || id >= _frames[_currentFrame]->_sprites.size()) {
+ if (id >= _sprites.size()) {
warning("Score::getSpriteById(%d): out of bounds. frame: %d", id, _currentFrame);
return nullptr;
}
- if (_frames[_currentFrame]->_sprites[id]) {
- return _frames[_currentFrame]->_sprites[id];
+ if (_sprites[id]) {
+ return _sprites[id];
} else {
warning("Sprite on frame %d width id %d not found", _currentFrame, id);
return nullptr;
}
}
+void Score::playSoundChannel(uint16 frameId) {
+ Frame *frame = _frames[frameId];
+ debug(0, "STUB: playSoundChannel(), Sound1 %d Sound2 %d", frame->_sound1, frame->_sound2);
+}
+
void Score::addZoomBox(ZoomBox *box) {
_zoomBoxes.push_back(box);
}
diff --git a/engines/director/score.h b/engines/director/score.h
index 4648ff06041..75476300163 100644
--- a/engines/director/score.h
+++ b/engines/director/score.h
@@ -102,6 +102,10 @@ public:
int getCurrentLabelNumber();
int getNextLabelNumber(int referenceFrame);
+ uint16 getSpriteIDFromPos(Common::Point pos);
+ bool checkSpriteIntersection(uint16 spriteId, Common::Point pos);
+ Common::Rect *getSpriteRect(uint16 spriteId);
+
void addZoomBox(ZoomBox *box);
void renderZoomBox(bool redraw = false);
bool haveZoomBox() { return !_zoomBoxes.empty(); }
@@ -109,9 +113,25 @@ public:
int32 getStageColor() { return _stageColor; }
Cast *getCastMember(int castId);
+ void renderFrame(uint16 frameId, bool forceUpdate = false, bool updateStageOnly = false);
+ void renderSprite(uint16 id);
+ void unrenderSprite(uint16 spriteId);
private:
void update();
+ void renderText(uint16 spriteId, Common::Rect *textSize);
+ void renderShape(uint16 spriteId);
+ void renderButton(uint16 spriteId);
+ void renderBitmap(uint16 spriteId);
+
+ void inkBasedBlit(Graphics::ManagedSurface *maskSurface, const Graphics::Surface &spriteSurface, InkType ink, Common::Rect drawRect, uint spriteId);
+ void drawBackgndTransSprite(const Graphics::Surface &sprite, Common::Rect &drawRect, int spriteId);
+ void drawMatteSprite(const Graphics::Surface &sprite, Common::Rect &drawRect);
+ void drawGhostSprite(const Graphics::Surface &sprite, Common::Rect &drawRect);
+ void drawReverseSprite(const Graphics::Surface &sprite, Common::Rect &drawRect, uint16 spriteId);
+
+ void playSoundChannel(uint16 frameId);
+
void readVersion(uint32 rid);
void loadPalette(Common::SeekableSubReadStreamEndian &stream);
void loadFrames(Common::SeekableSubReadStreamEndian &stream);
@@ -128,6 +148,7 @@ private:
public:
Common::Array _frames;
+ Common::Array _sprites;
Common::HashMap _castsInfo;
Common::HashMap _castsNames;
Common::SortedArray