Clean scaling pre-filter for formspec/HUD.
parent
b4247dff2e
commit
6d61375cc7
|
@ -143,9 +143,11 @@ LOCAL_SRC_FILES := \
|
||||||
jni/src/guiKeyChangeMenu.cpp \
|
jni/src/guiKeyChangeMenu.cpp \
|
||||||
jni/src/guiPasswordChange.cpp \
|
jni/src/guiPasswordChange.cpp \
|
||||||
jni/src/guiTable.cpp \
|
jni/src/guiTable.cpp \
|
||||||
|
jni/src/guiscalingfilter.cpp \
|
||||||
jni/src/guiVolumeChange.cpp \
|
jni/src/guiVolumeChange.cpp \
|
||||||
jni/src/httpfetch.cpp \
|
jni/src/httpfetch.cpp \
|
||||||
jni/src/hud.cpp \
|
jni/src/hud.cpp \
|
||||||
|
jni/src/imagefilters.cpp \
|
||||||
jni/src/inventory.cpp \
|
jni/src/inventory.cpp \
|
||||||
jni/src/inventorymanager.cpp \
|
jni/src/inventorymanager.cpp \
|
||||||
jni/src/itemdef.cpp \
|
jni/src/itemdef.cpp \
|
||||||
|
|
|
@ -172,6 +172,19 @@
|
||||||
#crosshair_alpha = 255
|
#crosshair_alpha = 255
|
||||||
# Scale gui by a user specified value
|
# Scale gui by a user specified value
|
||||||
#gui_scaling = 1.0
|
#gui_scaling = 1.0
|
||||||
|
# Use a nearest-neighbor-anti-alias filter to scale the GUI.
|
||||||
|
# This will smooth over some of the rough edges, and blend
|
||||||
|
# pixels when scaling down, at the cost of blurring some
|
||||||
|
# edge pixels when images are scaled by non-integer sizes.
|
||||||
|
#gui_scaling_filter = false
|
||||||
|
# When gui_scaling_filter = true, all GUI images need to be
|
||||||
|
# filtered in software, but some images are generated directly
|
||||||
|
# to hardare (e.g. render-to-texture for nodes in inventory).
|
||||||
|
# When gui_scaling_filter_txr2img is true, copy those images
|
||||||
|
# from hardware to software for scaling. When false, fall back
|
||||||
|
# to the old scaling method, for video drivers that don't
|
||||||
|
# propery support downloading textures back from hardware.
|
||||||
|
#gui_scaling_filter_txr2img = true
|
||||||
# Sensitivity multiplier
|
# Sensitivity multiplier
|
||||||
#mouse_sensitivity = 0.2
|
#mouse_sensitivity = 0.2
|
||||||
# Sound settings
|
# Sound settings
|
||||||
|
|
|
@ -405,9 +405,11 @@ set(client_SRCS
|
||||||
guiFormSpecMenu.cpp
|
guiFormSpecMenu.cpp
|
||||||
guiKeyChangeMenu.cpp
|
guiKeyChangeMenu.cpp
|
||||||
guiPasswordChange.cpp
|
guiPasswordChange.cpp
|
||||||
|
guiscalingfilter.cpp
|
||||||
guiTable.cpp
|
guiTable.cpp
|
||||||
guiVolumeChange.cpp
|
guiVolumeChange.cpp
|
||||||
hud.cpp
|
hud.cpp
|
||||||
|
imagefilters.cpp
|
||||||
keycode.cpp
|
keycode.cpp
|
||||||
localplayer.cpp
|
localplayer.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
|
|
|
@ -49,6 +49,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
#include "drawscene.h"
|
#include "drawscene.h"
|
||||||
#include "database-sqlite3.h"
|
#include "database-sqlite3.h"
|
||||||
#include "serialization.h"
|
#include "serialization.h"
|
||||||
|
#include "guiscalingfilter.h"
|
||||||
|
|
||||||
extern gui::IGUIEnvironment* guienv;
|
extern gui::IGUIEnvironment* guienv;
|
||||||
|
|
||||||
|
@ -1607,6 +1608,11 @@ void Client::afterContentReceived(IrrlichtDevice *device)
|
||||||
|
|
||||||
const wchar_t* text = wgettext("Loading textures...");
|
const wchar_t* text = wgettext("Loading textures...");
|
||||||
|
|
||||||
|
// Clear cached pre-scaled 2D GUI images, as this cache
|
||||||
|
// might have images with the same name but different
|
||||||
|
// content from previous sessions.
|
||||||
|
guiScalingCacheClear(device->getVideoDriver());
|
||||||
|
|
||||||
// Rebuild inherited images and recreate textures
|
// Rebuild inherited images and recreate textures
|
||||||
infostream<<"- Rebuilding images and textures"<<std::endl;
|
infostream<<"- Rebuilding images and textures"<<std::endl;
|
||||||
draw_load_screen(text,device, guienv, 0, 70);
|
draw_load_screen(text,device, guienv, 0, 70);
|
||||||
|
|
|
@ -34,6 +34,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
#include "gamedef.h"
|
#include "gamedef.h"
|
||||||
#include "strfnd.h"
|
#include "strfnd.h"
|
||||||
#include "util/string.h" // for parseColorString()
|
#include "util/string.h" // for parseColorString()
|
||||||
|
#include "imagefilters.h"
|
||||||
|
#include "guiscalingfilter.h"
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
#include <GLES/gl.h>
|
#include <GLES/gl.h>
|
||||||
|
@ -223,58 +225,9 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Apply the "clean transparent" filter to textures, removing borders on transparent textures.
|
// Apply the "clean transparent" filter, if configured.
|
||||||
* PNG optimizers discard RGB values of fully-transparent pixels, but filters may expose the
|
if (g_settings->getBool("texture_clean_transparent"))
|
||||||
* replacement colors at borders by blending to them; this filter compensates for that by
|
imageCleanTransparent(toadd, 127);
|
||||||
* filling in those RGB values from nearby pixels.
|
|
||||||
*/
|
|
||||||
if (g_settings->getBool("texture_clean_transparent")) {
|
|
||||||
const core::dimension2d<u32> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (need_to_grab)
|
if (need_to_grab)
|
||||||
toadd->grab();
|
toadd->grab();
|
||||||
|
@ -670,6 +623,7 @@ u32 TextureSource::generateTexture(const std::string &name)
|
||||||
#endif
|
#endif
|
||||||
// Create texture from resulting image
|
// Create texture from resulting image
|
||||||
tex = driver->addTexture(name.c_str(), img);
|
tex = driver->addTexture(name.c_str(), img);
|
||||||
|
guiScalingCache(io::path(name.c_str()), driver, img);
|
||||||
img->drop();
|
img->drop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -776,6 +730,7 @@ void TextureSource::rebuildImagesAndTextures()
|
||||||
video::ITexture *t = NULL;
|
video::ITexture *t = NULL;
|
||||||
if (img) {
|
if (img) {
|
||||||
t = driver->addTexture(ti->name.c_str(), img);
|
t = driver->addTexture(ti->name.c_str(), img);
|
||||||
|
guiScalingCache(io::path(ti->name.c_str()), driver, img);
|
||||||
img->drop();
|
img->drop();
|
||||||
}
|
}
|
||||||
video::ITexture *t_old = ti->texture;
|
video::ITexture *t_old = ti->texture;
|
||||||
|
@ -896,6 +851,8 @@ video::ITexture* TextureSource::generateTextureFromMesh(
|
||||||
rawImage->copyToScaling(inventory_image);
|
rawImage->copyToScaling(inventory_image);
|
||||||
rawImage->drop();
|
rawImage->drop();
|
||||||
|
|
||||||
|
guiScalingCache(io::path(params.rtt_texture_name.c_str()), driver, inventory_image);
|
||||||
|
|
||||||
video::ITexture *rtt = driver->addTexture(params.rtt_texture_name.c_str(), inventory_image);
|
video::ITexture *rtt = driver->addTexture(params.rtt_texture_name.c_str(), inventory_image);
|
||||||
inventory_image->drop();
|
inventory_image->drop();
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
#include "threads.h"
|
#include "threads.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include "util/numeric.h"
|
||||||
|
|
||||||
class IGameDef;
|
class IGameDef;
|
||||||
|
|
||||||
|
@ -135,21 +136,6 @@ public:
|
||||||
IWritableTextureSource* createTextureSource(IrrlichtDevice *device);
|
IWritableTextureSource* createTextureSource(IrrlichtDevice *device);
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
/**
|
|
||||||
* @param size get next npot2 value
|
|
||||||
* @return npot2 value
|
|
||||||
*/
|
|
||||||
inline unsigned int npot2(unsigned int size)
|
|
||||||
{
|
|
||||||
if (size == 0) return 0;
|
|
||||||
unsigned int npot = 1;
|
|
||||||
|
|
||||||
while ((size >>= 1) > 0) {
|
|
||||||
npot <<= 1;
|
|
||||||
}
|
|
||||||
return npot;
|
|
||||||
}
|
|
||||||
|
|
||||||
video::IImage * Align2Npot2(video::IImage * image, video::IVideoDriver* driver);
|
video::IImage * Align2Npot2(video::IImage * image, video::IVideoDriver* driver);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -137,6 +137,8 @@ void set_default_settings(Settings *settings)
|
||||||
settings->setDefault("crosshair_alpha", "255");
|
settings->setDefault("crosshair_alpha", "255");
|
||||||
settings->setDefault("hud_scaling", "1.0");
|
settings->setDefault("hud_scaling", "1.0");
|
||||||
settings->setDefault("gui_scaling", "1.0");
|
settings->setDefault("gui_scaling", "1.0");
|
||||||
|
settings->setDefault("gui_scaling_filter", "false");
|
||||||
|
settings->setDefault("gui_scaling_filter_txr2img", "true");
|
||||||
settings->setDefault("mouse_sensitivity", "0.2");
|
settings->setDefault("mouse_sensitivity", "0.2");
|
||||||
settings->setDefault("enable_sound", "true");
|
settings->setDefault("enable_sound", "true");
|
||||||
settings->setDefault("sound_volume", "0.8");
|
settings->setDefault("sound_volume", "0.8");
|
||||||
|
|
|
@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
#include "clientmap.h"
|
#include "clientmap.h"
|
||||||
#include "util/timetaker.h"
|
#include "util/timetaker.h"
|
||||||
#include "fontengine.h"
|
#include "fontengine.h"
|
||||||
|
#include "guiscalingfilter.h"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
LEFT = -1,
|
LEFT = -1,
|
||||||
|
@ -324,19 +325,19 @@ void draw_sidebyside_3d_mode(Camera& camera, bool show_hud,
|
||||||
//makeColorKeyTexture mirrors texture so we do it twice to get it right again
|
//makeColorKeyTexture mirrors texture so we do it twice to get it right again
|
||||||
driver->makeColorKeyTexture(hudtexture, irr::video::SColor(255, 0, 0, 0));
|
driver->makeColorKeyTexture(hudtexture, irr::video::SColor(255, 0, 0, 0));
|
||||||
|
|
||||||
driver->draw2DImage(left_image,
|
draw2DImageFilterScaled(driver, left_image,
|
||||||
irr::core::rect<s32>(0, 0, screensize.X/2, screensize.Y),
|
irr::core::rect<s32>(0, 0, screensize.X/2, screensize.Y),
|
||||||
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, false);
|
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, false);
|
||||||
|
|
||||||
driver->draw2DImage(hudtexture,
|
draw2DImageFilterScaled(driver, hudtexture,
|
||||||
irr::core::rect<s32>(0, 0, screensize.X/2, screensize.Y),
|
irr::core::rect<s32>(0, 0, screensize.X/2, screensize.Y),
|
||||||
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, true);
|
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, true);
|
||||||
|
|
||||||
driver->draw2DImage(right_image,
|
draw2DImageFilterScaled(driver, right_image,
|
||||||
irr::core::rect<s32>(screensize.X/2, 0, screensize.X, screensize.Y),
|
irr::core::rect<s32>(screensize.X/2, 0, screensize.X, screensize.Y),
|
||||||
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, false);
|
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, false);
|
||||||
|
|
||||||
driver->draw2DImage(hudtexture,
|
draw2DImageFilterScaled(driver, hudtexture,
|
||||||
irr::core::rect<s32>(screensize.X/2, 0, screensize.X, screensize.Y),
|
irr::core::rect<s32>(screensize.X/2, 0, screensize.X, screensize.Y),
|
||||||
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, true);
|
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, true);
|
||||||
|
|
||||||
|
@ -380,19 +381,19 @@ void draw_top_bottom_3d_mode(Camera& camera, bool show_hud,
|
||||||
//makeColorKeyTexture mirrors texture so we do it twice to get it right again
|
//makeColorKeyTexture mirrors texture so we do it twice to get it right again
|
||||||
driver->makeColorKeyTexture(hudtexture, irr::video::SColor(255, 0, 0, 0));
|
driver->makeColorKeyTexture(hudtexture, irr::video::SColor(255, 0, 0, 0));
|
||||||
|
|
||||||
driver->draw2DImage(left_image,
|
draw2DImageFilterScaled(driver, left_image,
|
||||||
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y/2),
|
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y/2),
|
||||||
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, false);
|
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, false);
|
||||||
|
|
||||||
driver->draw2DImage(hudtexture,
|
draw2DImageFilterScaled(driver, hudtexture,
|
||||||
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y/2),
|
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y/2),
|
||||||
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, true);
|
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, true);
|
||||||
|
|
||||||
driver->draw2DImage(right_image,
|
draw2DImageFilterScaled(driver, right_image,
|
||||||
irr::core::rect<s32>(0, screensize.Y/2, screensize.X, screensize.Y),
|
irr::core::rect<s32>(0, screensize.Y/2, screensize.X, screensize.Y),
|
||||||
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, false);
|
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, false);
|
||||||
|
|
||||||
driver->draw2DImage(hudtexture,
|
draw2DImageFilterScaled(driver, hudtexture,
|
||||||
irr::core::rect<s32>(0, screensize.Y/2, screensize.X, screensize.Y),
|
irr::core::rect<s32>(0, screensize.Y/2, screensize.X, screensize.Y),
|
||||||
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, true);
|
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, true);
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
#include "httpfetch.h"
|
#include "httpfetch.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "fontengine.h"
|
#include "fontengine.h"
|
||||||
|
#include "guiscalingfilter.h"
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
#include "client/tile.h"
|
#include "client/tile.h"
|
||||||
|
@ -409,7 +410,7 @@ void GUIEngine::drawBackground(video::IVideoDriver* driver)
|
||||||
{
|
{
|
||||||
for (unsigned int y = 0; y < screensize.Y; y += tilesize.Y )
|
for (unsigned int y = 0; y < screensize.Y; y += tilesize.Y )
|
||||||
{
|
{
|
||||||
driver->draw2DImage(texture,
|
draw2DImageFilterScaled(driver, texture,
|
||||||
core::rect<s32>(x, y, x+tilesize.X, y+tilesize.Y),
|
core::rect<s32>(x, y, x+tilesize.X, y+tilesize.Y),
|
||||||
core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
|
core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
|
||||||
NULL, NULL, true);
|
NULL, NULL, true);
|
||||||
|
@ -419,7 +420,7 @@ void GUIEngine::drawBackground(video::IVideoDriver* driver)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Draw background texture */
|
/* Draw background texture */
|
||||||
driver->draw2DImage(texture,
|
draw2DImageFilterScaled(driver, texture,
|
||||||
core::rect<s32>(0, 0, screensize.X, screensize.Y),
|
core::rect<s32>(0, 0, screensize.X, screensize.Y),
|
||||||
core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
|
core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
|
||||||
NULL, NULL, true);
|
NULL, NULL, true);
|
||||||
|
@ -438,7 +439,7 @@ void GUIEngine::drawOverlay(video::IVideoDriver* driver)
|
||||||
|
|
||||||
/* Draw background texture */
|
/* Draw background texture */
|
||||||
v2u32 sourcesize = texture->getOriginalSize();
|
v2u32 sourcesize = texture->getOriginalSize();
|
||||||
driver->draw2DImage(texture,
|
draw2DImageFilterScaled(driver, texture,
|
||||||
core::rect<s32>(0, 0, screensize.X, screensize.Y),
|
core::rect<s32>(0, 0, screensize.X, screensize.Y),
|
||||||
core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
|
core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
|
||||||
NULL, NULL, true);
|
NULL, NULL, true);
|
||||||
|
@ -471,7 +472,7 @@ void GUIEngine::drawHeader(video::IVideoDriver* driver)
|
||||||
|
|
||||||
video::SColor bgcolor(255,50,50,50);
|
video::SColor bgcolor(255,50,50,50);
|
||||||
|
|
||||||
driver->draw2DImage(texture, splashrect,
|
draw2DImageFilterScaled(driver, texture, splashrect,
|
||||||
core::rect<s32>(core::position2d<s32>(0,0),
|
core::rect<s32>(core::position2d<s32>(0,0),
|
||||||
core::dimension2di(texture->getOriginalSize())),
|
core::dimension2di(texture->getOriginalSize())),
|
||||||
NULL, NULL, true);
|
NULL, NULL, true);
|
||||||
|
@ -503,7 +504,7 @@ void GUIEngine::drawFooter(video::IVideoDriver* driver)
|
||||||
rect += v2s32(screensize.Width/2,screensize.Height-footersize.Y);
|
rect += v2s32(screensize.Width/2,screensize.Height-footersize.Y);
|
||||||
rect -= v2s32(footersize.X/2, 0);
|
rect -= v2s32(footersize.X/2, 0);
|
||||||
|
|
||||||
driver->draw2DImage(texture, rect,
|
draw2DImageFilterScaled(driver, texture, rect,
|
||||||
core::rect<s32>(core::position2d<s32>(0,0),
|
core::rect<s32>(core::position2d<s32>(0,0),
|
||||||
core::dimension2di(texture->getOriginalSize())),
|
core::dimension2di(texture->getOriginalSize())),
|
||||||
NULL, NULL, true);
|
NULL, NULL, true);
|
||||||
|
|
|
@ -51,6 +51,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
#include "util/hex.h"
|
#include "util/hex.h"
|
||||||
#include "util/numeric.h"
|
#include "util/numeric.h"
|
||||||
#include "util/string.h" // for parseColorString()
|
#include "util/string.h" // for parseColorString()
|
||||||
|
#include "guiscalingfilter.h"
|
||||||
|
|
||||||
#define MY_CHECKPOS(a,b) \
|
#define MY_CHECKPOS(a,b) \
|
||||||
if (v_pos.size() != 2) { \
|
if (v_pos.size() != 2) { \
|
||||||
|
@ -1307,8 +1308,8 @@ void GUIFormSpecMenu::parseImageButton(parserData* data,std::string element,
|
||||||
}
|
}
|
||||||
|
|
||||||
e->setUseAlphaChannel(true);
|
e->setUseAlphaChannel(true);
|
||||||
e->setImage(texture);
|
e->setImage(guiScalingImageButton(Environment->getVideoDriver(), texture, geom.X, geom.Y));
|
||||||
e->setPressedImage(pressed_texture);
|
e->setPressedImage(guiScalingImageButton(Environment->getVideoDriver(), pressed_texture, geom.X, geom.Y));
|
||||||
e->setScaleImage(true);
|
e->setScaleImage(true);
|
||||||
e->setNotClipped(noclip);
|
e->setNotClipped(noclip);
|
||||||
e->setDrawBorder(drawborder);
|
e->setDrawBorder(drawborder);
|
||||||
|
@ -1452,8 +1453,8 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data,std::string element)
|
||||||
}
|
}
|
||||||
|
|
||||||
e->setUseAlphaChannel(true);
|
e->setUseAlphaChannel(true);
|
||||||
e->setImage(texture);
|
e->setImage(guiScalingImageButton(Environment->getVideoDriver(), texture, geom.X, geom.Y));
|
||||||
e->setPressedImage(texture);
|
e->setPressedImage(guiScalingImageButton(Environment->getVideoDriver(), texture, geom.X, geom.Y));
|
||||||
e->setScaleImage(true);
|
e->setScaleImage(true);
|
||||||
spec.ftype = f_Button;
|
spec.ftype = f_Button;
|
||||||
rect+=data->basepos-padding;
|
rect+=data->basepos-padding;
|
||||||
|
@ -2283,7 +2284,7 @@ void GUIFormSpecMenu::drawMenu()
|
||||||
|
|
||||||
const video::SColor color(255,255,255,255);
|
const video::SColor color(255,255,255,255);
|
||||||
const video::SColor colors[] = {color,color,color,color};
|
const video::SColor colors[] = {color,color,color,color};
|
||||||
driver->draw2DImage(texture, rect,
|
draw2DImageFilterScaled(driver, texture, rect,
|
||||||
core::rect<s32>(core::position2d<s32>(0,0),
|
core::rect<s32>(core::position2d<s32>(0,0),
|
||||||
core::dimension2di(texture->getOriginalSize())),
|
core::dimension2di(texture->getOriginalSize())),
|
||||||
NULL/*&AbsoluteClippingRect*/, colors, true);
|
NULL/*&AbsoluteClippingRect*/, colors, true);
|
||||||
|
@ -2333,7 +2334,7 @@ void GUIFormSpecMenu::drawMenu()
|
||||||
core::rect<s32> rect = imgrect + spec.pos;
|
core::rect<s32> rect = imgrect + spec.pos;
|
||||||
const video::SColor color(255,255,255,255);
|
const video::SColor color(255,255,255,255);
|
||||||
const video::SColor colors[] = {color,color,color,color};
|
const video::SColor colors[] = {color,color,color,color};
|
||||||
driver->draw2DImage(texture, rect,
|
draw2DImageFilterScaled(driver, texture, rect,
|
||||||
core::rect<s32>(core::position2d<s32>(0,0),img_origsize),
|
core::rect<s32>(core::position2d<s32>(0,0),img_origsize),
|
||||||
NULL/*&AbsoluteClippingRect*/, colors, true);
|
NULL/*&AbsoluteClippingRect*/, colors, true);
|
||||||
}
|
}
|
||||||
|
@ -2362,7 +2363,7 @@ void GUIFormSpecMenu::drawMenu()
|
||||||
core::rect<s32> rect = imgrect + spec.pos;
|
core::rect<s32> rect = imgrect + spec.pos;
|
||||||
const video::SColor color(255,255,255,255);
|
const video::SColor color(255,255,255,255);
|
||||||
const video::SColor colors[] = {color,color,color,color};
|
const video::SColor colors[] = {color,color,color,color};
|
||||||
driver->draw2DImage(texture, rect,
|
draw2DImageFilterScaled(driver, texture, rect,
|
||||||
core::rect<s32>(core::position2d<s32>(0,0),
|
core::rect<s32>(core::position2d<s32>(0,0),
|
||||||
core::dimension2di(texture->getOriginalSize())),
|
core::dimension2di(texture->getOriginalSize())),
|
||||||
NULL/*&AbsoluteClippingRect*/, colors, true);
|
NULL/*&AbsoluteClippingRect*/, colors, true);
|
||||||
|
|
|
@ -36,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "settings.h" // for settings
|
#include "settings.h" // for settings
|
||||||
#include "porting.h" // for dpi
|
#include "porting.h" // for dpi
|
||||||
|
#include "guiscalingfilter.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
GUITable
|
GUITable
|
||||||
|
|
|
@ -0,0 +1,160 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2015 Aaron Suen <warr1024@gmail.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "guiscalingfilter.h"
|
||||||
|
#include "imagefilters.h"
|
||||||
|
#include "settings.h"
|
||||||
|
#include "main.h" // for g_settings
|
||||||
|
#include "util/numeric.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/* Maintain a static cache to store the images that correspond to textures
|
||||||
|
* in a format that's manipulable by code. Some platforms exhibit issues
|
||||||
|
* converting textures back into images repeatedly, and some don't even
|
||||||
|
* allow it at all.
|
||||||
|
*/
|
||||||
|
std::map<io::path, video::IImage *> imgCache;
|
||||||
|
|
||||||
|
/* Maintain a static cache of all pre-scaled textures. These need to be
|
||||||
|
* cleared as well when the cached images.
|
||||||
|
*/
|
||||||
|
std::map<io::path, video::ITexture *> txrCache;
|
||||||
|
|
||||||
|
/* Manually insert an image into the cache, useful to avoid texture-to-image
|
||||||
|
* conversion whenever we can intercept it.
|
||||||
|
*/
|
||||||
|
void guiScalingCache(io::path key, video::IVideoDriver *driver, video::IImage *value) {
|
||||||
|
if (!g_settings->getBool("gui_scaling_filter"))
|
||||||
|
return;
|
||||||
|
video::IImage *copied = driver->createImage(value->getColorFormat(),
|
||||||
|
value->getDimension());
|
||||||
|
value->copyTo(copied);
|
||||||
|
imgCache[key] = copied;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manually clear the cache, e.g. when switching to different worlds.
|
||||||
|
void guiScalingCacheClear(video::IVideoDriver *driver) {
|
||||||
|
for (std::map<io::path, video::IImage *>::iterator it = imgCache.begin();
|
||||||
|
it != imgCache.end(); it++) {
|
||||||
|
if (it->second != NULL)
|
||||||
|
it->second->drop();
|
||||||
|
}
|
||||||
|
imgCache.clear();
|
||||||
|
for (std::map<io::path, video::ITexture *>::iterator it = txrCache.begin();
|
||||||
|
it != txrCache.end(); it++) {
|
||||||
|
if (it->second != NULL)
|
||||||
|
driver->removeTexture(it->second);
|
||||||
|
}
|
||||||
|
txrCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get a cached, high-quality pre-scaled texture for display purposes. If the
|
||||||
|
* texture is not already cached, attempt to create it. Returns a pre-scaled texture,
|
||||||
|
* or the original texture if unable to pre-scale it.
|
||||||
|
*/
|
||||||
|
video::ITexture *guiScalingResizeCached(video::IVideoDriver *driver, video::ITexture *src,
|
||||||
|
const core::rect<s32> &srcrect, const core::rect<s32> &destrect) {
|
||||||
|
|
||||||
|
if (!g_settings->getBool("gui_scaling_filter"))
|
||||||
|
return src;
|
||||||
|
|
||||||
|
// Calculate scaled texture name.
|
||||||
|
char rectstr[200];
|
||||||
|
sprintf(rectstr, "%d:%d:%d:%d:%d:%d",
|
||||||
|
srcrect.UpperLeftCorner.X,
|
||||||
|
srcrect.UpperLeftCorner.Y,
|
||||||
|
srcrect.getWidth(),
|
||||||
|
srcrect.getHeight(),
|
||||||
|
destrect.getWidth(),
|
||||||
|
destrect.getHeight());
|
||||||
|
io::path origname = src->getName().getPath();
|
||||||
|
io::path scalename = origname + "@guiScalingFilter:" + rectstr;
|
||||||
|
|
||||||
|
// Search for existing scaled texture.
|
||||||
|
video::ITexture *scaled = txrCache[scalename];
|
||||||
|
if (scaled)
|
||||||
|
return scaled;
|
||||||
|
|
||||||
|
// Try to find the texture converted to an image in the cache.
|
||||||
|
// If the image was not found, try to extract it from the texture.
|
||||||
|
video::IImage* srcimg = imgCache[origname];
|
||||||
|
if (srcimg == NULL) {
|
||||||
|
if (!g_settings->getBool("gui_scaling_filter_txr2img"))
|
||||||
|
return src;
|
||||||
|
srcimg = driver->createImageFromData(src->getColorFormat(),
|
||||||
|
src->getSize(), src->lock(), false);
|
||||||
|
src->unlock();
|
||||||
|
imgCache[origname] = srcimg;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new destination image and scale the source into it.
|
||||||
|
imageCleanTransparent(srcimg, 0);
|
||||||
|
video::IImage *destimg = driver->createImage(src->getColorFormat(),
|
||||||
|
core::dimension2d<u32>((u32)destrect.getWidth(),
|
||||||
|
(u32)destrect.getHeight()));
|
||||||
|
imageScaleNNAA(srcimg, srcrect, destimg);
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
// Android is very picky about textures being powers of 2, so expand
|
||||||
|
// the image dimensions to the next power of 2, if necessary, for
|
||||||
|
// that platform.
|
||||||
|
video::IImage *po2img = driver->createImage(src->getColorFormat(),
|
||||||
|
core::dimension2d<u32>(npot2((u32)destrect.getWidth()),
|
||||||
|
npot2((u32)destrect.getHeight())));
|
||||||
|
po2img->fill(video::SColor(0, 0, 0, 0));
|
||||||
|
destimg->copyTo(po2img);
|
||||||
|
destimg->drop();
|
||||||
|
destimg = po2img;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Convert the scaled image back into a texture.
|
||||||
|
scaled = driver->addTexture(scalename, destimg, NULL);
|
||||||
|
destimg->drop();
|
||||||
|
txrCache[scalename] = scaled;
|
||||||
|
|
||||||
|
return scaled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convenience wrapper for guiScalingResizeCached that accepts parameters that
|
||||||
|
* are available at GUI imagebutton creation time.
|
||||||
|
*/
|
||||||
|
video::ITexture *guiScalingImageButton(video::IVideoDriver *driver, video::ITexture *src,
|
||||||
|
s32 width, s32 height) {
|
||||||
|
return guiScalingResizeCached(driver, src,
|
||||||
|
core::rect<s32>(0, 0, src->getSize().Width, src->getSize().Height),
|
||||||
|
core::rect<s32>(0, 0, width, height));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Replacement for driver->draw2DImage() that uses the high-quality pre-scaled
|
||||||
|
* texture, if configured.
|
||||||
|
*/
|
||||||
|
void draw2DImageFilterScaled(video::IVideoDriver *driver, video::ITexture *txr,
|
||||||
|
const core::rect<s32> &destrect, const core::rect<s32> &srcrect,
|
||||||
|
const core::rect<s32> *cliprect, const video::SColor *const colors,
|
||||||
|
bool usealpha) {
|
||||||
|
|
||||||
|
// Attempt to pre-scale image in software in high quality.
|
||||||
|
video::ITexture *scaled = guiScalingResizeCached(driver, txr, srcrect, destrect);
|
||||||
|
|
||||||
|
// Correct source rect based on scaled image.
|
||||||
|
const core::rect<s32> mysrcrect = (scaled != txr)
|
||||||
|
? core::rect<s32>(0, 0, destrect.getWidth(), destrect.getHeight())
|
||||||
|
: srcrect;
|
||||||
|
|
||||||
|
driver->draw2DImage(scaled, destrect, mysrcrect, cliprect, colors, usealpha);
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2015 Aaron Suen <warr1024@gmail.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
#ifndef _GUI_SCALING_FILTER_H_
|
||||||
|
#define _GUI_SCALING_FILTER_H_
|
||||||
|
|
||||||
|
#include "irrlichttypes_extrabloated.h"
|
||||||
|
|
||||||
|
/* Manually insert an image into the cache, useful to avoid texture-to-image
|
||||||
|
* conversion whenever we can intercept it.
|
||||||
|
*/
|
||||||
|
void guiScalingCache(io::path key, video::IVideoDriver *driver, video::IImage *value);
|
||||||
|
|
||||||
|
// Manually clear the cache, e.g. when switching to different worlds.
|
||||||
|
void guiScalingCacheClear(video::IVideoDriver *driver);
|
||||||
|
|
||||||
|
/* Get a cached, high-quality pre-scaled texture for display purposes. If the
|
||||||
|
* texture is not already cached, attempt to create it. Returns a pre-scaled texture,
|
||||||
|
* or the original texture if unable to pre-scale it.
|
||||||
|
*/
|
||||||
|
video::ITexture *guiScalingResizeCached(video::IVideoDriver *driver, video::ITexture *src,
|
||||||
|
const core::rect<s32> &srcrect, const core::rect<s32> &destrect);
|
||||||
|
|
||||||
|
/* Convenience wrapper for guiScalingResizeCached that accepts parameters that
|
||||||
|
* are available at GUI imagebutton creation time.
|
||||||
|
*/
|
||||||
|
video::ITexture *guiScalingImageButton(video::IVideoDriver *driver, video::ITexture *src,
|
||||||
|
s32 width, s32 height);
|
||||||
|
|
||||||
|
/* Replacement for driver->draw2DImage() that uses the high-quality pre-scaled
|
||||||
|
* texture, if configured.
|
||||||
|
*/
|
||||||
|
void draw2DImageFilterScaled(video::IVideoDriver *driver, video::ITexture *txr,
|
||||||
|
const core::rect<s32> &destrect, const core::rect<s32> &srcrect,
|
||||||
|
const core::rect<s32> *cliprect = 0, const video::SColor *const colors = 0,
|
||||||
|
bool usealpha = false);
|
||||||
|
|
||||||
|
#endif
|
13
src/hud.cpp
13
src/hud.cpp
|
@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
#include "camera.h"
|
#include "camera.h"
|
||||||
#include "porting.h"
|
#include "porting.h"
|
||||||
#include "fontengine.h"
|
#include "fontengine.h"
|
||||||
|
#include "guiscalingfilter.h"
|
||||||
#include <IGUIStaticText.h>
|
#include <IGUIStaticText.h>
|
||||||
|
|
||||||
#ifdef HAVE_TOUCHSCREENGUI
|
#ifdef HAVE_TOUCHSCREENGUI
|
||||||
|
@ -94,7 +95,7 @@ void Hud::drawItem(const ItemStack &item, const core::rect<s32>& rect, bool sele
|
||||||
imgrect2.LowerRightCorner.Y += (m_padding*2);
|
imgrect2.LowerRightCorner.Y += (m_padding*2);
|
||||||
video::ITexture *texture = tsrc->getTexture(hotbar_selected_image);
|
video::ITexture *texture = tsrc->getTexture(hotbar_selected_image);
|
||||||
core::dimension2di imgsize(texture->getOriginalSize());
|
core::dimension2di imgsize(texture->getOriginalSize());
|
||||||
driver->draw2DImage(texture, imgrect2,
|
draw2DImageFilterScaled(driver, texture, imgrect2,
|
||||||
core::rect<s32>(core::position2d<s32>(0,0), imgsize),
|
core::rect<s32>(core::position2d<s32>(0,0), imgsize),
|
||||||
NULL, hbar_colors, true);
|
NULL, hbar_colors, true);
|
||||||
} else {
|
} else {
|
||||||
|
@ -200,7 +201,7 @@ void Hud::drawItems(v2s32 upperleftpos, s32 itemcount, s32 offset,
|
||||||
core::rect<s32> rect2 = imgrect2 + pos;
|
core::rect<s32> rect2 = imgrect2 + pos;
|
||||||
video::ITexture *texture = tsrc->getTexture(hotbar_image);
|
video::ITexture *texture = tsrc->getTexture(hotbar_image);
|
||||||
core::dimension2di imgsize(texture->getOriginalSize());
|
core::dimension2di imgsize(texture->getOriginalSize());
|
||||||
driver->draw2DImage(texture, rect2,
|
draw2DImageFilterScaled(driver, texture, rect2,
|
||||||
core::rect<s32>(core::position2d<s32>(0,0), imgsize),
|
core::rect<s32>(core::position2d<s32>(0,0), imgsize),
|
||||||
NULL, hbar_colors, true);
|
NULL, hbar_colors, true);
|
||||||
}
|
}
|
||||||
|
@ -266,7 +267,7 @@ void Hud::drawLuaElements(v3s16 camera_offset) {
|
||||||
(e->align.Y - 1.0) * dstsize.Y / 2);
|
(e->align.Y - 1.0) * dstsize.Y / 2);
|
||||||
core::rect<s32> rect(0, 0, dstsize.X, dstsize.Y);
|
core::rect<s32> rect(0, 0, dstsize.X, dstsize.Y);
|
||||||
rect += pos + offset + v2s32(e->offset.X, e->offset.Y);
|
rect += pos + offset + v2s32(e->offset.X, e->offset.Y);
|
||||||
driver->draw2DImage(texture, rect,
|
draw2DImageFilterScaled(driver, texture, rect,
|
||||||
core::rect<s32>(core::position2d<s32>(0,0), imgsize),
|
core::rect<s32>(core::position2d<s32>(0,0), imgsize),
|
||||||
NULL, colors, true);
|
NULL, colors, true);
|
||||||
break; }
|
break; }
|
||||||
|
@ -378,7 +379,7 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture,
|
||||||
core::rect<s32> dstrect(0,0, dstd.Width, dstd.Height);
|
core::rect<s32> dstrect(0,0, dstd.Width, dstd.Height);
|
||||||
|
|
||||||
dstrect += p;
|
dstrect += p;
|
||||||
driver->draw2DImage(stat_texture, dstrect, srcrect, NULL, colors, true);
|
draw2DImageFilterScaled(driver, stat_texture, dstrect, srcrect, NULL, colors, true);
|
||||||
p += steppos;
|
p += steppos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,7 +389,7 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture,
|
||||||
core::rect<s32> dstrect(0,0, dstd.Width / 2, dstd.Height);
|
core::rect<s32> dstrect(0,0, dstd.Width / 2, dstd.Height);
|
||||||
|
|
||||||
dstrect += p;
|
dstrect += p;
|
||||||
driver->draw2DImage(stat_texture, dstrect, srcrect, NULL, colors, true);
|
draw2DImageFilterScaled(driver, stat_texture, dstrect, srcrect, NULL, colors, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,7 +503,7 @@ void drawItemStack(video::IVideoDriver *driver,
|
||||||
{
|
{
|
||||||
const video::SColor color(255,255,255,255);
|
const video::SColor color(255,255,255,255);
|
||||||
const video::SColor colors[] = {color,color,color,color};
|
const video::SColor colors[] = {color,color,color,color};
|
||||||
driver->draw2DImage(texture, rect,
|
draw2DImageFilterScaled(driver, texture, rect,
|
||||||
core::rect<s32>(core::position2d<s32>(0,0),
|
core::rect<s32>(core::position2d<s32>(0,0),
|
||||||
core::dimension2di(texture->getOriginalSize())),
|
core::dimension2di(texture->getOriginalSize())),
|
||||||
clip, colors, true);
|
clip, colors, true);
|
||||||
|
|
|
@ -0,0 +1,172 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2015 Aaron Suen <warr1024@gmail.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "imagefilters.h"
|
||||||
|
#include "util/numeric.h"
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
/* Fill in RGB values for transparent pixels, to correct for odd colors
|
||||||
|
* appearing at borders when blending. This is because many PNG optimizers
|
||||||
|
* like to discard RGB values of transparent pixels, but when blending then
|
||||||
|
* with non-transparent neighbors, their RGB values will shpw up nonetheless.
|
||||||
|
*
|
||||||
|
* This function modifies the original image in-place.
|
||||||
|
*
|
||||||
|
* Parameter "threshold" is the alpha level below which pixels are considered
|
||||||
|
* transparent. Should be 127 for 3d where alpha is threshold, but 0 for
|
||||||
|
* 2d where alpha is blended.
|
||||||
|
*/
|
||||||
|
void imageCleanTransparent(video::IImage *src, u32 threshold) {
|
||||||
|
|
||||||
|
core::dimension2d<u32> dim = src->getDimension();
|
||||||
|
|
||||||
|
// Walk each pixel looking for fully transparent ones.
|
||||||
|
// Note: loop y around x for better cache locality.
|
||||||
|
for (u32 ctry = 0; ctry < dim.Height; ctry++)
|
||||||
|
for (u32 ctrx = 0; ctrx < dim.Width; ctrx++) {
|
||||||
|
|
||||||
|
// Ignore opaque pixels.
|
||||||
|
irr::video::SColor c = src->getPixel(ctrx, ctry);
|
||||||
|
if (c.getAlpha() > threshold)
|
||||||
|
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 sy = (ctry < 1) ? 0 : (ctry - 1);
|
||||||
|
sy <= (ctry + 1) && sy < dim.Height; sy++)
|
||||||
|
for (u32 sx = (ctrx < 1) ? 0 : (ctrx - 1);
|
||||||
|
sx <= (ctrx + 1) && sx < dim.Width; sx++) {
|
||||||
|
|
||||||
|
// Ignore transparent pixels.
|
||||||
|
irr::video::SColor d = src->getPixel(sx, sy);
|
||||||
|
if (d.getAlpha() <= threshold)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Add RGB values weighted by alpha.
|
||||||
|
u32 a = d.getAlpha();
|
||||||
|
ss += a;
|
||||||
|
sr += a * d.getRed();
|
||||||
|
sg += a * d.getGreen();
|
||||||
|
sb += a * 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);
|
||||||
|
src->setPixel(ctrx, ctry, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scale a region of an image into another image, using nearest-neighbor with
|
||||||
|
* anti-aliasing; treat pixels as crisp rectangles, but blend them at boundaries
|
||||||
|
* to prevent non-integer scaling ratio artifacts. Note that this may cause
|
||||||
|
* some blending at the edges where pixels don't line up perfectly, but this
|
||||||
|
* filter is designed to produce the most accurate results for both upscaling
|
||||||
|
* and downscaling.
|
||||||
|
*/
|
||||||
|
void imageScaleNNAA(video::IImage *src, const core::rect<s32> &srcrect, video::IImage *dest) {
|
||||||
|
|
||||||
|
double sx, sy, minsx, maxsx, minsy, maxsy, area, ra, ga, ba, aa, pw, ph, pa;
|
||||||
|
u32 dy, dx;
|
||||||
|
video::SColor pxl;
|
||||||
|
|
||||||
|
// Cache rectsngle boundaries.
|
||||||
|
double sox = srcrect.UpperLeftCorner.X * 1.0;
|
||||||
|
double soy = srcrect.UpperLeftCorner.Y * 1.0;
|
||||||
|
double sw = srcrect.getWidth() * 1.0;
|
||||||
|
double sh = srcrect.getHeight() * 1.0;
|
||||||
|
|
||||||
|
// Walk each destination image pixel.
|
||||||
|
// Note: loop y around x for better cache locality.
|
||||||
|
core::dimension2d<u32> dim = dest->getDimension();
|
||||||
|
for (dy = 0; dy < dim.Height; dy++)
|
||||||
|
for (dx = 0; dx < dim.Width; dx++) {
|
||||||
|
|
||||||
|
// Calculate floating-point source rectangle bounds.
|
||||||
|
// Do some basic clipping, and for mirrored/flipped rects,
|
||||||
|
// make sure min/max are in the right order.
|
||||||
|
minsx = sox + (dx * sw / dim.Width);
|
||||||
|
minsx = rangelim(minsx, 0, sw);
|
||||||
|
maxsx = minsx + sw / dim.Width;
|
||||||
|
maxsx = rangelim(maxsx, 0, sw);
|
||||||
|
if (minsx > maxsx)
|
||||||
|
SWAP(double, minsx, maxsx);
|
||||||
|
minsy = soy + (dy * sh / dim.Height);
|
||||||
|
minsy = rangelim(minsy, 0, sh);
|
||||||
|
maxsy = minsy + sh / dim.Height;
|
||||||
|
maxsy = rangelim(maxsy, 0, sh);
|
||||||
|
if (minsy > maxsy)
|
||||||
|
SWAP(double, minsy, maxsy);
|
||||||
|
|
||||||
|
// Total area, and integral of r, g, b values over that area,
|
||||||
|
// initialized to zero, to be summed up in next loops.
|
||||||
|
area = 0;
|
||||||
|
ra = 0;
|
||||||
|
ga = 0;
|
||||||
|
ba = 0;
|
||||||
|
aa = 0;
|
||||||
|
|
||||||
|
// Loop over the integral pixel positions described by those bounds.
|
||||||
|
for (sy = floor(minsy); sy < maxsy; sy++)
|
||||||
|
for (sx = floor(minsx); sx < maxsx; sx++) {
|
||||||
|
|
||||||
|
// Calculate width, height, then area of dest pixel
|
||||||
|
// that's covered by this source pixel.
|
||||||
|
pw = 1;
|
||||||
|
if (minsx > sx)
|
||||||
|
pw += sx - minsx;
|
||||||
|
if (maxsx < (sx + 1))
|
||||||
|
pw += maxsx - sx - 1;
|
||||||
|
ph = 1;
|
||||||
|
if (minsy > sy)
|
||||||
|
ph += sy - minsy;
|
||||||
|
if (maxsy < (sy + 1))
|
||||||
|
ph += maxsy - sy - 1;
|
||||||
|
pa = pw * ph;
|
||||||
|
|
||||||
|
// Get source pixel and add it to totals, weighted
|
||||||
|
// by covered area and alpha.
|
||||||
|
pxl = src->getPixel((u32)sx, (u32)sy);
|
||||||
|
area += pa;
|
||||||
|
ra += pa * pxl.getRed();
|
||||||
|
ga += pa * pxl.getGreen();
|
||||||
|
ba += pa * pxl.getBlue();
|
||||||
|
aa += pa * pxl.getAlpha();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the destination image pixel to the average color.
|
||||||
|
if (area > 0) {
|
||||||
|
pxl.setRed(ra / area + 0.5);
|
||||||
|
pxl.setGreen(ga / area + 0.5);
|
||||||
|
pxl.setBlue(ba / area + 0.5);
|
||||||
|
pxl.setAlpha(aa / area + 0.5);
|
||||||
|
} else {
|
||||||
|
pxl.setRed(0);
|
||||||
|
pxl.setGreen(0);
|
||||||
|
pxl.setBlue(0);
|
||||||
|
pxl.setAlpha(0);
|
||||||
|
}
|
||||||
|
dest->setPixel(dx, dy, pxl);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2015 Aaron Suen <warr1024@gmail.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _IMAGE_FILTERS_H_
|
||||||
|
#define _IMAGE_FILTERS_H_
|
||||||
|
|
||||||
|
#include "irrlichttypes_extrabloated.h"
|
||||||
|
|
||||||
|
/* Fill in RGB values for transparent pixels, to correct for odd colors
|
||||||
|
* appearing at borders when blending. This is because many PNG optimizers
|
||||||
|
* like to discard RGB values of transparent pixels, but when blending then
|
||||||
|
* with non-transparent neighbors, their RGB values will shpw up nonetheless.
|
||||||
|
*
|
||||||
|
* This function modifies the original image in-place.
|
||||||
|
*
|
||||||
|
* Parameter "threshold" is the alpha level below which pixels are considered
|
||||||
|
* transparent. Should be 127 for 3d where alpha is threshold, but 0 for
|
||||||
|
* 2d where alpha is blended.
|
||||||
|
*/
|
||||||
|
void imageCleanTransparent(video::IImage *src, u32 threshold);
|
||||||
|
|
||||||
|
/* Scale a region of an image into another image, using nearest-neighbor with
|
||||||
|
* anti-aliasing; treat pixels as crisp rectangles, but blend them at boundaries
|
||||||
|
* to prevent non-integer scaling ratio artifacts. Note that this may cause
|
||||||
|
* some blending at the edges where pixels don't line up perfectly, but this
|
||||||
|
* filter is designed to produce the most accurate results for both upscaling
|
||||||
|
* and downscaling.
|
||||||
|
*/
|
||||||
|
void imageScaleNNAA(video::IImage *src, const core::rect<s32> &srcrect, video::IImage *dest);
|
||||||
|
|
||||||
|
#endif
|
|
@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
#include "gettime.h"
|
#include "gettime.h"
|
||||||
#include "util/numeric.h"
|
#include "util/numeric.h"
|
||||||
#include "porting.h"
|
#include "porting.h"
|
||||||
|
#include "guiscalingfilter.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
@ -130,15 +131,23 @@ TouchScreenGUI::TouchScreenGUI(IrrlichtDevice *device, IEventReceiver* receiver)
|
||||||
m_screensize = m_device->getVideoDriver()->getScreenSize();
|
m_screensize = m_device->getVideoDriver()->getScreenSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TouchScreenGUI::loadButtonTexture(button_info* btn, const char* path)
|
void TouchScreenGUI::loadButtonTexture(button_info* btn, const char* path, rect<s32> button_rect)
|
||||||
{
|
{
|
||||||
unsigned int tid;
|
unsigned int tid;
|
||||||
video::ITexture *texture = m_texturesource->getTexture(path,&tid);
|
video::ITexture *texture = guiScalingImageButton(m_device->getVideoDriver(),
|
||||||
|
m_texturesource->getTexture(path, &tid), button_rect.getWidth(), button_rect.getHeight());
|
||||||
if (texture) {
|
if (texture) {
|
||||||
btn->guibutton->setUseAlphaChannel(true);
|
btn->guibutton->setUseAlphaChannel(true);
|
||||||
btn->guibutton->setImage(texture);
|
if (g_settings->getBool("gui_scaling_filter")) {
|
||||||
btn->guibutton->setPressedImage(texture);
|
rect<s32> txr_rect = rect<s32>(0, 0, button_rect.getWidth(), button_rect.getHeight());
|
||||||
btn->guibutton->setScaleImage(true);
|
btn->guibutton->setImage(texture, txr_rect);
|
||||||
|
btn->guibutton->setPressedImage(texture, txr_rect);
|
||||||
|
btn->guibutton->setScaleImage(false);
|
||||||
|
} else {
|
||||||
|
btn->guibutton->setImage(texture);
|
||||||
|
btn->guibutton->setPressedImage(texture);
|
||||||
|
btn->guibutton->setScaleImage(true);
|
||||||
|
}
|
||||||
btn->guibutton->setDrawBorder(false);
|
btn->guibutton->setDrawBorder(false);
|
||||||
btn->guibutton->setText(L"");
|
btn->guibutton->setText(L"");
|
||||||
}
|
}
|
||||||
|
@ -157,7 +166,7 @@ void TouchScreenGUI::initButton(touch_gui_button_id id, rect<s32> button_rect,
|
||||||
btn->immediate_release = immediate_release;
|
btn->immediate_release = immediate_release;
|
||||||
btn->ids.clear();
|
btn->ids.clear();
|
||||||
|
|
||||||
loadButtonTexture(btn,touchgui_button_imagenames[id]);
|
loadButtonTexture(btn,touchgui_button_imagenames[id], button_rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int getMaxControlPadSize(float density) {
|
static int getMaxControlPadSize(float density) {
|
||||||
|
|
|
@ -130,7 +130,7 @@ private:
|
||||||
float repeat_delay = BUTTON_REPEAT_DELAY);
|
float repeat_delay = BUTTON_REPEAT_DELAY);
|
||||||
|
|
||||||
/* load texture */
|
/* load texture */
|
||||||
void loadButtonTexture(button_info* btn, const char* path);
|
void loadButtonTexture(button_info* btn, const char* path, rect<s32> button_rect);
|
||||||
|
|
||||||
struct id_status{
|
struct id_status{
|
||||||
int id;
|
int id;
|
||||||
|
|
|
@ -245,4 +245,3 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -411,5 +411,16 @@ inline bool is_power_of_two(u32 n)
|
||||||
return n != 0 && (n & (n-1)) == 0;
|
return n != 0 && (n & (n-1)) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
// Compute next-higher power of 2 efficiently, e.g. for power-of-2 texture sizes.
|
||||||
|
// Public Domain: https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
|
||||||
|
inline u32 npot2(u32 orig) {
|
||||||
|
orig--;
|
||||||
|
orig |= orig >> 1;
|
||||||
|
orig |= orig >> 2;
|
||||||
|
orig |= orig >> 4;
|
||||||
|
orig |= orig >> 8;
|
||||||
|
orig |= orig >> 16;
|
||||||
|
return orig + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue