summaryrefslogtreecommitdiff
path: root/src/client/tile.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/tile.cpp')
-rw-r--r--src/client/tile.cpp134
1 files changed, 113 insertions, 21 deletions
diff --git a/src/client/tile.cpp b/src/client/tile.cpp
index ec8c95f02..8f0c39465 100644
--- a/src/client/tile.cpp
+++ b/src/client/tile.cpp
@@ -948,11 +948,10 @@ video::ITexture* TextureSource::generateTextureFromMesh(
video::IImage* TextureSource::generateImage(const std::string &name)
{
- /*
- Get the base image
- */
+ // Get the base image
const char separator = '^';
+ const char escape = '\\';
const char paren_open = '(';
const char paren_close = ')';
@@ -960,7 +959,9 @@ video::IImage* TextureSource::generateImage(const std::string &name)
s32 last_separator_pos = -1;
u8 paren_bal = 0;
for (s32 i = name.size() - 1; i >= 0; i--) {
- switch(name[i]) {
+ if (i > 0 && name[i-1] == escape)
+ continue;
+ switch (name[i]) {
case separator:
if (paren_bal == 0) {
last_separator_pos = i;
@@ -1028,10 +1029,12 @@ video::IImage* TextureSource::generateImage(const std::string &name)
return NULL;
}
core::dimension2d<u32> dim = tmp->getDimension();
- if (!baseimg)
- baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
- blit_with_alpha(tmp, baseimg, v2s32(0, 0), v2s32(0, 0), dim);
- tmp->drop();
+ if (baseimg) {
+ blit_with_alpha(tmp, baseimg, v2s32(0, 0), v2s32(0, 0), dim);
+ tmp->drop();
+ } else {
+ baseimg = tmp;
+ }
} else if (!generateImagePart(last_part_of_name, baseimg)) {
// Generate image according to part of name
errorstream << "generateImage(): "
@@ -1099,9 +1102,27 @@ video::IImage * Align2Npot2(video::IImage * image,
#endif
+static std::string unescape_string(const std::string &str, const char esc = '\\')
+{
+ std::string out;
+ size_t pos = 0, cpos;
+ out.reserve(str.size());
+ while (1) {
+ cpos = str.find_first_of(esc, pos);
+ if (cpos == std::string::npos) {
+ out += str.substr(pos);
+ break;
+ }
+ out += str.substr(pos, cpos - pos) + str[cpos + 1];
+ pos = cpos + 2;
+ }
+ return out;
+}
+
bool TextureSource::generateImagePart(std::string part_of_name,
video::IImage *& baseimg)
{
+ const char escape = '\\'; // same as in generateImage()
video::IVideoDriver* driver = m_device->getVideoDriver();
sanity_check(driver);
@@ -1251,7 +1272,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
}
/*
[combine:WxH:X,Y=filename:X,Y=filename2
- Creates a bigger texture from an amount of smaller ones
+ Creates a bigger texture from any amount of smaller ones
*/
else if (str_starts_with(part_of_name, "[combine"))
{
@@ -1259,7 +1280,6 @@ bool TextureSource::generateImagePart(std::string part_of_name,
sf.next(":");
u32 w0 = stoi(sf.next("x"));
u32 h0 = stoi(sf.next(":"));
- //infostream<<"combined w="<<w0<<" h="<<h0<<std::endl;
core::dimension2d<u32> dim(w0,h0);
if (baseimg == NULL) {
baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
@@ -1268,11 +1288,11 @@ bool TextureSource::generateImagePart(std::string part_of_name,
while (sf.at_end() == false) {
u32 x = stoi(sf.next(","));
u32 y = stoi(sf.next("="));
- std::string filename = sf.next(":");
+ std::string filename = unescape_string(sf.next_esc(":", escape), escape);
infostream<<"Adding \""<<filename
<<"\" to combined ("<<x<<","<<y<<")"
<<std::endl;
- video::IImage *img = m_sourcecache.getOrLoad(filename, m_device);
+ video::IImage *img = generateImage(filename);
if (img) {
core::dimension2d<u32> dim = img->getDimension();
infostream<<"Size "<<dim.Width
@@ -1295,7 +1315,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
}
}
/*
- "[brighten"
+ [brighten
*/
else if (str_starts_with(part_of_name, "[brighten"))
{
@@ -1309,7 +1329,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
brighten(baseimg);
}
/*
- "[noalpha"
+ [noalpha
Make image completely opaque.
Used for the leaves texture when in old leaves mode, so
that the transparent parts don't look completely black
@@ -1336,7 +1356,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
}
}
/*
- "[makealpha:R,G,B"
+ [makealpha:R,G,B
Convert one color to transparent.
*/
else if (str_starts_with(part_of_name, "[makealpha:"))
@@ -1375,7 +1395,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
}
}
/*
- "[transformN"
+ [transformN
Rotates and/or flips the image.
N can be a number (between 0 and 7) or a transform name.
@@ -1543,12 +1563,11 @@ bool TextureSource::generateImagePart(std::string part_of_name,
Strfnd sf(part_of_name);
sf.next(":");
u32 percent = stoi(sf.next(":"));
- std::string filename = sf.next(":");
- //infostream<<"power part "<<percent<<"%% of "<<filename<<std::endl;
+ std::string filename = unescape_string(sf.next_esc(":", escape), escape);
if (baseimg == NULL)
baseimg = driver->createImage(video::ECF_A8R8G8B8, v2u32(16,16));
- video::IImage *img = m_sourcecache.getOrLoad(filename, m_device);
+ video::IImage *img = generateImage(filename);
if (img)
{
core::dimension2d<u32> dim = img->getDimension();
@@ -1628,9 +1647,9 @@ bool TextureSource::generateImagePart(std::string part_of_name,
}
Strfnd sf(part_of_name);
sf.next(":");
- std::string filename = sf.next(":");
+ std::string filename = unescape_string(sf.next_esc(":", escape), escape);
- video::IImage *img = m_sourcecache.getOrLoad(filename, m_device);
+ video::IImage *img = generateImage(filename);
if (img) {
apply_mask(img, baseimg, v2s32(0, 0), v2s32(0, 0),
img->getDimension());
@@ -1673,6 +1692,10 @@ bool TextureSource::generateImagePart(std::string part_of_name,
apply_colorize(baseimg, v2u32(0, 0), baseimg->getDimension(), color, ratio, keep_alpha);
}
+ /*
+ [applyfiltersformesh
+ Internal modifier
+ */
else if (str_starts_with(part_of_name, "[applyfiltersformesh"))
{
// Apply the "clean transparent" filter, if configured.
@@ -1735,6 +1758,75 @@ bool TextureSource::generateImagePart(std::string part_of_name,
baseimg->drop();
baseimg = image;
}
+ /*
+ [opacity:R
+ Makes the base image transparent according to the given ratio.
+ R must be between 0 and 255.
+ 0 means totally transparent.
+ 255 means totally opaque.
+ */
+ else if (str_starts_with(part_of_name, "[opacity:")) {
+ if (baseimg == NULL) {
+ errorstream << "generateImagePart(): baseimg == NULL "
+ << "for part_of_name=\"" << part_of_name
+ << "\", cancelling." << std::endl;
+ return false;
+ }
+
+ Strfnd sf(part_of_name);
+ sf.next(":");
+
+ u32 ratio = mystoi(sf.next(""), 0, 255);
+
+ core::dimension2d<u32> dim = baseimg->getDimension();
+
+ for (u32 y = 0; y < dim.Height; y++)
+ for (u32 x = 0; x < dim.Width; x++)
+ {
+ video::SColor c = baseimg->getPixel(x, y);
+ c.setAlpha(floor((c.getAlpha() * ratio) / 255 + 0.5));
+ baseimg->setPixel(x, y, c);
+ }
+ }
+ /*
+ [invert:mode
+ Inverts the given channels of the base image.
+ Mode may contain the characters "r", "g", "b", "a".
+ Only the channels that are mentioned in the mode string
+ will be inverted.
+ */
+ else if (str_starts_with(part_of_name, "[invert:")) {
+ if (baseimg == NULL) {
+ errorstream << "generateImagePart(): baseimg == NULL "
+ << "for part_of_name=\"" << part_of_name
+ << "\", cancelling." << std::endl;
+ return false;
+ }
+
+ Strfnd sf(part_of_name);
+ sf.next(":");
+
+ std::string mode = sf.next("");
+ u32 mask = 0;
+ if (mode.find("a") != std::string::npos)
+ mask |= 0xff000000UL;
+ if (mode.find("r") != std::string::npos)
+ mask |= 0x00ff0000UL;
+ if (mode.find("g") != std::string::npos)
+ mask |= 0x0000ff00UL;
+ if (mode.find("b") != std::string::npos)
+ mask |= 0x000000ffUL;
+
+ core::dimension2d<u32> dim = baseimg->getDimension();
+
+ for (u32 y = 0; y < dim.Height; y++)
+ for (u32 x = 0; x < dim.Width; x++)
+ {
+ video::SColor c = baseimg->getPixel(x, y);
+ c.color ^= mask;
+ baseimg->setPixel(x, y, c);
+ }
+ }
else
{
errorstream << "generateImagePart(): Invalid "