Skip to content

Commit

Permalink
Allow escaping of texture names when passed as an argument to a modifier
Browse files Browse the repository at this point in the history
  • Loading branch information
sfan5 authored and paramat committed Sep 14, 2016
1 parent 9dd22ae commit b77cee1
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 23 deletions.
14 changes: 12 additions & 2 deletions doc/lua_api.txt
Expand Up @@ -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

Expand Down Expand Up @@ -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.

Expand Down
65 changes: 44 additions & 21 deletions src/client/tile.cpp
Expand Up @@ -948,19 +948,20 @@ 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 = ')';

// Find last separator in the 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;
Expand Down Expand Up @@ -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(): "
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -1251,15 +1272,14 @@ 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"))
{
Strfnd sf(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);
Expand All @@ -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
Expand All @@ -1295,7 +1315,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
}
}
/*
"[brighten"
[brighten
*/
else if (str_starts_with(part_of_name, "[brighten"))
{
Expand All @@ -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
Expand All @@ -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:"))
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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.
Expand Down

0 comments on commit b77cee1

Please sign in to comment.