mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Merge pull request #1640 from PeterTh/master
Texture scaling improvements
This commit is contained in:
commit
655e7dbfbb
11 changed files with 348 additions and 66 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -28,6 +28,7 @@ Windows/ipch
|
|||
|
||||
# For ppsspp.ini, etc.
|
||||
ppsspp.ini
|
||||
PPSSPPControls.dat
|
||||
|
||||
# Qt Linguist files
|
||||
*.qm
|
||||
|
|
|
@ -100,7 +100,6 @@ void Config::Load(const char *iniFileName)
|
|||
graphics->Get("SSAA", &SSAntiAliasing, 0);
|
||||
graphics->Get("VBO", &bUseVBO, false);
|
||||
graphics->Get("FrameSkip", &iFrameSkip, 0);
|
||||
graphics->Get("XBRZTexScalingLevel", &iXBRZTexScalingLevel, 1);
|
||||
graphics->Get("UseMediaEngine", &bUseMediaEngine, true);
|
||||
#ifdef USING_GLES2
|
||||
graphics->Get("AnisotropyLevel", &iAnisotropyLevel, 0);
|
||||
|
@ -116,6 +115,8 @@ void Config::Load(const char *iniFileName)
|
|||
#else
|
||||
graphics->Get("MipMap", &bMipMap, false);
|
||||
#endif
|
||||
graphics->Get("TexScalingLevel", &iTexScalingLevel, 1);
|
||||
graphics->Get("TexScalingType", &iTexScalingType, 0);
|
||||
|
||||
IniFile::Section *sound = iniFile.GetOrCreateSection("Sound");
|
||||
sound->Get("Enable", &bEnableSound, true);
|
||||
|
@ -190,7 +191,6 @@ void Config::Save()
|
|||
graphics->Set("SSAA", SSAntiAliasing);
|
||||
graphics->Set("VBO", bUseVBO);
|
||||
graphics->Set("FrameSkip", iFrameSkip);
|
||||
graphics->Set("XBRZTexScalingLevel", iXBRZTexScalingLevel);
|
||||
graphics->Set("UseMediaEngine", bUseMediaEngine);
|
||||
graphics->Set("AnisotropyLevel", iAnisotropyLevel);
|
||||
graphics->Set("VertexCache", bVertexCache);
|
||||
|
@ -198,7 +198,8 @@ void Config::Save()
|
|||
graphics->Set("StretchToDisplay", bStretchToDisplay);
|
||||
graphics->Set("TrueColor", bTrueColor);
|
||||
graphics->Set("MipMap", bMipMap);
|
||||
graphics->Set("XBRZTexScalingLevel", iXBRZTexScalingLevel);
|
||||
graphics->Set("TexScalingLevel", iTexScalingLevel);
|
||||
graphics->Set("TexScalingType", iTexScalingType);
|
||||
|
||||
IniFile::Section *sound = iniFile.GetOrCreateSection("Sound");
|
||||
sound->Set("Enable", bEnableSound);
|
||||
|
|
|
@ -72,7 +72,8 @@ public:
|
|||
int iAnisotropyLevel;
|
||||
bool bTrueColor;
|
||||
bool bMipMap;
|
||||
int iXBRZTexScalingLevel;
|
||||
int iTexScalingLevel; // 1 = off, 2 = 2x, ..., 5 = 5x
|
||||
int iTexScalingType; // 0 = xBRZ, 1 = Hybrid
|
||||
|
||||
// Sound
|
||||
bool bEnableSound;
|
||||
|
|
|
@ -1221,7 +1221,7 @@ void TextureCache::LoadTextureLevel(TexCacheEntry &entry, int level) {
|
|||
|
||||
u32* pixelData = (u32*)finalBuf;
|
||||
|
||||
int scaleFactor = g_Config.iXBRZTexScalingLevel;
|
||||
int scaleFactor = g_Config.iTexScalingLevel;
|
||||
|
||||
// Don't scale the PPGe texture.
|
||||
if (entry.addr > 0x05000000 && entry.addr < 0x08800000)
|
||||
|
|
|
@ -41,10 +41,11 @@ namespace p = std::placeholders;
|
|||
#endif
|
||||
|
||||
namespace {
|
||||
// convert 4444 image to 8888, parallelizable
|
||||
void convert4444(u16* data, u32* out, int width, int l, int u) {
|
||||
for(int y = l; y < u; ++y) {
|
||||
for(int x = 0; x < width; ++x) {
|
||||
u32 val = ((u16*)data)[y*width + x];
|
||||
u32 val = data[y*width + x];
|
||||
u32 r = ((val>>12) & 0xF) * 17;
|
||||
u32 g = ((val>> 8) & 0xF) * 17;
|
||||
u32 b = ((val>> 4) & 0xF) * 17;
|
||||
|
@ -54,10 +55,11 @@ namespace {
|
|||
}
|
||||
}
|
||||
|
||||
// convert 565 image to 8888, parallelizable
|
||||
void convert565(u16* data, u32* out, int width, int l, int u) {
|
||||
for(int y = l; y < u; ++y) {
|
||||
for(int x = 0; x < width; ++x) {
|
||||
u32 val = ((u16*)data)[y*width + x];
|
||||
u32 val = data[y*width + x];
|
||||
u32 r = Convert5To8((val>>11) & 0x1F);
|
||||
u32 g = Convert6To8((val>> 5) & 0x3F);
|
||||
u32 b = Convert5To8((val ) & 0x1F);
|
||||
|
@ -66,10 +68,11 @@ namespace {
|
|||
}
|
||||
}
|
||||
|
||||
// convert 5551 image to 8888, parallelizable
|
||||
void convert5551(u16* data, u32* out, int width, int l, int u) {
|
||||
for(int y = l; y < u; ++y) {
|
||||
for(int x = 0; x < width; ++x) {
|
||||
u32 val = ((u16*)data)[y*width + x];
|
||||
u32 val = data[y*width + x];
|
||||
u32 r = Convert5To8((val>>11) & 0x1F);
|
||||
u32 g = Convert5To8((val>> 6) & 0x1F);
|
||||
u32 b = Convert5To8((val>> 1) & 0x1F);
|
||||
|
@ -78,6 +81,174 @@ namespace {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3x3 convolution with Neumann boundary conditions, parallelizable
|
||||
// quite slow, could be sped up a lot
|
||||
// especially handling of separable kernels
|
||||
void convolve3x3(u32* data, u32* out, const int kernel[3][3], int width, int height, int l, int u) {
|
||||
for(int y = l; y < u; ++y) {
|
||||
for(int x = 0; x < width; ++x) {
|
||||
int val = 0;
|
||||
for(int yoff = -1; yoff <= 1; ++yoff) {
|
||||
int yy = std::max(std::min(y+yoff, height-1), 0);
|
||||
for(int xoff = -1; xoff <= 1; ++xoff) {
|
||||
int xx = std::max(std::min(x+xoff, width-1), 0);
|
||||
val += data[yy*width + xx] * kernel[yoff+1][xoff+1];
|
||||
}
|
||||
}
|
||||
out[y*width + x] = abs(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define R(_col) ((_col>> 0)&0xFF)
|
||||
#define G(_col) ((_col>> 8)&0xFF)
|
||||
#define B(_col) ((_col>>16)&0xFF)
|
||||
#define A(_col) ((_col>>24)&0xFF)
|
||||
|
||||
#define DISTANCE(_p1,_p2) ( abs((int)((int)(R(_p1))-R(_p2))) + abs((int)((int)(G(_p1))-G(_p2))) \
|
||||
+ abs((int)((int)(B(_p1)-B(_p2)))) + abs((int)((int)(A(_p1)-A(_p2)))) )
|
||||
|
||||
void generateDistanceMask(u32* data, u32* out, int width, int height, int l, int u) {
|
||||
for(int y = l; y < u; ++y) {
|
||||
for(int x = 0; x < width; ++x) {
|
||||
out[y*width + x] = 0;
|
||||
u32 center = data[y*width + x];
|
||||
for(int yoff = -1; yoff <= 1; ++yoff) {
|
||||
int yy = y+yoff;
|
||||
if(yy == height || yy == -1) {
|
||||
out[y*width + x] += 1200; // assume distance at borders, usually makes for better result
|
||||
continue;
|
||||
}
|
||||
for(int xoff = -1; xoff <= 1; ++xoff) {
|
||||
if(yoff == 0 && xoff == 0) continue;
|
||||
int xx = x+xoff;
|
||||
if(xx == width || xx == -1) {
|
||||
out[y*width + x] += 400; // assume distance at borders, usually makes for better result
|
||||
continue;
|
||||
}
|
||||
out[y*width + x] += DISTANCE(data[yy*width + xx], center);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this is sadly much faster than an inline function with a loop, at least in VC10
|
||||
#define MIX_PIXELS(p0, p1, p2, factors) \
|
||||
((R(p0)*factors[0] + R(p1)*factors[1] + R(p2)*factors[2])/255 << 0 ) | \
|
||||
((G(p0)*factors[0] + G(p1)*factors[1] + G(p2)*factors[2])/255 << 8 ) | \
|
||||
((B(p0)*factors[0] + B(p1)*factors[1] + B(p2)*factors[2])/255 << 16 ) | \
|
||||
((A(p0)*factors[0] + A(p1)*factors[1] + A(p2)*factors[2])/255 << 24 )
|
||||
|
||||
void mix(u32* data, u32* source, u32* mask, u32 maskmax, int width, int l, int u) {
|
||||
for(int y = l; y < u; ++y) {
|
||||
for(int x = 0; x < width; ++x) {
|
||||
int pos = y*width + x;
|
||||
u8 mixFactors[3] = {0, (std::min(mask[pos], maskmax)*255)/maskmax, 0 };
|
||||
mixFactors[0] = 255-mixFactors[1];
|
||||
data[pos] = MIX_PIXELS(data[pos], source[pos], 0, mixFactors);
|
||||
if(A(source[pos]) == 0) data[pos] = data[pos] & 0x00FFFFFF; // xBRZ always does a better job with hard alpha
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const static u8 BILINEAR_FACTORS[4][5][3] = {
|
||||
{ { 76,179, 0}, { 0,179, 76}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0} }, // x2
|
||||
{ { 85,170, 0}, { 0,255, 0}, { 0,170, 85}, { 0, 0, 0}, { 0, 0, 0} }, // x3
|
||||
{ {102,153, 0}, { 51,204, 0}, { 0,204, 51}, { 0,153,102}, { 0, 0, 0} }, // x4
|
||||
{ {102,153, 0}, { 51,204, 0}, { 0,255, 0}, { 0,204, 51}, { 0,153,102} }, // x5
|
||||
};
|
||||
// integral bilinear upscaling by factor f, horizontal part
|
||||
template<int f>
|
||||
void bilinearHt(u32* data, u32* out, int w, int l, int u) {
|
||||
static_assert(f>1 && f<=5, "Bilinear scaling only implemented for factors 2 to 5");
|
||||
int outw = w*f;
|
||||
for(int y = l; y < u; ++y) {
|
||||
for(int x = 0; x < w; ++x) {
|
||||
int inpos = y*w + x;
|
||||
u32 left = data[inpos - (x==0 ?0:1)];
|
||||
u32 center = data[inpos];
|
||||
u32 right = data[inpos + (x==w-1?0:1)];
|
||||
for(int i=0; i<f; ++i) { // hope the compiler unrolls this
|
||||
out[y*outw + x*f + i] = MIX_PIXELS(left, center, right, BILINEAR_FACTORS[f-2][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void bilinearH(int factor, u32* data, u32* out, int w, int l, int u) {
|
||||
switch(factor) {
|
||||
case 2: bilinearHt<2>(data, out, w, l, u); break;
|
||||
case 3: bilinearHt<3>(data, out, w, l, u); break;
|
||||
case 4: bilinearHt<4>(data, out, w, l, u); break;
|
||||
case 5: bilinearHt<5>(data, out, w, l, u); break;
|
||||
default: ERROR_LOG(G3D, "Bilinear upsampling only implemented for factors 2 to 5");
|
||||
}
|
||||
}
|
||||
// integral bilinear upscaling by factor f, vertical part
|
||||
// gl/gu == global lower and upper bound
|
||||
template<int f>
|
||||
void bilinearVt(u32* data, u32* out, int w, int gl, int gu, int l, int u) {
|
||||
static_assert(f>1 && f<=5, "Bilinear scaling only implemented for 2x, 3x, 4x, and 5x");
|
||||
int outw = w*f;
|
||||
for(int y = l; y < u; ++y) {
|
||||
for(int x = 0; x < outw; ++x) {
|
||||
u32 upper = data[(y - (y==gl ?0:1)) * outw + x];
|
||||
u32 center = data[y * outw + x];
|
||||
u32 lower = data[(y + (y==gu-1?0:1)) * outw + x];
|
||||
for(int i=0; i<f; ++i) { // hope the compiler unrolls this
|
||||
out[(y*f + i)*outw + x] = MIX_PIXELS(upper, center, lower, BILINEAR_FACTORS[f-2][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void bilinearV(int factor, u32* data, u32* out, int w, int gl, int gu, int l, int u) {
|
||||
switch(factor) {
|
||||
case 2: bilinearVt<2>(data, out, w, gl, gu, l, u); break;
|
||||
case 3: bilinearVt<3>(data, out, w, gl, gu, l, u); break;
|
||||
case 4: bilinearVt<4>(data, out, w, gl, gu, l, u); break;
|
||||
case 5: bilinearVt<5>(data, out, w, gl, gu, l, u); break;
|
||||
default: ERROR_LOG(G3D, "Bilinear upsampling only implemented for factors 2 to 5");
|
||||
}
|
||||
}
|
||||
|
||||
#undef MIX_PIXELS
|
||||
#undef DISTANCE
|
||||
#undef R
|
||||
#undef G
|
||||
#undef B
|
||||
#undef A
|
||||
|
||||
// used for debugging texture scaling (writing textures to files)
|
||||
static int g_imgCount = 0;
|
||||
void dbgPPM(int w, int h, u8* pixels, const char* prefix = "dbg") { // 3 component RGB
|
||||
char fn[32];
|
||||
snprintf(fn, 32, "%s%04d.ppm", prefix, g_imgCount++);
|
||||
FILE *fp = fopen(fn, "wb");
|
||||
fprintf(fp, "P6\n%d %d\n255\n", w, h);
|
||||
for(int j = 0; j < h; ++j) {
|
||||
for(int i = 0; i < w; ++i) {
|
||||
static unsigned char color[3];
|
||||
color[0] = pixels[(j*w+i)*4+0]; /* red */
|
||||
color[1] = pixels[(j*w+i)*4+1]; /* green */
|
||||
color[2] = pixels[(j*w+i)*4+2]; /* blue */
|
||||
fwrite(color, 1, 3, fp);
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
void dbgPGM(int w, int h, u32* pixels, const char* prefix = "dbg") { // 1 component
|
||||
char fn[32];
|
||||
snprintf(fn, 32, "%s%04d.pgm", prefix, g_imgCount++);
|
||||
FILE *fp = fopen(fn, "wb");
|
||||
fprintf(fp, "P5\n%d %d\n65536\n", w, h);
|
||||
for(int j = 0; j < h; ++j) {
|
||||
for(int i = 0; i < w; ++i) {
|
||||
fwrite((pixels+(j*w+i)), 1, 2, fp);
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -89,40 +260,28 @@ void TextureScaler::Scale(u32* &data, GLenum &dstFmt, int &width, int &height, i
|
|||
double t_start = real_time_now();
|
||||
#endif
|
||||
|
||||
// depending on the factor and texture sizes, these can be pretty large (25 MB for a 512 by 512 texture with scaling factor 5)
|
||||
bufInput.resize(width*height); // used to store the input image image if it needs to be reformatted
|
||||
bufOutput.resize(width*height*factor*factor); // used to store the upscaled image
|
||||
u32 *xbrzInputBuf = bufInput.data();
|
||||
u32 *xbrzBuf = bufOutput.data();
|
||||
u32 *inputBuf = bufInput.data();
|
||||
u32 *outputBuf = bufOutput.data();
|
||||
|
||||
// convert texture to correct format for xBRZ
|
||||
switch(dstFmt) {
|
||||
case GL_UNSIGNED_BYTE:
|
||||
xbrzInputBuf = data; // already fine
|
||||
break;
|
||||
|
||||
case GL_UNSIGNED_SHORT_4_4_4_4:
|
||||
GlobalThreadPool::Loop(bind(&convert4444, (u16*)data, xbrzInputBuf, width, p::_1, p::_2), 0, height);
|
||||
break;
|
||||
|
||||
case GL_UNSIGNED_SHORT_5_6_5:
|
||||
GlobalThreadPool::Loop(bind(&convert565, (u16*)data, xbrzInputBuf, width, p::_1, p::_2), 0, height);
|
||||
break;
|
||||
|
||||
case GL_UNSIGNED_SHORT_5_5_5_1:
|
||||
GlobalThreadPool::Loop(bind(&convert5551, (u16*)data, xbrzInputBuf, width, p::_1, p::_2), 0, height);
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR_LOG(G3D, "iXBRZTexScaling: unsupported texture format");
|
||||
}
|
||||
// convert texture to correct format for scaling
|
||||
ConvertTo8888(dstFmt, data, inputBuf, width, height);
|
||||
|
||||
// scale
|
||||
xbrz::ScalerCfg cfg;
|
||||
GlobalThreadPool::Loop(bind(&xbrz::scale, factor, xbrzInputBuf, xbrzBuf, width, height, cfg, p::_1, p::_2), 0, height);
|
||||
switch(g_Config.iTexScalingType) {
|
||||
case XBRZ:
|
||||
ScaleXBRZ(factor, inputBuf, outputBuf, width, height);
|
||||
break;
|
||||
case HYBRID:
|
||||
ScaleHybrid(factor, inputBuf, outputBuf, width, height);
|
||||
break;
|
||||
default:
|
||||
ERROR_LOG(G3D, "Unknown scaling type: %d", g_Config.iTexScalingType);
|
||||
}
|
||||
|
||||
// update values accordingly
|
||||
data = xbrzBuf;
|
||||
data = outputBuf;
|
||||
dstFmt = GL_UNSIGNED_BYTE;
|
||||
width *= factor;
|
||||
height *= factor;
|
||||
|
@ -135,3 +294,68 @@ void TextureScaler::Scale(u32* &data, GLenum &dstFmt, int &width, int &height, i
|
|||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void TextureScaler::ScaleXBRZ(int factor, u32* source, u32* dest, int width, int height) {
|
||||
xbrz::ScalerCfg cfg;
|
||||
GlobalThreadPool::Loop(std::bind(&xbrz::scale, factor, source, dest, width, height, cfg, p::_1, p::_2), 0, height);
|
||||
}
|
||||
|
||||
void TextureScaler::ScaleBilinear(int factor, u32* source, u32* dest, int width, int height) {
|
||||
bufTmp1.resize(width*height*factor);
|
||||
u32 *tmpBuf = bufTmp1.data();
|
||||
GlobalThreadPool::Loop(std::bind(&bilinearH, factor, source, tmpBuf, width, p::_1, p::_2), 0, height);
|
||||
GlobalThreadPool::Loop(std::bind(&bilinearV, factor, tmpBuf, dest, width, 0, height, p::_1, p::_2), 0, height);
|
||||
}
|
||||
|
||||
void TextureScaler::ScaleHybrid(int factor, u32* source, u32* dest, int width, int height) {
|
||||
// Basic algorithm:
|
||||
// 1) determine a feature mask C based on a sobel-ish filter + splatting, and upscale that mask bilinearly
|
||||
// 2) generate 2 scaled images: A - using Bilinear filtering, B - using xBRZ
|
||||
// 3) output = A*C + B*(1-C)
|
||||
|
||||
const static int KERNEL_SPLAT[3][3] = {
|
||||
{ 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }
|
||||
};
|
||||
|
||||
bufTmp1.resize(width*height);
|
||||
bufTmp2.resize(width*height*factor*factor);
|
||||
bufTmp3.resize(width*height*factor*factor);
|
||||
GlobalThreadPool::Loop(std::bind(&generateDistanceMask, source, bufTmp1.data(), width, height, p::_1, p::_2), 0, height);
|
||||
GlobalThreadPool::Loop(std::bind(&convolve3x3, bufTmp1.data(), bufTmp2.data(), KERNEL_SPLAT, width, height, p::_1, p::_2), 0, height);
|
||||
ScaleBilinear(factor, bufTmp2.data(), bufTmp3.data(), width, height);
|
||||
// mask C is now in bufTmp3
|
||||
|
||||
ScaleXBRZ(factor, source, bufTmp2.data(), width, height);
|
||||
// xBRZ upscaled source is in bufTmp2
|
||||
|
||||
ScaleBilinear(factor, source, dest, width, height);
|
||||
// Bilinear upscaled source is in dest
|
||||
|
||||
// Now we can mix it all together
|
||||
// The factor 8192 was found through practical testing on a variety of textures
|
||||
GlobalThreadPool::Loop(std::bind(&mix, dest, bufTmp2.data(), bufTmp3.data(), 8192, width*factor, p::_1, p::_2), 0, height*factor);
|
||||
}
|
||||
|
||||
void TextureScaler::ConvertTo8888(GLenum format, u32* source, u32* &dest, int width, int height) {
|
||||
switch(format) {
|
||||
case GL_UNSIGNED_BYTE:
|
||||
dest = source; // already fine
|
||||
break;
|
||||
|
||||
case GL_UNSIGNED_SHORT_4_4_4_4:
|
||||
GlobalThreadPool::Loop(std::bind(&convert4444, (u16*)source, dest, width, p::_1, p::_2), 0, height);
|
||||
break;
|
||||
|
||||
case GL_UNSIGNED_SHORT_5_6_5:
|
||||
GlobalThreadPool::Loop(std::bind(&convert565, (u16*)source, dest, width, p::_1, p::_2), 0, height);
|
||||
break;
|
||||
|
||||
case GL_UNSIGNED_SHORT_5_5_5_1:
|
||||
GlobalThreadPool::Loop(std::bind(&convert5551, (u16*)source, dest, width, p::_1, p::_2), 0, height);
|
||||
break;
|
||||
|
||||
default:
|
||||
dest = source;
|
||||
ERROR_LOG(G3D, "iXBRZTexScaling: unsupported texture format");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,16 @@ public:
|
|||
|
||||
void Scale(u32* &data, GLenum &dstfmt, int &width, int &height, int factor);
|
||||
|
||||
enum { XBRZ= 0, HYBRID = 1 };
|
||||
|
||||
private:
|
||||
SimpleBuf<u32> bufInput;
|
||||
SimpleBuf<u32> bufOutput;
|
||||
void ScaleXBRZ(int factor, u32* source, u32* dest, int width, int height);
|
||||
void ScaleBilinear(int factor, u32* source, u32* dest, int width, int height);
|
||||
void ScaleHybrid(int factor, u32* source, u32* dest, int width, int height);
|
||||
void ConvertTo8888(GLenum format, u32* source, u32* &dest, int width, int height);
|
||||
|
||||
// depending on the factor and texture sizes, these can get pretty large
|
||||
// maximum is (100 MB total for a 512 by 512 texture with scaling factor 5 and hybrid scaling)
|
||||
// of course, scaling factor 5 is totally silly anyway
|
||||
SimpleBuf<u32> bufInput, bufOutput, bufTmp1, bufTmp2, bufTmp3;
|
||||
};
|
||||
|
|
|
@ -756,20 +756,20 @@ void GraphicsScreenP2::render() {
|
|||
g_Config.iAnisotropyLevel = 0;
|
||||
}
|
||||
|
||||
bool XBRZTexScaling = g_Config.iXBRZTexScalingLevel > 1;
|
||||
UICheckBox(GEN_ID, x, y += stride, gs->T("xBRZ Texture Scaling"), ALIGN_TOPLEFT, &XBRZTexScaling);
|
||||
if (XBRZTexScaling) {
|
||||
if (g_Config.iXBRZTexScalingLevel <= 1)
|
||||
g_Config.iXBRZTexScalingLevel = 2;
|
||||
bool TexScaling = g_Config.iTexScalingLevel > 1;
|
||||
UICheckBox(GEN_ID, x, y += stride, gs->T("xBRZ Texture Scaling"), ALIGN_TOPLEFT, &TexScaling);
|
||||
if (TexScaling) {
|
||||
if (g_Config.iTexScalingLevel <= 1)
|
||||
g_Config.iTexScalingLevel = 2;
|
||||
|
||||
ui_draw2d.DrawText(UBUNTU24, gs->T("Level :"), x + 60, y += stride + 10, 0xFFFFFFFF, ALIGN_LEFT);
|
||||
HLinear hlinear1(x + 160 , y + 5, 20);
|
||||
if (UIButton(GEN_ID, hlinear1, 45, 0, "2x", ALIGN_LEFT))
|
||||
g_Config.iXBRZTexScalingLevel = 2;
|
||||
g_Config.iTexScalingLevel = 2;
|
||||
if (UIButton(GEN_ID, hlinear1, 45, 0, "3x", ALIGN_LEFT))
|
||||
g_Config.iXBRZTexScalingLevel = 3;
|
||||
g_Config.iTexScalingLevel = 3;
|
||||
} else {
|
||||
g_Config.iXBRZTexScalingLevel = 1;
|
||||
g_Config.iTexScalingLevel = 1;
|
||||
}
|
||||
UIEnd();
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "GPU/GPUInterface.h"
|
||||
#include "GPU/GPUState.h"
|
||||
#include "native/image/png_load.h"
|
||||
#include "GPU/GLES/TextureScaler.h"
|
||||
|
||||
#ifdef THEMES
|
||||
#include "XPTheme.h"
|
||||
|
@ -175,8 +176,12 @@ namespace MainWindow
|
|||
ResizeDisplay();
|
||||
}
|
||||
|
||||
void setXbrzTexScaling(int num) {
|
||||
g_Config.iXBRZTexScalingLevel = num;
|
||||
void setTexScalingLevel(int num) {
|
||||
g_Config.iTexScalingLevel = num;
|
||||
if(gpu) gpu->ClearCacheNextFrame();
|
||||
}
|
||||
void setTexScalingType(int num) {
|
||||
g_Config.iTexScalingType = num;
|
||||
if(gpu) gpu->ClearCacheNextFrame();
|
||||
}
|
||||
|
||||
|
@ -501,19 +506,26 @@ namespace MainWindow
|
|||
break;
|
||||
|
||||
case ID_TEXTURESCALING_OFF:
|
||||
setXbrzTexScaling(1);
|
||||
setTexScalingLevel(1);
|
||||
break;
|
||||
case ID_TEXTURESCALING_2XBRZ:
|
||||
setXbrzTexScaling(2);
|
||||
case ID_TEXTURESCALING_2X:
|
||||
setTexScalingLevel(2);
|
||||
break;
|
||||
case ID_TEXTURESCALING_3XBRZ:
|
||||
setXbrzTexScaling(3);
|
||||
case ID_TEXTURESCALING_3X:
|
||||
setTexScalingLevel(3);
|
||||
break;
|
||||
case ID_TEXTURESCALING_4XBRZ:
|
||||
setXbrzTexScaling(4);
|
||||
case ID_TEXTURESCALING_4X:
|
||||
setTexScalingLevel(4);
|
||||
break;
|
||||
case ID_TEXTURESCALING_5XBRZ:
|
||||
setXbrzTexScaling(5);
|
||||
case ID_TEXTURESCALING_5X:
|
||||
setTexScalingLevel(5);
|
||||
break;
|
||||
|
||||
case ID_TEXTURESCALING_XBRZ:
|
||||
setTexScalingType(TextureScaler::XBRZ);
|
||||
break;
|
||||
case ID_TEXTURESCALING_HYBRID:
|
||||
setTexScalingType(TextureScaler::HYBRID);
|
||||
break;
|
||||
|
||||
case ID_OPTIONS_BUFFEREDRENDERING:
|
||||
|
@ -824,13 +836,21 @@ namespace MainWindow
|
|||
|
||||
static const int texscalingitems[] = {
|
||||
ID_TEXTURESCALING_OFF,
|
||||
ID_TEXTURESCALING_2XBRZ,
|
||||
ID_TEXTURESCALING_3XBRZ,
|
||||
ID_TEXTURESCALING_4XBRZ,
|
||||
ID_TEXTURESCALING_5XBRZ,
|
||||
ID_TEXTURESCALING_2X,
|
||||
ID_TEXTURESCALING_3X,
|
||||
ID_TEXTURESCALING_4X,
|
||||
ID_TEXTURESCALING_5X,
|
||||
};
|
||||
for (int i = 0; i < 5; i++) {
|
||||
CheckMenuItem(menu, texscalingitems[i], MF_BYCOMMAND | ((i == g_Config.iXBRZTexScalingLevel-1) ? MF_CHECKED : MF_UNCHECKED));
|
||||
CheckMenuItem(menu, texscalingitems[i], MF_BYCOMMAND | ((i == g_Config.iTexScalingLevel-1) ? MF_CHECKED : MF_UNCHECKED));
|
||||
}
|
||||
|
||||
static const int texscalingtypeitems[] = {
|
||||
ID_TEXTURESCALING_XBRZ,
|
||||
ID_TEXTURESCALING_HYBRID,
|
||||
};
|
||||
for (int i = 0; i < 3; i++) {
|
||||
CheckMenuItem(menu, texscalingtypeitems[i], MF_BYCOMMAND | ((i == g_Config.iTexScalingType) ? MF_CHECKED : MF_UNCHECKED));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -26,9 +26,10 @@ unsigned char getByte(uint32_t val) { return static_cast<unsigned char>((val >>
|
|||
|
||||
// adjusted for RGBA
|
||||
// - Durante
|
||||
inline unsigned char getRed (uint32_t val) { return getByte<3>(val); }
|
||||
inline unsigned char getGreen(uint32_t val) { return getByte<2>(val); }
|
||||
inline unsigned char getBlue (uint32_t val) { return getByte<1>(val); }
|
||||
inline unsigned char getRed (uint32_t val) { return getByte<0>(val); }
|
||||
inline unsigned char getGreen(uint32_t val) { return getByte<1>(val); }
|
||||
inline unsigned char getBlue (uint32_t val) { return getByte<2>(val); }
|
||||
inline unsigned char getAlpha(uint32_t val) { return getByte<3>(val); }
|
||||
|
||||
template <class T> inline
|
||||
T abs(T value)
|
||||
|
@ -396,6 +397,31 @@ double distYCbCr(uint32_t pix1, uint32_t pix2, double lumaWeight)
|
|||
return std::sqrt(square(lumaWeight * y) + square(c_b) + square(c_r));
|
||||
}
|
||||
|
||||
// distance function taking alpha distance into account
|
||||
inline
|
||||
double distYCbCrA(uint32_t pix1, uint32_t pix2, double lumaWeight)
|
||||
{
|
||||
//http://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion
|
||||
//YCbCr conversion is a matrix multiplication => take advantage of linearity by subtracting first!
|
||||
const int r_diff = static_cast<int>(getRed (pix1)) - getRed (pix2); //we may delay division by 255 to after matrix multiplication
|
||||
const int g_diff = static_cast<int>(getGreen(pix1)) - getGreen(pix2); //
|
||||
const int b_diff = static_cast<int>(getBlue (pix1)) - getBlue (pix2); //substraction for int is noticeable faster than for double!
|
||||
|
||||
const double k_b = 0.0722; //ITU-R BT.709 conversion
|
||||
const double k_r = 0.2126; //
|
||||
const double k_g = 1 - k_b - k_r;
|
||||
|
||||
const double scale_b = 0.5 / (1 - k_b);
|
||||
const double scale_r = 0.5 / (1 - k_r);
|
||||
|
||||
const double y = k_r * r_diff + k_g * g_diff + k_b * b_diff; //[!], analog YCbCr!
|
||||
const double c_b = scale_b * (b_diff - y);
|
||||
const double c_r = scale_r * (r_diff - y);
|
||||
|
||||
//we skip division by 255 to have similar range like other distance functions
|
||||
return std::sqrt(square(lumaWeight * y) + square(c_b) + square(c_r)+ square(static_cast<int>(getAlpha(pix1)) - getAlpha(pix2)));
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
double distYUV(uint32_t pix1, uint32_t pix2, double luminanceWeight)
|
||||
|
@ -443,8 +469,8 @@ double colorDist(uint32_t pix1, uint32_t pix2, double luminanceWeight)
|
|||
//return distLAB(pix1, pix2);
|
||||
//return distNonLinearRGB(pix1, pix2);
|
||||
//return distYUV(pix1, pix2, luminanceWeight);
|
||||
|
||||
return distYCbCr(pix1, pix2, luminanceWeight);
|
||||
//return distYCbCr(pix1, pix2, luminanceWeight);
|
||||
return distYCbCrA(pix1, pix2, luminanceWeight);
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue