1//? #version 330 2precision mediump float; 3 4in vec2 tex_coord; 5in vec2 source_size; 6in vec2 output_size; 7 8out vec4 frag_color; 9 10uniform sampler2D tex; 11uniform lowp float scale; 12 13const int BLEND_NONE = 0; 14const int BLEND_NORMAL = 1; 15const int BLEND_DOMINANT = 2; 16const float LUMINANCE_WEIGHT = 1.0; 17const float EQUAL_COLOR_TOLERANCE = 30.0 / 255.0; 18const float STEEP_DIRECTION_THRESHOLD = 2.2; 19const float DOMINANT_DIRECTION_THRESHOLD = 3.6; 20 21float ColorDist(vec4 a, vec4 b) { 22 // https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.2020_conversion 23 const vec3 K = vec3(0.2627, 0.6780, 0.0593); 24 const mat3 MATRIX = mat3(K, -.5 * K.r / (1.0 - K.b), -.5 * K.g / (1.0 - K.b), .5, .5, 25 -.5 * K.g / (1.0 - K.r), -.5 * K.b / (1.0 - K.r)); 26 vec4 diff = a - b; 27 vec3 YCbCr = diff.rgb * MATRIX; 28 // LUMINANCE_WEIGHT is currently 1, otherwise y would be multiplied by it 29 float d = length(YCbCr); 30 return sqrt(a.a * b.a * d * d + diff.a * diff.a); 31} 32 33bool IsPixEqual(const vec4 pixA, const vec4 pixB) { 34 return ColorDist(pixA, pixB) < EQUAL_COLOR_TOLERANCE; 35} 36 37float GetLeftRatio(vec2 center, vec2 origin, vec2 direction) { 38 vec2 P0 = center - origin; 39 vec2 proj = direction * (dot(P0, direction) / dot(direction, direction)); 40 vec2 distv = P0 - proj; 41 vec2 orth = vec2(-direction.y, direction.x); 42 float side = sign(dot(P0, orth)); 43 float v = side * length(distv * scale); 44 return smoothstep(-sqrt(2.0) / 2.0, sqrt(2.0) / 2.0, v); 45} 46 47#define P(x, y) textureOffset(tex, coord, ivec2(x, y)) 48 49void main() { 50 vec2 pos = fract(tex_coord * source_size) - vec2(0.5, 0.5); 51 vec2 coord = tex_coord - pos / source_size; 52 53 //--------------------------------------- 54 // Input Pixel Mapping: -|x|x|x|- 55 // x|A|B|C|x 56 // x|D|E|F|x 57 // x|G|H|I|x 58 // -|x|x|x|- 59 vec4 A = P(-1, -1); 60 vec4 B = P(0, -1); 61 vec4 C = P(1, -1); 62 vec4 D = P(-1, 0); 63 vec4 E = P(0, 0); 64 vec4 F = P(1, 0); 65 vec4 G = P(-1, 1); 66 vec4 H = P(0, 1); 67 vec4 I = P(1, 1); 68 // blendResult Mapping: x|y| 69 // w|z| 70 ivec4 blendResult = ivec4(BLEND_NONE, BLEND_NONE, BLEND_NONE, BLEND_NONE); 71 // Preprocess corners 72 // Pixel Tap Mapping: -|-|-|-|- 73 // -|-|B|C|- 74 // -|D|E|F|x 75 // -|G|H|I|x 76 // -|-|x|x|- 77 if (!((E == F && H == I) || (E == H && F == I))) { 78 float dist_H_F = ColorDist(G, E) + ColorDist(E, C) + ColorDist(P(0, 2), I) + 79 ColorDist(I, P(2, 0)) + (4.0 * ColorDist(H, F)); 80 float dist_E_I = ColorDist(D, H) + ColorDist(H, P(1, 2)) + ColorDist(B, F) + 81 ColorDist(F, P(2, 1)) + (4.0 * ColorDist(E, I)); 82 bool dominantGradient = (DOMINANT_DIRECTION_THRESHOLD * dist_H_F) < dist_E_I; 83 blendResult.z = ((dist_H_F < dist_E_I) && E != F && E != H) 84 ? ((dominantGradient) ? BLEND_DOMINANT : BLEND_NORMAL) 85 : BLEND_NONE; 86 } 87 // Pixel Tap Mapping: -|-|-|-|- 88 // -|A|B|-|- 89 // x|D|E|F|- 90 // x|G|H|I|- 91 // -|x|x|-|- 92 if (!((D == E && G == H) || (D == G && E == H))) { 93 float dist_G_E = ColorDist(P(-2, 1), D) + ColorDist(D, B) + ColorDist(P(-1, 2), H) + 94 ColorDist(H, F) + (4.0 * ColorDist(G, E)); 95 float dist_D_H = ColorDist(P(-2, 0), G) + ColorDist(G, P(0, 2)) + ColorDist(A, E) + 96 ColorDist(E, I) + (4.0 * ColorDist(D, H)); 97 bool dominantGradient = (DOMINANT_DIRECTION_THRESHOLD * dist_D_H) < dist_G_E; 98 blendResult.w = ((dist_G_E > dist_D_H) && E != D && E != H) 99 ? ((dominantGradient) ? BLEND_DOMINANT : BLEND_NORMAL) 100 : BLEND_NONE; 101 } 102 // Pixel Tap Mapping: -|-|x|x|- 103 // -|A|B|C|x 104 // -|D|E|F|x 105 // -|-|H|I|- 106 // -|-|-|-|- 107 if (!((B == C && E == F) || (B == E && C == F))) { 108 float dist_E_C = ColorDist(D, B) + ColorDist(B, P(1, -2)) + ColorDist(H, F) + 109 ColorDist(F, P(2, -1)) + (4.0 * ColorDist(E, C)); 110 float dist_B_F = ColorDist(A, E) + ColorDist(E, I) + ColorDist(P(0, -2), C) + 111 ColorDist(C, P(2, 0)) + (4.0 * ColorDist(B, F)); 112 bool dominantGradient = (DOMINANT_DIRECTION_THRESHOLD * dist_B_F) < dist_E_C; 113 blendResult.y = ((dist_E_C > dist_B_F) && E != B && E != F) 114 ? ((dominantGradient) ? BLEND_DOMINANT : BLEND_NORMAL) 115 : BLEND_NONE; 116 } 117 // Pixel Tap Mapping: -|x|x|-|- 118 // x|A|B|C|- 119 // x|D|E|F|- 120 // -|G|H|-|- 121 // -|-|-|-|- 122 if (!((A == B && D == E) || (A == D && B == E))) { 123 float dist_D_B = ColorDist(P(-2, 0), A) + ColorDist(A, P(0, -2)) + ColorDist(G, E) + 124 ColorDist(E, C) + (4.0 * ColorDist(D, B)); 125 float dist_A_E = ColorDist(P(-2, -1), D) + ColorDist(D, H) + ColorDist(P(-1, -2), B) + 126 ColorDist(B, F) + (4.0 * ColorDist(A, E)); 127 bool dominantGradient = (DOMINANT_DIRECTION_THRESHOLD * dist_D_B) < dist_A_E; 128 blendResult.x = ((dist_D_B < dist_A_E) && E != D && E != B) 129 ? ((dominantGradient) ? BLEND_DOMINANT : BLEND_NORMAL) 130 : BLEND_NONE; 131 } 132 vec4 res = E; 133 // Pixel Tap Mapping: -|-|-|-|- 134 // -|-|B|C|- 135 // -|D|E|F|x 136 // -|G|H|I|x 137 // -|-|x|x|- 138 if (blendResult.z != BLEND_NONE) { 139 float dist_F_G = ColorDist(F, G); 140 float dist_H_C = ColorDist(H, C); 141 bool doLineBlend = (blendResult.z == BLEND_DOMINANT || 142 !((blendResult.y != BLEND_NONE && !IsPixEqual(E, G)) || 143 (blendResult.w != BLEND_NONE && !IsPixEqual(E, C)) || 144 (IsPixEqual(G, H) && IsPixEqual(H, I) && IsPixEqual(I, F) && 145 IsPixEqual(F, C) && !IsPixEqual(E, I)))); 146 vec2 origin = vec2(0.0, 1.0 / sqrt(2.0)); 147 vec2 direction = vec2(1.0, -1.0); 148 if (doLineBlend) { 149 bool haveShallowLine = 150 (STEEP_DIRECTION_THRESHOLD * dist_F_G <= dist_H_C) && E != G && D != G; 151 bool haveSteepLine = 152 (STEEP_DIRECTION_THRESHOLD * dist_H_C <= dist_F_G) && E != C && B != C; 153 origin = haveShallowLine ? vec2(0.0, 0.25) : vec2(0.0, 0.5); 154 direction.x += haveShallowLine ? 1.0 : 0.0; 155 direction.y -= haveSteepLine ? 1.0 : 0.0; 156 } 157 vec4 blendPix = mix(H, F, step(ColorDist(E, F), ColorDist(E, H))); 158 res = mix(res, blendPix, GetLeftRatio(pos, origin, direction)); 159 } 160 // Pixel Tap Mapping: -|-|-|-|- 161 // -|A|B|-|- 162 // x|D|E|F|- 163 // x|G|H|I|- 164 // -|x|x|-|- 165 if (blendResult.w != BLEND_NONE) { 166 float dist_H_A = ColorDist(H, A); 167 float dist_D_I = ColorDist(D, I); 168 bool doLineBlend = (blendResult.w == BLEND_DOMINANT || 169 !((blendResult.z != BLEND_NONE && !IsPixEqual(E, A)) || 170 (blendResult.x != BLEND_NONE && !IsPixEqual(E, I)) || 171 (IsPixEqual(A, D) && IsPixEqual(D, G) && IsPixEqual(G, H) && 172 IsPixEqual(H, I) && !IsPixEqual(E, G)))); 173 vec2 origin = vec2(-1.0 / sqrt(2.0), 0.0); 174 vec2 direction = vec2(1.0, 1.0); 175 if (doLineBlend) { 176 bool haveShallowLine = 177 (STEEP_DIRECTION_THRESHOLD * dist_H_A <= dist_D_I) && E != A && B != A; 178 bool haveSteepLine = 179 (STEEP_DIRECTION_THRESHOLD * dist_D_I <= dist_H_A) && E != I && F != I; 180 origin = haveShallowLine ? vec2(-0.25, 0.0) : vec2(-0.5, 0.0); 181 direction.y += haveShallowLine ? 1.0 : 0.0; 182 direction.x += haveSteepLine ? 1.0 : 0.0; 183 } 184 origin = origin; 185 direction = direction; 186 vec4 blendPix = mix(H, D, step(ColorDist(E, D), ColorDist(E, H))); 187 res = mix(res, blendPix, GetLeftRatio(pos, origin, direction)); 188 } 189 // Pixel Tap Mapping: -|-|x|x|- 190 // -|A|B|C|x 191 // -|D|E|F|x 192 // -|-|H|I|- 193 // -|-|-|-|- 194 if (blendResult.y != BLEND_NONE) { 195 float dist_B_I = ColorDist(B, I); 196 float dist_F_A = ColorDist(F, A); 197 bool doLineBlend = (blendResult.y == BLEND_DOMINANT || 198 !((blendResult.x != BLEND_NONE && !IsPixEqual(E, I)) || 199 (blendResult.z != BLEND_NONE && !IsPixEqual(E, A)) || 200 (IsPixEqual(I, F) && IsPixEqual(F, C) && IsPixEqual(C, B) && 201 IsPixEqual(B, A) && !IsPixEqual(E, C)))); 202 vec2 origin = vec2(1.0 / sqrt(2.0), 0.0); 203 vec2 direction = vec2(-1.0, -1.0); 204 if (doLineBlend) { 205 bool haveShallowLine = 206 (STEEP_DIRECTION_THRESHOLD * dist_B_I <= dist_F_A) && E != I && H != I; 207 bool haveSteepLine = 208 (STEEP_DIRECTION_THRESHOLD * dist_F_A <= dist_B_I) && E != A && D != A; 209 origin = haveShallowLine ? vec2(0.25, 0.0) : vec2(0.5, 0.0); 210 direction.y -= haveShallowLine ? 1.0 : 0.0; 211 direction.x -= haveSteepLine ? 1.0 : 0.0; 212 } 213 vec4 blendPix = mix(F, B, step(ColorDist(E, B), ColorDist(E, F))); 214 res = mix(res, blendPix, GetLeftRatio(pos, origin, direction)); 215 } 216 // Pixel Tap Mapping: -|x|x|-|- 217 // x|A|B|C|- 218 // x|D|E|F|- 219 // -|G|H|-|- 220 // -|-|-|-|- 221 if (blendResult.x != BLEND_NONE) { 222 float dist_D_C = ColorDist(D, C); 223 float dist_B_G = ColorDist(B, G); 224 bool doLineBlend = (blendResult.x == BLEND_DOMINANT || 225 !((blendResult.w != BLEND_NONE && !IsPixEqual(E, C)) || 226 (blendResult.y != BLEND_NONE && !IsPixEqual(E, G)) || 227 (IsPixEqual(C, B) && IsPixEqual(B, A) && IsPixEqual(A, D) && 228 IsPixEqual(D, G) && !IsPixEqual(E, A)))); 229 vec2 origin = vec2(0.0, -1.0 / sqrt(2.0)); 230 vec2 direction = vec2(-1.0, 1.0); 231 if (doLineBlend) { 232 bool haveShallowLine = 233 (STEEP_DIRECTION_THRESHOLD * dist_D_C <= dist_B_G) && E != C && F != C; 234 bool haveSteepLine = 235 (STEEP_DIRECTION_THRESHOLD * dist_B_G <= dist_D_C) && E != G && H != G; 236 origin = haveShallowLine ? vec2(0.0, -0.25) : vec2(0.0, -0.5); 237 direction.x -= haveShallowLine ? 1.0 : 0.0; 238 direction.y += haveSteepLine ? 1.0 : 0.0; 239 } 240 vec4 blendPix = mix(D, B, step(ColorDist(E, B), ColorDist(E, D))); 241 res = mix(res, blendPix, GetLeftRatio(pos, origin, direction)); 242 } 243 frag_color = res; 244} 245