diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h index e3e3f51f75b..e0ab9546411 100644 --- a/engines/sci/engine/kernel.h +++ b/engines/sci/engine/kernel.h @@ -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); diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h index 22f9c9556a2..d32482d155b 100644 --- a/engines/sci/engine/kernel_tables.h +++ b/engines/sci/engine/kernel_tables.h @@ -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 diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp index 4b358642680..b1314ef02fd 100644 --- a/engines/sci/engine/kgraphics.cpp +++ b/engines/sci/engine/kgraphics.cpp @@ -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) { diff --git a/engines/sci/engine/selector.cpp b/engines/sci/engine/selector.cpp index c2f857f3196..a9aca9e22f5 100644 --- a/engines/sci/engine/selector.cpp +++ b/engines/sci/engine/selector.cpp @@ -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); diff --git a/engines/sci/engine/selector.h b/engines/sci/engine/selector.h index 085dd6e8327..bbd86bb03ed 100644 --- a/engines/sci/engine/selector.h +++ b/engines/sci/engine/selector.h @@ -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; diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp index 432ad051ca1..17b4b015d5d 100644 --- a/engines/sci/graphics/frameout.cpp +++ b/engines/sci/graphics/frameout.cpp @@ -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); } } } diff --git a/engines/sci/graphics/text32.cpp b/engines/sci/graphics/text32.cpp index 7be3874ed0c..8337d16fe67 100644 --- a/engines/sci/graphics/text32.cpp +++ b/engines/sci/graphics/text32.cpp @@ -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); + return planeRect; +} - 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); - } - - 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) { diff --git a/engines/sci/graphics/text32.h b/engines/sci/graphics/text32.h index 113fbb46a2b..4c1e53d9718 100644 --- a/engines/sci/graphics/text32.h +++ b/engines/sci/graphics/text32.h @@ -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 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; };