Handle some edge cases in tile images

This commit is contained in:
sfan5 2024-01-26 15:22:27 +01:00
parent a46fe79939
commit 8927e7caf6

@ -703,7 +703,7 @@ video::ITexture* TextureSource::getTextureForMesh(const std::string &name, u32 *
const bool filter_needed = const bool filter_needed =
m_setting_mipmap || m_setting_trilinear_filter || m_setting_mipmap || m_setting_trilinear_filter ||
m_setting_bilinear_filter || m_setting_anisotropic_filter; m_setting_bilinear_filter || m_setting_anisotropic_filter;
if (filter_needed) if (filter_needed && !name.empty())
return getTexture(name + "^[applyfiltersformesh", id); return getTexture(name + "^[applyfiltersformesh", id);
return getTexture(name, id); return getTexture(name, id);
} }
@ -1062,6 +1062,12 @@ video::IImage* TextureSource::generateImage(const std::string &name, std::set<st
if (baseimg == NULL) { if (baseimg == NULL) {
errorstream << "generateImage(): baseimg is NULL (attempted to" errorstream << "generateImage(): baseimg is NULL (attempted to"
" create texture \"" << name << "\")" << std::endl; " create texture \"" << name << "\")" << std::endl;
} else if (baseimg->getDimension().Width == 0 ||
baseimg->getDimension().Height == 0) {
errorstream << "generateImage(): zero-sized image was created?! "
"(attempted to create texture \"" << name << "\")" << std::endl;
baseimg->drop();
baseimg = nullptr;
} }
return baseimg; return baseimg;
@ -1173,20 +1179,25 @@ void blitBaseImage(video::IImage* &src, video::IImage* &dst)
#define CHECK_BASEIMG() \ #define CHECK_BASEIMG() \
do { \ do { \
if (!baseimg) { \ if (!baseimg) { \
errorstream << "generateImagePart(): baseimg == NULL " \ errorstream << "generateImagePart(): baseimg == NULL" \
<< "for part_of_name=\"" << part_of_name \ << " for part_of_name=\"" << part_of_name \
<< "\", cancelling." << std::endl; \ << "\", cancelling." << std::endl; \
return false; \ return false; \
} \ } \
} while(0) } while(0)
#define COMPLAIN_INVALID(description) \
do { \
errorstream << "generateImagePart(): invalid " << (description) \
<< " for part_of_name=\"" << part_of_name \
<< "\", cancelling." << std::endl; \
return false; \
} while(0)
#define CHECK_DIM(w, h) \ #define CHECK_DIM(w, h) \
do { \ do { \
if ((w) <= 0 || (h) <= 0 || (w) >= 0xffff || (h) >= 0xffff) { \ if ((w) <= 0 || (h) <= 0 || (w) >= 0xffff || (h) >= 0xffff) { \
errorstream << "generateImagePart(): invalid width or height " \ COMPLAIN_INVALID("width or height"); \
<< "for part_of_name=\"" << part_of_name \
<< "\", cancelling." << std::endl; \
return false; \
} \ } \
} while(0) } while(0)
@ -1197,26 +1208,34 @@ bool TextureSource::generateImagePart(std::string part_of_name,
video::IVideoDriver *driver = RenderingEngine::get_video_driver(); video::IVideoDriver *driver = RenderingEngine::get_video_driver();
sanity_check(driver); sanity_check(driver);
if (baseimg && (baseimg->getDimension().Width == 0 ||
baseimg->getDimension().Height == 0)) {
errorstream << "generateImagePart(): baseimg is zero-sized?!"
<< std::endl;
baseimg->drop();
baseimg = nullptr;
}
// Stuff starting with [ are special commands // Stuff starting with [ are special commands
if (part_of_name.empty() || part_of_name[0] != '[') { if (part_of_name.empty() || part_of_name[0] != '[') {
source_image_names.insert(part_of_name); source_image_names.insert(part_of_name);
video::IImage *image = m_sourcecache.getOrLoad(part_of_name); video::IImage *image = m_sourcecache.getOrLoad(part_of_name);
if (image == NULL) { if (!image) {
if (!part_of_name.empty()) { // Do not create the dummy texture
if (part_of_name.empty())
return true;
// Do not create normalmap dummies // Do not create normalmap dummies
if (part_of_name.find("_normal.png") != std::string::npos) { if (str_ends_with(part_of_name, "_normal.png")) {
warningstream << "generateImage(): Could not load normal map \"" warningstream << "generateImagePart(): Could not load normal map \""
<< part_of_name << "\"" << std::endl; << part_of_name << "\"" << std::endl;
return true; return true;
} }
errorstream << "generateImage(): Could not load image \"" errorstream << "generateImagePart(): Could not load image \""
<< part_of_name << "\" while building texture; " << part_of_name << "\" while building texture; "
"Creating a dummy image" << std::endl; "Creating a dummy image" << std::endl;
}
// Just create a dummy image
core::dimension2d<u32> dim(1,1); core::dimension2d<u32> dim(1,1);
image = driver->createImage(video::ECF_A8R8G8B8, dim); image = driver->createImage(video::ECF_A8R8G8B8, dim);
sanity_check(image != NULL); sanity_check(image != NULL);
@ -1314,9 +1333,13 @@ bool TextureSource::generateImagePart(std::string part_of_name,
u32 x = stoi(sf.next(",")); u32 x = stoi(sf.next(","));
u32 y = stoi(sf.next("=")); u32 y = stoi(sf.next("="));
std::string filename = unescape_string(sf.next_esc(":", escape), escape); std::string filename = unescape_string(sf.next_esc(":", escape), escape);
if (x >= w0 || y >= h0)
COMPLAIN_INVALID("X or Y offset");
infostream<<"Adding \""<<filename infostream<<"Adding \""<<filename
<<"\" to combined ("<<x<<","<<y<<")" <<"\" to combined ("<<x<<","<<y<<")"
<<std::endl; <<std::endl;
video::IImage *img = generateImage(filename, source_image_names); video::IImage *img = generateImage(filename, source_image_names);
if (img) { if (img) {
core::dimension2d<u32> dim = img->getDimension(); core::dimension2d<u32> dim = img->getDimension();
@ -1341,8 +1364,8 @@ bool TextureSource::generateImagePart(std::string part_of_name,
*/ */
else if (str_starts_with(part_of_name, "[fill")) else if (str_starts_with(part_of_name, "[fill"))
{ {
s32 x = 0; u32 x = 0;
s32 y = 0; u32 y = 0;
Strfnd sf(part_of_name); Strfnd sf(part_of_name);
sf.next(":"); sf.next(":");
@ -1362,6 +1385,12 @@ bool TextureSource::generateImagePart(std::string part_of_name,
core::dimension2d<u32> dim(width, height); core::dimension2d<u32> dim(width, height);
CHECK_DIM(dim.Width, dim.Height); CHECK_DIM(dim.Width, dim.Height);
if (baseimg) {
auto basedim = baseimg->getDimension();
if (x >= basedim.Width || y >= basedim.Height)
COMPLAIN_INVALID("X or Y offset");
}
video::IImage *img = driver->createImage(video::ECF_A8R8G8B8, dim); video::IImage *img = driver->createImage(video::ECF_A8R8G8B8, dim);
img->fill(color); img->fill(color);
@ -1497,8 +1526,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
errorstream << "generateImagePart(): Failed to create textures" errorstream << "generateImagePart(): Failed to create textures"
<< " for inventorycube \"" << part_of_name << "\"" << " for inventorycube \"" << part_of_name << "\""
<< std::endl; << std::endl;
baseimg = generateImage(imagename_top, source_image_names); return false;
return true;
} }
baseimg = createInventoryCubeImage(img_top, img_left, img_right); baseimg = createInventoryCubeImage(img_top, img_left, img_right);
@ -1518,30 +1546,26 @@ bool TextureSource::generateImagePart(std::string part_of_name,
{ {
Strfnd sf(part_of_name); Strfnd sf(part_of_name);
sf.next(":"); sf.next(":");
u32 percent = stoi(sf.next(":")); u32 percent = stoi(sf.next(":"), 0, 100);
std::string filename = unescape_string(sf.next_esc(":", escape), escape); 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 = generateImage(filename, source_image_names); video::IImage *img = generateImage(filename, source_image_names);
if (img) if (img) {
{
core::dimension2d<u32> dim = img->getDimension(); core::dimension2d<u32> dim = img->getDimension();
if (!baseimg)
baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
core::position2d<s32> pos_base(0, 0); core::position2d<s32> pos_base(0, 0);
video::IImage *img2 =
driver->createImage(video::ECF_A8R8G8B8, dim);
img->copyTo(img2);
img->drop();
core::position2d<s32> clippos(0, 0); core::position2d<s32> clippos(0, 0);
clippos.Y = dim.Height * (100-percent) / 100; clippos.Y = dim.Height * (100-percent) / 100;
core::dimension2d<u32> clipdim = dim; core::dimension2d<u32> clipdim = dim;
clipdim.Height = clipdim.Height * percent / 100 + 1; clipdim.Height = clipdim.Height * percent / 100 + 1;
core::rect<s32> cliprect(clippos, clipdim); core::rect<s32> cliprect(clippos, clipdim);
img2->copyToWithAlpha(baseimg, pos_base, img->copyToWithAlpha(baseimg, pos_base,
core::rect<s32>(v2s32(0,0), dim), core::rect<s32>(v2s32(0,0), dim),
video::SColor(255,255,255,255), video::SColor(255,255,255,255),
&cliprect); &cliprect);
img2->drop(); img->drop();
} }
} }
/* /*
@ -1564,6 +1588,8 @@ bool TextureSource::generateImagePart(std::string part_of_name,
<< "\", using frame_count = 1 instead." << std::endl; << "\", using frame_count = 1 instead." << std::endl;
frame_count = 1; frame_count = 1;
} }
if (frame_index >= frame_count)
frame_index = frame_count - 1;
v2u32 frame_size = baseimg->getDimension(); v2u32 frame_size = baseimg->getDimension();
frame_size.Y /= frame_count; frame_size.Y /= frame_count;
@ -1603,8 +1629,8 @@ bool TextureSource::generateImagePart(std::string part_of_name,
img->getDimension()); img->getDimension());
img->drop(); img->drop();
} else { } else {
errorstream << "generateImage(): Failed to load \"" errorstream << "generateImagePart(): Failed to load image \""
<< filename << "\"."; << filename << "\" for [mask" << std::endl;
} }
} }
/* /*
@ -1696,7 +1722,6 @@ bool TextureSource::generateImagePart(std::string part_of_name,
* equal to the target minimum. If e.g. this is a vertical frames * equal to the target minimum. If e.g. this is a vertical frames
* animation, the short dimension will be the real size. * animation, the short dimension will be the real size.
*/ */
CHECK_DIM(dim.Width, dim.Height);
u32 xscale = scaleto / dim.Width; u32 xscale = scaleto / dim.Width;
u32 yscale = scaleto / dim.Height; u32 yscale = scaleto / dim.Height;
const s32 scale = std::max(xscale, yscale); const s32 scale = std::max(xscale, yscale);
@ -1728,7 +1753,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
u32 height = stoi(sf.next("")); u32 height = stoi(sf.next(""));
CHECK_DIM(width, height); CHECK_DIM(width, height);
video::IImage *image = RenderingEngine::get_video_driver()-> video::IImage *image = driver->
createImage(video::ECF_A8R8G8B8, {width, height}); createImage(video::ECF_A8R8G8B8, {width, height});
baseimg->copyToScaling(image); baseimg->copyToScaling(image);
baseimg->drop(); baseimg->drop();
@ -1810,14 +1835,20 @@ bool TextureSource::generateImagePart(std::string part_of_name,
u32 y0 = stoi(sf.next(":")); u32 y0 = stoi(sf.next(":"));
CHECK_DIM(w0, h0); CHECK_DIM(w0, h0);
if (x0 >= w0 || y0 >= h0)
COMPLAIN_INVALID("tile position (X,Y)");
core::dimension2d<u32> img_dim = baseimg->getDimension(); core::dimension2d<u32> img_dim = baseimg->getDimension();
core::dimension2d<u32> tile_dim(v2u32(img_dim) / v2u32(w0, h0)); core::dimension2d<u32> tile_dim(v2u32(img_dim) / v2u32(w0, h0));
if (tile_dim.Width == 0)
tile_dim.Width = 1;
if (tile_dim.Height == 0)
tile_dim.Height = 1;
video::IImage *img = driver->createImage( video::IImage *img = driver->createImage(
video::ECF_A8R8G8B8, tile_dim); video::ECF_A8R8G8B8, tile_dim);
img->fill(video::SColor(0,0,0,0)); img->fill(video::SColor(0,0,0,0));
v2u32 vdim(tile_dim); v2u32 vdim(tile_dim);
core::rect<s32> rect(v2s32(x0 * vdim.X, y0 * vdim.Y), tile_dim); core::rect<s32> rect(v2s32(x0 * vdim.X, y0 * vdim.Y), tile_dim);
baseimg->copyToWithAlpha(img, v2s32(0), rect, baseimg->copyToWithAlpha(img, v2s32(0), rect,
@ -1834,15 +1865,12 @@ bool TextureSource::generateImagePart(std::string part_of_name,
to produce a valid string. to produce a valid string.
*/ */
else if (str_starts_with(part_of_name, "[png:")) { else if (str_starts_with(part_of_name, "[png:")) {
Strfnd sf(part_of_name);
sf.next(":");
std::string png; std::string png;
{ {
std::string blob = sf.next(""); std::string blob = part_of_name.substr(5);
if (!base64_is_valid(blob)) { if (!base64_is_valid(blob)) {
errorstream << "generateImagePart(): " errorstream << "generateImagePart(): "
<< "malformed base64 in '[png'" << "malformed base64 in [png" << std::endl;
<< std::endl;
return false; return false;
} }
png = base64_decode(blob); png = base64_decode(blob);
@ -1939,8 +1967,8 @@ bool TextureSource::generateImagePart(std::string part_of_name,
img->getDimension(), hardlight); img->getDimension(), hardlight);
img->drop(); img->drop();
} else { } else {
errorstream << "generateImage(): Failed to load \"" errorstream << "generateImage(): Failed to load image \""
<< filename << "\"."; << filename << "\" for [overlay or [hardlight" << std::endl;
} }
} }
/* /*
@ -1974,6 +2002,12 @@ bool TextureSource::generateImagePart(std::string part_of_name,
return true; return true;
} }
#undef CHECK_BASEIMG
#undef COMPLAIN_INVALID
#undef CHECK_DIM
/* /*
Calculate the color of a single pixel drawn on top of another pixel. Calculate the color of a single pixel drawn on top of another pixel.