SCI: Implement accurate renderer architecture for SCI32

This commit is contained in:
Colin Snover 2016-01-18 00:12:47 -06:00
parent 4ba0ff8deb
commit 75ccabc325
25 changed files with 6514 additions and 1238 deletions

View file

@ -137,6 +137,8 @@ Console::Console(SciEngine *engine) : GUI::Debugger(),
registerCmd("wl", WRAP_METHOD(Console, cmdWindowList)); // alias registerCmd("wl", WRAP_METHOD(Console, cmdWindowList)); // alias
registerCmd("plane_list", WRAP_METHOD(Console, cmdPlaneList)); registerCmd("plane_list", WRAP_METHOD(Console, cmdPlaneList));
registerCmd("pl", WRAP_METHOD(Console, cmdPlaneList)); // alias registerCmd("pl", WRAP_METHOD(Console, cmdPlaneList)); // alias
registerCmd("visible_plane_list", WRAP_METHOD(Console, cmdVisiblePlaneList));
registerCmd("vpl", WRAP_METHOD(Console, cmdVisiblePlaneList)); // alias
registerCmd("plane_items", WRAP_METHOD(Console, cmdPlaneItemList)); registerCmd("plane_items", WRAP_METHOD(Console, cmdPlaneItemList));
registerCmd("pi", WRAP_METHOD(Console, cmdPlaneItemList)); // alias registerCmd("pi", WRAP_METHOD(Console, cmdPlaneItemList)); // alias
registerCmd("saved_bits", WRAP_METHOD(Console, cmdSavedBits)); registerCmd("saved_bits", WRAP_METHOD(Console, cmdSavedBits));
@ -380,6 +382,7 @@ bool Console::cmdHelp(int argc, const char **argv) {
debugPrintf(" animate_list / al - Shows the current list of objects in kAnimate's draw list (SCI0 - SCI1.1)\n"); debugPrintf(" animate_list / al - Shows the current list of objects in kAnimate's draw list (SCI0 - SCI1.1)\n");
debugPrintf(" window_list / wl - Shows a list of all the windows (ports) in the draw list (SCI0 - SCI1.1)\n"); debugPrintf(" window_list / wl - Shows a list of all the windows (ports) in the draw list (SCI0 - SCI1.1)\n");
debugPrintf(" plane_list / pl - Shows a list of all the planes in the draw list (SCI2+)\n"); debugPrintf(" plane_list / pl - Shows a list of all the planes in the draw list (SCI2+)\n");
debugPrintf(" visible_plane_list / vpl - Shows a list of all the planes in the visible draw list (SCI2+)\n");
debugPrintf(" plane_items / pi - Shows a list of all items for a plane (SCI2+)\n"); debugPrintf(" plane_items / pi - Shows a list of all items for a plane (SCI2+)\n");
debugPrintf(" saved_bits - List saved bits on the hunk\n"); debugPrintf(" saved_bits - List saved bits on the hunk\n");
debugPrintf(" show_saved_bits - Display saved bits\n"); debugPrintf(" show_saved_bits - Display saved bits\n");
@ -1766,6 +1769,21 @@ bool Console::cmdPlaneList(int argc, const char **argv) {
return true; return true;
} }
bool Console::cmdVisiblePlaneList(int argc, const char **argv) {
#ifdef ENABLE_SCI32
if (_engine->_gfxFrameout) {
debugPrintf("Visible plane list:\n");
_engine->_gfxFrameout->printVisiblePlaneList(this);
} else {
debugPrintf("This SCI version does not have a list of planes\n");
}
#else
debugPrintf("SCI32 isn't included in this compiled executable\n");
#endif
return true;
}
bool Console::cmdPlaneItemList(int argc, const char **argv) { bool Console::cmdPlaneItemList(int argc, const char **argv) {
if (argc != 2) { if (argc != 2) {
debugPrintf("Shows the list of items for a plane\n"); debugPrintf("Shows the list of items for a plane\n");

View file

@ -96,6 +96,7 @@ private:
bool cmdAnimateList(int argc, const char **argv); bool cmdAnimateList(int argc, const char **argv);
bool cmdWindowList(int argc, const char **argv); bool cmdWindowList(int argc, const char **argv);
bool cmdPlaneList(int argc, const char **argv); bool cmdPlaneList(int argc, const char **argv);
bool cmdVisiblePlaneList(int argc, const char **argv);
bool cmdPlaneItemList(int argc, const char **argv); bool cmdPlaneItemList(int argc, const char **argv);
bool cmdSavedBits(int argc, const char **argv); bool cmdSavedBits(int argc, const char **argv);
bool cmdShowSavedBits(int argc, const char **argv); bool cmdShowSavedBits(int argc, const char **argv);

View file

@ -80,21 +80,18 @@ reg_t kCantBeHere32(EngineState *s, int argc, reg_t *argv) {
} }
reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv) { reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv) {
if (g_sci->_gfxFrameout->findScreenItem(argv[0]) == NULL)
g_sci->_gfxFrameout->kernelAddScreenItem(argv[0]); g_sci->_gfxFrameout->kernelAddScreenItem(argv[0]);
else return NULL_REG;
g_sci->_gfxFrameout->kernelUpdateScreenItem(argv[0]);
return s->r_acc;
} }
reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv) { reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv) {
g_sci->_gfxFrameout->kernelUpdateScreenItem(argv[0]); g_sci->_gfxFrameout->kernelUpdateScreenItem(argv[0]);
return s->r_acc; return NULL_REG;
} }
reg_t kDeleteScreenItem(EngineState *s, int argc, reg_t *argv) { reg_t kDeleteScreenItem(EngineState *s, int argc, reg_t *argv) {
g_sci->_gfxFrameout->kernelDeleteScreenItem(argv[0]); g_sci->_gfxFrameout->kernelDeleteScreenItem(argv[0]);
return s->r_acc; return NULL_REG;
} }
reg_t kAddPlane(EngineState *s, int argc, reg_t *argv) { reg_t kAddPlane(EngineState *s, int argc, reg_t *argv) {
@ -115,11 +112,12 @@ reg_t kUpdatePlane(EngineState *s, int argc, reg_t *argv) {
reg_t kAddPicAt(EngineState *s, int argc, reg_t *argv) { reg_t kAddPicAt(EngineState *s, int argc, reg_t *argv) {
reg_t planeObj = argv[0]; reg_t planeObj = argv[0];
GuiResourceId pictureId = argv[1].toUint16(); GuiResourceId pictureId = argv[1].toUint16();
int16 pictureX = argv[2].toSint16(); int16 x = argv[2].toSint16();
int16 pictureY = argv[3].toSint16(); int16 y = argv[3].toSint16();
bool mirrorX = argc > 4 ? argv[4].toSint16() : false;
g_sci->_gfxFrameout->kernelAddPicAt(planeObj, pictureId, pictureX, pictureY); g_sci->_gfxFrameout->kernelAddPicAt(planeObj, pictureId, x, y, mirrorX);
return s->r_acc; return NULL_REG;
} }
reg_t kGetHighPlanePri(EngineState *s, int argc, reg_t *argv) { reg_t kGetHighPlanePri(EngineState *s, int argc, reg_t *argv) {
@ -127,43 +125,13 @@ reg_t kGetHighPlanePri(EngineState *s, int argc, reg_t *argv) {
} }
reg_t kFrameOut(EngineState *s, int argc, reg_t *argv) { reg_t kFrameOut(EngineState *s, int argc, reg_t *argv) {
/* TODO: Transcribed from SCI engine disassembly. bool showBits = argc > 0 ? argv[0].toUint16() : true;
GraphicsMgr &graphicsMgr = g_sci->_graphicsMgr; g_sci->_gfxFrameout->kernelFrameout(showBits);
if (graphicsMgr.palMorphNeeded) {
graphicsMgr.PalMorphFrameOut(&g_PalStyleRanges, false);
}
else {
// TODO: Not sure if this is a pointer or not yet.
if (g_ScrollState != nullptr) {
kFrameOutDoScroll();
}
bool showBits = true;
if (argc == 1) {
showBits = (bool) argv[0].toUint16();
}
rect SOL_Rect = { .left = 0, .top = 0, .right = UINT32_MAX, .bottom = UINT32_MAX };
graphicsMgr.FrameOut(showBits, &rect);
}
*/
g_sci->_gfxFrameout->kernelFrameout();
return NULL_REG; return NULL_REG;
} }
reg_t kSetPalStyleRange(EngineState *s, int argc, reg_t *argv) { reg_t kSetPalStyleRange(EngineState *s, int argc, reg_t *argv) {
/* TODO: Transcribed from SCI engine disassembly. g_sci->_gfxFrameout->kernelSetPalStyleRange(argv[0].toUint16(), argv[1].toUint16());
uint16 start = argv[0].toUint16();
uint16 end = argv[1].toUint16();
if (end <= start) {
uint16 index = start;
while (index <= end) {
g_PalStyleRanges[index] = 0;
}
}
*/
kStub(s, argc, argv);
return NULL_REG; return NULL_REG;
} }
@ -266,72 +234,59 @@ reg_t kWinHelp(EngineState *s, int argc, reg_t *argv) {
} }
/** /**
* Used for scene transitions, replacing (but reusing parts of) the old * Causes an immediate plane transition with an optional transition
* transition code. * effect
*/ */
reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) { reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) {
// Can be called with 7 or 8 parameters ShowStyleType type = (ShowStyleType)argv[0].toUint16();
// The style defines which transition to perform. Related to the transition reg_t planeObj = argv[1];
// tables inside graphics/transitions.cpp int16 seconds = argv[2].toSint16();
uint16 showStyle = argv[0].toUint16(); // 0 - 15 // NOTE: This value seems to indicate whether the transition is an
reg_t planeObj = argv[1]; // the affected plane // “exit” transition (0) or an “enter” transition (-1) for fade
Common::String planeObjName = s->_segMan->getObjectName(planeObj); // transitions. For other types of transitions, it indicates a palette
uint16 seconds = argv[2].toUint16(); // seconds that the transition lasts // index value to use when filling the screen.
uint16 backColor = argv[3].toUint16(); // target back color(?). When fading out, it's 0x0000. When fading in, it's 0xffff int16 back = argv[3].toSint16();
int16 priority = argv[4].toSint16(); // always 0xc8 (200) when fading in/out int16 priority = argv[4].toSint16();
uint16 animate = argv[5].toUint16(); // boolean, animate or not while the transition lasts int16 animate = argv[5].toSint16();
uint16 refFrame = argv[6].toUint16(); // refFrame, always 0 when fading in/out // TODO: Rename to frameOutNow?
int16 refFrame = argv[6].toSint16();
int16 blackScreen;
reg_t pFadeArray;
int16 divisions; int16 divisions;
// If the game has the pFadeArray selector, another parameter is used here, // SCI 22.1early
// before the optional last parameter if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) {
bool hasFadeArray = g_sci->getKernel()->findSelector("pFadeArray") > 0; blackScreen = 0;
if (hasFadeArray) { pFadeArray = NULL_REG;
// argv[7] divisions = argc > 7 ? argv[7].toSint16() : -1;
divisions = (argc >= 9) ? argv[8].toSint16() : -1; // divisions (transition steps?) }
} else { // SCI 2.1mid2.1late
divisions = (argc >= 8) ? argv[7].toSint16() : -1; // divisions (transition steps?) else if (getSciVersion() < SCI_VERSION_3) {
blackScreen = 0;
pFadeArray = argc > 7 ? argv[7] : NULL_REG;
divisions = argc > 8 ? argv[8].toSint16() : -1;
}
// SCI 3
else {
blackScreen = argv[7].toSint16();
pFadeArray = argc > 8 ? argv[8] : NULL_REG;
divisions = argc > 9 ? argv[9].toSint16() : -1;
} }
if (showStyle > 15) { // TODO: Reuse later for SCI2 and SCI3 implementation and then discard
warning("kSetShowStyle: Illegal style %d for plane %04x:%04x", showStyle, PRINT_REG(planeObj)); // warning("kSetShowStyle: effect %d, plane: %04x:%04x (%s), sec: %d, "
return s->r_acc; // "dir: %d, prio: %d, animate: %d, ref frame: %d, black screen: %d, "
} // "pFadeArray: %04x:%04x (%s), divisions: %d",
// type, PRINT_REG(planeObj), s->_segMan->getObjectName(planeObj), seconds,
// back, priority, animate, refFrame, blackScreen,
// PRINT_REG(pFadeArray), s->_segMan->getObjectName(pFadeArray), divisions);
// GK1 calls fadeout (13) / fadein (14) with the following parameters: // NOTE: The order of planeObj and showStyle are reversed
// seconds: 1 // because this is how SCI3 called the corresponding method
// backColor: 0 / -1 // on the KernelMgr
// fade: 200 g_sci->_gfxFrameout->kernelSetShowStyle(argc, planeObj, type, seconds, back, priority, animate, refFrame, pFadeArray, divisions, blackScreen);
// animate: 0
// refFrame: 0
// divisions: 0 / 20
// TODO: Check if the plane is in the list of planes to draw return NULL_REG;
Common::String effectName = "unknown";
switch (showStyle) {
case 0: // no transition / show
effectName = "show";
break;
case 13: // fade out
effectName = "fade out";
// TODO
break;
case 14: // fade in
effectName = "fade in";
// TODO
break;
default:
// TODO
break;
}
warning("kSetShowStyle: effect %d (%s) - plane: %04x:%04x (%s), sec: %d, "
"back: %d, prio: %d, animate: %d, ref frame: %d, divisions: %d",
showStyle, effectName.c_str(), PRINT_REG(planeObj), planeObjName.c_str(),
seconds, backColor, priority, animate, refFrame, divisions);
return s->r_acc;
} }
reg_t kCelInfo(EngineState *s, int argc, reg_t *argv) { reg_t kCelInfo(EngineState *s, int argc, reg_t *argv) {
@ -359,6 +314,8 @@ reg_t kCelInfo(EngineState *s, int argc, reg_t *argv) {
} }
reg_t kScrollWindow(EngineState *s, int argc, reg_t *argv) { reg_t kScrollWindow(EngineState *s, int argc, reg_t *argv) {
return kStub(s, argc, argv);
#if 0
// Used by SQ6 and LSL6 hires for the text area in the bottom of the // Used by SQ6 and LSL6 hires for the text area in the bottom of the
// screen. The relevant scripts also exist in Phantasmagoria 1, but they're // screen. The relevant scripts also exist in Phantasmagoria 1, but they're
// unused. This is always called by scripts 64906 (ScrollerWindow) and // unused. This is always called by scripts 64906 (ScrollerWindow) and
@ -464,6 +421,7 @@ reg_t kScrollWindow(EngineState *s, int argc, reg_t *argv) {
} }
return s->r_acc; return s->r_acc;
#endif
} }
reg_t kSetFontRes(EngineState *s, int argc, reg_t *argv) { reg_t kSetFontRes(EngineState *s, int argc, reg_t *argv) {
@ -497,6 +455,8 @@ reg_t kFont(EngineState *s, int argc, reg_t *argv) {
// TODO: Eventually, all of the kBitmap operations should be put // TODO: Eventually, all of the kBitmap operations should be put
// in a separate class // in a separate class
// NOTE: This size is correct only for SCI2.1mid; the size for
// SCI2/2.1early is 36
#define BITMAP_HEADER_SIZE 46 #define BITMAP_HEADER_SIZE 46
reg_t kBitmap(EngineState *s, int argc, reg_t *argv) { reg_t kBitmap(EngineState *s, int argc, reg_t *argv) {
@ -673,6 +633,8 @@ reg_t kEditText(EngineState *s, int argc, reg_t *argv) {
} }
reg_t kAddLine(EngineState *s, int argc, reg_t *argv) { reg_t kAddLine(EngineState *s, int argc, reg_t *argv) {
return kStub(s, argc, argv);
#if 0
reg_t plane = argv[0]; reg_t plane = argv[0];
Common::Point startPoint(argv[1].toUint16(), argv[2].toUint16()); Common::Point startPoint(argv[1].toUint16(), argv[2].toUint16());
Common::Point endPoint(argv[3].toUint16(), argv[4].toUint16()); Common::Point endPoint(argv[3].toUint16(), argv[4].toUint16());
@ -681,10 +643,15 @@ reg_t kAddLine(EngineState *s, int argc, reg_t *argv) {
byte priority = (byte)argv[7].toUint16(); byte priority = (byte)argv[7].toUint16();
byte control = (byte)argv[8].toUint16(); byte control = (byte)argv[8].toUint16();
// argv[9] is unknown (usually a small number, 1 or 2). Thickness, perhaps? // argv[9] is unknown (usually a small number, 1 or 2). Thickness, perhaps?
return g_sci->_gfxFrameout->addPlaneLine(plane, startPoint, endPoint, color, priority, control); // return g_sci->_gfxFrameout->addPlaneLine(plane, startPoint, endPoint, color, priority, control);
return s->r_acc;
#endif
} }
reg_t kUpdateLine(EngineState *s, int argc, reg_t *argv) { reg_t kUpdateLine(EngineState *s, int argc, reg_t *argv) {
return kStub(s, argc, argv);
#if 0
reg_t hunkId = argv[0]; reg_t hunkId = argv[0];
reg_t plane = argv[1]; reg_t plane = argv[1];
Common::Point startPoint(argv[2].toUint16(), argv[3].toUint16()); Common::Point startPoint(argv[2].toUint16(), argv[3].toUint16());
@ -694,14 +661,18 @@ reg_t kUpdateLine(EngineState *s, int argc, reg_t *argv) {
byte priority = (byte)argv[8].toUint16(); byte priority = (byte)argv[8].toUint16();
byte control = (byte)argv[9].toUint16(); byte control = (byte)argv[9].toUint16();
// argv[10] is unknown (usually a small number, 1 or 2). Thickness, perhaps? // argv[10] is unknown (usually a small number, 1 or 2). Thickness, perhaps?
g_sci->_gfxFrameout->updatePlaneLine(plane, hunkId, startPoint, endPoint, color, priority, control); // g_sci->_gfxFrameout->updatePlaneLine(plane, hunkId, startPoint, endPoint, color, priority, control);
return s->r_acc; return s->r_acc;
#endif
} }
reg_t kDeleteLine(EngineState *s, int argc, reg_t *argv) { reg_t kDeleteLine(EngineState *s, int argc, reg_t *argv) {
return kStub(s, argc, argv);
#if 0
reg_t hunkId = argv[0]; reg_t hunkId = argv[0];
reg_t plane = argv[1]; reg_t plane = argv[1];
g_sci->_gfxFrameout->deletePlaneLine(plane, hunkId); // g_sci->_gfxFrameout->deletePlaneLine(plane, hunkId);
return s->r_acc; return s->r_acc;
#endif
} }
reg_t kSetScroll(EngineState *s, int argc, reg_t *argv) { reg_t kSetScroll(EngineState *s, int argc, reg_t *argv) {
@ -730,12 +701,7 @@ reg_t kSetScroll(EngineState *s, int argc, reg_t *argv) {
// Used by SQ6, script 900, the datacorder reprogramming puzzle (from room 270) // Used by SQ6, script 900, the datacorder reprogramming puzzle (from room 270)
reg_t kMorphOn(EngineState *s, int argc, reg_t *argv) { reg_t kMorphOn(EngineState *s, int argc, reg_t *argv) {
// TODO: g_sci->_gfxManager->palMorphIsOn = true g_sci->_gfxFrameout->_palMorphIsOn = true;
// This function sets the palMorphIsOn flag which causes kFrameOut to use
// an alternative FrameOut function (GraphicsMgr::PalMorphFrameOut instead
// of GraphicsMgr::FrameOut). At the end of the frame, kFrameOut sets the
// palMorphIsOn flag back to false.
kStub(s, argc, argv);
return NULL_REG; return NULL_REG;
} }

View file

@ -42,6 +42,19 @@ enum {
enum infoSelectorFlags { enum infoSelectorFlags {
kInfoFlagClone = 0x0001, kInfoFlagClone = 0x0001,
#ifdef ENABLE_SCI32
/**
* When set, indicates to game scripts that a screen
* item can be updated.
*/
kInfoFlagViewVisible = 0x0008, // TODO: "dirty" ?
/**
* When set, the object has an associated screen item in
* the rendering tree.
*/
kInfoFlagViewInserted = 0x0010,
#endif
kInfoFlagClass = 0x8000 kInfoFlagClass = 0x8000
}; };
@ -120,7 +133,24 @@ public:
_infoSelectorSci3 = info; _infoSelectorSci3 = info;
} }
// No setter for the -info- selector #ifdef ENABLE_SCI32
void setInfoSelectorFlag(infoSelectorFlags flag) {
if (getSciVersion() < SCI_VERSION_3) {
_variables[_offset + 2] |= flag;
} else {
_infoSelectorSci3 |= flag;
}
}
// NOTE: In real engine, -info- is treated as byte size
void clearInfoSelectorFlag(infoSelectorFlags flag) {
if (getSciVersion() < SCI_VERSION_3) {
_variables[_offset + 2] &= ~flag;
} else {
_infoSelectorSci3 &= ~flag;
}
}
#endif
reg_t getNameSelector() const { reg_t getNameSelector() const {
if (getSciVersion() < SCI_VERSION_3) if (getSciVersion() < SCI_VERSION_3)

View file

@ -187,6 +187,7 @@ void Kernel::mapSelectors() {
FIND_SELECTOR(inLeft); FIND_SELECTOR(inLeft);
FIND_SELECTOR(inBottom); FIND_SELECTOR(inBottom);
FIND_SELECTOR(inRight); FIND_SELECTOR(inRight);
FIND_SELECTOR(magnifier);
#endif #endif
} }
@ -211,8 +212,16 @@ void writeSelector(SegManager *segMan, reg_t object, Selector selectorId, reg_t
if (lookupSelector(segMan, object, selectorId, &address, NULL) != kSelectorVariable) if (lookupSelector(segMan, object, selectorId, &address, NULL) != kSelectorVariable)
error("Selector '%s' of object at %04x:%04x could not be" error("Selector '%s' of object at %04x:%04x could not be"
" written to", g_sci->getKernel()->getSelectorName(selectorId).c_str(), PRINT_REG(object)); " written to", g_sci->getKernel()->getSelectorName(selectorId).c_str(), PRINT_REG(object));
else else {
*address.getPointer(segMan) = value; *address.getPointer(segMan) = value;
#ifdef ENABLE_SCI32
// TODO: Make this correct for all SCI versions
// Selectors 26 through 44 are selectors for View script objects
if (getSciVersion() >= SCI_VERSION_2 && selectorId >= 26 && selectorId <= 44) {
segMan->getObject(object)->setInfoSelectorFlag(kInfoFlagViewVisible);
}
#endif
}
} }
void invokeSelector(EngineState *s, reg_t object, int selectorId, void invokeSelector(EngineState *s, reg_t object, int selectorId,

View file

@ -153,6 +153,8 @@ struct SelectorCache {
Selector useInsetRect; Selector useInsetRect;
Selector inTop, inLeft, inBottom, inRight; Selector inTop, inLeft, inBottom, inRight;
Selector magnifier;
#endif #endif
}; };

View file

@ -210,6 +210,20 @@ reg_t reg_t::operator^(const reg_t right) const {
return lookForWorkaround(right, "bitwise XOR"); return lookForWorkaround(right, "bitwise XOR");
} }
#ifdef ENABLE_SCI32
reg_t reg_t::operator&(int16 right) const {
return *this & make_reg(0, right);
}
reg_t reg_t::operator|(int16 right) const {
return *this | make_reg(0, right);
}
reg_t reg_t::operator^(int16 right) const {
return *this ^ make_reg(0, right);
}
#endif
int reg_t::cmp(const reg_t right, bool treatAsUnsigned) const { int reg_t::cmp(const reg_t right, bool treatAsUnsigned) const {
if (getSegment() == right.getSegment()) { // can compare things in the same segment if (getSegment() == right.getSegment()) { // can compare things in the same segment
if (treatAsUnsigned || !isNumber()) if (treatAsUnsigned || !isNumber())

View file

@ -136,6 +136,19 @@ struct reg_t {
reg_t operator|(const reg_t right) const; reg_t operator|(const reg_t right) const;
reg_t operator^(const reg_t right) const; reg_t operator^(const reg_t right) const;
#ifdef ENABLE_SCI32
reg_t operator&(int16 right) const;
reg_t operator|(int16 right) const;
reg_t operator^(int16 right) const;
void operator&=(const reg_t &right) { *this = *this & right; }
void operator|=(const reg_t &right) { *this = *this | right; }
void operator^=(const reg_t &right) { *this = *this ^ right; }
void operator&=(int16 right) { *this = *this & right; }
void operator|=(int16 right) { *this = *this | right; }
void operator^=(int16 right) { *this = *this ^ right; }
#endif
private: private:
/** /**
* Compares two reg_t's. * Compares two reg_t's.

View file

@ -0,0 +1,988 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "sci/resource.h"
#include "sci/engine/seg_manager.h"
#include "sci/engine/state.h"
#include "sci/graphics/celobj32.h"
#include "sci/graphics/frameout.h"
#include "sci/graphics/palette32.h"
#include "sci/graphics/picture.h"
#include "sci/graphics/view.h"
namespace Sci {
#pragma mark CelScaler
CelScaler *CelObj::_scaler = nullptr;
void CelScaler::activateScaleTables(const Ratio &scaleX, const Ratio &scaleY) {
const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth;
const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight;
for (int i = 0; i < ARRAYSIZE(_scaleTables); ++i) {
if (_scaleTables[i].scaleX == scaleX && _scaleTables[i].scaleY == scaleY) {
_activeIndex = i;
return;
}
}
int i = 1 - _activeIndex;
_activeIndex = i;
CelScalerTable &table = _scaleTables[i];
if (table.scaleX != scaleX) {
assert(screenWidth <= ARRAYSIZE(table.valuesX));
buildLookupTable(table.valuesX, scaleX, screenWidth);
}
if (table.scaleY != scaleY) {
assert(screenHeight <= ARRAYSIZE(table.valuesY));
buildLookupTable(table.valuesY, scaleY, screenHeight);
}
}
void CelScaler::buildLookupTable(int *table, const Ratio &ratio, const int size) {
int value = 0;
int remainder = 0;
int num = ratio.getNumerator();
for (int i = 0; i < size; ++i) {
*table++ = value;
remainder += ratio.getDenominator();
if (remainder >= num) {
value += remainder / num;
remainder %= num;
}
}
}
const CelScalerTable *CelScaler::getScalerTable(const Ratio &scaleX, const Ratio &scaleY) {
activateScaleTables(scaleX, scaleY);
return &_scaleTables[_activeIndex];
}
#pragma mark -
#pragma mark CelObj
void CelObj::init() {
_nextCacheId = 1;
delete _scaler;
_scaler = new CelScaler();
delete _cache;
_cache = new CelCache;
_cache->resize(100);
}
void CelObj::deinit() {
delete _scaler;
_scaler = nullptr;
delete _cache;
_cache = nullptr;
}
void CelObj::draw(Buffer &target, const ScreenItem &screenItem, const Common::Rect &targetRect) const {
const Buffer &priorityMap = g_sci->_gfxFrameout->getPriorityMap();
const Common::Point &scaledPosition = screenItem._scaledPosition;
const Ratio &scaleX = screenItem._ratioX;
const Ratio &scaleY = screenItem._ratioY;
if (_remap) {
if (g_sci->_gfxFrameout->_hasRemappedScreenItem) {
const uint8 priority = MAX((uint8)0, MIN((uint8)255, (uint8)screenItem._priority));
// NOTE: In the original engine code, there was a second branch for
// _remap here that would then call the following functions if _remap was false:
//
// drawHzFlip(Buffer &, Buffer &, Common::Rect &, Common::Point &, uint8)
// drawNoFlip(Buffer &, Buffer &, Common::Rect &, Common::Point &, uint8)
// drawUncompHzFlip(Buffer &, Buffer &, Common::Rect &, Common::Point &, uint8)
// drawUncompNoFlip(Buffer &, Buffer &, Common::Rect &, Common::Point &, uint8)
// scaleDraw(Buffer &, Buffer &, Ratio &, Ratio &, Common::Rect &, Common::Point &, uint8)
// scaleDrawUncomp(Buffer &, Buffer &, Ratio &, Ratio &, Common::Rect &, Common::Point &, uint8)
//
// However, obviously, _remap cannot be false here. This dead code branch existed in
// at least SCI2/GK1 and SCI2.1/SQ6.
if (scaleX.isOne() && scaleY.isOne()) {
if (_compressionType == kCelCompressionNone) {
if (_drawMirrored) {
drawUncompHzFlipMap(target, priorityMap, targetRect, scaledPosition, priority);
} else {
drawUncompNoFlipMap(target, priorityMap, targetRect, scaledPosition, priority);
}
} else {
if (_drawMirrored) {
drawHzFlipMap(target, priorityMap, targetRect, scaledPosition, priority);
} else {
drawNoFlipMap(target, priorityMap, targetRect, scaledPosition, priority);
}
}
} else {
if (_compressionType == kCelCompressionNone) {
scaleDrawUncompMap(target, priorityMap, scaleX, scaleY, targetRect, scaledPosition, priority);
} else {
scaleDrawMap(target, priorityMap, scaleX, scaleY, targetRect, scaledPosition, priority);
}
}
} else {
// NOTE: In the original code this check was `g_Remap_numActiveRemaps && _remap`,
// but since we are already in a `_remap` branch, there is no reason to check it
// again
if (/* TODO: g_Remap_numActiveRemaps */ false) {
if (scaleX.isOne() && scaleY.isOne()) {
if (_compressionType == kCelCompressionNone) {
if (_drawMirrored) {
drawUncompHzFlipMap(target, targetRect, scaledPosition);
} else {
drawUncompNoFlipMap(target, targetRect, scaledPosition);
}
} else {
if (_drawMirrored) {
drawHzFlipMap(target, targetRect, scaledPosition);
} else {
drawNoFlipMap(target, targetRect, scaledPosition);
}
}
} else {
if (_compressionType == kCelCompressionNone) {
scaleDrawUncompMap(target, scaleX, scaleY, targetRect, scaledPosition);
} else {
scaleDrawMap(target, scaleX, scaleY, targetRect, scaledPosition);
}
}
} else {
if (scaleX.isOne() && scaleY.isOne()) {
if (_compressionType == kCelCompressionNone) {
if (_drawMirrored) {
drawUncompHzFlip(target, targetRect, scaledPosition);
} else {
drawUncompNoFlip(target, targetRect, scaledPosition);
}
} else {
if (_drawMirrored) {
drawHzFlip(target, targetRect, scaledPosition);
} else {
drawNoFlip(target, targetRect, scaledPosition);
}
}
} else {
if (_compressionType == kCelCompressionNone) {
scaleDrawUncomp(target, scaleX, scaleY, targetRect, scaledPosition);
} else {
scaleDraw(target, scaleX, scaleY, targetRect, scaledPosition);
}
}
}
}
} else {
if (g_sci->_gfxFrameout->_hasRemappedScreenItem) {
const uint8 priority = MAX((uint8)0, MIN((uint8)255, (uint8)screenItem._priority));
if (scaleX.isOne() && scaleY.isOne()) {
if (_compressionType == kCelCompressionNone) {
if (_drawMirrored) {
drawUncompHzFlipNoMD(target, priorityMap, targetRect, scaledPosition, priority);
} else {
drawUncompNoFlipNoMD(target, priorityMap, targetRect, scaledPosition, priority);
}
} else {
if (_drawMirrored) {
drawHzFlipNoMD(target, priorityMap, targetRect, scaledPosition, priority);
} else {
drawNoFlipNoMD(target, priorityMap, targetRect, scaledPosition, priority);
}
}
} else {
if (_compressionType == kCelCompressionNone) {
scaleDrawUncompNoMD(target, priorityMap, scaleX, scaleY, targetRect, scaledPosition, priority);
} else {
scaleDrawNoMD(target, priorityMap, scaleX, scaleY, targetRect, scaledPosition, priority);
}
}
} else {
if (scaleX.isOne() && scaleY.isOne()) {
if (_compressionType == kCelCompressionNone) {
if (_transparent) {
if (_drawMirrored) {
drawUncompHzFlipNoMD(target, targetRect, scaledPosition);
} else {
drawUncompNoFlipNoMD(target, targetRect, scaledPosition);
}
} else {
if (_drawMirrored) {
drawUncompHzFlipNoMDNoSkip(target, targetRect, scaledPosition);
} else {
drawUncompNoFlipNoMDNoSkip(target, targetRect, scaledPosition);
}
}
} else {
if (_drawMirrored) {
drawHzFlipNoMD(target, targetRect, scaledPosition);
} else {
drawNoFlipNoMD(target, targetRect, scaledPosition);
}
}
} else {
if (_compressionType == kCelCompressionNone) {
scaleDrawUncompNoMD(target, scaleX, scaleY, targetRect, scaledPosition);
} else {
scaleDrawNoMD(target, scaleX, scaleY, targetRect, scaledPosition);
}
}
}
}
}
void CelObj::draw(Buffer &target, const ScreenItem &screenItem, const Common::Rect &targetRect, bool mirrorX) {
_drawMirrored = mirrorX;
draw(target, screenItem, targetRect);
}
void CelObj::draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, const bool mirrorX) {
_drawMirrored = mirrorX;
Ratio square;
drawTo(target, targetRect, scaledPosition, square, square);
}
void CelObj::drawTo(Buffer &target, Common::Rect const &targetRect, Common::Point const &scaledPosition, Ratio const &scaleX, Ratio const &scaleY) const {
if (_remap) {
if (scaleX.isOne() && scaleY.isOne()) {
if (_compressionType == kCelCompressionNone) {
if (_drawMirrored) {
drawUncompHzFlipMap(target, targetRect, scaledPosition);
} else {
drawUncompNoFlipMap(target, targetRect, scaledPosition);
}
} else {
if (_drawMirrored) {
drawHzFlipMap(target, targetRect, scaledPosition);
} else {
drawNoFlipMap(target, targetRect, scaledPosition);
}
}
} else {
if (_compressionType == kCelCompressionNone) {
scaleDrawUncompMap(target, scaleX, scaleY, targetRect, scaledPosition);
} else {
scaleDrawMap(target, scaleX, scaleY, targetRect, scaledPosition);
}
}
} else {
if (scaleX.isOne() && scaleY.isOne()) {
if (_compressionType == kCelCompressionNone) {
if (_drawMirrored) {
drawUncompHzFlipNoMD(target, targetRect, scaledPosition);
} else {
drawUncompNoFlipNoMD(target, targetRect, scaledPosition);
}
} else {
if (_drawMirrored) {
drawHzFlipNoMD(target, targetRect, scaledPosition);
} else {
drawNoFlipNoMD(target, targetRect, scaledPosition);
}
}
} else {
if (_compressionType == kCelCompressionNone) {
scaleDrawUncompNoMD(target, scaleX, scaleY, targetRect, scaledPosition);
} else {
scaleDrawNoMD(target, scaleX, scaleY, targetRect, scaledPosition);
}
}
}
}
uint8 CelObj::readPixel(uint16 x, const uint16 y, bool mirrorX) const {
byte *resource = getResPointer();
byte *celHeader = resource + _celHeaderOffset;
if (mirrorX) {
x = _width - x - 1;
}
if (_compressionType == kCelCompressionNone) {
byte *pixels = resource + READ_SCI11ENDIAN_UINT32(celHeader + 24);
return pixels[y * _width + x];
} else {
byte buffer[1024];
uint32 dataOffset = READ_SCI11ENDIAN_UINT32(celHeader + 24);
uint32 uncompressedDataOffset = READ_SCI11ENDIAN_UINT32(celHeader + 28);
uint32 controlOffset = READ_SCI11ENDIAN_UINT32(celHeader + 32);
// compressed data segment for row
byte *row = resource + dataOffset + READ_SCI11ENDIAN_UINT32(resource + controlOffset + y * 4);
// uncompressed data segment for row
byte *literal = resource + uncompressedDataOffset + READ_SCI11ENDIAN_UINT32(resource + controlOffset + _height * 4 + y * 4);
uint8 length;
byte controlByte;
for (uint i = 0; i <= x; i += length) {
controlByte = *row++;
length = controlByte;
// Run-length encoded
if (controlByte & 0x80) {
length &= 0x3F;
assert(i + length < sizeof(buffer));
// Fill with skip color
if (controlByte & 0x40) {
memset(buffer + i, _transparentColor, length);
// Next value is fill colour
} else {
memset(buffer + i, *literal, length);
++literal;
}
// Uncompressed
} else {
assert(i + length < sizeof(buffer));
memcpy(buffer + i, literal, length);
literal += length;
}
}
return buffer[x];
}
}
void CelObj::submitPalette() const {
if (_hunkPaletteOffset) {
Palette palette;
byte *res = getResPointer();
// NOTE: In SCI engine this uses HunkPalette::Init.
// TODO: Use a better size value
g_sci->_gfxPalette32->createFromData(res + _hunkPaletteOffset, 999999, &palette);
g_sci->_gfxPalette32->submit(palette);
}
}
#pragma mark -
#pragma mark CelObj - Caching
int CelObj::_nextCacheId = 1;
CelCache *CelObj::_cache = nullptr;
int CelObj::searchCache(const CelInfo32 &celInfo, int *nextInsertIndex) const {
int oldestId = _nextCacheId + 1;
int oldestIndex = -1;
for (int i = 0, len = _cache->size(); i < len; ++i) {
CelCacheEntry &entry = (*_cache)[i];
if (entry.celObj != nullptr) {
if (entry.celObj->_info == celInfo) {
entry.id = ++_nextCacheId;
return i;
}
if (oldestId > entry.id) {
oldestId = entry.id;
oldestIndex = i;
}
} else if (oldestIndex == -1) {
oldestIndex = i;
}
}
// NOTE: Unlike the original SCI engine code, the out-param
// here is only updated if there was not a cache hit.
*nextInsertIndex = oldestIndex;
return -1;
}
void CelObj::putCopyInCache(const int cacheIndex) const {
if (cacheIndex == -1) {
error("Invalid cache index");
}
CelCacheEntry &entry = (*_cache)[cacheIndex];
if (entry.celObj != nullptr) {
delete entry.celObj;
}
entry.celObj = duplicate();
entry.id = ++_nextCacheId;
}
#pragma mark -
#pragma mark CelObj - Drawing
void dummyFill(Buffer &target, const Common::Rect &targetRect) {
target.fillRect(targetRect, 250);
}
void CelObj::drawHzFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
debug("drawHzFlip");
dummyFill(target, targetRect);
}
void CelObj::drawNoFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
debug("drawNoFlip");
dummyFill(target, targetRect);
}
void CelObj::drawUncompNoFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
debug("drawUncompNoFlip");
dummyFill(target, targetRect);
}
void CelObj::drawUncompHzFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
debug("drawUncompHzFlip");
dummyFill(target, targetRect);
}
void CelObj::scaleDraw(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
debug("scaleDraw");
dummyFill(target, targetRect);
}
void CelObj::scaleDrawUncomp(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
debug("scaleDrawUncomp");
dummyFill(target, targetRect);
}
void CelObj::drawHzFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
debug("drawHzFlipMap");
dummyFill(target, targetRect);
}
void CelObj::drawNoFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
debug("drawNoFlipMap");
dummyFill(target, targetRect);
}
void CelObj::drawUncompNoFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
debug("drawUncompNoFlipMap");
dummyFill(target, targetRect);
}
void CelObj::drawUncompHzFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
debug("drawUncompHzFlipMap");
dummyFill(target, targetRect);
}
void CelObj::scaleDrawMap(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
debug("scaleDrawMap");
dummyFill(target, targetRect);
}
void CelObj::scaleDrawUncompMap(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
debug("scaleDrawUncompMap");
dummyFill(target, targetRect);
}
void CelObj::drawHzFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
debug("drawHzFlipNoMD");
dummyFill(target, targetRect);
}
void CelObj::drawNoFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
const int sourceX = targetRect.left - scaledPosition.x;
const int sourceY = targetRect.top - scaledPosition.y;
byte *targetPixel = (byte *)target.getPixels() + (targetRect.top * target.screenWidth) + targetRect.left;
const int stride = target.screenWidth - targetRect.width();
byte *resource = getResPointer();
byte *celHeader = resource + _celHeaderOffset;
byte buffer[1024];
uint32 dataOffset = READ_SCI11ENDIAN_UINT32(celHeader + 24);
uint32 uncompressedDataOffset = READ_SCI11ENDIAN_UINT32(celHeader + 28);
uint32 controlOffset = READ_SCI11ENDIAN_UINT32(celHeader + 32);
for (int y = sourceY; y < sourceY + targetRect.height(); ++y) {
// compressed data segment for row
byte *row = resource + dataOffset + READ_SCI11ENDIAN_UINT32(resource + controlOffset + y * 4);
// uncompressed data segment for row
byte *literal = resource + uncompressedDataOffset + READ_SCI11ENDIAN_UINT32(resource + controlOffset + _height * 4 + y * 4);
uint8 length;
byte controlByte;
for (int i = 0; i <= targetRect.width(); i += length) {
controlByte = *row++;
length = controlByte;
// Run-length encoded
if (controlByte & 0x80) {
length &= 0x3F;
assert(i + length < (int)sizeof(buffer));
// Fill with skip color
if (controlByte & 0x40) {
memset(buffer + i, _transparentColor, length);
// Next value is fill colour
} else {
memset(buffer + i, *literal, length);
++literal;
}
// Uncompressed
} else {
assert(i + length < (int)sizeof(buffer));
memcpy(buffer + i, literal, length);
literal += length;
}
}
for (int x = 0; x < targetRect.width(); ++x) {
byte pixel = buffer[sourceX + x];
if (pixel != _transparentColor) {
*targetPixel = pixel;
}
++targetPixel;
}
targetPixel += stride;
}
}
void CelObj::drawUncompNoFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
const int sourceX = targetRect.left - scaledPosition.x;
const int sourceY = targetRect.top - scaledPosition.y;
const int sourceStride = _width - targetRect.width();
const int targetStride = target.screenWidth - targetRect.width();
const int dataOffset = READ_SCI11ENDIAN_UINT32(getResPointer() + _celHeaderOffset + 24);
byte *sourcePixel = getResPointer() + dataOffset + (sourceY * _width) + sourceX;
byte *targetPixel = (byte *)target.getPixels() + targetRect.top * target.screenWidth + targetRect.left;
for (int y = 0; y < targetRect.height(); ++y) {
for (int x = 0; x < targetRect.width(); ++x) {
byte pixel = *sourcePixel++;
if (pixel != _transparentColor) {
*targetPixel = pixel;
}
++targetPixel;
}
sourcePixel += sourceStride;
targetPixel += targetStride;
}
}
void CelObj::drawUncompNoFlipNoMDNoSkip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
const int sourceX = targetRect.left - scaledPosition.x;
const int sourceY = targetRect.top - scaledPosition.y;
const int dataOffset = READ_SCI11ENDIAN_UINT32(getResPointer() + _celHeaderOffset + 24);
byte *sourcePixel = getResPointer() + dataOffset + (sourceY * _width) + sourceX;
target.copyRectToSurface(sourcePixel, _width, targetRect.left, targetRect.top, targetRect.width(), targetRect.height());
}
void CelObj::drawUncompHzFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
debug("drawUncompHzFlipNoMD");
dummyFill(target, targetRect);
}
void CelObj::drawUncompHzFlipNoMDNoSkip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
debug("drawUncompHzFlipNoMDNoSkip");
dummyFill(target, targetRect);
}
void CelObj::scaleDrawNoMD(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
debug("scaleDrawNoMD %d/%d, %d/%d", scaleX.getNumerator(), scaleX.getDenominator(), scaleY.getNumerator(), scaleY.getDenominator());
dummyFill(target, targetRect);
}
void CelObj::scaleDrawUncompNoMD(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
debug("scaleDrawUncompNoMD %d/%d, %d/%d", scaleX.getNumerator(), scaleX.getDenominator(), scaleY.getNumerator(), scaleY.getDenominator());
if (targetRect.isEmpty()) {
return;
}
const CelScalerTable *table = _scaler->getScalerTable(scaleX, scaleY);
int pixelX[1024];
int pixelY[1024];
bool use2xOptimisedDrawRoutine = false /* TODO: scaleX.getDenominator() * 2 == scaleX.getNumerator() */;
int16 sourceX = (scaledPosition.x * scaleX.getInverse()).toInt();
int16 sourceY = (scaledPosition.y * scaleY.getInverse()).toInt();
if (_drawMirrored) {
for (int x = targetRect.left; x < targetRect.right; ++x) {
pixelX[x] = _width - 1 - (table->valuesX[x] - sourceX);
}
} else {
for (int x = targetRect.left; x < targetRect.right; ++x) {
pixelX[x] = table->valuesX[x] - sourceX;
}
}
for (int y = targetRect.top; y < targetRect.bottom; ++y) {
pixelY[y] = table->valuesY[y] - sourceY;
}
byte *sourcePixels = getResPointer() + READ_SCI11ENDIAN_UINT32(getResPointer() + _celHeaderOffset + 24);
for (int y = targetRect.top; y < targetRect.bottom; ++y) {
byte *targetPixel = target.getAddress(targetRect.left, y);
byte *sourcePixel = sourcePixels + pixelY[y] * _width;
const int *sourcePixelIndex = pixelX + targetRect.left;
if (/* TODO */ use2xOptimisedDrawRoutine) {
// WriteUncompScaleLine2();
} else {
// start WriteUncompScaleLine
for (int x = targetRect.left; x < targetRect.right; ++x) {
byte value = sourcePixel[*sourcePixelIndex++];
if (value != _transparentColor) {
*targetPixel = value;
}
++targetPixel;
}
// end WriteUncompScaleLine
}
}
}
// TODO: These functions may all be vestigial.
void CelObj::drawHzFlipMap(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {}
void CelObj::drawNoFlipMap(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {}
void CelObj::drawUncompNoFlipMap(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {}
void CelObj::drawUncompHzFlipMap(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {}
void CelObj::scaleDrawMap(Buffer &target, const Buffer &priorityMap, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {}
void CelObj::scaleDrawUncompMap(Buffer &target, const Buffer &priorityMap, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {}
void CelObj::drawHzFlipNoMD(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {}
void CelObj::drawNoFlipNoMD(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {}
void CelObj::drawUncompNoFlipNoMD(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {}
void CelObj::drawUncompHzFlipNoMD(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {}
void CelObj::scaleDrawNoMD(Buffer &target, const Buffer &priorityMap, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {}
void CelObj::scaleDrawUncompNoMD(Buffer &target, const Buffer &priorityMap, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {}
#pragma mark -
#pragma mark CelObjView
CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int16 celNo) {
_info.type = kCelTypeView;
_info.resourceId = viewId;
_info.loopNo = loopNo;
_info.celNo = celNo;
_mirrorX = false;
_compressionType = kCelCompressionInvalid;
_transparent = true;
int cacheInsertIndex;
int cacheIndex = searchCache(_info, &cacheInsertIndex);
if (cacheIndex != -1) {
CelCacheEntry &entry = (*_cache)[cacheIndex];
*this = *dynamic_cast<CelObjView *>(entry.celObj);
entry.id = ++_nextCacheId;
return;
}
// TODO: The next code should be moved to a common file that
// generates view resource metadata for both SCI16 and SCI32
// implementations
Resource *resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, viewId), false);
// NOTE: SCI2.1/SQ6 just silently returns here.
if (!resource) {
warning("View resource %d not loaded", viewId);
return;
}
byte *data = resource->data;
_scaledWidth = READ_SCI11ENDIAN_UINT16(data + 14);
_scaledHeight = READ_SCI11ENDIAN_UINT16(data + 16);
if (_scaledWidth == 0 || _scaledHeight == 0) {
byte sizeFlag = data[5];
if (sizeFlag == 0) {
_scaledWidth = 320;
_scaledHeight = 200;
} else if (sizeFlag == 1) {
_scaledWidth = 640;
_scaledHeight = 480;
} else if (sizeFlag == 2) {
_scaledWidth = 640;
_scaledHeight = 400;
}
}
uint16 loopCount = data[2];
if (_info.loopNo >= loopCount) {
_info.loopNo = loopCount - 1;
}
// NOTE: This is the actual check, in the actual location,
// from SCI engine.
if (loopNo < 0) {
error("Loop is less than 0!");
}
const uint16 viewHeaderSize = READ_SCI11ENDIAN_UINT16(data);
const uint8 loopHeaderSize = data[12];
const uint8 viewHeaderFieldSize = 2;
byte *loopHeader = data + viewHeaderFieldSize + viewHeaderSize + (loopHeaderSize * _info.loopNo);
if ((int8)loopHeader[0] != -1) {
if (loopHeader[1] == 1) {
_mirrorX = true;
}
loopHeader = data + viewHeaderFieldSize + viewHeaderSize + (loopHeaderSize * (int8)loopHeader[0]);
}
uint8 celCount = loopHeader[2];
if (_info.celNo >= celCount) {
_info.celNo = celCount - 1;
}
_hunkPaletteOffset = READ_SCI11ENDIAN_UINT32(data + 8);
_celHeaderOffset = READ_SCI11ENDIAN_UINT32(loopHeader + 12) + (data[13] * _info.celNo);
byte *celHeader = data + _celHeaderOffset;
_width = READ_SCI11ENDIAN_UINT16(celHeader);
_height = READ_SCI11ENDIAN_UINT16(celHeader + 2);
_displace.x = _width / 2 - (int16)READ_SCI11ENDIAN_UINT16(celHeader + 4);
_displace.y = _height - (int16)READ_SCI11ENDIAN_UINT16(celHeader + 6) - 1;
_transparentColor = celHeader[8];
_compressionType = (CelCompressionType)celHeader[9];
if (_compressionType != kCelCompressionNone && _compressionType != kCelCompressionRLE) {
error("Compression type not supported - V: %d L: %d C: %d", _info.resourceId, _info.loopNo, _info.celNo);
}
if (celHeader[10] & 128) {
// NOTE: This is correct according to SCI2.1/SQ6/DOS;
// the engine re-reads the byte value as a word value
uint16 flags = READ_SCI11ENDIAN_UINT16(celHeader + 10);
_transparent = flags & 1 ? true : false;
_remap = flags & 2 ? true : false;
} else if (_compressionType == kCelCompressionNone) {
_remap = analyzeUncompressedForRemap();
} else {
_remap = analyzeForRemap();
}
putCopyInCache(cacheInsertIndex);
}
bool CelObjView::analyzeUncompressedForRemap() const {
byte *pixels = getResPointer() + READ_SCI11ENDIAN_UINT32(getResPointer() + _celHeaderOffset + 24);
for (int i = 0; i < _width * _height; ++i) {
uint8 pixel = pixels[i];
if (/* TODO: pixel >= Remap::minRemapColor && pixel <= Remap::maxRemapColor */ false && pixel != _transparentColor) {
return true;
}
}
return false;
}
bool CelObjView::analyzeForRemap() const {
// TODO: Implement decompression and analysis
return false;
}
void CelObjView::draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, bool mirrorX, const Ratio &scaleX, const Ratio &scaleY) {
_drawMirrored = mirrorX;
drawTo(target, targetRect, scaledPosition, scaleX, scaleY);
}
CelObjView *CelObjView::duplicate() const {
return new CelObjView(*this);
}
byte *CelObjView::getResPointer() const {
return g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, _info.resourceId), false)->data;
}
#pragma mark -
#pragma mark CelObjPic
CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) {
_info.type = kCelTypePic;
_info.resourceId = picId;
_info.loopNo = 0;
_info.celNo = celNo;
_mirrorX = false;
_compressionType = kCelCompressionInvalid;
_transparent = true;
_remap = false;
int cacheInsertIndex;
int cacheIndex = searchCache(_info, &cacheInsertIndex);
if (cacheIndex != -1) {
CelCacheEntry &entry = (*_cache)[cacheIndex];
*this = *dynamic_cast<CelObjPic *>(entry.celObj);
entry.id = ++_nextCacheId;
return;
}
Resource *resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypePic, picId), false);
// NOTE: SCI2.1/SQ6 just silently returns here.
if (!resource) {
warning("Pic resource %d not loaded", picId);
return;
}
byte *data = resource->data;
_celCount = data[2];
if (_info.celNo >= _celCount) {
error("Cel number %d greater than cel count %d", _info.celNo, _celCount);
}
_celHeaderOffset = READ_SCI11ENDIAN_UINT16(data) + (READ_SCI11ENDIAN_UINT16(data + 4) * _info.celNo);
_hunkPaletteOffset = READ_SCI11ENDIAN_UINT32(data + 6);
byte *celHeader = data + _celHeaderOffset;
_width = READ_SCI11ENDIAN_UINT16(celHeader);
_height = READ_SCI11ENDIAN_UINT16(celHeader + 2);
_displace.x = (int16)READ_SCI11ENDIAN_UINT16(celHeader + 4);
_displace.y = (int16)READ_SCI11ENDIAN_UINT16(celHeader + 6);
_transparentColor = celHeader[8];
_compressionType = (CelCompressionType)celHeader[9];
_priority = READ_SCI11ENDIAN_UINT16(celHeader + 36);
_relativePosition.x = (int16)READ_SCI11ENDIAN_UINT16(celHeader + 38);
_relativePosition.y = (int16)READ_SCI11ENDIAN_UINT16(celHeader + 40);
uint16 sizeFlag1 = READ_SCI11ENDIAN_UINT16(data + 10);
uint16 sizeFlag2 = READ_SCI11ENDIAN_UINT16(data + 12);
if (sizeFlag2) {
_scaledWidth = sizeFlag1;
_scaledHeight = sizeFlag2;
} else if (sizeFlag1 == 0) {
_scaledWidth = 320;
_scaledHeight = 200;
} else if (sizeFlag1 == 1) {
_scaledWidth = 640;
_scaledHeight = 480;
} else if (sizeFlag1 == 2) {
_scaledWidth = 640;
_scaledHeight = 400;
}
if (celHeader[10] & 128) {
// NOTE: This is correct according to SCI2.1/SQ6/DOS;
// the engine re-reads the byte value as a word value
uint16 flags = READ_SCI11ENDIAN_UINT16(celHeader + 10);
_transparent = flags & 1 ? true : false;
_remap = flags & 2 ? true : false;
} else {
_transparent = _compressionType != kCelCompressionNone ? true : analyzeUncompressedForSkip();
if (_compressionType != kCelCompressionNone && _compressionType != kCelCompressionRLE) {
error("Compression type not supported - P: %d C: %d", picId, celNo);
}
}
putCopyInCache(cacheInsertIndex);
}
bool CelObjPic::analyzeUncompressedForSkip() const {
byte *resource = getResPointer();
byte *pixels = resource + READ_SCI11ENDIAN_UINT32(resource + _celHeaderOffset + 24);
for (int i = 0; i < _width * _height; ++i) {
uint8 pixel = pixels[i];
if (pixel == _transparentColor) {
return true;
}
}
return false;
}
void CelObjPic::draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, const bool mirrorX) {
Ratio square;
_drawMirrored = mirrorX;
drawTo(target, targetRect, scaledPosition, square, square);
}
CelObjPic *CelObjPic::duplicate() const {
return new CelObjPic(*this);
}
byte *CelObjPic::getResPointer() const {
return g_sci->getResMan()->findResource(ResourceId(kResourceTypePic, _info.resourceId), false)->data;
}
#pragma mark -
#pragma mark CelObjMem
CelObjMem::CelObjMem(const reg_t bitmap) {
_info.type = kCelTypeMem;
_info.bitmap = bitmap;
_mirrorX = false;
_compressionType = kCelCompressionNone;
_celHeaderOffset = 0;
_transparent = true;
byte *bitmapData = g_sci->getEngineState()->_segMan->getHunkPointer(bitmap);
if (bitmapData == nullptr || READ_SCI11ENDIAN_UINT32(bitmapData + 28) != 46) {
error("Invalid Text bitmap %04x:%04x", PRINT_REG(bitmap));
}
_width = READ_SCI11ENDIAN_UINT16(bitmapData);
_height = READ_SCI11ENDIAN_UINT16(bitmapData + 2);
_displace.x = READ_SCI11ENDIAN_UINT16(bitmapData + 4);
_displace.y = READ_SCI11ENDIAN_UINT16(bitmapData + 6);
_transparentColor = bitmapData[8];
_scaledWidth = READ_SCI11ENDIAN_UINT16(bitmapData + 36);
_scaledHeight = READ_SCI11ENDIAN_UINT16(bitmapData + 38);
_hunkPaletteOffset = READ_SCI11ENDIAN_UINT16(bitmapData + 20);
_remap = (READ_SCI11ENDIAN_UINT16(bitmapData + 10) & 2) ? true : false;
}
CelObjMem *CelObjMem::duplicate() const {
return new CelObjMem(*this);
}
byte *CelObjMem::getResPointer() const {
return g_sci->getEngineState()->_segMan->getHunkPointer(_info.bitmap);
}
#pragma mark -
#pragma mark CelObjColor
CelObjColor::CelObjColor(const uint8 color, const int16 width, const int16 height) {
_info.type = kCelTypeColor;
_info.color = color;
_displace.x = 0;
_displace.y = 0;
_scaledWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
_scaledHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
_hunkPaletteOffset = 0;
_mirrorX = false;
_remap = false;
_width = width;
_height = height;
}
void CelObjColor::draw(Buffer &target, const ScreenItem &screenItem, const Common::Rect &targetRect, const bool mirrorX) {
// TODO: The original engine sets this flag but why? One cannot
// draw a solid colour mirrored.
_drawMirrored = mirrorX;
draw(target, targetRect);
}
void CelObjColor::draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, bool mirrorX) {
error("Unsupported method");
}
void CelObjColor::draw(Buffer &target, const Common::Rect &targetRect) const {
target.fillRect(targetRect, _info.color);
}
CelObjColor *CelObjColor::duplicate() const {
return new CelObjColor(*this);
}
byte *CelObjColor::getResPointer() const {
error("Unsupported method");
}
}

View file

@ -0,0 +1,577 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef SCI_GRAPHICS_CELOBJ32_H
#define SCI_GRAPHICS_CELOBJ32_H
#include "common/rational.h"
#include "common/rect.h"
#include "sci/resource.h"
#include "sci/engine/vm_types.h"
namespace Sci {
typedef Common::Rational Ratio;
enum CelType {
kCelTypeView = 0,
kCelTypePic = 1,
kCelTypeMem = 2,
kCelTypeColor = 3
};
enum CelCompressionType {
kCelCompressionNone = 0,
kCelCompressionRLE = 138,
kCelCompressionInvalid = 1000
};
/**
* A CelInfo32 object describes the basic properties of a
* cel object.
*/
struct CelInfo32 {
/**
* The type of the cel object.
*/
CelType type;
/**
* For cel objects that draw from resources, the ID of
* the resource to load.
*/
GuiResourceId resourceId;
/**
* For CelObjView, the loop number to draw from the
* view resource.
*/
int16 loopNo;
/**
* For CelObjView and CelObjPic, the cel number to draw
* from the view or pic resource.
*/
int16 celNo;
/**
* For CelObjMem, a segment register pointing to a heap
* resource containing headered bitmap data.
*/
reg_t bitmap;
/**
* For CelObjColor, the fill colour.
*/
uint8 color;
// NOTE: In at least SCI2.1/SQ6, color is left
// uninitialised.
CelInfo32() :
type(kCelTypeMem),
resourceId(0),
loopNo(0),
celNo(0),
bitmap(NULL_REG) {}
// NOTE: This is the equivalence criteria used by
// CelObj::searchCache in at least SCI2.1/SQ6. Notably,
// it does not check the color field.
inline bool operator==(const CelInfo32 &other) {
return (
type == other.type &&
resourceId == other.resourceId &&
loopNo == other.loopNo &&
celNo == other.celNo &&
bitmap == other.bitmap
);
}
};
class CelObj;
struct CelCacheEntry {
/**
* A monotonically increasing cache ID used to identify
* the least recently used item in the cache for
* replacement.
*/
int id;
CelObj *celObj;
CelCacheEntry() : id(0), celObj(nullptr) {}
};
typedef Common::Array<CelCacheEntry> CelCache;
#pragma mark -
#pragma mark CelScaler
struct CelScalerTable {
/**
* A lookup table of indexes that should be used to find
* the correct column to read from the source bitmap
* when drawing a scaled version of the source bitmap.
*/
int valuesX[1024];
/**
* The ratio used to generate the x-values.
*/
Ratio scaleX;
/**
* A lookup table of indexes that should be used to find
* the correct row to read from a source bitmap when
* drawing a scaled version of the source bitmap.
*/
int valuesY[1024];
/**
* The ratio used to generate the y-values.
*/
Ratio scaleY;
};
class CelScaler {
/**
* Cached scale tables.
*/
CelScalerTable _scaleTables[2];
/**
* The index of the most recently used scale table.
*/
int _activeIndex;
/**
* Activates a scale table for the given X and Y ratios.
* If there is no table that matches the given ratios,
* the least most recently used table will be replaced
* and activated.
*/
void activateScaleTables(const Ratio &scaleX, const Ratio &scaleY);
/**
* Builds a pixel lookup table in `table` for the given
* ratio. The table will be filled up to the specified
* size, which should be large enough to draw across the
* entire target buffer.
*/
void buildLookupTable(int *table, const Ratio &ratio, const int size);
public:
CelScaler() :
_scaleTables(),
_activeIndex(0) {
CelScalerTable &table = _scaleTables[_activeIndex];
table.scaleX = Ratio();
table.scaleY = Ratio();
for (int i = 0; i < ARRAYSIZE(table.valuesX); ++i) {
table.valuesX[i] = i;
table.valuesY[i] = i;
}
}
/**
* Retrieves scaler tables for the given X and Y ratios.
*/
const CelScalerTable *getScalerTable(const Ratio &scaleX, const Ratio &scaleY);
};
#pragma mark -
#pragma mark CelObj
class ScreenItem;
/**
* A cel object is the lowest-level rendering primitive in
* the SCI engine and draws itself directly to a target
* pixel buffer.
*/
class CelObj {
private:
static CelScaler *_scaler;
protected:
/**
* When true, this cel will be horizontally mirrored
* when it is drawn. This is an internal flag that is
* set by draw methods based on the combination of the
* cel's `_mirrorX` property and the owner screen item's
* `_mirrorX` property.
*/
bool _drawMirrored;
public:
/**
* The basic identifying information for this cel. This
* information effectively acts as a composite key for
* a cel object, and any cel object can be recreated
* from this data alone.
*/
CelInfo32 _info;
/**
* The offset to the cel header for this cel within the
* raw resource data.
*/
uint32 _celHeaderOffset;
/**
* The offset to the embedded palette for this cel
* within the raw resource data.
*/
uint32 _hunkPaletteOffset;
/**
* The natural dimensions of the cel.
*/
uint16 _width, _height;
/**
* TODO: Documentation
*/
Common::Point _displace;
/**
* The dimensions of the original coordinate system for
* the cel. Used to scale cels from their native size
* to the correct size on screen.
*
* @note This is set to scriptWidth/Height for
* CelObjColor. For other cel objects, the value comes
* from the raw resource data. For text bitmaps, this is
* the width/height of the coordinate system used to
* generate the text, which also defaults to
* scriptWidth/Height but seems to typically be changed
* to more closely match the native screen resolution.
*/
uint16 _scaledWidth, _scaledHeight;
/**
* The skip (transparent) colour for the cel. When
* compositing, any pixels matching this colour will not
* be copied to the buffer.
*/
uint8 _transparentColor;
/**
* Whether or not this cel has any transparent regions.
* This is used for optimised drawing of non-transparent
* cels.
*/
bool _transparent; // TODO: probably "skip"?
/**
* The compression type for the pixel data for this cel.
*/
CelCompressionType _compressionType;
/**
* Whether or not this cel should be palette-remapped?
*/
bool _remap;
/**
* If true, the cel contains pre-mirrored picture data.
* This value comes directly from the resource data and
* is XORed with the `_mirrorX` property of the owner
* screen item when rendering.
*/
bool _mirrorX;
/**
* Initialises static CelObj members.
*/
static void init();
/**
* Frees static CelObj members.
*/
static void deinit();
virtual ~CelObj() {};
/**
* Draws the cel to the target buffer using the priority
* and positioning information from the given screen
* item. The mirroring of the cel will be unchanged from
* any previous call to draw.
*/
void draw(Buffer &target, const ScreenItem &screenItem, const Common::Rect &targetRect) const;
/**
* Draws the cel to the target buffer using the priority
* and positioning information from the given screen
* item and the given mirror flag.
*
* @note In SCI engine, this function was a virtual
* function, but CelObjView, CelObjPic, and CelObjMem
* all used the same function and the compiler
* deduplicated the copies; we deduplicate the source by
* putting the implementation on CelObj instead of
* copying it to 3/4 of the subclasses.
*/
virtual void draw(Buffer &target, const ScreenItem &screenItem, const Common::Rect &targetRect, const bool mirrorX);
/**
* Draws the cel to the target buffer using the
* positioning and mirroring information from the
* provided arguments.
*
* @note In SCI engine, this function was a virtual
* function, but CelObjView, CelObjPic, and CelObjMem
* all used the same function and the compiler
* deduplicated the copies; we deduplicate the source by
* putting the implementation on CelObj instead of
* copying it to 3/4 of the subclasses.
*/
virtual void draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, const bool mirrorX);
/**
* Draws the cel to the target buffer using the given
* position and scaling parameters. The mirroring of the
* cel will be unchanged from any previous call to draw.
*/
void drawTo(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, const Ratio &scaleX, const Ratio &scaleY) const;
/**
* Creates a copy of this cel on the free store and
* returns a pointer to the new object. The new cel will
* point to a shared copy of bitmap/resource data.
*/
virtual CelObj *duplicate() const = 0;
/**
* Retrieves a pointer to the raw resource data for this
* cel. This method cannot be used with a CelObjColor.
*/
virtual byte *getResPointer() const = 0;
/**
* Reads the pixel at the given coordinates. This method
* is valid only for CelObjView and CelObjPic.
*/
inline uint8 readPixel(uint16 x, uint16 y, bool mirrorX) const;
/**
* Submits the palette from this cel to the palette
* manager for integration into the master screen
* palette.
*/
void submitPalette() const;
#pragma mark -
#pragma mark CelObj - Drawing
private:
void drawHzFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
void drawNoFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
void drawUncompNoFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
void drawUncompHzFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
void scaleDraw(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
void scaleDrawUncomp(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
void drawHzFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
void drawNoFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
void drawUncompNoFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
void drawUncompHzFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
void scaleDrawMap(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
void scaleDrawUncompMap(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
void drawHzFlipMap(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const;
void drawNoFlipMap(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const;
void drawUncompNoFlipMap(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const;
void drawUncompHzFlipMap(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const;
void scaleDrawMap(Buffer &target, const Buffer &priorityMap, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const;
void scaleDrawUncompMap(Buffer &target, const Buffer &priorityMap, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const;
void drawHzFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
void drawNoFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
void drawUncompNoFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
void drawUncompNoFlipNoMDNoSkip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
void drawUncompHzFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
void drawUncompHzFlipNoMDNoSkip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
void scaleDrawNoMD(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
void scaleDrawUncompNoMD(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
void drawHzFlipNoMD(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const;
void drawNoFlipNoMD(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const;
void drawUncompNoFlipNoMD(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const;
void drawUncompHzFlipNoMD(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const;
void scaleDrawNoMD(Buffer &target, const Buffer &priorityMap, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const;
void scaleDrawUncompNoMD(Buffer &target, const Buffer &priorityMap, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const;
#pragma mark -
#pragma mark CelObj - Caching
protected:
/**
* A monotonically increasing cache ID used to identify
* the least recently used item in the cache for
* replacement.
*/
static int _nextCacheId;
/**
* A cache of cel objects used to avoid reinitialisation
* overhead for cels with the same CelInfo32.
*/
// NOTE: At least SQ6 uses a fixed cache size of 100.
static CelCache *_cache;
/**
* Searches the cel cache for a CelObj matching the
* provided CelInfo32. If not found, -1 is returned.
* nextInsertIndex will receive the index of the oldest
* item in the cache, which can be used to replace
* the oldest item with a newer item.
*/
int searchCache(const CelInfo32 &celInfo, int *nextInsertIndex) const;
/**
* Puts a copy of this CelObj into the cache at the
* given cache index.
*/
void putCopyInCache(int index) const;
};
#pragma mark -
#pragma mark CelObjView
/**
* A CelObjView is the drawing primitive for a View type
* resource. Each CelObjView corresponds to a single cel
* within a single loop of a view.
*/
class CelObjView : public CelObj {
private:
/**
* Analyses resources without baked-in remap flags
* to determine whether or not they should be remapped.
*/
bool analyzeUncompressedForRemap() const;
/**
* Analyses compressed resources without baked-in remap
* flags to determine whether or not they should be
* remapped.
*/
bool analyzeForRemap() const;
public:
CelObjView(GuiResourceId viewId, int16 loopNo, int16 celNo);
virtual ~CelObjView() override {};
using CelObj::draw;
/**
* Draws the cel to the target buffer using the
* positioning, mirroring, and scaling information from
* the provided arguments.
*/
void draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, bool mirrorX, const Ratio &scaleX, const Ratio &scaleY);
virtual CelObjView *duplicate() const override;
virtual byte *getResPointer() const override;
};
#pragma mark -
#pragma mark CelObjPic
/**
* A CelObjPic is the drawing primitive for a Picture type
* resource. Each CelObjPic corresponds to a single cel
* within a picture.
*/
class CelObjPic : public CelObj {
private:
/**
* Analyses uncompressed resources without baked-in skip
* flags to determine whether or not they can use fast
* blitting.
*/
bool analyzeUncompressedForSkip() const;
public:
/**
* The number of cels in the original picture resource.
*/
uint8 _celCount;
/**
* The position of this cel relative to the top-left
* corner of the picture.
*/
Common::Point _relativePosition;
/**
* The z-buffer priority for this cel. Higher prorities
* are drawn on top of lower priorities.
*/
int16 _priority;
CelObjPic(GuiResourceId pictureId, int16 celNo);
virtual ~CelObjPic() override {};
using CelObj::draw;
virtual void draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, const bool mirrorX) override;
virtual CelObjPic *duplicate() const override;
virtual byte *getResPointer() const override;
};
#pragma mark -
#pragma mark CelObjMem
/**
* A CelObjMem is the drawing primitive for arbitrary
* bitmaps generated in memory. Generated bitmaps in SCI32
* include text & vector drawings and per-pixel screen
* transitions like dissolves.
*/
class CelObjMem : public CelObj {
public:
CelObjMem(reg_t bitmap);
virtual ~CelObjMem() override {};
virtual CelObjMem *duplicate() const override;
virtual byte *getResPointer() const override;
};
#pragma mark -
#pragma mark CelObjColor
/**
* A CelObjColor is the drawing primitive for fast,
* low-memory, flat colour fills.
*/
class CelObjColor : public CelObj {
public:
CelObjColor(uint8 color, int16 width, int16 height);
virtual ~CelObjColor() override {};
using CelObj::draw;
/**
* Block fills the target buffer with the cel colour.
*/
void draw(Buffer &target, const Common::Rect &targetRect) const;
virtual void draw(Buffer &target, const ScreenItem &screenItem, const Common::Rect &targetRect, bool mirrorX) override;
virtual void draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, bool mirrorX) override;
virtual CelObjColor *duplicate() const override;
virtual byte *getResPointer() const override;
};
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -23,85 +23,189 @@
#ifndef SCI_GRAPHICS_FRAMEOUT_H #ifndef SCI_GRAPHICS_FRAMEOUT_H
#define SCI_GRAPHICS_FRAMEOUT_H #define SCI_GRAPHICS_FRAMEOUT_H
#include "sci/graphics/plane32.h"
#include "sci/graphics/screen_item32.h"
namespace Sci { namespace Sci {
// TODO: Don't do this this way
int splitRects(Common::Rect r, const Common::Rect &other, Common::Rect(&outRects)[4]);
class GfxPicture; // TODO: Verify display styles and adjust names appropriately for
// types 1 through 12 & 15 (others are correct)
struct PlaneLineEntry { // Names should be:
reg_t hunkId; // * VShutterIn, VShutterOut
Common::Point startPoint; // * HShutterIn, HShutterOut
Common::Point endPoint; // * WipeLeft, WipeRight, WipeDown, WipeUp
byte color; // * PixelDissolve
byte priority; // * ShutDown and Kill? (and Plain and Fade?)
byte control; enum ShowStyleType /* : uint8 */ {
kShowStyleNone = 0,
kShowStyleHShutterOut = 1,
kShowStyleHShutterIn = 2,
kShowStyleVShutterOut = 3,
kShowStyleVShutterIn = 4,
kShowStyleWipeLeft = 5,
kShowStyleWipeRight = 6,
kShowStyleWipeUp = 7,
kShowStyleWipeDown = 8,
kShowStyleIrisOut = 9,
kShowStyleIrisIn = 10,
kShowStyle11 = 11,
kShowStyle12 = 12,
kShowStyleFadeOut = 13,
kShowStyleFadeIn = 14,
// TODO: Only in SCI3
kShowStyleUnknown = 15
}; };
typedef Common::List<PlaneLineEntry> PlaneLineList; /**
* Show styles represent transitions applied to draw planes.
* One show style per plane can be active at a time.
*/
struct ShowStyleEntry {
/**
* The ID of the plane this show style belongs to.
* In SCI2.1mid (at least SQ6), per-plane transitions
* were removed and a single plane ID is used.
*/
reg_t plane;
struct PlaneEntry { /**
reg_t object; * The type of the transition.
int16 priority; */
int16 lastPriority; ShowStyleType type;
int16 planeOffsetX;
int16 planeOffsetY; // TODO: This name is probably incorrect
GuiResourceId pictureId; bool fadeUp;
Common::Rect planeRect;
Common::Rect planeClipRect; /**
Common::Rect upscaledPlaneRect; * The number of steps for the show style.
Common::Rect upscaledPlaneClipRect; */
bool planePictureMirrored; int16 divisions;
byte planeBack;
PlaneLineList lines; // NOTE: This property exists from SCI2 through at least
// SCI2.1mid but is never used in the actual processing
// of the styles?
int unknownC;
/**
* The colour used by transitions that draw CelObjColor
* screen items. -1 for transitions that do not draw
* screen items.
*/
int16 color;
// TODO: Probably uint32
// TODO: This field probably should be used in order to
// provide time-accurate processing of show styles. In the
// actual SCI engine (at least 22.1mid) it appears that
// style transitions are drawn “as fast as possible”, one
// step per loop, even though this delay field exists
int delay;
// TODO: Probably bool, but never seems to be true?
int animate;
/**
* The wall time at which the next step of the animation
* should execute.
*/
uint32 nextTick;
/**
* During playback of the show style, the current step
* (out of divisions).
*/
int currentStep;
/**
* The next show style.
*/
ShowStyleEntry *next;
/**
* Whether or not this style has finished running and
* is ready for disposal.
*/
bool processed;
//
// Engine-specific properties for SCI2 through 2.1early
//
// TODO: Could union this stuff to save literally
// several bytes of memory.
/**
* The width of the plane. Used to determine the correct
* size of screen items for wipes.
*/
int width;
/**
* The height of the plane. Used to determine the correct
* size of screen items for wipes.
*/
int height;
/**
* The number of edges that a transition operates on.
* Slide wipe: 1 edge
* Reveal wipe: 2 edges
* Iris wipe: 4 edges
*/
// TODO: I have no idea why SCI engine stores this instead
// of a screenItems count
int edgeCount;
/**
* Used by transition types 1 through 10.
* One screen item per division per edge.
*/
ScreenItemList screenItems;
/**
* Used by transition types 11 and 12. A copy of the
* visible frame buffer.
*/
// TODO: This is a reg_t in SCI engine; not sure if
// we can avoid allocation through SegMan or not.
reg_t bitmapMemId;
/**
* Used by transition types 11 and 12. A screen item
* used to display the associated bitmap data.
*/
ScreenItem *bitmapScreenItem;
/**
* A number used to pick pixels to dissolve by types
* 11 and 12.
*/
int dissolveSeed;
int unknown3A;
// max?
int dissolveInitial;
//
// Engine specific properties for SCI2.1mid through SCI3
//
/**
* The number of entries in the fadeColorRanges array.
*/
uint8 fadeColorRangesCount;
/**
* A pointer to an dynamically sized array of palette
* indexes, in the order [ fromColor, toColor, ... ].
* Only colors within this range are transitioned.
*/
uint16 *fadeColorRanges;
}; };
typedef Common::List<PlaneEntry> PlaneList; typedef Common::Array<DrawList> ScreenItemListList;
typedef Common::Array<RectList> EraseListList;
struct FrameoutEntry {
uint16 givenOrderNr;
reg_t object;
GuiResourceId viewId;
int16 loopNo;
int16 celNo;
int16 x, y, z;
int16 priority;
uint16 signal;
uint16 scaleSignal;
int16 scaleX;
int16 scaleY;
Common::Rect celRect;
GfxPicture *picture;
int16 picStartX;
int16 picStartY;
bool visible;
};
typedef Common::List<FrameoutEntry *> FrameoutList;
struct PlanePictureEntry {
reg_t object;
int16 startX;
int16 startY;
GuiResourceId pictureId;
GfxPicture *picture;
FrameoutEntry *pictureCels; // temporary
};
typedef Common::List<PlanePictureEntry> PlanePictureList;
struct ScrollTextEntry {
reg_t bitmapHandle;
reg_t kWindow;
uint16 x;
uint16 y;
};
typedef Common::Array<ScrollTextEntry> ScrollTextList;
enum ViewScaleSignals32 {
kScaleSignalDoScaling32 = 0x0001, // enables scaling when drawing that cel (involves scaleX and scaleY)
kScaleSignalUnk1 = 0x0002, // unknown
kScaleSignalDisableGlobalScaling32 = 0x0004
};
class GfxCache; class GfxCache;
class GfxCoordAdjuster32; class GfxCoordAdjuster32;
@ -114,69 +218,285 @@ class GfxScreen;
* Roughly equivalent to GraphicsMgr in the actual SCI engine. * Roughly equivalent to GraphicsMgr in the actual SCI engine.
*/ */
class GfxFrameout { class GfxFrameout {
private:
bool _isHiRes;
GfxCache *_cache;
GfxCoordAdjuster32 *_coordAdjuster;
GfxPalette32 *_palette;
ResourceManager *_resMan;
GfxScreen *_screen;
SegManager *_segMan;
GfxPaint32 *_paint32;
public: public:
GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxCache *cache, GfxScreen *screen, GfxPalette32 *palette, GfxPaint32 *paint32); GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxCache *cache, GfxScreen *screen, GfxPalette32 *palette, GfxPaint32 *paint32);
~GfxFrameout(); ~GfxFrameout();
void kernelAddPlane(reg_t object);
void kernelUpdatePlane(reg_t object);
void kernelDeletePlane(reg_t object);
void applyGlobalScaling(FrameoutEntry *itemEntry, Common::Rect planeRect, int16 celHeight);
void kernelAddScreenItem(reg_t object);
void kernelUpdateScreenItem(reg_t object);
void kernelDeleteScreenItem(reg_t object);
void deletePlaneItems(reg_t planeObject);
FrameoutEntry *findScreenItem(reg_t object);
int16 kernelGetHighPlanePri();
void kernelAddPicAt(reg_t planeObj, GuiResourceId pictureId, int16 pictureX, int16 pictureY);
void kernelFrameout();
void addPlanePicture(reg_t object, GuiResourceId pictureId, uint16 startX, uint16 startY = 0);
void deletePlanePictures(reg_t object);
reg_t addPlaneLine(reg_t object, Common::Point startPoint, Common::Point endPoint, byte color, byte priority, byte control);
void updatePlaneLine(reg_t object, reg_t hunkId, Common::Point startPoint, Common::Point endPoint, byte color, byte priority, byte control);
void deletePlaneLine(reg_t object, reg_t hunkId);
void clear(); void clear();
void run();
// Scroll text functions #pragma mark -
void addScrollTextEntry(Common::String &text, reg_t kWindow, uint16 x, uint16 y, bool replace); #pragma mark Screen items
void showCurrentScrollText();
void initScrollText(uint16 maxItems) { _maxScrollTexts = maxItems; }
void clearScrollTexts();
void firstScrollText() { if (_scrollTexts.size() > 0) _curScrollText = 0; }
void lastScrollText() { if (_scrollTexts.size() > 0) _curScrollText = _scrollTexts.size() - 1; }
void prevScrollText() { if (_curScrollText > 0) _curScrollText--; }
void nextScrollText() { if (_curScrollText + 1 < (uint16)_scrollTexts.size()) _curScrollText++; }
void toggleScrollText(bool show) { _showScrollText = show; }
void printPlaneList(Console *con);
void printPlaneItemList(Console *con, reg_t planeObject);
private: private:
bool _isHiRes; void deleteScreenItem(ScreenItem *screenItem, const reg_t plane);
void showVideo(); public:
void createPlaneItemList(reg_t planeObject, FrameoutList &itemList); void kernelAddScreenItem(const reg_t object);
bool isPictureOutOfView(FrameoutEntry *itemEntry, Common::Rect planeRect, int16 planeOffsetX, int16 planeOffsetY); void kernelUpdateScreenItem(const reg_t object);
void drawPicture(FrameoutEntry *itemEntry, int16 planeOffsetX, int16 planeOffsetY, bool planePictureMirrored); void kernelDeleteScreenItem(const reg_t object);
SegManager *_segMan; #pragma mark -
ResourceManager *_resMan; #pragma mark Planes
GfxCoordAdjuster32 *_coordAdjuster; private:
GfxCache *_cache; /**
GfxPalette32 *_palette; * The list of planes (i.e. layers) that have been added
GfxScreen *_screen; * to the screen.
GfxPaint32 *_paint32; *
* @note This field is on `GraphicsMgr.screen` in SCI
FrameoutList _screenItems; * engine.
*/
PlaneList _planes; PlaneList _planes;
PlanePictureList _planePictures;
ScrollTextList _scrollTexts;
int16 _curScrollText;
bool _showScrollText;
uint16 _maxScrollTexts;
void sortPlanes(); /**
* Creates and adds a new plane to the plane list, or
* cancels deletion and updates an already-existing
* plane if a plane matching the given plane VM object
* already exists within the current plane list.
*
* @note This method is on Screen in SCI engine, but it
* is only ever called on `GraphicsMgr.screen`.
*/
void addPlane(Plane &plane);
/**
* Updates an existing plane with properties from the
* given VM object.
*/
void updatePlane(Plane &plane);
public:
const PlaneList &getPlanes() const {
return _planes;
}
void kernelAddPlane(const reg_t object);
void kernelUpdatePlane(const reg_t object);
void kernelDeletePlane(const reg_t object);
int16 kernelGetHighPlanePri();
#pragma mark -
#pragma mark Pics
public:
void kernelAddPicAt(const reg_t planeObject, const GuiResourceId pictureId, const int16 pictureX, const int16 pictureY, const bool mirrorX);
#pragma mark -
// TODO: Remap-related?
void kernelSetPalStyleRange(const uint8 fromColor, const uint8 toColor);
#pragma mark -
#pragma mark Transitions
private:
int *_dissolveSequenceSeeds;
int16 *_defaultDivisions;
int16 *_defaultUnknownC;
/**
* TODO: Documentation
*/
ShowStyleEntry *_showStyles;
inline ShowStyleEntry *findShowStyleForPlane(const reg_t planeObj) const;
inline ShowStyleEntry *deleteShowStyleInternal(ShowStyleEntry *const showStyle);
void processShowStyles();
bool processShowStyleNone(ShowStyleEntry *showStyle);
bool processShowStyleMorph(ShowStyleEntry *showStyle);
bool processShowStyleFade(const int direction, ShowStyleEntry *showStyle);
#if 0
bool processShowStyleWipe(const int direction, ShowStyleEntry *const showStyle);
#endif
public:
// NOTE: This signature is taken from SCI3 Phantasmagoria 2
// and is valid for all implementations of SCI32
void kernelSetShowStyle(const uint16 argc, const reg_t &planeObj, const ShowStyleType type, const int16 seconds, const int16 direction, const int16 priority, const int16 animate, const int16 frameOutNow, const reg_t &pFadeArray, const int16 divisions, const int16 blackScreen);
#pragma mark -
#pragma mark Rendering
private:
/**
* TODO: Documentation
*/
int8 _styleRanges[256];
/**
* The internal display pixel buffer. During frameOut,
* this buffer is drawn into according to the draw and
* erase rects calculated by `calcLists`, then drawn out
* to the hardware surface according to the `_showList`
* rects (which are also calculated by `calcLists`).
*/
Buffer _currentBuffer;
// TODO: In SCI2.1/SQ6, priority map pixels are not allocated
// by default. In SCI2/GK1, pixels are allocated, but not used
// anywhere except within CelObj::Draw in seemingly the same
// way they are used in SCI2.1/SQ6: that is, never read, only
// written.
Buffer _priorityMap;
/**
* TODO: Documentation
*/
bool _remapOccurred;
/**
* Whether or not the data in the current buffer is what
* is visible to the user. During rendering updates,
* this flag is set to false.
*/
bool _frameNowVisible;
/**
* TODO: Document
* TODO: Depending upon if the engine ever modifies this
* rect, it may be stupid to store it separately instead
* of just getting width/height from GfxScreen.
*
* @note This field is on `GraphicsMgr.screen` in SCI
* engine.
*/
Common::Rect _screenRect;
/**
* A list of rectangles, in display coordinates, that
* represent portions of the internal screen buffer that
* should be drawn to the hardware display surface.
*
* @note This field is on `GraphicsMgr.screen` in SCI
* engine.
*/
RectList _showList;
/**
* The amount of extra overdraw that is acceptable when
* merging two show list rectangles together into a
* single larger rectangle.
*
* @note This field is on `GraphicsMgr.screen` in SCI
* engine.
*/
int _overdrawThreshold;
/**
* A list of planes that are currently drawn to the
* hardware display surface. Used to calculate
* differences in plane properties between the last
* frame and current frame.
*
* @note This field is on `GraphicsMgr.visibleScreen` in
* SCI engine.
*/
PlaneList _visiblePlanes;
/**
* Calculates the location and dimensions of dirty rects
* over the entire screen for rendering the next frame.
* The draw and erase lists in `drawLists` and
* `eraseLists` each represent one plane on the screen.
*/
void calcLists(ScreenItemListList &drawLists, EraseListList &eraseLists, const Common::Rect &calcRect);
/**
* Erases the areas in the given erase list from the
* visible screen buffer by filling them with the color
* from the corresponding plane. This is an optimisation
* for coloured-type planes only; other plane types have
* to be redrawn from pixel data.
*/
void drawEraseList(const RectList &eraseList, const Plane &plane);
/**
* Draws all screen items from the given draw list to
* the visible screen buffer.
*/
void drawScreenItemList(const DrawList &screenItemList);
/**
* Updates the internal screen buffer for the next
* frame. If `shouldShowBits` is true, also sends the
* buffer to hardware.
*/
void frameOut(const bool shouldShowBits, const Common::Rect &rect);
/**
* Adds a new rectangle to the list of regions to write
* out to the hardware. The provided rect may be merged
* into an existing rectangle to reduce the number of
* blit operations.
*/
void mergeToShowList(const Common::Rect &drawRect, RectList &showList, const int overdrawThreshold);
/**
* TODO: Documentation
*/
void palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry *showStyle);
/**
* Writes the internal frame buffer out to hardware and
* clears the show list.
*/
void showBits();
public:
/**
* TODO: Document
* This is used by CelObj::Draw.
*/
bool _hasRemappedScreenItem;
/**
* Whether palMorphFrameOut should be used instead of
* frameOut for rendering. Used by kMorphOn to
* explicitly enable palMorphFrameOut for one frame.
*/
bool _palMorphIsOn;
inline Buffer &getCurrentBuffer() {
return _currentBuffer;
}
void kernelFrameout(const bool showBits);
/**
* Modifies the raw pixel data for the next frame with
* new palette indexes based on matched style ranges.
*/
void alterVmap(const Palette &palette1, const Palette &palette2, const int8 style, const int8 *const styleRanges);
// TODO: SCI2 engine never uses priority map?
inline Buffer &getPriorityMap() {
return _priorityMap;
}
// NOTE: This function is used within ScreenItem subsystem and assigned
// to various booleanish fields that seem to represent the state of the
// screen item (created, updated, deleted). In GK1/DOS, Phant1/m68k,
// SQ6/DOS, SQ6/Win, and Phant2/Win, this function simply returns 1. If
// you know of any game/environment where this function returns some
// value other than 1, or if you used to work at Sierra and can explain
// why this is a thing (and if anyone needs to care about it), please
// open a ticket!!
inline int getScreenCount() const {
return 1;
};
#pragma mark -
#pragma mark Debugging
public:
void printPlaneList(Console *con) const;
void printVisiblePlaneList(Console *con) const;
void printPlaneListInternal(Console *con, const PlaneList &planeList) const;
void printPlaneItemList(Console *con, const reg_t planeObject) const;
}; };
} // End of namespace Sci } // End of namespace Sci

View file

@ -26,6 +26,11 @@
#include "common/endian.h" // for READ_LE_UINT16 #include "common/endian.h" // for READ_LE_UINT16
#include "common/rect.h" #include "common/rect.h"
#include "common/serializer.h" #include "common/serializer.h"
#ifdef ENABLE_SCI32
#include "common/rational.h"
#include "graphics/pixelformat.h"
#include "graphics/surface.h"
#endif
#include "sci/engine/vm_types.h" #include "sci/engine/vm_types.h"
namespace Sci { namespace Sci {
@ -45,6 +50,9 @@ typedef int16 TextAlignment;
#define PORTS_FIRSTWINDOWID 2 #define PORTS_FIRSTWINDOWID 2
#define PORTS_FIRSTSCRIPTWINDOWID 3 #define PORTS_FIRSTSCRIPTWINDOWID 3
#ifdef ENABLE_SCI32
#define PRINT_RECT(x) (x).left,(x).top,(x).right,(x).bottom
#endif
struct Port { struct Port {
uint16 id; uint16 id;
@ -118,6 +126,79 @@ struct Window : public Port, public Common::Serializable {
} }
}; };
#ifdef ENABLE_SCI32
/**
* Multiplies a number by a rational number, rounding up to
* the nearest whole number.
*/
inline int mulru(const int value, const Common::Rational &ratio, const int extra = 0) {
int num = (value + extra) * ratio.getNumerator();
int result = num / ratio.getDenominator();
if (num > ratio.getDenominator() && num % ratio.getDenominator()) {
++result;
}
return result - extra;
}
/**
* Multiplies a point by two rational numbers for X and Y,
* rounding up to the nearest whole number. Modifies the
* point directly.
*/
inline void mulru(Common::Point &point, const Common::Rational &ratioX, const Common::Rational &ratioY) {
point.x = mulru(point.x, ratioX);
point.y = mulru(point.y, ratioY);
}
/**
* Multiplies a point by two rational numbers for X and Y,
* rounding up to the nearest whole number. Modifies the
* rect directly.
*/
inline void mulru(Common::Rect &rect, const Common::Rational &ratioX, const Common::Rational &ratioY, const int brExtra = 0) {
rect.left = mulru(rect.left, ratioX);
rect.top = mulru(rect.top, ratioY);
rect.right = mulru(rect.right, ratioX, brExtra);
rect.bottom = mulru(rect.bottom, ratioY, brExtra);
}
struct Buffer : public Graphics::Surface {
uint16 screenWidth;
uint16 screenHeight;
uint16 scriptWidth;
uint16 scriptHeight;
Buffer(const uint16 width, const uint16 height, uint8 *const pix) :
screenWidth(width),
screenHeight(height),
// TODO: These values are not correct for all games. Script
// dimensions were hard-coded per game in the original
// interpreter. Search all games for their internal script
// dimensions and set appropriately. (This code does not
// appear to exist at all in SCI3, which uses 640x480.)
scriptWidth(320),
scriptHeight(200) {
init(width, height, width, pix, Graphics::PixelFormat::createFormatCLUT8());
}
void clear(const uint8 value) {
memset(pixels, value, w * h);
}
inline uint8 *getAddress(const uint16 x, const uint16 y) {
return (uint8 *)getBasePtr(x, y);
}
inline uint8 *getAddressSimRes(const uint16 x, const uint16 y) {
return (uint8*)pixels + (y * w * screenHeight / scriptHeight) + (x * screenWidth / scriptWidth);
}
bool isNull() {
return pixels == nullptr;
}
};
#endif
struct Color { struct Color {
byte used; byte used;
byte r, g, b; byte r, g, b;

View file

@ -0,0 +1,192 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef SCI_GRAPHICS_LISTS32_H
#define SCI_GRAPHICS_LISTS32_H
#include "common/array.h"
namespace Sci {
/**
* StablePointerArray holds pointers in a fixed-size array
* that maintains position of erased items until `pack` is
* called. It is used by DrawList, RectList, and
* ScreenItemList. StablePointerArray takes ownership of
* all pointers that are passed to it and deletes them when
* calling `erase` or when destroying the
* StablePointerArray.
*/
template <class T, uint N>
class StablePointerArray {
uint _size;
T *_items[N];
public:
typedef T **iterator;
typedef T *const *const_iterator;
typedef T *value_type;
typedef uint size_type;
StablePointerArray() : _size(0), _items() {}
StablePointerArray(const StablePointerArray &other) : _size(other._size) {
for (size_type i = 0; i < _size; ++i) {
if (other._items[i] == nullptr) {
_items[i] = nullptr;
} else {
_items[i] = new T(*other._items[i]);
}
}
}
~StablePointerArray() {
for (size_type i = 0; i < _size; ++i) {
delete _items[i];
}
}
void operator=(const StablePointerArray &other) {
clear();
_size = other._size;
for (size_type i = 0; i < _size; ++i) {
if (other._items[i] == nullptr) {
_items[i] = nullptr;
} else {
_items[i] = new T(*other._items[i]);
}
}
}
T *const &operator[](size_type index) const {
assert(index >= 0 && index < _size);
return _items[index];
}
T *&operator[](size_type index) {
assert(index >= 0 && index < _size);
return _items[index];
}
/**
* Adds a new pointer to the array.
*/
void add(T *item) {
assert(_size < N);
_items[_size++] = item;
}
iterator begin() {
return _items;
}
const_iterator begin() const {
return _items;
}
void clear() {
for (size_type i = 0; i < _size; ++i) {
delete _items[i];
_items[i] = nullptr;
}
_size = 0;
}
iterator end() {
return _items + _size;
}
const_iterator end() const {
return _items + _size;
}
/**
* Erases the object pointed to by the given iterator.
*/
void erase(T *item) {
for (iterator it = begin(); it != end(); ++it) {
if (*it == item) {
delete *it;
*it = nullptr;
break;
}
}
}
/**
* Erases the object pointed to by the given iterator.
*/
void erase(iterator &it) {
assert(it >= _items && it < _items + _size);
delete *it;
*it = nullptr;
}
/**
* Erases the object pointed to at the given index.
*/
void erase_at(size_type index) {
assert(index >= 0 && index < _size);
delete _items[index];
_items[index] = nullptr;
}
/**
* Removes freed pointers from the pointer list.
*/
size_type pack() {
iterator freePtr = begin();
size_type newSize = 0;
for (iterator it = begin(), last = end(); it != last; ++it) {
if (*it != nullptr) {
*freePtr = *it;
++freePtr;
++newSize;
}
}
_size = newSize;
return newSize;
}
/**
* The number of populated slots in the array. The size
* of the array will only go down once `pack` is called.
*/
size_type size() const {
return _size;
}
};
template <typename T>
class FindByObject {
const reg_t &_object;
public:
FindByObject(const reg_t &object) : _object(object) {}
bool operator()(const T entry) const {
return entry->_object == _object;
}
};
}
#endif

View file

@ -47,7 +47,6 @@ GfxPalette32::GfxPalette32(ResourceManager *resMan, GfxScreen *screen)
_version(1), _versionUpdated(false) { _version(1), _versionUpdated(false) {
_varyPercent = _varyTargetPercent; _varyPercent = _varyTargetPercent;
memset(_fadeTable, 100, sizeof(_fadeTable)); memset(_fadeTable, 100, sizeof(_fadeTable));
// NOTE: In SCI engine, the palette manager constructor loads // NOTE: In SCI engine, the palette manager constructor loads
// the default palette, but in ScummVM this initialisation // the default palette, but in ScummVM this initialisation
// is performed by SciEngine::run; see r49523 for details // is performed by SciEngine::run; see r49523 for details
@ -67,6 +66,10 @@ inline void mergePaletteInternal(Palette *const to, const Palette *const from) {
} }
} }
const Palette *GfxPalette32::getNextPalette() const {
return &_nextPalette;
}
void GfxPalette32::submit(Palette &palette) { void GfxPalette32::submit(Palette &palette) {
// TODO: The resource manager in SCI32 retains raw data of palettes from // TODO: The resource manager in SCI32 retains raw data of palettes from
// the ResourceManager (ResourceMgr) through SegManager (MemoryMgr), and // the ResourceManager (ResourceMgr) through SegManager (MemoryMgr), and
@ -206,10 +209,20 @@ int16 GfxPalette32::matchColor(const byte r, const byte g, const byte b, const i
return bestIndex; return bestIndex;
} }
void GfxPalette32::updateForFrame() { bool GfxPalette32::updateForFrame() {
applyAll(); applyAll();
_versionUpdated = false; _versionUpdated = false;
// TODO: Implement remapping // TODO: Implement remapping
// return g_sci->_gfxFrameout->remapAllTables(_nextPalette != _sysPalette);
return false;
}
void GfxPalette32::updateFFrame() {
for (int i = 0; i < ARRAYSIZE(_nextPalette.colors); ++i) {
_nextPalette.colors[i] = _sourcePalette.colors[i];
}
_versionUpdated = false;
// TODO: Implement remapping
// g_sci->_gfxFrameout->remapAllTables(_nextPalette != _sysPalette); // g_sci->_gfxFrameout->remapAllTables(_nextPalette != _sysPalette);
} }
@ -410,7 +423,7 @@ void GfxPalette32::setVaryPercent(const int16 percent, const int time, const int
} }
int16 GfxPalette32::getVaryPercent() const { int16 GfxPalette32::getVaryPercent() const {
return abs(_varyPercent); return ABS(_varyPercent);
} }
void GfxPalette32::varyOff() { void GfxPalette32::varyOff() {
@ -773,6 +786,12 @@ void GfxPalette32::applyCycles() {
// Palette fading // Palette fading
// //
// NOTE: There are some game scripts (like SQ6 Sierra logo and main menu) that call
// setFade with numColorsToFade set to 256, but other parts of the engine like
// processShowStyleNone use 255 instead of 256. It is not clear if this is because
// the last palette entry is intentionally left unmodified, or if this is a bug
// in the engine. It certainly seems confused because all other places that accept
// colour ranges typically receive values in the range of 0255.
void GfxPalette32::setFade(uint8 percent, uint8 fromColor, uint16 numColorsToFade) { void GfxPalette32::setFade(uint8 percent, uint8 fromColor, uint16 numColorsToFade) {
if (fromColor > numColorsToFade) { if (fromColor > numColorsToFade) {
return; return;

View file

@ -25,6 +25,7 @@
#include "sci/graphics/palette.h" #include "sci/graphics/palette.h"
namespace Sci {
enum PalCyclerDirection { enum PalCyclerDirection {
PalCycleBackward = 0, PalCycleBackward = 0,
PalCycleForward = 1 PalCycleForward = 1
@ -71,13 +72,15 @@ struct PalCycler {
uint16 numTimesPaused; uint16 numTimesPaused;
}; };
namespace Sci {
class GfxPalette32 : public GfxPalette { class GfxPalette32 : public GfxPalette {
public: public:
GfxPalette32(ResourceManager *resMan, GfxScreen *screen); GfxPalette32(ResourceManager *resMan, GfxScreen *screen);
~GfxPalette32(); ~GfxPalette32();
protected: private:
// NOTE: currentPalette in SCI engine is called _sysPalette
// here.
/** /**
* The palette revision version. Increments once per game * The palette revision version. Increments once per game
* loop that changes the source palette. TODO: Possibly * loop that changes the source palette. TODO: Possibly
@ -105,32 +108,47 @@ namespace Sci {
*/ */
Palette _nextPalette; Palette _nextPalette;
// SQ6 defines 10 cyclers bool createPaletteFromResourceInternal(const GuiResourceId paletteId, Palette *const out) const;
PalCycler *_cyclers[10]; Palette getPaletteFromResourceInternal(const GuiResourceId paletteId) const;
public:
virtual void saveLoadWithSerializer(Common::Serializer &s) override;
const Palette *getNextPalette() const;
bool kernelSetFromResource(GuiResourceId resourceId, bool force) override;
int16 kernelFindColor(uint16 r, uint16 g, uint16 b) override;
void set(Palette *newPalette, bool force, bool forceRealMerge = false) override;
int16 matchColor(const byte matchRed, const byte matchGreen, const byte matchBlue, const int defaultDifference, int &lastCalculatedDifference, const bool *const matchTable);
/** /**
* The cycle map is used to detect overlapping cyclers. * Submits a palette to display. Entries marked as used in the
* According to SCI engine code, when two cyclers overlap, * submitted palette are merged into the existing entries of
* a fatal error has occurred and the engine will display * _sourcePalette.
* an error and then exit.
*/ */
bool _cycleMap[256]; void submit(Palette &palette);
inline void clearCycleMap(uint16 fromColor, uint16 numColorsToClear);
inline void setCycleMap(uint16 fromColor, uint16 numColorsToClear);
inline PalCycler *getCycler(uint16 fromColor);
/** bool updateForFrame();
* The fade table records the expected intensity level of each pixel void updateFFrame();
* in the palette that will be displayed on the next frame. void updateHardware();
*/ void applyAll();
byte _fadeTable[256];
#pragma mark -
#pragma mark Colour look-up
private:
/** /**
* An optional lookup table used to remap RGB565 colors to a palette * An optional lookup table used to remap RGB565 colors to a palette
* index. Used by Phantasmagoria 2 in 8-bit color environments. * index. Used by Phantasmagoria 2 in 8-bit color environments.
*/ */
byte *_clutTable; byte *_clutTable;
public:
bool loadClut(uint16 clutId);
byte matchClutColor(uint16 color);
void unloadClut();
#pragma mark -
#pragma mark Varying
private:
/** /**
* An optional palette used to describe the source colors used * An optional palette used to describe the source colors used
* in a palette vary operation. If this palette is not specified, * in a palette vary operation. If this palette is not specified,
@ -162,14 +180,13 @@ namespace Sci {
uint32 _varyLastTick; uint32 _varyLastTick;
/** /**
* TODO: Document * The amount of time to elapse, in ticks, between each cycle
* The velocity of change in percent? * of a palette vary animation.
*/ */
int _varyTime; int _varyTime;
/** /**
* TODO: Better documentation * The direction of change: -1, 0, or 1.
* The direction of change, -1, 0, or 1.
*/ */
int16 _varyDirection; int16 _varyDirection;
@ -190,29 +207,7 @@ namespace Sci {
*/ */
uint16 _varyNumTimesPaused; uint16 _varyNumTimesPaused;
/**
* Submits a palette to display. Entries marked as used in the
* submitted palette are merged into the existing entries of
* _sourcePalette.
*/
void submit(Palette &palette);
public: public:
virtual void saveLoadWithSerializer(Common::Serializer &s) override;
bool kernelSetFromResource(GuiResourceId resourceId, bool force) override;
int16 kernelFindColor(uint16 r, uint16 g, uint16 b) override;
void set(Palette *newPalette, bool force, bool forceRealMerge = false) override;
int16 matchColor(const byte matchRed, const byte matchGreen, const byte matchBlue, const int defaultDifference, int &lastCalculatedDifference, const bool *const matchTable);
void updateForFrame();
void updateHardware();
void applyAll();
bool loadClut(uint16 clutId);
byte matchClutColor(uint16 color);
void unloadClut();
void kernelPalVarySet(const GuiResourceId paletteId, const int16 percent, const int time, const int16 fromColor, const int16 toColor); void kernelPalVarySet(const GuiResourceId paletteId, const int16 percent, const int time, const int16 fromColor, const int16 toColor);
void kernelPalVaryMergeTarget(const GuiResourceId paletteId); void kernelPalVaryMergeTarget(const GuiResourceId paletteId);
void kernelPalVarySetTarget(const GuiResourceId paletteId); void kernelPalVarySetTarget(const GuiResourceId paletteId);
@ -231,13 +226,27 @@ namespace Sci {
void setTarget(const Palette *const palette); void setTarget(const Palette *const palette);
void setStart(const Palette *const palette); void setStart(const Palette *const palette);
void mergeStart(const Palette *const palette); void mergeStart(const Palette *const palette);
private:
bool createPaletteFromResourceInternal(const GuiResourceId paletteId, Palette *const out) const;
Palette getPaletteFromResourceInternal(const GuiResourceId paletteId) const;
void setVaryTimeInternal(const int16 percent, const int time); void setVaryTimeInternal(const int16 percent, const int time);
public:
void applyVary(); void applyVary();
#pragma mark -
#pragma mark Cycling
private:
// SQ6 defines 10 cyclers
PalCycler *_cyclers[10];
/**
* The cycle map is used to detect overlapping cyclers.
* According to SCI engine code, when two cyclers overlap,
* a fatal error has occurred and the engine will display
* an error and then exit.
*/
bool _cycleMap[256];
inline void clearCycleMap(uint16 fromColor, uint16 numColorsToClear);
inline void setCycleMap(uint16 fromColor, uint16 numColorsToClear);
inline PalCycler *getCycler(uint16 fromColor);
public:
void setCycle(const uint8 fromColor, const uint8 toColor, const int16 direction, const int16 delay); void setCycle(const uint8 fromColor, const uint8 toColor, const int16 direction, const int16 delay);
void doCycle(const uint8 fromColor, const int16 speed); void doCycle(const uint8 fromColor, const int16 speed);
void cycleOn(const uint8 fromColor); void cycleOn(const uint8 fromColor);
@ -249,6 +258,16 @@ namespace Sci {
void applyAllCycles(); void applyAllCycles();
void applyCycles(); void applyCycles();
#pragma mark -
#pragma mark Fading
private:
/**
* The fade table records the expected intensity level of each pixel
* in the palette that will be displayed on the next frame.
*/
byte _fadeTable[256];
public:
void setFade(const uint8 percent, const uint8 fromColor, const uint16 toColor); void setFade(const uint8 percent, const uint8 fromColor, const uint16 toColor);
void fadeOff(); void fadeOff();
void applyFade(); void applyFade();

View file

@ -209,12 +209,12 @@ void GfxPicture::drawSci32Vga(int16 celNo, int16 drawX, int16 drawY, int16 pictu
} }
// Header // Header
// [headerSize:WORD] [celCount:BYTE] [Unknown:BYTE] [Unknown:WORD] [paletteOffset:DWORD] [Unknown:DWORD] // 0[headerSize:WORD] 2[celCount:BYTE] 3[Unknown:BYTE] 4[celHeaderSize:WORD] 6[paletteOffset:DWORD] 10[Unknown:WORD] 12[Unknown:WORD]
// cel-header follow afterwards, each is 42 bytes // cel-header follow afterwards, each is 42 bytes
// Cel-Header // Cel-Header
// [width:WORD] [height:WORD] [displaceX:WORD] [displaceY:WORD] [clearColor:BYTE] [compressed:BYTE] // 0[width:WORD] 2[height:WORD] 4[displaceX:WORD] 6[displaceY:WORD] 8[clearColor:BYTE] 9[compressed:BYTE]
// offset 10-23 is unknown // offset 10-23 is unknown
// [rleOffset:DWORD] [literalOffset:DWORD] [Unknown:WORD] [Unknown:WORD] [priority:WORD] [relativeXpos:WORD] [relativeYpos:WORD] // 24[rleOffset:DWORD] 28[literalOffset:DWORD] 32[Unknown:WORD] 34[Unknown:WORD] 36[priority:WORD] 38[relativeXpos:WORD] 40[relativeYpos:WORD]
cel_headerPos += 42 * celNo; cel_headerPos += 42 * celNo;

View file

@ -38,6 +38,9 @@ enum {
class GfxPorts; class GfxPorts;
class GfxScreen; class GfxScreen;
class GfxPalette; class GfxPalette;
class GfxCoordAdjuster;
class ResourceManager;
class Resource;
/** /**
* Picture class, handles loading and displaying of picture resources * Picture class, handles loading and displaying of picture resources

View file

@ -0,0 +1,841 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "sci/console.h"
#include "sci/engine/kernel.h"
#include "sci/engine/selector.h"
#include "sci/engine/state.h"
#include "sci/graphics/frameout.h"
#include "sci/graphics/lists32.h"
#include "sci/graphics/plane32.h"
#include "sci/graphics/screen.h"
#include "sci/graphics/screen_item32.h"
namespace Sci {
#pragma mark DrawList
void DrawList::add(ScreenItem *screenItem, const Common::Rect &rect) {
DrawItem *drawItem = new DrawItem;
drawItem->screenItem = screenItem;
drawItem->rect = rect;
DrawListBase::add(drawItem);
}
#pragma mark -
#pragma mark Plane
uint16 Plane::_nextObjectId = 20000;
Plane::Plane(const Common::Rect &gameRect) :
_gameRect(gameRect),
_object(make_reg(0, _nextObjectId++)),
_back(0),
_pictureId(kPlanePicColored),
_mirrored(false),
_width(g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth),
_height(g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight),
_deleted(0),
_updated(0),
_priorityChanged(0),
_created(g_sci->_gfxFrameout->getScreenCount()),
_redrawAllCount(g_sci->_gfxFrameout->getScreenCount()) {
convertGameRectToPlaneRect();
_priority = MAX(10000, g_sci->_gfxFrameout->getPlanes().getTopPlanePriority() + 1);
setType();
_screenRect = _planeRect;
}
Plane::Plane(reg_t object) :
_object(object),
_width(g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth),
_height(g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight),
_created(g_sci->_gfxFrameout->getScreenCount()),
_redrawAllCount(g_sci->_gfxFrameout->getScreenCount()),
_deleted(0),
_updated(0),
_priorityChanged(false),
_moved(0) {
SegManager *segMan = g_sci->getEngineState()->_segMan;
_vanishingPoint.x = readSelectorValue(segMan, object, SELECTOR(vanishingX));
_vanishingPoint.y = readSelectorValue(segMan, object, SELECTOR(vanishingY));
_gameRect.left = readSelectorValue(segMan, object, SELECTOR(inLeft));
_gameRect.top = readSelectorValue(segMan, object, SELECTOR(inTop));
_gameRect.right = readSelectorValue(segMan, object, SELECTOR(inRight));
_gameRect.bottom = readSelectorValue(segMan, object, SELECTOR(inBottom));
convertGameRectToPlaneRect();
_back = readSelectorValue(segMan, object, SELECTOR(back));
_priority = readSelectorValue(segMan, object, SELECTOR(priority));
_pictureId = readSelectorValue(segMan, object, SELECTOR(picture));
setType();
_mirrored = readSelectorValue(segMan, object, SELECTOR(mirrored));
_screenRect = _planeRect;
changePic();
}
Plane::Plane(const Plane &other) :
_object(other._object),
_priority(other._priority),
_pictureId(other._pictureId),
_mirrored(other._mirrored),
_back(other._back),
_field_34(other._field_34), _field_38(other._field_38),
_field_3C(other._field_3C), _field_40(other._field_40),
_planeRect(other._planeRect),
_gameRect(other._gameRect),
_screenRect(other._screenRect),
_screenItemList(other._screenItemList) {}
void Plane::operator=(const Plane &other) {
_gameRect = other._gameRect;
_planeRect = other._planeRect;
_vanishingPoint = other._vanishingPoint;
_pictureId = other._pictureId;
_type = other._type;
_mirrored = other._mirrored;
_priority = other._priority;
_back = other._back;
_width = other._width;
_field_34 = other._field_34;
_height = other._height;
_screenRect = other._screenRect;
_field_3C = other._field_3C;
_priorityChanged = other._priorityChanged;
}
void Plane::init() {
_nextObjectId = 20000;
}
void Plane::convertGameRectToPlaneRect() {
const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth;
const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight;
const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
const Ratio ratioX = Ratio(screenWidth, scriptWidth);
const Ratio ratioY = Ratio(screenHeight, scriptHeight);
_planeRect = _gameRect;
mulru(_planeRect, ratioX, ratioY, 1);
}
void Plane::printDebugInfo(Console *con) const {
Common::String name;
if (_object.isNumber()) {
name = "-scummvm-";
} else {
name = g_sci->getEngineState()->_segMan->getObjectName(_object);
}
con->debugPrintf("%04x:%04x (%s): type %d, prio %d, pic %d, mirror %d, back %d\n",
PRINT_REG(_object),
name.c_str(),
_type,
_priority,
_pictureId,
_mirrored,
_back
);
con->debugPrintf(" game rect: (%d, %d, %d, %d), plane rect: (%d, %d, %d, %d)\n screen rect: (%d, %d, %d, %d)\n",
PRINT_RECT(_gameRect),
PRINT_RECT(_planeRect),
PRINT_RECT(_screenRect)
);
con->debugPrintf(" # screen items: %d\n", _screenItemList.size());
}
#pragma mark -
#pragma mark Plane - Pic
void Plane::addPicInternal(const GuiResourceId pictureId, const Common::Point *position, const bool mirrorX) {
uint16 celCount = 1000;
for (uint16 celNo = 0; celNo < celCount; ++celNo) {
CelObjPic *celObj = new CelObjPic(pictureId, celNo);
if (celCount == 1000) {
celCount = celObj->_celCount;
}
ScreenItem *screenItem = new ScreenItem(_object, celObj->_info);
screenItem->_pictureId = pictureId;
screenItem->_mirrorX = mirrorX;
screenItem->_priority = celObj->_priority;
screenItem->_fixPriority = true;
if (position != nullptr) {
screenItem->_position = *position;
} else {
screenItem->_position = celObj->_relativePosition;
}
_screenItemList.add(screenItem);
delete screenItem->_celObj;
screenItem->_celObj = celObj;
}
}
void Plane::addPic(const GuiResourceId pictureId, const Common::Point &position, const bool mirrorX) {
deletePic(pictureId);
addPicInternal(pictureId, &position, mirrorX);
// NOTE: In SCI engine this method returned the pictureId of the
// plane, but this return value was never used
}
void Plane::changePic() {
_pictureChanged = false;
if (_type != kPlaneTypePicture) {
return;
}
addPicInternal(_pictureId, nullptr, _mirrored);
}
void Plane::deletePic(const GuiResourceId pictureId) {
for (ScreenItemList::iterator it = _screenItemList.begin(); it != _screenItemList.end(); ++it) {
ScreenItem *screenItem = *it;
if (screenItem->_pictureId == pictureId) {
screenItem->_created = 0;
screenItem->_updated = 0;
screenItem->_deleted = g_sci->_gfxFrameout->getScreenCount();
}
}
}
void Plane::deletePic(const GuiResourceId oldPictureId, const GuiResourceId newPictureId) {
deletePic(oldPictureId);
_pictureId = newPictureId;
}
void Plane::deleteAllPics() {
for (ScreenItemList::iterator it = _screenItemList.begin(); it != _screenItemList.end(); ++it) {
ScreenItem *screenItem = *it;
if (screenItem != nullptr && screenItem->_celInfo.type == kCelTypePic) {
if (screenItem->_created == 0) {
screenItem->_created = 0;
screenItem->_updated = 0;
screenItem->_deleted = g_sci->_gfxFrameout->getScreenCount();
} else {
_screenItemList.erase(it);
}
}
}
_screenItemList.pack();
}
#pragma mark -
#pragma mark Plane - Rendering
void Plane::breakDrawListByPlanes(DrawList &drawList, const PlaneList &planeList) const {
int index = planeList.findIndexByObject(_object);
for (DrawList::size_type i = 0; i < drawList.size(); ++i) {
for (PlaneList::size_type j = index + 1; j < planeList.size(); ++j) {
if (planeList[j]->_type != kPlaneTypeTransparent) {
Common::Rect ptr[4];
int count = splitRects(drawList[i]->rect, planeList[j]->_screenRect, ptr);
if (count != -1) {
for (int k = count - 1; k >= 0; --k) {
drawList.add(drawList[i]->screenItem, ptr[k]);
}
drawList.erase_at(i);
break;
}
}
}
}
drawList.pack();
}
void Plane::breakEraseListByPlanes(RectList &eraseList, const PlaneList &planeList) const {
int index = planeList.findIndexByObject(_object);
for (RectList::size_type i = 0; i < eraseList.size(); ++i) {
for (PlaneList::size_type j = index + 1; j < planeList.size(); ++j) {
if (planeList[j]->_type != kPlaneTypeTransparent) {
Common::Rect ptr[4];
int count = splitRects(*eraseList[i], planeList[j]->_screenRect, ptr);
if (count != -1) {
for (int k = count - 1; k >= 0; --k) {
eraseList.add(ptr[k]);
}
eraseList.erase_at(i);
break;
}
}
}
}
eraseList.pack();
}
void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList &drawList, RectList &eraseList) {
ScreenItemList::size_type planeItemCount = _screenItemList.size();
ScreenItemList::size_type visiblePlaneItemCount = visiblePlane._screenItemList.size();
for (PlaneList::size_type i = 0; i < planeItemCount; ++i) {
ScreenItem *vitem = nullptr;
// NOTE: The original engine used an array without bounds checking
// so could just get the visible screen item directly; we need to
// verify that the index is actually within the valid range for
// the visible plane before accessing the item to avoid a range
// error.
if (i < visiblePlaneItemCount) {
vitem = visiblePlane._screenItemList[i];
}
ScreenItem *item = _screenItemList[i];
if (i < _screenItemList.size() && item != nullptr) {
if (item->_deleted) {
// add item's rect to erase list
if (i < visiblePlane._screenItemList.size() && vitem != nullptr) {
if (!vitem->_screenRect.isEmpty()) {
if (/* TODO: g_Remap_numActiveRemaps */ false) { // active remaps?
mergeToRectList(vitem->_screenRect, eraseList);
} else {
eraseList.add(vitem->_screenRect);
}
}
}
} else if (item->_created) {
// add item to draw list
item->getCelObj();
item->calcRects(*this);
if(!item->_screenRect.isEmpty()) {
if (/* TODO: g_Remap_numActiveRemaps */ false) { // active remaps?
drawList.add(item, item->_screenRect);
mergeToRectList(item->_screenRect, eraseList);
} else {
drawList.add(item, item->_screenRect);
}
}
} else if (item->_updated) {
// add old rect to erase list, new item to draw list
item->getCelObj();
item->calcRects(*this);
if (/* TODO: g_Remap_numActiveRemaps */ false) { // active remaps
// if item and vitem don't overlap, ...
if (item->_screenRect.isEmpty() ||
i >= visiblePlaneItemCount ||
vitem == nullptr ||
vitem->_screenRect.isEmpty() ||
!vitem->_screenRect.intersects(item->_screenRect)
) {
// add item to draw list, and old rect to erase list
if (!item->_screenRect.isEmpty()) {
drawList.add(item, item->_screenRect);
mergeToRectList(item->_screenRect, eraseList);
}
if (i < visiblePlaneItemCount && vitem != nullptr && !vitem->_screenRect.isEmpty()) {
mergeToRectList(vitem->_screenRect, eraseList);
}
} else {
// otherwise, add bounding box of old+new to erase list,
// and item to draw list
// TODO: This was changed from disasm, verify please!
Common::Rect extendedScreenItem = vitem->_screenRect;
extendedScreenItem.extend(item->_screenRect);
drawList.add(item, item->_screenRect);
mergeToRectList(extendedScreenItem, eraseList);
}
} else {
// if no active remaps, just add item to draw list and old rect
// to erase list
if (!item->_screenRect.isEmpty()) {
drawList.add(item, item->_screenRect);
}
if (i < visiblePlaneItemCount && vitem != nullptr && !vitem->_screenRect.isEmpty()) {
eraseList.add(vitem->_screenRect);
}
}
}
}
}
breakEraseListByPlanes(eraseList, planeList);
breakDrawListByPlanes(drawList, planeList);
if (/* TODO: dword_C6288 */ false) { // "high resolution pictures"????
_screenItemList.sort();
bool encounteredPic = false;
bool v81 = false;
for (RectList::size_type i = 0; i < eraseList.size(); ++i) {
Common::Rect *rect = eraseList[i];
for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) {
ScreenItem *item = _screenItemList[j];
if (j < _screenItemList.size() && item != nullptr) {
if (rect->intersects(item->_screenRect)) {
Common::Rect intersection = rect->findIntersectingRect(item->_screenRect);
if (!item->_deleted) {
if (encounteredPic) {
if (item->_celInfo.type == kCelTypePic) {
if (v81 || item->_celInfo.celNo == 0) {
drawList.add(item, intersection);
}
} else {
if (!item->_updated && !item->_created) {
drawList.add(item, intersection);
}
v81 = true;
}
} else {
if (!item->_updated && !item->_created) {
drawList.add(item, intersection);
}
if (item->_celInfo.type == kCelTypePic) {
encounteredPic = true;
}
}
}
}
}
}
}
_screenItemList.unsort();
} else {
// add all items overlapping the erase list to the draw list
for (RectList::size_type i = 0; i < eraseList.size(); ++i) {
for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) {
ScreenItem *item = _screenItemList[j];
if (j < _screenItemList.size() && item != nullptr && !item->_updated && !item->_deleted && !item->_created && eraseList[i]->intersects(item->_screenRect)) {
drawList.add(item, eraseList[i]->findIntersectingRect(item->_screenRect));
}
}
}
}
if (/* TODO: g_Remap_numActiveRemaps */ false) { // no remaps active?
// Add all items that overlap with items in the drawlist and have higher
// priority
for (DrawList::size_type i = 0; i < drawList.size(); ++i) {
DrawItem *dli = drawList[i];
for (PlaneList::size_type j = 0; j < planeItemCount; ++j) {
ScreenItem *sli = _screenItemList[j];
if (i < drawList.size() && dli) {
if (j < _screenItemList.size() && sli) {
if (!sli->_updated && !sli->_deleted && !sli->_created) {
ScreenItem *item = dli->screenItem;
if (sli->_priority > item->_priority || (sli->_priority == item->_priority && sli->_object > item->_object)) {
if (dli->rect.intersects(sli->_screenRect)) {
drawList.add(sli, dli->rect.findIntersectingRect(sli->_screenRect));
}
}
}
}
}
}
}
}
decrementScreenItemArrayCounts(&visiblePlane, false);
_screenItemList.pack();
visiblePlane._screenItemList.pack();
}
void Plane::decrementScreenItemArrayCounts(Plane *visiblePlane, const bool forceUpdate) {
// The size of the screenItemList may change, so it is
// critical to re-check the size on each iteration
for (ScreenItemList::size_type i = 0; i < _screenItemList.size(); ++i) {
ScreenItem *item = _screenItemList[i];
if (item != nullptr) {
// update item in visiblePlane if item is updated
if (
item->_updated ||
(
forceUpdate &&
visiblePlane != nullptr &&
Common::find(visiblePlane->_screenItemList.begin(), visiblePlane->_screenItemList.end(), item) != visiblePlane->_screenItemList.end()
)
) {
*visiblePlane->_screenItemList[i] = *_screenItemList[i];
}
if (item->_updated) {
item->_updated--;
}
// create new item in visiblePlane if item was added
if (item->_created) {
item->_created--;
if (visiblePlane != nullptr) {
ScreenItem *n = new ScreenItem(*item);
visiblePlane->_screenItemList.add(n);
}
}
// delete item from both planes if it was deleted
if (item->_deleted) {
item->_deleted--;
if (!item->_deleted) {
visiblePlane->_screenItemList.erase_at(i);
_screenItemList.erase_at(i);
}
}
}
}
}
void Plane::filterDownEraseRects(DrawList &drawList, RectList &eraseList, RectList &transparentEraseList) const {
if (_type == kPlaneTypeTransparent) {
for (RectList::size_type i = 0; i < transparentEraseList.size(); ++i) {
Common::Rect *r = transparentEraseList[i];
for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) {
ScreenItem *item = _screenItemList[j];
if (item != nullptr) {
if (r->intersects(item->_screenRect)) {
mergeToDrawList(j, *r, drawList);
}
}
}
}
} else {
for (RectList::size_type i = 0; i < transparentEraseList.size(); ++i) {
Common::Rect *r = transparentEraseList[i];
if (r->intersects(_screenRect)) {
r->clip(_screenRect);
mergeToRectList(*r, eraseList);
for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) {
ScreenItem *item = _screenItemList[j];
if (item != nullptr) {
if (r->intersects(item->_screenRect)) {
mergeToDrawList(j, *r, drawList);
}
}
}
Common::Rect ptr[4];
Common::Rect *r2 = transparentEraseList[i];
int count = splitRects(*r2, *r, ptr);
for (int k = count - 1; k >= 0; --k) {
transparentEraseList.add(ptr[k]);
}
transparentEraseList.erase_at(i);
}
}
transparentEraseList.pack();
}
}
void Plane::filterUpDrawRects(DrawList &transparentDrawList, const DrawList &drawList) const {
for (DrawList::size_type i = 0; i < drawList.size(); ++i) {
Common::Rect &r = drawList[i]->rect;
for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) {
ScreenItem *item = _screenItemList[j];
if (item != nullptr) {
if (r.intersects(item->_screenRect)) {
mergeToDrawList(j, r, transparentDrawList);
}
}
}
}
}
void Plane::filterUpEraseRects(DrawList &drawList, RectList &eraseList) const {
for (RectList::size_type i = 0; i < eraseList.size(); ++i) {
Common::Rect &r = *eraseList[i];
for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) {
ScreenItem *item = _screenItemList[j];
if (item != nullptr) {
if (r.intersects(item->_screenRect)) {
mergeToDrawList(j, r, drawList);
}
}
}
}
}
void Plane::mergeToDrawList(const DrawList::size_type index, const Common::Rect &rect, DrawList &drawList) const {
RectList rects;
Common::Rect r = _screenItemList[index]->_screenRect;
r.clip(rect);
rects.add(r);
ScreenItem *item = _screenItemList[index];
for (RectList::size_type i = 0; i < rects.size(); ++i) {
r = *rects[i];
for (DrawList::size_type j = 0; j < drawList.size(); ++j) {
DrawItem *drawitem = drawList[j];
if (item->_object == drawitem->screenItem->_object) {
if (drawitem->rect.contains(r)) {
rects.erase_at(i);
break;
}
Common::Rect outRects[4];
int count = splitRects(r, drawitem->rect, outRects);
if (count != -1) {
for (int k = count - 1; k >= 0; --k) {
rects.add(outRects[k]);
}
rects.erase_at(i);
// proceed to the next rect
r = *rects[++i];
}
}
}
}
rects.pack();
for (RectList::size_type i = 0; i < rects.size(); ++i) {
drawList.add(item, *rects[i]);
}
}
void Plane::mergeToRectList(const Common::Rect &rect, RectList &rectList) const {
RectList temp;
temp.add(rect);
for (RectList::size_type i = 0; i < temp.size(); ++i) {
Common::Rect *outerRect = temp[i];
for (RectList::size_type j = 0; j < rectList.size(); ++j) {
Common::Rect *innerRect = rectList[i];
if (innerRect->intersects(*outerRect)) {
Common::Rect out[4];
int count = splitRects(*outerRect, *innerRect, out);
for (int k = count - 1; k >= 0; --k) {
temp.add(out[k]);
}
temp.erase_at(i);
} else {
temp.erase_at(i);
}
}
}
temp.pack();
for (RectList::size_type i = 0; i < temp.size(); ++i) {
rectList.add(*temp[i]);
}
}
void Plane::redrawAll(Plane *visiblePlane, const PlaneList &planeList, DrawList &drawList, RectList &eraseList) {
for (ScreenItemList::const_iterator screenItemPtr = _screenItemList.begin(); screenItemPtr != _screenItemList.end(); ++screenItemPtr) {
if (*screenItemPtr != nullptr) {
ScreenItem &screenItem = **screenItemPtr;
if (!screenItem._deleted) {
screenItem.getCelObj();
screenItem.calcRects(*this);
if (!screenItem._screenRect.isEmpty()) {
drawList.add(&screenItem, screenItem._screenRect);
}
}
}
}
eraseList.clear();
if (!_screenRect.isEmpty() && _type != kPlaneTypePicture && _type != kPlaneTypeOpaque) {
eraseList.add(_screenRect);
}
breakEraseListByPlanes(eraseList, planeList);
breakDrawListByPlanes(drawList, planeList);
--_redrawAllCount;
decrementScreenItemArrayCounts(visiblePlane, true);
_screenItemList.pack();
if (visiblePlane != nullptr) {
visiblePlane->_screenItemList.pack();
}
}
void Plane::setType() {
if (_pictureId == kPlanePicOpaque) {
_type = kPlaneTypeOpaque;
} else if (_pictureId == kPlanePicTransparent) {
_type = kPlaneTypeTransparent;
} else if (_pictureId == kPlanePicColored) {
_type = kPlaneTypeColored;
} else {
_type = kPlaneTypePicture;
}
}
void Plane::sync(const Plane *other, const Common::Rect &screenRect) {
if (other == nullptr) {
if (_pictureChanged) {
deleteAllPics();
setType();
changePic();
_redrawAllCount = g_sci->_gfxFrameout->getScreenCount();
} else {
setType();
}
} else {
if (
_planeRect.top != other->_planeRect.top ||
_planeRect.left != other->_planeRect.left ||
_planeRect.right > other->_planeRect.right ||
_planeRect.bottom > other->_planeRect.bottom
) {
_redrawAllCount = g_sci->_gfxFrameout->getScreenCount();
_updated = g_sci->_gfxFrameout->getScreenCount();
} else if (_planeRect != other->_planeRect) {
_updated = g_sci->_gfxFrameout->getScreenCount();
}
if (_priority != other->_priority) {
_priorityChanged = g_sci->_gfxFrameout->getScreenCount();
}
if (_pictureId != other->_pictureId || _mirrored != other->_mirrored || _pictureChanged) {
deleteAllPics();
setType();
changePic();
_redrawAllCount = g_sci->_gfxFrameout->getScreenCount();
}
if (_back != other->_back) {
_redrawAllCount = g_sci->_gfxFrameout->getScreenCount();
}
}
_deleted = 0;
if (_created == 0) {
_moved = g_sci->_gfxFrameout->getScreenCount();
}
convertGameRectToPlaneRect();
_width = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
_height = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
_screenRect = _planeRect;
// NOTE: screenRect originally was retrieved through globals
// instead of being passed into the function
clipScreenRect(screenRect);
}
void Plane::update(const reg_t object) {
SegManager *segMan = g_sci->getEngineState()->_segMan;
_vanishingPoint.x = readSelectorValue(segMan, object, SELECTOR(vanishingX));
_vanishingPoint.y = readSelectorValue(segMan, object, SELECTOR(vanishingY));
_gameRect.left = readSelectorValue(segMan, object, SELECTOR(inLeft));
_gameRect.top = readSelectorValue(segMan, object, SELECTOR(inTop));
_gameRect.right = readSelectorValue(segMan, object, SELECTOR(inRight));
_gameRect.bottom = readSelectorValue(segMan, object, SELECTOR(inBottom));
convertGameRectToPlaneRect();
_priority = readSelectorValue(segMan, object, SELECTOR(priority));
GuiResourceId pictureId = readSelectorValue(segMan, object, SELECTOR(picture));
if (_pictureId != pictureId) {
_pictureId = pictureId;
_pictureChanged = true;
}
_mirrored = readSelectorValue(segMan, object, SELECTOR(mirrored));
_back = readSelectorValue(segMan, object, SELECTOR(back));
}
#pragma mark -
#pragma mark PlaneList
void PlaneList::clear() {
for (iterator it = begin(); it != end(); ++it) {
delete *it;
}
PlaneListBase::clear();
}
void PlaneList::erase(Plane *plane) {
for (iterator it = begin(); it != end(); ++it) {
if (*it == plane) {
erase(it);
break;
}
}
}
int PlaneList::findIndexByObject(const reg_t object) const {
for (size_type i = 0; i < size(); ++i) {
if ((*this)[i] != nullptr && (*this)[i]->_object == object) {
return i;
}
}
return -1;
}
Plane *PlaneList::findByObject(const reg_t object) const {
const_iterator planeIt = Common::find_if(begin(), end(), FindByObject<Plane *>(object));
if (planeIt == end()) {
return nullptr;
}
return *planeIt;
}
int16 PlaneList::getTopPlanePriority() const {
if (size() > 0) {
return (*this)[size() - 1]->_priority;
}
return 0;
}
int16 PlaneList::getTopSciPlanePriority() const {
int16 priority = 0;
for (const_iterator it = begin(); it != end(); ++it) {
if ((*it)->_priority >= 10000) {
break;
}
priority = (*it)->_priority;
}
return priority;
}
void PlaneList::add(Plane *plane) {
for (iterator it = begin(); it != end(); ++it) {
if ((*it)->_priority < plane->_priority) {
insert(it, plane);
return;
}
}
push_back(plane);
}
}

View file

@ -0,0 +1,465 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef SCI_GRAPHICS_PLANE32_H
#define SCI_GRAPHICS_PLANE32_H
#include "common/array.h"
#include "common/rect.h"
#include "sci/engine/vm_types.h"
#include "sci/graphics/helpers.h"
#include "sci/graphics/lists32.h"
#include "sci/graphics/screen_item32.h"
namespace Sci {
enum PlaneType {
kPlaneTypeColored = 0,
kPlaneTypePicture = 1,
kPlaneTypeTransparent = 2,
kPlaneTypeOpaque = 3
};
enum PlanePictureCodes {
// NOTE: Any value at or below 65532 means the plane
// is a kPlaneTypePicture.
kPlanePic = 65532,
kPlanePicOpaque = 65533,
kPlanePicTransparent = 65534,
kPlanePicColored = 65535
};
#pragma mark -
#pragma mark RectList
typedef StablePointerArray<Common::Rect, 200> RectListBase;
class RectList : public RectListBase {
public:
void add(const Common::Rect &rect) {
RectListBase::add(new Common::Rect(rect));
}
};
#pragma mark -
#pragma mark DrawList
struct DrawItem {
ScreenItem *screenItem;
Common::Rect rect;
inline bool operator<(const DrawItem &other) const {
return *screenItem < *other.screenItem;
}
};
typedef StablePointerArray<DrawItem, 250> DrawListBase;
class DrawList : public DrawListBase {
private:
inline static bool sortHelper(const DrawItem *a, const DrawItem *b) {
return *a < *b;
}
public:
void add(ScreenItem *screenItem, const Common::Rect &rect);
inline void sort() {
pack();
Common::sort(begin(), end(), sortHelper);
}
};
class PlaneList;
#pragma mark -
#pragma mark Plane
/**
* A plane is a grouped layer of screen items.
*/
class Plane {
private:
/**
* A serial used for planes that are generated inside
* the graphics engine, rather than the interpreter.
*/
static uint16 _nextObjectId;
/**
* The dimensions of the plane, in game script
* coordinates.
* TODO: These are never used and are always
* scriptWidth x scriptHeight in SCI engine? The actual
* dimensions of the plane are always in
* gameRect/planeRect.
*/
int16 _width, _height;
/**
* For planes that are used to render picture data, the
* resource ID of the picture to be displayed. This
* value may also be one of the special
* PlanePictureCodes, in which case the plane becomes a
* non-picture plane.
*/
GuiResourceId _pictureId;
/**
* Whether or not the contents of picture planes should
* be drawn horizontally mirrored. Only applies to
* planes of type kPlaneTypePicture.
*/
bool _mirrored;
/**
* Whether the picture ID for this plane has changed.
* This flag is set when the plane is created or updated
* from a VM object, and is cleared when the plane is
* synchronised to another plane (which calls
* changePic).
*/
bool _pictureChanged; // ?
// TODO: Are these ever actually used?
int _field_34, _field_38; // probably a point or ratio
int _field_3C, _field_40; // probably a point or ratio
/**
* Converts the dimensions of the game rect used by
* scripts to the dimensions of the plane rect used to
* render content to the screen. Coordinates with
* remainders are rounded up to the next whole pixel.
*/
void convertGameRectToPlaneRect();
/**
* Sets the type of the plane according to its assigned
* picture resource ID.
*/
void setType();
public:
/**
* The colour to use when erasing the plane. Only
* applies to planes of type kPlaneTypeColored.
*/
byte _back;
/**
* Whether the priority of this plane has changed.
* This flag is set when the plane is updated from
* another plane and cleared when draw list calculation
* occurs.
*/
int _priorityChanged; // ?
/**
* A handle to the VM object corresponding to this
* plane. Some planes are generated purely within the
* graphics engine and have a numeric object value.
*/
reg_t _object;
/**
* The rendering priority of the plane. Higher
* priorities are drawn above lower priorities.
*/
int16 _priority;
/**
* TODO: Document
*/
int _redrawAllCount;
PlaneType _type;
/**
* Flags indicating the state of the plane.
* - `created` is set when the plane is first created,
* either from a VM object or from within the engine
* itself
* - `updated` is set when the plane is updated from
* another plane and the two planes' `planeRect`s do
* not match
* - `deleted` is set when the plane is deleted by a
* kernel call
* - `moved` is set when the plane is synchronised from
* another plane and is not already in the "created"
* state
*/
int _created, _updated, _deleted, _moved;
/**
* The vanishing point for the plane. Used when
* calculating the correct scaling of the plane's screen
* items according to their position.
*/
Common::Point _vanishingPoint;
/**
* The position & dimensions of the plane in screen
* coordinates. This rect is not clipped to the screen,
* so may include coordinates that are offscreen.
*/
Common::Rect _planeRect;
/**
* The position & dimensions of the plane in game script
* coordinates.
*/
Common::Rect _gameRect;
/**
* The position & dimensions of the plane in screen
* coordinates. This rect is clipped to the screen.
*/
Common::Rect _screenRect;
/**
* The list of screen items grouped within this plane.
*/
ScreenItemList _screenItemList;
public:
/**
* Initialises static Plane members.
*/
static void init();
Plane(const Common::Rect &gameRect);
Plane(const reg_t object);
Plane(const Plane &other);
void operator=(const Plane &other);
inline bool operator<(const Plane &other) const {
// TODO: In SCI engine, _object is actually a uint16 and can either
// contain a MemID (a handle to MemoryMgr, similar to reg_t) or
// a serial (Plane::_nextObjectId). These numbers can be compared
// directly in the real engine and the lowest MemID wins, but in
// ScummVM reg_t pointers are not comparable so we have to use a
// different strategy when two planes generated by scripts conflict.
// For now we just don't check if the priority is below 0, since
// that priority is used to represent hidden planes and is guaranteed
// to generate conflicts with script-generated planes. If there are
// other future conflicts with script-generated planes then we need
// to come up with a solution that works, similar to
// reg_t::pointerComparisonWithInteger used by SCI16.
return _priority < other._priority || (_priority == other._priority && _priority > -1 && _object < other._object);
}
/**
* Clips the screen rect of this plane to fit within the
* given screen rect.
*/
inline void clipScreenRect(const Common::Rect &screenRect) {
if (_screenRect.intersects(screenRect)) {
_screenRect.clip(screenRect);
} else {
_screenRect.left = 0;
_screenRect.top = 0;
_screenRect.right = 0;
_screenRect.bottom = 0;
}
}
void printDebugInfo(Console *con) const;
/**
* Compares the properties of the current plane against
* the properties of the `other` plane (which is the
* corresponding plane from the visible plane list) to
* discover which properties have been changed on this
* plane by a call to `update(reg_t)`.
*
* @note This method was originally called UpdatePlane
* in SCI engine.
*/
void sync(const Plane *other, const Common::Rect &screenRect);
/**
* Updates the plane to match the state of the plane
* object from the virtual machine.
*
* @note This method was originally called UpdatePlane
* in SCI engine.
*/
void update(const reg_t object);
#pragma mark -
#pragma mark Plane - Pic
private:
/**
* Adds all cels from the specified picture resource to
* the plane as screen items. If a position is provided,
* the screen items will be given that position;
* otherwise, the default relative positions for each
* cel will be taken from the picture resource data.
*/
inline void addPicInternal(const GuiResourceId pictureId, const Common::Point *position, const bool mirrorX);
/**
* If the plane is a picture plane, re-adds all cels
* from its picture resource to the plane.
*/
void changePic();
/**
* Marks all screen items to be deleted that are within
* this plane and match the given picture ID.
*/
void deletePic(const GuiResourceId pictureId);
/**
* Marks all screen items to be deleted that are within
* this plane and match the given picture ID, then sets
* the picture ID of the plane to the new picture ID
* without adding any screen items.
*/
void deletePic(const GuiResourceId oldPictureId, const GuiResourceId newPictureId);
/**
* Marks all screen items to be deleted that are within
* this plane and are picture cels.
*/
void deleteAllPics();
public:
/**
* Marks all existing screen items matching the current
* picture to be deleted, then adds all cels from the
* new picture resource to the plane at the given
* position.
*/
void addPic(const GuiResourceId pictureId, const Common::Point &position, const bool mirrorX);
#pragma mark -
#pragma mark Plane - Rendering
private:
/**
* Splits all rects in the given draw list at the edges
* of all non-transparent planes above the current
* plane.
*/
void breakDrawListByPlanes(DrawList &drawList, const PlaneList &planeList) const;
/**
* Splits all rects in the given erase list rects at the
* edges of all non-transparent planes above the current
* plane.
*/
void breakEraseListByPlanes(RectList &eraseList, const PlaneList &planeList) const;
/**
* Synchronises changes to screen items from the current
* plane to the visible plane and deletes screen items
* from the current plane that have been marked as
* deleted. If `forceUpdate` is true, all screen items
* on the visible plane will be updated, even if they
* are not marked as having changed.
*/
void decrementScreenItemArrayCounts(Plane *visiblePlane, const bool forceUpdate);
/**
* Merges the screen item from this plane at the given
* index into the given draw list, clipped to the given
* rect. TODO: Finish documenting
*/
void mergeToDrawList(const DrawList::size_type index, const Common::Rect &rect, DrawList &drawList) const;
/**
* Adds the given rect into the given rect list,
* merging it with other rects already inside the list,
* if possible, to avoid overdraw. TODO: Finish
* documenting
*/
void mergeToRectList(const Common::Rect &rect, RectList &rectList) const;
public:
/**
* Calculates the location and dimensions of dirty rects
* of the screen items in this plane and adds them to
* the given draw and erase lists, and synchronises this
* plane's list of screen items to the given visible
* plane.
*/
void calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList &drawList, RectList &eraseList);
/**
* TODO: Documentation
*/
void filterDownEraseRects(DrawList &drawList, RectList &eraseList, RectList &transparentEraseList) const;
/**
* TODO: Documentation
*/
void filterUpEraseRects(DrawList &drawList, RectList &eraseList) const;
/**
* TODO: Documentation
*/
void filterUpDrawRects(DrawList &transparentDrawList, const DrawList &drawList) const;
/**
* Updates all of the plane's non-deleted screen items
* and adds them to the given draw and erase lists.
*/
void redrawAll(Plane *visiblePlane, const PlaneList &planeList, DrawList &drawList, RectList &eraseList);
};
#pragma mark -
#pragma mark PlaneList
typedef Common::Array<Plane *> PlaneListBase;
class PlaneList : public PlaneListBase {
private:
inline static bool sortHelper(const Plane *a, const Plane *b) {
return *a < *b;
}
using PlaneListBase::push_back;
public:
// A method for finding the index of a plane inside a
// PlaneList is used because entries in the main plane
// list and visible plane list of GfxFrameout are
// synchronised by index
int findIndexByObject(const reg_t object) const;
Plane *findByObject(const reg_t object) const;
/**
* Gets the priority of the top plane in the plane list.
*/
int16 getTopPlanePriority() const;
/**
* Gets the priority of the top plane in the plane list
* created by a game script.
*/
int16 getTopSciPlanePriority() const;
void add(Plane *plane);
void clear();
using PlaneListBase::erase;
void erase(Plane *plane);
inline void sort() {
Common::sort(begin(), end(), sortHelper);
}
};
}
#endif

View file

@ -76,6 +76,11 @@ public:
byte getColorWhite() { return _colorWhite; } byte getColorWhite() { return _colorWhite; }
byte getColorDefaultVectorData() { return _colorDefaultVectorData; } byte getColorDefaultVectorData() { return _colorDefaultVectorData; }
#ifdef ENABLE_SCI32
byte *getDisplayScreen() { return _displayScreen; }
byte *getPriorityScreen() { return _priorityScreen; }
#endif
void clearForRestoreGame(); void clearForRestoreGame();
void copyToScreen(); void copyToScreen();
void copyFromScreen(byte *buffer); void copyFromScreen(byte *buffer);

View file

@ -0,0 +1,534 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "sci/console.h"
#include "sci/resource.h"
#include "sci/engine/kernel.h"
#include "sci/engine/selector.h"
#include "sci/engine/state.h"
#include "sci/graphics/celobj32.h"
#include "sci/graphics/frameout.h"
#include "sci/graphics/screen_item32.h"
#include "sci/graphics/view.h"
namespace Sci {
#pragma mark ScreenItem
uint16 ScreenItem::_nextObjectId = 20000;
ScreenItem::ScreenItem(const reg_t object) :
_mirrorX(false),
_pictureId(-1),
_celObj(nullptr),
_object(object),
_deleted(0),
_updated(0),
_created(g_sci->_gfxFrameout->getScreenCount()) {
SegManager *segMan = g_sci->getEngineState()->_segMan;
setFromObject(segMan, object, true, true);
_plane = readSelector(segMan, object, SELECTOR(plane));
}
ScreenItem::ScreenItem(const reg_t plane, const CelInfo32 &celInfo) :
_position(0, 0),
_z(0),
_object(make_reg(0, _nextObjectId++)),
_celInfo(celInfo),
_plane(plane),
_celObj(nullptr),
_useInsetRect(false),
_fixPriority(false),
_mirrorX(false),
_pictureId(-1),
_updated(0),
_deleted(0),
_created(g_sci->_gfxFrameout->getScreenCount()) {}
ScreenItem::ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Rect &rect) :
_position(rect.left, rect.top),
_z(0),
_object(make_reg(0, _nextObjectId++)),
_celInfo(celInfo),
_plane(plane),
_celObj(nullptr),
_useInsetRect(false),
_fixPriority(false),
_mirrorX(false),
_pictureId(-1),
_updated(0),
_deleted(0),
_created(g_sci->_gfxFrameout->getScreenCount()) {
if (celInfo.type == kCelTypeColor) {
_insetRect = rect;
}
}
ScreenItem::ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Rect &rect, const ScaleInfo &scaleInfo) :
_position(rect.left, rect.top),
_z(0),
_object(make_reg(0, _nextObjectId++)),
_celInfo(celInfo),
_plane(plane),
_celObj(nullptr),
_useInsetRect(false),
_fixPriority(false),
_mirrorX(false),
_pictureId(-1),
_updated(0),
_deleted(0),
_created(g_sci->_gfxFrameout->getScreenCount()),
_scale(scaleInfo) {}
ScreenItem::ScreenItem(const ScreenItem &other) :
_object(other._object),
_plane(other._plane),
_celInfo(other._celInfo),
_celObj(nullptr),
_screenRect(other._screenRect),
_mirrorX(other._mirrorX),
_useInsetRect(other._useInsetRect),
_scale(other._scale),
_scaledPosition(other._scaledPosition) {
if (other._useInsetRect) {
_insetRect = other._insetRect;
}
}
void ScreenItem::operator=(const ScreenItem &other) {
_celInfo = other._celInfo;
_screenRect = other._screenRect;
_mirrorX = other._mirrorX;
_useInsetRect = other._useInsetRect;
if (other._useInsetRect) {
_insetRect = other._insetRect;
}
_scale = other._scale;
_scaledPosition = other._scaledPosition;
}
void ScreenItem::init() {
_nextObjectId = 20000;
}
void ScreenItem::setFromObject(SegManager *segMan, const reg_t object, const bool updateCel, const bool updateBitmap) {
_position.x = readSelectorValue(segMan, object, SELECTOR(x));
_position.y = readSelectorValue(segMan, object, SELECTOR(y));
_scale.x = readSelectorValue(segMan, object, SELECTOR(scaleX));
_scale.y = readSelectorValue(segMan, object, SELECTOR(scaleY));
_scale.max = readSelectorValue(segMan, object, SELECTOR(maxScale));
_scale.signal = (ScaleSignals32)(readSelectorValue(segMan, object, SELECTOR(scaleSignal)) & 3);
if (updateCel) {
_celInfo.resourceId = (GuiResourceId)readSelectorValue(segMan, object, SELECTOR(view));
_celInfo.loopNo = readSelectorValue(segMan, object, SELECTOR(loop));
_celInfo.celNo = readSelectorValue(segMan, object, SELECTOR(cel));
if (_celInfo.resourceId <= kPlanePic) {
// TODO: Enhance GfxView or ResourceManager to allow
// metadata for resources to be retrieved once, from a
// single location
Resource *view = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, _celInfo.resourceId), false);
if (!view) {
error("Failed to load resource %d", _celInfo.resourceId);
}
// NOTE: +2 because the header size field itself is excluded from
// the header size in the data
const uint16 headerSize = READ_SCI11ENDIAN_UINT16(view->data) + 2;
const uint8 loopCount = view->data[2];
const uint8 loopSize = view->data[12];
if (_celInfo.loopNo >= loopCount) {
const int maxLoopNo = loopCount - 1;
_celInfo.loopNo = maxLoopNo;
writeSelectorValue(segMan, object, SELECTOR(loop), maxLoopNo);
}
byte *loopData = view->data + headerSize + (_celInfo.loopNo * loopSize);
const int8 seekEntry = loopData[0];
if (seekEntry != -1) {
loopData = view->data + headerSize + (seekEntry * loopSize);
}
const uint8 celCount = loopData[2];
if (_celInfo.celNo >= celCount) {
const int maxCelNo = celCount - 1;
_celInfo.celNo = maxCelNo;
writeSelectorValue(segMan, object, SELECTOR(cel), maxCelNo);
}
}
}
if (updateBitmap) {
const reg_t bitmap = readSelector(segMan, object, SELECTOR(bitmap));
if (!bitmap.isNull()) {
_celInfo.bitmap = bitmap;
_celInfo.type = kCelTypeMem;
} else {
_celInfo.bitmap = NULL_REG;
_celInfo.type = kCelTypeView;
}
}
if (updateCel || updateBitmap) {
delete _celObj;
_celObj = nullptr;
}
if (readSelectorValue(segMan, object, SELECTOR(fixPriority))) {
_fixPriority = true;
_priority = readSelectorValue(segMan, object, SELECTOR(priority));
} else {
_fixPriority = false;
writeSelectorValue(segMan, object, SELECTOR(priority), _position.y);
}
_z = readSelectorValue(segMan, object, SELECTOR(z));
_position.y -= _z;
if (readSelectorValue(segMan, object, SELECTOR(useInsetRect))) {
_useInsetRect = true;
_insetRect.left = readSelectorValue(segMan, object, SELECTOR(inLeft));
_insetRect.top = readSelectorValue(segMan, object, SELECTOR(inTop));
_insetRect.right = readSelectorValue(segMan, object, SELECTOR(inRight));
_insetRect.bottom = readSelectorValue(segMan, object, SELECTOR(inBottom));
} else {
_useInsetRect = false;
}
// TODO: SCI2.1/SQ6 engine clears this flag any time ScreenItem::Update(MemID)
// or ScreenItem::ScreenItem(MemID) are called, but doing this breaks
// view cycling because the flag isn't being set again later. There are over
// 100 places in the engine code where this flag is set, so it is probably
// a matter of figuring out what all of those calls are that re-set it. For
// now, since these are the *only* calls that clear this flag, we can just
// leave it set all the time.
// segMan->getObject(object)->clearInfoSelectorFlag(kInfoFlagViewVisible);
}
void ScreenItem::calcRects(const Plane &plane) {
const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth;
const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight;
Common::Rect celRect(_celObj->_width, _celObj->_height);
if (_useInsetRect) {
if (_insetRect.intersects(celRect)) {
_insetRect.clip(celRect);
} else {
_insetRect = Common::Rect();
}
} else {
_insetRect = celRect;
}
Ratio newRatioX;
Ratio newRatioY;
if (_scale.signal & kScaleSignalDoScaling32) {
if (_scale.signal & kScaleSignalUseVanishingPoint) {
int num = _scale.max * (_position.y - plane._vanishingPoint.y) / (scriptWidth - plane._vanishingPoint.y);
newRatioX = Ratio(num, 128);
newRatioY = Ratio(num, 128);
} else {
newRatioX = Ratio(_scale.x, 128);
newRatioY = Ratio(_scale.y, 128);
}
}
if (newRatioX.getNumerator() && newRatioY.getNumerator()) {
_screenItemRect = _insetRect;
if (_celObj->_scaledWidth != scriptWidth || _celObj->_scaledHeight != scriptHeight) {
if (_useInsetRect) {
Ratio celScriptXRatio(_celObj->_scaledWidth, scriptWidth);
Ratio celScriptYRatio(_celObj->_scaledHeight, scriptHeight);
mulru(_screenItemRect, celScriptXRatio, celScriptYRatio);
if (_screenItemRect.intersects(celRect)) {
_screenItemRect.clip(celRect);
} else {
_screenItemRect = Common::Rect();
}
}
int displaceX = _celObj->_displace.x;
int displaceY = _celObj->_displace.y;
if (_mirrorX != _celObj->_mirrorX && _celInfo.type != kCelTypePic) {
displaceX = _celObj->_width - _celObj->_displace.x - 1;
}
if (!newRatioX.isOne() || !newRatioY.isOne()) {
mulru(_screenItemRect, newRatioX, newRatioY);
displaceX = (displaceX * newRatioX).toInt();
displaceY = (displaceY * newRatioY).toInt();
}
Ratio celXRatio(screenWidth, _celObj->_scaledWidth);
Ratio celYRatio(screenHeight, _celObj->_scaledHeight);
displaceX = (displaceX * celXRatio).toInt();
displaceY = (displaceY * celYRatio).toInt();
mulru(_screenItemRect, celXRatio, celYRatio);
if (/* TODO: dword_C6288 */ false && _celInfo.type == kCelTypePic) {
_scaledPosition.x = _position.x;
_scaledPosition.y = _position.y;
} else {
_scaledPosition.x = (_position.x * screenWidth / scriptWidth) - displaceX;
_scaledPosition.y = (_position.y * screenHeight / scriptHeight) - displaceY;
}
_screenItemRect.translate(_scaledPosition.x, _scaledPosition.y);
if (_mirrorX != _celObj->_mirrorX && _celInfo.type == kCelTypePic) {
Common::Rect temp(_insetRect);
if (!newRatioX.isOne()) {
mulru(temp, newRatioX, Ratio());
}
mulru(temp, celXRatio, Ratio());
CelObjPic *celObjPic = dynamic_cast<CelObjPic *>(_celObj);
temp.translate(celObjPic->_relativePosition.x * screenWidth / scriptWidth - displaceX, 0);
// TODO: This is weird, and probably wrong calculation of widths
// due to BR-inclusion
int deltaX = plane._planeRect.right - plane._planeRect.left + 1 - temp.right - 1 - temp.left;
_scaledPosition.x += deltaX;
_screenItemRect.translate(deltaX, 0);
}
_scaledPosition.x += plane._planeRect.left;
_scaledPosition.y += plane._planeRect.top;
_screenItemRect.translate(plane._planeRect.left, plane._planeRect.top);
_ratioX = newRatioX * Ratio(screenWidth, _celObj->_scaledWidth);
_ratioY = newRatioY * Ratio(screenHeight, _celObj->_scaledHeight);
} else {
int displaceX = _celObj->_displace.x;
if (_mirrorX != _celObj->_mirrorX && _celInfo.type != kCelTypePic) {
displaceX = _celObj->_width - _celObj->_displace.x - 1;
}
if (!newRatioX.isOne() || !newRatioY.isOne()) {
mulru(_screenItemRect, newRatioX, newRatioY);
// TODO: This was in the original code, baked into the
// multiplication though it is not immediately clear
// why this is the only one that reduces the BR corner
_screenItemRect.right -= 1;
_screenItemRect.bottom -= 1;
}
_scaledPosition.x = _position.x - (displaceX * newRatioX).toInt();
_scaledPosition.y = _position.y - (_celObj->_displace.y * newRatioY).toInt();
_screenItemRect.translate(_scaledPosition.x, _scaledPosition.y);
if (_mirrorX != _celObj->_mirrorX && _celInfo.type == kCelTypePic) {
Common::Rect temp(_insetRect);
if (!newRatioX.isOne()) {
mulru(temp, newRatioX, Ratio());
temp.right -= 1;
}
CelObjPic *celObjPic = dynamic_cast<CelObjPic *>(_celObj);
temp.translate(celObjPic->_relativePosition.x - (displaceX * newRatioX).toInt(), celObjPic->_relativePosition.y - (_celObj->_displace.y * newRatioY).toInt());
// TODO: This is weird, and probably wrong calculation of widths
// due to BR-inclusion
int deltaX = plane._gameRect.right - plane._gameRect.left + 1 - temp.right - 1 - temp.left;
_scaledPosition.x += deltaX;
_screenItemRect.translate(deltaX, 0);
}
_scaledPosition.x += plane._gameRect.left;
_scaledPosition.y += plane._gameRect.top;
_screenItemRect.translate(plane._gameRect.left, plane._gameRect.top);
if (screenWidth != _celObj->_scaledWidth || _celObj->_scaledHeight != screenHeight) {
Ratio celXRatio(screenWidth, _celObj->_scaledWidth);
Ratio celYRatio(screenHeight, _celObj->_scaledHeight);
mulru(_scaledPosition, celXRatio, celYRatio);
mulru(_screenItemRect, celXRatio, celYRatio, 1);
}
_ratioX = newRatioX * Ratio(screenWidth, _celObj->_scaledWidth);
_ratioY = newRatioY * Ratio(screenHeight, _celObj->_scaledHeight);
}
_screenRect = _screenItemRect;
if (_screenRect.intersects(plane._screenRect)) {
_screenRect.clip(plane._screenRect);
} else {
_screenRect.right = 0;
_screenRect.bottom = 0;
_screenRect.left = 0;
_screenRect.top = 0;
}
if (!_fixPriority) {
_priority = _z + _position.y;
}
} else {
_screenRect.left = 0;
_screenRect.top = 0;
_screenRect.right = 0;
_screenRect.bottom = 0;
}
}
CelObj &ScreenItem::getCelObj() {
if (_celObj == nullptr) {
switch (_celInfo.type) {
case kCelTypeView:
_celObj = new CelObjView(_celInfo.resourceId, _celInfo.loopNo, _celInfo.celNo);
break;
case kCelTypePic:
error("Internal error, pic screen item with no cel.");
break;
case kCelTypeMem:
_celObj = new CelObjMem(_celInfo.bitmap);
break;
case kCelTypeColor:
_celObj = new CelObjColor(_celInfo.color, _insetRect.width(), _insetRect.height());
break;
}
}
return *_celObj;
}
void ScreenItem::printDebugInfo(Console *con) const {
con->debugPrintf("prio %d, x %d, y %d, z: %d, scaledX: %d, scaledY: %d flags: %d\n",
_priority,
_position.x,
_position.y,
_z,
_scaledPosition.x,
_scaledPosition.y,
_created | (_updated << 1) | (_deleted << 2)
);
con->debugPrintf(" screen rect (%d, %d, %d, %d)\n", PRINT_RECT(_screenRect));
if (_useInsetRect) {
con->debugPrintf(" inset rect: (%d, %d, %d, %d)\n", PRINT_RECT(_insetRect));
}
Common::String celType;
switch (_celInfo.type) {
case kCelTypePic:
celType = "pic";
break;
case kCelTypeView:
celType = "view";
break;
case kCelTypeColor:
celType = "color";
break;
case kCelTypeMem:
celType = "mem";
break;
}
con->debugPrintf(" type: %s, res %d, loop %d, cel %d, bitmap %04x:%04x, color: %d\n",
celType.c_str(),
_celInfo.resourceId,
_celInfo.loopNo,
_celInfo.celNo,
PRINT_REG(_celInfo.bitmap),
_celInfo.color
);
if (_celObj != nullptr) {
con->debugPrintf(" width %d, height %d, scaledWidth %d, scaledHeight %d\n",
_celObj->_width,
_celObj->_height,
_celObj->_scaledWidth,
_celObj->_scaledHeight
);
}
}
void ScreenItem::update(const reg_t object) {
SegManager *segMan = g_sci->getEngineState()->_segMan;
const GuiResourceId view = readSelectorValue(segMan, object, SELECTOR(view));
const int16 loopNo = readSelectorValue(segMan, object, SELECTOR(loop));
const int16 celNo = readSelectorValue(segMan, object, SELECTOR(cel));
const bool updateCel = (
_celInfo.resourceId != view ||
_celInfo.loopNo != loopNo ||
_celInfo.celNo != celNo
);
const bool updateBitmap = !readSelector(segMan, object, SELECTOR(bitmap)).isNull();
setFromObject(segMan, object, updateCel, updateBitmap);
if (!_created) {
_updated = g_sci->_gfxFrameout->getScreenCount();
}
_deleted = 0;
}
#pragma mark -
#pragma mark ScreenItemList
ScreenItem *ScreenItemList::findByObject(const reg_t object) const {
const_iterator screenItemIt = Common::find_if(begin(), end(), FindByObject<ScreenItem *>(object));
if (screenItemIt == end()) {
return nullptr;
}
return *screenItemIt;
}
void ScreenItemList::sort() {
// TODO: SCI engine used _unsorted as an array of indexes into the
// list itself and then performed the same swap operations on the
// _unsorted array as the _storage array during sorting, but the
// only reason to do this would be if some of the pointers in the
// list were replaced so the pointer values themselves couldnt
// simply be recorded and then restored later. It is not yet
// verified whether this simplification of the sort/unsort is
// safe.
for (size_type i = 0; i < size(); ++i) {
_unsorted[i] = (*this)[i];
}
Common::sort(begin(), end(), sortHelper);
}
void ScreenItemList::unsort() {
for (size_type i = 0; i < size(); ++i) {
(*this)[i] = _unsorted[i];
}
}
}

View file

@ -0,0 +1,280 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef SCI_GRAPHICS_SCREEN_ITEM32_H
#define SCI_GRAPHICS_SCREEN_ITEM32_H
#include "common/rect.h"
#include "sci/graphics/celobj32.h"
#include "sci/graphics/lists32.h"
namespace Sci {
enum ScaleSignals32 {
kScaleSignalNone = 0,
kScaleSignalDoScaling32 = 1, // enables scaling when drawing that cel (involves scaleX and scaleY)
kScaleSignalUseVanishingPoint = 2,
// TODO: Is this actually a thing? I have not seen it and
// the original engine masks &3 where it uses scale signals.
kScaleSignalDisableGlobalScaling32 = 4
};
struct ScaleInfo {
int x, y, max;
ScaleSignals32 signal;
ScaleInfo() : x(128), y(128), max(100), signal(kScaleSignalNone) {}
};
class CelObj;
class Plane;
class SegManager;
#pragma mark -
#pragma mark ScreenItem
/**
* A ScreenItem is the engine-side representation of a
* game script View.
*/
class ScreenItem {
private:
/**
* A serial used for screen items that are generated
* inside the graphics engine, rather than the
* interpreter.
*/
static uint16 _nextObjectId;
/**
* The parent plane of this screen item.
*/
reg_t _plane;
/**
* Scaling data used to calculate the final screen
* dimensions of the screen item as well as the scaling
* ratios used when drawing the item to screen.
*/
ScaleInfo _scale;
/**
* The position & dimensions of the screen item in
* screen coordinates. This rect includes the offset
* of the parent plane, but is not clipped to the
* screen, so may include coordinates that are
* offscreen.
*/
Common::Rect _screenItemRect;
/**
* TODO: Document
*/
bool _useInsetRect;
/**
* TODO: Documentation
* The insetRect is also used to describe the fill
* rectangle of a screen item that is drawn using
* CelObjColor.
*/
Common::Rect _insetRect;
/**
* The z-index of the screen item in pseudo-3D space.
* Higher values are drawn on top of lower values.
*/
int _z;
/**
* Sets the common properties of a screen item that must
* be set both during creation and update of a screen
* item.
*/
void setFromObject(SegManager *segMan, const reg_t object, const bool updateCel, const bool updateBitmap);
public:
/**
* A descriptor for the cel object represented by the
* screen item.
*/
CelInfo32 _celInfo;
/**
* The cel object used to actually render the screen
* item. This member is populated by calling
* `getCelObj`.
*/
CelObj *_celObj;
/**
* If set, the priority for this screen item is fixed
* in place. Otherwise, the priority of the screen item
* is calculated from its y-position + z-index.
*/
bool _fixPriority;
/**
* The rendering priority of the screen item, relative
* only to the other screen items within the same plane.
* Higher priorities are drawn above lower priorities.
*/
int16 _priority;
/**
* The top-left corner of the screen item, in game
* script coordinates, relative to the parent plane.
*/
Common::Point _position;
/**
* The associated View script object that was
* used to create the ScreenItem, or a numeric
* value in the case of a ScreenItem that was
* generated outside of the VM.
*/
reg_t _object;
/**
* For screen items representing picture resources,
* the resource ID of the picture.
*/
GuiResourceId _pictureId;
/**
* Flags indicating the state of the screen item.
* - `created` is set when the screen item is first
* created, either from a VM object or from within the
* engine itself
* - `updated` is set when `created` is not already set
* and the screen item is updated from a VM object
* - `deleted` is set by the parent plane, if the parent
* plane is a pic type and its picture resource ID has
* changed
*/
int _created, _updated, _deleted; // ?
/**
* For screen items that represent picture cels, this
* value is set to match the `_mirrorX` property of the
* parent plane and indicates that the cel should be
* drawn horizontally mirrored. For final drawing, it is
* XORed with the `_mirrorX` property of the cel object.
* The cel object's `_mirrorX` property comes from the
* resource data itself.
*/
bool _mirrorX;
/**
* The scaling ratios to use when drawing this screen
* item. These values are calculated according to the
* scale info whenever the screen item is updated.
*/
Ratio _ratioX, _ratioY;
/**
* The top-left corner of the screen item, in screen
* coordinates.
*/
Common::Point _scaledPosition;
/**
* The position & dimensions of the screen item in
* screen coordinates. This rect includes the offset of
* the parent plane and is clipped to the screen.
*/
Common::Rect _screenRect;
/**
* Initialises static Plane members.
*/
static void init();
ScreenItem(const reg_t screenItem);
ScreenItem(const reg_t plane, const CelInfo32 &celInfo);
ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Rect &rect);
ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Rect &rect, const ScaleInfo &scaleInfo);
ScreenItem(const ScreenItem &other);
void operator=(const ScreenItem &);
inline bool operator<(const ScreenItem &other) const {
if (_priority < other._priority) {
return true;
}
if (_priority == other._priority) {
if (_position.y + _z < other._position.y + other._z) {
return true;
}
if (_position.y + _z == other._position.y + other._z) {
return false;
// TODO: Failure in SQ6 room 220
// return _object < other._object;
}
}
return false;
}
/**
* Calculates the dimensions and scaling parameters for
* the screen item, using the given plane as the parent
* plane for screen rect positioning.
*
* @note This method was called Update in SCI engine.
*/
void calcRects(const Plane &plane);
/**
* Retrieves the corresponding cel object for this
* screen item. If a cel object does not already exist,
* one will be created and assigned.
*/
CelObj &getCelObj();
void printDebugInfo(Console *con) const;
/**
* Updates the properties of the screen item from a
* VM object.
*/
void update(const reg_t object);
};
#pragma mark -
#pragma mark ScreenItemList
typedef StablePointerArray<ScreenItem, 250> ScreenItemListBase;
class ScreenItemList : public ScreenItemListBase {
static bool inline sortHelper(const ScreenItem *a, const ScreenItem *b) {
return *a < *b;
}
public:
ScreenItem *_unsorted[250];
ScreenItem *findByObject(const reg_t object) const;
void sort();
void unsort();
};
}
#endif

View file

@ -81,10 +81,13 @@ MODULE_OBJS := \
ifdef ENABLE_SCI32 ifdef ENABLE_SCI32
MODULE_OBJS += \ MODULE_OBJS += \
engine/kgraphics32.o \ engine/kgraphics32.o \
graphics/celobj32.o \
graphics/controls32.o \ graphics/controls32.o \
graphics/frameout.o \ graphics/frameout.o \
graphics/paint32.o \ graphics/paint32.o \
graphics/plane32.o \
graphics/palette32.o \ graphics/palette32.o \
graphics/screen_item32.o\
graphics/text32.o \ graphics/text32.o \
video/robot_decoder.o video/robot_decoder.o
endif endif

View file

@ -694,6 +694,7 @@ void SciEngine::initGraphics() {
_gfxControls32 = new GfxControls32(_gamestate->_segMan, _gfxCache, _gfxText32); _gfxControls32 = new GfxControls32(_gamestate->_segMan, _gfxCache, _gfxText32);
_robotDecoder = new RobotDecoder(getPlatform() == Common::kPlatformMacintosh); _robotDecoder = new RobotDecoder(getPlatform() == Common::kPlatformMacintosh);
_gfxFrameout = new GfxFrameout(_gamestate->_segMan, _resMan, _gfxCoordAdjuster, _gfxCache, _gfxScreen, _gfxPalette32, _gfxPaint32); _gfxFrameout = new GfxFrameout(_gamestate->_segMan, _resMan, _gfxCoordAdjuster, _gfxCache, _gfxScreen, _gfxPalette32, _gfxPaint32);
_gfxFrameout->run();
} else { } else {
#endif #endif
// SCI0-SCI1.1 graphic objects creation // SCI0-SCI1.1 graphic objects creation