From 1e4fb80d46da634f04d84e3f1065aaabc6909468 Mon Sep 17 00:00:00 2001 From: Aaron Suen Date: Sat, 7 Mar 2015 19:52:59 -0500 Subject: [PATCH] Configurable automatic texture scaling and filtering at load time. Signed off by: Zeno, kwolekr --- minetest.conf.example | 13 +++++++ src/client/tile.cpp | 83 +++++++++++++++++++++++++++++++++++++++++ src/defaultsettings.cpp | 2 + 3 files changed, 98 insertions(+) diff --git a/minetest.conf.example b/minetest.conf.example index 5e8637897..2c6f5e861 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -193,6 +193,19 @@ #anisotropic_filter = false #bilinear_filter = false #trilinear_filter = false +# Filtered textures can blend RGB values with fully-transparent neighbors, +# which PNG optimizers usually discard, sometimes resulting in a dark or +# light edge to transparent textures. Apply this filter to clean that up +# at texture load time. +#texture_clean_transparent = true +# When using bilinear/trilinear/anisotropic filters, low-resolution textures +# can be blurred, so automatically upscale them with nearest-neighbor +# interpolation to preserve crisp pixels. This sets the minimum texture size +# for the upscaled textures; higher values look sharper, but require more +# memory. Powers of 2 are recommended. Setting this higher than 1 may not +# have a visible effect unless bilinear/trilinear/anisotropic filtering is +# enabled. +#texture_min_size = 16 # Set to true to pre-generate all item visuals #preload_item_visuals = false # Set to true to enable shaders. Disable them if video_driver = direct3d9/8. diff --git a/src/client/tile.cpp b/src/client/tile.cpp index 541247fa8..078e62741 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -223,6 +223,89 @@ public: } } + /* Apply the "clean transparent" filter to textures, removing borders on transparent textures. + * PNG optimizers discard RGB values of fully-transparent pixels, but filters may expose the + * replacement colors at borders by blending to them; this filter compensates for that by + * filling in those RGB values from nearby pixels. + */ + if (g_settings->getBool("texture_clean_transparent")) { + const core::dimension2d dim = toadd->getDimension(); + + // Walk each pixel looking for ones that will show as transparent. + for (u32 ctrx = 0; ctrx < dim.Width; ctrx++) + for (u32 ctry = 0; ctry < dim.Height; ctry++) { + irr::video::SColor c = toadd->getPixel(ctrx, ctry); + if (c.getAlpha() > 127) + continue; + + // Sample size and total weighted r, g, b values. + u32 ss = 0, sr = 0, sg = 0, sb = 0; + + // Walk each neighbor pixel (clipped to image bounds). + for (u32 sx = (ctrx < 1) ? 0 : (ctrx - 1); + sx <= (ctrx + 1) && sx < dim.Width; sx++) + for (u32 sy = (ctry < 1) ? 0 : (ctry - 1); + sy <= (ctry + 1) && sy < dim.Height; sy++) { + + // Ignore the center pixel (its RGB is already + // presumed meaningless). + if ((sx == ctrx) && (sy == ctry)) + continue; + + // Ignore other nearby pixels that would be + // transparent upon display. + irr::video::SColor d = toadd->getPixel(sx, sy); + if(d.getAlpha() < 128) + continue; + + // Add one weighted sample. + ss++; + sr += d.getRed(); + sg += d.getGreen(); + sb += d.getBlue(); + } + + // If we found any neighbor RGB data, set pixel to average + // weighted by alpha. + if (ss > 0) { + c.setRed(sr / ss); + c.setGreen(sg / ss); + c.setBlue(sb / ss); + toadd->setPixel(ctrx, ctry, c); + } + } + } + + /* Upscale textures to user's requested minimum size. This is a trick to make + * filters look as good on low-res textures as on high-res ones, by making + * low-res textures BECOME high-res ones. This is helpful for worlds that + * mix high- and low-res textures, or for mods with least-common-denominator + * textures that don't have the resources to offer high-res alternatives. + */ + s32 scaleto = g_settings->getS32("texture_min_size"); + if (scaleto > 0) { + + /* Calculate scaling needed to make the shortest texture dimension + * equal to the target minimum. If e.g. this is a vertical frames + * animation, the short dimension will be the real size. + */ + const core::dimension2d dim = toadd->getDimension(); + u32 xscale = scaleto / dim.Width; + u32 yscale = scaleto / dim.Height; + u32 scale = (xscale > yscale) ? xscale : yscale; + + // Never downscale; only scale up by 2x or more. + if (scale > 1) { + u32 w = scale * dim.Width; + u32 h = scale * dim.Height; + const core::dimension2d newdim = core::dimension2d(w, h); + video::IImage *newimg = driver->createImage( + toadd->getColorFormat(), newdim); + toadd->copyToScaling(newimg); + toadd = newimg; + } + } + if (need_to_grab) toadd->grab(); m_images[name] = toadd; diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index f49fbb008..ff2d148aa 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -149,6 +149,8 @@ void set_default_settings(Settings *settings) settings->setDefault("anisotropic_filter", "false"); settings->setDefault("bilinear_filter", "false"); settings->setDefault("trilinear_filter", "false"); + settings->setDefault("texture_clean_transparent", "true"); + settings->setDefault("texture_min_size", "16"); settings->setDefault("preload_item_visuals", "false"); settings->setDefault("enable_bumpmapping", "false"); settings->setDefault("enable_parallax_occlusion", "false");