Initial commit
This commit is contained in:
326
engines/tetraedge/te/te_text_base2.cpp
Normal file
326
engines/tetraedge/te/te_text_base2.cpp
Normal file
@@ -0,0 +1,326 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//#define TETRAEDGE_DUMP_RENDERED_FONTS
|
||||
|
||||
#ifdef TETRAEDGE_DUMP_RENDERED_FONTS
|
||||
#include "image/png.h"
|
||||
#endif
|
||||
|
||||
#include "tetraedge/te/te_text_base2.h"
|
||||
|
||||
namespace Tetraedge {
|
||||
|
||||
TeTextBase2::TeTextBase2() : _drawRect(0, 0), _size(0, 0),
|
||||
_alignStyle(TeIFont::AlignLeft), _interLine(0.0f), _globalColor(0xff, 0xff, 0xff, 0xff),
|
||||
_wrapMode(WrapModeFixed), _strikethrough(false), _fontSize(10), _valueWasSet(true) {
|
||||
_mesh = TeMesh::makeInstance();
|
||||
_mesh->setglTexEnvBlend();
|
||||
_mesh->setShouldDraw(true);
|
||||
}
|
||||
|
||||
TeTextBase2::~TeTextBase2() {
|
||||
delete _mesh;
|
||||
}
|
||||
|
||||
#ifdef TETRAEDGE_DUMP_RENDERED_FONTS
|
||||
static int dumpCount = 0;
|
||||
#endif
|
||||
|
||||
void TeTextBase2::build() {
|
||||
if (!_text.size() || !_fontSize)
|
||||
return;
|
||||
|
||||
TeIntrusivePtr<TeIFont> font = _fonts[0];
|
||||
if (!font.get()) {
|
||||
warning("[TeTextBase2::build()] Warning : font missing");
|
||||
return;
|
||||
}
|
||||
|
||||
_valueWasSet = false;
|
||||
_size = TeVector2s32(0, 0);
|
||||
_wrappedLines.clear();
|
||||
|
||||
Common::Array<uint32> endPts = _lineBreaks;
|
||||
uint32 npos = Common::String::npos;
|
||||
endPts.push_back(npos);
|
||||
uint32 start = 0;
|
||||
for (uint32 end : endPts) {
|
||||
Common::Array<Common::String> lines;
|
||||
Common::String txt = _text.substr(start, end - start);
|
||||
if (txt.find(' ') != Common::String::npos)
|
||||
font->wordWrapText(txt, _fontSize, _drawRect._x, lines);
|
||||
else
|
||||
lines.push_back(txt);
|
||||
_wrappedLines.push_back(lines);
|
||||
start = end;
|
||||
}
|
||||
|
||||
Common::Array<float> lineoffsets;
|
||||
float lineHeight = font->getHeight(_fontSize);
|
||||
float height = 0;
|
||||
for (const Common::String &line : _wrappedLines) {
|
||||
if (_alignStyle == TeIFont::AlignJustify) {
|
||||
warning("TODO: Implement TeTextBase2::computeNbSpaces for Justify");
|
||||
//computeNbSpaces(&line, offset, line.endOffset);
|
||||
}
|
||||
Common::Rect lineSize = font->getBBox(line, _fontSize);
|
||||
if (lineSize.right > _size._x)
|
||||
_size._x = lineSize.right;
|
||||
|
||||
lineoffsets.push_back(height);
|
||||
height += lineHeight + _interLine;
|
||||
}
|
||||
|
||||
// Round up to the nearest 2 pixels so it centres in the
|
||||
// area without a half pixel offset.
|
||||
_size._y = (((int)ceilf(height) + 1) / 2) * 2;
|
||||
_size._x = ((_size._x + 1) / 2) * 2;
|
||||
|
||||
TeImage img;
|
||||
Common::SharedPtr<TePalette> nullpal;
|
||||
img.createImg(_size._x, _size._y, nullpal, Graphics::PixelFormat::createFormatRGBA32());
|
||||
// fill with global color, alpha 0 so that the font anti-aliasing blends
|
||||
// to the right color (see eg, the cellphone display)
|
||||
img.fill(_globalColor.r(), _globalColor.g(), _globalColor.b(), 0);
|
||||
|
||||
for (uint i = 0; i < _wrappedLines.size(); i++) {
|
||||
drawLine(img, _wrappedLines[i], lineoffsets[i]);
|
||||
}
|
||||
|
||||
TeIntrusivePtr<Te3DTexture> texture = Te3DTexture::makeInstance();
|
||||
texture->load(img);
|
||||
|
||||
#ifdef TETRAEDGE_DUMP_RENDERED_FONTS
|
||||
Common::DumpFile dumpFile;
|
||||
dumpFile.open(Common::String::format("/tmp/rendered-font-dump-%04d.png", dumpCount));
|
||||
dumpCount++;
|
||||
Image::writePNG(dumpFile, img);
|
||||
dumpFile.close();
|
||||
#endif
|
||||
|
||||
_mesh->setConf(4, 4, TeMesh::MeshMode_TriangleStrip, 0, 0);
|
||||
_mesh->defaultMaterial(texture);
|
||||
_mesh->setglTexEnvBlend();
|
||||
_mesh->setShouldDraw(true);
|
||||
_mesh->setColor(_globalColor);
|
||||
_mesh->setVertex(0, TeVector3f32(_size._x * -0.5f, _size._y * -0.5f, 0.0f));
|
||||
_mesh->setTextureUV(0, TeVector2f32(0, 1));
|
||||
_mesh->setNormal(0, TeVector3f32(0.0f, 0.0f, 1.0f));
|
||||
_mesh->setColor(0, _globalColor);
|
||||
_mesh->setVertex(1, TeVector3f32(_size._x * 0.5f, _size._y * -0.5f, 0.0f));
|
||||
_mesh->setTextureUV(1, TeVector2f32(1, 1));
|
||||
_mesh->setNormal(1, TeVector3f32(0.0f, 0.0f, 1.0f));
|
||||
_mesh->setColor(1, _globalColor);
|
||||
_mesh->setVertex(2, TeVector3f32(_size._x * 0.5f, _size._y * 0.5f, 0.0f));
|
||||
_mesh->setTextureUV(2, TeVector2f32(1, 0));
|
||||
_mesh->setNormal(2, TeVector3f32(0.0f, 0.0f, 1.0f));
|
||||
_mesh->setColor(2, _globalColor);
|
||||
_mesh->setVertex(3, TeVector3f32(_size._x * -0.5f, _size._y * 0.5f, 0.0f));
|
||||
_mesh->setTextureUV(3, TeVector2f32(0, 0));
|
||||
_mesh->setNormal(3, TeVector3f32(0.0f, 0.0f, 1.0f));
|
||||
_mesh->setColor(3, _globalColor);
|
||||
_mesh->setIndex(0, 0);
|
||||
_mesh->setIndex(1, 1);
|
||||
_mesh->setIndex(2, 3);
|
||||
_mesh->setIndex(3, 2);
|
||||
_mesh->setHasAlpha(true);
|
||||
}
|
||||
|
||||
void TeTextBase2::clear() {
|
||||
clearText();
|
||||
clearStyles();
|
||||
_valueWasSet = true;
|
||||
}
|
||||
|
||||
void TeTextBase2::clearStyles() {
|
||||
_lineBreaks.clear();
|
||||
_fonts.clear();
|
||||
_colors.clear();
|
||||
_valueWasSet = true;
|
||||
}
|
||||
|
||||
void TeTextBase2::clearText() {
|
||||
_text.clear();
|
||||
_valueWasSet = true;
|
||||
}
|
||||
|
||||
void TeTextBase2::computeNbSpaces(Line &line, uint startOffset, uint endOffset) {
|
||||
// only needed if we implement Justify
|
||||
error("TODO: Implement TeTextBase2::computeNbSpaces");
|
||||
}
|
||||
|
||||
TeColor TeTextBase2::currentColor(uint offset) const {
|
||||
if (_colors.size() == 0)
|
||||
return _globalColor;
|
||||
int closest_off = -1;
|
||||
TeColor result;
|
||||
// Find closest without going over.
|
||||
for (auto &pair : _colors) {
|
||||
if ((int)pair._key > closest_off && pair._key <= offset) {
|
||||
result = pair._value;
|
||||
closest_off = pair._key;
|
||||
}
|
||||
}
|
||||
if (closest_off == -1)
|
||||
return _globalColor;
|
||||
return result;
|
||||
}
|
||||
|
||||
TeIntrusivePtr<TeIFont> TeTextBase2::currentFont(uint offset) {
|
||||
if (_fonts.size() == 0)
|
||||
return TeIntrusivePtr<TeIFont>();
|
||||
int closest_off = -1;
|
||||
TeIntrusivePtr<TeIFont> result;
|
||||
// Find closest without going over.
|
||||
for (auto &pair : _fonts) {
|
||||
if ((int)pair._key > closest_off && pair._key <= offset) {
|
||||
result = pair._value;
|
||||
closest_off = pair._key;
|
||||
}
|
||||
}
|
||||
if (closest_off == -1)
|
||||
return TeIntrusivePtr<TeIFont>();
|
||||
return result;
|
||||
}
|
||||
|
||||
void TeTextBase2::draw() {
|
||||
if (_text.empty() || (_drawRect._x <= 0 && _wrapMode == WrapModeFixed))
|
||||
return;
|
||||
|
||||
if (_valueWasSet)
|
||||
build();
|
||||
|
||||
_mesh->draw();
|
||||
|
||||
//if (_strikethrough)
|
||||
// warning("TODO: Implement TeTextBase2::draw strikethrough support");
|
||||
}
|
||||
|
||||
void TeTextBase2::drawEmptyChar(uint offset) {
|
||||
error("TODO: Implement TeTextBase2::drawEmptychar");
|
||||
}
|
||||
|
||||
void TeTextBase2::drawLine(TeImage &img, const Common::String &str, int yoffset) {
|
||||
TeIntrusivePtr<TeIFont> font = _fonts[0];
|
||||
|
||||
// Note: We draw this with black because the global color will be applied on
|
||||
// the mesh.
|
||||
font->draw(img, str, _fontSize, yoffset, TeColor(0, 0, 0, 255), _alignStyle);
|
||||
}
|
||||
|
||||
uint TeTextBase2::endOfWord(uint offset) const {
|
||||
while (offset < _text.size() && !newLines(offset) && !isASpace(offset))
|
||||
offset++;
|
||||
return offset;
|
||||
}
|
||||
|
||||
void TeTextBase2::insertNewLine(uint offset) {
|
||||
_lineBreaks.push_back(offset);
|
||||
}
|
||||
|
||||
bool TeTextBase2::isASpace(uint offset) const {
|
||||
char c = _text[offset];
|
||||
return (c == ' ' || c == '\t' || c == '\n' || c == '\r');
|
||||
}
|
||||
|
||||
int TeTextBase2::newLines(uint offset) const {
|
||||
int result = 0;
|
||||
for (uint off : _lineBreaks) {
|
||||
if (off == offset)
|
||||
result++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int TeTextBase2::nextNonSpaceChar(uint offset) {
|
||||
while (isASpace(offset))
|
||||
offset++;
|
||||
return offset; // TODO: or offset - 1?
|
||||
}
|
||||
|
||||
void TeTextBase2::setAlignStyle(TeIFont::AlignStyle style) {
|
||||
_alignStyle = style;
|
||||
_valueWasSet = true;
|
||||
}
|
||||
|
||||
void TeTextBase2::setColor(uint offset, const TeColor &color) {
|
||||
_colors.setVal(offset, color);
|
||||
_valueWasSet = true;
|
||||
}
|
||||
|
||||
void TeTextBase2::setFont(uint offset, const TeIntrusivePtr<TeIFont> &newfont) {
|
||||
_fonts.setVal(offset, newfont);
|
||||
_valueWasSet = true;
|
||||
}
|
||||
|
||||
void TeTextBase2::setFontSize(int newSize) {
|
||||
if (_fontSize != newSize) {
|
||||
_fontSize = newSize;
|
||||
_valueWasSet = true;
|
||||
}
|
||||
}
|
||||
|
||||
void TeTextBase2::setGlobalColor(const TeColor &color) {
|
||||
_globalColor = color;
|
||||
_valueWasSet = true;
|
||||
}
|
||||
|
||||
void TeTextBase2::setInterLine(float val) {
|
||||
_interLine = val;
|
||||
_valueWasSet = true;
|
||||
}
|
||||
|
||||
void TeTextBase2::setRect(const TeVector2s32 &rect) {
|
||||
_drawRect = rect;
|
||||
_valueWasSet = true;
|
||||
}
|
||||
|
||||
void TeTextBase2::setText(const Common::String &newText) {
|
||||
_valueWasSet = true;
|
||||
_text = newText;
|
||||
//int len = newText.size();
|
||||
//_mesh.setConf(len * 4, len * 6, TeMesh::MeshMode_Triangles, 1, len * 2);
|
||||
}
|
||||
|
||||
void TeTextBase2::setWrapMode(TeTextBase2::WrapMode &mode) {
|
||||
_wrapMode = mode;
|
||||
_valueWasSet = true;
|
||||
}
|
||||
|
||||
TeVector2s32 TeTextBase2::size() {
|
||||
if (_valueWasSet)
|
||||
build();
|
||||
return _size;
|
||||
}
|
||||
|
||||
void TeTextBase2::strikethrough(bool val) {
|
||||
if (_strikethrough != val) {
|
||||
_strikethrough = val;
|
||||
_valueWasSet = true;
|
||||
}
|
||||
if (val) {
|
||||
warning("TODO: Implement TeTextBase2::draw strikethrough support");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // end namespace Tetraedge
|
||||
Reference in New Issue
Block a user