diff --git a/assets/shaders/tex_mmpx.csh b/assets/shaders/tex_mmpx.csh index 9b586196cf..5e606678ab 100644 --- a/assets/shaders/tex_mmpx.csh +++ b/assets/shaders/tex_mmpx.csh @@ -6,7 +6,7 @@ 2025 Enhanced by CrashGG. */ -// Performs 2x upscaling +// Performs 2x upscaling. #define ABGR8 uint @@ -17,28 +17,38 @@ Normalization loss: Using readColor (without 'u') reads as floats (vec4), mapping integer range (0-255) to [0.0, 1.0], causing precision loss (255→1.0, 1→0.0039215686...). The unpackUnorm4x8 function in MMPX converts uint to vec4 (normalized floats) - this step is lossy. */ + ABGR8 src(int x, int y) { return readColoru(uvec2(clamp(x, 0, params.width - 1), clamp(y, 0, params.height - 1))); } - -// This luma is only for brightness decision mechanism. Each alpha point multiplies the final output -// +/* +// Original luminance decision mechanism where small alpha changes cause significant output amplification uint luma(ABGR8 C) { uint alpha = (C & 0xFF000000u) >> 24; return (((C & 0x00FF0000u) >> 16) + ((C & 0x0000FF00u) >> 8) + (C & 0x000000FFu) + 1u) * (256u - alpha); } -// -// Modified: Linear brightness decision mechanism -/* -uint luma(ABGR8 C) { - uint alpha = (C >> 24) & 0xFFu; // Simplified bit operations - if (alpha == 0u) return 1530u; // Ensure fully transparent pixels return max value 1530 - uint rgbSum = ((C >> 16) & 0xFFu) + ((C >> 8) & 0xFFu) + (C & 0xFFu); - float factor = 1.0f + (255.0f - alpha) * 0.00392157f; // Multiplication equivalent to division by 255 - return uint(rgbSum * factor); -} */ +// +// Modified ternary alpha segmentation for efficiency optimization (LUT possible) +uint luma(ABGR8 C) { + uint alpha = (C & 0xFF000000u) >> 24; + + uint sum = ((C & 0x00FF0000u) >> 16) + + ((C & 0x0000FF00u) >> 8) + + (C & 0x000000FFu) + 1u; + + uint alphafactor = + (alpha == 0) ? 7000 : // Fully transparent + (alpha > 217) ? 1000 : // ≈0.8535534×255 (Two golden cuts) + (alpha > 157) ? 2000 : // ≈0.618034×255 (One golden cut) + (alpha > 97) ? 3000 : // ≈0.381966×255 (Short golden cut) + (alpha > 37) ? 4000 : 5000; // ≈0.1464466×255 (Two short golden cuts) + + return sum + alphafactor; +} +// + uvec4 extractPIX(ABGR8 color) { uint r = (color >> 0) & 0xFFu; uint g = (color >> 8) & 0xFFu; @@ -47,33 +57,27 @@ uvec4 extractPIX(ABGR8 color) { return uvec4(r, g, b, a); } -// RGB approximate equality (RGB Euclidean distance ≈0.00932276 after golden ratio^3), alpha difference within 14.59% (golden ratio^2) - +// Approximate RGB equality (Euclidean distance threshold ≈0.00932276) and alpha difference <14.59% (≈37.2/255) bool same(ABGR8 B, ABGR8 A0) { uvec4 b_pix = extractPIX(B); uvec4 a0_pix = extractPIX(A0); - // Calculate sum of squared RGB differences + // Sum of squared RGB differences ivec3 diff = ivec3(b_pix.rgb) - ivec3(a0_pix.rgb); - - // Calculate alpha difference (0-255 range) uint alphaDiff = abs(int(b_pix.a) - int(a0_pix.a)); - // 14.59%≈37.2 - bool alphaDiffCheck = alphaDiff < 38u; - + bool alphaDiffCheck = alphaDiff < 38u; // 5.57% ≈14.21, 14.59%≈37.2 return dot(diff, diff) < 606u && alphaDiffCheck; } -// Checks exact RGB equality (morphology), ignores alpha +// Full RGBA equality bool fullsame(ABGR8 B, ABGR8 A0){ - - return B == A0; //exact RGB match + return B == A0; } -// Full difference including alpha channel +// RGBA inequality bool notsame(ABGR8 B, ABGR8 A0){ - return (B!=A0); + return B != A0; } bool all_eq2(ABGR8 B, ABGR8 A0, ABGR8 A1) { @@ -105,101 +109,79 @@ bool none_eq4(ABGR8 B, ABGR8 A0, ABGR8 A1, ABGR8 A2, ABGR8 A3) { } /////////////////////// Test Colors /////////////////////// - vec4 testcolor = vec4(1.0, 0.0, 1.0, 1.0); // Magenta (opaque) - vec4 testcolor2 = vec4(0.0, 1.0, 1.0, 1.0); // Cyan (opaque) + //vec4 testcolor = vec4(1.0, 0.0, 1.0, 1.0); // Magenta (opaque) + //vec4 testcolor2 = vec4(0.0, 1.0, 1.0, 1.0); // Cyan (opaque) + //vec4 testcolor3 = vec4(1.0, 1.0, 0.0, 1.0); // Yellow (opaque) + //vec4 testcolor4 = vec4(1.0, 1.0, 1.0, 1.0); // White (opaque) float LumaFactor(vec4 col1, vec4 col2) { - // Calculate brightness difference using RGB average (0-1 range) + // Calculate luminance difference (0-1) using RGB average float LumaDiff = abs((col1.r + col1.g + col1.b) - (col2.r + col2.g + col2.b))*0.3333333; - // Apply golden ratio scaling (0.618 - 1.618) + float alphaDiff = abs(col1.a - col2.a); + // Golden ratio scaling (0.618 - 1.618) float LumaGolden = (1.618034 - LumaDiff) * (1.618034 - LumaDiff); // Squared for dot() operations - return LumaGolden; + + return LumaGolden/(1.0 - alphaDiff); // Amplify result with alpha difference } - vec4 admixC(ABGR8 X1, ABGR8 X2, vec4 rgbaE) { - // Unpack ABGR8 to RGBA float vector (0.0-1.0 range) + // Type-C: Weak blending dominance. Returns original if conditions unmet to avoid artifacts. + if (rgbaE.a < 0.01) return rgbaE; // Handle transparent center + vec4 rgbaX1 = unpackUnorm4x8(X1); vec4 rgbaX2 = unpackUnorm4x8(X2); - // Extract RGB components - vec3 rgbX1 = rgbaX1.rgb; - vec3 rgbX2 = rgbaX2.rgb; - vec3 rgbE = rgbaE.rgb; - - // Return early if both sides are dark (RGB magnitude <0.05) - if (dot(rgbX1, rgbX1) < 0.05 && dot(rgbX2, rgbX2) < 0.05) return rgbaE; - - // Return early if both sides are fully transparent + // Return if both sides are black (avoid blending) + if (dot(rgbaX1.rgb, rgbaX1.rgb) < 0.05 && dot(rgbaX2.rgb, rgbaX2.rgb) < 0.05) return rgbaE; + // Return if both sides are transparent if (rgbaX1.a < 0.01 && rgbaX2.a < 0.01) return rgbaE; - // Calculate squared Euclidean distance to reference color - float rgbDistX1 = dot(rgbX1 - rgbE, rgbX1 - rgbE); - float rgbDistX2 = dot(rgbX2 - rgbE, rgbX2 - rgbE); + // Calculate squared Euclidean distance + float rgbaDistX1 = dot(rgbaX1 - rgbaE, rgbaX1 - rgbaE); + float rgbaDistX2 = dot(rgbaX2 - rgbaE, rgbaX2 - rgbaE); - // Apply brightness factor adjustment - float rgb_lumaX1 = rgbDistX1 * LumaFactor(rgbaX1, rgbaE); - float rgb_lumaX2 = rgbDistX2 * LumaFactor(rgbaX2, rgbaE); + // Apply luminance factor adjustment + float rgb_lumaX1 = rgbaDistX1 * LumaFactor(rgbaX1, rgbaE); + float rgb_lumaX2 = rgbaDistX2 * LumaFactor(rgbaX2, rgbaE); // Select closer reference color bool useX1 = rgb_lumaX1 < rgb_lumaX2; float rgb_luma = useX1 ? rgb_lumaX1 : rgb_lumaX2; vec4 rgbaX = useX1 ? rgbaX1 : rgbaX2; - // Avoid copying X directly if center is transparent (could cause artifacts) - if (rgbaE.a < 0.01) return rgbaE; - - // Blend or retain original color + // Blend or return original return (rgb_luma < 0.75) ? mix(rgbaX, rgbaE, 0.5) : rgbaE; } vec4 admixK(ABGR8 X, vec4 rgbaE) { - // Unpack ABGR8 to RGBA float vector (0.0-1.0 range) vec4 rgbaX = unpackUnorm4x8(X); + // Type-K: Weak blending. Return original if transparent. + if (rgbaX.a < 0.01 || rgbaE.a < 0.01) return rgbaE; - // Return E if X is transparent - if (rgbaX.a < 0.01) return rgbaE; - - // Return X if E is transparent - if (rgbaE.a < 0.01) return rgbaX; + // Weighted dot product difference with luminance + float rgbaDist = dot(rgbaX - rgbaE, rgbaX - rgbaE); + float rgb_luma = rgbaDist * LumaFactor(rgbaX, rgbaE); - // Extract RGB components - vec3 rgbX = rgbaX.rgb; - vec3 rgbE = rgbaE.rgb; - - // Calculate weighted squared distance with brightness factor - float rgbDist = dot(rgbX - rgbE, rgbX - rgbE); - float rgb_luma = rgbDist * LumaFactor(rgbaX, rgbaE); - - // Blend based on threshold return (rgb_luma < 0.75) ? mix(rgbaX, rgbaE, 0.381966) : rgbaE; } -vec4 admixL(vec4 rgbaX, vec4 rgbaE) { - // If E is transparent, return X - if (rgbaE.a < 0.01) return rgbaX; - - // If X is transparent, return E - if (rgbaX.a < 0.01) return rgbaE; - - // Extract RGB components - vec3 rgbX = rgbaX.rgb; - vec3 rgbE = rgbaE.rgb; - - // Calculate squared RGB distance - float rgbDist = dot(rgbX - rgbE, rgbX - rgbE); - - // Check for nearly identical pixels - if (rgbDist < 0.00136041) return rgbaE; +vec4 admixL(vec4 rgbaX, vec4 rgbaE, ABGR8 S) { + // Type-L: Strong blending dominance + if (rgbaE.a < 0.01) return rgbaX; // Copy target if E transparent + if (rgbaX.a < 0.01) return rgbaE; // Return E if X transparent - // Apply brightness factor adjustment - float rgb_luma = rgbDist * LumaFactor(rgbaX, rgbaE); - - // Blend result + float rgbaDist = dot(rgbaX - rgbaE, rgbaX - rgbaE); + if (rgbaDist < 0.00136041) return rgbaX; // Return if nearly identical + + vec4 rgbaS = unpackUnorm4x8(S); + float rgbaXSDist = dot(rgbaX - rgbaS, rgbaX - rgbaS); + if (rgbaXSDist > 0.00136041) return rgbaX; // Return if X differs from S (already blended) + + float rgb_luma = rgbaDist * LumaFactor(rgbaX, rgbaE); return (rgb_luma < 0.75) ? mix(rgbaX, rgbaE, 0.381966) : rgbaX; } -/* Main corner logic with X-cross detection +/* Main corner detection with X-cross check P ┌───┬───┬───┐ ┌───┬───┬───┐ │ A │ B │ C │ │ A │ B2│ 1 │ @@ -208,23 +190,18 @@ vec4 admixL(vec4 rgbaX, vec4 rgbaE) { ├───┼───┼───┤ ├───┼───┼───┤ │ G │ H │ I │ │ 5 │ 4 │ 3 │ └───┴───┴───┘ └───┴───┴───┘ - Euclidean Formula: √(ΔR² + ΔG² + ΔB²) - Black↔White: 1.732, Black↔RGB: 1.0, White↔R↔G↔B: 1.414 - - Squared Distance: dot(Δ,Δ) avoids sqrt (10x cheaper) - Black↔White: 3.0, Black↔RGB: 1.0, White↔R↔G↔B: 2.0 - - Golden Ratio Thresholds: - Euclidean: 0.382 0.5 0.618 0.382² - Squared: 0.4377 0.75 1.1459 0.06386 - + Euclidean: √(ΔR² + ΔG² + ΔB²) + Black↔White: 1.732, Black↔RGB: 1.0, White↔R/G/B: 1.414 + Dot product: dot(LA-LB, LA-LB) = Euclidean² (faster) + Black↔White: 3.0, Black↔RGB: 1.0, White↔R/G/B: 2.0 + + Golden ratio positions (Euclidean): 0.382, 0.5, 0.618, 0.382² + Corresponding dot thresholds: 0.4377, 0.75, 1.1459, 0.06386 */ vec4 admixX(ABGR8 LE, ABGR8 LB1, ABGR8 LB2, ABGR8 LA, ABGR8 L1, ABGR8 L2, ABGR8 L3, ABGR8 L4, ABGR8 L5, ABGR8 LP, ABGR8 LQ) { - // First check using approximate pixels to prevent false positives on gradient lines vec4 rgbaLE = unpackUnorm4x8(LE); - if (fullsame(LE,LB1)||fullsame(LE,LB2)) return rgbaLE; + if (fullsame(LE,LB1)||fullsame(LE,LB2)) return rgbaLE; // Early exit if identical - // Unpack neighbor colors vec4 rgbaLB1 = unpackUnorm4x8(LB1); vec4 rgbaLB2 = unpackUnorm4x8(LB2); vec4 rgbaLA = unpackUnorm4x8(LA); @@ -236,95 +213,95 @@ vec4 admixX(ABGR8 LE, ABGR8 LB1, ABGR8 LB2, ABGR8 LA, ABGR8 L1, ABGR8 L2, ABGR8 vec4 rgbaLP = unpackUnorm4x8(LP); vec4 rgbaLQ = unpackUnorm4x8(LQ); - // Define LB blend candidate. Handle transparency edge cases - + // Define LB blending (handle transparency) vec4 rgbaLB = mix(rgbaLB1, rgbaLB2, 0.5); if (rgbaLB1.a < 0.01 ) rgbaLB=rgbaLB2; if (rgbaLB2.a < 0.01 ) rgbaLB=rgbaLB1; - // If the RGB difference between the two LBs is large, the alpha of the side with less alpha can be used after mixing to reduce burrs - if (rgbaLB1.a < rgbaLB.a ) rgbaLB.a=rgbaLB1.a; else rgbaLB.a=rgbaLB2.a; + if (rgbaLB1.a < rgbaLB.a ) rgbaLB.a=rgbaLB1.a; else rgbaLB.a=rgbaLB2.a; // Reduce artifacts in gradients (e.g., "Jojo's Wall") - // Calculate squared RGB distance and brightness adjustment + // Calculate RGBA dot product difference float rgbaDist = dot(rgbaLB - rgbaLE, rgbaLB - rgbaLE); float rgb_luma = rgbaDist * LumaFactor(rgbaLB, rgbaLE); float alphaDiff = abs((rgbaLB1.a + rgbaLB2.a) - rgbaLE.a * 2 ); - bool LBLEalpha0 = rgbaLB.a < 0.01 || rgbaLE.a < 0.01; // Transparency check - - // X-cross pattern detection core logic + bool LBLEalpha0 = rgbaLB.a < 0.01 || rgbaLE.a < 0.01; // Transparency flag + + // Cross pattern detection (core rule) if (same(LE,LA)) { - // Weak blend for very similar pixels to avoid artifacts - if (rgbaDist < 0.06386) return mix(rgbaLE,rgbaLB,0.381966); + if (rgbaDist < 0.06386) return mix(rgbaLE,rgbaLB,0.381966); // Weak blend for near-identical pixels // Special pattern: Tuning fork bool same_LB1_L4 = same(LB1,L4); bool same_LB1_L5 = same(LB1,L5); bool same_LB2_L1 = same(LB2,L1); bool same_LB2_L2 = same(LB2,L2); - if ( same_LB1_L4 && same_LB2_L1 && !same_LB2_L2 || same_LB1_L5 && same_LB2_L2 && !same_LB1_L4 ) return rgbaLE; + if ( (same_LB1_L4 && same_LB2_L1 && !same_LB2_L2) || + (same_LB1_L5 && same_LB2_L2 && !same_LB1_L4) ) return rgbaLE; // Special pattern: Large block cross - bool same_LB2_LP = same(LB2,LP); - bool same_LB1_LQ = same(LB1,LQ); bool same_LE_L2 = same(LE,L2); bool same_LE_L4 = same(LE,L4); + // Special pattern: Large block cross + if (same_LB2_L1 && same_LB1_L5 && (same(LB2,LP) && same_LE_L2 || same(LB1,LQ) && same_LE_L4)) return rgbaLE; - if (same_LB2_L1 && same_LB1_L5 && (same_LB2_LP && same_LE_L2 || same_LB1_LQ && same_LE_L4) ) return rgbaLE; - - // Special pattern: Diagonal cross grid + // Diagonal grid cross bool same_LE_L1 = same(LE,L1); bool same_LE_L3 = same(LE,L3); bool same_LE_L5 = same(LE,L5); - // A point - if (same_LE_L1 && same_LE_L3 && same_LE_L5 && !same_LE_L2 && !same_LE_L4) return rgbaLE; - bool same_LB1_L3 = same(LB1,L3); - bool same_LB2_L3 = same(LB2,L3); + if ( same_LE_L1 && same_LE_L3 && same_LE_L5 && !same_LE_L2 && !same_LE_L4 ) return rgbaLE; // B point - if ( same_LB2_L2 && same_LB1_L4 && !same_LB2_L1 && !same_LB1_L5 && !same_LB1_L3&& !same_LB2_L3 ) return rgbaLE; + if ( same_LB2_L2 && same_LB1_L4 && !same_LB2_L1 && !same_LB1_L5 && !same(LB1,L3)&& !same(LB2,L3) ) return rgbaLE; - // Scoring system for cross patterns + + // Scoring system for pattern recognition int score1 = 0; // Diagonal pattern score int score2 = 0; // Straight pattern score int scoreBonus = 0; // Diagonal pattern scoring - score1 += int(same_LE_L3); - score1 += int(same_LE_L1); - score1 += int(same_LE_L5); - score1 += int(same(L2, L4)); - score1 += int(same(LA, LP)); - score1 += int(same(LA, LQ)); + score1 += int(same_LE_L3); + + score1 += int(same_LE_L1); + score1 += int(same_LE_L5); + score1 += int(same(L2, L4)); + + score1 += int(same(LA, LP)); + score1 += int(same(LA, LQ)); + // Diagonal Y if ( score1<1) { if (same_LE_L2 && same_LB2_L1 || same_LE_L4 && same_LB1_L5 ) return rgbaLE; } - score1 += int(same_LB2_L2); - score1 += int(same_LB1_L4); - + score1 += int(same_LB2_L2) + int(same_LB1_L4); + // Straight pattern scoring score2 += int(fullsame(LP, LB2) && fullsame(L5, LB1)); score2 += int(fullsame(LQ, LB1) && fullsame(L1, LB2)); - // Penalize large brightness differences - float LumaDiff = abs((rgbaLE.r + rgbaLE.g + rgbaLE.b + rgbaLE.a) - (rgbaLB.r + rgbaLB.g + rgbaLB.b + rgbaLB.a))*0.3333333; - if (LumaDiff > 0.8541 ) scoreBonus -= 1; - if (alphaDiff > 1.0) scoreBonus -= 1; // Points are deducted when the cross-pixel alpha difference exceeds half + // Penalize high luminance/alpha differences + float LumaDiff = abs((rgbaLE.r+rgbaLE.g+rgbaLE.b+rgbaLE.a)-(rgbaLB.r+rgbaLB.g+rgbaLB.b+rgbaLB.a))*0.3333333; + if (LumaDiff > 0.8541) scoreBonus -= 1; // High luminance difference penalty + if (alphaDiff > 1.0) scoreBonus -= 1; // High alpha difference penalty + score1 += scoreBonus; score2 += scoreBonus; // Blend based on scores and thresholds - if (rgb_luma < 0.75 && (score1 >= 1 || score2 >= 1)) return (LBLEalpha0) ? rgbaLE : mix(rgbaLE, rgbaLB,0.5); - if (rgb_luma < 1.1459 && (score1 == 1 && score2 >= 1)) return (LBLEalpha0) ? rgbaLE : mix(rgbaLE, rgbaLB,0.381966); - if (score1 < 2 && score2 >= 1) return (LBLEalpha0) ? rgbaLE : mix(rgbaLE, rgbaLB,0.145898); + if (rgb_luma < 0.75 && (score1 >= 1 || score2 >= 1)) + return LBLEalpha0 ? rgbaLE : mix(rgbaLE, rgbaLB, 0.5); + if (rgb_luma < 1.1459 && (score1 == 1 && score2 >= 1)) + return LBLEalpha0 ? rgbaLE : mix(rgbaLE, rgbaLB, 0.381966); + if (score1 < 2 && score2 >= 1) + return LBLEalpha0 ? rgbaLE : mix(rgbaLE, rgbaLB, 0.145898); - // Final decision for strong patterns - return (score1 >= 2) ? rgbaLB : rgbaLE; + return (score1 >= 2) ? rgbaLB : rgbaLE; // Final decision } // Non-cross patterns - if (LBLEalpha0) return rgbaLB; - if (fullsame(LB1,LB2)) return (rgb_luma < 0.75) ? mix(rgbaLB, rgbaLE,0.381966) : rgbaLB; - return (rgb_luma < 0.75) ? mix(rgbaLB, rgbaLE,0.5) : rgbaLB; + if (LBLEalpha0) return rgbaLB; // Handle transparency + if (notsame(LB1,LB2)) return (rgb_luma < 0.75) ? mix(rgbaLB, rgbaLE, 0.5) : rgbaLB; // Gradient artifact prevention + if (fullsame(LB1,LA)) return rgbaLB; // Avoid blending on edges + return (rgb_luma < 0.75) ? mix(rgbaLB, rgbaLE, 0.381966) : rgbaLB; // Default weak blend } @@ -337,100 +314,174 @@ void applyScaling(uvec2 xy) { ABGR8 D = src(srcX - 1, srcY + 0), E = src(srcX, srcY + 0), F = src(srcX + 1, srcY + 0); ABGR8 G = src(srcX - 1, srcY + 1), H = src(srcX, srcY + 1), I = src(srcX + 1, srcY + 1); - // Default output pixels (center color) + // Default output: center color (E) vec4 centerColor = unpackUnorm4x8(E); vec4 J = centerColor, K = centerColor, L = centerColor, M = centerColor; - // Only process if neighborhood isn't uniform + // Process if neighborhood is non-uniform if (((A ^ E) | (B ^ E) | (C ^ E) | (D ^ E) | (F ^ E) | (G ^ E) | (H ^ E) | (I ^ E)) != 0u) { // Extended sampling for pattern detection ABGR8 P = src(srcX, srcY - 2), S = src(srcX, srcY + 2); ABGR8 Q = src(srcX - 2, srcY), R = src(srcX + 2, srcY); ABGR8 Bl = luma(B), Dl = luma(D), El = luma(E), Fl = luma(F), Hl = luma(H); - // Main scaling rules (J, K, L, M calculation) - if ((same(D,B) && none_eq2(D,H,F) && none_eq2(B,H,F)) && ((El>=Dl&&El>=Bl) || fullsame(E,A)) && any_eq3(E,A,C,G) && ((El=Bl&&El>=Fl) || fullsame(E,C)) && any_eq3(E,A,C,I) && ((El=Hl&&El>=Dl) || fullsame(E,G)) && any_eq3(E,A,G,I) && ((El=Fl&&El>=Hl) || fullsame(E,I)) && any_eq3(E,C,G,I) && ((El=Dl&&El>=Bl) || fullsame(E,A)) && (same_E_A||same_E_C||same_E_G) && ((El=Bl&&El>=Fl) || fullsame(E,C)) && (same_E_A||same_E_C||same_E_I) && ((El=Hl&&El>=Dl) || fullsame(E,G)) && (same_E_A||same_E_G||same_E_I) && ((El=Fl&&El>=Hl) || fullsame(E,I)) && (same_E_C||same_E_G||same_E_I) && ((El M + else if (!same_H_F && notsame(F,I) && all_eq3(E,C,B,D) && same(F,G) && same(R,H) && !same(E,F) && !same(E,H)) M=unpackUnorm4x8(F); + } + + + // . A B Ⓒ + // 🆀 🅳 🄴 F R + // G 🅷 🅸 + if (none_eq2(H,F,src(srcX-2,srcY-1))) { + + if (full_eq3(H,I,D,Q)) M=admixL(L,M,H); + // D -> L + else if (!same_H_D && notsame(D,G) && all_eq3(E,A,B,F) && same(D,I) && same(Q,H) && !same(E,D) && !same(E,H)) L=unpackUnorm4x8(D); + } + + } + // E under 2:1 (◥) -2:1 (◤) if (notsame(B,I) && notsame(B,G) && notsame(B,E)) { - // 🅰️ 🅱 C - // Q D 🄴 🅵 🆁 - // Ⓖ H I . - if (full_eq3(B,A,F,R) && none_eq2(B,D,src(srcX+2,srcY+1))) J=admixL(K,J); - // A 🅱 🅲 - // 🆀 🅳 🄴 F R - // . G H Ⓘ - if (full_eq3(B,C,D,Q) && none_eq2(B,F,src(srcX-2,srcY+1))) K=admixL(J,K); - } + // 🅰️🅱C + // Q D 🄴 🅵 🆁 + // Ⓖ H I . + if (none_eq2(B,D,src(srcX+2,srcY+1))) { + + if (full_eq3(B,A,F,R)) J=admixL(K,J,B); + // F -> K + else if (!same_B_F && notsame(C,F) && all_eq3(E,D,H,I) && same(A,F) && same(B,R) && !same(E,B) && !same(E,F)) K=unpackUnorm4x8(F); + } + + + // A 🅱🅲 + // 🆀 🅳 🄴 F R + // . G H Ⓘ + if (none_eq2(B,F,src(srcX-2,srcY+1))) { + + if (full_eq3(B,C,D,Q)) K=admixL(J,K,B); + // D -> J + else if (!same_B_D && notsame(A,D) && all_eq3(E,F,G,H) && same(B,Q) && same(C,D) && !same(E,B) && !same(E,D)) J=unpackUnorm4x8(D); + } + } + } if (notsame(F,D)) { + + if (notsame(D,I) && notsame(D,E) && notsame(D,C)) { - // 🅰B Ⓒ - // Q 🅳 🄴 F R - // G 🅷 I - // 🆂 . - if (full_eq3(D,A,H,S) && none_eq2(D,B,src(srcX+1,srcY+2))) J=admixL(L,J); + // 🅰B Ⓒ + // Q 🅳 🄴 F R + // G 🅷 I + // 🆂 . + if (none_eq2(D,B,src(srcX+1,srcY+2))) { + + if (full_eq3(D,A,H,S)) J=admixL(L,J,D); + // H -> L + else if (!same_H_D && notsame(G,H) && all_eq3(E,B,F,I) && same(A,H) && same(D,S) && !same(E,D) && !same(E,H)) L=unpackUnorm4x8(H); + } - // 🅿 . - // A 🅱 C - // Q 🅳 🄴 F R - // 🅶 H Ⓘ - if (full_eq3(D,G,B,P) && none_eq2(D,H,src(srcX+1,srcY-2))) L=admixL(J,L); - } + // 🅿. + // A 🅱C + // Q 🅳 🄴 F R + // 🅶 H Ⓘ + if (none_eq2(D,H,src(srcX+1,srcY-2))) { + + if (full_eq3(D,G,B,P)) L=admixL(J,L,D); + // B -> J + else if (!same_B_D && notsame(B,A) && all_eq3(E,C,F,H) && same(P,D) && same(B,G) && !same(E,B) && !same(E,D)) J=unpackUnorm4x8(B); + } + + } if (notsame(F,E) && notsame(F,A) && notsame(F,G)) { - // Ⓐ B 🅲 - // Q D 🄴 🅵 R - // G 🅷 I - // . 🆂 - if (full_eq3(F,C,H,S) && none_eq2(F,B,src(srcX-1,srcY+2))) K=admixL(M,K); + // Ⓐ B 🅲 + // Q D 🄴 🅵 R + // G 🅷 I + // . 🆂 + if (none_eq2(F,B,src(srcX-1,srcY+2))) { + + if (full_eq3(F,C,H,S)) K=admixL(M,K,F); + // H -> M + else if (!same_H_F && notsame(H,I) && all_eq3(E,B,D,G) && same(C,H) && same(F,S) && !same(E,F) && !same(E,H)) M=unpackUnorm4x8(H); + } + + // . 🅿 + // A 🅱C + // Q D 🄴 🅵 R + // Ⓖ H 🅸 + if (none_eq2(F,H,src(srcX-1,srcY-2))) { + + if (full_eq3(F,I,B,P)) M=admixL(K,M,F); + // B -> K + else if (!same_B_F && notsame(B,C) && all_eq3(E,A,D,H) && same(P,F) && same(B,I) && !same(E,B) && !same(E,F)) K=unpackUnorm4x8(B); + } - // . 🅿 - // A 🅱 C - // Q D 🄴 🅵 R - // Ⓖ H 🅸 - if (full_eq3(F,I,B,P) && none_eq2(F,H,src(srcX-1,srcY-2))) M=admixL(K,M); } } // F !== D } // not constant - // Write 2x2 upscaled pixels + + // Write four pixels at once. ivec2 destXY = ivec2(xy) * 2; writeColorf(destXY, J); writeColorf(destXY + ivec2(1, 0), K);