mmpx_enhanced.glsl1096 lines · 35.1 KB · raw
1// MMPX Enhanced
2// Original MMPX by Morgan McGuire and Mara Gagiu, MIT license.
3// Adapted for Slang by hunterk. Enhanced by CrashGG.
4// Ported from libretro/slang-shaders mmpx-ex.slang to Geargrafx GLSL preset uniforms.
5
6// Adapted to a compute shader with num workgroups equal to ceil(inputSize / 16); each invocation writes 2x2 output pixels
7
8layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in;
9
10layout(rgba8, binding = 0) uniform readonly image2D Source;
11layout(rgba8, binding = 1) uniform writeonly image2D Output;
12
13uint pack_color(vec3 color)
14{
15    uvec3 value = uvec3(clamp(color, vec3(0.0), vec3(1.0)) * 255.0 + vec3(0.5));
16    return value.r | (value.g << 8u) | (value.b << 16u) | (255u << 24u);
17}
18
19vec3 unpack_color(uint value)
20{
21    return vec3(float(value & 255u), float((value >> 8u) & 255u), float((value >> 16u) & 255u)) / 255.0;
22}
23
24float flag(bool value)
25{
26	return value ? 1.0 : 0.0;
27}
28
29
30float luma(vec3 col){
31   //Use BT.601 standard from the CRT era
32   return dot(col, vec3(0.299, 0.587, 0.114));
33}
34
35/* Constant definitions:
360.145898	:	Two short golden ratios of 1.0
370.0638587	:	Squared short golden ratio (2x) for RGB Euclidean distance
380.4377		:	Squared short golden ratio (1x) for RGB Euclidean distance
390.75			:	Squared half of RGB Euclidean distance
40*/
41
42bool simb(vec3 col1, vec3 col2) {
43
44	vec3 diff = col1 - col2;
45
46	float maxdiff = max(diff.r, max(diff.g, diff.b));
47	float mindiff = min(diff.r, min(diff.g, diff.b));
48
49	// Luminance weight floor: both colors must be > 0.078.
50	float weight = step(0.234, min(col1.r+col1.g+col1.b, col2.r+col2.g+col2.b));
51
52	// Find the most opposite channel: take the smallest absolute value if one is positive and the other negative; 0 if same sign
53	// Filter same-sign cases using max(0.0, ...)
54	// Skip team_rebel if either pixel's luminance < 0.078
55	float team_rebel = min(max(0.0, maxdiff), max(0.0, -mindiff)) * weight;
56	float finaldist = (maxdiff - mindiff) + team_rebel;
57
58	float dot_diff = dot(diff, diff);
59
60	// Equivalent to squared final distance scaled by the short golden ratio.
61	float factor = (finaldist * finaldist) * 46.9787;
62
63	return dot_diff < mix(0.0638587, 0.0, factor);
64}
65
66bool sim(vec3 col1, vec3 col2) {
67
68	vec3 diff = col1 - col2;
69
70	// RGB color difference range (max_diff - min_diff)
71	float delta_range = max(diff.r, max(diff.g, diff.b)) - min(diff.r, min(diff.g, diff.b));
72
73	float dot_diff = dot(diff, diff);
74
75	// Equivalent to squared delta range scaled by the short golden ratio.
76	float factor = (delta_range * delta_range) * 6.8541;
77
78	return dot_diff < mix(0.0638587, 0.0, factor);
79}
80
81bool vi_sim(vec3 col1, uint uC1, uint uC2) {
82    if (uC1==uC2) return true;
83    vec3 col2 = unpack_color(uC2);
84    return sim(col1, col2);
85}
86
87float mixGate(vec3 col1, vec3 col2) {
88
89	vec3 diff = col1 - col2;
90
91	// RGB color difference range (max_diff - min_diff)
92	float delta_range = max(diff.r, max(diff.g, diff.b)) - min(diff.r, min(diff.g, diff.b));
93
94	float dot_diff = dot(diff, diff);
95
96	// Equivalent to squared delta range scaled by the long golden ratio.
97	float factor = (delta_range * delta_range) * 2.618034;
98
99	return step(dot_diff, mix(0.75, 0.0, factor));
100}
101
102
103#define eq(a,b) (a==b)
104
105#define neq(a,b) (a!=b)
106
107#define all_eq2(a, b1, b2) \
108	( eq(a,b1) && eq(a,b2))
109
110#define all_eq3(a, b1, b2, b3) \
111	( eq(a,b1) && eq(a,b2) && eq(a,b3))
112
113#define all_eq4(a, b1, b2, b3, b4) \
114	( eq(a,b1) && eq(a,b2) && eq(a,b3) && eq(a,b4))
115
116#define any_eq2(a, b1, b2) (eq(a,b1)||eq(a,b2))
117#define any_eq3(a, b1, b2, b3) (eq(a,b1)||eq(a,b2)||eq(a,b3))
118// Better than a!=b1 && a!=b2
119#define none_eq2(a, b1, b2) !any_eq2(a, b1, b2)
120
121
122// Allow total int2 difference across 3 channels
123//#define vec_neq(a,b) (dot(abs(a-b), vec3(1.0)) > 0.01)
124// Allow int2 difference per channel for 3 channels
125#define vec_eq(a,b) all(lessThan(abs(a-b), vec3(0.01)))
126#define vec_neq(a, b) any(greaterThan(abs(a-b), vec3(0.01)))
127
128
129// Pre-define
130//const vec3 testcolor  = vec3(1.0, 0.0, 1.0);  // Magenta
131//const vec3 testcolor2 = vec3(0.0, 1.0, 1.0);  // Cyan
132//const vec3 testcolor3 = vec3(1.0, 1.0, 0.0);  // Yellow
133//const vec3 testcolor4 = vec3(1.0, 1.0, 1.0);  // White
134const vec3 slopOFF   = vec3(2.0);
135const vec3 slopeBAD  = vec3(4.0);
136const vec3 theEXIT   = vec3(8.0);
137
138#define mixXE mix(vX,vE,mixFactor)
139#define mixXEoff mixXE+slopOFF
140#define Xoff vX+slopOFF
141//#define checkblack(col) ((col).g < 0.078 && (col).r < 0.1 && (col).b < 0.1)
142#define checkblack(col) all(lessThan((col).rgb, vec3(0.1, 0.078, 0.1)))
143#define checkwhite(col) all(greaterThan((col).rgb, vec3(0.92, 0.92, 0.92)))
144
145//pin zz
146// "Concave + Cross" weak blend (weak/no blending)
147vec3 admixC(vec3 vX, vec3 vE) {
148	// Weak blending. Mix if 0.618, else 1.0
149	float mixFactor = mixGate(vX, vE) * (-0.381966) + 1.0;
150
151	return mixXE;
152}
153
154// K-type forced weak blend
155vec3 admixK(vec3 vX, vec3 vE) {
156    vec3 diff = vX - vE;
157	// mixFactor scales from 0.5-1.0 with quadratic falloff (steeper near 1.0) based on squared distance
158	float mixFactor = dot(diff, diff) * 0.16666 + 0.5;
159	// mixFactor scales linearly from 0.5-1.0 based on Euclidean distance
160	//float mixFactor = distance(vX, vE) * 0.28867 + 0.5;
161	return mixXE;
162}
163
164// L-type 2:1 slope - main corner extension
165// Practical rule: this requires 4 pixels on the strict slope to be identical. Artifacts occur otherwise!
166vec3 admixL(vec3 vX, vec3 vE, vec3 vS) {
167
168    // If target X differs from reference S(sample), it's already blended once - copy directly without re-blending
169	if (vec_neq(vX, vS)) return vX;
170
171	float mixFactor = 0.381966 * mixGate(vX,vE);
172
173    return mixXE;
174}
175
176/**************************************************************************************************************************************
177 * 												Main slope + X cross-processing mechanism						                *
178 ******************************************************************************************************************************** zz  */
179vec3 admixX( uint A, uint B, uint C, uint D, uint E, uint F, uint G, uint H, uint I
180		  , uint P, uint PA, uint PC, uint Q, uint QA, uint QG, uint R, uint RC, uint RI, uint S, uint SG, uint SI, uint AA, uint CC, uint GG
181		  , float El, float Bl, float Dl, float Fl, float Hl
182		  , vec3 vE, vec3 vB, vec3 vD, vec3 vC, vec3 vG
183		  ) {
184
185
186	bool eq_B_C = eq(B,C);
187	bool eq_D_G = eq(D,G);
188
189    // Exit if enclosed by straight walls on both sides
190    if (eq_B_C && eq_D_G) return slopeBAD;
191
192
193	//Pre-declare
194	bool eq_B_P;		bool eq_B_PA;	bool eq_B_PC;
195	bool eq_D_Q;		bool eq_D_QA;	bool eq_D_QG;
196	bool eq_E_F;		bool eq_E_H;		bool eq_A_AA;
197
198	vec3 vX;
199	float mixFactor;
200
201	bool eq_E_C = eq(E,C);
202	bool eq_E_G = eq(E,G);
203    bool eq_A_P = eq(A,P);
204    bool eq_A_Q = eq(A,Q);
205    bool comboE3 = eq_E_C && eq_E_G;
206    bool comboA3 = eq_A_P && eq_A_Q;
207
208/*=========================================
209                    B != D
210  ==================================== zz */
211if (neq(B,D)){
212
213	// E == A violates logic, exit
214	if (eq(E,A)) return slopeBAD;
215
216	// B-D unconnected? Removed
217
218	// If B and D differ significantly (more than either to center E), exit
219	float diffBD = abs(Bl-Dl);
220	if (diffBD > El-Bl || diffBD > El-Dl) return slopeBAD;
221
222
223	// X is blend of B and D
224	vX = mix(vB, vD, 0.5);
225
226	mixFactor = 0.381966 * mixGate(vX,vE);
227
228	eq_B_PC = eq(B,PC);
229	eq_D_QG = eq(D,QG);
230
231	// Strong pattern set
232    if (none_eq2(A,B,D)){
233		if (comboA3) return mixXEoff;
234		if ( eq_A_P && eq_B_PC && !eq_B_C ) return mixXEoff;
235		if ( eq_A_Q && eq_D_QG && !eq_D_G ) return mixXEoff;
236
237		// Double slope clamping BD (direction-aware)
238		if ( eq_A_P && eq_E_G ) return mixXEoff;
239		if ( eq_A_Q && eq_E_C ) return mixXEoff;
240
241		// Hollow L inner corner
242		if ( eq_E_C && eq_D_G ) return mixXEoff;
243		if ( eq_E_G && eq_B_C ) return mixXEoff;
244
245}
246    // E-side triple match
247    if ( comboE3 ) return mixXEoff;
248
249	// Original rule with added slope condition
250    if ( eq_E_C && eq_B_PC && neq(B,P)) return mixXEoff;
251	if ( eq_E_G && eq_D_QG && neq(D,Q)) return mixXEoff;
252
253	eq_E_F = eq(E,F);
254
255	// F == H
256	if (eq(F,H)) {
257
258		// Double slope (exclude single-pixel C enclosure, BD are weakly connected)
259		if ( eq_E_C && !eq_D_G && (!eq_E_F||neq(E,P)) ) return mixXEoff;
260		if ( eq_E_G && !eq_B_C && (!eq_E_F||neq(E,Q)) ) return mixXEoff;
261
262		// F+H extension
263		if ( !eq_E_F && eq_B_PC && eq(F,RC) ) return mixXEoff;
264		if ( !eq_E_F && eq_D_QG && eq(H,SG) ) return mixXEoff;
265	}
266
267    return slopeBAD;
268} // B != D
269
270
271						/*********  B == D  *********/
272
273	// Prevent font edges from being crushed by black background on 3 sides
274	bool Xisblack = checkblack(vB);
275	if ( Xisblack && El >0.5 && (Fl<0.078 || Hl<0.078) ) return theEXIT;
276
277	vX = vB;
278
279	mixFactor = 0.381966 * mixGate(vX,vE);
280
281	bool B_slope;	bool B_tower;	bool B_wall;
282    bool D_slope;	bool D_tower;	bool D_wall;
283	bool En3;
284    #define En4square En3&&eq(E,I)
285
286/*===================================================
287                    E - A intersection
288  ============================================== zz */
289if (eq(E,A)) {
290
291    // Special pattern: dithering
292    // Goal: force blending
293
294	eq_E_F = eq(E,F);
295	eq_E_H = eq(E,H);
296
297	bool Eisblack = checkblack(vE);
298
299	// 1. Dither center
300    if ( comboE3 && !eq_E_F && !eq_E_H && eq(E,I) ) {
301
302		// Exit if center E is black (KOF '96 power gauge, Punisher belt) - avoid high-contrast blending
303		if (Eisblack) return theEXIT;
304		// Practical 1: skip black B points (handled by normal logic)
305		// Mix if 0.381966, else 0.618034
306		mixFactor = 0.618034 * (1.0 - mixFactor);
307		return mixXEoff;
308	}
309
310	eq_A_AA = eq(A,AA);
311
312	// 2. Dither edge
313    if ( comboA3 && eq_A_AA && none_eq2(A,PA,QA) )  {
314		if (Eisblack) return theEXIT;
315		// Mix if 0.381966, else 0.618034
316		mixFactor = 0.618034 * (1.0 - mixFactor);
317        // Strong blend for layered gradient edges
318		if ( neq(B,PA) && eq(PA,QA) ) return mixXEoff;
319        // Remainder: perfect cross = dither edge, use weak blend
320		// Practical: no special handling needed for health bar borders
321        // Floor weak blend. Mix if 0.618, else 0.854
322		// Note: mixFactor already modified above
323		mixFactor += 0.236068;
324		return mixXEoff;
325	}
326
327    eq_B_PC = eq(B,PC);
328    eq_B_PA = eq(B,PA);
329    eq_D_QG = eq(D,QG);
330    eq_D_QA = eq(D,QA);
331
332	// Subsequent checks skip Eisblack
333	// 3. Half-dither (usually silhouette edge shading), use weak blend
334
335	if ( comboE3 && comboA3 &&
336		(eq_B_PC || eq_D_QG) && eq_D_QA && eq_B_PA) {
337        // Floor weak blend. Mix if 0.618, else 0.854
338		mixFactor = mixFactor * (-0.618034) + 0.8541;
339        return mixXEoff;
340	}
341
342    // 4. Quarter-dither (prevents ugly "pinky" artifacts: SF2 Guile plane, Cadillacs and Dinosaurs character select)
343
344	if ( comboE3 && eq_A_P
345		 && eq_B_PA && eq_D_QA && eq_D_QG
346		 && eq_E_H
347		) {// Floor weak blend. Mix if 0.618, else 0.854
348		mixFactor = mixFactor * (-0.618034) + 0.8541;
349        return mixXEoff;
350		}
351
352	if ( comboE3 && eq_A_Q
353		 && eq_B_PA && eq_D_QA && eq_B_PC
354		 && eq_E_F
355		) {// Floor weak blend. Mix if 0.618, else 0.854
356		mixFactor = mixFactor * (-0.618034) + 0.8541;
357        return mixXEoff;
358		}
359
360
361    // A-side triple match (strong pattern, after dither logic)
362	if (comboA3) return Xoff;
363
364    // E-side triple match (after comboA3)
365    if (comboE3) return mixXEoff;
366
367	eq_B_P = eq(B, P);
368	eq_D_Q = eq(D, Q);
369
370	B_slope = eq_B_PC && !eq_B_P && !eq_B_C && !eq_B_PA;
371	D_slope = eq_D_QG && !eq_D_Q && !eq_D_G && !eq_D_QA;
372
373	B_wall = eq_B_C && !eq_B_PC && !eq_B_P;	// Removed one misalignment check
374	D_wall = eq_D_G && !eq_D_QG && !eq_D_Q;	// Removed one misalignment check
375
376	B_tower = eq_B_P && !eq_B_PC && !eq_B_C && !eq_B_PA;
377	D_tower = eq_D_Q && !eq_D_QG && !eq_D_G && !eq_D_QA;
378
379
380	if ( B_slope && eq_E_G ) return mixXEoff;
381	if ( D_slope && eq_E_C ) return mixXEoff;
382
383
384// E/B/D zone scoring system
385
386    float scoreE = 0.0; float scoreB = 0.0; float scoreD = 0.0; float scoreZ = 0.0;
387
388//	E Zone
389    if (eq_E_C) {
390		scoreE += 1.0 + flag(eq(F,H)) + flag(B_slope);
391		scoreE -= flag(all_eq2(E,P,PC)&&!D_wall);
392	}
393
394    if (eq_E_G) {
395		scoreE += 1.0 + flag(eq(F,H)) + flag(D_slope);
396		scoreE -= flag(all_eq2(E,Q,QG)&&!B_wall);
397    }
398
399	// Higher priority than rectangles
400	scoreE += flag(B_slope && eq_A_Q || D_slope && eq_A_P);
401
402    En3 = eq_E_F && eq_E_H;
403
404	// Clear 4/6-square: early exit, skip final Z long-slope check
405	if ( scoreE<0.1 && mixFactor<0.1 && En4square && eq(E,S)==eq(E,SI) && eq(E,R)==eq(E,RI) ) return theEXIT;
406
407	// No score for En3
408	//if ( scoreE==0 && En3 ) scoreE += 1;
409
410	// Single bar
411	if ( scoreE<0.1 && !En3 && neq(E,I) ) {
412		if ( B_wall && eq_E_F ) return theEXIT;
413		if ( D_wall && eq_E_H ) return theEXIT;
414    }
415
416	// Lower priority than single bar
417	scoreE += flag(B_slope && eq_A_P || D_slope && eq_A_Q);
418
419    if ( !En3 && eq(F,H) ) {
420		if (Eisblack) return slopeBAD;		// Black single pixel
421		// slope+eq_F_H combination disabled (avoids bubbles with inner L in BD zone)
422		//scoreE += flag(B_slope&&neq(C,F))+flag(D_slope&&neq(G,H));
423
424		bool condZ1 = B_wall && (eq(F,R) || eq(F,RC) || eq(G,H) || eq(F,I));
425		bool condZ2 = D_wall && (eq(C,F) || eq(H,SG) || eq(H,S) || eq(F,I));
426		scoreZ = flag(condZ1 || condZ2);
427    }
428
429
430//	B Zone
431
432    if (eq_B_PA) {
433		scoreB -= 1.0 + flag(eq(P,C)) + flag(eq_A_AA);
434	}
435
436	if (eq(P,C)){
437		scoreB -= flag(eq_A_AA);
438		// Critical: prevent Z scoring on mirrored shapes when only F==H
439		// Equivalent: if (scoreE==0) scoreZ = 0;
440		scoreZ *= flag(scoreE < 0.1);
441	}
442
443//  D Zone
444
445    if (eq_D_QA) {
446		scoreD -= 1.0 + flag(eq(G,Q)) + flag(eq_A_AA);
447	}
448
449	if (eq(G,Q)){
450		scoreD -= flag(eq_A_AA);
451		// Same logic as B zone
452		scoreZ *= flag(scoreE < 0.1);
453	}
454
455    float scoreFinal = scoreE + scoreB + scoreD + scoreZ ;
456
457	// Long slope: return unblended vX if B/D zones have no penalties and form a gentle slope
458	scoreFinal += flag(min(scoreB,scoreD) > -0.1 && (B_wall && D_tower || B_tower && D_wall)) * 2.0;
459
460	// Set mixFactor to 0 (no blend) if scoreFinal >= 2, return vX
461	mixFactor *= (1.0 - step(1.9, scoreFinal));
462	// Return mixXE if scoreFinal >=1, else slopeBAD
463	return mixXE + slopeBAD*(1.0 - step(0.9, scoreFinal));
464
465}	// E == A
466
467
468/*===============================================
469                 Main rule: E - C - G
470  ========================================== zz */
471
472    if (eq_E_C ) {
473		if (comboA3) return vX;
474		if (comboE3) return mixXE;
475		if (all_eq2(B,A,PA) && all_eq3(E,F,P,PC)) return theEXIT;
476		return mixXE;
477	}
478
479	if (eq_E_G) {
480		if (comboA3) return vX;
481		if (comboE3) return mixXE;
482		if (all_eq2(D,A,QA) && all_eq3(E,H,Q,QG)) return theEXIT;
483		return mixXE;
484	}
485
486
487/*=========================================================
488                   F - H / B+ D+ extension rules
489  ==================================================== zz */
490
491	// This section handles leftovers from previous filters; center En4square is naturally walled off from BD logic
492    // B-D unconnected? No longer needed with new "double slope" logic
493	// Rule 1: Flatten inner L corner, not outer
494	// Rule 2: Flatten outer "" edge, not inner
495
496    bool eq_A_B = eq(A,B);
497    bool eq_F_H = eq(F,H);
498
499	eq_B_P  = eq(B,P);
500	eq_B_PC = eq(B,PC);
501	eq_B_PA = eq(B,PA);
502	eq_D_Q  = eq(D,Q);
503	eq_D_QG = eq(D,QG);
504	eq_D_QA = eq(D,QA);
505
506	B_slope = eq_B_PC && !eq_B_P && !eq_B_C;
507	D_slope = eq_D_QG && !eq_D_Q && !eq_D_G;
508	B_tower = eq_B_P && !eq_B_PC && !eq_B_C && !eq_B_PA;
509	D_tower = eq_D_Q && !eq_D_QG && !eq_D_G && !eq_D_QA;
510	B_wall = eq_B_C && !eq_B_PC && !eq_B_P;
511	D_wall = eq_D_G && !eq_D_QG && !eq_D_Q;
512
513
514//	1. B-D hollow slope
515    if (!eq_A_B) {
516
517        // A-side triple match (high priority)
518		// Note: comboA3 only valid when A!=B in this section
519        if (comboA3) return Xoff;
520
521        if ( (B_slope||B_tower) && (D_slope||D_tower) ) return Xoff;
522
523        if ( B_slope && eq_A_P ) return mixXEoff;
524        if ( D_slope && eq_A_Q ) return mixXEoff;
525
526        if ( (B_slope || D_slope) && eq_F_H ) return mixXEoff;
527
528        if ( B_slope && eq(H,SG) ) return mixXEoff;
529        if ( D_slope && eq(F,RC) ) return mixXEoff;
530
531        if ( B_slope && eq_A_Q && eq(Q,QG) ) return mixXEoff;
532        if ( D_slope && eq_A_P && eq(P,PC) ) return mixXEoff;
533
534    }
535
536
537
538	bool sim_EC = sim(vE, vC);
539	bool sim_EG = sim(vE, vG);
540
541	// Exit if center E is a high-contrast single pixel
542	// Tighten threshold for bright E
543	float E_lumDiff = mix(0.381966, 0.145898, max((El - 0.8541),0.0) * 6.8541);
544
545	// High contrast to neighbors (lower priority than slope detection)
546    if ( mixFactor<0.1 && !sim_EC && !sim_EG && neq(E,I) && abs(El-Fl)>E_lumDiff && abs(El-Hl)>E_lumDiff ) return slopeBAD;
547
548
549	eq_E_F = eq(E,F);
550	eq_E_H = eq(E,H);
551
552    // Long gentle slope (preserve squares for later En4square check)
553	if ( eq_B_C && eq_D_Q ) {
554		if ( eq(P,PC) && eq(A,QA) && !eq_D_QG && eq_E_F && !eq_E_H && eq(H,I)) return theEXIT;
555		if ( eq_A_B ) return slopeBAD;
556		if ( B_wall && D_tower && eq_E_F) return vX;
557		return mixXEoff;
558	}
559
560	if ( eq(D,G) && eq(B,P)) {
561		if ( eq(Q,QG) && eq(A,PA) && !eq_B_PC && eq_E_H && !eq_E_F && eq(F,I)) return theEXIT;
562		if ( eq_A_B ) return slopeBAD;
563		if ( B_tower && D_wall && eq_E_H) return vX;
564		return mixXEoff;
565	}
566
567
568    En3 = eq_E_F && eq_E_H;
569
570    // Wall-enclosed 4-square (En3 && eq(E,I))
571	if ( En4square ) {  // Must come after previous rule
572        // Exit for solid L enclosure (font edges, building corners)
573        // Solid L corner / high-contrast clear 4-square / 6-rectangle (no need for eq(G,H)/eq(C,F) checks)
574        if ( ( eq_B_C || eq_D_G) && eq_A_B) return theEXIT;
575        if ( ( eq_B_C || eq_D_G || mixFactor<0.1) && (eq(E,S) == eq(E, SI) && eq(E,R) == eq(E, RI)) ) return theEXIT;
576        return mixXEoff;
577    }
578
579	// BD non-wall solid shapes
580	if (!eq_B_C && !eq_D_G ) {
581		 // B/D semi-solid 1 (requires F-H)
582		if ( comboA3 && eq_F_H ) return Xoff;
583
584		// B/D semi-solid 2 (with "definite rounding" trend)
585		if ( comboA3&&eq_B_PC&&eq(C,CC) ) return Xoff;
586		if ( comboA3&&eq_D_QG&&eq(G,GG) ) return Xoff;
587
588		// Exit if B/D unconnected and not En3 (required for this branch)
589		if ( !eq_B_P && !eq_B_PC && !eq_D_Q && !eq_D_QG && !En3 ) return slopeBAD;
590
591		// 3 diagonal gradients (after above filter)
592		if (eq_A_Q&&sim_EC) return mixXEoff;
593		if (eq_A_P&&sim_EG) return mixXEoff;
594		if (sim_EC&&sim_EG ) return mixXEoff;
595	}
596
597    // Wall-enclosed triangle (remove solid corner, pass to next rule)
598 	if ( En3 && eq_A_B) return theEXIT;
599
600    // F - H
601	// Rule: connect inner L corner, not outer
602	if (eq_F_H) {
603
604		// F-H triple pattern (huge quality boost! priority over A==B)
605		if ( eq_B_PC&&eq(F,RC) || eq_D_QG&&eq(H,SG) ) return mixXEoff;
606
607		if (eq_A_B) return slopeBAD;
608
609		if ( eq_B_C || eq_D_G) return mixXEoff;
610		if ( eq_B_PC || eq_D_QG) return mixXEoff;
611
612	}
613
614	return slopeBAD;
615
616}	// admixX
617
618
619vec3 admixS( uint A, uint B, uint C, uint D, uint E, uint F, uint G, uint H, uint I
620		   , uint R, uint RC, uint RI, uint S, uint SG, uint SI, uint II, uint CC
621		   , vec3 vE, vec3 vF, vec3 vC
622		   ) {
623
624			//                                    B  .
625			//                                           Zone 4
626			//
627			//                                      S
628
629
630    if (any_eq2(F,C,I)) return vE;
631
632	// Skip destructive patterns on opposite side
633	if ( (eq(F,RI) || eq(G,S) || eq(R, RI)) && neq(R,I) ) return vE;
634
635    if (eq(H, S) && none_eq2(H,I,SG)) return vE;
636
637    if ( eq(R, RC) || eq(G,SG) ) return vE;
638
639	// Extend one extra pixel in trend direction if E is white in D==E==C pattern (Street Fighter II Guile's face)
640	if ( checkwhite(vE) && all_eq2(E,C,D) && none_eq2(E,RC,CC)) return vE;
641
642	// Old opposite trend check
643	// if ( none_eq2(I,H,S) && (neq(SI,RI) || eq(I,II)) ) return vE;
644
645
646	#define vX vF
647	float mixFactor = 0.381966 * mixGate(vX,vE);
648
649	if ( eq(E,C) && (eq(E,D)||eq(B,D)) ) return mixXE;
650
651	bool sim_E_C = sim(vE,vC);
652
653	if ( sim_E_C && eq(E,D) && eq(B,C) ) return mixXE;
654
655	if ( (sim_E_C || mixFactor>0.1) && all_eq2(B,C,D) ) return mixXE;
656
657    return vE;
658}
659
660////////////////////////////////////////////////////////////////////////////////////////////////////////// zz
661
662void main()
663{
664    ivec2 inputPosition = ivec2(gl_GlobalInvocationID.xy);
665    vec2 SourceSize = imageSize(Source);
666    vec2 vTexCoord = vec2(inputPosition) / SourceSize;
667
668#define srcf(c,d) imageLoad(Source, inputPosition + ivec2(c, d)).rgb
669#define src(c,d) pack_color(srcf(c,d))
670
671	vec3 vE = srcf(0, 0);
672	vec3 vB = srcf(0, -1);
673	vec3 vD = srcf(-1, 0);
674	vec3 vF = srcf(+1, 0);
675	vec3 vH = srcf(0, +1);
676
677    uint E = pack_color(vE);
678    uint B = pack_color(vB);
679    uint D = pack_color(vD);
680    uint F = pack_color(vF);
681    uint H = pack_color(vH);
682
683
684    bool eq_E_D = eq(E,D);
685    bool eq_E_F = eq(E,F);
686    bool eq_E_B = eq(E,B);
687    bool eq_E_H = eq(E,H);
688    bool eq_B_H = eq(B,H);
689    bool eq_D_F = eq(D,F);
690
691// Skip horizontal/vertical 3x1 lines
692bool skiprest = (eq_E_D && eq_E_F) || (eq_E_B && eq_E_H) || (eq_B_H && eq_D_F);
693if (!skiprest) {
694
695
696
697    //Sample 5x5 grid
698	vec3 vA = srcf(-1, -1);
699	vec3 vC = srcf(+1, -1);
700	vec3 vG = srcf(-1, +1);
701	vec3 vI = srcf(+1, +1);
702
703    uint A = pack_color(vA);
704    uint C = pack_color(vC);
705    uint G = pack_color(vG);
706    uint I = pack_color(vI);
707
708	uint P  = src( 0, -2);
709	uint Q  = src(-2,  0);
710	uint R  = src(+2,  0);
711	uint S  = src( 0, +2);
712
713	uint PA = src(-1, -2);
714	uint PC = src(+1, -2);
715	uint QA = src(-2, -1);
716	uint QG = src(-2, +1); //             AA    PA    [P]   PC    CC
717	uint RC = src(+2, -1); //
718	uint RI = src(+2, +1); //             QA   A   B  C   RC
719	uint SG = src(-1, +2); //
720	uint SI = src(+1, +2); //            [Q]   D   E  F   [R]
721	uint AA = src(-2, -2); //
722	uint CC = src(+2, -2); //             QG   G   H  I   RI
723	uint GG = src(-2, +2); //
724	uint II = src(+2, +2); //             GG    SG    [S]   SI    II
725
726    // Default: nearest-neighbor upscale
727    vec3 J = vE;    vec3 K = vE;    vec3 L = vE;    vec3 M = vE;
728
729    // ------------------------ Boundary check --- Libretro -----------------------
730    // Define dummy pixel color (impossible value)
731    #define fakeColor vec3(1.234)
732	// 1. Precompute integer pixel coordinates.
733    vec2 pixelPos = vTexCoord * SourceSize.xy;
734    ivec2 currPixel = ivec2(floor(pixelPos)); // Current pixel integer coords (0-based)
735    ivec2 texSize = ivec2(SourceSize.xy); // Actual texture pixel dimensions
736
737    // 3. Check edge overflow - use dummy color to influence luminance logic
738    if (currPixel.y - 1 < 0) vB = fakeColor;          // Top pixel overflow (y-1 < 0)
739    if (currPixel.x - 1 < 0) vD = fakeColor;          // Left pixel overflow (x-1 < 0)
740    if (currPixel.x + 1 >= texSize.x) vF = fakeColor; // Right pixel overflow (x+1 >= width)
741    if (currPixel.y + 1 >= texSize.y) vH = fakeColor; // Bottom pixel overflow (y+1 >= height)
742    // ----------------------------------------------------------------------
743
744// Precompute luminance
745    float Bl = luma(vB);
746    float Dl = luma(vD);
747    float El = luma(vE);
748    float Fl = luma(vF);
749    float Hl = luma(vH);
750
751
752// 	Pre-calcs
753    bool eq_B_D = eq(B,D);
754    bool eq_B_F = eq(B,F);
755    bool eq_D_H = eq(D,H);
756    bool eq_F_H = eq(F,H);
757
758    // Any opposite pair encloses center
759    bool oppoPix =  eq_B_H || eq_D_F;
760	// Flag if caught by 1:1 slope rule and entered admixX
761    bool slope1 = false;    bool slope2 = false;    bool slope3 = false;    bool slope4 = false;
762	// Standard pixel returned successfully from 1:1 slope rule
763    bool slope1ok = false;  bool slope2ok = false;  bool slope3ok = false;  bool slope4ok = false;
764    bool slope1end = false;  bool slope2end = false;  bool slope3end = false;  bool slope4end = false;
765	// slopeBAD: entered admixX but returned E (for at least one JKLM)
766    // slopOFF: returned with OFF flag - skip long slope calculations
767
768
769// B - D
770	if (
771		(!eq_E_B && !eq_E_D && !oppoPix) && (!eq_D_H && !eq_B_F)
772	 && (eq(E,A) || El>=Dl&&El>=Bl) && ( (El<Dl&&El<Bl) || none_eq2(A,B,D) || neq(E,P) || neq(E,Q) )
773	 && ( eq_B_D &&(eq(E,A)||eq(B,PC)||eq(D,QG)||sim(vE,vC)||sim(vE,vG)) || simb(vB,vD)&&(eq_F_H||eq(E,C)||eq(E,G)) )
774		) {
775		J=admixX(A,B,C,D,E,F,G,H,I
776				,P,PA,PC,Q,QA,QG,R,RC,RI,S,SG,SI,AA,CC,GG
777				,El, Bl, Dl, Fl, Hl
778				,vE, vB, vD, vC, vG
779				);
780		slope1 = true;			// Mark on entry
781		slope1ok = (J.b < 1.1);	// Valid pixel
782		slope1end = (J.b < 3.1);	// Modified, skip long slope (mostly simBD)
783		skiprest = (J.b > 7.1);	// theEXIT
784		J = (J.b > 3.1) ? vE :		// Restore vE for slopeBAD/theEXIT
785			(J.b > 1.1) ? (J - 2.0) :// slopeoff
786			J;					// Normal pixel [0-1.0]
787	}
788// B - F
789	if ( !slope1
790	 && (!eq_E_B && !eq_E_F && !oppoPix) && (!eq_B_D && !eq_F_H)
791	 && (eq(E,C) || El>=Bl&&El>=Fl) && ( (El<Bl&&El<Fl) || none_eq2(C,B,F) || neq(E,P) || neq(E,R) )
792	 && ( eq_B_F &&(eq(E,C)||eq(B,PA)||eq(F,RI)||sim(vE,vA)||sim(vE,vI)) || simb(vB,vF)&&(eq_D_H||eq(E,A)||eq(E,I)) )
793	 ) {
794		K=admixX(C,F,I,B,E,H,A,D,G
795				,R,RC,RI,P,PC,PA,S,SI,SG,Q,QA,QG,CC,II,AA
796				,El,Fl,Bl,Hl,Dl
797				,vE,vF,vB,vI,vA
798				);
799		slope2 = true;
800		slope2ok = (K.b < 1.1);
801		slope2end = (K.b < 3.1);
802		skiprest = (K.b > 7.1);
803		K = (K.b > 3.1) ? vE :
804			(K.b > 1.1) ? (K - 2.0) :
805			K;
806	}
807// D - H
808	if ( !slope1 && !skiprest
809	 && (!eq_E_D && !eq_E_H && !oppoPix) && (!eq_F_H && !eq_B_D)
810	 && (eq(E,G) || El>=Hl&&El>=Dl)  &&  ((El<Hl&&El<Dl) || none_eq2(G,D,H) || neq(E,S) || neq(E,Q))
811	 &&	( eq_D_H &&(eq(E,G)||eq(D,QA)||eq(H,SI)||sim(vE,vA)||sim(vE,vI)) || simb(vD,vH)&&(eq_B_F||eq(E,A)||eq(E,I)) )
812	 ) {
813		L=admixX(G,D,A,H,E,B,I,F,C
814				,Q,QG,QA,S,SG,SI,P,PA,PC,R,RI,RC,GG,AA,II
815				,El,Dl,Hl,Bl,Fl
816				,vE,vD,vH,vA,vI
817				);
818		slope3 = true;
819		slope3ok = (L.b < 1.1);
820		slope3end = (L.b < 3.1);
821		skiprest = (L.b > 7.1);
822		L = (L.b > 3.1) ? vE :
823			(L.b > 1.1) ? (L - 2.0) :
824			L;
825	}
826// F - H
827	if ( !slope2 && !slope3 && !skiprest
828	 && (!eq_E_F && !eq_E_H && !oppoPix) && (!eq_B_F && !eq_D_H)
829	 && (eq(E,I) || El>=Fl&&El>=Hl)  &&  ((El<Fl&&El<Hl) || none_eq2(I,F,H) || neq(E,R) || neq(E,S))
830	 && ( eq_F_H &&(eq(E,I)||eq(F,RC)||eq(H,SG)||sim(vE,vC)||sim(vE,vG)) || simb(vF,vH)&&(eq_B_D||eq(E,C)||eq(E,G)) )
831	  ) {
832		M=admixX(I,H,G,F,E,D,C,B,A
833				,S,SI,SG,R,RI,RC,Q,QG,QA,P,PC,PA,II,GG,CC
834				,El,Hl,Fl,Dl,Bl
835				,vE,vH,vF,vG,vC
836				);
837		slope4 = true;
838		slope4ok = (M.b < 1.1);
839		slope4end = (M.b < 3.1);
840		skiprest = (M.b > 7.1);
841		M = (M.b > 3.1) ? vE :
842			(M.b > 1.1) ? (M - 2.0) :
843			M;
844	}
845
846
847//  Long gentle 2:1 slope  (P100)
848
849	if (slope4ok) { //zone4 long slope
850		// Original ext 1: pass neighbor to admixL to prevent double-blending
851		// Original ext 2: prevent L-shape reoccurrence within opposite pixels unless walled
852		if (all_eq2(R,F,G) && neq(R, RC) && (neq(Q,G)||eq(Q, QA))) {L=admixL(M,L,vH); skiprest = true;}
853		// vertical
854		if (all_eq2(S,H,C) && neq(S, SG) && (neq(P,C)||eq(P, PA))) {K=admixL(M,K,vF); skiprest = true;}
855	}
856
857	if (slope3ok) { //zone3 long slope
858		// horizontal
859		if (all_eq2(Q,D,I) && neq(Q, QA) && (neq(R,I)||eq(R, RC))) {M=admixL(L,M,vH); skiprest = true;}
860		// vertical
861		if (all_eq2(S,H,A) && neq(S, SI) && (neq(A,P)||eq(P, PC))) {J=admixL(L,J,vD); skiprest = true;}
862	}
863
864	if (slope2ok) { //zone2 long slope
865		// horizontal
866		if (all_eq2(R,F,A) && neq(R, RI) && (neq(A,Q)||eq(Q, QG))) {J=admixL(K,J,vB); skiprest = true;}
867		// vertical
868		if (all_eq2(P,B,I) && neq(P, PA) && (neq(I,S)||eq(S, SG))) {M=admixL(K,M,vF); skiprest = true;}
869	}
870
871	if (slope1ok) { //zone1 long slope
872		// horizontal
873		if (all_eq2(Q,D,C) && neq(Q, QG) && (neq(C,R)||eq(R, RI))) {K=admixL(J,K,vB); skiprest = true;}
874		// vertical
875		if (all_eq2(P,B,G) && neq(P, PC) && (neq(G,S)||eq(S, SI))) {L=admixL(J,L,vD); skiprest = true;}
876	}
877
878// Longslope complete - exit early; diagonal sawslope unlikely
879// Note: sawslope entry cannot exclude diagonal slopes (including slopeok)
880if (!skiprest && !oppoPix) {
881
882
883        // horizontal bottom
884    if (!eq_E_H && none_eq2(H,A,C)) {
885
886        //                                    A B
887        //                                  Q D          Zone 4
888        //					                 I
889        //
890        // (!slope3 && D!=H) required to fully exclude trend
891        if ( (!slope2 && !eq_B_F) && (!slope3 && !eq_D_H) && (!slope4end && !eq_F_H) &&
892            !eq_E_F && eq(R,H) && eq(F,G) ) {
893            M = admixS( A, B, C, D, E, F, G, H, I
894                      , R, RC, RI, S, SG, SI, II, CC
895                      , vE, vF, vC
896                      );
897            skiprest = true;}
898
899        //                                    A  C
900        //                                      R       Zone 3
901        //                                     G
902        //
903        if ( !skiprest && (!slope1 && !eq_B_D) && (!slope4 && !eq_F_H) && (!slope3end && !eq_D_H) &&
904             !eq_E_D && eq(Q,H) && eq(D,I) ) {
905            L = admixS( C, B, A, F, E, D, I, H, G
906                      , Q, QA, QG, S, SI, SG, GG, AA
907                      , vE, vD, vA
908                      );
909            skiprest = true;}
910    }
911
912    // horizontal up
913    if ( !skiprest && !eq_E_B && none_eq2(B,G,I)) {
914
915        //
916        //
917        //                                            Zone 2
918        //                                     H  I  .
919        if ( (!slope1 && !eq_B_D)  && (!slope4 && !eq_F_H) && (!slope2end && !eq_B_F) &&
920              !eq_E_F && eq(B,R) && eq(A,F) ) {
921            K = admixS( G, H, I, D, E, F, A, B, C
922                      , R, RI, RC, P, PA, PC, CC, II
923                      , vE, vF, vI
924                      );
925            skiprest = true;}
926
927        //
928        //                                    A
929        //                                     R        Zone 1
930        //                                  . G  I
931        if ( !skiprest && (!slope2 && !eq_B_F) && (!slope3 && !eq_D_H) && (!slope1end && !eq_B_D) &&
932             !eq_E_D && eq(B,Q) && eq(C,D) ) {
933            J = admixS( I, H, G, F, E, D, C, B, A
934                      , Q, QG, QA, P, PC, PA, AA, GG
935                      , vE, vD, vG
936                      );
937            skiprest = true;}
938
939    }
940
941    // vertical left
942    if ( !skiprest && !eq_E_D && none_eq2(D,C,I) ) {
943
944        //                                     B
945        //                                  Q    R
946        //                                      I        Zone 3
947        //
948        if ( (!slope1 && !eq_B_D) && (!slope4 && !eq_F_H) && (!slope3end && !eq_D_H) &&
949              !eq_E_H && eq(D,S) && eq(A,H) ) {
950            L = admixS( C, F, I, B, E, H, A, D, G
951                      , S, SI, SG, Q, QA, QG, GG, II
952                      , vE, vH, vI
953                      );
954            skiprest = true;}
955
956        //
957        //                                    A  C
958        //                                  Q   F R       Zone 1
959        //
960        if ( !skiprest && (!slope3 && !eq_D_H) && (!slope2 && !eq_B_F) && (!slope1end && !eq_B_D) &&
961              !eq_E_B && eq(P,D) && eq(B,G) ) {
962            J = admixS( I, F, C, H, E, B, G, D, A
963                      , P, PC, PA, Q, QG, QA, AA, CC
964                      , vE, vB, vC
965                      );
966            skiprest = true;}
967
968    }
969
970    // vertical right
971    if ( !skiprest && !eq_E_F && none_eq2(F,A,G) ) { // right
972
973        //                                    A B
974        //                                  Q D   R
975        //                                    G  I        Zone 4
976        //                                    .
977        if ( (!slope2 && !eq_B_F) && (!slope3 && !eq_D_H) && (!slope4end && !eq_F_H) &&
978              !eq_E_H && eq(S,F) && eq(H,C) ) {
979            M = admixS( A, D, G, B, E, H, C, F
980                      , I, S, SG, SI, R, RC, RI, II, GG
981                      , vE, vH, vG
982                      );
983            skiprest = true;}
984
985        //
986        //                                    A  C
987        //                                  Q D   R        Zone 2
988        //                                    G H
989        if ( !skiprest && (!slope1 && !eq_B_D) && (!slope4 && !eq_F_H) && (!slope2end && !eq_B_F) &&
990             !eq_E_B && eq(P,F) && eq(B,I) ) {
991            K = admixS( G, D, A, H, E, B, I, F, C
992                      , P, PA, PC, R, RI, RC, CC, AA
993                      , vE, vB, vA
994                      );
995            skiprest = true;}
996
997    } // vertical right
998} // sawslope
999
1000// Exit after sawslope; old logic: skiprest||slopeBAD (still uses slopeOFF (weak) and slopeok (strong) with minor effect)
1001skiprest = skiprest||slope1||slope2||slope3||slope4;
1002
1003/**************************************************
1004       "Concave + Cross" blending (P100)
1005 *************************************************/
1006// Use approximate pixels for cross distant edges - improves horizontal lines + aliasing and layered gradients
1007// Example: glowing text in SFIIIn2 intro, Japanese houses in SFZ3mix, Wolf Fang intro
1008
1009vec3 vT;		// Temp T
1010
1011if (!skiprest &&
1012    Bl<El && !eq_E_D && !eq_E_F && eq_E_H && none_eq2(E,A,C) && all_eq2(G,H,I) && vi_sim(vE,E,S) ) { // TOP
1013
1014    if (eq_B_D||eq_B_F) { J=admixC(vB,J);    K=J;
1015        if (eq_D_F) { L=mix(J,L, 0.61804);   M=L; }
1016    } else { vT = El-Bl < abs(El-Dl) ? vB : vD;  J=admixC(vT,J);
1017            if (eq_D_F) { K=J;  L=mix(J,L, 0.61804);    M=L; }
1018            else {vT = El-Bl < abs(El-Fl) ? vB : vF; 		K=admixC(vT,K); }
1019           }
1020
1021   skiprest = true;
1022}
1023
1024if (!skiprest &&
1025    Hl<El && !eq_E_D && !eq_E_F && eq_E_B && none_eq2(E,G,I) && all_eq2(A,B,C) && vi_sim(vE,E,P) ) { // BOTTOM
1026
1027    if (eq_D_H||eq_F_H) { L=admixC(vH,L);    M=L;
1028        if (eq_D_F) { J=mix(L,J, 0.61804);   K=J; }
1029    } else { vT = El-Hl < abs(El-Dl) ? vH : vD;  L=admixC(vT,L);
1030            if (eq_D_F) { M=L;  J=mix(L,J, 0.61804);    K=J; }
1031            else { vT = El-Hl < abs(El-Fl) ? vH : vF;    M=admixC(vT,M); }
1032           }
1033
1034   skiprest = true;
1035}
1036
1037if (!skiprest &&
1038    Fl<El && !eq_E_B && !eq_E_H && eq_E_D && none_eq2(E,C,I) && all_eq2(A,D,G) && vi_sim(vE,E,Q) ) { // RIGHT
1039
1040    if (eq_B_F||eq_F_H) { K=admixC(vF,K);    M=K;
1041        if (eq_B_H) { J=mix(K,J, 0.61804);   L=J; }
1042    } else { vT = El-Fl < abs(El-Bl) ? vF : vB;  K=admixC(vT,K);
1043            if (eq_B_H) { M=K;  J=mix(K,J, 0.61804);    L=J; }
1044            else { vT = El-Fl < abs(El-Hl) ? vF : vH;    M=admixC(vT,M); }
1045           }
1046
1047   skiprest = true;
1048}
1049
1050if (!skiprest &&
1051    Dl<El && !eq_E_B && !eq_E_H && eq_E_F && none_eq2(E,A,G) && all_eq2(C,F,I) && vi_sim(vE,E,R) ) { // LEFT
1052
1053    if (eq_B_D||eq_D_H) { J=admixC(vD,J);    L=J;
1054        if (eq_B_H) { K=mix(J,K, 0.61804);   M=K; }
1055    } else { vT = El-Dl < abs(El-Bl) ? vD : vB;  J=admixC(vT,J);
1056            if (eq_B_H) { L=J;   K=mix(J,K, 0.61804);    M=K; }
1057            else { vT = El-Dl < abs(El-Hl) ? vD : vH;    L=admixC(vT,L); }
1058           }
1059
1060   skiprest = true;
1061}
1062
1063/*
1064
1065
1066         Scorpion pattern (P99). Resembles Matrix sentinels. Smooths regular staggered pixels.
1067*/
1068// Practical rules:
1069// 1. Do NOT use approximate pixels (causes artifacts)
1070// 2. Shorten scorpion tail by 1 pixel for bright centers to catch more patterns
1071// Scorpion is exclusive - won't trigger if any earlier rule matched (entered)
1072
1073if (!skiprest && !eq_E_F&&eq_E_D&&eq_B_F&&eq_F_H && all_eq2(E,C,I) && (eq(E,Q)||El>Fl) && neq(F,src(+3, 0)) ) {K=admixK(vF,K); M=K;skiprest=true;}	// RIGHT
1074if (!skiprest && !eq_E_D&&eq_E_F&&eq_B_D&&eq_D_H && all_eq2(E,A,G) && (eq(E,R)||El>Dl) && neq(D,src(-3, 0)) ) {J=admixK(vD,J); L=J;skiprest=true;}	// LEFT
1075if (!skiprest && !eq_E_H&&eq_E_B&&eq_D_H&&eq_F_H && all_eq2(E,G,I) && (eq(E,P)||El>Hl) && neq(H,src(0, +3)) ) {L=admixK(vH,L); M=L;skiprest=true;}	// BOTTOM
1076if (!skiprest && !eq_E_B&&eq_E_H&&eq_B_D&&eq_B_F && all_eq2(E,A,C) && (eq(E,S)||El>Bl) && neq(B,src(0, -3)) ) {J=admixK(vB,J); K=J;}				// TOP
1077
1078    ivec2 outputPosition = 2 * ivec2(gl_GlobalInvocationID.xy);
1079    imageStore(Output, outputPosition + ivec2(0, 0), vec4(J, 1));
1080    imageStore(Output, outputPosition + ivec2(1, 0), vec4(K, 1));
1081    imageStore(Output, outputPosition + ivec2(0, 1), vec4(L, 1));
1082    imageStore(Output, outputPosition + ivec2(1, 1), vec4(M, 1));
1083
1084	//float fx = step(0.5, a.x);
1085	//float fy = step(0.5, a.y);
1086
1087	//FragColor.rgb = mix(mix(J, L, fy), mix(K, M, fy), fx);
1088
1089} else {
1090    ivec2 outputPosition = 2 * ivec2(gl_GlobalInvocationID.xy);
1091    imageStore(Output, outputPosition + ivec2(0, 0), vec4(vE, 1));
1092    imageStore(Output, outputPosition + ivec2(1, 0), vec4(vE, 1));
1093    imageStore(Output, outputPosition + ivec2(0, 1), vec4(vE, 1));
1094    imageStore(Output, outputPosition + ivec2(1, 1), vec4(vE, 1));
1095}
1096}