forked from Mirrorlandia_minetest/minetest
Add embedded PNG texture modifier (#11498)
This commit is contained in:
parent
fe5cb2cdfb
commit
02292e03e4
@ -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
|
color, meaning white surfaces get a lot of your new color while black parts
|
||||||
don't change very much.
|
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
|
Hardware coloring
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
@ -102,12 +102,22 @@ local function gen_checkers(w, h, tile)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local fractal = mandelbrot(512, 512, 128)
|
local fractal = mandelbrot(512, 512, 128)
|
||||||
|
local frac_emb = mandelbrot(64, 64, 64)
|
||||||
local checker = gen_checkers(512, 512, 32)
|
local checker = gen_checkers(512, 512, 32)
|
||||||
|
|
||||||
local floor = math.floor
|
local floor = math.floor
|
||||||
local abs = math.abs
|
local abs = math.abs
|
||||||
|
local data_emb = {}
|
||||||
local data_mb = {}
|
local data_mb = {}
|
||||||
local data_ck = {}
|
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
|
for i=1, #fractal do
|
||||||
data_mb[i] = {
|
data_mb[i] = {
|
||||||
r = floor(fractal[i] * 255),
|
r = floor(fractal[i] * 255),
|
||||||
@ -140,3 +150,24 @@ minetest.register_node("testnodes:generated_png_ck", {
|
|||||||
|
|
||||||
groups = { dig_immediate = 2 },
|
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 },
|
||||||
|
})
|
||||||
|
@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "imagefilters.h"
|
#include "imagefilters.h"
|
||||||
#include "guiscalingfilter.h"
|
#include "guiscalingfilter.h"
|
||||||
#include "renderingengine.h"
|
#include "renderingengine.h"
|
||||||
|
#include "util/base64.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
A cache from texture name to texture path
|
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;
|
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,
|
bool TextureSource::generateImagePart(std::string part_of_name,
|
||||||
video::IImage *& baseimg)
|
video::IImage *& baseimg)
|
||||||
{
|
{
|
||||||
@ -1122,41 +1162,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
|
|||||||
// Else blit on base.
|
// Else blit on base.
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//infostream<<"Blitting "<<part_of_name<<" on base"<<std::endl;
|
blitBaseImage(image, baseimg);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
//cleanup
|
//cleanup
|
||||||
image->drop();
|
image->drop();
|
||||||
@ -1784,6 +1790,43 @@ bool TextureSource::generateImagePart(std::string part_of_name,
|
|||||||
baseimg->drop();
|
baseimg->drop();
|
||||||
baseimg = img;
|
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
|
else
|
||||||
{
|
{
|
||||||
errorstream << "generateImagePart(): Invalid "
|
errorstream << "generateImagePart(): Invalid "
|
||||||
|
Loading…
Reference in New Issue
Block a user