mirror of
https://github.com/mkxp-z/mkxp-z.git
synced 2025-04-21 21:52:04 +02:00
Merge c41affaef3
into 68a344afcf
This commit is contained in:
commit
d3609b8732
7 changed files with 211 additions and 1 deletions
|
@ -84,6 +84,7 @@
|
|||
// 2: Bicubic
|
||||
// 3: Lanczos3
|
||||
// 4: xBRZ
|
||||
// 5: Area
|
||||
// (default: 0)
|
||||
//
|
||||
// "smoothScaling": 0,
|
||||
|
|
139
shader/area.frag
Normal file
139
shader/area.frag
Normal file
|
@ -0,0 +1,139 @@
|
|||
// From https://raw.githubusercontent.com/dolphin-emu/dolphin/39e266c5bf66a5626ead9cc8295e9638e305e7fc/Data/Sys/Shaders/default_pre_post_process.glsl
|
||||
// mkxp-z modifications by Splendide Imaginarius.
|
||||
// Public domain.
|
||||
|
||||
#ifdef GLSLES
|
||||
precision highp float;
|
||||
#endif
|
||||
|
||||
uniform sampler2D texture;
|
||||
uniform vec2 sourceSize;
|
||||
uniform vec2 texSizeInv;
|
||||
uniform vec2 targetSize;
|
||||
uniform vec2 targetSizeInv;
|
||||
varying vec2 v_texCoord;
|
||||
|
||||
/***** COLOR SAMPLING *****/
|
||||
|
||||
// Non filtered sample (nearest neighbor)
|
||||
vec4 QuickSample(vec2 uv)
|
||||
{
|
||||
return texture2D(texture, uv);
|
||||
}
|
||||
vec4 QuickSampleByPixel(vec2 xy)
|
||||
{
|
||||
vec2 uv = vec2(xy * texSizeInv);
|
||||
return QuickSample(uv);
|
||||
}
|
||||
|
||||
/***** Area Sampling *****/
|
||||
|
||||
// By Sam Belliveau and Filippo Tarpini. Public Domain license.
|
||||
// Effectively a more accurate sharp bilinear filter when upscaling,
|
||||
// that also works as a mathematically perfect downscale filter.
|
||||
// https://entropymine.com/imageworsener/pixelmixing/
|
||||
// https://github.com/obsproject/obs-studio/pull/1715
|
||||
// https://legacy.imagemagick.org/Usage/filter/
|
||||
vec4 AreaSampling(vec2 uv)
|
||||
{
|
||||
// Compute the top-left and bottom-right corners of the target pixel box.
|
||||
vec2 t_beg = floor(uv * targetSize);
|
||||
vec2 t_end = t_beg + vec2(1.0, 1.0);
|
||||
|
||||
// Convert the target pixel box to source pixel box.
|
||||
vec2 beg = t_beg * targetSizeInv * sourceSize;
|
||||
vec2 end = t_end * targetSizeInv * sourceSize;
|
||||
|
||||
// Compute the top-left and bottom-right corners of the pixel box.
|
||||
vec2 f_beg = floor(beg);
|
||||
vec2 f_end = floor(end);
|
||||
|
||||
// Compute how much of the start and end pixels are covered horizontally & vertically.
|
||||
float area_w = 1.0 - fract(beg.x);
|
||||
float area_n = 1.0 - fract(beg.y);
|
||||
float area_e = fract(end.x);
|
||||
float area_s = fract(end.y);
|
||||
|
||||
// Compute the areas of the corner pixels in the pixel box.
|
||||
float area_nw = area_n * area_w;
|
||||
float area_ne = area_n * area_e;
|
||||
float area_sw = area_s * area_w;
|
||||
float area_se = area_s * area_e;
|
||||
|
||||
// Initialize the color accumulator.
|
||||
vec4 avg_color = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
|
||||
// Prevents rounding errors due to the coordinates flooring above
|
||||
const vec2 offset = vec2(0.5, 0.5);
|
||||
|
||||
// Accumulate corner pixels.
|
||||
avg_color += area_nw * QuickSampleByPixel(vec2(f_beg.x, f_beg.y) + offset);
|
||||
avg_color += area_ne * QuickSampleByPixel(vec2(f_end.x, f_beg.y) + offset);
|
||||
avg_color += area_sw * QuickSampleByPixel(vec2(f_beg.x, f_end.y) + offset);
|
||||
avg_color += area_se * QuickSampleByPixel(vec2(f_end.x, f_end.y) + offset);
|
||||
|
||||
// Determine the size of the pixel box.
|
||||
int x_range = int(f_end.x - f_beg.x - 0.5);
|
||||
int y_range = int(f_end.y - f_beg.y - 0.5);
|
||||
|
||||
// Workaround to compile the shader with DX11/12.
|
||||
// If this isn't done, it will complain that the loop could have too many iterations.
|
||||
// This number should be enough to guarantee downscaling from very high to very small resolutions.
|
||||
// Note that this number might be referenced in the UI.
|
||||
const int max_iterations = 16;
|
||||
|
||||
// Fix up the average calculations in case we reached the upper limit
|
||||
x_range = min(x_range, max_iterations);
|
||||
y_range = min(y_range, max_iterations);
|
||||
|
||||
// Accumulate top and bottom edge pixels.
|
||||
for (int ix = 0; ix < max_iterations; ++ix)
|
||||
{
|
||||
if (ix < x_range)
|
||||
{
|
||||
float x = f_beg.x + 1.0 + float(ix);
|
||||
avg_color += area_n * QuickSampleByPixel(vec2(x, f_beg.y) + offset);
|
||||
avg_color += area_s * QuickSampleByPixel(vec2(x, f_end.y) + offset);
|
||||
}
|
||||
}
|
||||
|
||||
// Accumulate left and right edge pixels and all the pixels in between.
|
||||
for (int iy = 0; iy < max_iterations; ++iy)
|
||||
{
|
||||
if (iy < y_range)
|
||||
{
|
||||
float y = f_beg.y + 1.0 + float(iy);
|
||||
|
||||
avg_color += area_w * QuickSampleByPixel(vec2(f_beg.x, y) + offset);
|
||||
avg_color += area_e * QuickSampleByPixel(vec2(f_end.x, y) + offset);
|
||||
|
||||
for (int ix = 0; ix < max_iterations; ++ix)
|
||||
{
|
||||
if (ix < x_range)
|
||||
{
|
||||
float x = f_beg.x + 1.0 + float(ix);
|
||||
avg_color += QuickSampleByPixel(vec2(x, y) + offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the area of the pixel box that was sampled.
|
||||
float area_corners = area_nw + area_ne + area_sw + area_se;
|
||||
float area_edges = float(x_range) * (area_n + area_s) + float(y_range) * (area_w + area_e);
|
||||
float area_center = float(x_range) * float(y_range);
|
||||
|
||||
// Return the normalized average color.
|
||||
return avg_color / (area_corners + area_edges + area_center);
|
||||
}
|
||||
|
||||
/***** Main Functions *****/
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 color;
|
||||
|
||||
color = AreaSampling(v_texCoord);
|
||||
|
||||
gl_FragColor = color;
|
||||
}
|
|
@ -17,6 +17,7 @@ embedded_shaders = [
|
|||
'flashMap.frag',
|
||||
'bicubic.frag',
|
||||
'lanczos3.frag',
|
||||
'area.frag',
|
||||
'minimal.vert',
|
||||
'simple.vert',
|
||||
'simpleColor.vert',
|
||||
|
|
|
@ -240,6 +240,17 @@ static void _blitBegin(FBO::ID fbo, const Vec2i &size, int scaleIsSpecial)
|
|||
|
||||
break;
|
||||
#endif
|
||||
case Area:
|
||||
{
|
||||
AreaShader &shader = shState->shaders().area;
|
||||
shader.bind();
|
||||
shader.applyViewportProj();
|
||||
shader.setTranslation(Vec2i());
|
||||
shader.setTexSize(Vec2i(size.x, size.y));
|
||||
shader.setTargetSize(Vec2(size.x, size.y)); // Dummy value
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
{
|
||||
SimpleShader &shader = shState->shaders().simple;
|
||||
|
@ -336,6 +347,14 @@ void blitSource(TEXFBO &source, int scaleIsSpecial)
|
|||
|
||||
break;
|
||||
#endif
|
||||
case Area:
|
||||
{
|
||||
AreaShader &shader = shState->shaders().area;
|
||||
shader.bind();
|
||||
shader.setTexSize(Vec2i(blitSrcWidthHires, blitSrcHeightHires));
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
{
|
||||
SimpleShader &shader = shState->shaders().simple;
|
||||
|
@ -381,13 +400,30 @@ void blitRectangle(const IntRect &src, const IntRect &dst, bool smooth)
|
|||
}
|
||||
else
|
||||
{
|
||||
int scaleIsSpecial = UpScale;
|
||||
if (abs(scaledDstWidth) == abs(scaledSrcWidth) && abs(scaledDstHeight) == abs(scaledSrcHeight))
|
||||
{
|
||||
scaleIsSpecial = SameScale;
|
||||
}
|
||||
if (abs(scaledDstWidth) < abs(scaledSrcWidth) && abs(scaledDstHeight) < abs(scaledSrcHeight))
|
||||
{
|
||||
scaleIsSpecial = DownScale;
|
||||
}
|
||||
int method = smoothScalingMethod(scaleIsSpecial);
|
||||
|
||||
#ifdef MKXPZ_SSL
|
||||
if (shState->config().smoothScaling == xBRZ)
|
||||
if (method == xBRZ)
|
||||
{
|
||||
XbrzShader &shader = shState->shaders().xbrz;
|
||||
shader.setTargetScale(Vec2((float)(shState->config().xbrzScalingFactor), (float)(shState->config().xbrzScalingFactor)));
|
||||
}
|
||||
#endif
|
||||
if (method == Area)
|
||||
{
|
||||
AreaShader &shader = shState->shaders().area;
|
||||
// Sometimes the dest height is negative, but the area shader can't handle that, so take abs of it.
|
||||
shader.setTargetSize(Vec2((float)blitSrcWidthHires * (float)scaledDstWidth / (float)scaledSrcWidth, (float)blitSrcHeightHires * (float)abs(scaledDstHeight) / (float)scaledSrcHeight));
|
||||
}
|
||||
if (smooth)
|
||||
TEX::setSmooth(true);
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#ifdef MKXPZ_SSL
|
||||
#include "xbrz.frag.xxd"
|
||||
#endif
|
||||
#include "area.frag.xxd"
|
||||
#include "minimal.vert.xxd"
|
||||
#include "simple.vert.xxd"
|
||||
#include "simpleColor.vert.xxd"
|
||||
|
@ -869,3 +870,21 @@ void XbrzShader::setTargetScale(const Vec2 &value)
|
|||
gl.Uniform2f(u_targetScale, value.x, value.y);
|
||||
}
|
||||
#endif
|
||||
|
||||
AreaShader::AreaShader()
|
||||
{
|
||||
INIT_SHADER(simple, area, AreaShader);
|
||||
|
||||
ShaderBase::init();
|
||||
|
||||
GET_U(texOffsetX);
|
||||
GET_U(sourceSize);
|
||||
GET_U(targetSize);
|
||||
GET_U(targetSizeInv);
|
||||
}
|
||||
|
||||
void AreaShader::setTargetSize(const Vec2 &value)
|
||||
{
|
||||
gl.Uniform2f(u_targetSize, value.x, value.y);
|
||||
gl.Uniform2f(u_targetSizeInv, 1.f / value.x, 1.f / value.y);
|
||||
}
|
||||
|
|
|
@ -365,6 +365,18 @@ protected:
|
|||
};
|
||||
#endif
|
||||
|
||||
class AreaShader : public Lanczos3Shader
|
||||
{
|
||||
public:
|
||||
AreaShader();
|
||||
|
||||
void setTargetSize(const Vec2 &value);
|
||||
|
||||
protected:
|
||||
GLint u_targetSize;
|
||||
GLint u_targetSizeInv;
|
||||
};
|
||||
|
||||
class Lanczos3SpriteShader : public SimpleSpriteShader
|
||||
{
|
||||
public:
|
||||
|
@ -424,6 +436,7 @@ struct ShaderSet
|
|||
#ifdef MKXPZ_SSL
|
||||
XbrzShader xbrz;
|
||||
#endif
|
||||
AreaShader area;
|
||||
Lanczos3SpriteShader lanczos3Sprite;
|
||||
BicubicSpriteShader bicubicSprite;
|
||||
#ifdef MKXPZ_SSL
|
||||
|
|
|
@ -209,6 +209,7 @@ enum InterpolationMethod
|
|||
#ifdef MKXPZ_SSL
|
||||
xBRZ = 4,
|
||||
#endif
|
||||
Area = 5,
|
||||
};
|
||||
|
||||
enum SpecialScale
|
||||
|
|
Loading…
Add table
Reference in a new issue