From d21c47e019434797217b1b44708c8f1810a003b3 Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Wed, 31 Dec 2014 16:14:45 -1000 Subject: XEEN: Lot of font code implemented --- engines/xeen/resdata.cpp | 43 ++++++++ engines/xeen/resdata.h | 2 + engines/xeen/screen.cpp | 33 ++++-- engines/xeen/screen.h | 7 +- engines/xeen/xsurface.cpp | 248 ++++++++++++++++++++++++++++++++++++++++++++-- engines/xeen/xsurface.h | 29 +++++- 6 files changed, 345 insertions(+), 17 deletions(-) diff --git a/engines/xeen/resdata.cpp b/engines/xeen/resdata.cpp index 44002c62f2..1cdd3ac0fb 100644 --- a/engines/xeen/resdata.cpp +++ b/engines/xeen/resdata.cpp @@ -148,4 +148,47 @@ const byte SYMBOLS[20][64] = { } }; +const byte TEXT_COLORS[40][4] = { + { 0x00, 0x19, 0x19, 0x19 }, + { 0x00, 0x08, 0x08, 0x08 }, + { 0x00, 0x0F, 0x0F, 0x0F }, + { 0x00, 0x15, 0x15, 0x15 }, + { 0x00, 0x01, 0x01, 0x01 }, + { 0x00, 0x21, 0x21, 0x21 }, + { 0x00, 0x26, 0x26, 0x26 }, + { 0x00, 0x2B, 0x2B, 0x2B }, + { 0x00, 0x31, 0x31, 0x31 }, + { 0x00, 0x36, 0x36, 0x36 }, + { 0x00, 0x3D, 0x3D, 0x3D }, + { 0x00, 0x41, 0x41, 0x41 }, + { 0x00, 0x46, 0x46, 0x46 }, + { 0x00, 0x4C, 0x4C, 0x4C }, + { 0x00, 0x50, 0x50, 0x50 }, + { 0x00, 0x55, 0x55, 0x55 }, + { 0x00, 0x5D, 0x5D, 0x5D }, + { 0x00, 0x60, 0x60, 0x60 }, + { 0x00, 0x65, 0x65, 0x65 }, + { 0x00, 0x6C, 0x6C, 0x6C }, + { 0x00, 0x70, 0x70, 0x70 }, + { 0x00, 0x75, 0x75, 0x75 }, + { 0x00, 0x7B, 0x7B, 0x7B }, + { 0x00, 0x80, 0x80, 0x80 }, + { 0x00, 0x85, 0x85, 0x85 }, + { 0x00, 0x8D, 0x8D, 0x8D }, + { 0x00, 0x90, 0x90, 0x90 }, + { 0x00, 0x97, 0x97, 0x97 }, + { 0x00, 0x9D, 0x9D, 0x9D }, + { 0x00, 0xA4, 0xA4, 0xA4 }, + { 0x00, 0xAB, 0xAB, 0xAB }, + { 0x00, 0xB0, 0xB0, 0xB0 }, + { 0x00, 0xB6, 0xB6, 0xB6 }, + { 0x00, 0xBD, 0xBD, 0xBD }, + { 0x00, 0xC0, 0xC0, 0xC0 }, + { 0x00, 0xC6, 0xC6, 0xC6 }, + { 0x00, 0xCD, 0xCD, 0xCD }, + { 0x00, 0xD0, 0xD0, 0xD0 }, + { 0x00, 0xD6, 0xD6, 0xD6 }, + { 0x00, 0xDB, 0xDB, 0xDB }, +}; + } // End of namespace Xeen diff --git a/engines/xeen/resdata.h b/engines/xeen/resdata.h index ce355e71cb..deb6e589be 100644 --- a/engines/xeen/resdata.h +++ b/engines/xeen/resdata.h @@ -30,6 +30,8 @@ namespace Xeen { extern const byte SYMBOLS[20][64]; +extern const byte TEXT_COLORS[40][4]; + } // End of namespace Xeen #endif /* XEEN_RESDATA_H */ diff --git a/engines/xeen/screen.cpp b/engines/xeen/screen.cpp index 4563e37498..2da2412866 100644 --- a/engines/xeen/screen.cpp +++ b/engines/xeen/screen.cpp @@ -66,8 +66,8 @@ void Window::open2() { void Window::frame() { Screen &screen = *_vm->_screen; - int xCount = (_bounds.width() - 9) / SYMBOL_WIDTH; - int yCount = (_bounds.height() - 9) / SYMBOL_HEIGHT; + int xCount = (_bounds.width() - 9) / FONT_WIDTH; + int yCount = (_bounds.height() - 9) / FONT_HEIGHT; // Write the top line screen._writePos = Common::Point(_bounds.left, _bounds.top); @@ -82,7 +82,7 @@ void Window::frame() { } } - screen._writePos.x = _bounds.right - SYMBOL_WIDTH; + screen._writePos.x = _bounds.right - FONT_WIDTH; screen.writeSymbol(5); // Write the vertical edges @@ -94,7 +94,7 @@ void Window::frame() { screen._writePos.x = _bounds.left; screen.writeSymbol(symbolId); - screen._writePos.x = _bounds.right - SYMBOL_WIDTH; + screen._writePos.x = _bounds.right - FONT_WIDTH; screen.writeSymbol(symbolId + 4); if (++symbolId == 10) @@ -103,7 +103,7 @@ void Window::frame() { } // Write the bottom line - screen._writePos = Common::Point(_bounds.left, _bounds.bottom - SYMBOL_HEIGHT); + screen._writePos = Common::Point(_bounds.left, _bounds.bottom - FONT_HEIGHT); screen.writeSymbol(14); if (xCount > 0) { @@ -115,7 +115,7 @@ void Window::frame() { } } - screen._writePos.x = _bounds.right - SYMBOL_WIDTH; + screen._writePos.x = _bounds.right - FONT_WIDTH; screen.writeSymbol(19); } @@ -154,6 +154,17 @@ void Window::addDirtyRect(const Common::Rect &r) { _dirtyRects.push(r); } +/** + * Fill the content area of a window with the current background color + */ +void Window::fill() { + fillRect(_innerBounds, _bgColor); +} + +void Window::writeString(const Common::String &s) { + _vm->_screen->writeString(s, _innerBounds); +} + /*------------------------------------------------------------------------*/ /** @@ -163,6 +174,16 @@ Screen::Screen(XeenEngine *vm) : _vm(vm) { _fadeIn = false; create(SCREEN_WIDTH, SCREEN_HEIGHT); setupWindows(); + + // Load font data for the screen + File f("fnt"); + byte *data = new byte[f.size()]; + f.read(data, f.size()); + _fontData = data; +} + +Screen::~Screen() { + delete[] _fontData; } void Screen::setupWindows() { diff --git a/engines/xeen/screen.h b/engines/xeen/screen.h index cbf57670b3..907409dbfb 100644 --- a/engines/xeen/screen.h +++ b/engines/xeen/screen.h @@ -70,7 +70,10 @@ public: void update(); void frame(); -}; + + void fill(); + + void writeString(const Common::String &s);}; class Screen: public XSurface { private: @@ -104,6 +107,8 @@ public: public: Screen(XeenEngine *vm); + virtual ~Screen(); + void closeWindows(); void update(); diff --git a/engines/xeen/xsurface.cpp b/engines/xeen/xsurface.cpp index d3c547861b..5e7d831380 100644 --- a/engines/xeen/xsurface.cpp +++ b/engines/xeen/xsurface.cpp @@ -21,16 +21,29 @@ */ #include "common/algorithm.h" +#include "common/util.h" #include "xeen/xsurface.h" #include "xeen/resdata.h" namespace Xeen { -XSurface::XSurface(): Graphics::Surface() { +const byte *XSurface::_fontData; + +XSurface::XSurface() : Graphics::Surface(), _bgColor(DEFAULT_BG_COLOR), _fontReduced(false), + _fontJustify(JUSTIFY_NONE), _msgWraps(false) { + _textColors[0] = 0; + _textColors[1] = 0x40; + _textColors[2] = 0x30; + _textColors[3] = 0x20; } -XSurface::XSurface(int w, int h) : Graphics::Surface() { +XSurface::XSurface(int w, int h) : Graphics::Surface(), _bgColor(DEFAULT_BG_COLOR), + _fontReduced(false), _fontJustify(JUSTIFY_NONE), _msgWraps(false) { create(w, h); + _textColors[0] = 0; + _textColors[1] = 0x40; + _textColors[2] = 0x30; + _textColors[3] = 0x20; } XSurface::~XSurface() { @@ -87,10 +100,10 @@ void XSurface::blitTo(XSurface &dest, const Common::Point &destPos) const { void XSurface::writeSymbol(int symbolId) { const byte *srcP = &SYMBOLS[symbolId][0]; - for (int yp = 0; yp < SYMBOL_HEIGHT; ++yp) { + for (int yp = 0; yp < FONT_HEIGHT; ++yp) { byte *destP = (byte *)getBasePtr(_writePos.x, _writePos.y + yp); - for (int xp = 0; xp < SYMBOL_WIDTH; ++xp, ++destP) { + for (int xp = 0; xp < FONT_WIDTH; ++xp, ++destP) { byte b = *srcP++; if (b) *destP = b; @@ -102,9 +115,158 @@ void XSurface::writeSymbol(int symbolId) { /** * Write a string to the surface + * @param s String to display + * @param bounds Window bounds to display string within + * @returns Any string remainder that couldn't be displayed + * @remarks Note that bounds is just used for wrapping purposes. Unless + * justification is set, the message will be written at _writePos */ -void XSurface::writeString(const Common::String &s) { - error("TODO"); +Common::String XSurface::writeString(const Common::String &s, const Common::Rect &bounds) { + _displayString = s.c_str(); + + for (;;) { + _msgWraps = false; + + // Get the size of the string that can be displayed on the likne + int xp = _fontJustify ? bounds.left : _writePos.x; + while (!getNextCharWidth(xp)) { + if (xp >= bounds.right) { + --_displayString; + _msgWraps = true; + } + } + + // Get the end point of the text that can be displayed + const char *displayEnd = _displayString; + _displayString = s.c_str(); + + if (*displayEnd && _fontJustify != JUSTIFY_RIGHT && xp >= bounds.right) { + // Need to handle justification of text + // First, move backwards to find the end of the previous word + // for a convenient point to break the line at + const char *endP = displayEnd; + while (endP > _displayString && (*endP & 0x7f) != ' ') + --endP; + + if (endP == _displayString) { + // There was no word breaks at all in the string + --displayEnd; + if (_fontJustify == JUSTIFY_NONE && _writePos.x != bounds.left) { + // Move to the next line + if (!newLine(bounds)) + continue; + // Ran out of space to display string + break; + } + } else { + // Found word break, find end of previous word + while (displayEnd > _displayString && (*displayEnd & 0x7f) == ' ') + --displayEnd; + } + } + + // Main character display loop + while (_displayString <= displayEnd) { + char c = getNextChar(); + + if (c == ' ') { + _writePos.x += _fontReduced ? 3 : 4; + } else if (c == '\r') { + fillRect(bounds, _bgColor); + _writePos = Common::Point(bounds.left, bounds.top); + } else if (c == 1) { + // Turn off reduced font mode + _fontReduced = false; + } else if (c == 2) { + // Turn on reduced font mode + _fontReduced = true; + } else if (c == 3) { + // Justify text + c = getNextChar(); + if (c == 'r') + _fontJustify = JUSTIFY_RIGHT; + else if (c == 'c') + _fontJustify = JUSTIFY_CENTER; + else + _fontJustify = JUSTIFY_NONE; + } else if (c == 4) { + // Draw an empty box of a given width + int w = fontAtoi(); + Common::Point pt = _writePos; + if (_fontJustify == JUSTIFY_RIGHT) + pt.x -= w; + fillRect(Common::Rect(pt.x, pt.y, pt.x + w, pt.y + (_fontReduced ? 9 : 10)), + _bgColor); + } else if (c == 5) { + continue; + } else if (c == 6) { + // Non-breakable space + writeChar(' '); + } else if (c == 7) { + // Set text background color + int c = fontAtoi(); + _bgColor = (c < 0 || c > 255) ? DEFAULT_BG_COLOR : c; + } else if (c == 8) { + // Draw a character outline + c = getNextChar(); + if (c == ' ') { + c = '\0'; + _writePos.x -= 3; + } else { + if (c == 6) + c = ' '; + byte charSize = _fontData[0x1000 + (int)c + (_fontReduced ? 0x80 : 0)]; + _writePos.x -= charSize; + } + + if (_writePos.x < bounds.left) + _writePos.x = bounds.left; + + if (c) { + int oldX = _writePos.x; + byte oldColor[4]; + Common::copy(&_textColors[0], &_textColors[4], &oldColor[0]); + + _textColors[1] = _textColors[2] = _textColors[3] = _bgColor; + writeChar(c); + + Common::copy(&oldColor[0], &oldColor[4], &_textColors[0]); + _writePos.x = oldX; + } + } else if (c == 9) { + // Skip x position + int xp = fontAtoi(); + _writePos.x = MIN(bounds.left + xp, (int)bounds.right); + } else if (c == 10) { + // Newline + if (newLine(bounds)) + break; + } else if (c == 11) { + // Skip y position + int yp = fontAtoi( ); + _writePos.y = MIN(bounds.top + yp, (int)bounds.bottom); + } else if (c == 12) { + // Set text colors + int idx = fontAtoi(); + if (idx < 0) + idx = 0; + setTextColor(idx); + } else if (c < ' ') { + // Invalid command + displayEnd = nullptr; + break; + } else { + // Standard character - write it out + writeChar(c); + } + } + + if (_displayString > displayEnd && _fontJustify != JUSTIFY_RIGHT && _msgWraps + && newLine(bounds)) + break; + } + + return Common::String(_displayString); } /** @@ -114,5 +276,79 @@ void XSurface::writeChar(char c) { error("TODO"); } +/** + * Return the next pending character to display + */ +char XSurface::getNextChar() { + return *_displayString++ & 0x7f; +} + +/** +* Return the width of a given character +*/ +bool XSurface::getNextCharWidth(int &total) { + char c = getNextChar(); + + if (c > ' ') { + total += _fontData[0x1000 + (int)c + (_fontReduced ? 0x80 : 0)]; + return false; + } else if (c == ' ') { + total += 4; + return false; + } else if (c == 8) { + c = getNextChar(); + if (c == ' ') { + total -= 2; + return false; + } else { + _displayString -= 2; + return true; + } + } else if (c == 12) { + c = getNextChar(); + if (c != 'd') + getNextChar(); + return false; + } else { + --_displayString; + return true; + } +} + +bool XSurface::newLine(const Common::Rect &bounds) { + // Move past any spaces currently being pointed to + while ((*_displayString & 0x7f) == ' ') + ++_displayString; + + _msgWraps = false; + _writePos.x = bounds.left; + + int h = _fontReduced ? 9 : 10; + _writePos.y += h; + + return ((_writePos.y + h - 1) > bounds.bottom); +} + +int XSurface::fontAtoi(int len) { + int total = 0; + for (int i = 0; i < len; ++i) { + char c = getNextChar(); + if (c == ' ') + c = '0'; + + int digit = c - '0'; + if (digit < 0 || digit > 9) + return -1; + + total = total * 10 + digit; + } + + return total; +} + +void XSurface::setTextColor(int idx) { + const byte *colP = &TEXT_COLORS[idx][0]; + Common::copy(colP, colP + 4, &_textColors[0]); +} } // End of namespace Xeen diff --git a/engines/xeen/xsurface.h b/engines/xeen/xsurface.h index 6688285361..6e88ae9f97 100644 --- a/engines/xeen/xsurface.h +++ b/engines/xeen/xsurface.h @@ -30,18 +30,39 @@ namespace Xeen { -#define SYMBOL_WIDTH 8 -#define SYMBOL_HEIGHT 8 +#define FONT_WIDTH 8 +#define FONT_HEIGHT 8 +#define DEFAULT_BG_COLOR 0x99 + +enum Justify { JUSTIFY_NONE = 0, JUSTIFY_CENTER = 1, JUSTIFY_RIGHT = 2 }; class XSurface: public Graphics::Surface { +private: + const char *_displayString; + bool _msgWraps; + + char getNextChar(); + + bool getNextCharWidth(int &total); + + bool newLine(const Common::Rect &bounds); + + int fontAtoi(int len = 3); + + void setTextColor(int idx); public: + static const byte *_fontData; Common::Point _writePos; + byte _textColors[4]; + byte _bgColor; + bool _fontReduced; + Justify _fontJustify; public: virtual void addDirtyRect(const Common::Rect &r) {} public: XSurface(); XSurface(int w, int h); - ~XSurface(); + virtual ~XSurface(); void create(uint16 w, uint16 h); @@ -57,7 +78,7 @@ public: void writeSymbol(int symbolId); - void writeString(const Common::String &s); + Common::String writeString(const Common::String &s, const Common::Rect &bounds); void writeChar(char c); }; -- cgit v1.2.3