diff --git a/games/devtest/mods/testnodes/textures.lua b/games/devtest/mods/testnodes/textures.lua
index 4aeb25048..90a71af94 100644
--- a/games/devtest/mods/testnodes/textures.lua
+++ b/games/devtest/mods/testnodes/textures.lua
@@ -372,13 +372,32 @@ minetest.register_node("testnodes:glyph_font", {
description = S("Combine Test Node"),
tiles = {{
name = "testnodes_generated_mb.png"..
- "^[text:regular font::::white"..
- "^[text:large font(16pt):16::0,32:white"..
- "^[text:larger font(32pt):32::0,64:white"..
- "^[text:bold font(20pt):20:bold:0,96:white"..
- "^[text:italic font(20pt):20:italic:0,128:white"..
- "^[text:monospace font(20pt):20:mono:0,160:white"..
- "^[text:red monospace font(20pt):20:mono:0,192:red",
+ "^[text:" .. minetest.encode_base64([[A hypertext element
+Normal test
+This is a normal text. 中文也应当可以渲染。special characters ^[:,
+
+style test
+
+ .
+ ]]),
+ align_style = "world",
+ scale = 8,
+ },
+ {
+ name = "testnodes_generated_mb.png"..
+ "^[text:" .. minetest.encode_base64([[
+This is a test of the global tag. The parameters are:
+background=#80AAAAAA margin=20 valign=bottom halign=right color=pink hovercolor=purple size=12 font=mono
+action]]) .. ":500x240",
+ align_style = "world",
+ scale = 8,
+ },
+ {
+ name = "testnodes_generated_mb.png"..
+ "^[text:" .. minetest.encode_base64([[
+Custom tag test
+
+color=green font=mono size=24]]) .. ":500x260:4,3",
align_style = "world",
scale = 8,
}},
diff --git a/src/client/imagesource.cpp b/src/client/imagesource.cpp
index 82201c214..2c3e65974 100644
--- a/src/client/imagesource.cpp
+++ b/src/client/imagesource.cpp
@@ -16,6 +16,8 @@
#include "util/strfnd.h"
#include "client/fontengine.h"
#include "irrlicht_changes/CGUITTFont.h"
+#include "gui/guiHyperText.h"
+#include
////////////////////////////////
@@ -1765,97 +1767,116 @@ bool ImageSource::generateImagePart(std::string_view part_of_name,
baseimg->getDimension(), brightness, contrast);
}
/*
- [text:string:size:mono,bold,italic:x,y:color
- Render a character at given position
- size and font is optional, but colon should be kept
+ [text:string:WxH:x,y
+ [text:string:WxH
+ [text:string
+ Render a character at given position, string is encoded by base64
+ coordinate is optional, but colon should be kept
*/
else if (str_starts_with(part_of_name, "[text:")) {
- Strfnd sf(part_of_name);
- sf.next(":");
- std::string textdef = sf.next(":");
+ const auto colonSeparatedPartsOfName = str_split(part_of_name, ':');
+ auto numPartsOfName = colonSeparatedPartsOfName.size();
+ if (numPartsOfName < 2) {
+ errorstream << "generateImagePart(): "
+ << "text is missing in [text" << std::endl;
+ return false;
+ }
+
+ std::string textdef;
+ {
+ auto blob = colonSeparatedPartsOfName[1];
+ if (!base64_is_valid(blob)) {
+ errorstream << "generateImagePart(): "
+ << "malformed base64 in [text" << std::endl;
+ return false;
+ }
+ textdef = base64_decode(blob);
+ }
core::stringw textdefW = utf8_to_stringw(textdef);
- unsigned int fontSize = FONT_SIZE_UNSPECIFIED;
- std::string sizeStr = sf.next(":");
- if (is_number(sizeStr))
- {
- fontSize = mystoi(sizeStr,0,200);
- }
-
- FontMode mode = FM_Standard;
- bool bold = false, italic = false;
- std::string fontStyle = sf.next(":");
- std::vector styleWords = str_split(fontStyle, ',');
- for(auto word : styleWords){
- if (word == "mono")
- {
- mode = FM_Mono;
- }else if (word == "bold")
- {
- bold = true;
- }else if(word == "italic"){
- italic = true;
+ core::dimension2du size(0,0);
+ if (numPartsOfName >= 3) {
+ auto sizeStr = colonSeparatedPartsOfName[2];
+ std::vector sizeStringArr = str_split(sizeStr, 'x');
+ if (sizeStringArr.size() >= 2) {
+ if (is_number(sizeStringArr[0])) {
+ size.Width = mystoi(std::string(sizeStringArr[0]));
+ }
+ if (is_number(sizeStringArr[1])) {
+ size.Height = mystoi(std::string(sizeStringArr[1]));
+ }
}
+ infostream << "size string: " << sizeStringArr.size() <getDimension();
+ } else {
+ errorstream << "generateImagePart(): width and height is not specified and cannot be inferred from base image" << std::endl;
+ return false;
}
- FontSpec spec(fontSize, mode, bold, italic);
core::position2di pos(0,0);
- std::string positionStr = sf.next(":");
- std::vector positionStrs = str_split(positionStr, ',');
- if (positionStrs.size() >= 2)
+ if (numPartsOfName >= 4)
{
- if (is_number(positionStrs[0])) {
- pos.X = mystoi(positionStrs[0]);
- }
- if (is_number(positionStrs[1])) {
- pos.Y = mystoi(positionStrs[1]);
+ auto posStr = colonSeparatedPartsOfName[3];
+ std::vector posStringArr = str_split(posStr, ',');
+ if (posStringArr.size() >= 2) {
+ if (is_number(posStringArr[0])) {
+ pos.X = mystoi(std::string(posStringArr[0]));
+ }
+ if (is_number(posStringArr[1])) {
+ pos.Y = mystoi(std::string(posStringArr[1]));
+ }
}
}
- video::SColor color(0xff,0xff,0xff,0xff);
- std::string colorStr = sf.next("");
- if (!parseColorString(colorStr,color,false)){
- return false;
- };
-
- irr::gui::CGUITTFont *font =
- static_cast(g_fontengine->getFont(spec));
- core::dimension2d sizeText = font->getDimension(textdefW.c_str());
-
- video::ECOLOR_FORMAT colorFormat = video::ECF_A8R8G8B8;
- core::dimension2d size =sizeText;
- if (baseimg)
- {
- colorFormat = baseimg->getColorFormat();
- size =baseimg->getDimension();
+ video::ITexture* referredTexture = nullptr;
+ TextDrawer drawer(textdefW.c_str(), [&, driver, baseimg](auto texturePath){
+ if (referredTexture) {
+ driver->removeTexture(referredTexture);
+ }
+ video::IImage *image = m_sourcecache.getOrLoad(texturePath);
+ referredTexture = driver->addTexture("text_renderer_base__", baseimg);
+ return referredTexture;
+ });
+ if (referredTexture) {
+ driver->removeTexture(referredTexture);
}
- std::string textureName("text_renderer__");
- textureName.append(part_of_name);
+
+ core::dimension2du canvasSize = size;
+ if (baseimg) {
+ canvasSize = baseimg->getDimension();
+ }
+ core::recti drawingArea(pos, size);
+ video::ECOLOR_FORMAT colorFormat =
+ baseimg ? baseimg->getColorFormat() : video::ECF_A8R8G8B8;
+ drawer.place(drawingArea);
auto texture =
- driver->addRenderTargetTexture(size, textureName, colorFormat);
+ driver->addRenderTargetTexture(canvasSize, "text_renderer__", colorFormat);
if (driver->setRenderTarget(texture, video::ECBF_ALL, video::SColor(0,0,0,0))) {
if (baseimg) {
auto baseTexture = driver->addTexture("text_renderer_base__", baseimg);
- driver->draw2DImage(baseTexture, core::vector2di(0,0));
+ driver->draw2DImage(baseTexture, core::position2di(0,0));
driver->removeTexture(baseTexture);
}
- font->draw(textdefW, core::recti(pos, sizeText), color);
+ drawer.draw(drawingArea, pos, driver, nullptr);
driver->setRenderTarget(NULL);
void* lockedData = texture->lock();
if (lockedData) {
if (baseimg) {
baseimg->drop();
}
- baseimg = driver->createImageFromData(colorFormat, size, lockedData, false);
+ baseimg = driver->createImageFromData(colorFormat, canvasSize, lockedData, false);
texture->unlock();
} else {
- errorstream << "no data inside texture, internal error" << std::endl;
+ errorstream << "generateImagePart(): no data inside texture, internal error" << std::endl;
+ return false;
}
} else {
- errorstream << "fails to set render target, "
+ errorstream << "generateImagePart(): fails to set render target, "
"can this driver renders to target:" <<
driver->queryFeature(video::EVDF_RENDER_TO_TARGET) << std::endl;
+ return false;
}
driver->removeTexture(texture);
}