mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-03 09:23:37 +00:00
DIRECTOR: Lazily load ink masks
This is for mask and matte inks. Now, rather than a bitmap sprite requiring a flood fill every time (for mask ink) the matte is created and cached when matte ink is first requested on that cast. Mask ink does not require a new surface to be created, but it uses the same new channel interface.
This commit is contained in:
parent
509871448d
commit
a8b779ee0e
@ -45,6 +45,7 @@ BitmapCastMember::BitmapCastMember(Cast *cast, uint16 castId, Common::ReadStream
|
||||
: CastMember(cast, castId) {
|
||||
_type = kCastBitmap;
|
||||
_img = nullptr;
|
||||
_matte = nullptr;
|
||||
_bytes = 0;
|
||||
_pitch = 0;
|
||||
_flags = 0;
|
||||
@ -121,6 +122,9 @@ BitmapCastMember::BitmapCastMember(Cast *cast, uint16 castId, Common::ReadStream
|
||||
BitmapCastMember::~BitmapCastMember() {
|
||||
if (_img)
|
||||
delete _img;
|
||||
|
||||
if (_matte)
|
||||
delete _matte;
|
||||
}
|
||||
|
||||
void BitmapCastMember::createWidget() {
|
||||
@ -135,6 +139,61 @@ void BitmapCastMember::createWidget() {
|
||||
_widget->getSurface()->blitFrom(*_img->getSurface());
|
||||
}
|
||||
|
||||
void BitmapCastMember::createMatte() {
|
||||
// Like background trans, but all white pixels NOT ENCLOSED by coloured pixels
|
||||
// are transparent
|
||||
Graphics::Surface tmp;
|
||||
tmp.create(_initialRect.width(), _initialRect.height(), Graphics::PixelFormat::createFormatCLUT8());
|
||||
tmp.copyFrom(*_img->getSurface());
|
||||
|
||||
// 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 (g_director->getPalette()[color * 3 + 0] == 0xff &&
|
||||
g_director->getPalette()[color * 3 + 1] == 0xff &&
|
||||
g_director->getPalette()[color * 3 + 2] == 0xff) {
|
||||
whiteColor = color;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (whiteColor == -1) {
|
||||
debugC(1, kDebugImages, "BitmapCastMember::createMatte(): No white color for matte image");
|
||||
} else {
|
||||
delete _matte;
|
||||
|
||||
_matte = new Graphics::FloodFill(&tmp, whiteColor, 0, true);
|
||||
|
||||
for (int yy = 0; yy < tmp.h; yy++) {
|
||||
_matte->addSeed(0, yy);
|
||||
_matte->addSeed(tmp.w - 1, yy);
|
||||
}
|
||||
|
||||
for (int xx = 0; xx < tmp.w; xx++) {
|
||||
_matte->addSeed(xx, 0);
|
||||
_matte->addSeed(xx, tmp.h - 1);
|
||||
}
|
||||
|
||||
_matte->fillMask();
|
||||
}
|
||||
|
||||
tmp.free();
|
||||
}
|
||||
|
||||
Graphics::Surface *BitmapCastMember::getMatte() {
|
||||
// Lazy loading of mattes
|
||||
if (!_matte) {
|
||||
createMatte();
|
||||
}
|
||||
|
||||
return _matte ? _matte->getMask() : nullptr;
|
||||
}
|
||||
|
||||
DigitalVideoCastMember::DigitalVideoCastMember(Cast *cast, uint16 castId, Common::ReadStreamEndian &stream, uint16 version)
|
||||
: CastMember(cast, castId) {
|
||||
_type = kCastDigitalVideo;
|
||||
|
@ -29,6 +29,7 @@
|
||||
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
struct FloodFill;
|
||||
class MacText;
|
||||
class MacWindowManager;
|
||||
class MacButton;
|
||||
@ -86,9 +87,13 @@ public:
|
||||
BitmapCastMember(Cast *cast, uint16 castId, Common::ReadStreamEndian &stream, uint32 castTag, uint16 version);
|
||||
~BitmapCastMember();
|
||||
virtual void createWidget() override;
|
||||
|
||||
void createMatte();
|
||||
Graphics::Surface *getMatte();
|
||||
// virtual void setColors(int *fgcolor, int *bgcolor) override;
|
||||
|
||||
Image::ImageDecoder *_img;
|
||||
Graphics::FloodFill *_matte;
|
||||
|
||||
uint16 _pitch;
|
||||
uint16 _regX;
|
||||
|
@ -836,10 +836,11 @@ void inkDrawPixel(int x, int y, int color, void *data) {
|
||||
if (*src == p->backColor)
|
||||
break;
|
||||
// fall through
|
||||
case kInkTypeMatte:
|
||||
case kInkTypeMask:
|
||||
// Only unmasked pixels make it here, so copy them straight
|
||||
case kInkTypeCopy:
|
||||
*dst = *src;
|
||||
case kInkTypeMask:
|
||||
// TODO: Migrate from Stage to here
|
||||
break;
|
||||
case kInkTypeTransparent:
|
||||
// FIXME: Is colour to ignore always white (last entry in pallette)?
|
||||
@ -870,9 +871,6 @@ void inkDrawPixel(int x, int y, int color, void *data) {
|
||||
if (*src != p->numColors - 1)
|
||||
*dst = *dst | *src;
|
||||
break;
|
||||
case kInkTypeMatte:
|
||||
// TODO: Migrate from Stage to here.
|
||||
break;
|
||||
// Arithmetic ink types
|
||||
case kInkTypeBlend:
|
||||
if (*src != p->numColors - 1)
|
||||
|
@ -68,6 +68,37 @@ Graphics::ManagedSurface *Channel::getSurface() {
|
||||
}
|
||||
}
|
||||
|
||||
const Graphics::Surface *Channel::getMask() {
|
||||
switch (_sprite->_ink) {
|
||||
case kInkTypeMatte:
|
||||
// Mattes are only supported in bitmaps for now. Shapes don't need mattes,
|
||||
// as they already have all non-enclosed white pixels transparent.
|
||||
// Matte on text has a trivial enough effect to not worry about implementing.
|
||||
if (_sprite->_cast && _sprite->_cast->_type == kCastBitmap) {
|
||||
return ((BitmapCastMember *)_sprite->_cast)->getMatte();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
case kInkTypeMask: {
|
||||
CastMember *member = g_director->getCurrentMovie()->getCastMember(_sprite->_castId + 1);
|
||||
|
||||
if (_sprite->_cast && member->_initialRect == _sprite->_cast->_initialRect) {
|
||||
return &member->_widget->getSurface()->rawSurface();
|
||||
} else {
|
||||
warning("Channel::getMask(): Requested cast mask, but no matching mask was found");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Silence warning
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool Channel::isDirty(Sprite *nextSprite) {
|
||||
// When a sprite is puppeted setTheSprite ensures that the dirty flag here is
|
||||
// set. Otherwise, we need to rerender when the position, bounding box, or
|
||||
|
@ -26,6 +26,7 @@
|
||||
//#include "graphics/macgui/macwindowmanager.h"
|
||||
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
class ManagedSurface;
|
||||
class Font;
|
||||
class MacWindow;
|
||||
@ -112,10 +113,12 @@ struct Channel {
|
||||
|
||||
Channel(Sprite *sp);
|
||||
bool isDirty(Sprite *nextSprite = nullptr);
|
||||
|
||||
Common::Rect getBbox();
|
||||
Common::Point getPosition();
|
||||
MacShape *getShape();
|
||||
Graphics::ManagedSurface *getSurface();
|
||||
const Graphics::Surface *getMask();
|
||||
|
||||
void setClean(Sprite *nextSprite, int spriteId);
|
||||
void addDelta(Common::Point pos);
|
||||
|
@ -185,86 +185,20 @@ void Stage::inkBlitFrom(Channel *channel, Common::Rect destRect, Graphics::Manag
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, we are drawing a cast type that does have a built-in surface, so
|
||||
// blit from that.
|
||||
// TODO: Work this ink type into inkDrawPixel.
|
||||
if (sprite->_ink == kInkTypeMatte) {
|
||||
drawMatteSprite(channel, srcRect, destRect, blitTo);
|
||||
return;
|
||||
}
|
||||
// First, get any masks that might be needed.
|
||||
const Graphics::Surface *mask = channel->getMask();
|
||||
|
||||
pd.srcPoint.y = MAX(abs(srcRect.top - destRect.top), 0);
|
||||
for (int i = 0; i < destRect.height(); i++, pd.srcPoint.y++) {
|
||||
pd.srcPoint.x = MAX(abs(srcRect.left - destRect.left), 0);
|
||||
const byte *msk = mask ? (const byte *)mask->getBasePtr(pd.srcPoint.x, pd.srcPoint.y) : nullptr;
|
||||
|
||||
for (int j = 0; j < destRect.width(); j++, pd.srcPoint.x++)
|
||||
inkDrawPixel(destRect.left + j, destRect.top + i, 0, &pd);
|
||||
if (!mask || (msk && (sprite->_ink == kInkTypeMatte ? !(*msk++) : *msk++)))
|
||||
inkDrawPixel(destRect.left + j, destRect.top + i, 0, &pd);
|
||||
}
|
||||
}
|
||||
|
||||
void Stage::drawMatteSprite(Channel *channel, Common::Rect &srcRect, Common::Rect &destRect, Graphics::ManagedSurface *blitTo) {
|
||||
// Like background trans, but all white pixels NOT ENCLOSED by coloured pixels are transparent
|
||||
Graphics::Surface tmp;
|
||||
tmp.create(destRect.width(), destRect.height(), Graphics::PixelFormat::createFormatCLUT8());
|
||||
tmp.copyFrom(channel->getSurface()->rawSurface());
|
||||
|
||||
if (!blitTo->clip(srcRect, destRect))
|
||||
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 (g_director->getPalette()[color * 3 + 0] == 0xff &&
|
||||
g_director->getPalette()[color * 3 + 1] == 0xff &&
|
||||
g_director->getPalette()[color * 3 + 2] == 0xff) {
|
||||
whiteColor = color;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (whiteColor == -1) {
|
||||
debugC(1, kDebugImages, "Score::drawMatteSprite(): No white color for Matte image");
|
||||
|
||||
for (int yy = 0; yy < destRect.height(); yy++) {
|
||||
const byte *src = (const byte *)channel->getSurface()->getBasePtr(MAX(abs(srcRect.left - destRect.left), 0), MAX(abs(srcRect.top - destRect.top + yy), 0));
|
||||
byte *dst = (byte *)blitTo->getBasePtr(destRect.left, destRect.top + yy);
|
||||
|
||||
for (int xx = 0; xx < destRect.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 < destRect.height(); yy++) {
|
||||
const byte *mask = (const byte *)ff.getMask()->getBasePtr(MAX(abs(srcRect.left - destRect.left), 0), MAX(abs(srcRect.top - destRect.top - yy), 0));
|
||||
const byte *src = (const byte *)channel->getSurface()->getBasePtr(MAX(abs(srcRect.left - destRect.left), 0), MAX(abs(srcRect.top - destRect.top - yy), 0));
|
||||
byte *dst = (byte *)blitTo->getBasePtr(destRect.left, destRect.top + yy);
|
||||
|
||||
for (int xx = 0; xx < destRect.width(); xx++, src++, dst++, mask++)
|
||||
if (*mask == 0)
|
||||
*dst = *src;
|
||||
}
|
||||
}
|
||||
|
||||
tmp.free();
|
||||
}
|
||||
|
||||
Common::Point Stage::getMousePos() {
|
||||
return g_system->getEventManager()->getMousePos() - Common::Point(_dims.left, _dims.top);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user