Initial commit
This commit is contained in:
476
engines/bagel/spacebar/boflib/gfx/text.cpp
Normal file
476
engines/bagel/spacebar/boflib/gfx/text.cpp
Normal file
@@ -0,0 +1,476 @@
|
||||
/* 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 "graphics/fonts/ttf.h"
|
||||
#include "bagel/spacebar/boflib/app.h"
|
||||
#include "bagel/spacebar/boflib/gfx/text.h"
|
||||
|
||||
namespace Bagel {
|
||||
namespace SpaceBar {
|
||||
|
||||
#define START_SIZE 8
|
||||
#define MONO_FONT "LiberationMono-Regular.ttf"
|
||||
#define SERIF_FONT_REGULAR "LiberationSans-Regular.ttf"
|
||||
#define SERIF_FONT_BOLD "LiberationSans-Bold.ttf"
|
||||
#define TAB_SIZE 50
|
||||
|
||||
int CBofText::_tabStop;
|
||||
bool CBofText::_initialized;
|
||||
Graphics::Font *CBofText::_defaultFonts[NUM_POINT_SIZES];
|
||||
Graphics::Font *CBofText::_fixedFonts[NUM_POINT_SIZES];
|
||||
|
||||
|
||||
ErrorCode CBofText::initialize() {
|
||||
_initialized = true;
|
||||
_tabStop = 20; // tabstops every 20 pixels
|
||||
Common::fill(_defaultFonts, _defaultFonts + NUM_POINT_SIZES,
|
||||
(Graphics::Font *)nullptr);
|
||||
Common::fill(_fixedFonts, _fixedFonts + NUM_POINT_SIZES,
|
||||
(Graphics::Font *)nullptr);
|
||||
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
ErrorCode CBofText::shutdown() {
|
||||
for (int i = 0; i < NUM_POINT_SIZES; i++) {
|
||||
delete _defaultFonts[i];
|
||||
delete _fixedFonts[i];
|
||||
}
|
||||
|
||||
_initialized = false;
|
||||
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
CBofText::CBofText() {
|
||||
initializeFields(); // Initialize stuff
|
||||
}
|
||||
|
||||
CBofText::CBofText(const CBofRect *pRect, int nJustify, uint32 nFormatFlags) {
|
||||
// Can't access null pointers
|
||||
assert(pRect != nullptr);
|
||||
|
||||
// Initialize stuff
|
||||
initializeFields();
|
||||
|
||||
// Build the work areas
|
||||
setupText(pRect, nJustify, nFormatFlags);
|
||||
}
|
||||
|
||||
CBofText::~CBofText() {
|
||||
delete _pWork;
|
||||
_pWork = nullptr;
|
||||
|
||||
delete _pBackground;
|
||||
_pBackground = nullptr;
|
||||
}
|
||||
|
||||
void CBofText::initializeFields() {
|
||||
_pBackground = nullptr;
|
||||
_pWork = nullptr;
|
||||
_bSaved = false;
|
||||
|
||||
_cPosition = CBofPoint(0, 0);
|
||||
_cSize = CBofSize(0, 0);
|
||||
_cRect.setRect(0, 0, 0, 0);
|
||||
|
||||
_cShadowColor = RGB(0, 0, 0);
|
||||
_nShadow_DX = 0;
|
||||
_nShadow_DY = 0;
|
||||
|
||||
_nJustify = JUSTIFY_LEFT;
|
||||
|
||||
_nFormatFlags = FORMAT_DEFAULT;
|
||||
_bMultiLine = false;
|
||||
|
||||
_nCurSize = 10;
|
||||
_nCurWeight = TEXT_DONTCARE;
|
||||
_cTextColor = CTEXT_COLOR;
|
||||
}
|
||||
|
||||
ErrorCode CBofText::setupText(const CBofRect *pRect, int nJustify, uint32 nFormatFlags) {
|
||||
// Can't access null pointers
|
||||
assert(pRect != nullptr);
|
||||
|
||||
_nJustify = nJustify;
|
||||
|
||||
// Setup the fields for location and size of the text area
|
||||
_cRect = *pRect;
|
||||
_cSize.cx = _cRect.width();
|
||||
_cSize.cy = _cRect.height();
|
||||
|
||||
delete _pWork;
|
||||
_pWork = nullptr;
|
||||
|
||||
delete _pBackground;
|
||||
_pBackground = nullptr;
|
||||
|
||||
CBofPalette *pPalette = CBofApp::getApp()->getPalette();
|
||||
|
||||
// Create a bitmap to serve as our work area as we output text
|
||||
_pWork = new CBofBitmap(_cSize.cx, _cSize.cy, pPalette);
|
||||
|
||||
// Create a bitmap to hold the background we overwrite
|
||||
_pBackground = new CBofBitmap(_cSize.cx, _cSize.cy, pPalette);
|
||||
|
||||
return _errCode;
|
||||
}
|
||||
|
||||
ErrorCode CBofText::setupTextOpt(const CBofRect *pRect, int nJustify, uint32 nFormatFlags) {
|
||||
// Can't access null pointers
|
||||
assert(pRect != nullptr);
|
||||
|
||||
_nJustify = nJustify;
|
||||
_nFormatFlags = nFormatFlags;
|
||||
|
||||
// Setup the fields for location and size of the text area
|
||||
_cRect = *pRect;
|
||||
_cSize.cx = _cRect.width();
|
||||
_cSize.cy = _cRect.height();
|
||||
|
||||
return _errCode;
|
||||
}
|
||||
|
||||
ErrorCode CBofText::erase(CBofWindow *pWnd) {
|
||||
// Can't access null pointers
|
||||
assert(pWnd != nullptr);
|
||||
|
||||
if (_pBackground != nullptr && _bSaved) {
|
||||
// Simply splat the background art back where it came from
|
||||
_errCode = _pBackground->paint(pWnd, &_cRect);
|
||||
}
|
||||
|
||||
return _errCode;
|
||||
}
|
||||
|
||||
ErrorCode CBofText::erase(CBofBitmap *pBmp) {
|
||||
// Can't access null pointers
|
||||
assert(pBmp != nullptr);
|
||||
|
||||
if (_pBackground != nullptr && _bSaved) {
|
||||
// Simply splat the background art back where it came from
|
||||
_errCode = _pBackground->paint(pBmp, &_cRect);
|
||||
}
|
||||
|
||||
return _errCode;
|
||||
}
|
||||
|
||||
ErrorCode CBofText::display(CBofWindow *pWnd, const char *pszText, const int nSize, const int nWeight, const COLORREF cColor, int nFont) {
|
||||
assert(isValidObject(this));
|
||||
|
||||
// Can't access null pointers
|
||||
assert(pWnd != nullptr);
|
||||
|
||||
_cTextColor = cColor;
|
||||
|
||||
return displayText(pWnd, pszText, &_cRect, nSize, nWeight, false, nFont);
|
||||
}
|
||||
|
||||
ErrorCode CBofText::display(CBofWindow *pWnd) {
|
||||
assert(isValidObject(this));
|
||||
assert(pWnd != nullptr);
|
||||
|
||||
return display(pWnd, _cCurString, _nCurSize, _nCurWeight, _cTextColor);
|
||||
}
|
||||
|
||||
ErrorCode CBofText::display(CBofBitmap *pBmp) {
|
||||
assert(isValidObject(this));
|
||||
assert(pBmp != nullptr);
|
||||
|
||||
return display(pBmp, _cCurString, _nCurSize, _nCurWeight, _cTextColor);
|
||||
}
|
||||
|
||||
ErrorCode CBofText::display(CBofBitmap *pBmp, const char *pszText, const int nSize, const int nWeight, const COLORREF cColor, int nFont) {
|
||||
// Can't access null pointers
|
||||
assert(pBmp != nullptr);
|
||||
|
||||
_cTextColor = cColor;
|
||||
|
||||
return displayText(pBmp, pszText, &_cRect, nSize, nWeight, false, nFont);
|
||||
}
|
||||
|
||||
ErrorCode CBofText::displayShadowed(CBofWindow *pWnd, const char *pszText, const int nSize, const int nWeight, const COLORREF cColor, const COLORREF cShadow, const int nDX, const int nDY, int nFont) {
|
||||
// Can't access null pointers
|
||||
assert(pWnd != nullptr);
|
||||
|
||||
_cTextColor = cColor;
|
||||
_cShadowColor = cShadow;
|
||||
_nShadow_DX = nDX;
|
||||
_nShadow_DY = nDY;
|
||||
|
||||
return displayText(pWnd, pszText, &_cRect, nSize, nWeight, true, nFont);
|
||||
}
|
||||
|
||||
ErrorCode CBofText::displayText(CBofWindow *pWnd, const char *pszText, CBofRect *pRect, const int nSize, const int nWeight, const bool bShadowed, int nFont) {
|
||||
assert(isValidObject(this));
|
||||
assert(pWnd != nullptr);
|
||||
assert(pszText != nullptr);
|
||||
assert(pRect != nullptr);
|
||||
|
||||
CBofRect cRect(0, 0, pRect->width() - 1, pRect->height() - 1);
|
||||
|
||||
assert(_pBackground != nullptr);
|
||||
assert(_pWork != nullptr);
|
||||
|
||||
if (!_bSaved) {
|
||||
CBofBitmap::setUseBackdrop(true);
|
||||
_pBackground->captureScreen(pWnd, pRect);
|
||||
CBofBitmap::setUseBackdrop(false);
|
||||
_bSaved = true;
|
||||
}
|
||||
|
||||
_pBackground->paint(_pWork, 0, 0);
|
||||
|
||||
displayTextEx(_pWork, pszText, &cRect, nSize, nWeight, bShadowed, nFont);
|
||||
|
||||
_pWork->paint(pWnd, pRect);
|
||||
|
||||
return _errCode;
|
||||
}
|
||||
|
||||
ErrorCode CBofText::displayText(CBofBitmap *pBmp, const char *pszText, CBofRect *pRect, const int nSize, const int nWeight, const bool bShadowed, int nFont) {
|
||||
assert(isValidObject(this));
|
||||
assert(pBmp != nullptr);
|
||||
assert(pszText != nullptr);
|
||||
assert(pRect != nullptr);
|
||||
|
||||
CBofRect cRect(0, 0, pRect->width() - 1, pRect->height() - 1);
|
||||
|
||||
assert(_pWork != nullptr);
|
||||
|
||||
assert(_pBackground != nullptr);
|
||||
|
||||
if (!_bSaved) {
|
||||
CBofRect r = _pBackground->getRect();
|
||||
pBmp->paint(_pBackground, &r, pRect);
|
||||
_bSaved = true;
|
||||
}
|
||||
|
||||
_pBackground->paint(_pWork, 0, 0);
|
||||
|
||||
displayTextEx(_pWork, pszText, &cRect, nSize, nWeight, bShadowed, nFont);
|
||||
|
||||
_pWork->paint(pBmp, pRect);
|
||||
|
||||
return _errCode;
|
||||
}
|
||||
|
||||
Graphics::Font *CBofText::getFont(int nFont, int nSize, int nWeight) {
|
||||
Graphics::Font *font;
|
||||
|
||||
// Attempt to use one of the fonts that we pre-allocated
|
||||
if (nFont != FONT_MONO) {
|
||||
font = _defaultFonts[nSize - START_SIZE];
|
||||
} else {
|
||||
font = _fixedFonts[nSize - START_SIZE];
|
||||
}
|
||||
|
||||
// Last resort - create the font now
|
||||
if (font == nullptr) {
|
||||
if (nFont != FONT_MONO) {
|
||||
font = Graphics::loadTTFFontFromArchive(SERIF_FONT_REGULAR, nSize, Graphics::kTTFSizeModeCell);
|
||||
_defaultFonts[nSize - START_SIZE] = font;
|
||||
} else {
|
||||
font = Graphics::loadTTFFontFromArchive(MONO_FONT, nSize, Graphics::kTTFSizeModeCell);
|
||||
_fixedFonts[nSize - START_SIZE] = font;
|
||||
}
|
||||
}
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
ErrorCode CBofText::displayTextEx(CBofBitmap *pBmp, const char *pszText, CBofRect *pRect, const int nSize, const int nWeight, const bool bShadowed, int nFont) {
|
||||
assert(isValidObject(this));
|
||||
|
||||
// can't access null pointers
|
||||
assert(pBmp != nullptr);
|
||||
assert(pszText != nullptr);
|
||||
assert(pRect != nullptr);
|
||||
|
||||
Graphics::ManagedSurface surface = pBmp->getSurface();
|
||||
Graphics::Font *font = getFont(nFont, nSize, nWeight);
|
||||
int color;
|
||||
|
||||
// Split lines
|
||||
Common::U32StringArray lines;
|
||||
font->wordWrapText(Common::U32String(pszText, Common::kWindows1252), pRect->width(), lines);
|
||||
|
||||
// Iterate the lines to get the maximum width
|
||||
int maxWidth = 0;
|
||||
for (uint i = 0; i < lines.size(); ++i)
|
||||
maxWidth = MAX(maxWidth, font->getStringWidth(lines[i]));
|
||||
Common::Point textInfo(maxWidth, (int)lines.size() * font->getFontHeight());
|
||||
|
||||
_cPosition.y = (_cSize.cy - textInfo.y) >> 1;
|
||||
|
||||
Graphics::TextAlign align = Graphics::kTextAlignLeft;
|
||||
switch (_nJustify) {
|
||||
case JUSTIFY_CENTER:
|
||||
_cPosition.x = (_cSize.cx - textInfo.x) >> 1;
|
||||
align = Graphics::kTextAlignCenter;
|
||||
break;
|
||||
|
||||
case JUSTIFY_LEFT:
|
||||
// align left
|
||||
_cPosition.x = 0;
|
||||
break;
|
||||
|
||||
case JUSTIFY_RIGHT:
|
||||
_cPosition.x = _cSize.cx - textInfo.x;
|
||||
align = Graphics::kTextAlignRight;
|
||||
break;
|
||||
|
||||
case JUSTIFY_WRAP:
|
||||
// Align left
|
||||
_bMultiLine = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// text starts relative to area for painting
|
||||
_cPosition += pRect->topLeft();
|
||||
|
||||
// Note: Under ScummVM, even single line drawing uses the multiLine code
|
||||
Common::Rect newRect = *pRect;
|
||||
|
||||
if ((_nFormatFlags & FORMAT_TOP_CENTER) == FORMAT_TOP_CENTER) {
|
||||
int h = lines.size() * font->getFontHeight();
|
||||
newRect.top = (newRect.top + newRect.bottom) / 2 - h / 2;
|
||||
newRect.bottom = newRect.top + h;
|
||||
}
|
||||
|
||||
Common::Rect shadowRect = newRect;
|
||||
shadowRect.translate(_nShadow_DX, _nShadow_DY);
|
||||
|
||||
for (uint i = 0; i < lines.size(); ++i) {
|
||||
const Common::U32String &line = lines[i];
|
||||
|
||||
if (bShadowed) {
|
||||
color = CBofApp::getApp()->getPalette()->getNearestIndex(_cShadowColor);
|
||||
displayLine(font, surface, line, shadowRect.left, shadowRect.top,
|
||||
shadowRect.width(), color, align);
|
||||
}
|
||||
|
||||
color = CBofApp::getApp()->getPalette()->getNearestIndex(_cTextColor);
|
||||
displayLine(font, surface, line, newRect.left, newRect.top,
|
||||
newRect.width(), color, align);
|
||||
|
||||
newRect.top += font->getFontHeight();
|
||||
shadowRect.top += font->getFontHeight();
|
||||
}
|
||||
|
||||
return _errCode;
|
||||
}
|
||||
|
||||
void CBofText::displayLine(Graphics::Font *font, Graphics::ManagedSurface &surface,
|
||||
const Common::U32String &line, int left, int top, int width, int color, Graphics::TextAlign align) {
|
||||
if (!line.contains('\t')) {
|
||||
font->drawString(&surface, line, left, top, width, color, align);
|
||||
|
||||
} else {
|
||||
// Special rendering of tabbed text
|
||||
Common::U32String str = line;
|
||||
|
||||
while (!str.empty()) {
|
||||
if (str[0] == '\t') {
|
||||
// Move to next tab stop
|
||||
left = (left + TAB_SIZE) / TAB_SIZE * TAB_SIZE;
|
||||
str.deleteChar(0);
|
||||
|
||||
} else {
|
||||
Common::U32String fragment;
|
||||
size_t tab = str.findFirstOf('\t');
|
||||
if (tab == Common::U32String::npos) {
|
||||
fragment = str;
|
||||
str.clear();
|
||||
} else {
|
||||
fragment = Common::U32String(str.c_str(), str.c_str() + tab);
|
||||
str = Common::U32String(str.c_str() + tab);
|
||||
}
|
||||
|
||||
int fragmentWidth = font->getStringWidth(fragment);
|
||||
font->drawString(&surface, fragment, left, top, width, color, align);
|
||||
|
||||
left += fragmentWidth;
|
||||
width -= fragmentWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ErrorCode paintText(CBofWindow *pWnd, CBofRect *pRect, const char *pszString, const int nSize, const int nWeight, const COLORREF cColor, int nJustify, uint32 nFormatFlags, int nFont) {
|
||||
assert(pWnd != nullptr);
|
||||
assert(pRect != nullptr);
|
||||
|
||||
CBofText cText(pRect, nJustify, nFormatFlags);
|
||||
return cText.display(pWnd, pszString, nSize, nWeight, cColor, nFont);
|
||||
}
|
||||
|
||||
ErrorCode paintText(CBofBitmap *pBmp, CBofRect *pRect, const char *pszString, const int nSize, const int nWeight, const COLORREF cColor, int nJustify, uint32 nFormatFlags, int nFont) {
|
||||
assert(pBmp != nullptr);
|
||||
assert(pRect != nullptr);
|
||||
|
||||
CBofText cText;
|
||||
cText.setupTextOpt(pRect, nJustify, nFormatFlags);
|
||||
|
||||
cText.setColor(cColor);
|
||||
|
||||
return cText.displayTextEx(pBmp, pszString, pRect, nSize, nWeight, false, nFont);
|
||||
}
|
||||
|
||||
ErrorCode paintShadowedText(CBofBitmap *pBmp, CBofRect *pRect, const char *pszString, const int nSize, const int nWeight, const COLORREF cColor, int nJustify, uint32 nFormatFlags, int nFont) {
|
||||
assert(pBmp != nullptr);
|
||||
assert(pRect != nullptr);
|
||||
|
||||
CBofText cText;
|
||||
cText.setupTextOpt(pRect, nJustify, nFormatFlags);
|
||||
|
||||
cText.setColor(cColor);
|
||||
cText.setShadowColor(CTEXT_SHADOW_COLOR);
|
||||
cText.setShadowSize(CTEXT_SHADOW_DX, CTEXT_SHADOW_DY);
|
||||
|
||||
return cText.displayTextEx(pBmp, pszString, pRect, nSize, nWeight, true, nFont);
|
||||
}
|
||||
|
||||
CBofRect calculateTextRect(CBofWindow *pWnd, const CBofString *pStr, int nSize, int nFont) {
|
||||
return calculateTextRect(pWnd->getRect(), pStr, nSize, nFont);
|
||||
}
|
||||
|
||||
CBofRect calculateTextRect(CBofRect rect, const CBofString *pStr, int nSize, int nFont) {
|
||||
// Get the font to use
|
||||
Graphics::Font *font = CBofText::getFont(nFont, nSize, TEXT_NORMAL);
|
||||
|
||||
// Wrap the text as necessary
|
||||
Common::U32StringArray lines;
|
||||
font->wordWrapText(Common::U32String(pStr->getBuffer(), Common::kWindows1252), rect.width(), lines);
|
||||
|
||||
// Iterate the lines to get the maximum width
|
||||
int maxWidth = 0;
|
||||
for (uint i = 0; i < lines.size(); ++i)
|
||||
maxWidth = MAX(maxWidth, font->getStringWidth(lines[i]));
|
||||
|
||||
return CBofRect(0, 0, maxWidth, (int)lines.size() * font->getFontHeight());
|
||||
}
|
||||
|
||||
} // namespace SpaceBar
|
||||
} // namespace Bagel
|
||||
Reference in New Issue
Block a user