429 lines
10 KiB
C++
429 lines
10 KiB
C++
/* 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#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<Common::U32String> 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
|