/* 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. * * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/engines/sci/graphics/text16.cpp $ * $Id: text16.cpp 55178 2011-01-08 23:16:44Z thebluegr $ * */ #include "common/util.h" #include "common/stack.h" #include "graphics/primitives.h" #include "sci/sci.h" #include "sci/engine/kernel.h" #include "sci/engine/selector.h" #include "sci/engine/state.h" #include "sci/graphics/cache.h" #include "sci/graphics/font.h" #include "sci/graphics/screen.h" #include "sci/graphics/text32.h" namespace Sci { GfxText32::GfxText32(SegManager *segMan, GfxCache *fonts, GfxScreen *screen) : _segMan(segMan), _cache(fonts), _screen(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(); } void GfxText32::createTextBitmap(reg_t textObject) { 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); } // TODO: Finish this! void GfxText32::drawTextBitmap(reg_t textObject, uint16 textX, uint16 textY, uint16 w) { 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; while (*txt) { charCount = GetLongest(txt, w, 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) { reg_t stringObject = readSelector(_segMan, textObject, SELECTOR(text)); // 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(); 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 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, charCount; uint16 curX = 0, curY = 0; maxTextWidth = 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; } int16 GfxText32::GetLongest(const char *text, int16 maxWidth, GfxFont *font) { uint16 curChar = 0; int16 maxChars = 0, curCharCount = 0; uint16 width = 0; while (width <= maxWidth) { curChar = (*(const byte *)text++); switch (curChar) { // We need to add 0xD, 0xA and 0xD 0xA to curCharCount and then exit // which means, we split text like // 'Mature, experienced software analyst available.' 0xD 0xA // 'Bug installation a proven speciality. "No version too clean."' (normal game text, this is from lsl2) // and 0xA '-------' 0xA (which is the official sierra subtitle separator) // Sierra did it the same way. case 0xD: // Check, if 0xA is following, if so include it as well if ((*(const unsigned char *)text) == 0xA) curCharCount++; // it's meant to pass through here case 0xA: curCharCount++; // and it's also meant to pass through here case 0: return curCharCount; case ' ': maxChars = curCharCount; // return count up to (but not including) breaking space break; } if (width + font->getCharWidth(curChar) > maxWidth) break; width += font->getCharWidth(curChar); curCharCount++; } return maxChars; } } // End of namespace Sci