mirror of
https://github.com/libretro/beetle-psx-libretro.git
synced 2024-11-24 01:09:51 +00:00
Merge pull request #170 from hizzlekizzle/master
add SABR and 6xBRZ texture filters
This commit is contained in:
commit
5d002c6031
26
libretro.cpp
26
libretro.cpp
@ -1711,10 +1711,10 @@ static int Load(const char *name, MDFNFILE *fp)
|
||||
static int LoadCD(std::vector<CDIF *> *CDInterfaces)
|
||||
{
|
||||
InitCommon(CDInterfaces);
|
||||
|
||||
|
||||
if (psx_skipbios == 1)
|
||||
BIOSROM->WriteU32(0x6990, 0);
|
||||
|
||||
|
||||
MDFNGameInfo->GameType = GMT_CDROM;
|
||||
|
||||
return(1);
|
||||
@ -2567,7 +2567,7 @@ static void check_variables(bool startup)
|
||||
#endif
|
||||
|
||||
var.key = option_cpu_overclock;
|
||||
|
||||
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
||||
{
|
||||
if (strcmp(var.value, "enabled") == 0)
|
||||
@ -2577,7 +2577,7 @@ static void check_variables(bool startup)
|
||||
}
|
||||
else
|
||||
psx_cpu_overclock = false;
|
||||
|
||||
|
||||
var.key = option_skip_bios;
|
||||
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
||||
@ -2586,8 +2586,8 @@ static void check_variables(bool startup)
|
||||
psx_skipbios = 1;
|
||||
else
|
||||
psx_skipbios = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var.key = option_widescreen_hack;
|
||||
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
||||
@ -2858,7 +2858,7 @@ static void check_variables(bool startup)
|
||||
display_internal_framerate = false;
|
||||
|
||||
var.key = option_crop_overscan;
|
||||
|
||||
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
||||
{
|
||||
if (strcmp(var.value, "enabled") == 0)
|
||||
@ -3987,7 +3987,7 @@ void retro_set_environment(retro_environment_t cb)
|
||||
static const struct retro_variable vars[] = {
|
||||
#if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES) || defined(HAVE_VULKAN)
|
||||
{ option_renderer, "Renderer (restart); " FIRST_RENDERER EXT_RENDERER },
|
||||
{ option_renderer_software_fb, "Software framebuffer; enabled|disabled" },
|
||||
{ option_renderer_software_fb, "Software framebuffer; enabled|disabled" },
|
||||
#endif
|
||||
#ifdef HAVE_VULKAN
|
||||
{ option_adaptive_smoothing, "Adaptive smoothing; enabled|disabled" },
|
||||
@ -3995,7 +3995,7 @@ void retro_set_environment(retro_environment_t cb)
|
||||
{ option_internal_resolution, "Internal GPU resolution; 1x(native)|2x|4x|8x" },
|
||||
#if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
|
||||
// Only used in GL renderer for now.
|
||||
{ option_filter, "Texture filtering; nearest|3point N64|bilinear" },
|
||||
{ option_filter, "Texture filtering; nearest|3point N64|SABR|6xBRZ|bilinear" },
|
||||
{ option_depth, "Internal color depth; dithered 16bpp (native)|32bpp" },
|
||||
{ option_wireframe, "Wireframe mode; disabled|enabled" },
|
||||
{ option_display_vram, "Display full VRAM; disabled|enabled" },
|
||||
@ -4006,7 +4006,7 @@ void retro_set_environment(retro_environment_t cb)
|
||||
{ option_pgxp_texture, "PGXP perspective correct texturing; disabled|enabled" },
|
||||
#endif
|
||||
{ option_dither_mode, "Dithering pattern; 1x(native)|internal resolution|disabled" },
|
||||
{ option_scale_dither, "Scale dithering pattern with internal resolution; enabled|disabled" },
|
||||
{ option_scale_dither, "Scale dithering pattern with internal resolution; enabled|disabled" },
|
||||
{ option_initial_scanline, "Initial scanline; 0|1|2|3|4|5|6|7|8|9|10|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39|40" },
|
||||
{ option_last_scanline, "Last scanline; 239|238|237|236|235|234|232|231|230|229|228|227|226|225|224|223|222|221|220|219|218|217|216|215|214|213|212|211|210" },
|
||||
{ option_initial_scanline_pal, "Initial scanline PAL; 0|1|2|3|4|5|6|7|8|9|10|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39|40" },
|
||||
@ -4182,7 +4182,7 @@ void retro_cheat_set(unsigned index, bool enabled, const char * code)
|
||||
const CheatFormatStruct* cf = CheatFormats;
|
||||
char name[256];
|
||||
MemoryPatch patch;
|
||||
|
||||
|
||||
//Decode the cheat
|
||||
try
|
||||
{
|
||||
@ -4192,10 +4192,10 @@ void retro_cheat_set(unsigned index, bool enabled, const char * code)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//Generate a name
|
||||
sprintf(name,"cheat_%u",index);
|
||||
|
||||
|
||||
//Set parameters
|
||||
patch.name=std::string(name);
|
||||
patch.status=enabled;
|
||||
|
@ -40,8 +40,12 @@ GlRenderer::GlRenderer(DrawConfig* config)
|
||||
filter = 0;
|
||||
else if (!strcmp(var.value, "3point N64"))
|
||||
filter = 1;
|
||||
else if (!strcmp(var.value, "bilinear"))
|
||||
else if (!strcmp(var.value, "SABR"))
|
||||
filter = 2;
|
||||
else if (!strcmp(var.value, "6xBRZ"))
|
||||
filter = 3;
|
||||
else if (!strcmp(var.value, "bilinear"))
|
||||
filter = 4;
|
||||
|
||||
this->filter_type = filter;
|
||||
}
|
||||
@ -572,8 +576,12 @@ bool GlRenderer::refresh_variables()
|
||||
filter = 0;
|
||||
else if (!strcmp(var.value, "3point N64"))
|
||||
filter = 1;
|
||||
else if (!strcmp(var.value, "bilinear"))
|
||||
else if (!strcmp(var.value, "SABR"))
|
||||
filter = 2;
|
||||
else if (!strcmp(var.value, "6xBRZ"))
|
||||
filter = 3;
|
||||
else if (!strcmp(var.value, "bilinear"))
|
||||
filter = 4;
|
||||
|
||||
this->filter_type = filter;
|
||||
}
|
||||
|
@ -39,7 +39,9 @@ const uint BLEND_MODE_TEXTURE_BLEND = 2U;
|
||||
|
||||
const uint FILTER_MODE_NEAREST = 0U;
|
||||
const uint FILTER_MODE_3POINT = 1U;
|
||||
const uint FILTER_MODE_BILINEAR = 2U;
|
||||
const uint FILTER_MODE_SABR = 2U;
|
||||
const uint FILTER_MODE_6XBRZ = 3U;
|
||||
const uint FILTER_MODE_BILINEAR = 4U;
|
||||
|
||||
// Read a pixel in VRAM
|
||||
vec4 vram_get_pixel(uint x, uint y) {
|
||||
@ -137,9 +139,51 @@ vec4 sample_texel(vec2 coords) {
|
||||
return texel;
|
||||
}
|
||||
|
||||
// constants and functions for sabr
|
||||
const vec4 Ai = vec4( 1.0, -1.0, -1.0, 1.0);
|
||||
const vec4 B45 = vec4( 1.0, 1.0, -1.0, -1.0);
|
||||
const vec4 C45 = vec4( 1.5, 0.5, -0.5, 0.5);
|
||||
const vec4 B30 = vec4( 0.5, 2.0, -0.5, -2.0);
|
||||
const vec4 C30 = vec4( 1.0, 1.0, -0.5, 0.0);
|
||||
const vec4 B60 = vec4( 2.0, 0.5, -2.0, -0.5);
|
||||
const vec4 C60 = vec4( 2.0, 0.0, -1.0, 0.5);
|
||||
|
||||
const vec4 M45 = vec4(0.4, 0.4, 0.4, 0.4);
|
||||
const vec4 M30 = vec4(0.2, 0.4, 0.2, 0.4);
|
||||
const vec4 M60 = M30.yxwz;
|
||||
const vec4 Mshift = vec4(0.2, 0.2, 0.2, 0.2);
|
||||
|
||||
const float coef = 2.0;
|
||||
|
||||
const vec4 threshold = vec4(0.32, 0.32, 0.32, 0.32);
|
||||
|
||||
const vec3 lum = vec3(0.21, 0.72, 0.07);
|
||||
|
||||
vec4 lum_to(vec3 v0, vec3 v1, vec3 v2, vec3 v3) {
|
||||
return vec4(dot(lum, v0), dot(lum, v1), dot(lum, v2), dot(lum, v3));
|
||||
}
|
||||
|
||||
vec4 lum_df(vec4 A, vec4 B) {
|
||||
return abs(A - B);
|
||||
}
|
||||
|
||||
bvec4 lum_eq(vec4 A, vec4 B) {
|
||||
return lessThan(lum_df(A, B) , vec4(threshold));
|
||||
}
|
||||
|
||||
vec4 lum_wd(vec4 a, vec4 b, vec4 c, vec4 d, vec4 e, vec4 f, vec4 g, vec4 h) {
|
||||
return lum_df(a, b) + lum_df(a, c) + lum_df(d, e) + lum_df(d, f) + 4.0 * lum_df(g, h);
|
||||
}
|
||||
|
||||
float c_df(vec3 c1, vec3 c2) {
|
||||
vec3 df = abs(c1 - c2);
|
||||
return df.r + df.g + df.b;
|
||||
}
|
||||
|
||||
// 3-point filtering
|
||||
vec4 get_texel_3point()
|
||||
{
|
||||
|
||||
float x = frag_texture_coord.x;
|
||||
float y = frag_texture_coord.y;
|
||||
|
||||
@ -181,6 +225,437 @@ vec4 get_texel_3point()
|
||||
return texel;
|
||||
}
|
||||
|
||||
//sabr
|
||||
vec4 get_texel_sabr()
|
||||
{
|
||||
vec2 tc = vec2(frag_texture_coord.x, frag_texture_coord.y);// * vec2(1.00001);
|
||||
vec4 xyp_1_2_3 = tc.xxxy + vec4( -1., 0.0, 1., -2.0 * 1.);
|
||||
vec4 xyp_6_7_8 = tc.xxxy + vec4( -1., 0.0, 1., -1.);
|
||||
vec4 xyp_11_12_13 = tc.xxxy + vec4( -1., 0.0, 1., 0.0);
|
||||
vec4 xyp_16_17_18 = tc.xxxy + vec4( -1., 0.0, 1., 1.);
|
||||
vec4 xyp_21_22_23 = tc.xxxy + vec4( -1., 0.0, 1., 2.0 * 1.);
|
||||
vec4 xyp_5_10_15 = tc.xyyy + vec4(-2.0 * 1., -1., 0.0, 1.);
|
||||
vec4 xyp_9_14_9 = tc.xyyy + vec4( 2.0 * 1., -1., 0.0, 1.);
|
||||
|
||||
// Store mask values
|
||||
vec3 P1 = sample_texel(xyp_1_2_3.xw ).rgb;
|
||||
vec3 P2 = sample_texel(xyp_1_2_3.yw ).rgb;
|
||||
vec3 P3 = sample_texel(xyp_1_2_3.zw ).rgb;
|
||||
|
||||
vec3 P6 = sample_texel(xyp_6_7_8.xw ).rgb;
|
||||
vec3 P7 = sample_texel(xyp_6_7_8.yw ).rgb;
|
||||
vec3 P8 = sample_texel(xyp_6_7_8.zw ).rgb;
|
||||
|
||||
vec3 P11 = sample_texel(xyp_11_12_13.xw).rgb;
|
||||
vec3 P12 = sample_texel(xyp_11_12_13.yw).rgb;
|
||||
vec3 P13 = sample_texel(xyp_11_12_13.zw).rgb;
|
||||
|
||||
vec3 P16 = sample_texel(xyp_16_17_18.xw).rgb;
|
||||
vec3 P17 = sample_texel(xyp_16_17_18.yw).rgb;
|
||||
vec3 P18 = sample_texel(xyp_16_17_18.zw).rgb;
|
||||
|
||||
vec3 P21 = sample_texel(xyp_21_22_23.xw).rgb;
|
||||
vec3 P22 = sample_texel(xyp_21_22_23.yw).rgb;
|
||||
vec3 P23 = sample_texel(xyp_21_22_23.zw).rgb;
|
||||
|
||||
vec3 P5 = sample_texel(xyp_5_10_15.xy ).rgb;
|
||||
vec3 P10 = sample_texel(xyp_5_10_15.xz ).rgb;
|
||||
vec3 P15 = sample_texel(xyp_5_10_15.xw ).rgb;
|
||||
|
||||
vec3 P9 = sample_texel(xyp_9_14_9.xy ).rgb;
|
||||
vec3 P14 = sample_texel(xyp_9_14_9.xz ).rgb;
|
||||
vec3 P19 = sample_texel(xyp_9_14_9.xw ).rgb;
|
||||
|
||||
// Store luminance values of each point
|
||||
vec4 p7 = lum_to(P7, P11, P17, P13);
|
||||
vec4 p8 = lum_to(P8, P6, P16, P18);
|
||||
vec4 p11 = p7.yzwx; // P11, P17, P13, P7
|
||||
vec4 p12 = lum_to(P12, P12, P12, P12);
|
||||
vec4 p13 = p7.wxyz; // P13, P7, P11, P17
|
||||
vec4 p14 = lum_to(P14, P2, P10, P22);
|
||||
vec4 p16 = p8.zwxy; // P16, P18, P8, P6
|
||||
vec4 p17 = p7.zwxy; // P11, P17, P13, P7
|
||||
vec4 p18 = p8.wxyz; // P18, P8, P6, P16
|
||||
vec4 p19 = lum_to(P19, P3, P5, P21);
|
||||
vec4 p22 = p14.wxyz; // P22, P14, P2, P10
|
||||
vec4 p23 = lum_to(P23, P9, P1, P15);
|
||||
|
||||
vec2 fp = fract(tc);
|
||||
|
||||
vec4 ma45 = smoothstep(C45 - M45, C45 + M45, Ai * fp.y + B45 * fp.x);
|
||||
vec4 ma30 = smoothstep(C30 - M30, C30 + M30, Ai * fp.y + B30 * fp.x);
|
||||
vec4 ma60 = smoothstep(C60 - M60, C60 + M60, Ai * fp.y + B60 * fp.x);
|
||||
vec4 marn = smoothstep(C45 - M45 + Mshift, C45 + M45 + Mshift, Ai * fp.y + B45 * fp.x);
|
||||
|
||||
vec4 e45 = lum_wd(p12, p8, p16, p18, p22, p14, p17, p13);
|
||||
vec4 econt = lum_wd(p17, p11, p23, p13, p7, p19, p12, p18);
|
||||
vec4 e30 = lum_df(p13, p16);
|
||||
vec4 e60 = lum_df(p8, p17);
|
||||
|
||||
vec4 final45 = vec4(1.0);
|
||||
vec4 final30 = vec4(0.0);
|
||||
vec4 final60 = vec4(0.0);
|
||||
vec4 final36 = vec4(0.0);
|
||||
vec4 finalrn = vec4(0.0);
|
||||
|
||||
vec4 px = step(lum_df(p12, p17), lum_df(p12, p13));
|
||||
|
||||
vec4 mac = final36 * max(ma30, ma60) + final30 * ma30 + final60 * ma60 + final45 * ma45 + finalrn * marn;
|
||||
|
||||
vec3 res1 = P12;
|
||||
res1 = mix(res1, mix(P13, P17, px.x), mac.x);
|
||||
res1 = mix(res1, mix(P7 , P13, px.y), mac.y);
|
||||
res1 = mix(res1, mix(P11, P7 , px.z), mac.z);
|
||||
res1 = mix(res1, mix(P17, P11, px.w), mac.w);
|
||||
|
||||
vec3 res2 = P12;
|
||||
res2 = mix(res2, mix(P17, P11, px.w), mac.w);
|
||||
res2 = mix(res2, mix(P11, P7 , px.z), mac.z);
|
||||
res2 = mix(res2, mix(P7 , P13, px.y), mac.y);
|
||||
res2 = mix(res2, mix(P13, P17, px.x), mac.x);
|
||||
|
||||
float texel_alpha = sample_texel(vec2(frag_texture_coord.x, frag_texture_coord.y)).w;
|
||||
|
||||
vec4 texel = vec4(mix(res1, res2, step(c_df(P12, res1), c_df(P12, res2))), texel_alpha);
|
||||
|
||||
return texel;
|
||||
}
|
||||
|
||||
//consts and functions for 6xbrz
|
||||
//#define BLEND_NONE 0
|
||||
//#define BLEND_NORMAL 1
|
||||
//#define BLEND_DOMINANT 2
|
||||
//#define LUMINANCE_WEIGHT 1.0
|
||||
//#define EQUAL_COLOR_TOLERANCE 30.0/255.0
|
||||
//#define STEEP_DIRECTION_THRESHOLD 2.2
|
||||
//#define DOMINANT_DIRECTION_THRESHOLD 3.6
|
||||
|
||||
const float one_sixth = 1.0 / 6.0;
|
||||
const float two_sixth = 2.0 / 6.0;
|
||||
const float four_sixth = 4.0 / 6.0;
|
||||
const float five_sixth = 5.0 / 6.0;
|
||||
|
||||
float reduce(const vec3 color)
|
||||
{
|
||||
return dot(color, vec3(65536.0, 256.0, 1.0));
|
||||
}
|
||||
|
||||
float DistYCbCr(const vec3 pixA, const vec3 pixB)
|
||||
{
|
||||
const vec3 w = vec3(0.2627, 0.6780, 0.0593);
|
||||
const float scaleB = 0.5 / (1.0 - w.b);
|
||||
const float scaleR = 0.5 / (1.0 - w.r);
|
||||
vec3 diff = pixA - pixB;
|
||||
float Y = dot(diff, w);
|
||||
float Cb = scaleB * (diff.b - Y);
|
||||
float Cr = scaleR * (diff.r - Y);
|
||||
|
||||
return sqrt( ((1.0 * Y) * (1.0 * Y)) + (Cb * Cb) + (Cr * Cr) );
|
||||
}
|
||||
|
||||
bool IsPixEqual(const vec3 pixA, const vec3 pixB)
|
||||
{
|
||||
return (DistYCbCr(pixA, pixB) < 0.117647059);
|
||||
}
|
||||
|
||||
bool IsBlendingNeeded(const ivec4 blend)
|
||||
{
|
||||
return any(notEqual(blend, ivec4(0)));
|
||||
}
|
||||
|
||||
//6xbrz
|
||||
vec4 get_texel_6xbrz()
|
||||
{
|
||||
vec2 vTexCoord = frag_texture_coord.xy;
|
||||
vec4 t1 = vTexCoord.xxxy + vec4( -1., 0.0, 1.,-2.0); // A1 B1 C1
|
||||
vec4 t2 = vTexCoord.xxxy + vec4( -1., 0.0, 1., -1.); // A B C
|
||||
vec4 t3 = vTexCoord.xxxy + vec4( -1., 0.0, 1., 0.0); // D E F
|
||||
vec4 t4 = vTexCoord.xxxy + vec4( -1., 0.0, 1., 1.); // G H I
|
||||
vec4 t5 = vTexCoord.xxxy + vec4( -1., 0.0, 1., 2.0); // G5 H5 I5
|
||||
vec4 t6 = vTexCoord.xyyy + vec4(-2.0,-1., 0.0, 1.); // A0 D0 G0
|
||||
vec4 t7 = vTexCoord.xyyy + vec4( 2.0,-1., 0.0, 1.); // C4 F4 I4
|
||||
|
||||
vec2 f = fract(vTexCoord.xy);
|
||||
|
||||
//---------------------------------------
|
||||
// Input Pixel Mapping: 20|21|22|23|24
|
||||
// 19|06|07|08|09
|
||||
// 18|05|00|01|10
|
||||
// 17|04|03|02|11
|
||||
// 16|15|14|13|12
|
||||
|
||||
vec3 src[25];
|
||||
|
||||
src[21] = sample_texel(t1.xw).rgb;
|
||||
src[22] = sample_texel(t1.yw).rgb;
|
||||
src[23] = sample_texel(t1.zw).rgb;
|
||||
src[ 6] = sample_texel(t2.xw).rgb;
|
||||
src[ 7] = sample_texel(t2.yw).rgb;
|
||||
src[ 8] = sample_texel(t2.zw).rgb;
|
||||
src[ 5] = sample_texel(t3.xw).rgb;
|
||||
src[ 0] = sample_texel(t3.yw).rgb;
|
||||
src[ 1] = sample_texel(t3.zw).rgb;
|
||||
src[ 4] = sample_texel(t4.xw).rgb;
|
||||
src[ 3] = sample_texel(t4.yw).rgb;
|
||||
src[ 2] = sample_texel(t4.zw).rgb;
|
||||
src[15] = sample_texel(t5.xw).rgb;
|
||||
src[14] = sample_texel(t5.yw).rgb;
|
||||
src[13] = sample_texel(t5.zw).rgb;
|
||||
src[19] = sample_texel(t6.xy).rgb;
|
||||
src[18] = sample_texel(t6.xz).rgb;
|
||||
src[17] = sample_texel(t6.xw).rgb;
|
||||
src[ 9] = sample_texel(t7.xy).rgb;
|
||||
src[10] = sample_texel(t7.xz).rgb;
|
||||
src[11] = sample_texel(t7.xw).rgb;
|
||||
|
||||
float v[9];
|
||||
v[0] = reduce(src[0]);
|
||||
v[1] = reduce(src[1]);
|
||||
v[2] = reduce(src[2]);
|
||||
v[3] = reduce(src[3]);
|
||||
v[4] = reduce(src[4]);
|
||||
v[5] = reduce(src[5]);
|
||||
v[6] = reduce(src[6]);
|
||||
v[7] = reduce(src[7]);
|
||||
v[8] = reduce(src[8]);
|
||||
|
||||
ivec4 blendResult = ivec4(0);
|
||||
|
||||
// Preprocess corners
|
||||
// Pixel Tap Mapping: --|--|--|--|--
|
||||
// --|--|07|08|--
|
||||
// --|05|00|01|10
|
||||
// --|04|03|02|11
|
||||
// --|--|14|13|--
|
||||
// Corner (1, 1)
|
||||
if ( ((v[0] == v[1] && v[3] == v[2]) || (v[0] == v[3] && v[1] == v[2])) == false)
|
||||
{
|
||||
float dist_03_01 = DistYCbCr(src[ 4], src[ 0]) + DistYCbCr(src[ 0], src[ 8]) + DistYCbCr(src[14], src[ 2]) + DistYCbCr(src[ 2], src[10]) + (4.0 * DistYCbCr(src[ 3], src[ 1]));
|
||||
float dist_00_02 = DistYCbCr(src[ 5], src[ 3]) + DistYCbCr(src[ 3], src[13]) + DistYCbCr(src[ 7], src[ 1]) + DistYCbCr(src[ 1], src[11]) + (4.0 * DistYCbCr(src[ 0], src[ 2]));
|
||||
bool dominantGradient = (3.6 * dist_03_01) < dist_00_02;
|
||||
blendResult[2] = ((dist_03_01 < dist_00_02) && (v[0] != v[1]) && (v[0] != v[3])) ? ((dominantGradient) ? 2 : 1) : 0;
|
||||
}
|
||||
|
||||
// Pixel Tap Mapping: --|--|--|--|--
|
||||
// --|06|07|--|--
|
||||
// 18|05|00|01|--
|
||||
// 17|04|03|02|--
|
||||
// --|15|14|--|--
|
||||
// Corner (0, 1)
|
||||
if ( ((v[5] == v[0] && v[4] == v[3]) || (v[5] == v[4] && v[0] == v[3])) == false)
|
||||
{
|
||||
float dist_04_00 = DistYCbCr(src[17], src[ 5]) + DistYCbCr(src[ 5], src[ 7]) + DistYCbCr(src[15], src[ 3]) + DistYCbCr(src[ 3], src[ 1]) + (4.0 * DistYCbCr(src[ 4], src[ 0]));
|
||||
float dist_05_03 = DistYCbCr(src[18], src[ 4]) + DistYCbCr(src[ 4], src[14]) + DistYCbCr(src[ 6], src[ 0]) + DistYCbCr(src[ 0], src[ 2]) + (4.0 * DistYCbCr(src[ 5], src[ 3]));
|
||||
bool dominantGradient = (3.6 * dist_05_03) < dist_04_00;
|
||||
blendResult[3] = ((dist_04_00 > dist_05_03) && (v[0] != v[5]) && (v[0] != v[3])) ? ((dominantGradient) ? 2 : 1) : 0;
|
||||
}
|
||||
|
||||
// Pixel Tap Mapping: --|--|22|23|--
|
||||
// --|06|07|08|09
|
||||
// --|05|00|01|10
|
||||
// --|--|03|02|--
|
||||
// --|--|--|--|--
|
||||
// Corner (1, 0)
|
||||
if ( ((v[7] == v[8] && v[0] == v[1]) || (v[7] == v[0] && v[8] == v[1])) == false)
|
||||
{
|
||||
float dist_00_08 = DistYCbCr(src[ 5], src[ 7]) + DistYCbCr(src[ 7], src[23]) + DistYCbCr(src[ 3], src[ 1]) + DistYCbCr(src[ 1], src[ 9]) + (4.0 * DistYCbCr(src[ 0], src[ 8]));
|
||||
float dist_07_01 = DistYCbCr(src[ 6], src[ 0]) + DistYCbCr(src[ 0], src[ 2]) + DistYCbCr(src[22], src[ 8]) + DistYCbCr(src[ 8], src[10]) + (4.0 * DistYCbCr(src[ 7], src[ 1]));
|
||||
bool dominantGradient = (3.6 * dist_07_01) < dist_00_08;
|
||||
blendResult[1] = ((dist_00_08 > dist_07_01) && (v[0] != v[7]) && (v[0] != v[1])) ? ((dominantGradient) ? 2 : 1) : 0;
|
||||
}
|
||||
|
||||
// Pixel Tap Mapping: --|21|22|--|--
|
||||
// 19|06|07|08|--
|
||||
// 18|05|00|01|--
|
||||
// --|04|03|--|--
|
||||
// --|--|--|--|--
|
||||
// Corner (0, 0)
|
||||
if ( ((v[6] == v[7] && v[5] == v[0]) || (v[6] == v[5] && v[7] == v[0])) == false)
|
||||
{
|
||||
float dist_05_07 = DistYCbCr(src[18], src[ 6]) + DistYCbCr(src[ 6], src[22]) + DistYCbCr(src[ 4], src[ 0]) + DistYCbCr(src[ 0], src[ 8]) + (4.0 * DistYCbCr(src[ 5], src[ 7]));
|
||||
float dist_06_00 = DistYCbCr(src[19], src[ 5]) + DistYCbCr(src[ 5], src[ 3]) + DistYCbCr(src[21], src[ 7]) + DistYCbCr(src[ 7], src[ 1]) + (4.0 * DistYCbCr(src[ 6], src[ 0]));
|
||||
bool dominantGradient = (3.6 * dist_05_07) < dist_06_00;
|
||||
blendResult[0] = ((dist_05_07 < dist_06_00) && (v[0] != v[5]) && (v[0] != v[7])) ? ((dominantGradient) ? 2 : 1) : 0;
|
||||
}
|
||||
|
||||
vec3 dst[36];
|
||||
dst[ 0] = src[0];
|
||||
dst[ 1] = src[0];
|
||||
dst[ 2] = src[0];
|
||||
dst[ 3] = src[0];
|
||||
dst[ 4] = src[0];
|
||||
dst[ 5] = src[0];
|
||||
dst[ 6] = src[0];
|
||||
dst[ 7] = src[0];
|
||||
dst[ 8] = src[0];
|
||||
dst[ 9] = src[0];
|
||||
dst[10] = src[0];
|
||||
dst[11] = src[0];
|
||||
dst[12] = src[0];
|
||||
dst[13] = src[0];
|
||||
dst[14] = src[0];
|
||||
dst[15] = src[0];
|
||||
dst[16] = src[0];
|
||||
dst[17] = src[0];
|
||||
dst[18] = src[0];
|
||||
dst[19] = src[0];
|
||||
dst[20] = src[0];
|
||||
dst[21] = src[0];
|
||||
dst[22] = src[0];
|
||||
dst[23] = src[0];
|
||||
dst[24] = src[0];
|
||||
dst[25] = src[0];
|
||||
dst[26] = src[0];
|
||||
dst[27] = src[0];
|
||||
dst[28] = src[0];
|
||||
dst[29] = src[0];
|
||||
dst[30] = src[0];
|
||||
dst[31] = src[0];
|
||||
dst[32] = src[0];
|
||||
dst[33] = src[0];
|
||||
dst[34] = src[0];
|
||||
dst[35] = src[0];
|
||||
|
||||
// Scale pixel
|
||||
if (IsBlendingNeeded(blendResult) == true)
|
||||
{
|
||||
float dist_01_04 = DistYCbCr(src[1], src[4]);
|
||||
float dist_03_08 = DistYCbCr(src[3], src[8]);
|
||||
bool haveShallowLine = (2.2 * dist_01_04 <= dist_03_08) && (v[0] != v[4]) && (v[5] != v[4]);
|
||||
bool haveSteepLine = (2.2 * dist_03_08 <= dist_01_04) && (v[0] != v[8]) && (v[7] != v[8]);
|
||||
bool needBlend = (blendResult[2] != 0);
|
||||
bool doLineBlend = ( blendResult[2] >= 2 ||
|
||||
((blendResult[1] != 0 && !IsPixEqual(src[0], src[4])) ||
|
||||
(blendResult[3] != 0 && !IsPixEqual(src[0], src[8])) ||
|
||||
(IsPixEqual(src[4], src[3]) && IsPixEqual(src[3], src[2]) && IsPixEqual(src[2], src[1]) && IsPixEqual(src[1], src[8]) && IsPixEqual(src[0], src[2]) == false) ) == false );
|
||||
|
||||
vec3 blendPix = ( DistYCbCr(src[0], src[1]) <= DistYCbCr(src[0], src[3]) ) ? src[1] : src[3];
|
||||
dst[10] = mix(dst[10], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 0.250 : 0.000);
|
||||
dst[11] = mix(dst[11], blendPix, (needBlend && doLineBlend) ? ((haveSteepLine) ? 0.750 : ((haveShallowLine) ? 0.250 : 0.000)) : 0.000);
|
||||
dst[12] = mix(dst[12], blendPix, (needBlend && doLineBlend) ? ((!haveShallowLine && !haveSteepLine) ? 0.500 : 1.000) : 0.000);
|
||||
dst[13] = mix(dst[13], blendPix, (needBlend && doLineBlend) ? ((haveShallowLine) ? 0.750 : ((haveSteepLine) ? 0.250 : 0.000)) : 0.000);
|
||||
dst[14] = mix(dst[14], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 0.250 : 0.000);
|
||||
dst[25] = mix(dst[25], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 0.250 : 0.000);
|
||||
dst[26] = mix(dst[26], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 0.750 : 0.000);
|
||||
dst[27] = mix(dst[27], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 1.000 : 0.000);
|
||||
dst[28] = mix(dst[28], blendPix, (needBlend) ? ((doLineBlend) ? ((haveSteepLine) ? 1.000 : ((haveShallowLine) ? 0.750 : 0.500)) : 0.05652034508) : 0.000);
|
||||
dst[29] = mix(dst[29], blendPix, (needBlend) ? ((doLineBlend) ? 1.000 : 0.4236372243) : 0.000);
|
||||
dst[30] = mix(dst[30], blendPix, (needBlend) ? ((doLineBlend) ? 1.000 : 0.9711013910) : 0.000);
|
||||
dst[31] = mix(dst[31], blendPix, (needBlend) ? ((doLineBlend) ? 1.000 : 0.4236372243) : 0.000);
|
||||
dst[32] = mix(dst[32], blendPix, (needBlend) ? ((doLineBlend) ? ((haveShallowLine) ? 1.000 : ((haveSteepLine) ? 0.750 : 0.500)) : 0.05652034508) : 0.000);
|
||||
dst[33] = mix(dst[33], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 1.000 : 0.000);
|
||||
dst[34] = mix(dst[34], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 0.750 : 0.000);
|
||||
dst[35] = mix(dst[35], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 0.250 : 0.000);
|
||||
|
||||
dist_01_04 = DistYCbCr(src[7], src[2]);
|
||||
dist_03_08 = DistYCbCr(src[1], src[6]);
|
||||
haveShallowLine = (2.2 * dist_01_04 <= dist_03_08) && (v[0] != v[2]) && (v[3] != v[2]);
|
||||
haveSteepLine = (2.2 * dist_03_08 <= dist_01_04) && (v[0] != v[6]) && (v[5] != v[6]);
|
||||
needBlend = (blendResult[1] != 0);
|
||||
doLineBlend = ( blendResult[1] >= 2 ||
|
||||
!((blendResult[0] != 0 && !IsPixEqual(src[0], src[2])) ||
|
||||
(blendResult[2] != 0 && !IsPixEqual(src[0], src[6])) ||
|
||||
(IsPixEqual(src[2], src[1]) && IsPixEqual(src[1], src[8]) && IsPixEqual(src[8], src[7]) && IsPixEqual(src[7], src[6]) && !IsPixEqual(src[0], src[8])) ) );
|
||||
|
||||
dist_01_04 = DistYCbCr(src[7], src[2]);
|
||||
dist_03_08 = DistYCbCr(src[1], src[6]);
|
||||
haveShallowLine = (2.2 * dist_01_04 <= dist_03_08) && (v[0] != v[2]) && (v[3] != v[2]);
|
||||
haveSteepLine = (2.2 * dist_03_08 <= dist_01_04) && (v[0] != v[6]) && (v[5] != v[6]);
|
||||
needBlend = (blendResult[1] != 0);
|
||||
doLineBlend = ( blendResult[1] >= 2 ||
|
||||
!((blendResult[0] != 0 && !IsPixEqual(src[0], src[2])) ||
|
||||
(blendResult[2] != 0 && !IsPixEqual(src[0], src[6])) ||
|
||||
(IsPixEqual(src[2], src[1]) && IsPixEqual(src[1], src[8]) && IsPixEqual(src[8], src[7]) && IsPixEqual(src[7], src[6]) && !IsPixEqual(src[0], src[8])) ) );
|
||||
|
||||
blendPix = ( DistYCbCr(src[0], src[7]) <= DistYCbCr(src[0], src[1]) ) ? src[7] : src[1];
|
||||
dst[ 7] = mix(dst[ 7], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 0.250 : 0.000);
|
||||
dst[ 8] = mix(dst[ 8], blendPix, (needBlend && doLineBlend) ? ((haveSteepLine) ? 0.750 : ((haveShallowLine) ? 0.250 : 0.000)) : 0.000);
|
||||
dst[ 9] = mix(dst[ 9], blendPix, (needBlend && doLineBlend) ? ((!haveShallowLine && !haveSteepLine) ? 0.500 : 1.000) : 0.000);
|
||||
dst[10] = mix(dst[10], blendPix, (needBlend && doLineBlend) ? ((haveShallowLine) ? 0.750 : ((haveSteepLine) ? 0.250 : 0.000)) : 0.000);
|
||||
dst[11] = mix(dst[11], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 0.250 : 0.000);
|
||||
dst[20] = mix(dst[20], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 0.250 : 0.000);
|
||||
dst[21] = mix(dst[21], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 0.750 : 0.000);
|
||||
dst[22] = mix(dst[22], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 1.000 : 0.000);
|
||||
dst[23] = mix(dst[23], blendPix, (needBlend) ? ((doLineBlend) ? ((haveSteepLine) ? 1.000 : ((haveShallowLine) ? 0.750 : 0.500)) : 0.05652034508) : 0.000);
|
||||
dst[24] = mix(dst[24], blendPix, (needBlend) ? ((doLineBlend) ? 1.000 : 0.4236372243) : 0.000);
|
||||
dst[25] = mix(dst[25], blendPix, (needBlend) ? ((doLineBlend) ? 1.000 : 0.9711013910) : 0.000);
|
||||
dst[26] = mix(dst[26], blendPix, (needBlend) ? ((doLineBlend) ? 1.000 : 0.4236372243) : 0.000);
|
||||
dst[27] = mix(dst[27], blendPix, (needBlend) ? ((doLineBlend) ? ((haveShallowLine) ? 1.000 : ((haveSteepLine) ? 0.750 : 0.500)) : 0.05652034508) : 0.000);
|
||||
dst[28] = mix(dst[28], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 1.000 : 0.000);
|
||||
dst[29] = mix(dst[29], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 0.750 : 0.000);
|
||||
dst[30] = mix(dst[30], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 0.250 : 0.000);
|
||||
|
||||
dist_01_04 = DistYCbCr(src[5], src[8]);
|
||||
dist_03_08 = DistYCbCr(src[7], src[4]);
|
||||
haveShallowLine = (2.2 * dist_01_04 <= dist_03_08) && (v[0] != v[8]) && (v[1] != v[8]);
|
||||
haveSteepLine = (2.2 * dist_03_08 <= dist_01_04) && (v[0] != v[4]) && (v[3] != v[4]);
|
||||
needBlend = (blendResult[0] != 0);
|
||||
doLineBlend = ( blendResult[0] >= 2 ||
|
||||
!((blendResult[3] != 0 && !IsPixEqual(src[0], src[8])) ||
|
||||
(blendResult[1] != 0 && !IsPixEqual(src[0], src[4])) ||
|
||||
(IsPixEqual(src[8], src[7]) && IsPixEqual(src[7], src[6]) && IsPixEqual(src[6], src[5]) && IsPixEqual(src[5], src[4]) && !IsPixEqual(src[0], src[6])) ) );
|
||||
|
||||
blendPix = ( DistYCbCr(src[0], src[5]) <= DistYCbCr(src[0], src[7]) ) ? src[5] : src[7];
|
||||
dst[ 4] = mix(dst[ 4], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 0.250 : 0.000);
|
||||
dst[ 5] = mix(dst[ 5], blendPix, (needBlend && doLineBlend) ? ((haveSteepLine) ? 0.750 : ((haveShallowLine) ? 0.250 : 0.000)) : 0.000);
|
||||
dst[ 6] = mix(dst[ 6], blendPix, (needBlend && doLineBlend) ? ((!haveShallowLine && !haveSteepLine) ? 0.500 : 1.000) : 0.000);
|
||||
dst[ 7] = mix(dst[ 7], blendPix, (needBlend && doLineBlend) ? ((haveShallowLine) ? 0.750 : ((haveSteepLine) ? 0.250 : 0.000)) : 0.000);
|
||||
dst[ 8] = mix(dst[ 8], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 0.250 : 0.000);
|
||||
dst[35] = mix(dst[35], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 0.250 : 0.000);
|
||||
dst[16] = mix(dst[16], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 0.750 : 0.000);
|
||||
dst[17] = mix(dst[17], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 1.000 : 0.000);
|
||||
dst[18] = mix(dst[18], blendPix, (needBlend) ? ((doLineBlend) ? ((haveSteepLine) ? 1.000 : ((haveShallowLine) ? 0.750 : 0.500)) : 0.05652034508) : 0.000);
|
||||
dst[19] = mix(dst[19], blendPix, (needBlend) ? ((doLineBlend) ? 1.000 : 0.4236372243) : 0.000);
|
||||
dst[20] = mix(dst[20], blendPix, (needBlend) ? ((doLineBlend) ? 1.000 : 0.9711013910) : 0.000);
|
||||
dst[21] = mix(dst[21], blendPix, (needBlend) ? ((doLineBlend) ? 1.000 : 0.4236372243) : 0.000);
|
||||
dst[22] = mix(dst[22], blendPix, (needBlend) ? ((doLineBlend) ? ((haveShallowLine) ? 1.000 : ((haveSteepLine) ? 0.750 : 0.500)) : 0.05652034508) : 0.000);
|
||||
dst[23] = mix(dst[23], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 1.000 : 0.000);
|
||||
dst[24] = mix(dst[24], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 0.750 : 0.000);
|
||||
dst[25] = mix(dst[25], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 0.250 : 0.000);
|
||||
|
||||
|
||||
dist_01_04 = DistYCbCr(src[3], src[6]);
|
||||
dist_03_08 = DistYCbCr(src[5], src[2]);
|
||||
haveShallowLine = (2.2 * dist_01_04 <= dist_03_08) && (v[0] != v[6]) && (v[7] != v[6]);
|
||||
haveSteepLine = (2.2 * dist_03_08 <= dist_01_04) && (v[0] != v[2]) && (v[1] != v[2]);
|
||||
needBlend = (blendResult[3] != 0);
|
||||
doLineBlend = ( blendResult[3] >= 2 ||
|
||||
!((blendResult[2] != 0 && !IsPixEqual(src[0], src[6])) ||
|
||||
(blendResult[0] != 0 && !IsPixEqual(src[0], src[2])) ||
|
||||
(IsPixEqual(src[6], src[5]) && IsPixEqual(src[5], src[4]) && IsPixEqual(src[4], src[3]) && IsPixEqual(src[3], src[2]) && !IsPixEqual(src[0], src[4])) ) );
|
||||
|
||||
blendPix = ( DistYCbCr(src[0], src[3]) <= DistYCbCr(src[0], src[5]) ) ? src[3] : src[5];
|
||||
dst[13] = mix(dst[13], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 0.250 : 0.000);
|
||||
dst[14] = mix(dst[14], blendPix, (needBlend && doLineBlend) ? ((haveSteepLine) ? 0.750 : ((haveShallowLine) ? 0.250 : 0.000)) : 0.000);
|
||||
dst[15] = mix(dst[15], blendPix, (needBlend && doLineBlend) ? ((!haveShallowLine && !haveSteepLine) ? 0.500 : 1.000) : 0.000);
|
||||
dst[ 4] = mix(dst[ 4], blendPix, (needBlend && doLineBlend) ? ((haveShallowLine) ? 0.750 : ((haveSteepLine) ? 0.250 : 0.000)) : 0.000);
|
||||
dst[ 5] = mix(dst[ 5], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 0.250 : 0.000);
|
||||
dst[30] = mix(dst[30], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 0.250 : 0.000);
|
||||
dst[31] = mix(dst[31], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 0.750 : 0.000);
|
||||
dst[32] = mix(dst[32], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 1.000 : 0.000);
|
||||
dst[33] = mix(dst[33], blendPix, (needBlend) ? ((doLineBlend) ? ((haveSteepLine) ? 1.000 : ((haveShallowLine) ? 0.750 : 0.500)) : 0.05652034508) : 0.000);
|
||||
dst[34] = mix(dst[34], blendPix, (needBlend) ? ((doLineBlend) ? 1.000 : 0.4236372243) : 0.000);
|
||||
dst[35] = mix(dst[35], blendPix, (needBlend) ? ((doLineBlend) ? 1.000 : 0.9711013910) : 0.000);
|
||||
dst[16] = mix(dst[16], blendPix, (needBlend) ? ((doLineBlend) ? 1.000 : 0.4236372243) : 0.000);
|
||||
dst[17] = mix(dst[17], blendPix, (needBlend) ? ((doLineBlend) ? ((haveShallowLine) ? 1.000 : ((haveSteepLine) ? 0.750 : 0.500)) : 0.05652034508) : 0.000);
|
||||
dst[18] = mix(dst[18], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 1.000 : 0.000);
|
||||
dst[19] = mix(dst[19], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 0.750 : 0.000);
|
||||
dst[20] = mix(dst[20], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 0.250 : 0.000);
|
||||
|
||||
}
|
||||
|
||||
vec3 res = mix( mix( mix( mix( mix( mix(dst[20], dst[21], step(one_sixth, f.x) ), dst[22], step(two_sixth, f.x) ), mix( mix(dst[23], dst[24], step(four_sixth, f.x) ), dst[25], step(five_sixth, f.x) ), step(0.50, f.x) ),
|
||||
mix( mix( mix(dst[19], dst[ 6], step(one_sixth, f.x) ), dst[ 7], step(two_sixth, f.x) ), mix( mix(dst[ 8], dst[ 9], step(four_sixth, f.x) ), dst[26], step(five_sixth, f.x) ), step(0.50, f.x) ), step(one_sixth, f.y) ),
|
||||
mix( mix( mix(dst[18], dst[ 5], step(one_sixth, f.x) ), dst[ 0], step(two_sixth, f.x) ), mix( mix(dst[ 1], dst[10], step(four_sixth, f.x) ), dst[27], step(five_sixth, f.x) ), step(0.50, f.x) ), step(two_sixth, f.y) ),
|
||||
mix( mix( mix( mix( mix(dst[17], dst[ 4], step(one_sixth, f.x) ), dst[ 3], step(two_sixth, f.x) ), mix( mix(dst[ 2], dst[11], step(four_sixth, f.x) ), dst[28], step(five_sixth, f.x) ), step(0.50, f.x) ),
|
||||
mix( mix( mix(dst[16], dst[15], step(one_sixth, f.x) ), dst[14], step(two_sixth, f.x) ), mix( mix(dst[13], dst[12], step(four_sixth, f.x) ), dst[29], step(five_sixth, f.x) ), step(0.50, f.x) ), step(four_sixth, f.y) ),
|
||||
mix( mix( mix(dst[35], dst[34], step(one_sixth, f.x) ), dst[33], step(two_sixth, f.x) ), mix( mix(dst[32], dst[31], step(four_sixth, f.x) ), dst[30], step(five_sixth, f.x) ), step(0.50, f.x) ), step(five_sixth, f.y) ),
|
||||
step(0.50, f.y) );
|
||||
|
||||
float texel_alpha = sample_texel(vec2(frag_texture_coord.x, frag_texture_coord.y)).w;
|
||||
return vec4(res, texel_alpha);
|
||||
}
|
||||
|
||||
// Bilinear filtering
|
||||
vec4 get_texel_bilinear()
|
||||
{
|
||||
@ -232,6 +707,10 @@ void main() {
|
||||
texel = get_texel_3point();
|
||||
} else if (texture_flt == FILTER_MODE_BILINEAR) {
|
||||
texel = get_texel_bilinear();
|
||||
} else if (texture_flt == FILTER_MODE_SABR) {
|
||||
texel = get_texel_sabr();
|
||||
} else if (texture_flt == FILTER_MODE_6XBRZ) {
|
||||
texel = get_texel_6xbrz();
|
||||
} else {
|
||||
texel = sample_texel(vec2(frag_texture_coord.x,
|
||||
frag_texture_coord.y));
|
||||
|
Loading…
Reference in New Issue
Block a user