diff options
Diffstat (limited to 'engines/zvision/text')
-rw-r--r-- | engines/zvision/text/string_manager.h | 1 | ||||
-rw-r--r-- | engines/zvision/text/subtitles.cpp | 9 | ||||
-rw-r--r-- | engines/zvision/text/subtitles.h | 2 | ||||
-rw-r--r-- | engines/zvision/text/text.cpp | 429 | ||||
-rw-r--r-- | engines/zvision/text/text.h | 53 | ||||
-rw-r--r-- | engines/zvision/text/truetype_font.cpp | 171 | ||||
-rw-r--r-- | engines/zvision/text/truetype_font.h | 26 |
7 files changed, 379 insertions, 312 deletions
diff --git a/engines/zvision/text/string_manager.h b/engines/zvision/text/string_manager.h index f4564ee1ec..2c31cf7afe 100644 --- a/engines/zvision/text/string_manager.h +++ b/engines/zvision/text/string_manager.h @@ -23,7 +23,6 @@ #ifndef ZVISION_STRING_MANAGER_H #define ZVISION_STRING_MANAGER_H -#include "zvision/detection.h" #include "zvision/text/truetype_font.h" namespace Graphics { diff --git a/engines/zvision/text/subtitles.cpp b/engines/zvision/text/subtitles.cpp index acf4c37c2f..ffc9e2b808 100644 --- a/engines/zvision/text/subtitles.cpp +++ b/engines/zvision/text/subtitles.cpp @@ -27,7 +27,7 @@ namespace ZVision { -Subtitle::Subtitle(ZVision *engine, const Common::String &subname) : +Subtitle::Subtitle(ZVision *engine, const Common::String &subname, bool upscaleToHires) : _engine(engine), _areaId(-1), _subId(-1) { @@ -44,6 +44,8 @@ Subtitle::Subtitle(ZVision *engine, const Common::String &subname) : int32 x1, y1, x2, y2; sscanf(str.c_str(), "%*[^:]:%d %d %d %d", &x1, &y1, &x2, &y2); Common::Rect rct = Common::Rect(x1, y1, x2, y2); + if (upscaleToHires) + _engine->getRenderManager()->upscaleRect(rct); _areaId = _engine->getRenderManager()->createSubArea(rct); } else if (str.matchString("*TextFile*", true)) { char filename[64]; @@ -67,6 +69,11 @@ Subtitle::Subtitle(ZVision *engine, const Common::String &subname) : int32 sb; if (sscanf(str.c_str(), "%*[^:]:(%d,%d)=%d", &st, &en, &sb) == 3) { if (sb <= (int32)_subs.size()) { + if (upscaleToHires) { + // Convert from 15FPS (AVI) to 29.97FPS (VOB) + st = st * 2997 / 1500; + en = en * 2997 / 1500; + } _subs[sb].start = st; _subs[sb].stop = en; } diff --git a/engines/zvision/text/subtitles.h b/engines/zvision/text/subtitles.h index c3da6583a4..329339be55 100644 --- a/engines/zvision/text/subtitles.h +++ b/engines/zvision/text/subtitles.h @@ -31,7 +31,7 @@ class ZVision; class Subtitle { public: - Subtitle(ZVision *engine, const Common::String &subname); + Subtitle(ZVision *engine, const Common::String &subname, bool upscaleToHires = false); ~Subtitle(); void process(int32 time); diff --git a/engines/zvision/text/text.cpp b/engines/zvision/text/text.cpp index a5ed044424..868ee4f1ae 100644 --- a/engines/zvision/text/text.cpp +++ b/engines/zvision/text/text.cpp @@ -38,7 +38,7 @@ namespace ZVision { -cTxtStyle::cTxtStyle() { +TextStyleState::TextStyleState() { _fontname = "Arial"; _blue = 255; _green = 255; @@ -49,7 +49,7 @@ cTxtStyle::cTxtStyle() { _escapement = 0; #endif _italic = false; - _justify = TXT_JUSTIFY_LEFT; + _justification = TEXT_JUSTIFY_LEFT; _size = 12; #if 0 _skipcolor = false; @@ -60,10 +60,10 @@ cTxtStyle::cTxtStyle() { _sharp = false; } -txtReturn cTxtStyle::parseStyle(const Common::String &strin, int16 ln) { - Common::String buf = Common::String(strin.c_str(), ln); +TextChange TextStyleState::parseStyle(const Common::String &str, int16 len) { + Common::String buf = Common::String(str.c_str(), len); - int8 retval = TXT_RET_NOTHING; + uint retval = TEXT_CHANGE_NONE; Common::StringTokenizer tokenizer(buf, " "); Common::String token; @@ -89,7 +89,7 @@ txtReturn cTxtStyle::parseStyle(const Common::String &strin, int16 ln) { if (!tokenizer.empty()) _fontname = token; } - retval |= TXT_RET_FNTCHG; + retval |= TEXT_CHANGE_FONT_TYPE; } else if (token.matchString("blue", true)) { if (!tokenizer.empty()) { @@ -97,7 +97,7 @@ txtReturn cTxtStyle::parseStyle(const Common::String &strin, int16 ln) { int32 tmp = atoi(token.c_str()); if (_blue != tmp) { _blue = tmp; - retval |= TXT_RET_FNTSTL; + retval |= TEXT_CHANGE_FONT_STYLE; } } } else if (token.matchString("red", true)) { @@ -106,7 +106,7 @@ txtReturn cTxtStyle::parseStyle(const Common::String &strin, int16 ln) { int32 tmp = atoi(token.c_str()); if (_red != tmp) { _red = tmp; - retval |= TXT_RET_FNTSTL; + retval |= TEXT_CHANGE_FONT_STYLE; } } } else if (token.matchString("green", true)) { @@ -115,7 +115,7 @@ txtReturn cTxtStyle::parseStyle(const Common::String &strin, int16 ln) { int32 tmp = atoi(token.c_str()); if (_green != tmp) { _green = tmp; - retval |= TXT_RET_FNTSTL; + retval |= TEXT_CHANGE_FONT_STYLE; } } } else if (token.matchString("newline", true)) { @@ -125,14 +125,14 @@ txtReturn cTxtStyle::parseStyle(const Common::String &strin, int16 ln) { _newline++; #endif - retval |= TXT_RET_NEWLN; + retval |= TEXT_CHANGE_NEWLINE; } else if (token.matchString("point", true)) { if (!tokenizer.empty()) { token = tokenizer.nextToken(); int32 tmp = atoi(token.c_str()); if (_size != tmp) { _size = tmp; - retval |= TXT_RET_FNTCHG; + retval |= TEXT_CHANGE_FONT_TYPE; } } } else if (token.matchString("escapement", true)) { @@ -149,12 +149,12 @@ txtReturn cTxtStyle::parseStyle(const Common::String &strin, int16 ln) { if (token.matchString("on", true)) { if (_italic != true) { _italic = true; - retval |= TXT_RET_FNTSTL; + retval |= TEXT_CHANGE_FONT_STYLE; } } else if (token.matchString("off", true)) { if (_italic != false) { _italic = false; - retval |= TXT_RET_FNTSTL; + retval |= TEXT_CHANGE_FONT_STYLE; } } } @@ -164,12 +164,12 @@ txtReturn cTxtStyle::parseStyle(const Common::String &strin, int16 ln) { if (token.matchString("on", true)) { if (_underline != true) { _underline = true; - retval |= TXT_RET_FNTSTL; + retval |= TEXT_CHANGE_FONT_STYLE; } } else if (token.matchString("off", true)) { if (_underline != false) { _underline = false; - retval |= TXT_RET_FNTSTL; + retval |= TEXT_CHANGE_FONT_STYLE; } } } @@ -179,12 +179,12 @@ txtReturn cTxtStyle::parseStyle(const Common::String &strin, int16 ln) { if (token.matchString("on", true)) { if (_strikeout != true) { _strikeout = true; - retval |= TXT_RET_FNTSTL; + retval |= TEXT_CHANGE_FONT_STYLE; } } else if (token.matchString("off", true)) { if (_strikeout != false) { _strikeout = false; - retval |= TXT_RET_FNTSTL; + retval |= TEXT_CHANGE_FONT_STYLE; } } } @@ -194,12 +194,12 @@ txtReturn cTxtStyle::parseStyle(const Common::String &strin, int16 ln) { if (token.matchString("on", true)) { if (_bold != true) { _bold = true; - retval |= TXT_RET_FNTSTL; + retval |= TEXT_CHANGE_FONT_STYLE; } } else if (token.matchString("off", true)) { if (_bold != false) { _bold = false; - retval |= TXT_RET_FNTSTL; + retval |= TEXT_CHANGE_FONT_STYLE; } } } @@ -220,24 +220,24 @@ txtReturn cTxtStyle::parseStyle(const Common::String &strin, int16 ln) { if (!tokenizer.empty()) { token = tokenizer.nextToken(); _statebox = atoi(token.c_str()); - retval |= TXT_RET_HASSTBOX; + retval |= TEXT_CHANGE_HAS_STATE_BOX; } } else if (token.matchString("justify", true)) { if (!tokenizer.empty()) { token = tokenizer.nextToken(); if (token.matchString("center", true)) - _justify = TXT_JUSTIFY_CENTER; + _justification = TEXT_JUSTIFY_CENTER; else if (token.matchString("left", true)) - _justify = TXT_JUSTIFY_LEFT; + _justification = TEXT_JUSTIFY_LEFT; else if (token.matchString("right", true)) - _justify = TXT_JUSTIFY_RIGHT; + _justification = TEXT_JUSTIFY_RIGHT; } } } - return (txtReturn)retval; + return (TextChange)retval; } -void cTxtStyle::readAllStyle(const Common::String &txt) { +void TextStyleState::readAllStyles(const Common::String &txt) { int16 startTextPosition = -1; int16 endTextPosition = -1; @@ -246,252 +246,273 @@ void cTxtStyle::readAllStyle(const Common::String &txt) { startTextPosition = i; else if (txt[i] == '>') { endTextPosition = i; - if (startTextPosition != -1) - if ((endTextPosition - startTextPosition - 1) > 0) + if (startTextPosition != -1) { + if ((endTextPosition - startTextPosition - 1) > 0) { parseStyle(Common::String(txt.c_str() + startTextPosition + 1), endTextPosition - startTextPosition - 1); + } + } } } } -void cTxtStyle::setFontStyle(StyledTTFont &font) { +void TextStyleState::updateFontWithTextState(StyledTTFont &font) { uint tempStyle = 0; - if (_bold) - tempStyle |= StyledTTFont::STTF_BOLD; - - if (_italic) - tempStyle |= StyledTTFont::STTF_ITALIC; - - if (_underline) - tempStyle |= StyledTTFont::STTF_UNDERLINE; - - if (_strikeout) - tempStyle |= StyledTTFont::STTF_STRIKEOUT; - - if (_sharp) - tempStyle |= StyledTTFont::STTF_SHARP; + if (_bold) { + tempStyle |= StyledTTFont::TTF_STYLE_BOLD; + } + if (_italic) { + tempStyle |= StyledTTFont::TTF_STYLE_ITALIC; + } + if (_underline) { + tempStyle |= StyledTTFont::TTF_STYLE_UNDERLINE; + } + if (_strikeout) { + tempStyle |= StyledTTFont::TTF_STYLE_STRIKETHROUGH; + } + if (_sharp) { + tempStyle |= StyledTTFont::TTF_STYLE_SHARP; + } - font.setStyle(tempStyle); + font.loadFont(_fontname, _size, tempStyle); } -void cTxtStyle::setFont(StyledTTFont &font) { - uint tempStyle = 0; - - if (_bold) - tempStyle |= StyledTTFont::STTF_BOLD; +void TextRenderer::drawTextWithJustification(const Common::String &text, StyledTTFont &font, uint32 color, Graphics::Surface &dest, int lineY, TextJustification justify) { + if (justify == TEXT_JUSTIFY_LEFT) + font.drawString(&dest, text, 0, lineY, dest.w, color, Graphics::kTextAlignLeft); + else if (justify == TEXT_JUSTIFY_CENTER) + font.drawString(&dest, text, 0, lineY, dest.w, color, Graphics::kTextAlignCenter); + else if (justify == TEXT_JUSTIFY_RIGHT) + font.drawString(&dest, text, 0, lineY, dest.w, color, Graphics::kTextAlignRight); +} - if (_italic) - tempStyle |= StyledTTFont::STTF_ITALIC; +int32 TextRenderer::drawText(const Common::String &text, TextStyleState &state, Graphics::Surface &dest) { + StyledTTFont font(_engine); + state.updateFontWithTextState(font); - if (_underline) - tempStyle |= StyledTTFont::STTF_UNDERLINE; + uint32 color = _engine->_resourcePixelFormat.RGBToColor(state._red, state._green, state._blue); + drawTextWithJustification(text, font, color, dest, 0, state._justification); - if (_strikeout) - tempStyle |= StyledTTFont::STTF_STRIKEOUT; + return font.getStringWidth(text); +} - if (_sharp) - tempStyle |= StyledTTFont::STTF_SHARP; +struct TextSurface { + TextSurface(Graphics::Surface *surface, Common::Point surfaceOffset, uint lineNumber) + : _surface(surface), + _surfaceOffset(surfaceOffset), + _lineNumber(lineNumber) { + } - font.loadFont(_fontname, _size, tempStyle); -} + Graphics::Surface *_surface; + Common::Point _surfaceOffset; + uint _lineNumber; +}; -Graphics::Surface *TextRenderer::render(StyledTTFont &fnt, const Common::String &txt, cTxtStyle &style) { - style.setFontStyle(fnt); - uint32 clr = _engine->_resourcePixelFormat.RGBToColor(style._red, style._green, style._blue); - return fnt.renderSolidText(txt, clr); -} +void TextRenderer::drawTextWithWordWrapping(const Common::String &text, Graphics::Surface &dest) { + Common::Array<TextSurface> textSurfaces; + Common::Array<uint> lineWidths; + Common::Array<TextJustification> lineJustifications; -void TextRenderer::drawTxtWithJustify(const Common::String &txt, StyledTTFont &fnt, uint32 color, Graphics::Surface &dst, int lineY, txtJustify justify) { - if (justify == TXT_JUSTIFY_LEFT) - fnt.drawString(&dst, txt, 0, lineY, dst.w, color, Graphics::kTextAlignLeft); - else if (justify == TXT_JUSTIFY_CENTER) - fnt.drawString(&dst, txt, 0, lineY, dst.w, color, Graphics::kTextAlignCenter); - else if (justify == TXT_JUSTIFY_RIGHT) - fnt.drawString(&dst, txt, 0, lineY, dst.w, color, Graphics::kTextAlignRight); -} + // Create the initial text state + TextStyleState currentState; -int32 TextRenderer::drawTxt(const Common::String &txt, cTxtStyle &fontStyle, Graphics::Surface &dst) { + // Create an empty font and bind it to the state StyledTTFont font(_engine); - fontStyle.setFont(font); + currentState.updateFontWithTextState(font); - dst.fillRect(Common::Rect(dst.w, dst.h), 0); + Common::String currentSentence; // Not a true 'grammatical' sentence. Rather, it's just a collection of words + Common::String currentWord; + int sentenceWidth = 0; + int wordWidth = 0; + int lineWidth = 0; + int lineHeight = font.getFontHeight(); - uint32 clr = _engine->_resourcePixelFormat.RGBToColor(fontStyle._red, fontStyle._green, fontStyle._blue); + uint currentLineNumber = 0u; - int16 w; + uint numSpaces = 0u; + int spaceWidth = 0; - w = font.getStringWidth(txt); + // The pixel offset to the currentSentence + Common::Point sentencePixelOffset; - drawTxtWithJustify(txt, font, clr, dst, 0, fontStyle._justify); + uint i = 0u; + uint stringlen = text.size(); - return w; -} + while (i < stringlen) { + if (text[i] == '<') { + // Flush the currentWord to the currentSentence + currentSentence += currentWord; + sentenceWidth += wordWidth; + + // Reset the word variables + currentWord.clear(); + wordWidth = 0; + + // Parse the style tag + uint startTextPosition = i; + while (i < stringlen && text[i] != '>') { + ++i; + } + uint endTextPosition = i; -void TextRenderer::drawTxtInOneLine(const Common::String &text, Graphics::Surface &dst) { - const int16 TXT_CFG_TEXTURES_LINES = 256; // For now I don't want remake it - const int TXT_CFG_TEXTURES_PER_LINE = 6; - cTxtStyle style, style2; - int16 startTextPosition = -1; - int16 endTextPosition = -1; - int16 i = 0; - int16 dx = 0, dy = 0; - int16 textPixelWidth; - int16 textPosition = 0; - Common::String buf; - Common::String buf2; - - Graphics::Surface *TxtSurfaces[TXT_CFG_TEXTURES_LINES][TXT_CFG_TEXTURES_PER_LINE]; - int16 currentline = 0, currentlineitm = 0; - - int TxtJustify[TXT_CFG_TEXTURES_LINES]; - int TxtPoint[TXT_CFG_TEXTURES_LINES]; - - for (int16 k = 0; k < TXT_CFG_TEXTURES_LINES; k++) { - TxtPoint[k] = 0; - for (int j = 0; j < TXT_CFG_TEXTURES_PER_LINE; j++) - TxtSurfaces[k][j] = NULL; - } + uint32 textColor = currentState.getTextColor(_engine); - int16 stringlen = text.size(); + uint stateChanges = 0u; + if ((endTextPosition - startTextPosition - 1) > 0) { + stateChanges = currentState.parseStyle(Common::String(text.c_str() + startTextPosition + 1), endTextPosition - startTextPosition - 1); + } - StyledTTFont font(_engine); + if (stateChanges & (TEXT_CHANGE_FONT_TYPE | TEXT_CHANGE_FONT_STYLE)) { + // Use the last state to render out the current sentence + // Styles apply to the text 'after' them + if (!currentSentence.empty()) { + textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, textColor), sentencePixelOffset, currentLineNumber)); - style.setFont(font); + lineWidth += sentenceWidth; + sentencePixelOffset.x += sentenceWidth; - int16 prevbufspace = 0, prevtxtspace = 0; + // Reset the sentence variables + currentSentence.clear(); + sentenceWidth = 0; + } - while (i < stringlen) { - TxtJustify[currentline] = style._justify; - if (text[i] == '<') { - int16 ret = 0; + // Update the current state with the style information + currentState.updateFontWithTextState(font); - startTextPosition = i; - while (i < stringlen && text[i] != '>') - i++; - endTextPosition = i; - if (startTextPosition != -1) - if ((endTextPosition - startTextPosition - 1) > 0) { - style2 = style; - ret = style.parseStyle(Common::String(text.c_str() + startTextPosition + 1), endTextPosition - startTextPosition - 1); + lineHeight = MAX(lineHeight, font.getFontHeight()); + spaceWidth = font.getCharWidth(' '); + } + if (stateChanges & TEXT_CHANGE_NEWLINE) { + // If the current sentence has content, render it out + if (!currentSentence.empty()) { + textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, textColor), sentencePixelOffset, currentLineNumber)); } + + // Set line width + lineWidths.push_back(lineWidth + sentenceWidth - (numSpaces * spaceWidth)); + + currentSentence.clear(); + sentenceWidth = 0; + + // Update the offsets + sentencePixelOffset.x = 0u; + sentencePixelOffset.y += lineHeight; + + // Reset the line variables + lineHeight = font.getFontHeight(); + lineWidth = 0; + ++currentLineNumber; + lineJustifications.push_back(currentState._justification); + } + if (stateChanges & TEXT_CHANGE_HAS_STATE_BOX) { + Common::String temp = Common::String::format("%d", _engine->getScriptManager()->getStateValue(currentState._statebox)); + wordWidth += font.getStringWidth(temp); - if (ret & (TXT_RET_FNTCHG | TXT_RET_FNTSTL | TXT_RET_NEWLN)) { - if (buf.size() > 0) { - textPixelWidth = font.getStringWidth(buf); - - TxtSurfaces[currentline][currentlineitm] = render(font, buf, style2); - TxtPoint[currentline] = MAX(font.getFontHeight(), TxtPoint[currentline]); + // If the word causes the line to overflow, render the sentence and start a new line + if (lineWidth + sentenceWidth + wordWidth > dest.w) { + textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, textColor), sentencePixelOffset, currentLineNumber)); - currentlineitm++; + // Set line width + lineWidths.push_back(lineWidth + sentenceWidth - (numSpaces * spaceWidth)); - buf.clear(); - prevbufspace = 0; - textPosition = 0; - dx += textPixelWidth; + currentSentence.clear(); + sentenceWidth = 0; - } - if (ret & TXT_RET_FNTCHG) { - style.setFont(font); - } - if (ret & TXT_RET_FNTSTL) - style.setFontStyle(font); + // Update the offsets + sentencePixelOffset.x = 0u; + sentencePixelOffset.y += lineHeight; - if (ret & TXT_RET_NEWLN) { - currentline++; - currentlineitm = 0; - dx = 0; + // Reset the line variables + lineHeight = font.getFontHeight(); + lineWidth = 0; + ++currentLineNumber; + lineJustifications.push_back(currentState._justification); } } - - if (ret & TXT_RET_HASSTBOX) { - Common::String buf3; - buf3 = Common::String::format("%d", _engine->getScriptManager()->getStateValue(style._statebox)); - buf += buf3; - textPosition += buf3.size(); - } - } else { - - buf += text[i]; - textPosition++; + currentWord += text[i]; + wordWidth += font.getCharWidth(text[i]); if (text[i] == ' ') { - prevbufspace = textPosition - 1; - prevtxtspace = i; - } + // When we hit the first space, flush the current word to the sentence + if (!currentWord.empty()) { + currentSentence += currentWord; + sentenceWidth += wordWidth; - if (font.isLoaded()) { - textPixelWidth = font.getStringWidth(buf); - if (textPixelWidth + dx > dst.w) { - if (prevbufspace == 0) { - prevtxtspace = i; - prevbufspace = textPosition - 1; - } - buf2 = Common::String(buf.c_str(), prevbufspace + 1); + currentWord.clear(); + wordWidth = 0; + } - if (buf2.size() > 0) { - TxtSurfaces[currentline][currentlineitm] = render(font, buf2, style); - TxtPoint[currentline] = MAX(font.getFontHeight(), TxtPoint[currentline]); + // We track the number of spaces so we can disregard their width in lineWidth calculations + ++numSpaces; + } else { + // If the word causes the line to overflow, render the sentence and start a new line + if (lineWidth + sentenceWidth + wordWidth > dest.w) { + // Only render out content + if (!currentSentence.empty()) { + textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, currentState.getTextColor(_engine)), sentencePixelOffset, currentLineNumber)); } - buf.clear(); - i = prevtxtspace; - prevbufspace = 0; - textPosition = 0; - currentline++; - currentlineitm = 0; - dx = 0; + // Set line width + lineWidths.push_back(lineWidth + sentenceWidth - (numSpaces * spaceWidth)); + + currentSentence.clear(); + sentenceWidth = 0; + + // Update the offsets + sentencePixelOffset.x = 0u; + sentencePixelOffset.y += lineHeight; + + // Reset the line variables + lineHeight = font.getFontHeight(); + lineWidth = 0; + ++currentLineNumber; + lineJustifications.push_back(currentState._justification); } + + numSpaces = 0u; } } + i++; } - if (buf.size() > 0) { - TxtSurfaces[currentline][currentlineitm] = render(font, buf, style); - TxtPoint[currentline] = MAX(font.getFontHeight(), TxtPoint[currentline]); + // Render out any remaining words/sentences + if (!currentWord.empty() || !currentSentence.empty()) { + currentSentence += currentWord; + sentenceWidth += wordWidth; + + textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, currentState.getTextColor(_engine)), sentencePixelOffset, currentLineNumber)); } - dy = 0; - for (i = 0; i <= currentline; i++) { - int16 j = 0; - int16 width = 0; - while (TxtSurfaces[i][j] != NULL) { - width += TxtSurfaces[i][j]->w; - j++; - } - dx = 0; - Common::Rect empty; - for (int32 jj = 0; jj < j; jj++) { - if (TxtJustify[i] == TXT_JUSTIFY_LEFT) - _engine->getRenderManager()->blitSurfaceToSurface(*TxtSurfaces[i][jj], empty, dst, dx, dy + TxtPoint[i] - TxtSurfaces[i][jj]->h, 0); + lineWidths.push_back(lineWidth + sentenceWidth); + lineJustifications.push_back(currentState._justification); - else if (TxtJustify[i] == TXT_JUSTIFY_CENTER) - _engine->getRenderManager()->blitSurfaceToSurface(*TxtSurfaces[i][jj], empty, dst, ((dst.w - width) / 2) + dx, dy + TxtPoint[i] - TxtSurfaces[i][jj]->h, 0); - - else if (TxtJustify[i] == TXT_JUSTIFY_RIGHT) - _engine->getRenderManager()->blitSurfaceToSurface(*TxtSurfaces[i][jj], empty, dst, dst.w - width + dx, dy + TxtPoint[i] - TxtSurfaces[i][jj]->h, 0); + for (Common::Array<TextSurface>::iterator iter = textSurfaces.begin(); iter != textSurfaces.end(); ++iter) { + Common::Rect empty; - dx += TxtSurfaces[i][jj]->w; + if (lineJustifications[iter->_lineNumber] == TEXT_JUSTIFY_LEFT) { + _engine->getRenderManager()->blitSurfaceToSurface(*iter->_surface, empty, dest, iter->_surfaceOffset.x, iter->_surfaceOffset.y, 0); + } else if (lineJustifications[iter->_lineNumber] == TEXT_JUSTIFY_CENTER) { + _engine->getRenderManager()->blitSurfaceToSurface(*iter->_surface, empty, dest, ((dest.w - lineWidths[iter->_lineNumber]) / 2) + iter->_surfaceOffset.x, iter->_surfaceOffset.y, 0); + } else if (lineJustifications[iter->_lineNumber] == TEXT_JUSTIFY_RIGHT) { + _engine->getRenderManager()->blitSurfaceToSurface(*iter->_surface, empty, dest, dest.w - lineWidths[iter->_lineNumber] + iter->_surfaceOffset.x, iter->_surfaceOffset.y, 0); } - dy += TxtPoint[i]; + // Release memory + iter->_surface->free(); + delete iter->_surface; } - - for (i = 0; i < TXT_CFG_TEXTURES_LINES; i++) - for (int32 j = 0; j < TXT_CFG_TEXTURES_PER_LINE; j++) - if (TxtSurfaces[i][j] != NULL) { - TxtSurfaces[i][j]->free(); - delete TxtSurfaces[i][j]; - } } Common::String readWideLine(Common::SeekableReadStream &stream) { Common::String asciiString; - while (!stream.eos()) { + while (true) { uint32 value = stream.readUint16LE(); + if (stream.eos()) + break; // Check for CRLF if (value == 0x0A0D) { // Read in the extra NULL char diff --git a/engines/zvision/text/text.h b/engines/zvision/text/text.h index c942b8141a..5dd872a440 100644 --- a/engines/zvision/text/text.h +++ b/engines/zvision/text/text.h @@ -24,7 +24,6 @@ #ifndef ZVISION_TEXT_H #define ZVISION_TEXT_H -#include "zvision/detection.h" #include "zvision/text/truetype_font.h" #include "zvision/zvision.h" @@ -32,59 +31,53 @@ namespace ZVision { class ZVision; -enum txtJustify { - TXT_JUSTIFY_CENTER = 0, - TXT_JUSTIFY_LEFT = 1, - TXT_JUSTIFY_RIGHT = 2 +enum TextJustification { + TEXT_JUSTIFY_CENTER = 0, + TEXT_JUSTIFY_LEFT = 1, + TEXT_JUSTIFY_RIGHT = 2 }; -enum txtReturn { - TXT_RET_NOTHING = 0x0, - TXT_RET_FNTCHG = 0x1, - TXT_RET_FNTSTL = 0x2, - TXT_RET_NEWLN = 0x4, - TXT_RET_HASSTBOX = 0x8 +enum TextChange { + TEXT_CHANGE_NONE = 0x0, + TEXT_CHANGE_FONT_TYPE = 0x1, + TEXT_CHANGE_FONT_STYLE = 0x2, + TEXT_CHANGE_NEWLINE = 0x4, + TEXT_CHANGE_HAS_STATE_BOX = 0x8 }; -class cTxtStyle { +class TextStyleState { public: - cTxtStyle(); - txtReturn parseStyle(const Common::String &strin, int16 len); - void readAllStyle(const Common::String &txt); - void setFontStyle(StyledTTFont &font); - void setFont(StyledTTFont &font); + TextStyleState(); + TextChange parseStyle(const Common::String &str, int16 len); + void readAllStyles(const Common::String &txt); + void updateFontWithTextState(StyledTTFont &font); + + uint32 getTextColor(ZVision *engine) { + return engine->_resourcePixelFormat.RGBToColor(_red, _green, _blue); + } public: Common::String _fontname; - txtJustify _justify; // 0 - center, 1-left, 2-right + TextJustification _justification; int16 _size; uint8 _red; // 0-255 uint8 _green; // 0-255 uint8 _blue; // 0-255 -#if 0 - int8 _newline; - int8 _escapement; -#endif bool _italic; bool _bold; bool _underline; bool _strikeout; -#if 0 - bool _skipcolor; -#endif int32 _statebox; bool _sharp; - // char image ?? }; class TextRenderer { public: TextRenderer(ZVision *engine): _engine(engine) {}; - void drawTxtWithJustify(const Common::String &txt, StyledTTFont &fnt, uint32 color, Graphics::Surface &dst, int lineY, txtJustify justify); - int32 drawTxt(const Common::String &txt, cTxtStyle &fontStyle, Graphics::Surface &dst); - Graphics::Surface *render(StyledTTFont &fnt, const Common::String &txt, cTxtStyle &style); - void drawTxtInOneLine(const Common::String &txt, Graphics::Surface &dst); + void drawTextWithJustification(const Common::String &text, StyledTTFont &font, uint32 color, Graphics::Surface &dest, int lineY, TextJustification jusification); + int32 drawText(const Common::String &text, TextStyleState &state, Graphics::Surface &dest); + void drawTextWithWordWrapping(const Common::String &text, Graphics::Surface &dest); private: ZVision *_engine; diff --git a/engines/zvision/text/truetype_font.cpp b/engines/zvision/text/truetype_font.cpp index 622a02a6a8..acb053ea8d 100644 --- a/engines/zvision/text/truetype_font.cpp +++ b/engines/zvision/text/truetype_font.cpp @@ -26,6 +26,7 @@ #include "common/file.h" #include "common/system.h" #include "common/unzip.h" +#include "common/ustr.h" #include "graphics/font.h" #include "graphics/fonts/ttf.h" #include "graphics/surface.h" @@ -36,65 +37,73 @@ namespace ZVision { +const FontStyle systemFonts[] = { + { "*times new roman*", "times", "FreeSerif", "Italic", "LiberationSerif" }, + { "*times*", "times", "FreeSerif", "Italic", "LiberationSerif" }, + { "*century schoolbook*", "censcbk", "FreeSerif", "Italic", "LiberationSerif" }, + { "*garamond*", "gara", "FreeSerif", "Italic", "LiberationSerif" }, + { "*courier new*", "cour", "FreeMono", "Oblique", "LiberationMono" }, + { "*courier*", "cour", "FreeMono", "Oblique", "LiberationMono" }, + { "*ZorkDeath*", "cour", "FreeMono", "Oblique", "LiberationMono" }, + { "*arial*", "arial", "FreeSans", "Oblique", "LiberationSans" }, + { "*ZorkNormal*", "arial", "FreeSans", "Oblique", "LiberationSans" } +}; + +const FontStyle getSystemFont(int fontIndex) { + return systemFonts[fontIndex]; +} + StyledTTFont::StyledTTFont(ZVision *engine) { _engine = engine; _style = 0; - _font = NULL; + _font = nullptr; _lineHeight = 0; } StyledTTFont::~StyledTTFont() { - if (_font) - delete _font; + delete _font; } bool StyledTTFont::loadFont(const Common::String &fontName, int32 point, uint style) { - _style = style; - return loadFont(fontName, point); -} + // Don't re-load the font if we've already loaded it + // We have to check for empty so we can default to Arial + if (!fontName.empty() && _fontName.equalsIgnoreCase(fontName) && _lineHeight == point && _style == style) { + return true; + } -bool StyledTTFont::loadFont(const Common::String &fontName, int32 point) { - struct FontStyle { - const char *zorkFont; - const char *fontBase; - const char *freeFontBase; - const char *freeFontItalicName; - }; - - const FontStyle systemFonts[] = { - { "*times new roman*", "times", "FreeSerif", "Italic" }, - { "*times*", "times", "FreeSerif", "Italic" }, - { "*century schoolbook*", "censcbk", "FreeSerif", "Italic" }, - { "*garamond*", "gara", "FreeSerif", "Italic" }, - { "*courier new*", "cour", "FreeMono", "Oblique" }, - { "*courier*", "cour", "FreeMono", "Oblique" }, - { "*ZorkDeath*", "cour", "FreeMono", "Oblique" }, - { "*arial*", "arial", "FreeSans", "Oblique" }, - { "*ZorkNormal*", "arial", "FreeSans", "Oblique" }, - }; + _style = style; Common::String newFontName; Common::String freeFontName; + Common::String liberationFontName; - for (int i = 0; i < ARRAYSIZE(systemFonts); i++) { - if (fontName.matchString(systemFonts[i].zorkFont, true)) { - newFontName = systemFonts[i].fontBase; - freeFontName = systemFonts[i].freeFontBase; + for (int i = 0; i < FONT_COUNT; i++) { + FontStyle curFont = getSystemFont(i); + if (fontName.matchString(curFont.zorkFont, true)) { + newFontName = curFont.fontBase; + freeFontName = curFont.freeFontBase; + liberationFontName = curFont.liberationFontBase; - if ((_style & STTF_BOLD) && (_style & STTF_ITALIC)) { + if ((_style & TTF_STYLE_BOLD) && (_style & TTF_STYLE_ITALIC)) { newFontName += "bi"; freeFontName += "Bold"; - freeFontName += systemFonts[i].freeFontItalicName; - } else if (_style & STTF_BOLD) { + freeFontName += curFont.freeFontItalicName; + liberationFontName += "-BoldItalic"; + } else if (_style & TTF_STYLE_BOLD) { newFontName += "bd"; freeFontName += "Bold"; - } else if (_style & STTF_ITALIC) { + liberationFontName += "-Bold"; + } else if (_style & TTF_STYLE_ITALIC) { newFontName += "i"; - freeFontName += systemFonts[i].freeFontItalicName; + freeFontName += curFont.freeFontItalicName; + liberationFontName += "-Italic"; + } else { + liberationFontName += "-Regular"; } newFontName += ".ttf"; freeFontName += ".ttf"; + liberationFontName += ".ttf"; break; } } @@ -103,72 +112,99 @@ bool StyledTTFont::loadFont(const Common::String &fontName, int32 point) { debug("Could not identify font: %s. Reverting to Arial", fontName.c_str()); newFontName = "arial.ttf"; freeFontName = "FreeSans.ttf"; + liberationFontName = "LiberationSans-Regular.ttf"; } - bool sharp = (_style & STTF_SHARP) == STTF_SHARP; + bool sharp = (_style & TTF_STYLE_SHARP) == TTF_STYLE_SHARP; Common::File file; - if (!file.open(newFontName) && !file.open(freeFontName) && !_engine->getSearchManager()->openFile(file, newFontName) && !_engine->getSearchManager()->openFile(file, freeFontName)) - error("Unable to open font file %s (free alternative: %s)", newFontName.c_str(), freeFontName.c_str()); - - Graphics::Font *_newFont = Graphics::loadTTFFont(file, point, 60, (sharp ? Graphics::kTTFRenderModeMonochrome : Graphics::kTTFRenderModeNormal)); // 66 dpi for 640 x 480 on 14" display - if (_newFont) { - if (!_font) - delete _font; - _font = _newFont; + if (!file.open(newFontName) && !_engine->getSearchManager()->openFile(file, newFontName) && + !file.open(liberationFontName) && !_engine->getSearchManager()->openFile(file, liberationFontName) && + !file.open(freeFontName) && !_engine->getSearchManager()->openFile(file, freeFontName)) + error("Unable to open font file %s (Liberation Font alternative: %s, FreeFont alternative: %s)", newFontName.c_str(), liberationFontName.c_str(), freeFontName.c_str()); + + Graphics::Font *newFont = Graphics::loadTTFFont(file, point, 60, (sharp ? Graphics::kTTFRenderModeMonochrome : Graphics::kTTFRenderModeNormal)); // 66 dpi for 640 x 480 on 14" display + if (newFont == nullptr) { + return false; } - _fntName = fontName; - _lineHeight = point; + delete _font; + _font = newFont; - if (_font) - return true; - return false; -} + _fontName = fontName; + _lineHeight = point; -void StyledTTFont::setStyle(uint newStyle) { - if ((_style & (STTF_BOLD | STTF_ITALIC | STTF_SHARP)) != (newStyle & (STTF_BOLD | STTF_ITALIC | STTF_SHARP))) { - _style = newStyle; - loadFont(_fntName, _lineHeight); - } else { - _style = newStyle; - } + return true; } int StyledTTFont::getFontHeight() { if (_font) return _font->getFontHeight(); + return 0; } int StyledTTFont::getMaxCharWidth() { if (_font) return _font->getMaxCharWidth(); + return 0; } int StyledTTFont::getCharWidth(byte chr) { if (_font) return _font->getCharWidth(chr); + return 0; } int StyledTTFont::getKerningOffset(byte left, byte right) { if (_font) return _font->getKerningOffset(left, right); + return 0; } +Common::U32String StyledTTFont::convertUtf8ToUtf32(const Common::String &str) { + // The String class, and therefore the Font class as well, assume one + // character is one byte, but in this case it's actually an UTF-8 + // string with up to 4 bytes per character. To work around this, + // convert it to an U32String before drawing it, because our Font class + // can handle that. + Common::U32String u32str; + uint i = 0; + while (i < str.size()) { + uint32 chr = 0; + if ((str[i] & 0xF8) == 0xF0) { + chr |= (str[i++] & 0x07) << 18; + chr |= (str[i++] & 0x3F) << 12; + chr |= (str[i++] & 0x3F) << 6; + chr |= (str[i++] & 0x3F); + } else if ((str[i] & 0xF0) == 0xE0) { + chr |= (str[i++] & 0x0F) << 12; + chr |= (str[i++] & 0x3F) << 6; + chr |= (str[i++] & 0x3F); + } else if ((str[i] & 0xE0) == 0xC0) { + chr |= (str[i++] & 0x1F) << 6; + chr |= (str[i++] & 0x3F); + } else { + chr = (str[i++] & 0x7F); + } + u32str += chr; + } + return u32str; +} + void StyledTTFont::drawChar(Graphics::Surface *dst, byte chr, int x, int y, uint32 color) { if (_font) { _font->drawChar(dst, chr, x, y, color); - if (_style & STTF_UNDERLINE) { - int16 pos = floor(_font->getFontHeight() * 0.87); + if (_style & TTF_STYLE_UNDERLINE) { + int16 pos = (int16)floor(_font->getFontHeight() * 0.87); int thk = MAX((int)(_font->getFontHeight() * 0.05), 1); dst->fillRect(Common::Rect(x, y + pos, x + _font->getCharWidth(chr), y + pos + thk), color); } - if (_style & STTF_STRIKEOUT) { - int16 pos = floor(_font->getFontHeight() * 0.60); + if (_style & TTF_STYLE_STRIKETHROUGH) { + int16 pos = (int16)floor(_font->getFontHeight() * 0.60); int thk = MAX((int)(_font->getFontHeight() * 0.05), 1); dst->fillRect(Common::Rect(x, y + pos, x + _font->getCharWidth(chr), y + pos + thk), color); } @@ -177,10 +213,11 @@ void StyledTTFont::drawChar(Graphics::Surface *dst, byte chr, int x, int y, uint void StyledTTFont::drawString(Graphics::Surface *dst, const Common::String &str, int x, int y, int w, uint32 color, Graphics::TextAlign align) { if (_font) { - _font->drawString(dst, str, x, y, w, color, align); - if (_style & STTF_UNDERLINE) { - int16 pos = floor(_font->getFontHeight() * 0.87); - int16 wd = MIN(_font->getStringWidth(str), w); + Common::U32String u32str = convertUtf8ToUtf32(str); + _font->drawString(dst, u32str, x, y, w, color, align); + if (_style & TTF_STYLE_UNDERLINE) { + int16 pos = (int16)floor(_font->getFontHeight() * 0.87); + int16 wd = MIN(_font->getStringWidth(u32str), w); int16 stX = x; if (align == Graphics::kTextAlignCenter) stX += (w - wd) / 2; @@ -191,9 +228,9 @@ void StyledTTFont::drawString(Graphics::Surface *dst, const Common::String &str, dst->fillRect(Common::Rect(stX, y + pos, stX + wd, y + pos + thk), color); } - if (_style & STTF_STRIKEOUT) { - int16 pos = floor(_font->getFontHeight() * 0.60); - int16 wd = MIN(_font->getStringWidth(str), w); + if (_style & TTF_STYLE_STRIKETHROUGH) { + int16 pos = (int16)floor(_font->getFontHeight() * 0.60); + int16 wd = MIN(_font->getStringWidth(u32str), w); int16 stX = x; if (align == Graphics::kTextAlignCenter) stX += (w - wd) / 2; diff --git a/engines/zvision/text/truetype_font.h b/engines/zvision/text/truetype_font.h index b5fac4af8a..6abe05cda6 100644 --- a/engines/zvision/text/truetype_font.h +++ b/engines/zvision/text/truetype_font.h @@ -34,6 +34,16 @@ struct Surface; namespace ZVision { +struct FontStyle { + const char *zorkFont; + const char *fontBase; + const char *freeFontBase; + const char *freeFontItalicName; + const char *liberationFontBase; +}; + +#define FONT_COUNT 9 + class ZVision; // Styled TTF @@ -43,11 +53,11 @@ public: ~StyledTTFont(); enum { - STTF_BOLD = 1, - STTF_ITALIC = 2, - STTF_UNDERLINE = 4, - STTF_STRIKEOUT = 8, - STTF_SHARP = 16 + TTF_STYLE_BOLD = 0x01, + TTF_STYLE_ITALIC = 0x02, + TTF_STYLE_UNDERLINE = 0x04, + TTF_STYLE_STRIKETHROUGH = 0x08, + TTF_STYLE_SHARP = 0x10 }; private: @@ -55,18 +65,18 @@ private: Graphics::Font *_font; int _lineHeight; uint _style; - Common::String _fntName; + Common::String _fontName; public: - bool loadFont(const Common::String &fontName, int32 point); bool loadFont(const Common::String &fontName, int32 point, uint style); - void setStyle(uint newStyle); int getFontHeight(); int getMaxCharWidth(); int getCharWidth(byte chr); int getKerningOffset(byte left, byte right); + Common::U32String convertUtf8ToUtf32(const Common::String &str); + void drawChar(Graphics::Surface *dst, byte chr, int x, int y, uint32 color); void drawString(Graphics::Surface *dst, const Common::String &str, int x, int y, int w, uint32 color, Graphics::TextAlign align = Graphics::kTextAlignLeft); |