From f13144a15d83d6495478888c71abb5eb2b0986c6 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Thu, 29 Aug 2024 23:14:16 +0200 Subject: [PATCH 1/2] 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. --- doc/lua_api.md | 19 ++++--- src/client/imagesource.cpp | 92 +++++++++++++++++++++++++++------ src/network/networkprotocol.cpp | 1 + 3 files changed, 90 insertions(+), 22 deletions(-) diff --git a/doc/lua_api.md b/doc/lua_api.md index 709bf2984..77e030fcd 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -562,17 +562,22 @@ Example: default_cobble.png^[crack:10:1 -#### `[combine:x:,=:,=:...` - -* ``: width -* ``: height -* ``: x position, negative numbers allowed -* ``: y position, negative numbers allowed -* ``: texture to combine +#### `[combine:x:,,=:,,=:...` Creates a texture of size `` times `` and blits the listed files to their specified coordinates. +Note: the output texture size may vary if one or more of `` is provided. +The aspect ratio is preserved. + +* ``: X offset of insertion, negative numbers allowed +* ``: Y offset of insertion, negative numbers allowed +* `` (optional): Expected texture width + * Scales the output image if ` != input texture width` to perform lossless blit. + * Supported since protocol version 46. +* ``: texture to combine + + Example: [combine:16x32:0,0=default_cobble.png:0,16=default_wood.png diff --git a/src/client/imagesource.cpp b/src/client/imagesource.cpp index a14dac290..1ba798149 100644 --- a/src/client/imagesource.cpp +++ b/src/client/imagesource.cpp @@ -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 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(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); } } /* diff --git a/src/network/networkprotocol.cpp b/src/network/networkprotocol.cpp index f77e85d3c..c7a8b58f6 100644 --- a/src/network/networkprotocol.cpp +++ b/src/network/networkprotocol.cpp @@ -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] */ From 3128beba7faf124e7134b846f200bfba1db5acbd Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Thu, 28 Nov 2024 18:54:32 +0100 Subject: [PATCH 2/2] attempt to improve docs --- doc/lua_api.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/lua_api.md b/doc/lua_api.md index 77e030fcd..faa051eb0 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -573,8 +573,11 @@ The aspect ratio is preserved. * ``: X offset of insertion, negative numbers allowed * ``: Y offset of insertion, negative numbers allowed * `` (optional): Expected texture width - * Scales the output image if ` != input texture width` to perform lossless blit. - * Supported since protocol version 46. + * If the provided texture width is (e.g. 2 times) larger than ``, it will + result in an output texture that is also (2 times) larger. + * If the provided texture width is smaller, it will be upscaled proportionally + to match the width ``. + * Supported since protocol version 47. Old clients will ignore this parameter. * ``: texture to combine