/* 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 3 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, see . * */ #include "common/rational.h" #include "common/unicode-bidi.h" #include "asylum/system/text.h" #include "asylum/system/graphics.h" #include "asylum/system/screen.h" #include "asylum/asylum.h" #include "asylum/respack.h" #if defined(USE_FREETYPE2) #include "graphics/fonts/ttf.h" #endif namespace Asylum { Text::Text(AsylumEngine *engine) : _vm(engine), _fontResource(nullptr), _transTableNum(0), _curFontFlags(0), _chineseFontLoadAttempted(false) { } Text::~Text() { delete _fontResource; } ResourceId Text::loadFont(ResourceId resourceId) { if (_fontResource && resourceId == _fontResource->getResourceId()) return resourceId; ResourceId previousFont = _fontResource ? _fontResource->getResourceId() : kResourceNone; delete _fontResource; _fontResource = nullptr; if (resourceId != kResourceNone) { _fontResource = new GraphicResource(_vm, resourceId); _curFontFlags = Common::Rational(_fontResource->getData().flags, 16).toInt() & 0x0F; } return previousFont; } void Text::loadChineseFont() { if (_chineseFontLoadAttempted) return; _chineseFontLoadAttempted = true; #if defined(USE_FREETYPE2) _chineseFont.reset(Graphics::loadTTFFontFromArchive("NotoSansSC-Regular.otf", 16, Graphics::kTTFSizeModeCharacter, 0, 0, Graphics::kTTFRenderModeLight)); #endif } void Text::setPosition(const Common::Point &point) { _position = point; } int16 Text::getWidth(char c) { if (!_fontResource) error("[Text::getWidth] Font not initialized properly"); GraphicFrame *font = _fontResource->getFrame((uint8)c); return (int16)(font->surface.w + font->x - _curFontFlags); } int16 Text::getChineseWidth(const Common::U32String &utext) { loadChineseFont(); if (!_chineseFont) return 0; return _chineseFont->getStringWidth(utext); } int16 Text::getWidth(const char *text) { if (!_fontResource) error("[Text::getWidth] font resource hasn't been loaded yet!"); if (_vm->getLanguage() == Common::Language::ZH_CHN) { return getChineseWidth(Common::U32String(text, Common::CodePage::kGBK)); } int16 width = 0; char character = *text; while (character) { GraphicFrame *font = _fontResource->getFrame((uint8)character); width += (int16)(font->surface.w + font->x - _curFontFlags); text++; character = *text; } return width; } int16 Text::getWidth(const char *text, int16 length) { if (!_fontResource) error("[Text::getWidth] font resource hasn't been loaded yet!"); if (length == 0) return 0; if (_vm->getLanguage() == Common::Language::ZH_CHN) { return getChineseWidth(Common::U32String(text, length, Common::CodePage::kGBK)); } int16 width = 0; char character = *text; while (character && length > 0) { GraphicFrame *font = _fontResource->getFrame((uint8)character); width += (int16)(font->surface.w + font->x - _curFontFlags); text++; character = *text; length--; } return width; } int16 Text::getWidth(ResourceId resourceId) { return getWidth(get(resourceId)); } char *Text::get(ResourceId resourceId) { ResourceEntry *textRes = getResource()->get(resourceId); return (char *)textRes->data; } void Text::drawChar(char character) { if (!_fontResource) error("[Text::drawChar] font resource hasn't been loaded yet!"); if (_transTableNum) { getScreen()->drawTransparent(_fontResource, (uint8)character, _position, kDrawFlagNone, _transTableNum); } else { getScreen()->draw(_fontResource, (uint8)character, _position); } GraphicFrame *fontLetter = _fontResource->getFrame((uint8)character); _position.x += (int16)fontLetter->surface.w + fontLetter->x - _curFontFlags; } void Text::drawChinese(const Common::U32String &utext) { loadChineseFont(); if (!_chineseFont) return; Graphics::Surface *surf = getScreen()->getSurface(); uint8 color = 0; // TODO: Add more colors switch ((uint32_t)_fontResource->getResourceId()) { case 0: // Case to quiet VS C4065 warning default: debug(5, "Unrecognized font resource 0x%x for string %s", _fontResource->getResourceId(), utext.encode().c_str()); color = 1; break; case 0x80010039: color = 0xff; break; case 0x8005000d: color = 0x10; break; case 0x8005000e: color = 0x25; break; case 0x8005000f: color = 0x1f; break; case 0x80120012: color = 0x69; break; } _chineseFont->drawString(surf, utext, _position.x, _position.y, surf->w - _position.x, color); _position.x += _chineseFont->getStringWidth(utext); } void Text::draw(const char *text) { if (!text) return; if (_vm->getLanguage() == Common::Language::ZH_CHN) { drawChinese(Common::U32String(text, Common::CodePage::kGBK)); return; } Common::String textRef; if (_vm->getLanguage() == Common::HE_ISR) { textRef = Common::convertBiDiString(text, Common::kWindows1255); text = textRef.c_str(); } while (*text) { drawChar(text[0]); text++; } } void Text::draw(const char *text, int16 length) { if (length == 0) return; if (!text) return; if (_vm->getLanguage() == Common::Language::ZH_CHN) { drawChinese(Common::U32String(text, length, Common::CodePage::kGBK)); return; } if (_vm->getLanguage() == Common::HE_ISR) text = Common::convertBiDiString(Common::String(text, length), Common::kWindows1255).c_str(); for (int16 i = 0; i < length; i++) drawChar(text[i]); } void Text::draw(ResourceId resourceId) { ResourceEntry *textRes = getResource()->get(resourceId); draw((char *)textRes->data); } void Text::draw(const Common::Point &point, const char *text) { setPosition(Common::Point(point.x - (int16)getWidth(text), point.y)); draw(text); } void Text::draw(const Common::Point &point, ResourceId resourceId) { draw(point, get(resourceId)); } void Text::draw(const char *text, ResourceId fontResourceId, int16 y) { if (text) { loadFont(fontResourceId); draw(kTextCenter, Common::Point(20, y), 16, 600, text); } } int16 Text::draw(TextCentering centering, const Common::Point &point, int16 spacing, int16 width, const char *text) { return draw(0, 99, centering, point, spacing, width, text); } int16 Text::draw(int16 a1, int16 a2, TextCentering centering, const Common::Point &point, int16 spacing, int16 width, const char *text) { if (!text || !*text) return 0; // TODO: Make non-Chinese into Graphics::Font as well. if (_vm->getLanguage() == Common::Language::ZH_CHN) { Common::Array lines; Common::Point coords = point; int16 printed = 0; loadChineseFont(); if (!_chineseFont) return 0; char *buf = scumm_strdup(text); for (char *ptr = buf; *ptr; ptr++) if (*ptr == 1 || *ptr == 2) // Start of heading (SOH) or start of text (STX) *ptr = '\n'; Common::U32String utext(buf, Common::CodePage::kGBK); free(buf); _chineseFont->wordWrapText(utext, width, lines); for (int index = a1; index <= (a1 + a2) && index < (int)lines.size(); index++) { switch (centering) { default: case kTextCalculate: break; case kTextCenter: setPosition(coords + Common::Point((width - getChineseWidth(lines[index])) / 2, 0)); drawChinese(lines[index]); break; case kTextNormal: setPosition(coords); drawChinese(lines[index]); break; } coords.y += spacing; ++printed; } return printed; } Common::Point coords = point; int16 printed = 0; bool drawText = false; int32 spaceWidth = 0; int32 index = 0; const char *string = text; const char *endText = text; for (;;) { label_start: int32 charWidth = 0; // Draw the text if (drawText) { char currentChar = *endText; if (index >= a1 && index <= (a1 + a2)) { switch (centering) { default: case kTextCalculate: break; case kTextCenter: drawCentered(coords, width, (int16)(endText - string), string); break; case kTextNormal: setPosition(coords); draw(text, (int16)(endText - text)); break; } coords.y += spacing; ++printed; } ++index; if (!currentChar) break; charWidth = 0; spaceWidth = 0; text = endText + 1; // Skip spaces if (currentChar == ' ' && *text == ' ') do { ++text; } while (*text == ' '); } const char *txt = text; int32 w = 0; for (;;) { char c = *txt; w += charWidth; charWidth = spaceWidth; const char *txt2 = txt; if (*txt != ' ') { do { if (!c) break; if (c == 1) // Start of heading (SOH) break; charWidth += getWidth(c); txt++; c = txt[0]; } while (c != ' '); } if ((w + charWidth) > width) { string = text; endText = txt2 - 1; drawText = true; goto label_start; } if (!*txt) break; if (*txt == 1 || *txt == 2) // Start of heading (SOH) or start of text (STX) break; ++txt; spaceWidth = getWidth(' '); } string = text; endText = txt; drawText = true; } return printed; } void Text::drawCentered(const Common::Point &point, int16 width, const char *text) { setPosition(Common::Point(point.x + (width - getWidth(text)) / 2, point.y)); draw(text); } void Text::drawCentered(const Common::Point &point, int16 width, int16 length, const char *text) { setPosition(Common::Point(point.x + (width - getWidth(text, length)) / 2, point.y)); draw(text, length); } void Text::drawCentered(const Common::Point &point, int16 width, ResourceId resourceId) { drawCentered(point, width, get(resourceId)); } } // end of namespace Asylum