SCI: Proper implementation of text drawing for SCI2+

This commit is contained in:
Filippos Karapetis 2011-10-11 01:24:28 +03:00
parent 1402b47674
commit 96ce226967
8 changed files with 96 additions and 161 deletions

View file

@ -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);

View file

@ -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

View file

@ -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) {

View file

@ -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);

View file

@ -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;

View file

@ -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);
}
}
}

View file

@ -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) {

View file

@ -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;
};