1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-06-27 16:36:03 +00:00

Client: Extend [combine modifier with optional width parameter

This addition makes it possible to combine textures easily without
needing to take care of the texture pack size e.g. by resizing
every texture before combining. This feature automates such labour.
This commit is contained in:
SmallJoker 2024-08-29 23:14:16 +02:00
parent c175046d30
commit f13144a15d
3 changed files with 90 additions and 22 deletions

View file

@ -562,17 +562,22 @@ Example:
default_cobble.png^[crack:10:1
#### `[combine:<w>x<h>:<x1>,<y1>=<file1>:<x2>,<y2>=<file2>:...`
* `<w>`: width
* `<h>`: height
* `<x>`: x position, negative numbers allowed
* `<y>`: y position, negative numbers allowed
* `<file>`: texture to combine
#### `[combine:<w>x<h>:<x1>,<y1>,<w1>=<file1>:<x2>,<y2>,<w2>=<file2>:...`
Creates a texture of size `<w>` times `<h>` and blits the listed files to their
specified coordinates.
Note: the output texture size may vary if one or more of `<wN>` is provided.
The aspect ratio is preserved.
* `<xN>`: X offset of insertion, negative numbers allowed
* `<yN>`: Y offset of insertion, negative numbers allowed
* `<wN>` (optional): Expected texture width
* Scales the output image if `<wN> != input texture width` to perform lossless blit.
* Supported since protocol version 46.
* `<file>`: texture to combine
Example:
[combine:16x32:0,0=default_cobble.png:0,16=default_wood.png

View file

@ -1118,21 +1118,78 @@ bool ImageSource::generateImagePart(std::string_view part_of_name,
{
Strfnd sf(part_of_name);
sf.next(":");
// grid size
u32 w0 = stoi(sf.next("x"));
u32 h0 = stoi(sf.next(":"));
struct ImagePart {
v2s32 offset;
std::string filename;
video::IImage *img = nullptr;
int expected_width = 0;
~ImagePart()
{
if (img)
img->drop();
}
};
std::list<ImagePart> image_parts;
// fixed point precision to allow textures smaller than the grid size
constexpr int FX_FACTOR = 1024;
// By how much to scale (w0, h0) for the resulting image
u32 scale = 0; // includes FX_FACTOR
while (!sf.at_end()) {
// X,Y(,W)=image_esc(:X,Y...)
auto &it = image_parts.emplace_back();
auto parts = str_split(sf.next("="), ',');
if (parts.size() >= 1)
it.offset.X = stoi(parts[0]);
if (parts.size() >= 2)
it.offset.Y = stoi(parts[1]);
if (parts.size() >= 3)
it.expected_width = stoi(parts[2]);
it.filename = unescape_string(sf.next_esc(":", escape), escape);
it.img = generateImage(it.filename, source_image_names);
if (!it.img) {
errorstream << "generateImagePart(): Failed to load image \""
<< it.filename << "\" for [combine" << std::endl;
image_parts.pop_back();
continue;
}
const auto dim = it.img->getDimension();
if (it.expected_width <= 0) {
// Parameter not specified -> do not scale
it.expected_width = dim.Width;
}
scale = std::max<u32>(scale, FX_FACTOR * dim.Width / it.expected_width);
}
if (!baseimg) {
if (scale > 0) {
w0 = w0 * scale / FX_FACTOR;
h0 = h0 * scale / FX_FACTOR;
}
CHECK_DIM(w0, h0);
// create desired
baseimg = driver->createImage(video::ECF_A8R8G8B8, {w0, h0});
baseimg->fill(video::SColor(0,0,0,0));
}
while (!sf.at_end()) {
v2s32 pos_base;
pos_base.X = stoi(sf.next(","));
pos_base.Y = stoi(sf.next("="));
std::string filename = unescape_string(sf.next_esc(":", escape), escape);
const auto basedim = baseimg->getDimension();
for (ImagePart &it : image_parts) {
// Shift insertion offset by the same factor as we scaled `baseimg`
const v2s32 pos_base = it.offset * scale / FX_FACTOR;
const std::string &filename = it.filename;
auto basedim = baseimg->getDimension();
if (pos_base.X > (s32)basedim.Width || pos_base.Y > (s32)basedim.Height) {
warningstream << "generateImagePart(): Skipping \""
<< filename << "\" as it's out-of-bounds " << pos_base
@ -1142,23 +1199,28 @@ bool ImageSource::generateImagePart(std::string_view part_of_name,
infostream << "Adding \"" << filename<< "\" to combined "
<< pos_base << std::endl;
video::IImage *img = generateImage(filename, source_image_names);
if (!img) {
errorstream << "generateImagePart(): Failed to load image \""
<< filename << "\" for [combine" << std::endl;
continue;
auto dim = it.img->getDimension();
u32 wanted_width = it.expected_width * scale / FX_FACTOR;
if (dim.Width != wanted_width) {
// needs resize
video::IImage *newimg = driver->createImage(
baseimg->getColorFormat(),
{ wanted_width, (dim.Height * wanted_width) / dim.Width }
);
it.img->copyToScaling(newimg);
it.img->drop();
it.img = newimg;
dim = it.img->getDimension();
}
const auto dim = img->getDimension();
if (pos_base.X + dim.Width <= 0 || pos_base.Y + dim.Height <= 0) {
warningstream << "generateImagePart(): Skipping \""
<< filename << "\" as it's out-of-bounds " << pos_base
<< " for [combine" << std::endl;
img->drop();
continue;
}
blit_with_alpha(img, baseimg, pos_base, dim);
img->drop();
blit_with_alpha(it.img, baseimg, pos_base, dim);
}
}
/*

View file

@ -61,6 +61,7 @@
[scheduled bump for 5.10.0]
PROTOCOL VERSION 47
Add particle blend mode "clip"
"[combine:WxH:x1,y1,w1=" 3rd parameter extension
[scheduled bump for 5.11.0]
*/