/* 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 . * */ //#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 font = _fonts[0]; if (!font.get()) { warning("[TeTextBase2::build()] Warning : font missing"); return; } _valueWasSet = false; _size = TeVector2s32(0, 0); _wrappedLines.clear(); Common::Array endPts = _lineBreaks; uint32 npos = Common::String::npos; endPts.push_back(npos); uint32 start = 0; for (uint32 end : endPts) { Common::Array 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 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 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 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 TeTextBase2::currentFont(uint offset) { if (_fonts.size() == 0) return TeIntrusivePtr(); int closest_off = -1; TeIntrusivePtr 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(); 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 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 &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