- [Canvas] Fix flood fill, magic selector combine black and fully transparent pixels.

This commit is contained in:
Tanasart 2024-05-29 11:30:01 +07:00
parent 1274fc9405
commit 196daeb849
8 changed files with 177 additions and 109 deletions

View file

@ -77,7 +77,7 @@ event_inherited();
}
if(preset_selecting == i)
var _palRes = drawPaletteGrid(pal.palette, ui(16), yy + ui(28), ww, _gs, selector.current_color);
var _palRes = drawPaletteGrid(pal.palette, ui(16), yy + ui(28), ww, _gs, { color : selector.current_color, mx : _m[0], my : _m[1] });
else
drawPalette(pal.palette, ui(16), yy + ui(28), ww, ui(20));

View file

@ -190,7 +190,7 @@ event_inherited();
}
if(palette_selecting == i)
var _palRes = drawPaletteGrid(pal.palette, ui(16), yy + ui(28), ww, _gs);
var _palRes = drawPaletteGrid(pal.palette, ui(16), yy + ui(28), ww, _gs, { mx : _m[0], my : _m[1] });
else
drawPalette(pal.palette, ui(16), yy + ui(28), ww, ui(20));

View file

@ -2,13 +2,15 @@ function _ff_getPixel(_x, _y) { return buffer_read_at(_ff_buff, (_y * _ff_w + _x
function canvas_ff_fillable(colorBase, colorFill, _x, _y, _thres) { #region
var c = _ff_getPixel(_x, _y);
var d = color_diff(colorBase, c, true, true);
var d = color_diff_alpha(colorBase, c);
//print($"Checking [{_x}, {_y}]: {colorBase} - {c} : {_color_get_alpha(colorBase)} - {_color_get_alpha(c)} | {d}");
return d <= _thres && c != colorFill;
} #endregion
function canvas_flood_fill_scanline(_surf, _x, _y, _thres, _corner = false) { #region
var colorFill = CURRENT_COLOR;
var colorBase = surface_getpixel_ext(_surf, _x, _y);
var colorBase = int64(surface_getpixel_ext(_surf, _x, _y));
if(colorFill == colorBase) return; //Clicking on the same color as the fill color
@ -24,13 +26,15 @@ function canvas_flood_fill_scanline(_surf, _x, _y, _thres, _corner = false) { #r
var spanAbove, spanBelow;
var thr = _thres * _thres;
var queue = ds_queue_create();
ds_queue_enqueue(queue, [_x, _y]);
var qx = ds_queue_create();
var qy = ds_queue_create();
ds_queue_enqueue(qx, _x);
ds_queue_enqueue(qy, _y);
while(!ds_queue_empty(queue)) {
var pos = ds_queue_dequeue(queue);
x1 = pos[0];
y1 = pos[1];
while(!ds_queue_empty(qx)) {
x1 = ds_queue_dequeue(qx);
y1 = ds_queue_dequeue(qy);
if(_ff_getPixel(x1, y1) == colorFill) continue; //Color in queue is already filled
@ -47,32 +51,47 @@ function canvas_flood_fill_scanline(_surf, _x, _y, _thres, _corner = false) { #r
buffer_write(_ff_buff, buffer_u32, _c);
if(y1 > 0) {
if(_corner && x1 > 0 && canvas_ff_fillable(colorBase, colorFill, x1 - 1, y1 - 1, thr)) //Check top left pixel
ds_queue_enqueue(queue, [x1 - 1, y1 - 1]);
if(_corner && x1 > 0 && canvas_ff_fillable(colorBase, colorFill, x1 - 1, y1 - 1, thr)) { //Check top left pixel
ds_queue_enqueue(qx, x1 - 1);
ds_queue_enqueue(qy, y1 - 1);
}
if(canvas_ff_fillable(colorBase, colorFill, x1, y1 - 1, thr)) //Check top pixel
ds_queue_enqueue(queue, [x1, y1 - 1]);
if(canvas_ff_fillable(colorBase, colorFill, x1, y1 - 1, thr)) { //Check top pixel
ds_queue_enqueue(qx, x1);
ds_queue_enqueue(qy, y1 - 1);
}
}
if(y1 < surface_h - 1) {
if(_corner && x1 > 0 && canvas_ff_fillable(colorBase, colorFill, x1 - 1, y1 + 1, thr)) //Check bottom left pixel
ds_queue_enqueue(queue, [x1 - 1, y1 + 1]);
if(_corner && x1 > 0 && canvas_ff_fillable(colorBase, colorFill, x1 - 1, y1 + 1, thr)) { //Check bottom left pixel
ds_queue_enqueue(qx, x1 - 1);
ds_queue_enqueue(qy, y1 + 1);
}
if(canvas_ff_fillable(colorBase, colorFill, x1, y1 + 1, thr)) //Check bottom pixel
ds_queue_enqueue(queue, [x1, y1 + 1]);
if(canvas_ff_fillable(colorBase, colorFill, x1, y1 + 1, thr)) { //Check bottom pixel
ds_queue_enqueue(qx, x1);
ds_queue_enqueue(qy, y1 + 1);
}
}
if(_corner && x1 < surface_w - 1) {
if(y1 > 0 && canvas_ff_fillable(colorBase, colorFill, x1 + 1, y1 - 1, thr)) //Check top right pixel
ds_queue_enqueue(queue, [x1 + 1, y1 - 1]);
if(y1 > 0 && canvas_ff_fillable(colorBase, colorFill, x1 + 1, y1 - 1, thr)) { //Check top right pixel
ds_queue_enqueue(qx, x1 + 1);
ds_queue_enqueue(qy, y1 - 1);
}
if(y1 < surface_h - 1 && canvas_ff_fillable(colorBase, colorFill, x1 + 1, y1 + 1, thr)) //Check bottom right pixel
ds_queue_enqueue(queue, [x1 + 1, y1 + 1]);
if(y1 < surface_h - 1 && canvas_ff_fillable(colorBase, colorFill, x1 + 1, y1 + 1, thr)) { //Check bottom right pixel
ds_queue_enqueue(qx, x1 + 1);
ds_queue_enqueue(qy, y1 + 1);
}
}
x1++;
}
}
ds_queue_destroy(qx);
ds_queue_destroy(qy);
draw_set_alpha(1);
buffer_delete(_ff_buff);
@ -100,7 +119,7 @@ function canvas_flood_fill_all(_surf, _x, _y, _thres) { #region
for (var j = 0; j < _ff_w; j++) {
var c = buffer_read(_ff_buff, buffer_u32);
var d = color_diff(colorBase, c, true, true);
var d = color_diff_alpha(colorBase, c);
if(d > _thres) continue;
draw_point(j, i);

View file

@ -1,13 +1,13 @@
function canvas_ms_fillable(colorBase, colorFill, _x, _y, _thres) { #region
var c = _ff_getPixel(_x, _y);
var d = color_diff(colorBase, c, true, true);
var d = color_diff_alpha(colorBase, c);
return d <= _thres;
} #endregion
function canvas_magic_selection_scanline(_surf, _x, _y, _thres, _corner = false) { #region
var colorBase = surface_getpixel_ext(_surf, _x, _y);
var colorBase = int64(surface_getpixel_ext(_surf, _x, _y));
var colorFill = colorBase;
var x1, y1, x_start;
@ -19,8 +19,10 @@ function canvas_magic_selection_scanline(_surf, _x, _y, _thres, _corner = false)
_ff_buff = buffer_create(_ff_w * _ff_h * 4, buffer_fixed, 4);
buffer_get_surface(_ff_buff, _surf, 0);
var queue = ds_queue_create();
ds_queue_enqueue(queue, [_x, _y]);
var qx = ds_queue_create();
var qy = ds_queue_create();
ds_queue_enqueue(qx, _x);
ds_queue_enqueue(qy, _y);
var sel_x0 = surface_w;
var sel_y0 = surface_h;
@ -30,10 +32,10 @@ function canvas_magic_selection_scanline(_surf, _x, _y, _thres, _corner = false)
var _arr = array_create(surface_w * surface_h, 0);
draw_set_color(c_white);
while(!ds_queue_empty(queue)) {
var pos = ds_queue_dequeue(queue);
x1 = pos[0];
y1 = pos[1];
while(!ds_queue_empty(qx)) {
x1 = ds_queue_dequeue(qx);
y1 = ds_queue_dequeue(qy);
if(_arr[y1 * surface_w + x1] == 1) continue; //Color in queue is already filled
@ -60,34 +62,48 @@ function canvas_magic_selection_scanline(_surf, _x, _y, _thres, _corner = false)
//print($"> Filling {x1}, {y1}: {canvas_get_color_buffer(x1, y1)}");
if(y1 > 0) {
if(_corner && x1 > 0 && canvas_ms_fillable(colorBase, colorFill, x1 - 1, y1 - 1, thr)) //Check top left pixel
ds_queue_enqueue(queue, [x1 - 1, y1 - 1]);
if(_corner && x1 > 0 && canvas_ms_fillable(colorBase, colorFill, x1 - 1, y1 - 1, thr)) { //Check top left pixel
ds_queue_enqueue(qx, x1 - 1);
ds_queue_enqueue(qy, y1 - 1);
}
if(canvas_ms_fillable(colorBase, colorFill, x1, y1 - 1, thr)) //Check top pixel
ds_queue_enqueue(queue, [x1, y1 - 1]);
if(canvas_ms_fillable(colorBase, colorFill, x1, y1 - 1, thr)) { //Check top pixel
ds_queue_enqueue(qx, x1);
ds_queue_enqueue(qy, y1 - 1);
}
}
if(y1 < surface_h - 1) {
if(_corner && x1 > 0 && canvas_ms_fillable(colorBase, colorFill, x1 - 1, y1 + 1, thr)) //Check bottom left pixel
ds_queue_enqueue(queue, [x1 - 1, y1 + 1]);
if(_corner && x1 > 0 && canvas_ms_fillable(colorBase, colorFill, x1 - 1, y1 + 1, thr)) { //Check bottom left pixel
ds_queue_enqueue(qx, x1 - 1);
ds_queue_enqueue(qy, y1 + 1);
}
if(canvas_ms_fillable(colorBase, colorFill, x1, y1 + 1, thr)) //Check bottom pixel
ds_queue_enqueue(queue, [x1, y1 + 1]);
if(canvas_ms_fillable(colorBase, colorFill, x1, y1 + 1, thr)) { //Check bottom pixel
ds_queue_enqueue(qx, x1);
ds_queue_enqueue(qy, y1 + 1);
}
}
if(_corner && x1 < surface_w - 1) {
if(y1 > 0 && canvas_ms_fillable(colorBase, colorFill, x1 + 1, y1 - 1, thr)) //Check top right pixel
ds_queue_enqueue(queue, [x1 + 1, y1 - 1]);
if(y1 > 0 && canvas_ms_fillable(colorBase, colorFill, x1 + 1, y1 - 1, thr)) { //Check top right pixel
ds_queue_enqueue(qx, x1 + 1);
ds_queue_enqueue(qy, y1 - 1);
}
if(y1 < surface_h - 1 && canvas_ms_fillable(colorBase, colorFill, x1 + 1, y1 + 1, thr)) //Check bottom right pixel
ds_queue_enqueue(queue, [x1 + 1, y1 + 1]);
if(y1 < surface_h - 1 && canvas_ms_fillable(colorBase, colorFill, x1 + 1, y1 + 1, thr)) { //Check bottom right pixel
ds_queue_enqueue(qx, x1 + 1);
ds_queue_enqueue(qy, y1 + 1);
}
}
x1++;
}
}
ds_queue_destroy(queue);
ds_queue_destroy(qx);
ds_queue_destroy(qy);
buffer_delete(_ff_buff);
return [ sel_x0, sel_y0, sel_x1, sel_y1 ];
@ -115,7 +131,7 @@ function canvas_magic_selection_all(_surf, _x, _y, _thres) { #region
for (var j = 0; j < _ff_w; j++) {
var c = buffer_read(_ff_buff, buffer_u32);
var d = color_diff(colorBase, c, true, true);
var d = color_diff_alpha(colorBase, c);
if(d > _thres) continue;
draw_point(j, i);

View file

@ -21,9 +21,9 @@ function canvas_tool_fill(toolAttr) : canvas_tool() constructor {
surface_set_target(_canvas_surface);
switch(_fill_type) {
case 0 : canvas_flood_fill_scanline(_canvas_surface, mouse_cur_x, mouse_cur_y, _thr, false); break;
case 1 : canvas_flood_fill_scanline(_canvas_surface, mouse_cur_x, mouse_cur_y, _thr, true); break;
case 2 : canvas_flood_fill_all( _canvas_surface, mouse_cur_x, mouse_cur_y, _thr); break;
case 0 :
case 1 : canvas_flood_fill_scanline(_canvas_surface, mouse_cur_x, mouse_cur_y, _thr, _fill_type); break;
case 2 : canvas_flood_fill_all( _canvas_surface, mouse_cur_x, mouse_cur_y, _thr); break;
}
surface_reset_target();

View file

@ -153,19 +153,32 @@
} #endregion
#endregion
function color_diff(c1, c2, fast = false, alpha = false) { #region
var _c1_r = _color_get_red(c1);
var _c1_g = _color_get_green(c1);
var _c1_b = _color_get_blue(c1);
var _c1_a = _color_get_alpha(c1);
function color_diff_fast(c1, c2) { #region
INLINE
var _c2_r = _color_get_red(c2);
var _c2_g = _color_get_green(c2);
var _c2_b = _color_get_blue(c2);
var _c2_a = _color_get_alpha(c2);
return (abs(_color_get_red(c1) - _color_get_red(c2)) +
abs(_color_get_green(c1) - _color_get_green(c2)) +
abs(_color_get_blue(c1) - _color_get_blue(c2))
) / 3;
} #endregion
function color_diff_alpha(c1, c2) { #region
INLINE
if(fast) return (abs(_c1_r - _c2_r) + abs(_c1_g - _c2_g) + abs(_c1_b - _c2_b)) / 3;
return sqrt(sqr(_c1_r - _c2_r) + sqr(_c1_g - _c2_g) + sqr(_c1_b - _c2_b));
return sqrt(sqr(_color_get_red(c1) - _color_get_red(c2)) +
sqr(_color_get_green(c1) - _color_get_green(c2)) +
sqr(_color_get_blue(c1) - _color_get_blue(c2)) +
sqr(_color_get_alpha(c1) - _color_get_alpha(c2))
);
} #endregion
function color_diff(c1, c2) { #region
INLINE
return sqrt(sqr(_color_get_red(c1) - _color_get_red(c2)) +
sqr(_color_get_green(c1) - _color_get_green(c2)) +
sqr(_color_get_blue(c1) - _color_get_blue(c2))
);
} #endregion
#region merge

View file

@ -21,11 +21,14 @@ function Node_De_Corner(_x, _y, _group = noone) : Node_Processor(_x, _y, _group)
__init_mask_modifier(5); // inputs 7, 8,
inputs[| 9] = nodeValue("Include", self, JUNCTION_CONNECT.input, VALUE_TYPE.integer, 0b11)
.setDisplay(VALUE_DISPLAY.toggle, { data: [ "Inner", "Side" ] });
outputs[| 0] = nodeValue("Surface out", self, JUNCTION_CONNECT.output, VALUE_TYPE.surface, noone);
input_display_list = [ 1,
["Surfaces", true], 0, 5, 6, 7, 8,
["Effect", false], 4, 2, 3,
["Effect", false], 4, 9, 2, 3,
]
attribute_surface_depth();
@ -41,6 +44,7 @@ function Node_De_Corner(_x, _y, _group = noone) : Node_Processor(_x, _y, _group)
var _tol = _data[2];
var _itr = _data[3];
var _str = _data[4];
var _inn = _data[9];
var _sw = surface_get_width_safe(surf);
var _sh = surface_get_height_safe(surf);
@ -57,6 +61,8 @@ function Node_De_Corner(_x, _y, _group = noone) : Node_Processor(_x, _y, _group)
shader_set_f("dimension", _sw, _sh);
shader_set_f("tolerance", _tol);
shader_set_i("strict", _str);
shader_set_i("inner", bool(_inn & 0b01));
shader_set_i("side", bool(_inn & 0b10));
draw_surface_safe(temp_surface[!_bg]);
surface_reset_shader();

View file

@ -1,22 +1,33 @@
//
// Simple passthrough fragment shader
//
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
uniform vec2 dimension;
uniform float tolerance;
uniform int strict;
uniform int inner;
uniform int side;
float d(in vec4 c1, in vec4 c2) { return length(c1.rgb * c1.a - c2.rgb * c2.a) / sqrt(3.); }
bool s(in vec4 c1, in vec4 c2) { return d(c1, c2) <= tolerance; }
vec4 a4;
bool s(in vec4 c2) { return d(a4, c2) <= tolerance; }
bool s(in bool b, in vec4 c2) { return b || d(a4, c2) <= tolerance; }
bool s(in vec4 c1, in vec4 c2) { return d(c1, c2) <= tolerance; }
bool s(in bool b, in vec4 c1, in vec4 c2) { return b || d(c1, c2) <= tolerance; }
bool ns(in vec4 c2) { return d(a4, c2) > tolerance; }
bool ns(in bool b, in vec4 c2) { return b || d(a4, c2) > tolerance; }
bool ns(in vec4 c1, in vec4 c2) { return d(c1, c2) > tolerance; }
bool ns(in bool b, in vec4 c1, in vec4 c2) { return b || d(c1, c2) > tolerance; }
float bright(in vec4 c) { return dot(c.rgb, vec3(0.2126, 0.7152, 0.0722)) * c.a; }
#region select closet color
vec4 sel2(in vec4 c, in vec4 c0, in vec4 c1) {
float d0 = d(c, c0);
float d1 = d(c, c1);
vec4 sel2(in vec4 c0, in vec4 c1) {
float d0 = d(a4, c0);
float d1 = d(a4, c1);
float mn = min(d0, d1);
@ -24,10 +35,10 @@ float bright(in vec4 c) { return dot(c.rgb, vec3(0.2126, 0.7152, 0.0722)) * c.a;
return c1;
}
vec4 sel3(in vec4 c, in vec4 c0, in vec4 c1, in vec4 c2) {
float d0 = d(c, c0);
float d1 = d(c, c1);
float d2 = d(c, c2);
vec4 sel3(in vec4 c0, in vec4 c1, in vec4 c2) {
float d0 = d(a4, c0);
float d1 = d(a4, c1);
float d2 = d(a4, c2);
float mn = min(min(d0, d1), d2);
@ -36,11 +47,11 @@ float bright(in vec4 c) { return dot(c.rgb, vec3(0.2126, 0.7152, 0.0722)) * c.a;
return c2;
}
vec4 sel4(in vec4 c, in vec4 c0, in vec4 c1, in vec4 c2, in vec4 c3) {
float d0 = d(c, c0);
float d1 = d(c, c1);
float d2 = d(c, c2);
float d3 = d(c, c3);
vec4 sel4(in vec4 c0, in vec4 c1, in vec4 c2, in vec4 c3) {
float d0 = d(a4, c0);
float d1 = d(a4, c1);
float d2 = d(a4, c2);
float d3 = d(a4, c3);
float mn = min(min(d0, d1), min(d2, d3));
@ -63,7 +74,7 @@ void main() {
// 3 4 5
// 6 7 8
vec4 a4 = texture2D( gm_BaseTexture, v_vTexcoord );
a4 = texture2D( gm_BaseTexture, v_vTexcoord );
vec2 tx = 1. / dimension;
gl_FragColor = a4;
@ -80,62 +91,65 @@ void main() {
vec4 a7 = sample( v_vTexcoord + vec2( 0., tx.y) );
vec4 a8 = sample( v_vTexcoord + vec2( tx.x, tx.y) );
bool n = inner == 0;
bool d = side == 0;
if(strict == 0) {
if(s(a4, a0) && s(a4, a1) && s(a4, a3) && !s(a4, a2) && !s(a4, a5) && !s(a4, a6) && !s(a4, a7) && !s(a4, a8)) { // A A 2
// A A 5
// 6 7 8
gl_FragColor = sel3(a4, sel2(a4, a2, a6), sel2(a4, a5, a7), a8);
if(s(n, a0) && s(a1) && s(a3) && ns(d, a2) && ns(d, a5) && ns(d, a6) && ns(d, a7) && ns(a8)) { // A A 2
// A A 5
// 6 7 8
gl_FragColor = n? sel3(a5, a7, a8) : sel3(sel2(a2, a6), sel2(a5, a7), a8);
return;
}
if(s(a4, a1) && s(a4, a2) && s(a4, a5) && !s(a4, a0) && !s(a4, a3) && !s(a4, a6) && !s(a4, a7) && !s(a4, a8)) { // 0 A A
// 3 A A
// 6 7 8
gl_FragColor = sel3(a4, sel2(a4, a0, a8), sel2(a4, a3, a7), a6);
if(s(a1) && s(n, a2) && s(a5) && ns(d, a0) && ns(d, a3) && ns(a6) && ns(d, a7) && ns(d, a8)) { // 0 A A
// 3 A A
// 6 7 8
gl_FragColor = n? sel3(a3, a6, a7) : sel3(sel2(a0, a8), sel2(a3, a7), a6);
return;
}
if(s(a4, a3) && s(a4, a6) && s(a4, a7) && !s(a4, a0) && !s(a4, a1) && !s(a4, a2) && !s(a4, a5) && !s(a4, a8)) { // 0 1 2
// A A 5
// A A 8
gl_FragColor = sel3(a4, sel2(a4, a0, a8), sel2(a4, a1, a5), a2);
if(s(a3) && s(n, a6) && s(a7) && ns(d, a0) && ns(d, a1) && ns(a2) && ns(d, a5) && ns(d, a8)) { // 0 1 2
// A A 5
// A A 8
gl_FragColor = n? sel3(a1, a2, a5) : sel3(sel2(a0, a8), sel2(a1, a5), a2);
return;
}
if(s(a4, a5) && s(a4, a7) && s(a4, a8) && !s(a4, a0) && !s(a4, a1) && !s(a4, a2) && !s(a4, a3) && !s(a4, a6)) { // 0 1 2
// 3 A A
// 6 A A
gl_FragColor = sel3(a4, sel2(a4, a2, a6), sel2(a4, a1, a3), a0);
if(s(a5) && s(a7) && s(n, a8) && ns(a0) && ns(d, a1) && ns(d, a2) && ns(d, a3) && ns(d, a6)) { // 0 1 2
// 3 A A
// 6 A A
gl_FragColor = n? sel3(a0, a1, a3) : sel3(sel2(a2, a6), sel2(a1, a3), a0);
return;
}
} else if(strict == 1) {
if(s(a5, a7) && s(a4, a1) && s(a4, a3) && s(a4, a0) && !s(a4, a2) && !s(a4, a5) && !s(a4, a6) && !s(a4, a7)) { // B B C
// B B A
// C A 8
gl_FragColor = sel3(a4, sel2(a4, a2, a6), sel2(a4, a5, a7), a8);
if(s(a5, a7) && s(a1) && s(a3) && s(n, a0) && ns(d, a2) && ns(d, a5) && ns(d, a6) && ns(d, a7)) { // B B C
// B B A
// C A 8
gl_FragColor = n? sel3(a5, a7, a8) : sel3(sel2(a2, a6), sel2(a5, a7), a8);
return;
}
if(s(a3, a7) && s(a4, a1) && s(a4, a2) && s(a4, a5) && !s(a4, a0) && !s(a4, a3) && !s(a4, a7) && !s(a4, a8)) { // C B B
// A B B
// 6 A C
gl_FragColor = sel3(a4, sel2(a4, a0, a8), sel2(a4, a3, a7), a6);
if(s(a3, a7) && s(a1) && s(n, a2) && s(a5) && ns(d, a0) && ns(d, a3) && ns(d, a7) && ns(d, a8)) { // C B B
// A B B
// 6 A C
gl_FragColor = n? sel3(a3, a6, a7) : sel3(sel2(a0, a8), sel2(a3, a7), a6);
return;
}
if(s(a5, a1) && s(a4, a3) && s(a4, a6) && s(a4, a7) && !s(a4, a0) && !s(a4, a1) && !s(a4, a5) && !s(a4, a8)) { // C A 2
// B B A
// B B C
gl_FragColor = sel3(a4, sel2(a4, a0, a8), sel2(a4, a1, a5), a2);
if(s(a5, a1) && s(a3) && s(n, a6) && s(a7) && ns(d, a0) && ns(d, a1) && ns(d, a5) && ns(d, a8)) { // C A 2
// B B A
// B B C
gl_FragColor = n? sel3(a1, a2, a5) : sel3(sel2(a0, a8), sel2(a1, a5), a2);
return;
}
if(s(a3, a1) && s(a4, a5) && s(a4, a8) && s(a4, a7) && !s(a4, a2) && !s(a4, a1) && !s(a4, a3) && !s(a4, a6)) { // 0 A C
// A B B
// C B B
gl_FragColor = sel3(a4, sel2(a4, a2, a6), sel2(a4, a1, a3), a0);
if(s(a3, a1) && s(a5) && s(n, a8) && s(a7) && ns(d, a2) && ns(d, a1) && ns(d, a3) && ns(d, a6)) { // 0 A C
// A B B
// C B B
gl_FragColor = n? sel3(a0, a1, a3) : sel3(sel2(a2, a6), sel2(a1, a3), a0);
return;
}
}