Skip to content

Commit 1e4fb80

Browse files
Warr1024kwolekr
authored andcommittedMar 21, 2015
Configurable automatic texture scaling and filtering at load time.
Signed off by: Zeno, kwolekr
1 parent 26153ba commit 1e4fb80

File tree

3 files changed

+98
-0
lines changed

3 files changed

+98
-0
lines changed
 

‎minetest.conf.example

+13
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,19 @@
193193
#anisotropic_filter = false
194194
#bilinear_filter = false
195195
#trilinear_filter = false
196+
# Filtered textures can blend RGB values with fully-transparent neighbors,
197+
# which PNG optimizers usually discard, sometimes resulting in a dark or
198+
# light edge to transparent textures. Apply this filter to clean that up
199+
# at texture load time.
200+
#texture_clean_transparent = true
201+
# When using bilinear/trilinear/anisotropic filters, low-resolution textures
202+
# can be blurred, so automatically upscale them with nearest-neighbor
203+
# interpolation to preserve crisp pixels. This sets the minimum texture size
204+
# for the upscaled textures; higher values look sharper, but require more
205+
# memory. Powers of 2 are recommended. Setting this higher than 1 may not
206+
# have a visible effect unless bilinear/trilinear/anisotropic filtering is
207+
# enabled.
208+
#texture_min_size = 16
196209
# Set to true to pre-generate all item visuals
197210
#preload_item_visuals = false
198211
# Set to true to enable shaders. Disable them if video_driver = direct3d9/8.

‎src/client/tile.cpp

+83
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,89 @@ class SourceImageCache
223223
}
224224
}
225225

226+
/* Apply the "clean transparent" filter to textures, removing borders on transparent textures.
227+
* PNG optimizers discard RGB values of fully-transparent pixels, but filters may expose the
228+
* replacement colors at borders by blending to them; this filter compensates for that by
229+
* filling in those RGB values from nearby pixels.
230+
*/
231+
if (g_settings->getBool("texture_clean_transparent")) {
232+
const core::dimension2d<u32> dim = toadd->getDimension();
233+
234+
// Walk each pixel looking for ones that will show as transparent.
235+
for (u32 ctrx = 0; ctrx < dim.Width; ctrx++)
236+
for (u32 ctry = 0; ctry < dim.Height; ctry++) {
237+
irr::video::SColor c = toadd->getPixel(ctrx, ctry);
238+
if (c.getAlpha() > 127)
239+
continue;
240+
241+
// Sample size and total weighted r, g, b values.
242+
u32 ss = 0, sr = 0, sg = 0, sb = 0;
243+
244+
// Walk each neighbor pixel (clipped to image bounds).
245+
for (u32 sx = (ctrx < 1) ? 0 : (ctrx - 1);
246+
sx <= (ctrx + 1) && sx < dim.Width; sx++)
247+
for (u32 sy = (ctry < 1) ? 0 : (ctry - 1);
248+
sy <= (ctry + 1) && sy < dim.Height; sy++) {
249+
250+
// Ignore the center pixel (its RGB is already
251+
// presumed meaningless).
252+
if ((sx == ctrx) && (sy == ctry))
253+
continue;
254+
255+
// Ignore other nearby pixels that would be
256+
// transparent upon display.
257+
irr::video::SColor d = toadd->getPixel(sx, sy);
258+
if(d.getAlpha() < 128)
259+
continue;
260+
261+
// Add one weighted sample.
262+
ss++;
263+
sr += d.getRed();
264+
sg += d.getGreen();
265+
sb += d.getBlue();
266+
}
267+
268+
// If we found any neighbor RGB data, set pixel to average
269+
// weighted by alpha.
270+
if (ss > 0) {
271+
c.setRed(sr / ss);
272+
c.setGreen(sg / ss);
273+
c.setBlue(sb / ss);
274+
toadd->setPixel(ctrx, ctry, c);
275+
}
276+
}
277+
}
278+
279+
/* Upscale textures to user's requested minimum size. This is a trick to make
280+
* filters look as good on low-res textures as on high-res ones, by making
281+
* low-res textures BECOME high-res ones. This is helpful for worlds that
282+
* mix high- and low-res textures, or for mods with least-common-denominator
283+
* textures that don't have the resources to offer high-res alternatives.
284+
*/
285+
s32 scaleto = g_settings->getS32("texture_min_size");
286+
if (scaleto > 0) {
287+
288+
/* Calculate scaling needed to make the shortest texture dimension
289+
* equal to the target minimum. If e.g. this is a vertical frames
290+
* animation, the short dimension will be the real size.
291+
*/
292+
const core::dimension2d<u32> dim = toadd->getDimension();
293+
u32 xscale = scaleto / dim.Width;
294+
u32 yscale = scaleto / dim.Height;
295+
u32 scale = (xscale > yscale) ? xscale : yscale;
296+
297+
// Never downscale; only scale up by 2x or more.
298+
if (scale > 1) {
299+
u32 w = scale * dim.Width;
300+
u32 h = scale * dim.Height;
301+
const core::dimension2d<u32> newdim = core::dimension2d<u32>(w, h);
302+
video::IImage *newimg = driver->createImage(
303+
toadd->getColorFormat(), newdim);
304+
toadd->copyToScaling(newimg);
305+
toadd = newimg;
306+
}
307+
}
308+
226309
if (need_to_grab)
227310
toadd->grab();
228311
m_images[name] = toadd;

‎src/defaultsettings.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ void set_default_settings(Settings *settings)
149149
settings->setDefault("anisotropic_filter", "false");
150150
settings->setDefault("bilinear_filter", "false");
151151
settings->setDefault("trilinear_filter", "false");
152+
settings->setDefault("texture_clean_transparent", "true");
153+
settings->setDefault("texture_min_size", "16");
152154
settings->setDefault("preload_item_visuals", "false");
153155
settings->setDefault("enable_bumpmapping", "false");
154156
settings->setDefault("enable_parallax_occlusion", "false");

0 commit comments

Comments
 (0)