Initial commit
This commit is contained in:
341
engines/titanic/support/font.cpp
Normal file
341
engines/titanic/support/font.cpp
Normal file
@@ -0,0 +1,341 @@
|
||||
/* 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/textconsole.h"
|
||||
#include "titanic/support/font.h"
|
||||
#include "titanic/support/files_manager.h"
|
||||
#include "titanic/titanic.h"
|
||||
|
||||
namespace Titanic {
|
||||
|
||||
STFont::STFont() {
|
||||
_dataPtr = nullptr;
|
||||
_dataSize = 0;
|
||||
_fontHeight = 0;
|
||||
_dataWidth = 0;
|
||||
_fontR = _fontG = _fontB = 0;
|
||||
}
|
||||
|
||||
STFont::~STFont() {
|
||||
delete[] _dataPtr;
|
||||
}
|
||||
|
||||
void STFont::load(int fontNumber) {
|
||||
assert(!_dataPtr);
|
||||
Common::SeekableReadStream *stream = g_vm->_filesManager->getResource(
|
||||
CString::format("STFONT/%d", fontNumber));
|
||||
if (!stream)
|
||||
error("Could not locate the specified font");
|
||||
|
||||
_fontHeight = stream->readUint32LE();
|
||||
_dataWidth = stream->readUint32LE();
|
||||
for (uint idx = 0; idx < 256; ++idx)
|
||||
_chars[idx]._width = stream->readUint32LE();
|
||||
for (uint idx = 0; idx < 256; ++idx)
|
||||
_chars[idx]._offset = stream->readUint32LE();
|
||||
|
||||
_dataSize = stream->readUint32LE();
|
||||
_dataPtr = new byte[_dataSize];
|
||||
stream->read(_dataPtr, _dataSize);
|
||||
|
||||
delete stream;
|
||||
}
|
||||
|
||||
void STFont::setColor(byte r, byte g, byte b) {
|
||||
_fontR = r;
|
||||
_fontG = g;
|
||||
_fontB = b;
|
||||
}
|
||||
|
||||
uint16 STFont::getColor() const {
|
||||
return g_system->getScreenFormat().RGBToColor(_fontR, _fontG, _fontB);
|
||||
}
|
||||
|
||||
int STFont::getTextBounds(const CString &str, int maxWidth, Point *sizeOut) const {
|
||||
Point textSize;
|
||||
|
||||
// Reset output dimensions if provided
|
||||
if (sizeOut)
|
||||
*sizeOut = Point(0, 0);
|
||||
|
||||
if (_fontHeight == 0 || !_dataPtr)
|
||||
// No font, so return immediately
|
||||
return 0;
|
||||
|
||||
// Loop through the characters of the string
|
||||
if (!str.empty()) {
|
||||
for (const char *strP = str.c_str(); *strP; ++strP) {
|
||||
if (*strP == TEXTCMD_NPC) {
|
||||
strP += 3;
|
||||
} else if (*strP == TEXTCMD_SET_COLOR) {
|
||||
strP += 4;
|
||||
} else {
|
||||
if (*strP == ' ') {
|
||||
// Check for line wrapping
|
||||
checkLineWrap(textSize, maxWidth, strP);
|
||||
}
|
||||
|
||||
extendBounds(textSize, *strP, maxWidth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sizeOut)
|
||||
*sizeOut = textSize;
|
||||
|
||||
return textSize.y + _fontHeight;
|
||||
}
|
||||
|
||||
int STFont::stringWidth(const CString &text) const {
|
||||
if (text.empty())
|
||||
return 0;
|
||||
|
||||
const char *srcP = text.c_str();
|
||||
int total = 0;
|
||||
char c;
|
||||
while ((c = *srcP++)) {
|
||||
if (c == 26) {
|
||||
// Skip over command parameter bytes
|
||||
srcP += 3;
|
||||
} else if (c == TEXTCMD_SET_COLOR) {
|
||||
// Skip over command parameter bytes
|
||||
srcP += 4;
|
||||
} else if (c != '\n') {
|
||||
total += _chars[(byte)c]._width;
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
int STFont::writeString(CVideoSurface *surface, const Rect &rect1, const Rect &destRect,
|
||||
int yOffset, const CString &str, CTextCursor *textCursor) {
|
||||
if (!_fontHeight || !_dataPtr)
|
||||
return -1;
|
||||
|
||||
Point textSize(0, -yOffset);
|
||||
Rect destBounds = destRect;
|
||||
destBounds.constrain(rect1);
|
||||
if (destBounds.isEmpty())
|
||||
return -1;
|
||||
|
||||
const char *endP = nullptr;
|
||||
const char *strEndP = str.c_str() + str.size() - 1;
|
||||
for (const char *srcP = str.c_str(); *srcP; ++srcP) {
|
||||
if (*srcP == TEXTCMD_NPC) {
|
||||
srcP += 3;
|
||||
} else if (*srcP == TEXTCMD_SET_COLOR) {
|
||||
// Change the color used for characters
|
||||
byte r = *++srcP;
|
||||
byte g = *++srcP;
|
||||
byte b = *++srcP;
|
||||
++srcP;
|
||||
setColor(r, g, b);
|
||||
} else {
|
||||
if (*srcP == ' ') {
|
||||
// Check for line wrapping
|
||||
checkLineWrap(textSize, rect1.width(), srcP);
|
||||
if (!*srcP)
|
||||
return endP - str.c_str();
|
||||
}
|
||||
|
||||
if (*srcP != '\n') {
|
||||
WriteCharacterResult result = writeChar(surface, *srcP, textSize, rect1, &destBounds);
|
||||
if (result == WC_OUTSIDE_BOTTOM)
|
||||
return endP - str.c_str();
|
||||
else if (result == WC_IN_BOUNDS)
|
||||
endP = srcP;
|
||||
}
|
||||
|
||||
if (srcP < strEndP)
|
||||
extendBounds(textSize, *srcP, rect1.width());
|
||||
}
|
||||
}
|
||||
|
||||
if (textCursor && textCursor->getMode() == -2) {
|
||||
Point cursorPos(rect1.left + textSize.x, rect1.top + textSize.y);
|
||||
textCursor->setPos(cursorPos);
|
||||
}
|
||||
|
||||
return endP ? endP - str.c_str() : 0;
|
||||
}
|
||||
|
||||
void STFont::writeString(CVideoSurface *surface, const Point &destPos, Rect &clipRect,
|
||||
const CString &str, int lineWidth) {
|
||||
if (!_fontHeight || !_dataPtr || str.empty())
|
||||
return;
|
||||
if (!lineWidth)
|
||||
// No line width specified, so get in the width
|
||||
lineWidth = stringWidth(str);
|
||||
|
||||
Rect textRect(0, 0, lineWidth, _fontHeight);
|
||||
Point textPt = destPos;
|
||||
|
||||
// Perform clipping as necessary if the text will fall outside clipping area
|
||||
if (textPt.y > clipRect.bottom)
|
||||
return;
|
||||
|
||||
if ((textPt.y + textRect.height()) > clipRect.bottom)
|
||||
textRect.bottom = textRect.top - textPt.y + clipRect.bottom;
|
||||
|
||||
if (textPt.y < clipRect.top) {
|
||||
if ((textPt.y + textRect.height()) < clipRect.top)
|
||||
return;
|
||||
|
||||
textRect.top += clipRect.top - textPt.y;
|
||||
textPt.y = clipRect.top;
|
||||
}
|
||||
|
||||
// Iterate through each character of the string
|
||||
for (const byte *srcP = (const byte *)str.c_str(); *srcP; ++srcP) {
|
||||
byte c = *srcP;
|
||||
if (c == 0xE9)
|
||||
c = '$';
|
||||
|
||||
// Form a rect of the area of the next character to draw
|
||||
Rect charRect(_chars[c]._offset, textRect.top,
|
||||
_chars[c]._offset + _chars[c]._width, textRect.bottom);
|
||||
int textX = textPt.x;
|
||||
|
||||
if (textPt.x < clipRect.left) {
|
||||
// Character is either partially or entirely left off-screen
|
||||
if ((textPt.x + charRect.width()) < clipRect.left) {
|
||||
textPt.x += _chars[c]._width;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Partially clipped on left-hand side
|
||||
charRect.left = clipRect.left - textPt.x;
|
||||
textPt.x = clipRect.left;
|
||||
} else if ((textPt.x + charRect.width()) > clipRect.right) {
|
||||
if (textPt.x > clipRect.right)
|
||||
// Now entirely off right-hand side, so stop drawing
|
||||
break;
|
||||
|
||||
// Partially clipped on right-hand side
|
||||
charRect.right += clipRect.right - textPt.x - charRect.width();
|
||||
}
|
||||
|
||||
// At this point, we know we've got to draw at least part of a character,
|
||||
// and have figured out the area of the character to draw
|
||||
copyRect(surface, textPt, charRect);
|
||||
textPt.x = textX + _chars[c]._width;
|
||||
}
|
||||
}
|
||||
|
||||
WriteCharacterResult STFont::writeChar(CVideoSurface *surface, unsigned char c, const Point &pt,
|
||||
const Rect &destRect, const Rect *srcRect) {
|
||||
if (c == 233)
|
||||
c = '$';
|
||||
|
||||
Rect charRect(_chars[c]._offset, 0,
|
||||
_chars[c]._offset + _chars[c]._width, _fontHeight);
|
||||
Point destPos(pt.x + destRect.left, pt.y + destRect.top);
|
||||
|
||||
if (srcRect->isEmpty())
|
||||
srcRect = &destRect;
|
||||
if (destPos.y > srcRect->bottom)
|
||||
return WC_OUTSIDE_BOTTOM;
|
||||
|
||||
if ((destPos.y + charRect.height()) > srcRect->bottom) {
|
||||
charRect.bottom += srcRect->bottom - (destPos.y + charRect.height());
|
||||
}
|
||||
|
||||
if (destPos.y < srcRect->top) {
|
||||
if ((charRect.height() + destPos.y) < srcRect->top)
|
||||
return WC_OUTSIDE_TOP;
|
||||
|
||||
charRect.top += srcRect->top - destPos.y;
|
||||
destPos.y = srcRect->top;
|
||||
}
|
||||
|
||||
if (destPos.x < srcRect->left) {
|
||||
if ((charRect.width() + destPos.x) < srcRect->left)
|
||||
return WC_OUTSIDE_LEFT;
|
||||
|
||||
charRect.left += srcRect->left - destPos.x;
|
||||
destPos.x = srcRect->left;
|
||||
} else {
|
||||
if ((destPos.x + charRect.width()) > srcRect->right) {
|
||||
if (destPos.x > srcRect->right)
|
||||
return WC_OUTSIDE_RIGHT;
|
||||
|
||||
charRect.right += srcRect->right - destPos.x - charRect.width();
|
||||
}
|
||||
}
|
||||
|
||||
copyRect(surface, destPos, charRect);
|
||||
return WC_IN_BOUNDS;
|
||||
}
|
||||
|
||||
void STFont::copyRect(CVideoSurface *surface, const Point &pt, Rect &rect) {
|
||||
if (surface->lock()) {
|
||||
uint16 *lineP = surface->getBasePtr(pt.x, pt.y);
|
||||
uint16 color = getColor();
|
||||
|
||||
for (int yp = rect.top; yp < rect.bottom; ++yp, lineP += surface->getWidth()) {
|
||||
uint16 *destP = lineP;
|
||||
for (int xp = rect.left; xp < rect.right; ++xp, ++destP) {
|
||||
const byte *transP = _dataPtr + yp * _dataWidth + xp;
|
||||
surface->copyPixel(destP, &color, *transP >> 3,
|
||||
surface->getRawSurface()->format, true);
|
||||
}
|
||||
}
|
||||
|
||||
surface->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void STFont::extendBounds(Point &textSize, byte c, int maxWidth) const {
|
||||
textSize.x += _chars[c]._width;
|
||||
|
||||
if (c == '\n' || textSize.x > maxWidth) {
|
||||
textSize.x = 0;
|
||||
textSize.y += _fontHeight;
|
||||
}
|
||||
}
|
||||
|
||||
void STFont::checkLineWrap(Point &textSize, int maxWidth, const char *&str) const {
|
||||
bool flag = false;
|
||||
int totalWidth = 0;
|
||||
|
||||
// Loop forward getting the width of the word (including preceding space)
|
||||
// until a space is encountered following at least one character
|
||||
for (const char *srcPtr = str; *srcPtr && (*srcPtr != ' ' || !flag); ++srcPtr) {
|
||||
if (*srcPtr == TEXTCMD_NPC) {
|
||||
srcPtr += 3;
|
||||
} else if (*srcPtr == TEXTCMD_SET_COLOR) {
|
||||
srcPtr += 4;
|
||||
} else {
|
||||
totalWidth += _chars[(byte)*srcPtr]._width;
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((textSize.x + totalWidth) >= maxWidth && totalWidth < maxWidth) {
|
||||
// Word wrap
|
||||
textSize.x = 0;
|
||||
textSize.y += _fontHeight;
|
||||
++str;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Titanic
|
||||
Reference in New Issue
Block a user