Add embedded PNG texture modifier (#11498)

This commit is contained in:
hecks 2021-10-13 17:51:37 +02:00 committed by GitHub
parent fe5cb2cdfb
commit 02292e03e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 126 additions and 35 deletions

View File

@ -628,6 +628,23 @@ Result is more like what you'd expect if you put a color on top of another
color, meaning white surfaces get a lot of your new color while black parts
don't change very much.
#### `[png:<base64>`
Embed a base64 encoded PNG image in the texture string.
You can produce a valid string for this by calling
`minetest.encode_base64(minetest.encode_png(tex))`,
refer to the documentation of these functions for details.
You can use this to send disposable images such as captchas
to individual clients, or render things that would be too
expensive to compose with `[combine:`.
IMPORTANT: Avoid sending large images this way.
This is not a replacement for asset files, do not use it to do anything
that you could instead achieve by just using a file.
In particular consider `minetest.dynamic_add_media` and test whether
using other texture modifiers could result in a shorter string than
embedding a whole image, this may vary by use case.
Hardware coloring
-----------------

View File

@ -102,12 +102,22 @@ local function gen_checkers(w, h, tile)
end
local fractal = mandelbrot(512, 512, 128)
local frac_emb = mandelbrot(64, 64, 64)
local checker = gen_checkers(512, 512, 32)
local floor = math.floor
local abs = math.abs
local data_emb = {}
local data_mb = {}
local data_ck = {}
for i=1, #frac_emb do
data_emb[i] = {
r = floor(abs(frac_emb[i] * 2 - 1) * 255),
g = floor(abs(1 - frac_emb[i]) * 255),
b = floor(frac_emb[i] * 255),
a = frac_emb[i] < 0.95 and 255 or 0,
}
end
for i=1, #fractal do
data_mb[i] = {
r = floor(fractal[i] * 255),
@ -140,3 +150,24 @@ minetest.register_node("testnodes:generated_png_ck", {
groups = { dig_immediate = 2 },
})
local png_emb = "[png:" .. minetest.encode_base64(minetest.encode_png(64,64,data_emb))
minetest.register_node("testnodes:generated_png_emb", {
description = S("Generated In-Band Mandelbrot PNG Test Node"),
tiles = { png_emb },
groups = { dig_immediate = 2 },
})
minetest.register_node("testnodes:generated_png_src_emb", {
description = S("Generated In-Band Source Blit Mandelbrot PNG Test Node"),
tiles = { png_emb .. "^testnodes_damage_neg.png" },
groups = { dig_immediate = 2 },
})
minetest.register_node("testnodes:generated_png_dst_emb", {
description = S("Generated In-Band Dest Blit Mandelbrot PNG Test Node"),
tiles = { "testnodes_generated_ck.png^" .. png_emb },
groups = { dig_immediate = 2 },
})

View File

@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "imagefilters.h"
#include "guiscalingfilter.h"
#include "renderingengine.h"
#include "util/base64.h"
/*
A cache from texture name to texture path
@ -1059,6 +1060,45 @@ static std::string unescape_string(const std::string &str, const char esc = '\\'
return out;
}
void blitBaseImage(video::IImage* &src, video::IImage* &dst)
{
//infostream<<"Blitting "<<part_of_name<<" on base"<<std::endl;
// Size of the copied area
core::dimension2d<u32> dim = src->getDimension();
//core::dimension2d<u32> dim(16,16);
// Position to copy the blitted to in the base image
core::position2d<s32> pos_to(0,0);
// Position to copy the blitted from in the blitted image
core::position2d<s32> pos_from(0,0);
// Blit
/*image->copyToWithAlpha(baseimg, pos_to,
core::rect<s32>(pos_from, dim),
video::SColor(255,255,255,255),
NULL);*/
core::dimension2d<u32> dim_dst = dst->getDimension();
if (dim == dim_dst) {
blit_with_alpha(src, dst, pos_from, pos_to, dim);
} else if (dim.Width * dim.Height < dim_dst.Width * dim_dst.Height) {
// Upscale overlying image
video::IImage *scaled_image = RenderingEngine::get_video_driver()->
createImage(video::ECF_A8R8G8B8, dim_dst);
src->copyToScaling(scaled_image);
blit_with_alpha(scaled_image, dst, pos_from, pos_to, dim_dst);
scaled_image->drop();
} else {
// Upscale base image
video::IImage *scaled_base = RenderingEngine::get_video_driver()->
createImage(video::ECF_A8R8G8B8, dim);
dst->copyToScaling(scaled_base);
dst->drop();
dst = scaled_base;
blit_with_alpha(src, dst, pos_from, pos_to, dim);
}
}
bool TextureSource::generateImagePart(std::string part_of_name,
video::IImage *& baseimg)
{
@ -1122,41 +1162,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
// Else blit on base.
else
{
//infostream<<"Blitting "<<part_of_name<<" on base"<<std::endl;
// Size of the copied area
core::dimension2d<u32> dim = image->getDimension();
//core::dimension2d<u32> dim(16,16);
// Position to copy the blitted to in the base image
core::position2d<s32> pos_to(0,0);
// Position to copy the blitted from in the blitted image
core::position2d<s32> pos_from(0,0);
// Blit
/*image->copyToWithAlpha(baseimg, pos_to,
core::rect<s32>(pos_from, dim),
video::SColor(255,255,255,255),
NULL);*/
core::dimension2d<u32> dim_dst = baseimg->getDimension();
if (dim == dim_dst) {
blit_with_alpha(image, baseimg, pos_from, pos_to, dim);
} else if (dim.Width * dim.Height < dim_dst.Width * dim_dst.Height) {
// Upscale overlying image
video::IImage *scaled_image = RenderingEngine::get_video_driver()->
createImage(video::ECF_A8R8G8B8, dim_dst);
image->copyToScaling(scaled_image);
blit_with_alpha(scaled_image, baseimg, pos_from, pos_to, dim_dst);
scaled_image->drop();
} else {
// Upscale base image
video::IImage *scaled_base = RenderingEngine::get_video_driver()->
createImage(video::ECF_A8R8G8B8, dim);
baseimg->copyToScaling(scaled_base);
baseimg->drop();
baseimg = scaled_base;
blit_with_alpha(image, baseimg, pos_from, pos_to, dim);
}
blitBaseImage(image, baseimg);
}
//cleanup
image->drop();
@ -1784,6 +1790,43 @@ bool TextureSource::generateImagePart(std::string part_of_name,
baseimg->drop();
baseimg = img;
}
/*
[png:base64
Decodes a PNG image in base64 form.
Use minetest.encode_png and minetest.encode_base64
to produce a valid string.
*/
else if (str_starts_with(part_of_name, "[png:")) {
Strfnd sf(part_of_name);
sf.next(":");
std::string png;
{
std::string blob = sf.next("");
if (!base64_is_valid(blob)) {
errorstream << "generateImagePart(): "
<< "malformed base64 in '[png'"
<< std::endl;
return false;
}
png = base64_decode(blob);
}
auto *device = RenderingEngine::get_raw_device();
auto *fs = device->getFileSystem();
auto *vd = device->getVideoDriver();
auto *memfile = fs->createMemoryReadFile(png.data(), png.size(), "__temp_png");
video::IImage* pngimg = vd->createImageFromFile(memfile);
memfile->drop();
if (baseimg) {
blitBaseImage(pngimg, baseimg);
} else {
core::dimension2d<u32> dim = pngimg->getDimension();
baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
pngimg->copyTo(baseimg);
}
pngimg->drop();
}
else
{
errorstream << "generateImagePart(): Invalid "