mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-27 05:32:45 +00:00
DIRECTOR: Move to channel-based rendering
This commit is contained in:
parent
9fc6197c32
commit
5babbb30db
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user