Merge pull request #20485 from crashGG/mmpx-enhace

enhance MMPX algorithm, fix some minor graphics defects.
This commit is contained in:
Henrik Rydgård
2025-06-10 15:17:55 +02:00
committed by GitHub
+133
View File
@@ -46,6 +46,108 @@ bool none_eq4(ABGR8 B, ABGR8 A0, ABGR8 A1, ABGR8 A2, ABGR8 A3) {
return B != A0 && B != A1 && B != A2 && B != A3;
}
// Calculate the RGB distance between two ABGR8 colors
float rgb_distance(ABGR8 a, ABGR8 b)
{
vec4 ca = unpackUnorm4x8(a);
vec4 cb = unpackUnorm4x8(b);
return distance(ca.rgb, cb.rgb);
}
// Calculate the normalized luminance difference between two ABGR8 colors. In practice, luma values range from 0.001 to 0.9998.
float luma_distance(ABGR8 a, ABGR8 b)
{
return abs(luma(a) - luma(b)) * 0.00000509f; // Multiplicative substitute for division by (255.0 * 256.0);
}
/*=============================================================================
Auxiliary function for 4-pixel cross determination: Scores the number of matches at specific positions in the pattern. Three pattern conditions must be met to achieve 6 points.
┌───┬───┬───┐ ┌───┬───┬───┐
│ A │ B │ C │ │ A │ B │ 1 │
├───┼───┼───┤ ├───┼───┼───┤
│ D │ E │ F │ => L │ B │ A │ 2 │
├───┼───┼───┤ ├───┼───┼───┤
│ G │ H │ I │ │ 5 │ 4 │ 3 │
└───┴───┴───┘ └───┴───┴───┘
=============================================================================*/
bool countPatternMatches(ABGR8 LA, ABGR8 LB, ABGR8 L1, ABGR8 L2, ABGR8 L3, ABGR8 L4, ABGR8 L5) {
int score1 = 0; // Diagonal pattern 1
int score2 = 0; // Diagonal pattern 2
int score3 = 0; // Horizontal/vertical line pattern
int scoreBonus = 0;
// Jointly determine the perceptual difference between two pixels using RGB distance and luminance distance
float pixelDist = rgb_distance(LA, LB)*0.356822 + luma_distance(LA, LB)*0.382; // Proportions follow the golden ratio
// Add details for very similar colors, reduce details for highly different colors (font edges)
if (pixelDist < 0.12) { // Colors are quite similar
scoreBonus += 1;
} else if (pixelDist > 0.9) { // High contrast (e.g., black and white)
scoreBonus -= 1;
}
// Diagonals use a penalty system: deduct points for crosses, add back if conditions are met
// 1. Diagonal pattern ╲ (Condition: B = 2 or 4)
if (LB == L2 || LB == L4) {
score1 -= int(LB == L2 && LA == L1) * 1; // A-1 and B-2 form a cross: deduct points
score1 -= int(LB == L4 && LA == L5) * 1; // A-5 and B-4 form a cross: deduct points
// Add points if the following triangular patterns are satisfied (canceling previous cross deductions)
score1 += int(LB == L1 && L1 == L2) * 1; // B-1-2 form a triangle: add points
score1 += int(LB == L4 && L4 == L5) * 1; // B-4-5 form a triangle: add points
score1 += int(L2 == L3 && L3 == L4) * 1; // 2-3-4 form a triangle: add points
score1 += scoreBonus + 6;
}
// 2. Diagonal pattern (Condition: A = 1 or 5)
if (LA == L1 || LA == L5) {
score2 -= int(LB == L2 && LA == L1) * 1; // A-1 and B-2 cross: deduct points
score2 -= int(LB == L4 && LA == L5) * 1; // A-5 and B-4 cross: deduct points
score2 -= int(LA == L3) * 1; // A-3 forms a cross: deduct points
// Add points if the following triangular patterns are satisfied (canceling previous cross deductions)
score2 += int(LB == L1 && L1 == L2) * 1; // B-1-2 form a triangle: add points
score2 += int(LB == L4 && L4 == L5) * 1; // B-4-5 form a triangle: add points
score2 += int(L2 == L3 && L3 == L4) * 1; // 2-3-4 form a triangle: add points
score2 += scoreBonus + 6;
}
// 3. Horizontal/vertical line patterns (Condition: horizontal continuity) use a scoring system; pass only if conditions are met
if (LA == L2 || LB == L1 || LA == L4 || LB == L5 || (L1 == L2 && L2 == L3) || (L3 == L4 && L4 == L5)) {
score3 += int(LA == L2); // A matches 2 +1
score3 += int(LB == L1); // B matches 1 +1
score3 += int(L3 == L4); // 3 matches 4 +1
score3 += int(L4 == L5); // 4 matches 5 +1
score3 += int(L3 == L4 && L4 == L5) ; // 3-4-5 continuous
score3 += int(LB == L5); // B matches 5 +1
score3 += int(LA == L4); // A matches 4 +1
score3 += int(L2 == L3); // 2 matches 3 +1
score3 += int(L1 == L2); // 1 matches 2 +1
score3 += int(L1 == L2 && L2 == L3) ; // 1-2-3 continuous
// A x 4 square
score3 += int(LA == L2 && L2 == L3 && L3 == L4) * 2;
// Patch for the above rule to avoid bubble-like cross patterns in large grids. Some games use single-side patterns, so bilateral checks are preferred (work in progress)
score3 -= int(LB == L1 && L1 == L5 && LA == L2 && L2 == L4)*3 ;
score3 -= int(LA == L1 && LA == L5) ; // Deduct points if L1 and L5 are both A to avoid excessive scoring and diagonal pattern misidentification
// Bonus points
score3 += scoreBonus; // Experience: Even with very similar colors, avoid over-scoring to prevent bubbles in Z-shaped crosses
}
// Take the maximum of the four scores
int score = max(max(score1, score2), score3);
return score < 6; // Requires a minimum of 6 points
}
void applyScaling(uvec2 xy) {
int srcX = int(xy.x);
int srcY = int(xy.y);
@@ -61,6 +163,37 @@ void applyScaling(uvec2 xy) {
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);
// Default output (to avoid code duplication)
ivec2 destXY = ivec2(xy) * 2;
ABGR8 defaultColor = E;
writeColorf(destXY, unpackUnorm4x8(defaultColor));
writeColorf(destXY + ivec2(1, 0), unpackUnorm4x8(defaultColor));
writeColorf(destXY + ivec2(0, 1), unpackUnorm4x8(defaultColor));
writeColorf(destXY + ivec2(1, 1), unpackUnorm4x8(defaultColor));
// Check for "convex" shapes to avoid single-pixel spikes on long straight edges
if (A == B && B == C && E == H && A != D && C != F && rgb_distance(D, F) < 0.2 && rgb_distance(B, E) > 0.6) return;
if (A == D && D == G && E == F && A != B && G != H && rgb_distance(B, H) < 0.2 && rgb_distance(D, E) > 0.6) return;
if (C == F && F == I && E == D && B != C && H != I && rgb_distance(B, H) < 0.2 && rgb_distance(E, F) > 0.6) return;
if (G == H && H == I && B == E && D != G && F != I && rgb_distance(D, F) < 0.2 && rgb_distance(E, H) > 0.6) return;
// Check each 4-pixel cross in "田" (field) shape and pass five surrounding pixels for pattern judgment
if (A == E && B == D && A != B && countPatternMatches(A, B, C, F, I, H, G)) return;
if (C == E && B == F && C != B && countPatternMatches(C, B, A, D, G, H, I)) return;
if (G == E && D == H && G != H && countPatternMatches(G, H, I, F, C, B, A)) return;
if (I == E && F == H && I != H && countPatternMatches(I, H, G, D, A, B, C)) return;
// Original MMPX logic
// 1:1 slope rules
if ((D == B && D != H && D != F) && (El >= Dl || E == A) && any_eq3(E, A, C, G) && ((El < Dl) || A != D || E != P || E != Q)) J = D;
if ((B == F && B != D && B != H) && (El >= Bl || E == C) && any_eq3(E, A, C, I) && ((El < Bl) || C != B || E != P || E != R)) K = B;