Allow escaping of texture names when passed as an argument to a modifier

This commit is contained in:
sfan5 2016-09-03 17:53:15 +02:00 committed by paramat
parent 9dd22aebc7
commit b77cee146b
2 changed files with 56 additions and 23 deletions

@ -263,7 +263,17 @@ Textures can be grouped together by enclosing them in `(` and `)`.
Example: `cobble.png^(thing1.png^thing2.png)` Example: `cobble.png^(thing1.png^thing2.png)`
A texture for `thing1.png^thing2.png` is created and the resulting 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 ### Advanced texture modifiers
@ -351,7 +361,7 @@ Example:
default_stone.png^[transformFXR90 default_stone.png^[transformFXR90
#### `[inventorycube{<top>{<left>{<right>` #### `[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. Create an inventory cube texture using the side textures.

@ -948,11 +948,10 @@ video::ITexture* TextureSource::generateTextureFromMesh(
video::IImage* TextureSource::generateImage(const std::string &name) video::IImage* TextureSource::generateImage(const std::string &name)
{ {
/* // Get the base image
Get the base image
*/
const char separator = '^'; const char separator = '^';
const char escape = '\\';
const char paren_open = '('; const char paren_open = '(';
const char paren_close = ')'; const char paren_close = ')';
@ -960,7 +959,9 @@ video::IImage* TextureSource::generateImage(const std::string &name)
s32 last_separator_pos = -1; s32 last_separator_pos = -1;
u8 paren_bal = 0; u8 paren_bal = 0;
for (s32 i = name.size() - 1; i >= 0; i--) { 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: case separator:
if (paren_bal == 0) { if (paren_bal == 0) {
last_separator_pos = i; last_separator_pos = i;
@ -1028,10 +1029,12 @@ video::IImage* TextureSource::generateImage(const std::string &name)
return NULL; return NULL;
} }
core::dimension2d<u32> dim = tmp->getDimension(); core::dimension2d<u32> dim = tmp->getDimension();
if (!baseimg) if (baseimg) {
baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
blit_with_alpha(tmp, baseimg, v2s32(0, 0), v2s32(0, 0), dim); blit_with_alpha(tmp, baseimg, v2s32(0, 0), v2s32(0, 0), dim);
tmp->drop(); tmp->drop();
} else {
baseimg = tmp;
}
} else if (!generateImagePart(last_part_of_name, baseimg)) { } else if (!generateImagePart(last_part_of_name, baseimg)) {
// Generate image according to part of name // Generate image according to part of name
errorstream << "generateImage(): " errorstream << "generateImage(): "
@ -1099,9 +1102,27 @@ video::IImage * Align2Npot2(video::IImage * image,
#endif #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, bool TextureSource::generateImagePart(std::string part_of_name,
video::IImage *& baseimg) video::IImage *& baseimg)
{ {
const char escape = '\\'; // same as in generateImage()
video::IVideoDriver* driver = m_device->getVideoDriver(); video::IVideoDriver* driver = m_device->getVideoDriver();
sanity_check(driver); sanity_check(driver);
@ -1251,7 +1272,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
} }
/* /*
[combine:WxH:X,Y=filename:X,Y=filename2 [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")) else if (str_starts_with(part_of_name, "[combine"))
{ {
@ -1259,7 +1280,6 @@ bool TextureSource::generateImagePart(std::string part_of_name,
sf.next(":"); sf.next(":");
u32 w0 = stoi(sf.next("x")); u32 w0 = stoi(sf.next("x"));
u32 h0 = stoi(sf.next(":")); u32 h0 = stoi(sf.next(":"));
//infostream<<"combined w="<<w0<<" h="<<h0<<std::endl;
core::dimension2d<u32> dim(w0,h0); core::dimension2d<u32> dim(w0,h0);
if (baseimg == NULL) { if (baseimg == NULL) {
baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
@ -1268,11 +1288,11 @@ bool TextureSource::generateImagePart(std::string part_of_name,
while (sf.at_end() == false) { while (sf.at_end() == false) {
u32 x = stoi(sf.next(",")); u32 x = stoi(sf.next(","));
u32 y = 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 infostream<<"Adding \""<<filename
<<"\" to combined ("<<x<<","<<y<<")" <<"\" to combined ("<<x<<","<<y<<")"
<<std::endl; <<std::endl;
video::IImage *img = m_sourcecache.getOrLoad(filename, m_device); video::IImage *img = generateImage(filename);
if (img) { if (img) {
core::dimension2d<u32> dim = img->getDimension(); core::dimension2d<u32> dim = img->getDimension();
infostream<<"Size "<<dim.Width 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")) else if (str_starts_with(part_of_name, "[brighten"))
{ {
@ -1309,7 +1329,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
brighten(baseimg); brighten(baseimg);
} }
/* /*
"[noalpha" [noalpha
Make image completely opaque. Make image completely opaque.
Used for the leaves texture when in old leaves mode, so Used for the leaves texture when in old leaves mode, so
that the transparent parts don't look completely black 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. Convert one color to transparent.
*/ */
else if (str_starts_with(part_of_name, "[makealpha:")) 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. Rotates and/or flips the image.
N can be a number (between 0 and 7) or a transform name. 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); Strfnd sf(part_of_name);
sf.next(":"); sf.next(":");
u32 percent = stoi(sf.next(":")); u32 percent = stoi(sf.next(":"));
std::string filename = sf.next(":"); std::string filename = unescape_string(sf.next_esc(":", escape), escape);
//infostream<<"power part "<<percent<<"%% of "<<filename<<std::endl;
if (baseimg == NULL) if (baseimg == NULL)
baseimg = driver->createImage(video::ECF_A8R8G8B8, v2u32(16,16)); 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) if (img)
{ {
core::dimension2d<u32> dim = img->getDimension(); core::dimension2d<u32> dim = img->getDimension();
@ -1628,9 +1647,9 @@ bool TextureSource::generateImagePart(std::string part_of_name,
} }
Strfnd sf(part_of_name); Strfnd sf(part_of_name);
sf.next(":"); 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) { if (img) {
apply_mask(img, baseimg, v2s32(0, 0), v2s32(0, 0), apply_mask(img, baseimg, v2s32(0, 0), v2s32(0, 0),
img->getDimension()); 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); apply_colorize(baseimg, v2u32(0, 0), baseimg->getDimension(), color, ratio, keep_alpha);
} }
/*
[applyfiltersformesh
Internal modifier
*/
else if (str_starts_with(part_of_name, "[applyfiltersformesh")) else if (str_starts_with(part_of_name, "[applyfiltersformesh"))
{ {
// Apply the "clean transparent" filter, if configured. // Apply the "clean transparent" filter, if configured.