Allow escaping of texture names when passed as an argument to a modifier
authorsfan5 <sfan5@live.de>
Sat, 3 Sep 2016 15:53:15 +0000 (17:53 +0200)
committerparamat <mat.gregory@virginmedia.com>
Wed, 14 Sep 2016 08:19:36 +0000 (09:19 +0100)
doc/lua_api.txt
src/client/tile.cpp

index 1da45ad66758e9eeb240bcd3344b8799b9e7f379..74d4b90d5e23e5233f987a03dc562e460821355e 100644 (file)
@@ -263,7 +263,17 @@ Textures can be grouped together by enclosing them in `(` and `)`.
 Example: `cobble.png^(thing1.png^thing2.png)`
 
 A texture for `thing1.png^thing2.png` is created and the resulting
-texture is overlaid over `cobble.png`.
+texture is overlaid on top of `cobble.png`.
+
+### Escaping
+Modifiers that accept texture names (e.g. `[combine`) accept escaping to allow
+passing complex texture names as arguments. Escaping is done with backslash and
+is required for `^` and `:`.
+
+Example: `cobble.png^[lowpart:50:color.png\^[mask\:trans.png`
+
+The lower 50 percent of `color.png^[mask:trans.png` are overlaid
+on top of `cobble.png`.
 
 ### Advanced texture modifiers
 
@@ -351,7 +361,7 @@ Example:
     default_stone.png^[transformFXR90
 
 #### `[inventorycube{<top>{<left>{<right>`
-`^` is replaced by `&` in texture names.
+Escaping does not apply here and `^` is replaced by `&` in texture names instead.
 
 Create an inventory cube texture using the side textures.
 
index 3b5d2a3ae4cd7d4fd853f711365ed093ef5fe0cd..67d5d8d1aa97bdfaa017a8902322624290ecfcfe 100644 (file)
@@ -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.