Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,140 @@
/* 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/>.
*
*/
/*
* This file is based on WME Lite.
* http://dead-code.org/redir.php?target=wmelite
* Copyright (c) 2011 Jan Nedoma
*/
#include "engines/wintermute/base/font/base_font.h"
#include "engines/wintermute/base/font/base_font_bitmap.h"
#include "engines/wintermute/base/font/base_font_truetype.h"
#include "engines/wintermute/base/base_parser.h"
#include "engines/wintermute/base/base_file_manager.h"
#include "engines/wintermute/base/base_game.h"
namespace Wintermute {
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
IMPLEMENT_PERSISTENT(BaseFont, false)
//////////////////////////////////////////////////////////////////////
BaseFont::BaseFont(BaseGame *inGame) : BaseObject(inGame) {
}
//////////////////////////////////////////////////////////////////////
BaseFont::~BaseFont() {
}
//////////////////////////////////////////////////////////////////////
void BaseFont::drawText(const byte *text, int x, int y, int width, TTextAlign align, int maxHeight, int maxLength) {
}
//////////////////////////////////////////////////////////////////////
int BaseFont::getTextHeight(const byte *text, int width) {
return 0;
}
//////////////////////////////////////////////////////////////////////
int BaseFont::getTextWidth(const byte *text, int maxLength) {
return 0;
}
//////////////////////////////////////////////////////////////////////////
int BaseFont::getLetterHeight() {
return 0;
}
//////////////////////////////////////////////////////////////////////////
bool BaseFont::persist(BasePersistenceManager *persistMgr) {
BaseObject::persist(persistMgr);
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
BaseFont *BaseFont::createFromFile(BaseGame *game, const char *filename) {
if (isTrueType(game, filename)) {
BaseFontTT *font = new BaseFontTT(game);
if (font) {
if (DID_FAIL(font->loadFile(filename))) {
delete font;
return nullptr;
}
}
return font;
} else {
BaseFontBitmap *font = new BaseFontBitmap(game);
if (font) {
if (DID_FAIL(font->loadFile(filename))) {
delete font;
return nullptr;
}
}
return font;
}
}
TOKEN_DEF_START
TOKEN_DEF(FONT)
TOKEN_DEF(TTFONT)
TOKEN_DEF_END
//////////////////////////////////////////////////////////////////////////
bool BaseFont::isTrueType(BaseGame *game, const char *filename) {
TOKEN_TABLE_START(commands)
TOKEN_TABLE(FONT)
TOKEN_TABLE(TTFONT)
TOKEN_TABLE_END
char *buffer = (char *)game->_fileManager->readWholeFile(filename);
if (buffer == nullptr) {
return false;
}
char *workBuffer = buffer;
char *params;
BaseParser parser(game);
bool ret = false;
if (parser.getCommand(&workBuffer, commands, &params) == TOKEN_TTFONT) {
ret = true;
}
delete[] buffer;
return ret;
}
} // End of namespace Wintermute

View File

@@ -0,0 +1,60 @@
/* 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/>.
*
*/
/*
* This file is based on WME Lite.
* http://dead-code.org/redir.php?target=wmelite
* Copyright (c) 2011 Jan Nedoma
*/
#ifndef WINTERMUTE_BASE_FONT_H
#define WINTERMUTE_BASE_FONT_H
#include "engines/wintermute/base/base_object.h"
#define NUM_CHARACTERS 256
namespace Wintermute {
class BaseFont : public BaseObject {
public:
DECLARE_PERSISTENT(BaseFont, BaseObject)
virtual int getTextWidth(const byte *text, int maxLength = -1);
virtual int getTextHeight(const byte *text, int width);
virtual void drawText(const byte *text, int x, int y, int width, TTextAlign align = TAL_LEFT, int max_height = -1, int maxLength = -1);
virtual int getLetterHeight();
virtual void initLoop() {}
virtual void afterLoad() {}
BaseFont(BaseGame *inGame);
~BaseFont() override;
static BaseFont *createFromFile(BaseGame *game, const char *filename);
private:
//bool loadBuffer(char *buffer);
//bool loadFile(const char* Filename);
static bool isTrueType(BaseGame *game, const char *filename);
};
} // End of namespace Wintermute
#endif

View File

@@ -0,0 +1,628 @@
/* 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/>.
*
*/
/*
* This file is based on WME Lite.
* http://dead-code.org/redir.php?target=wmelite
* Copyright (c) 2011 Jan Nedoma
*/
#include "engines/wintermute/base/font/base_font_bitmap.h"
#include "engines/wintermute/utils/string_util.h"
#include "engines/wintermute/base/base_engine.h"
#include "engines/wintermute/base/base_parser.h"
#include "engines/wintermute/base/base_frame.h"
#include "engines/wintermute/base/gfx/base_surface.h"
#include "engines/wintermute/base/gfx/base_renderer.h"
#include "engines/wintermute/base/base_game.h"
#include "engines/wintermute/base/base_sub_frame.h"
#include "engines/wintermute/base/base_frame.h"
#include "engines/wintermute/base/base_sprite.h"
#include "engines/wintermute/base/base_file_manager.h"
#include "engines/wintermute/platform_osystem.h"
#include "engines/wintermute/dcgf.h"
namespace Wintermute {
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
IMPLEMENT_PERSISTENT(BaseFontBitmap, false)
//////////////////////////////////////////////////////////////////////
BaseFontBitmap::BaseFontBitmap(BaseGame *inGame) : BaseFont(inGame) {
_subframe = nullptr;
_sprite = nullptr;
_widthsFrame = 0;
memset(_widths, 0, NUM_CHARACTERS);
_tileWidth = _tileHeight = _numColumns = 0;
_fontextFix = false;
_freezable = false;
_wholeCell = false;
}
//////////////////////////////////////////////////////////////////////
BaseFontBitmap::~BaseFontBitmap() {
SAFE_DELETE(_subframe);
SAFE_DELETE(_sprite);
}
//////////////////////////////////////////////////////////////////////
void BaseFontBitmap::drawText(const byte *text, int x, int y, int width, TTextAlign align, int maxHeight, int maxLength) {
textHeightDraw(text, x, y, width, align, true, maxHeight, maxLength);
}
//////////////////////////////////////////////////////////////////////
int BaseFontBitmap::getTextHeight(const byte *text, int width) {
return textHeightDraw(text, 0, 0, width, TAL_LEFT, false);
}
//////////////////////////////////////////////////////////////////////
int BaseFontBitmap::getTextWidth(const byte *text, int maxLength) {
AnsiString str;
if (_game->_textEncoding == TEXT_UTF8) {
WideString wstr = StringUtil::utf8ToWide(Utf8String((const char *)text));
str = StringUtil::wideToAnsi(wstr);
} else {
str = AnsiString((const char *)text);
}
if (maxLength >= 0 && (int)str.size() > maxLength) {
str = Common::String(str.c_str(), (uint32)maxLength);
}
//str.substr(0, maxLength); // TODO: Remove
int textWidth = 0;
for (int i = 0; (uint32)i < str.size(); i++) {
textWidth += getCharWidth((byte)str[i]);
}
return textWidth;
}
//////////////////////////////////////////////////////////////////////
int BaseFontBitmap::textHeightDraw(const byte *text, int x, int y, int width, TTextAlign align, bool draw, int maxHeight, int maxLength) {
if (maxLength == 0) {
return 0;
}
if (text == nullptr || text[0] == '\0') {
return _tileHeight;
}
AnsiString str;
if (_game->_textEncoding == TEXT_UTF8) {
WideString wstr = StringUtil::utf8ToWide(Utf8String((const char *)text));
str = StringUtil::wideToAnsi(wstr);
} else {
str = AnsiString((const char *)text);
}
if (str.empty()) {
return 0;
}
int lineLength = 0;
int realLength = 0;
int numLines = 0;
int i;
int index = -1;
int start = 0;
int end = 0;
int last_end = 0;
bool done = false;
bool newLine = false;
bool longLine = false;
#ifdef ENABLE_FOXTAIL
bool minimizeSpacing = BaseEngine::instance().isFoxTail();
#endif
if (draw) {
_game->_renderer->startSpriteBatch();
}
while (!done) {
if (maxHeight > 0 && (numLines + 1) * _tileHeight > maxHeight) {
if (draw) {
_game->_renderer->endSpriteBatch();
}
return numLines * _tileHeight;
}
index++;
if (str[index] == ' ' && (maxHeight < 0 || maxHeight / _tileHeight > 1)) {
end = index - 1;
realLength = lineLength;
}
if (str[index] == '\n') {
end = index - 1;
realLength = lineLength;
newLine = true;
}
if (lineLength + getCharWidth(str[index]) > width && last_end == end) {
end = index - 1;
realLength = lineLength;
newLine = true;
longLine = true;
}
if ((int)str.size() == (index + 1) || (maxLength >= 0 && index == maxLength - 1)) {
done = true;
if (!newLine) {
end = index;
lineLength += getCharWidth(str[index]);
realLength = lineLength;
}
} else {
lineLength += getCharWidth(str[index]);
}
if ((lineLength > width) || done || newLine) {
if (end < 0) {
done = true;
}
int startX = x;
switch (align) {
case TAL_CENTER:
startX = x + (width - realLength) / 2;
break;
case TAL_RIGHT:
startX = x + width - realLength;
break;
case TAL_LEFT:
startX = x;
break;
default:
break;
}
for (i = start; i < end + 1; i++) {
if (draw) {
drawChar(str[i], startX, y);
}
startX += getCharWidth(str[i]);
}
y += _tileHeight;
#ifdef ENABLE_FOXTAIL
if (minimizeSpacing) {
y -= 3;
}
#endif
last_end = end;
if (longLine) {
end--;
}
start = end + 2;
index = end + 1;
lineLength = 0;
newLine = false;
longLine = false;
numLines++;
}
}
if (draw) {
_game->_renderer->endSpriteBatch();
}
return numLines * _tileHeight;
}
//////////////////////////////////////////////////////////////////////
void BaseFontBitmap::drawChar(byte c, int x, int y) {
if (_fontextFix) {
c--;
}
int row, col;
row = c / _numColumns;
col = c % _numColumns;
Common::Rect32 rect;
/* l t r b */
int tileWidth;
if (_wholeCell) {
tileWidth = _tileWidth;
} else {
tileWidth = _widths[c];
}
BasePlatform::setRect(&rect, col * _tileWidth, row * _tileHeight, col * _tileWidth + tileWidth, (row + 1) * _tileHeight);
bool handled = false;
if (_sprite) {
_sprite->getCurrentFrame();
if (_sprite->_currentFrame >= 0 && _sprite->_currentFrame < _sprite->_frames.getSize() && _sprite->_frames[_sprite->_currentFrame]) {
if (_sprite->_frames[_sprite->_currentFrame]->_subframes.getSize() > 0) {
_sprite->_frames[_sprite->_currentFrame]->_subframes[0]->_surface->displayTrans(x, y, rect);
}
handled = true;
}
}
if (!handled && _subframe) {
_subframe->_surface->displayTrans(x, y, rect, _subframe->_alpha);
}
}
//////////////////////////////////////////////////////////////////////
bool BaseFontBitmap::loadFile(const char *filename) {
char *buffer = (char *)_game->_fileManager->readWholeFile(filename);
if (buffer == nullptr) {
_game->LOG(0, "BaseFontBitmap::loadFile failed for file '%s'", filename);
return STATUS_FAILED;
}
bool ret;
setFilename(filename);
if (DID_FAIL(ret = loadBuffer(buffer))) {
_game->LOG(0, "Error parsing FONT file '%s'", filename);
}
delete[] buffer;
return ret;
}
TOKEN_DEF_START
TOKEN_DEF(FONTEXT_FIX)
TOKEN_DEF(FONT)
TOKEN_DEF(IMAGE)
TOKEN_DEF(TRANSPARENT)
TOKEN_DEF(COLUMNS)
TOKEN_DEF(TILE_WIDTH)
TOKEN_DEF(TILE_HEIGHT)
TOKEN_DEF(DEFAULT_WIDTH)
TOKEN_DEF(WIDTHS)
TOKEN_DEF(AUTO_WIDTH)
TOKEN_DEF(SPACE_WIDTH)
TOKEN_DEF(EXPAND_WIDTH)
TOKEN_DEF(EDITOR_PROPERTY)
TOKEN_DEF(SPRITE)
TOKEN_DEF(WIDTHS_FRAME)
TOKEN_DEF(PAINT_WHOLE_CELL)
#ifdef ENABLE_FOXTAIL
TOKEN_DEF(COLOR)
#endif
TOKEN_DEF_END
//////////////////////////////////////////////////////////////////////
bool BaseFontBitmap::loadBuffer(char *buffer) {
TOKEN_TABLE_START(commands)
TOKEN_TABLE(FONTEXT_FIX)
TOKEN_TABLE(FONT)
TOKEN_TABLE(IMAGE)
TOKEN_TABLE(TRANSPARENT)
TOKEN_TABLE(COLUMNS)
TOKEN_TABLE(TILE_WIDTH)
TOKEN_TABLE(TILE_HEIGHT)
TOKEN_TABLE(DEFAULT_WIDTH)
TOKEN_TABLE(WIDTHS)
TOKEN_TABLE(AUTO_WIDTH)
TOKEN_TABLE(SPACE_WIDTH)
TOKEN_TABLE(EXPAND_WIDTH)
TOKEN_TABLE(EDITOR_PROPERTY)
TOKEN_TABLE(SPRITE)
TOKEN_TABLE(WIDTHS_FRAME)
TOKEN_TABLE(PAINT_WHOLE_CELL)
#ifdef ENABLE_FOXTAIL
TOKEN_TABLE(COLOR)
#endif
TOKEN_TABLE_END
char *params;
int cmd;
BaseParser parser(_game);
if (parser.getCommand(&buffer, commands, &params) != TOKEN_FONT) {
_game->LOG(0, "'FONT' keyword expected.");
return STATUS_FAILED;
}
buffer = params;
int widths[300];
int num = 0, defaultWidth = 8;
int lastWidth = 0;
int i;
int r = 255, g = 255, b = 255;
bool customTrans = false;
#ifdef ENABLE_FOXTAIL
int ar = 255, ag = 255, ab = 255;
bool customAlpha = false;
#endif
char *surfaceFile = nullptr;
char *spriteFile = nullptr;
bool autoWidth = false;
int spaceWidth = 0;
int expandWidth = 0;
while ((cmd = parser.getCommand(&buffer, commands, &params)) > 0) {
switch (cmd) {
case TOKEN_IMAGE:
surfaceFile = params;
break;
case TOKEN_SPRITE:
spriteFile = params;
break;
case TOKEN_TRANSPARENT:
parser.scanStr(params, "%d,%d,%d", &r, &g, &b);
customTrans = true;
break;
#ifdef ENABLE_FOXTAIL
case TOKEN_COLOR:
parser.scanStr(params, "%d,%d,%d", &ar, &ag, &ab);
customAlpha = true;
break;
#endif
case TOKEN_WIDTHS:
parser.scanStr(params, "%D", widths, &num);
for (i = 0; lastWidth < NUM_CHARACTERS && num > 0; lastWidth++, num--, i++) {
_widths[lastWidth] = (byte)widths[i];
}
break;
case TOKEN_DEFAULT_WIDTH:
parser.scanStr(params, "%d", &defaultWidth);
break;
case TOKEN_WIDTHS_FRAME:
parser.scanStr(params, "%d", &_widthsFrame);
break;
case TOKEN_COLUMNS:
parser.scanStr(params, "%d", &_numColumns);
break;
case TOKEN_TILE_WIDTH:
parser.scanStr(params, "%d", &_tileWidth);
break;
case TOKEN_TILE_HEIGHT:
parser.scanStr(params, "%d", &_tileHeight);
break;
case TOKEN_AUTO_WIDTH:
parser.scanStr(params, "%b", &autoWidth);
break;
case TOKEN_FONTEXT_FIX:
parser.scanStr(params, "%b", &_fontextFix);
break;
case TOKEN_PAINT_WHOLE_CELL:
parser.scanStr(params, "%b", &_wholeCell);
break;
case TOKEN_SPACE_WIDTH:
parser.scanStr(params, "%d", &spaceWidth);
break;
case TOKEN_EXPAND_WIDTH:
parser.scanStr(params, "%d", &expandWidth);
break;
case TOKEN_EDITOR_PROPERTY:
parseEditorProperty(params, false);
break;
default:
break;
}
}
if (cmd == PARSERR_TOKENNOTFOUND) {
_game->LOG(0, "Syntax error in FONT definition");
return STATUS_FAILED;
}
if (spriteFile != nullptr) {
SAFE_DELETE(_sprite);
_sprite = new BaseSprite(_game, this);
if (!_sprite || DID_FAIL(_sprite->loadFile(spriteFile))) {
SAFE_DELETE(_sprite);
}
}
if (surfaceFile != nullptr && !_sprite) {
_subframe = new BaseSubFrame(_game);
if (customTrans) {
_subframe->setSurface(surfaceFile, false, r, g, b);
} else {
_subframe->setSurface(surfaceFile);
}
#ifdef ENABLE_FOXTAIL
if (customAlpha) {
_subframe->_alpha = BYTETORGBA(ar, ag, ab, 255);
}
#endif
}
if (((_subframe == nullptr || _subframe->_surface == nullptr) && _sprite == nullptr) || _numColumns == 0 || _tileWidth == 0 || _tileHeight == 0) {
_game->LOG(0, "Incomplete font definition");
return STATUS_FAILED;
}
if (autoWidth) {
// calculate characters width
getWidths();
// do we need to modify widths?
if (expandWidth != 0) {
for (i = 0; i < NUM_CHARACTERS; i++) {
int newWidth = (int)_widths[i] + expandWidth;
if (newWidth < 0) {
newWidth = 0;
}
_widths[i] = (byte)newWidth;
}
}
// handle space character
uint32 spaceChar = ' ';
if (_fontextFix) {
spaceChar--;
}
if (spaceWidth != 0) {
_widths[spaceChar] = spaceWidth;
} else {
if (_widths[spaceChar] == expandWidth || _widths[spaceChar] == 0) {
_widths[spaceChar] = (_widths[(uint32)'m'] + _widths[(uint32)'i']) / 2;
}
}
} else {
for (i = lastWidth; i < NUM_CHARACTERS; i++) {
_widths[i] = defaultWidth;
}
}
#ifdef ENABLE_FOXTAIL
if (BaseEngine::instance().isFoxTail()) {
for (i = lastWidth; i < NUM_CHARACTERS; i++) {
_widths[i]--;
}
}
#endif
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
bool BaseFontBitmap::persist(BasePersistenceManager *persistMgr) {
BaseFont::persist(persistMgr);
persistMgr->transferSint32(TMEMBER(_numColumns));
persistMgr->transferPtr(TMEMBER_PTR(_subframe));
persistMgr->transferSint32(TMEMBER(_tileHeight));
persistMgr->transferSint32(TMEMBER(_tileWidth));
persistMgr->transferPtr(TMEMBER_PTR(_sprite));
persistMgr->transferSint32(TMEMBER(_widthsFrame));
if (persistMgr->getIsSaving()) {
persistMgr->putBytes(_widths, sizeof(_widths));
} else {
persistMgr->getBytes(_widths, sizeof(_widths));
}
persistMgr->transferBool(TMEMBER(_fontextFix));
persistMgr->transferBool(TMEMBER(_wholeCell));
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
int BaseFontBitmap::getCharWidth(byte index) {
if (_fontextFix) {
index--;
}
return _widths[index];
}
//////////////////////////////////////////////////////////////////////////
bool BaseFontBitmap::getWidths() {
BaseSurface *surf = nullptr;
if (_sprite) {
if (_widthsFrame >= 0 && _widthsFrame < _sprite->_frames.getSize()) {
if (_sprite->_frames[_widthsFrame] && _sprite->_frames[_widthsFrame]->_subframes.getSize() > 0) {
surf = _sprite->_frames[_widthsFrame]->_subframes[0]->_surface;
}
}
}
if (surf == nullptr && _subframe) {
surf = _subframe->_surface;
}
if (!surf || DID_FAIL(surf->startPixelOp())) {
return STATUS_FAILED;
}
for (int i = 0; i < NUM_CHARACTERS; i++) {
int xxx = (i % _numColumns) * _tileWidth;
int yyy = (i / _numColumns) * _tileHeight;
int minCol = -1;
for (int row = 0; row < _tileHeight; row++) {
for (int col = _tileWidth - 1; col >= minCol + 1; col--) {
if (xxx + col < 0 || xxx + col >= surf->getWidth() || yyy + row < 0 || yyy + row >= surf->getHeight()) {
continue;
}
if (!surf->isTransparentAtLite(xxx + col, yyy + row)) {
//min_col = col;
minCol = MAX(col, minCol);
break;
}
}
if (minCol == _tileWidth - 1) {
break;
}
}
_widths[i] = minCol + 1;
}
surf->endPixelOp();
/*
_game->LOG(0, "----- %s ------", _filename);
for(int j=0; j<16; j++)
{
_game->LOG(0, "%02d %02d %02d %02d %02d %02d %02d %02d %02d %02d %02d %02d %02d %02d %02d %02d", _widths[j*16+0], _widths[j*16+1], _widths[j*16+2], _widths[j*16+3], _widths[j*16+4], _widths[j*16+5], _widths[j*16+6], _widths[j*16+7], _widths[j*16+8], _widths[j*16+9], _widths[j*16+10], _widths[j*16+11], _widths[j*16+12], _widths[j*16+13], _widths[j*16+14], _widths[j*16+15]);
}
*/
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
int BaseFontBitmap::getLetterHeight() {
return _tileHeight;
}
} // End of namespace Wintermute

View File

@@ -0,0 +1,70 @@
/* 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/>.
*
*/
/*
* This file is based on WME Lite.
* http://dead-code.org/redir.php?target=wmelite
* Copyright (c) 2011 Jan Nedoma
*/
#ifndef WINTERMUTE_BASE_FONTBITMAP_H
#define WINTERMUTE_BASE_FONTBITMAP_H
#include "engines/wintermute/base/font/base_font.h"
namespace Wintermute {
class BaseSubFrame;
class BaseFontBitmap : public BaseFont {
public:
DECLARE_PERSISTENT(BaseFontBitmap, BaseFont)
bool loadBuffer(char *buffer);
bool loadFile(const char *filename);
int getTextWidth(const byte *text, int maxLength = -1) override;
int getTextHeight(const byte *text, int width) override;
void drawText(const byte *text, int x, int y, int width, TTextAlign align = TAL_LEFT, int max_height = -1, int maxLength = -1) override;
int getLetterHeight() override;
BaseFontBitmap(BaseGame *inGame);
~BaseFontBitmap() override;
bool getWidths();
BaseSprite *_sprite;
int32 _widthsFrame;
bool _fontextFix;
int32 _numColumns;
int32 _tileHeight;
int32 _tileWidth;
byte _widths[NUM_CHARACTERS];
BaseSubFrame *_subframe;
bool _wholeCell;
private:
int getCharWidth(byte index);
void drawChar(byte c, int x, int y);
int textHeightDraw(const byte *text, int x, int y, int width, TTextAlign align, bool draw, int max_height = -1, int maxLength = -1);
};
} // End of namespace Wintermute
#endif

View File

@@ -0,0 +1,139 @@
/* 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/>.
*
*/
/*
* This file is based on WME Lite.
* http://dead-code.org/redir.php?target=wmelite
* Copyright (c) 2011 Jan Nedoma
*/
#include "engines/wintermute/base/font/base_font_storage.h"
#include "engines/wintermute/base/font/base_font.h"
#include "engines/wintermute/base/base_game.h"
#include "common/str.h"
namespace Wintermute {
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
IMPLEMENT_PERSISTENT(BaseFontStorage, true)
//////////////////////////////////////////////////////////////////////////
BaseFontStorage::BaseFontStorage(BaseGame *inGame) : BaseClass(inGame) {
}
//////////////////////////////////////////////////////////////////////////
BaseFontStorage::~BaseFontStorage() {
cleanup(true);
}
//////////////////////////////////////////////////////////////////////////
bool BaseFontStorage::cleanup(bool warn) {
for (int32 i = 0; i < _fonts.getSize(); i++) {
if (warn)
_game->LOG(0, "Removing orphan font '%s'", _fonts[i]->_filename);
delete _fonts[i];
}
_fonts.removeAll();
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
bool BaseFontStorage::initLoop() {
for (int32 i = 0; i < _fonts.getSize(); i++) {
_fonts[i]->initLoop();
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
BaseFont *BaseFontStorage::addFont(const char *filename) {
if (!filename) {
return nullptr;
}
for (int32 i = 0; i < _fonts.getSize(); i++) {
if (scumm_stricmp(_fonts[i]->_filename, filename) == 0) {
_fonts[i]->_refCount++;
return _fonts[i];
}
}
/*
BaseFont* font = new BaseFont(_game);
if (!font) return nullptr;
if (DID_FAIL(font->loadFile(filename))) {
delete font;
return nullptr;
}
else {
font->_refCount = 1;
_fonts.add(font);
return font;
}
*/
BaseFont *font = BaseFont::createFromFile(_game, filename);
if (font) {
font->_refCount = 1;
_fonts.add(font);
}
return font;
}
//////////////////////////////////////////////////////////////////////////
bool BaseFontStorage::removeFont(BaseFont *font) {
if (!font) {
return STATUS_FAILED;
}
for (int32 i = 0; i < _fonts.getSize(); i++) {
if (_fonts[i] == font) {
_fonts[i]->_refCount--;
if (_fonts[i]->_refCount <= 0) {
delete _fonts[i];
_fonts.removeAt(i);
}
break;
}
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
bool BaseFontStorage::persist(BasePersistenceManager *persistMgr) {
if (!persistMgr->getIsSaving()) {
cleanup(false);
}
persistMgr->transferPtr(TMEMBER_PTR(_game));
_fonts.persist(persistMgr);
return STATUS_OK;
}
} // End of namespace Wintermute

View File

@@ -0,0 +1,55 @@
/* 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/>.
*
*/
/*
* This file is based on WME Lite.
* http://dead-code.org/redir.php?target=wmelite
* Copyright (c) 2011 Jan Nedoma
*/
#ifndef WINTERMUTE_BASE_FONTSTORAGE_H
#define WINTERMUTE_BASE_FONTSTORAGE_H
#include "engines/wintermute/base/base.h"
#include "engines/wintermute/persistent.h"
#include "engines/wintermute/coll_templ.h"
namespace Wintermute {
class BaseFont;
class BaseFontStorage : public BaseClass {
public:
DECLARE_PERSISTENT(BaseFontStorage, BaseClass)
bool cleanup(bool warn = false);
bool removeFont(BaseFont *font);
BaseFont *addFont(const char *filename);
BaseFontStorage(BaseGame *inGame);
~BaseFontStorage() override;
BaseArray<BaseFont *> _fonts;
bool initLoop();
};
} // End of namespace Wintermute
#endif

View File

@@ -0,0 +1,629 @@
/* 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/>.
*
*/
/*
* This file is based on WME Lite.
* http://dead-code.org/redir.php?target=wmelite
* Copyright (c) 2011 Jan Nedoma
*/
#include "engines/wintermute/base/font/base_font_truetype.h"
#include "engines/wintermute/utils/string_util.h"
#include "engines/wintermute/base/gfx/base_renderer.h"
#include "engines/wintermute/base/gfx/base_surface.h"
#include "engines/wintermute/base/base_parser.h"
#include "engines/wintermute/base/base_game.h"
#include "engines/wintermute/base/base_engine.h"
#include "engines/wintermute/base/base_file_manager.h"
#include "engines/wintermute/utils/utils.h"
#include "engines/wintermute/platform_osystem.h"
#include "engines/wintermute/wintermute.h"
#include "engines/wintermute/dcgf.h"
#include "graphics/fonts/ttf.h"
#include "graphics/fontman.h"
#include "common/unicode-bidi.h"
#include "common/compression/unzip.h"
namespace Wintermute {
IMPLEMENT_PERSISTENT(BaseFontTT, false)
//////////////////////////////////////////////////////////////////////////
BaseFontTT::BaseFontTT(BaseGame *inGame) : BaseFont(inGame) {
_fontHeight = 12;
_isBold = _isItalic = _isUnderline = _isStriked = false;
_charset = CHARSET_ANSI;
_fontFile = nullptr;
_font = nullptr;
_fallbackFont = nullptr;
_deletableFont = nullptr;
for (int i = 0; i < NUM_CACHED_TEXTS; i++) {
_cachedTexts[i] = nullptr;
}
_lineHeight = 0;
_maxCharWidth = _maxCharHeight = 0;
}
//////////////////////////////////////////////////////////////////////////
BaseFontTT::~BaseFontTT() {
clearCache();
for (int32 i = 0; i < _layers.getSize(); i++) {
delete _layers[i];
}
_layers.removeAll();
SAFE_DELETE_ARRAY(_fontFile);
SAFE_DELETE(_deletableFont);
_font = nullptr;
}
//////////////////////////////////////////////////////////////////////////
void BaseFontTT::clearCache() {
for (int i = 0; i < NUM_CACHED_TEXTS; i++) {
if (_cachedTexts[i]) {
delete _cachedTexts[i];
}
_cachedTexts[i] = nullptr;
}
}
//////////////////////////////////////////////////////////////////////////
void BaseFontTT::initLoop() {
}
//////////////////////////////////////////////////////////////////////////
int BaseFontTT::getTextWidth(const byte *text, int maxLength) {
WideString textStr;
if (_game->_textEncoding == TEXT_UTF8) {
textStr = StringUtil::utf8ToWide((const char *)text);
} else {
textStr = StringUtil::ansiToWide((const char *)text, _charset);
}
if (maxLength >= 0 && (int)textStr.size() > maxLength) {
textStr = textStr.substr(0, (uint32)maxLength);
}
//text = text.substr(0, MaxLength); // TODO: Remove
int textWidth, textHeight;
measureText(textStr, -1, -1, textWidth, textHeight);
return textWidth;
}
//////////////////////////////////////////////////////////////////////////
int BaseFontTT::getTextHeight(const byte *text, int width) {
WideString textStr;
if (_game->_textEncoding == TEXT_UTF8) {
textStr = StringUtil::utf8ToWide((const char *)text);
} else {
textStr = StringUtil::ansiToWide((const char *)text, _charset);
}
int textWidth, textHeight;
measureText(textStr, width, -1, textWidth, textHeight);
return textHeight;
}
//////////////////////////////////////////////////////////////////////////
void BaseFontTT::drawText(const byte *text, int x, int y, int width, TTextAlign align, int maxHeight, int maxLength) {
if (text == nullptr || strcmp((const char *)text, "") == 0) {
return;
}
WideString textStr;
// TODO: Why do we still insist on Widestrings everywhere?
// HACK: J.U.L.I.A. uses CP1252, we need to fix that,
// And we still don't have any UTF8-support.
if (_game->_textEncoding == TEXT_UTF8) {
textStr = StringUtil::utf8ToWide((const char *)text);
} else {
textStr = StringUtil::ansiToWide((const char *)text, _charset);
}
if (maxLength >= 0 && textStr.size() > (uint32)maxLength) {
textStr = textStr.substr(0, (uint32)maxLength);
}
//text = text.substr(0, MaxLength); // TODO: Remove
BaseRenderer *renderer = _game->_renderer;
// find cached surface, if exists
uint32 minUseTime = INT_MAX_VALUE;
int minIndex = -1;
BaseSurface *surface = nullptr;
int textOffset = 0;
for (int i = 0; i < NUM_CACHED_TEXTS; i++) {
if (_cachedTexts[i] == nullptr) {
minUseTime = 0;
minIndex = i;
} else {
if (_cachedTexts[i]->_text == textStr && _cachedTexts[i]->_align == align && _cachedTexts[i]->_width == width && _cachedTexts[i]->_maxHeight == maxHeight && _cachedTexts[i]->_maxLength == maxLength) {
surface = _cachedTexts[i]->_surface;
textOffset = _cachedTexts[i]->_textOffset;
_cachedTexts[i]->_marked = true;
_cachedTexts[i]->_lastUsed = BasePlatform::getTime();
break;
} else {
if (_cachedTexts[i]->_lastUsed < minUseTime) {
minUseTime = _cachedTexts[i]->_lastUsed;
minIndex = i;
}
}
}
}
// not found, create one
if (!surface) {
debugC(kWintermuteDebugFont, "Draw text: %s", text);
surface = renderTextToTexture(textStr, width, align, maxHeight, textOffset);
if (surface) {
// write surface to cache
if (_cachedTexts[minIndex] != nullptr) {
delete _cachedTexts[minIndex];
}
_cachedTexts[minIndex] = new BaseCachedTTFontText;
_cachedTexts[minIndex]->_surface = surface;
_cachedTexts[minIndex]->_align = align;
_cachedTexts[minIndex]->_width = width;
_cachedTexts[minIndex]->_maxHeight = maxHeight;
_cachedTexts[minIndex]->_maxLength = maxLength;
_cachedTexts[minIndex]->_text = textStr;
_cachedTexts[minIndex]->_textOffset = textOffset;
_cachedTexts[minIndex]->_marked = true;
_cachedTexts[minIndex]->_lastUsed = BasePlatform::getTime();
}
}
// and paint it
if (surface) {
Common::Rect32 rc;
BasePlatform::setRect(&rc, 0, 0, surface->getWidth(), surface->getHeight());
for (int32 i = 0; i < _layers.getSize(); i++) {
uint32 color = _layers[i]->_color;
uint32 origForceAlpha = renderer->_forceAlphaColor;
if (renderer->_forceAlphaColor != 0) {
color = BYTETORGBA(RGBCOLGetR(color), RGBCOLGetG(color), RGBCOLGetB(color), RGBCOLGetA(renderer->_forceAlphaColor));
renderer->_forceAlphaColor = 0;
}
surface->displayTrans(x, y - textOffset, rc, color, Graphics::BLEND_NORMAL, false, false, _layers[i]->_offsetX, _layers[i]->_offsetY);
renderer->_forceAlphaColor = origForceAlpha;
}
}
}
//////////////////////////////////////////////////////////////////////////
BaseSurface *BaseFontTT::renderTextToTexture(const WideString &text, int width, TTextAlign align, int maxHeight, int &textOffset) {
//TextLineList lines;
// TODO: Use WideString-conversion here.
//WrapText(text, width, maxHeight, lines);
Common::Array<WideString> lines;
_font->wordWrapText(text, width, lines);
while (maxHeight > 0 && lines.size() * _lineHeight > maxHeight) {
lines.pop_back();
}
if (lines.size() == 0) {
return nullptr;
}
Graphics::TextAlign alignment = Graphics::kTextAlignInvalid;
if (align == TAL_LEFT) {
alignment = Graphics::kTextAlignLeft;
} else if (align == TAL_CENTER) {
alignment = Graphics::kTextAlignCenter;
} else if (align == TAL_RIGHT) {
alignment = Graphics::kTextAlignRight;
}
// TODO: This debug call does not work with WideString because text.c_str() returns an uint32 array.
//debugC(kWintermuteDebugFont, "%s %d %d %d %d", text.c_str(), RGBCOLGetR(_layers[0]->_color), RGBCOLGetG(_layers[0]->_color), RGBCOLGetB(_layers[0]->_color), RGBCOLGetA(_layers[0]->_color));
// void drawAlphaString(Surface *dst, const Common::String &str, int x, int y, int w, uint32 color, TextAlign align = kTextAlignLeft, int deltax = 0, bool useEllipsis = true) const;
Graphics::Surface *surface = new Graphics::Surface();
surface->create((uint16)width, (uint16)(_lineHeight * lines.size()), _game->_renderer->getPixelFormat());
uint32 useColor = 0xffffffff;
Common::Array<WideString>::iterator it;
int heightOffset = 0;
for (it = lines.begin(); it != lines.end(); ++it) {
WideString str;
if (_game->_textRTL) {
str = Common::convertBiDiU32String(*it, Common::BIDI_PAR_RTL);
} else {
str = Common::convertBiDiU32String(*it, Common::BIDI_PAR_LTR);
}
_font->drawAlphaString(surface, str, 0, heightOffset, width, useColor, alignment);
heightOffset += (int)_lineHeight;
}
BaseSurface *retSurface = _game->_renderer->createSurface();
retSurface->create(surface->w, surface->h);
retSurface->putSurface(*surface, true);
surface->free();
delete surface;
return retSurface;
// TODO: _isUnderline, _isBold, _isItalic, _isStriked
}
//////////////////////////////////////////////////////////////////////////
int BaseFontTT::getLetterHeight() {
return (int)_lineHeight;
}
//////////////////////////////////////////////////////////////////////
bool BaseFontTT::loadFile(const char *filename) {
char *buffer = (char *)_game->_fileManager->readWholeFile(filename);
if (buffer == nullptr) {
_game->LOG(0, "BaseFontTT::loadFile failed for file '%s'", filename);
return STATUS_FAILED;
}
bool ret;
setFilename(filename);
if (DID_FAIL(ret = loadBuffer(buffer))) {
_game->LOG(0, "Error parsing TTFONT file '%s'", filename);
}
delete[] buffer;
return ret;
}
TOKEN_DEF_START
TOKEN_DEF(TTFONT)
TOKEN_DEF(SIZE)
TOKEN_DEF(FACE)
TOKEN_DEF(FILENAME)
TOKEN_DEF(BOLD)
TOKEN_DEF(ITALIC)
TOKEN_DEF(UNDERLINE)
TOKEN_DEF(STRIKE)
TOKEN_DEF(CHARSET)
TOKEN_DEF(COLOR)
TOKEN_DEF(ALPHA)
TOKEN_DEF(LAYER)
TOKEN_DEF(OFFSET_X)
TOKEN_DEF(OFFSET_Y)
TOKEN_DEF_END
//////////////////////////////////////////////////////////////////////
bool BaseFontTT::loadBuffer(char *buffer) {
TOKEN_TABLE_START(commands)
TOKEN_TABLE(TTFONT)
TOKEN_TABLE(SIZE)
TOKEN_TABLE(FACE)
TOKEN_TABLE(FILENAME)
TOKEN_TABLE(BOLD)
TOKEN_TABLE(ITALIC)
TOKEN_TABLE(UNDERLINE)
TOKEN_TABLE(STRIKE)
TOKEN_TABLE(CHARSET)
TOKEN_TABLE(COLOR)
TOKEN_TABLE(ALPHA)
TOKEN_TABLE(LAYER)
TOKEN_TABLE_END
char *params;
int cmd;
BaseParser parser(_game);
if (parser.getCommand(&buffer, commands, &params) != TOKEN_TTFONT) {
_game->LOG(0, "'TTFONT' keyword expected.");
return STATUS_FAILED;
}
buffer = params;
uint32 baseColor = 0x00000000;
while ((cmd = parser.getCommand(&buffer, commands, &params)) > 0) {
switch (cmd) {
case TOKEN_SIZE:
parser.scanStr(params, "%d", &_fontHeight);
break;
case TOKEN_FACE:
// we don't need this anymore
break;
case TOKEN_FILENAME:
BaseUtils::setString(&_fontFile, params);
break;
case TOKEN_BOLD:
parser.scanStr(params, "%b", &_isBold);
break;
case TOKEN_ITALIC:
parser.scanStr(params, "%b", &_isItalic);
break;
case TOKEN_UNDERLINE:
parser.scanStr(params, "%b", &_isUnderline);
break;
case TOKEN_STRIKE:
parser.scanStr(params, "%b", &_isStriked);
break;
case TOKEN_CHARSET:
parser.scanStr(params, "%d", &_charset);
break;
case TOKEN_COLOR: {
int r, g, b;
parser.scanStr(params, "%d,%d,%d", &r, &g, &b);
baseColor = BYTETORGBA(r, g, b, RGBCOLGetA(baseColor));
}
break;
case TOKEN_ALPHA: {
int a;
parser.scanStr(params, "%d", &a);
baseColor = BYTETORGBA(RGBCOLGetR(baseColor), RGBCOLGetG(baseColor), RGBCOLGetB(baseColor), a);
}
break;
case TOKEN_LAYER: {
BaseTTFontLayer *layer = new BaseTTFontLayer;
if (layer && DID_SUCCEED(parseLayer(layer, params))) {
_layers.add(layer);
} else {
SAFE_DELETE(layer);
cmd = PARSERR_TOKENNOTFOUND;
}
}
break;
default:
break;
}
}
if (cmd == PARSERR_TOKENNOTFOUND) {
_game->LOG(0, "Syntax error in TTFONT definition");
return STATUS_FAILED;
}
// create at least one layer
if (_layers.getSize() == 0) {
BaseTTFontLayer *layer = new BaseTTFontLayer;
layer->_color = baseColor;
_layers.add(layer);
}
if (!_fontFile) {
BaseUtils::setString(&_fontFile, "arial.ttf");
}
return initFont();
}
//////////////////////////////////////////////////////////////////////////
bool BaseFontTT::parseLayer(BaseTTFontLayer *layer, char *buffer) {
TOKEN_TABLE_START(commands)
TOKEN_TABLE(OFFSET_X)
TOKEN_TABLE(OFFSET_Y)
TOKEN_TABLE(COLOR)
TOKEN_TABLE(ALPHA)
TOKEN_TABLE_END
char *params;
int cmd;
BaseParser parser(_game);
while ((cmd = parser.getCommand(&buffer, commands, &params)) > 0) {
switch (cmd) {
case TOKEN_OFFSET_X:
parser.scanStr(params, "%d", &layer->_offsetX);
break;
case TOKEN_OFFSET_Y:
parser.scanStr(params, "%d", &layer->_offsetY);
break;
case TOKEN_COLOR: {
int r, g, b;
parser.scanStr(params, "%d,%d,%d", &r, &g, &b);
layer->_color = BYTETORGBA(r, g, b, RGBCOLGetA(layer->_color));
}
break;
case TOKEN_ALPHA: {
int a;
parser.scanStr(params, "%d", &a);
layer->_color = BYTETORGBA(RGBCOLGetR(layer->_color), RGBCOLGetG(layer->_color), RGBCOLGetB(layer->_color), a);
}
break;
default:
break;
}
}
if (cmd != PARSERR_EOF) {
return STATUS_FAILED;
} else {
return STATUS_OK;
}
}
//////////////////////////////////////////////////////////////////////////
bool BaseFontTT::persist(BasePersistenceManager *persistMgr) {
BaseFont::persist(persistMgr);
persistMgr->transferBool(TMEMBER(_isBold));
persistMgr->transferBool(TMEMBER(_isItalic));
persistMgr->transferBool(TMEMBER(_isUnderline));
persistMgr->transferBool(TMEMBER(_isStriked));
persistMgr->transferSint32(TMEMBER(_fontHeight));
persistMgr->transferCharPtr(TMEMBER(_fontFile));
persistMgr->transferSint32(TMEMBER_INT(_charset));
// persist layers
int32 numLayers;
if (persistMgr->getIsSaving()) {
numLayers = _layers.getSize();
persistMgr->transferSint32(TMEMBER(numLayers));
for (int i = 0; i < numLayers; i++) {
_layers[i]->persist(persistMgr);
}
} else {
numLayers = _layers.getSize();
persistMgr->transferSint32(TMEMBER(numLayers));
for (int i = 0; i < numLayers; i++) {
BaseTTFontLayer *layer = new BaseTTFontLayer;
layer->persist(persistMgr);
_layers.add(layer);
}
}
if (!persistMgr->getIsSaving()) {
for (int i = 0; i < NUM_CACHED_TEXTS; i++) {
_cachedTexts[i] = nullptr;
}
}
// initialise to defaults
if (!persistMgr->getIsSaving()) {
_fallbackFont = _font = _deletableFont = nullptr;
_lineHeight = 0;
_maxCharWidth = _maxCharHeight = 0;
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
void BaseFontTT::afterLoad() {
initFont();
}
//////////////////////////////////////////////////////////////////////////
bool BaseFontTT::initFont() {
if (!_fontFile) {
return STATUS_FAILED;
}
#ifdef USE_FREETYPE2
const char *fallbackFilename;
// Handle Bold atleast for the fallback-case.
// TODO: Handle italic. (Needs a test-case)
if (_isBold) {
fallbackFilename = "LiberationSans-Bold.ttf";
} else {
fallbackFilename = "LiberationSans-Regular.ttf";
}
// Load a file, but avoid having the File-manager handle the disposal of it.
Common::SeekableReadStream *file = BaseFileManager::getEngineInstance()->openFile(_fontFile, true, false);
if (!file) {
if (Common::String(_fontFile) != "arial.ttf") {
warning("%s has no replacement font yet, using %s for now (if available)", _fontFile, fallbackFilename);
}
// Fallback1: Try to find the LiberationSans font
file = SearchMan.createReadStreamForMember(fallbackFilename);
}
if (file) {
_deletableFont = Graphics::loadTTFFont(file, DisposeAfterUse::YES, _fontHeight, Graphics::kTTFSizeModeCharacter, 96); // Use the same dpi as WME (96 vs 72).
_font = _deletableFont;
}
// Fallback2: Try load the font from the common fonts archive:
if (!_font) {
_deletableFont = Graphics::loadTTFFontFromArchive(fallbackFilename, _fontHeight, Graphics::kTTFSizeModeCharacter, 96); // Use the same dpi as WME (96 vs 72).
_font = _deletableFont;
}
#else
warning("BaseFontTT::InitFont - FreeType2-support not compiled in, TTF-fonts will not be loaded");
#endif // USE_FREETYPE2
// Fallback3: Just use the Big GUI-font. (REALLY undesirable)
if (!_font) {
_font = _fallbackFont = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont);
warning("BaseFontTT::InitFont - Couldn't load font: %s", _fontFile);
}
_lineHeight = _font->getFontHeight();
#ifdef ENABLE_FOXTAIL
if (BaseEngine::instance().isFoxTail(FOXTAIL_1_2_896, FOXTAIL_LATEST_VERSION)) {
_lineHeight -= 1;
}
#endif
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
void BaseFontTT::measureText(const WideString &text, int maxWidth, int maxHeight, int &textWidth, int &textHeight) {
//TextLineList lines;
if (maxWidth >= 0) {
Common::Array<WideString> lines;
_font->wordWrapText(text, maxWidth, lines);
Common::Array<WideString>::iterator it;
textWidth = 0;
for (it = lines.begin(); it != lines.end(); ++it) {
if (!it)
continue;
textWidth = MAX(textWidth, _font->getStringWidth(*it));
}
//WrapText(text, maxWidth, maxHeight, lines);
textHeight = (int)(lines.size() * _lineHeight);
} else {
textWidth = _font->getStringWidth(text);
textHeight = _fontHeight;
}
/*
TextLineList::iterator it;
for (it = lines.begin(); it != lines.end(); ++it) {
TextLine *line = (*it);
textWidth = MAX(textWidth, line->GetWidth());
delete line;
line = nullptr;
}*/
}
} // End of namespace Wintermute

View File

@@ -0,0 +1,148 @@
/* 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/>.
*
*/
/*
* This file is based on WME Lite.
* http://dead-code.org/redir.php?target=wmelite
* Copyright (c) 2011 Jan Nedoma
*/
#ifndef WINTERMUTE_BASE_FONTTT_H
#define WINTERMUTE_BASE_FONTTT_H
#include "engines/wintermute/base/font/base_font_storage.h"
#include "engines/wintermute/base/font/base_font.h"
#include "engines/wintermute/base/gfx/base_surface.h"
#include "common/rect.h"
#include "graphics/surface.h"
#include "graphics/font.h"
#define NUM_CACHED_TEXTS 30
namespace Wintermute {
class BaseFontTT : public BaseFont {
private:
//////////////////////////////////////////////////////////////////////////
class BaseCachedTTFontText {
public:
WideString _text;
int32 _width;
TTextAlign _align;
int32 _maxHeight;
int32 _maxLength;
BaseSurface *_surface;
//int32 _priority;
int32 _textOffset;
bool _marked;
uint32 _lastUsed;
BaseCachedTTFontText() : _text() {
//_text = L"";
_width = _maxHeight = _maxLength = -1;
_align = TAL_LEFT;
_surface = nullptr;
//_priority = -1;
_textOffset = 0;
_lastUsed = 0;
_marked = false;
}
virtual ~BaseCachedTTFontText() {
if (_surface) {
delete _surface;
}
}
};
public:
//////////////////////////////////////////////////////////////////////////
class BaseTTFontLayer {
public:
BaseTTFontLayer() {
_offsetX = _offsetY = 0;
_color = 0x00000000;
}
bool persist(BasePersistenceManager *persistMgr) {
persistMgr->transferSint32(TMEMBER(_offsetX));
persistMgr->transferSint32(TMEMBER(_offsetY));
persistMgr->transferUint32(TMEMBER(_color));
return STATUS_OK;
}
int32 _offsetX;
int32 _offsetY;
uint32 _color;
};
public:
DECLARE_PERSISTENT(BaseFontTT, BaseFont)
BaseFontTT(BaseGame *inGame);
~BaseFontTT() override;
int getTextWidth(const byte *text, int maxLength = -1) override;
int getTextHeight(const byte *text, int width) override;
void drawText(const byte *text, int x, int y, int width, TTextAlign align = TAL_LEFT, int max_height = -1, int maxLength = -1) override;
int getLetterHeight() override;
bool loadBuffer(char *buffer);
bool loadFile(const char *filename);
void afterLoad() override;
void initLoop() override;
private:
bool parseLayer(BaseTTFontLayer *layer, char *buffer);
void measureText(const WideString &text, int maxWidth, int maxHeight, int &textWidth, int &textHeight);
BaseSurface *renderTextToTexture(const WideString &text, int width, TTextAlign align, int maxHeight, int &textOffset);
BaseCachedTTFontText *_cachedTexts[NUM_CACHED_TEXTS];
bool initFont();
Graphics::Font *_deletableFont;
const Graphics::Font *_font;
const Graphics::Font *_fallbackFont;
float _lineHeight;
size_t _maxCharWidth;
size_t _maxCharHeight;
private:
bool _isBold;
bool _isItalic;
bool _isUnderline;
bool _isStriked;
int32 _fontHeight;
char *_fontFile;
TTextCharset _charset;
BaseArray<BaseTTFontLayer *> _layers;
void clearCache();
};
} // End of namespace Wintermute
#endif