[Scummvm-git-logs] scummvm master -> 1149e0edd3867e8fb714899a2a542a7e4fb620b0

sev- sev at scummvm.org
Sun May 17 21:21:38 UTC 2020


This automated email contains information about 4 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
3fa90b4ade DIRECTOR: Move from drawRects to bboxes
9fc6197c32 DIRECTOR: Precompute cast types of sprite
5babbb30db DIRECTOR: Move to channel-based rendering
1149e0edd3 DIRECTOR: Disable ink inversion hack


Commit: 3fa90b4adec1b714b96d2b0fab73ecb2183fe2c5
    https://github.com/scummvm/scummvm/commit/3fa90b4adec1b714b96d2b0fab73ecb2183fe2c5
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2020-05-17T23:21:33+02:00

Commit Message:
DIRECTOR: Move from drawRects to bboxes

Changed paths:
    engines/director/events.cpp
    engines/director/frame.cpp
    engines/director/frame.h
    engines/director/lingo/lingo-the.cpp
    engines/director/score.cpp
    engines/director/score.h
    engines/director/sprite.cpp
    engines/director/sprite.h


diff --git a/engines/director/events.cpp b/engines/director/events.cpp
index 5793100c1e..05909b5656 100644
--- a/engines/director/events.cpp
+++ b/engines/director/events.cpp
@@ -88,6 +88,7 @@ void DirectorEngine::processEvents(bool bufferLingoEvents) {
 						Common::Point delta = pos - _draggingSpritePos;
 						draggedSprite->_currentPoint.x += delta.x;
 						draggedSprite->_currentPoint.y += delta.y;
+						draggedSprite->_dirtyBbox.translate(delta.x, delta.y);
 						_draggingSpritePos = pos;
 					} else {
 						releaseDraggedSprite();
diff --git a/engines/director/frame.cpp b/engines/director/frame.cpp
index fd3a247705..2a741b9e5f 100644
--- a/engines/director/frame.cpp
+++ b/engines/director/frame.cpp
@@ -108,7 +108,6 @@ Frame::Frame(const Frame &frame) {
 
 Frame::~Frame() {
 	delete _palette;
-	_drawRects.clear();
 
 	for (uint16 i = 0; i < _sprites.size(); i++)
 		delete _sprites[i];
@@ -527,7 +526,6 @@ void Frame::readSprite(Common::SeekableSubReadStreamEndian &stream, uint16 offse
 }
 
 void Frame::prepareFrame(Score *score, bool updateStageOnly) {
-	_drawRects.clear();
 	renderSprites(*score->_surface, false);
 	renderSprites(*score->_trailSurface, true);
 
@@ -555,6 +553,62 @@ void Frame::playSoundChannel() {
 	debug(0, "STUB: playSoundChannel(), Sound1 %d Sound2 %d", _sound1, _sound2);
 }
 
+CastType Frame::getCastType(uint16 spriteId) {
+	CastType castType = kCastTypeNull;
+	Sprite *sprite = _sprites[spriteId];
+
+	if (_vm->getVersion() < 4) {
+		debugC(1, kDebugImages, "Frame::getCastType(): Channel: %d type: %d", spriteId, sprite->_spriteType);
+		switch (sprite->_spriteType) {
+		case kBitmapSprite:
+			castType = kCastBitmap;
+			break;
+		case kRectangleSprite:
+		case kRoundedRectangleSprite:
+		case kOvalSprite:
+		case kLineTopBottomSprite:
+		case kLineBottomTopSprite:
+		case kOutlinedRectangleSprite:
+		case kOutlinedRoundedRectangleSprite:
+		case kOutlinedOvalSprite:
+		case kCastMemberSprite:
+			if (sprite->_cast != nullptr) {
+				switch (sprite->_cast->_type) {
+				case kCastButton:
+					castType = kCastButton;
+					break;
+				default:
+					castType = kCastShape;
+					break;
+				}
+			} else {
+				castType = kCastShape;
+			}
+			break;
+		case kTextSprite:
+			castType = kCastText;
+			break;
+		case kButtonSprite:
+		case kCheckboxSprite:
+		case kRadioButtonSprite:
+			castType = kCastButton;
+			break;
+		default:
+			warning("Frame::getCastType(): Unhandled sprite type %d", sprite->_spriteType);
+			break;
+		}
+	} else {
+		Cast *member = _vm->getCastMember(_sprites[spriteId]->_castId);
+		if (!member) {
+			debugC(1, kDebugImages, "Frame::renderSprites(): Cast id %d not found", _sprites[spriteId]->_castId);
+		} else {
+			castType = member->_type;
+		}
+	}
+
+	return castType;
+}
+
 void Frame::renderSprites(Graphics::ManagedSurface &surface, bool renderTrail) {
 	for (uint16 i = 0; i <= _numChannels; i++) {
 		if (!_sprites[i]->_enabled)
@@ -563,60 +617,16 @@ void Frame::renderSprites(Graphics::ManagedSurface &surface, bool renderTrail) {
 		if ((_sprites[i]->_trails == 0 && renderTrail) || (_sprites[i]->_trails == 1 && !renderTrail))
 			continue;
 
-		CastType castType = kCastTypeNull;
-		if (_vm->getVersion() < 4) {
-			debugC(1, kDebugImages, "Frame::renderSprites(): Channel: %d type: %d", i, _sprites[i]->_spriteType);
-			switch (_sprites[i]->_spriteType) {
-			case kBitmapSprite:
-				castType = kCastBitmap;
-				break;
-			case kRectangleSprite:
-			case kRoundedRectangleSprite:
-			case kOvalSprite:
-			case kLineTopBottomSprite:
-			case kLineBottomTopSprite:
-			case kOutlinedRectangleSprite:
-			case kOutlinedRoundedRectangleSprite:
-			case kOutlinedOvalSprite:
-			case kCastMemberSprite:
-				if (_sprites[i]->_cast != nullptr) {
-					switch (_sprites[i]->_cast->_type) {
-					case kCastButton:
-						castType = kCastButton;
-						break;
-					default:
-						castType = kCastShape;
-						break;
-					}
-				} else {
-					castType = kCastShape;
-				}
-				break;
-			case kTextSprite:
-				castType = kCastText;
-				break;
-			case kButtonSprite:
-			case kCheckboxSprite:
-			case kRadioButtonSprite:
-				castType = kCastButton;
-				break;
-			default:
-				warning("Frame::renderSprites(): Unhandled sprite type %d", _sprites[i]->_spriteType);
-				break;
-			}
-		} else {
-			Cast *member = _vm->getCastMember(_sprites[i]->_castId);
-			if (!member) {
-				debugC(1, kDebugImages, "Frame::renderSprites(): Cast id %d not found", _sprites[i]->_castId);
-			} else {
-				castType = member->_type;
-			}
-		}
+		CastType castType = getCastType(i);
+		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) {
@@ -638,13 +648,6 @@ void Frame::renderSprites(Graphics::ManagedSurface &surface, bool renderTrail) {
 	}
 }
 
-void Frame::addDrawRect(uint16 spriteId, Common::Rect &rect) {
-	FrameEntity *fi = new FrameEntity();
-	fi->spriteId = spriteId;
-	fi->rect = rect;
-	_drawRects.push_back(fi);
-}
-
 void Frame::renderShape(Graphics::ManagedSurface &surface, uint16 spriteId) {
 	Sprite *sp = _sprites[spriteId];
 
@@ -700,10 +703,7 @@ void Frame::renderShape(Graphics::ManagedSurface &surface, uint16 spriteId) {
 	// for outlined shapes, line thickness of 1 means invisible.
 	lineSize -= 1;
 
-	Common::Rect shapeRect = Common::Rect(sp->_currentPoint.x,
-		sp->_currentPoint.y,
-		sp->_currentPoint.x + sp->_width,
-		sp->_currentPoint.y + sp->_height);
+	Common::Rect shapeRect = sp->_currentBbox;
 
 	Graphics::ManagedSurface tmpSurface, maskSurface;
 	tmpSurface.create(shapeRect.width(), shapeRect.height(), Graphics::PixelFormat::createFormatCLUT8());
@@ -762,7 +762,6 @@ void Frame::renderShape(Graphics::ManagedSurface &surface, uint16 spriteId) {
 		break;
 	}
 
-	addDrawRect(spriteId, shapeRect);
 	inkBasedBlit(surface, &maskSurface, tmpSurface, ink, shapeRect, spriteId);
 }
 
@@ -798,28 +797,16 @@ void Frame::renderButton(Graphics::ManagedSurface &surface, uint16 spriteId) {
 		}
 	}
 
-	uint32 rectLeft = button->_initialRect.left;
-	uint32 rectTop = button->_initialRect.top;
-
-	int x = _sprites[spriteId]->_currentPoint.x;
-	int y = _sprites[spriteId]->_currentPoint.y;
-
-	if (_vm->getVersion() > 3) {
-		x += rectLeft;
-		y += rectTop;
-	}
-
-	int height = button->_initialRect.height();
-	int width = button->_initialRect.width() + 3;
-
 	bool invert = spriteId == _vm->getCurrentScore()->_currentMouseDownSpriteId;
 
 	// TODO: review all cases to confirm if we should use text height.
 	// height = textRect.height();
 
-	Common::Rect _rect;
+	Common::Rect _rect = _sprites[spriteId]->_currentBbox;
+	int16 x = _rect.left;
+	int16 y = _rect.top;
 
-	Common::Rect textRect(0, 0, width, height);
+	Common::Rect textRect(0, 0, _rect.width(), _rect.height());
 
 	// WORKAROUND, HACK
 	// Because we're not drawing text with transparency
@@ -828,27 +815,20 @@ void Frame::renderButton(Graphics::ManagedSurface &surface, uint16 spriteId) {
 	if (!invert)
 		renderText(surface, spriteId, &textRect);
 
-	Graphics::MacPlotData plotStroke(&surface, nullptr, &_vm->getPatterns(), 1, -_rect.left, -_rect.top, 1, 0);
+	Graphics::MacPlotData plotStroke(&surface, nullptr, &_vm->getPatterns(), 1, 0, 0, 1, 0);
 
 	switch (buttonType) {
 	case kCheckboxSprite:
-		// Magic numbers: checkbox square need to move left about 5px from text and 12px side size (D4)
-		_rect = Common::Rect(x, y + 2, x + 12, y + 14);
 		surface.frameRect(_rect, 0);
-		addDrawRect(spriteId, _rect);
 		break;
 	case kButtonSprite: {
-			_rect = Common::Rect(x, y, x + width, y + height + 3);
 			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);
-			addDrawRect(spriteId, _rect);
 		}
 		break;
 	case kRadioButtonSprite:
-		_rect = Common::Rect(x, y + 2, x + 12, y + 14);
 		Graphics::drawEllipse(x, y + 2, x + 11, y + 13, 0, false, Graphics::macDrawPixel, &plotStroke);
-		addDrawRect(spriteId, _rect);
 		break;
 	default:
 		warning("renderButton: Unknown buttonType");
@@ -869,20 +849,11 @@ void Frame::renderText(Graphics::ManagedSurface &surface, uint16 spriteId, Commo
 	Score *score = _vm->getCurrentScore();
 	Sprite *sprite = _sprites[spriteId];
 
-	int x = sprite->_currentPoint.x; // +rectLeft;
-	int y = sprite->_currentPoint.y; // +rectTop;
-	int height = textCast->_initialRect.height(); //_sprites[spriteId]->_height;
-	int width;
-
-	if (_vm->getVersion() >= 4) {
-		if (textRect == NULL) {
-			width = textCast->_initialRect.right;
-		} else {
-			width = textRect->width();
-		}
-	} else {
-		width = textCast->_initialRect.width(); //_sprites[spriteId]->_width;
-	}
+	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?
@@ -1055,18 +1026,8 @@ void Frame::renderBitmap(Graphics::ManagedSurface &surface, uint16 spriteId) {
 		ink = sprite->_ink;
 
 	BitmapCast *bc = (BitmapCast *)sprite->_cast;
+	Common::Rect drawRect = sprite->_currentBbox;
 
-	int32 regX = bc->_regX;
-	int32 regY = bc->_regY;
-	int32 rectLeft = bc->_initialRect.left;
-	int32 rectTop = bc->_initialRect.top;
-
-	int x = sprite->_currentPoint.x - regX + rectLeft;
-	int y = sprite->_currentPoint.y - regY + rectTop;
-	int height = sprite->_height;
-	int width = _vm->getVersion() > 4 ? bc->_initialRect.width() : sprite->_width;
-	Common::Rect drawRect(x, y, x + width, y + height);
-	addDrawRect(spriteId, drawRect);
 	inkBasedBlit(surface, nullptr, *(bc->_surface), ink, drawRect, spriteId);
 }
 
@@ -1260,29 +1221,22 @@ void Frame::drawMatteSprite(Graphics::ManagedSurface &target, const Graphics::Su
 }
 
 uint16 Frame::getSpriteIDFromPos(Common::Point pos) {
-	// Find first from front to back
-	for (int dr = _drawRects.size() - 1; dr >= 0; dr--)
-		if (_drawRects[dr]->rect.contains(pos))
-			return _drawRects[dr]->spriteId;
+	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) {
-	// Find first from front to back
-	for (int dr = _drawRects.size() - 1; dr >= 0; dr--)
-		if (_drawRects[dr]->spriteId == spriteId && _drawRects[dr]->rect.contains(pos))
-			return true;
+	if (_sprites[spriteId]->_currentBbox.contains(pos))
+		return true;
 
 	return false;
 }
 
 Common::Rect *Frame::getSpriteRect(uint16 spriteId) {
-	for (int dr = _drawRects.size() - 1; dr >= 0; dr--)
-		if (_drawRects[dr]->spriteId == spriteId)
-			return &_drawRects[dr]->rect;
-
-	return nullptr;
+	return &_sprites[spriteId]->_currentBbox;
 }
 
 } // End of namespace Director
diff --git a/engines/director/frame.h b/engines/director/frame.h
index 6891f11290..96a4525292 100644
--- a/engines/director/frame.h
+++ b/engines/director/frame.h
@@ -76,6 +76,7 @@ public:
 	uint16 getSpriteIDFromPos(Common::Point pos);
 	bool checkSpriteIntersection(uint16 spriteId, Common::Point pos);
 	Common::Rect *getSpriteRect(uint16 spriteId);
+	CastType getCastType(uint16 spriteId);
 
 	void executeImmediateScripts();
 
@@ -97,7 +98,6 @@ private:
 	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);
-	void addDrawRect(uint16 entityId, Common::Rect &rect);
 
 public:
 	int _numChannels;
@@ -124,7 +124,6 @@ public:
 	uint8 _skipFrameFlag;
 	uint8 _blend;
 	Common::Array<Sprite *> _sprites;
-	Common::Array<FrameEntity *> _drawRects;
 	DirectorEngine *_vm;
 };
 
diff --git a/engines/director/lingo/lingo-the.cpp b/engines/director/lingo/lingo-the.cpp
index 5f9144eebc..5459c5f44d 100644
--- a/engines/director/lingo/lingo-the.cpp
+++ b/engines/director/lingo/lingo-the.cpp
@@ -573,7 +573,7 @@ Datum Lingo::getTheSprite(Datum &id1, int field) {
 		d.u.i = sprite->_blend;
 		break;
 	case kTheBottom:
-		d.u.i = sprite->_bottom;
+		d.u.i = sprite->_currentBbox.bottom;
 		break;
 	case kTheCastNum:
 		d.u.i = sprite->_castId;
@@ -594,7 +594,7 @@ Datum Lingo::getTheSprite(Datum &id1, int field) {
 		d.u.i = sprite->_ink;
 		break;
 	case kTheLeft:
-		d.u.i = sprite->_left;
+		d.u.i = sprite->_currentBbox.left;
 		break;
 	case kTheLineSize:
 		d.u.i = sprite->_thickness & 0x3;
@@ -621,7 +621,7 @@ Datum Lingo::getTheSprite(Datum &id1, int field) {
 		d.u.i = sprite->_puppet;
 		break;
 	case kTheRight:
-		d.u.i = sprite->_right;
+		d.u.i = sprite->_currentBbox.right;
 		break;
 	case kTheStartTime:
 		d.u.i = sprite->_startTime;
@@ -633,7 +633,7 @@ Datum Lingo::getTheSprite(Datum &id1, int field) {
 		d.u.i = sprite->_stretch;
 		break;
 	case kTheTop:
-		d.u.i = sprite->_top;
+		d.u.i = sprite->_currentBbox.top;
 		break;
 	case kTheTrails:
 		d.u.i = sprite->_trails;
@@ -687,9 +687,6 @@ void Lingo::setTheSprite(Datum &id1, int field, Datum &d) {
 	case kTheBlend:
 		sprite->_blend = d.asInt();
 		break;
-	case kTheBottom:
-		sprite->_bottom = d.asInt();
-		break;
 	case kTheCastNum:
 		if (_vm->getCastMember(d.asInt())) {
 			sprite->_cast = _vm->getCastMember(d.asInt());
@@ -711,9 +708,6 @@ void Lingo::setTheSprite(Datum &id1, int field, Datum &d) {
 	case kTheInk:
 		sprite->_ink = static_cast<InkType>(d.asInt());
 		break;
-	case kTheLeft:
-		sprite->_left = d.asInt();
-		break;
 	case kTheLineSize:
 		sprite->_thickness = d.asInt();
 		break;
@@ -725,8 +719,10 @@ void Lingo::setTheSprite(Datum &id1, int field, Datum &d) {
 		break;
 	case kTheMoveableSprite:
 		sprite->_moveable = d.asInt();
-		if (!d.u.i)
+		if (!d.u.i) {
 			sprite->_currentPoint = sprite->_startPoint;
+			sprite->_dirtyBbox = sprite->_startBbox;
+		}
 		break;
 	case kTheMovieRate:
 		sprite->_movieRate = d.asInt();
@@ -740,9 +736,6 @@ void Lingo::setTheSprite(Datum &id1, int field, Datum &d) {
 	case kThePuppet:
 		sprite->_puppet = d.asInt();
 		break;
-	case kTheRight:
-		sprite->_right = d.asInt();
-		break;
 	case kTheStartTime:
 		sprite->_startTime = d.asInt();
 		break;
@@ -752,9 +745,6 @@ void Lingo::setTheSprite(Datum &id1, int field, Datum &d) {
 	case kTheStretch:
 		sprite->_stretch = d.asInt();
 		break;
-	case kTheTop:
-		sprite->_top = d.asInt();
-		break;
 	case kTheTrails:
 		sprite->_trails = d.asInt();
 		break;
diff --git a/engines/director/score.cpp b/engines/director/score.cpp
index 587bc6400b..ef0ce793a5 100644
--- a/engines/director/score.cpp
+++ b/engines/director/score.cpp
@@ -278,8 +278,8 @@ void Score::loadArchive() {
 		debug("STUB: Unhandled 'SCVW' resource");
 	}
 
-
 	setSpriteCasts();
+	setSpriteBboxes();
 	loadSpriteImages(false);
 	loadSpriteSounds(false);
 
@@ -772,6 +772,128 @@ void Score::setSpriteCasts() {
 	}
 }
 
+void Score::setSpriteBboxes() {
+	for (uint16 i = 0; i < _frames.size(); i++) {
+		for (uint16 j = 0; j < _frames[i]->_sprites.size(); j++) {
+			Sprite *sp = _frames[i]->_sprites[j];
+
+			if (sp->_castId == 0)
+				continue;
+
+			CastType castType = _frames[i]->getCastType(j);
+
+			switch (castType) {
+			case kCastShape:
+				sp->_startBbox = Common::Rect(sp->_currentPoint.x,
+																			sp->_currentPoint.y,
+																			sp->_currentPoint.x + sp->_width,
+																			sp->_currentPoint.y + sp->_height);
+				break;
+			case kCastRTE:
+			case kCastText: {
+				TextCast *textCast = (TextCast*)sp->_cast;
+				int x = sp->_currentPoint.x; // +rectLeft;
+				int y = sp->_currentPoint.y; // +rectTop;
+				int height = textCast->_initialRect.height(); //_sprites[spriteId]->_height;
+				int width;
+				Common::Rect *textRect = NULL;
+
+				if (_vm->getVersion() >= 4) {
+					// where does textRect come from?
+					if (textRect == NULL) {
+						width = textCast->_initialRect.right;
+					} else {
+						width = textRect->width();
+					}
+				} else {
+					width = textCast->_initialRect.width(); //_sprites[spriteId]->_width;
+				}
+
+				sp->_startBbox = Common::Rect(x, y, x + width, y + height);
+				break;
+			}
+			case kCastButton: {
+				uint16 castId = sp->_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!
+				ButtonCast *button = (ButtonCast *)_vm->getCurrentScore()->_loadedCast->getVal(castId);
+
+				// 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 = sp->_spriteType;
+				if (buttonType == kCastMemberSprite) {
+					switch (button->_buttonType) {
+					case kTypeCheckBox:
+						buttonType = kCheckboxSprite;
+						break;
+					case kTypeButton:
+						buttonType = kButtonSprite;
+						break;
+					case kTypeRadio:
+						buttonType = kRadioButtonSprite;
+						break;
+					}
+				}
+
+				uint32 rectLeft = button->_initialRect.left;
+				uint32 rectTop = button->_initialRect.top;
+
+				int x = sp->_currentPoint.x;
+				int y = sp->_currentPoint.y;
+
+				if (_vm->getVersion() > 3) {
+					x += rectLeft;
+					y += rectTop;
+				}
+
+				int height = button->_initialRect.height();
+				int width = button->_initialRect.width() + 3;
+
+				switch (buttonType) {
+				case kCheckboxSprite:
+					// Magic numbers: checkbox square need to move left about 5px from
+					// text and 12px side size (D4)
+					sp->_startBbox = Common::Rect(x, y + 2, x + 12, y + 14);
+					break;
+				case kButtonSprite:
+					sp->_startBbox = Common::Rect(x, y, x + width, y + height + 3);
+					break;
+				case kRadioButtonSprite:
+					sp->_startBbox = Common::Rect(x, y + 2, x + 12, y + 14);
+					break;
+				default:
+					warning("Score::setSpriteBboxes: Unknown buttonType");
+				}
+				break;
+			}
+			case kCastBitmap: {
+				BitmapCast *bc = (BitmapCast *)sp->_cast;
+
+				int32 regX = bc->_regX;
+				int32 regY = bc->_regY;
+				int32 rectLeft = bc->_initialRect.left;
+				int32 rectTop = bc->_initialRect.top;
+
+				int x = sp->_currentPoint.x - regX + rectLeft;
+				int y = sp->_currentPoint.y - regY + rectTop;
+				int height = sp->_height;
+				int width = _vm->getVersion() > 4 ? bc->_initialRect.width() : sp->_width;
+
+				sp->_startBbox = Common::Rect(x, y, x + width, y + height);
+				break;
+			}
+			default:
+				warning("Score::setSpriteBboxes(): Unhandled cast type: %d", castType);
+			}
+			sp->_currentBbox = sp->_startBbox;
+			sp->_dirtyBbox = sp->_startBbox;
+		}
+	}
+}
+
 void Score::loadCastData(Common::SeekableSubReadStreamEndian &stream, uint16 id, Resource *res) {
 	// IDs are stored as relative to the start of the cast array.
 	id += _castArrayStart;
diff --git a/engines/director/score.h b/engines/director/score.h
index 5b6044d674..4648ff0604 100644
--- a/engines/director/score.h
+++ b/engines/director/score.h
@@ -89,6 +89,7 @@ public:
 	Common::String getMacName() const { return _macName; }
 	Sprite *getSpriteById(uint16 id);
 	void setSpriteCasts();
+	void setSpriteBboxes();
 	void loadSpriteImages(bool isSharedCast);
 	void loadSpriteSounds(bool isSharedCast);
 	void copyCastStxts();
diff --git a/engines/director/sprite.cpp b/engines/director/sprite.cpp
index 0e9408db09..e7b12bc862 100644
--- a/engines/director/sprite.cpp
+++ b/engines/director/sprite.cpp
@@ -53,10 +53,6 @@ Sprite::Sprite() {
 	_backColor = 255;
 	_foreColor = 0;
 
-	_left = 0;
-	_right = 0;
-	_top = 0;
-	_bottom = 0;
 	_blend = 0;
 	_visible = false;
 	_movieRate = 0;
diff --git a/engines/director/sprite.h b/engines/director/sprite.h
index 7009c63978..796ac84b8e 100644
--- a/engines/director/sprite.h
+++ b/engines/director/sprite.h
@@ -84,6 +84,9 @@ public:
 	byte _thickness;
 	Common::Point _startPoint;
 	Common::Point _currentPoint;
+	Common::Rect _startBbox;
+	Common::Rect _currentBbox;
+	Common::Rect _dirtyBbox;
 	uint16 _width;
 	uint16 _height;
 	// TODO: default constraint = 0, if turned on, sprite is constrainted to the bounding rect
@@ -95,10 +98,6 @@ public:
 	byte _backColor;
 	byte _foreColor;
 
-	uint16 _left;
-	uint16 _right;
-	uint16 _top;
-	uint16 _bottom;
 	byte _blend;
 	bool _visible;
 	// Using in digital movie sprites


Commit: 9fc6197c32688443921c70e1d3b54fe9092ce952
    https://github.com/scummvm/scummvm/commit/9fc6197c32688443921c70e1d3b54fe9092ce952
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2020-05-17T23:21:33+02:00

Commit Message:
DIRECTOR: Precompute cast types of sprite

Changed paths:
    engines/director/frame.cpp
    engines/director/frame.h
    engines/director/score.cpp
    engines/director/sprite.h


diff --git a/engines/director/frame.cpp b/engines/director/frame.cpp
index 2a741b9e5f..f506fd00e9 100644
--- a/engines/director/frame.cpp
+++ b/engines/director/frame.cpp
@@ -553,62 +553,6 @@ void Frame::playSoundChannel() {
 	debug(0, "STUB: playSoundChannel(), Sound1 %d Sound2 %d", _sound1, _sound2);
 }
 
-CastType Frame::getCastType(uint16 spriteId) {
-	CastType castType = kCastTypeNull;
-	Sprite *sprite = _sprites[spriteId];
-
-	if (_vm->getVersion() < 4) {
-		debugC(1, kDebugImages, "Frame::getCastType(): Channel: %d type: %d", spriteId, sprite->_spriteType);
-		switch (sprite->_spriteType) {
-		case kBitmapSprite:
-			castType = kCastBitmap;
-			break;
-		case kRectangleSprite:
-		case kRoundedRectangleSprite:
-		case kOvalSprite:
-		case kLineTopBottomSprite:
-		case kLineBottomTopSprite:
-		case kOutlinedRectangleSprite:
-		case kOutlinedRoundedRectangleSprite:
-		case kOutlinedOvalSprite:
-		case kCastMemberSprite:
-			if (sprite->_cast != nullptr) {
-				switch (sprite->_cast->_type) {
-				case kCastButton:
-					castType = kCastButton;
-					break;
-				default:
-					castType = kCastShape;
-					break;
-				}
-			} else {
-				castType = kCastShape;
-			}
-			break;
-		case kTextSprite:
-			castType = kCastText;
-			break;
-		case kButtonSprite:
-		case kCheckboxSprite:
-		case kRadioButtonSprite:
-			castType = kCastButton;
-			break;
-		default:
-			warning("Frame::getCastType(): Unhandled sprite type %d", sprite->_spriteType);
-			break;
-		}
-	} else {
-		Cast *member = _vm->getCastMember(_sprites[spriteId]->_castId);
-		if (!member) {
-			debugC(1, kDebugImages, "Frame::renderSprites(): Cast id %d not found", _sprites[spriteId]->_castId);
-		} else {
-			castType = member->_type;
-		}
-	}
-
-	return castType;
-}
-
 void Frame::renderSprites(Graphics::ManagedSurface &surface, bool renderTrail) {
 	for (uint16 i = 0; i <= _numChannels; i++) {
 		if (!_sprites[i]->_enabled)
@@ -617,7 +561,7 @@ void Frame::renderSprites(Graphics::ManagedSurface &surface, bool renderTrail) {
 		if ((_sprites[i]->_trails == 0 && renderTrail) || (_sprites[i]->_trails == 1 && !renderTrail))
 			continue;
 
-		CastType castType = getCastType(i);
+		CastType castType = _sprites[i]->_castType;
 		if (castType == kCastTypeNull)
 			continue;
 
diff --git a/engines/director/frame.h b/engines/director/frame.h
index 96a4525292..5ab89bd330 100644
--- a/engines/director/frame.h
+++ b/engines/director/frame.h
@@ -76,7 +76,6 @@ public:
 	uint16 getSpriteIDFromPos(Common::Point pos);
 	bool checkSpriteIntersection(uint16 spriteId, Common::Point pos);
 	Common::Rect *getSpriteRect(uint16 spriteId);
-	CastType getCastType(uint16 spriteId);
 
 	void executeImmediateScripts();
 
diff --git a/engines/director/score.cpp b/engines/director/score.cpp
index ef0ce793a5..b04454bc20 100644
--- a/engines/director/score.cpp
+++ b/engines/director/score.cpp
@@ -767,6 +767,59 @@ void Score::setSpriteCasts() {
 			Cast *member = _vm->getCastMember(castId);
 			if (member) {
 				_frames[i]->_sprites[j]->_cast = member;
+
+				CastType castType = kCastTypeNull;
+				Sprite *sprite = _frames[i]->_sprites[j];
+
+				if (_vm->getVersion() < 4) {
+					debugC(1, kDebugImages, "Score::setSpriteCasts(): Frame: %d Channel: %d type: %d", i, j, sprite->_spriteType);
+					switch (sprite->_spriteType) {
+					case kBitmapSprite:
+						castType = kCastBitmap;
+						break;
+					case kRectangleSprite:
+					case kRoundedRectangleSprite:
+					case kOvalSprite:
+					case kLineTopBottomSprite:
+					case kLineBottomTopSprite:
+					case kOutlinedRectangleSprite:
+					case kOutlinedRoundedRectangleSprite:
+					case kOutlinedOvalSprite:
+					case kCastMemberSprite:
+						if (sprite->_cast != nullptr) {
+							switch (sprite->_cast->_type) {
+							case kCastButton:
+								castType = kCastButton;
+								break;
+							default:
+								castType = kCastShape;
+								break;
+							}
+						} else {
+							castType = kCastShape;
+						}
+						break;
+					case kTextSprite:
+						castType = kCastText;
+						break;
+					case kButtonSprite:
+					case kCheckboxSprite:
+					case kRadioButtonSprite:
+						castType = kCastButton;
+						break;
+					default:
+						warning("Score::setSpriteCasts(): Unhandled sprite type %d", sprite->_spriteType);
+						break;
+					}
+				} else {
+					member = _vm->getCastMember(sprite->_castId);
+					if (!member) {
+						debugC(1, kDebugImages, "Score::setSpriteCasts(): Cast id %d not found", sprite->_castId);
+					} else {
+						castType = member->_type;
+					}
+				}
+				sprite->_castType = castType;
 			}
 		}
 	}
@@ -780,7 +833,7 @@ void Score::setSpriteBboxes() {
 			if (sp->_castId == 0)
 				continue;
 
-			CastType castType = _frames[i]->getCastType(j);
+			CastType castType = sp->_castType;
 
 			switch (castType) {
 			case kCastShape:
diff --git a/engines/director/sprite.h b/engines/director/sprite.h
index 796ac84b8e..844d175fa6 100644
--- a/engines/director/sprite.h
+++ b/engines/director/sprite.h
@@ -75,6 +75,7 @@ public:
 	uint16 _castId;
 	uint16 _castIndex;
 	SpriteType _spriteType;
+	CastType _castType;
 	byte _inkData;
 	InkType _ink;
 	uint16 _trails;


Commit: 5babbb30db4f71ca31b92d26fc935ab508a25c8d
    https://github.com/scummvm/scummvm/commit/5babbb30db4f71ca31b92d26fc935ab508a25c8d
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2020-05-17T23:21:33+02:00

Commit Message:
DIRECTOR: Move to channel-based rendering

Changed paths:
    engines/director/events.cpp
    engines/director/frame.cpp
    engines/director/frame.h
    engines/director/lingo/lingo-builtins.cpp
    engines/director/lingo/lingo-the.cpp
    engines/director/score.cpp
    engines/director/score.h


diff --git a/engines/director/events.cpp b/engines/director/events.cpp
index 05909b5656..6af7ef8ed4 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 f506fd00e9..782a39f64b 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 5ab89bd330..684488841d 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 f0ef57eaa0..17877d00fb 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 5459c5f44d..0383d7057d 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 b04454bc20..70258152df 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 4648ff0604..7547630016 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<Frame *> _frames;
+	Common::Array<Sprite *> _sprites;
 	Common::HashMap<uint16, CastInfo *> _castsInfo;
 	Common::HashMap<Common::String, int, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> _castsNames;
 	Common::SortedArray<Label *> *_labels;
@@ -136,7 +157,7 @@ public:
 	Common::HashMap<uint16, Common::String> _fontMap;
 	Common::Array<uint16> _castScriptIds;
 	Graphics::ManagedSurface *_surface;
-	Graphics::ManagedSurface *_trailSurface;
+	Graphics::ManagedSurface *_maskSurface;
 	Graphics::ManagedSurface *_backSurface;
 	Graphics::ManagedSurface *_backSurface2;
 	Graphics::Font *_font;


Commit: 1149e0edd3867e8fb714899a2a542a7e4fb620b0
    https://github.com/scummvm/scummvm/commit/1149e0edd3867e8fb714899a2a542a7e4fb620b0
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2020-05-17T23:21:33+02:00

Commit Message:
DIRECTOR: Disable ink inversion hack

Changed paths:
    engines/director/score.cpp


diff --git a/engines/director/score.cpp b/engines/director/score.cpp
index 70258152df..34b81dda8d 100644
--- a/engines/director/score.cpp
+++ b/engines/director/score.cpp
@@ -2208,8 +2208,8 @@ void Score::renderText(uint16 spriteId, Common::Rect *textRect) {
 
 		InkType ink = sprite->_ink;
 
-		if (spriteId == score->_currentMouseDownSpriteId)
-			ink = kInkTypeReverse;
+		// if (spriteId == score->_currentMouseDownSpriteId)
+		// 	ink = kInkTypeReverse;
 
 		inkBasedBlit(nullptr, textCast->_widget->getSurface()->rawSurface(), ink, Common::Rect(x, y, x + width, y + height), spriteId);
 
@@ -2341,8 +2341,8 @@ void Score::renderText(uint16 spriteId, Common::Rect *textRect) {
 
 	InkType ink = sprite->_ink;
 
-	if (spriteId == score->_currentMouseDownSpriteId)
-		ink = kInkTypeReverse;
+	// if (spriteId == score->_currentMouseDownSpriteId)
+	// 	ink = kInkTypeReverse;
 
 	inkBasedBlit(nullptr, textWithFeatures, ink, Common::Rect(x, y, x + width, y + height), spriteId);
 }
@@ -2351,9 +2351,9 @@ void Score::renderBitmap(uint16 spriteId) {
 	InkType ink;
 	Sprite *sprite = _sprites[spriteId];
 
-	if (spriteId == _vm->getCurrentScore()->_currentMouseDownSpriteId)
-		ink = kInkTypeReverse;
-	else
+	// if (spriteId == _vm->getCurrentScore()->_currentMouseDownSpriteId)
+	// 	ink = kInkTypeReverse;
+	// else
 		ink = sprite->_ink;
 
 	BitmapCast *bc = (BitmapCast *)sprite->_cast;




More information about the Scummvm-git-logs mailing list