SCI: Proper implementation of text drawing for SCI2+
This commit is contained in:
parent
1402b47674
commit
96ce226967
8 changed files with 96 additions and 161 deletions
|
@ -442,6 +442,7 @@ reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv);
|
|||
reg_t kDeleteScreenItem(EngineState *s, int argc, reg_t *argv);
|
||||
// Text
|
||||
reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv);
|
||||
reg_t kDisposeTextBitmap(EngineState *s, int argc, reg_t *argv);
|
||||
// "Planes" in SCI32 are pictures
|
||||
reg_t kAddPlane(EngineState *s, int argc, reg_t *argv);
|
||||
reg_t kDeletePlane(EngineState *s, int argc, reg_t *argv);
|
||||
|
|
|
@ -477,6 +477,7 @@ static SciKernelMapEntry s_kernelMap[] = {
|
|||
{ MAP_CALL(CreateTextBitmap), SIG_EVERYWHERE, "i(.*)", NULL, NULL },
|
||||
{ MAP_CALL(DeletePlane), SIG_EVERYWHERE, "o", NULL, NULL },
|
||||
{ MAP_CALL(DeleteScreenItem), SIG_EVERYWHERE, "o", NULL, NULL },
|
||||
{ MAP_CALL(DisposeTextBitmap), SIG_EVERYWHERE, "r", NULL, NULL },
|
||||
{ MAP_CALL(FrameOut), SIG_EVERYWHERE, "", NULL, NULL },
|
||||
{ MAP_CALL(GetHighPlanePri), SIG_EVERYWHERE, "", NULL, NULL },
|
||||
{ MAP_CALL(InPolygon), SIG_EVERYWHERE, "iio", NULL, NULL },
|
||||
|
@ -501,7 +502,6 @@ static SciKernelMapEntry s_kernelMap[] = {
|
|||
// SCI2 unmapped functions - TODO!
|
||||
|
||||
// SetScroll - called by script 64909, Styler::doit()
|
||||
// DisposeTextBitmap
|
||||
// PalCycle - called by Game::newRoom. Related to RemapColors.
|
||||
// VibrateMouse - used in QFG4
|
||||
// ObjectIntersect - used in QFG4
|
||||
|
|
|
@ -1405,6 +1405,11 @@ reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) {
|
|||
}
|
||||
}
|
||||
|
||||
reg_t kDisposeTextBitmap(EngineState *s, int argc, reg_t *argv) {
|
||||
g_sci->_gfxText32->disposeTextBitmap(argv[0]);
|
||||
return s->r_acc;
|
||||
}
|
||||
|
||||
reg_t kGetWindowsOption(EngineState *s, int argc, reg_t *argv) {
|
||||
uint16 windowsOption = argv[0].toUint16();
|
||||
switch (windowsOption) {
|
||||
|
|
|
@ -167,6 +167,7 @@ void Kernel::mapSelectors() {
|
|||
#ifdef ENABLE_SCI32
|
||||
FIND_SELECTOR(data);
|
||||
FIND_SELECTOR(picture);
|
||||
FIND_SELECTOR(bitmap);
|
||||
FIND_SELECTOR(plane);
|
||||
FIND_SELECTOR(top);
|
||||
FIND_SELECTOR(left);
|
||||
|
|
|
@ -132,6 +132,7 @@ struct SelectorCache {
|
|||
#ifdef ENABLE_SCI32
|
||||
Selector data; // Used by Array()/String()
|
||||
Selector picture; // Used to hold the picture ID for SCI32 pictures
|
||||
Selector bitmap; // Used to hold the text bitmap for SCI32 texts
|
||||
|
||||
Selector plane;
|
||||
Selector top;
|
||||
|
|
|
@ -547,16 +547,7 @@ void GfxFrameout::kernelFrameout() {
|
|||
} else {
|
||||
// Most likely a text entry
|
||||
if (lookupSelector(_segMan, itemEntry->object, SELECTOR(text), NULL, NULL) == kSelectorVariable) {
|
||||
TextEntry *textEntry = g_sci->_gfxText32->getTextEntry(itemEntry->object);
|
||||
uint16 startX = ((textEntry->x * _screen->getWidth()) / scriptsRunningWidth) + it->planeRect.left;
|
||||
uint16 startY = ((textEntry->y * _screen->getHeight()) / scriptsRunningHeight) + it->planeRect.top;
|
||||
// Upscale the coordinates/width if the fonts are already upscaled
|
||||
if (_screen->fontIsUpscaled()) {
|
||||
startX = startX * _screen->getDisplayWidth() / _screen->getWidth();
|
||||
startY = startY * _screen->getDisplayHeight() / _screen->getHeight();
|
||||
}
|
||||
|
||||
g_sci->_gfxText32->drawTextBitmap(itemEntry->object, startX, startY, it->planeRect.width());
|
||||
g_sci->_gfxText32->drawTextBitmap(itemEntry->object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,163 +43,114 @@ GfxText32::GfxText32(SegManager *segMan, GfxCache *fonts, GfxScreen *screen)
|
|||
}
|
||||
|
||||
GfxText32::~GfxText32() {
|
||||
purgeCache();
|
||||
}
|
||||
|
||||
void GfxText32::purgeCache() {
|
||||
for (TextCache::iterator cacheIterator = _textCache.begin(); cacheIterator != _textCache.end(); cacheIterator++) {
|
||||
delete[] cacheIterator->_value->surface;
|
||||
delete cacheIterator->_value;
|
||||
cacheIterator->_value = 0;
|
||||
}
|
||||
|
||||
_textCache.clear();
|
||||
}
|
||||
|
||||
reg_t GfxText32::createTextBitmap(reg_t textObject, uint16 maxWidth, uint16 maxHeight) {
|
||||
if (_textCache.size() >= MAX_CACHED_TEXTS)
|
||||
purgeCache();
|
||||
|
||||
uint32 textId = (textObject.segment << 16) | textObject.offset;
|
||||
|
||||
if (_textCache.contains(textId)) {
|
||||
// Delete the old entry
|
||||
TextEntry *oldEntry = _textCache[textId];
|
||||
delete[] oldEntry->surface;
|
||||
delete oldEntry;
|
||||
_textCache.erase(textId);
|
||||
}
|
||||
|
||||
_textCache[textId] = createTextEntry(textObject, maxWidth, maxHeight);
|
||||
|
||||
// TODO: Create a new hunk pointer with the created surface
|
||||
return NULL_REG;
|
||||
}
|
||||
|
||||
// TODO: Finish this!
|
||||
void GfxText32::drawTextBitmap(reg_t textObject, uint16 textX, uint16 textY, uint16 planeWidth) {
|
||||
uint32 textId = (textObject.segment << 16) | textObject.offset;
|
||||
|
||||
if (!_textCache.contains(textId))
|
||||
createTextBitmap(textObject);
|
||||
|
||||
TextEntry *entry = _textCache[textId];
|
||||
|
||||
// This draws text the "SCI0-SCI11" way. In SCI2, text is prerendered in kCreateTextBitmap
|
||||
// TODO: rewrite this the "SCI2" way (i.e. implement the text buffer to draw inside kCreateTextBitmap)
|
||||
GfxFont *font = _cache->getFont(readSelectorValue(_segMan, textObject, SELECTOR(font)));
|
||||
bool dimmed = readSelectorValue(_segMan,textObject, SELECTOR(dimmed));
|
||||
uint16 foreColor = readSelectorValue(_segMan, textObject, SELECTOR(fore));
|
||||
|
||||
const char *txt = entry->text.c_str();
|
||||
int16 charCount;
|
||||
uint16 maxWidth = (planeWidth > 0) ? planeWidth : _screen->getWidth() - textX;
|
||||
|
||||
while (*txt) {
|
||||
charCount = GetLongest(txt, maxWidth, font);
|
||||
if (charCount == 0)
|
||||
break;
|
||||
|
||||
uint16 curX = textX;
|
||||
|
||||
for (int i = 0; i < charCount; i++) {
|
||||
unsigned char curChar = txt[i];
|
||||
font->draw(curChar, textY, curX, foreColor, dimmed);
|
||||
curX += font->getCharWidth(curChar);
|
||||
}
|
||||
|
||||
textY += font->getHeight();
|
||||
txt += charCount;
|
||||
while (*txt == ' ')
|
||||
txt++; // skip over breaking spaces
|
||||
}
|
||||
|
||||
// TODO: The "SCI2" way of font drawing. Currently buggy
|
||||
/*
|
||||
for (int x = textX; x < entry->width; x++) {
|
||||
for (int y = textY; y < entry->height; y++) {
|
||||
byte pixel = entry->surface[y * entry->width + x];
|
||||
if (pixel)
|
||||
_screen->putPixel(x, y, 1, pixel, 0, 0);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
TextEntry *GfxText32::getTextEntry(reg_t textObject) {
|
||||
uint32 textId = (textObject.segment << 16) | textObject.offset;
|
||||
|
||||
if (!_textCache.contains(textId))
|
||||
createTextBitmap(textObject);
|
||||
|
||||
return _textCache[textId];
|
||||
}
|
||||
|
||||
// TODO: Finish this! Currently buggy.
|
||||
TextEntry *GfxText32::createTextEntry(reg_t textObject, uint16 maxWidth, uint16 maxHeight) {
|
||||
reg_t stringObject = readSelector(_segMan, textObject, SELECTOR(text));
|
||||
|
||||
// TODO: maxWidth, maxHeight (if > 0)
|
||||
|
||||
// The object in the text selector of the item can be either a raw string
|
||||
// or a Str object. In the latter case, we need to access the object's data
|
||||
// selector to get the raw string.
|
||||
if (_segMan->isHeapObject(stringObject))
|
||||
stringObject = readSelector(_segMan, stringObject, SELECTOR(data));
|
||||
|
||||
const char *text = _segMan->getString(stringObject).c_str();
|
||||
Common::String text = _segMan->getString(stringObject);
|
||||
GfxFont *font = _cache->getFont(readSelectorValue(_segMan, textObject, SELECTOR(font)));
|
||||
bool dimmed = readSelectorValue(_segMan, textObject, SELECTOR(dimmed));
|
||||
uint16 foreColor = readSelectorValue(_segMan, textObject, SELECTOR(fore));
|
||||
uint16 x = readSelectorValue(_segMan, textObject, SELECTOR(x));
|
||||
uint16 y = readSelectorValue(_segMan, textObject, SELECTOR(y));
|
||||
|
||||
// Now get the bounding box from the associated plane
|
||||
Common::Rect planeRect = getPlaneRect(textObject);
|
||||
uint16 width = planeRect.width();
|
||||
uint16 height = planeRect.height();
|
||||
|
||||
// Limit rectangle dimensions, if requested
|
||||
if (maxWidth > 0)
|
||||
width = maxWidth;
|
||||
if (maxHeight > 0)
|
||||
height = maxHeight;
|
||||
|
||||
int entrySize = width * height;
|
||||
reg_t memoryId = _segMan->allocateHunkEntry("TextBitmap()", entrySize);
|
||||
writeSelector(_segMan, textObject, SELECTOR(bitmap), memoryId);
|
||||
byte *memoryPtr = _segMan->getHunkPointer(memoryId);
|
||||
memset(memoryPtr, 0, entrySize);
|
||||
|
||||
int16 charCount = 0;
|
||||
uint16 curX = 0, curY = 0;
|
||||
const char *txt = text.c_str();
|
||||
|
||||
while (*txt) {
|
||||
charCount = GetLongest(txt, width, font);
|
||||
if (charCount == 0)
|
||||
break;
|
||||
|
||||
for (int i = 0; i < charCount; i++) {
|
||||
unsigned char curChar = txt[i];
|
||||
font->drawToBuffer(curChar, curY, curX, foreColor, dimmed, memoryPtr, width, height);
|
||||
curX += font->getCharWidth(curChar);
|
||||
}
|
||||
|
||||
curX = 0;
|
||||
curY += font->getHeight();
|
||||
txt += charCount;
|
||||
while (*txt == ' ')
|
||||
txt++; // skip over breaking spaces
|
||||
}
|
||||
|
||||
return memoryId;
|
||||
}
|
||||
|
||||
void GfxText32::disposeTextBitmap(reg_t hunkId) {
|
||||
_segMan->freeHunkEntry(hunkId);
|
||||
}
|
||||
|
||||
void GfxText32::drawTextBitmap(reg_t textObject) {
|
||||
reg_t hunkId = readSelector(_segMan, textObject, SELECTOR(bitmap));
|
||||
byte *surface = _segMan->getHunkPointer(hunkId);
|
||||
|
||||
if (!surface)
|
||||
error("Attempt to draw an invalid text bitmap");
|
||||
|
||||
int curByte = 0;
|
||||
Common::Rect nsRect = getNSRect(textObject);
|
||||
Common::Rect planeRect = getPlaneRect(textObject);
|
||||
uint16 textX = planeRect.left + nsRect.left;
|
||||
uint16 textY = planeRect.top + nsRect.top;
|
||||
uint16 width = nsRect.width();
|
||||
uint16 height = nsRect.height();
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
byte pixel = surface[curByte++];
|
||||
if (pixel)
|
||||
_screen->putPixel(x + textX, y + textY, 1, pixel, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Common::Rect GfxText32::getPlaneRect(reg_t textObject) {
|
||||
Common::Rect planeRect(0, 0, _screen->getWidth(), _screen->getHeight());
|
||||
|
||||
reg_t planeObject = readSelector(_segMan, textObject, SELECTOR(plane));
|
||||
Common::Rect planeRect;
|
||||
if (!planeObject.isNull()) {
|
||||
planeRect.top = readSelectorValue(_segMan, planeObject, SELECTOR(top));
|
||||
planeRect.left = readSelectorValue(_segMan, planeObject, SELECTOR(left));
|
||||
planeRect.bottom = readSelectorValue(_segMan, planeObject, SELECTOR(bottom)) + 1;
|
||||
planeRect.right = readSelectorValue(_segMan, planeObject, SELECTOR(right)) + 1;
|
||||
} else {
|
||||
planeRect.top = 0;
|
||||
planeRect.left = 0;
|
||||
planeRect.bottom = _screen->getHeight();
|
||||
planeRect.right = _screen->getWidth();
|
||||
}
|
||||
|
||||
TextEntry *newEntry = new TextEntry();
|
||||
newEntry->object = stringObject;
|
||||
newEntry->x = x;
|
||||
newEntry->y = y;
|
||||
newEntry->width = planeRect.width();
|
||||
newEntry->height = planeRect.height();
|
||||
newEntry->surface = new byte[newEntry->width * newEntry->height];
|
||||
memset(newEntry->surface, 0, newEntry->width * newEntry->height);
|
||||
newEntry->text = _segMan->getString(stringObject);
|
||||
|
||||
int16 /*maxTextWidth = 0,*/ charCount = 0;
|
||||
uint16 curX = 0, curY = 0;
|
||||
|
||||
while (*text) {
|
||||
charCount = GetLongest(text, planeRect.width(), font);
|
||||
if (charCount == 0)
|
||||
break;
|
||||
|
||||
for (int i = 0; i < charCount; i++) {
|
||||
unsigned char curChar = text[i];
|
||||
font->drawToBuffer(curChar, curY, curX, foreColor, dimmed, newEntry->surface, newEntry->width, newEntry->height);
|
||||
curX += font->getCharWidth(curChar);
|
||||
return planeRect;
|
||||
}
|
||||
|
||||
curY += font->getHeight();
|
||||
text += charCount;
|
||||
while (*text == ' ')
|
||||
text++; // skip over breaking spaces
|
||||
}
|
||||
|
||||
return newEntry;
|
||||
Common::Rect GfxText32::getNSRect(reg_t textObject) {
|
||||
Common::Rect nsRect;
|
||||
nsRect.top = readSelectorValue(_segMan, textObject, SELECTOR(nsTop));
|
||||
nsRect.left = readSelectorValue(_segMan, textObject, SELECTOR(nsLeft));
|
||||
nsRect.bottom = readSelectorValue(_segMan, textObject, SELECTOR(nsBottom)) + 1;
|
||||
nsRect.right = readSelectorValue(_segMan, textObject, SELECTOR(nsRight)) + 1;
|
||||
return nsRect;
|
||||
}
|
||||
|
||||
int16 GfxText32::GetLongest(const char *text, int16 maxWidth, GfxFont *font) {
|
||||
|
|
|
@ -30,20 +30,6 @@
|
|||
|
||||
namespace Sci {
|
||||
|
||||
struct TextEntry {
|
||||
reg_t object;
|
||||
uint16 x;
|
||||
uint16 y;
|
||||
uint16 width;
|
||||
uint16 height;
|
||||
byte *surface;
|
||||
Common::String text;
|
||||
};
|
||||
|
||||
// TODO: Move to Cache, perhaps?
|
||||
#define MAX_CACHED_TEXTS 20
|
||||
typedef Common::HashMap<uint32, TextEntry *> TextCache;
|
||||
|
||||
/**
|
||||
* Text32 class, handles text calculation and displaying of text for SCI2, SCI21 and SCI3 games
|
||||
*/
|
||||
|
@ -52,22 +38,21 @@ public:
|
|||
GfxText32(SegManager *segMan, GfxCache *fonts, GfxScreen *screen);
|
||||
~GfxText32();
|
||||
reg_t createTextBitmap(reg_t textObject, uint16 maxWidth = 0, uint16 maxHeight = 0);
|
||||
void drawTextBitmap(reg_t textObject, uint16 textX, uint16 textY, uint16 planeWidth);
|
||||
void disposeTextBitmap(reg_t hunkId);
|
||||
void drawTextBitmap(reg_t textObject);
|
||||
int16 GetLongest(const char *text, int16 maxWidth, GfxFont *font);
|
||||
TextEntry *getTextEntry(reg_t textObject);
|
||||
|
||||
void kernelTextSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight);
|
||||
|
||||
private:
|
||||
TextEntry *createTextEntry(reg_t textObject, uint16 maxWidth, uint16 maxHeight);
|
||||
int16 Size(Common::Rect &rect, const char *text, GuiResourceId fontId, int16 maxWidth);
|
||||
void Width(const char *text, int16 from, int16 len, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight, bool restoreFont);
|
||||
void StringWidth(const char *str, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight);
|
||||
void purgeCache();
|
||||
Common::Rect getPlaneRect(reg_t textObject);
|
||||
Common::Rect getNSRect(reg_t textObject);
|
||||
|
||||
SegManager *_segMan;
|
||||
GfxCache *_cache;
|
||||
TextCache _textCache;
|
||||
GfxScreen *_screen;
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue