summaryrefslogtreecommitdiff
path: root/shaders
diff options
context:
space:
mode:
authorM Henning <drawoc@darkrefraction.com>2022-02-06 21:00:31 -0500
committerM Henning <drawoc@darkrefraction.com>2022-06-20 14:28:03 -0400
commit276ec89977dce2cd8a78ef33279f79bbe53d63cf (patch)
tree3ea79e4cf5a404dc21c94e9bdb17750ac20fb059 /shaders
parenta9b6151af8741a82f6e502ae4849d626da96f283 (diff)
shaders: Import shaders from gtk4 GL renderer
Captured from gtk4-widget-factory on gtk 4.6.0 Reviewed-by: Emma Anholt <emma@anholt.net>
Diffstat (limited to 'shaders')
-rw-r--r--shaders/gtk4/102.shader_test465
-rw-r--r--shaders/gtk4/105.shader_test465
-rw-r--r--shaders/gtk4/108.shader_test463
-rw-r--r--shaders/gtk4/111.shader_test392
-rw-r--r--shaders/gtk4/114.shader_test392
-rw-r--r--shaders/gtk4/117.shader_test390
-rw-r--r--shaders/gtk4/12.shader_test375
-rw-r--r--shaders/gtk4/120.shader_test445
-rw-r--r--shaders/gtk4/123.shader_test445
-rw-r--r--shaders/gtk4/126.shader_test443
-rw-r--r--shaders/gtk4/129.shader_test402
-rw-r--r--shaders/gtk4/132.shader_test402
-rw-r--r--shaders/gtk4/135.shader_test400
-rw-r--r--shaders/gtk4/138.shader_test402
-rw-r--r--shaders/gtk4/141.shader_test402
-rw-r--r--shaders/gtk4/144.shader_test400
-rw-r--r--shaders/gtk4/146.shader_test54
-rw-r--r--shaders/gtk4/15.shader_test375
-rw-r--r--shaders/gtk4/18.shader_test373
-rw-r--r--shaders/gtk4/21.shader_test417
-rw-r--r--shaders/gtk4/24.shader_test417
-rw-r--r--shaders/gtk4/27.shader_test415
-rw-r--r--shaders/gtk4/3.shader_test672
-rw-r--r--shaders/gtk4/30.shader_test401
-rw-r--r--shaders/gtk4/33.shader_test401
-rw-r--r--shaders/gtk4/36.shader_test399
-rw-r--r--shaders/gtk4/39.shader_test378
-rw-r--r--shaders/gtk4/42.shader_test378
-rw-r--r--shaders/gtk4/45.shader_test376
-rw-r--r--shaders/gtk4/48.shader_test391
-rw-r--r--shaders/gtk4/51.shader_test391
-rw-r--r--shaders/gtk4/54.shader_test389
-rw-r--r--shaders/gtk4/57.shader_test385
-rw-r--r--shaders/gtk4/6.shader_test672
-rw-r--r--shaders/gtk4/60.shader_test385
-rw-r--r--shaders/gtk4/63.shader_test383
-rw-r--r--shaders/gtk4/66.shader_test443
-rw-r--r--shaders/gtk4/69.shader_test443
-rw-r--r--shaders/gtk4/72.shader_test441
-rw-r--r--shaders/gtk4/75.shader_test382
-rw-r--r--shaders/gtk4/78.shader_test382
-rw-r--r--shaders/gtk4/81.shader_test380
-rw-r--r--shaders/gtk4/84.shader_test405
-rw-r--r--shaders/gtk4/87.shader_test405
-rw-r--r--shaders/gtk4/9.shader_test670
-rw-r--r--shaders/gtk4/90.shader_test403
-rw-r--r--shaders/gtk4/93.shader_test402
-rw-r--r--shaders/gtk4/96.shader_test402
-rw-r--r--shaders/gtk4/99.shader_test400
49 files changed, 20293 insertions, 0 deletions
diff --git a/shaders/gtk4/102.shader_test b/shaders/gtk4/102.shader_test
new file mode 100644
index 0000000..64871f6
--- /dev/null
+++ b/shaders/gtk4/102.shader_test
@@ -0,0 +1,465 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// linear_gradient.glsl
+uniform vec4 u_points;
+
+_OUT_ vec4 info;
+
+void main() {
+ gl_Position = u_projection * (u_modelview * vec4(aPosition, 0.0, 1.0));
+
+ vec2 mv0 = u_modelview[0].xy;
+ vec2 mv1 = u_modelview[1].xy;
+ vec2 offset = aPosition - u_points.xy;
+ vec2 coord = vec2(dot(mv0, offset),
+ dot(mv1, offset));
+
+ // Original equation:
+ // VS | maxDist = length(end - start);
+ // VS | gradient = end - start;
+ // VS | gradientLength = length(gradient);
+ // FS | pos = frag_coord - start
+ // FS | proj = (dot(gradient, pos) / (gradientLength * gradientLength)) * gradient
+ // FS | offset = length(proj) / maxDist
+
+ // Simplified formula derivation:
+ // 1. Notice that maxDist = gradientLength:
+ // offset = length(proj) / gradientLength
+ // 2. Let gnorm = gradient / gradientLength, then:
+ // proj = (dot(gnorm * gradientLength, pos) / (gradientLength * gradientLength)) * (gnorm * gradientLength) =
+ // = dot(gnorm, pos) * gnorm
+ // 3. Since gnorm is unit length then:
+ // length(proj) = length(dot(gnorm, pos) * gnorm) = dot(gnorm, pos)
+ // 4. We can avoid the FS division by passing a scaled pos from the VS:
+ // offset = dot(gnorm, pos) / gradientLength = dot(gnorm, pos / gradientLength)
+ // 5. 1.0 / length(gradient) is inversesqrt(dot(gradient, gradient)) in GLSL
+ vec2 gradient = vec2(dot(mv0, u_points.zw),
+ dot(mv1, u_points.zw));
+ float rcp_gradient_length = inversesqrt(dot(gradient, gradient));
+
+ info = rcp_gradient_length * vec4(coord, gradient);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// linear_gradient.glsl
+
+#define MAX_COLOR_STOPS 6
+
+#ifdef GSK_LEGACY
+uniform int u_num_color_stops;
+#else
+uniform highp int u_num_color_stops; // Why? Because it works like this.
+#endif
+
+uniform float u_color_stops[MAX_COLOR_STOPS * 5];
+uniform bool u_repeat;
+
+_IN_ vec4 info;
+
+float get_offset(int index) {
+ // u_color_stops[5 * index] makes Intel Windows driver crash.
+ // See https://gitlab.gnome.org/GNOME/gtk/-/issues/3783
+ int base = 5 * index;
+ return u_color_stops[base];
+}
+
+vec4 get_color(int index) {
+ int base = 5 * index + 1;
+
+ return vec4(u_color_stops[base],
+ u_color_stops[base + 1],
+ u_color_stops[base + 2],
+ u_color_stops[base + 3]);
+}
+
+void main() {
+ float offset = dot(info.xy, info.zw);
+ float curr_offset;
+ float next_offset;
+
+ if (u_repeat) {
+ offset = fract(offset);
+ }
+
+ next_offset = get_offset(0);
+ if (offset < next_offset) {
+ gskSetOutputColor(gsk_scaled_premultiply(get_color(0), u_alpha));
+ return;
+ }
+
+ if (offset >= get_offset(u_num_color_stops - 1)) {
+ gskSetOutputColor(gsk_scaled_premultiply(get_color(u_num_color_stops - 1), u_alpha));
+ return;
+ }
+
+ for (int i = 0; i < MAX_COLOR_STOPS; i++) {
+ curr_offset = next_offset;
+ next_offset = get_offset(i + 1);
+
+ if (offset < next_offset) {
+ float f = (offset - curr_offset) / (next_offset - curr_offset);
+ vec4 curr_color = gsk_premultiply(get_color(i));
+ vec4 next_color = gsk_premultiply(get_color(i + 1));
+ vec4 color = mix(curr_color, next_color, f);
+ gskSetScaledOutputColor(color, u_alpha);
+ return;
+ }
+ }
+}
+
diff --git a/shaders/gtk4/105.shader_test b/shaders/gtk4/105.shader_test
new file mode 100644
index 0000000..b568bff
--- /dev/null
+++ b/shaders/gtk4/105.shader_test
@@ -0,0 +1,465 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// linear_gradient.glsl
+uniform vec4 u_points;
+
+_OUT_ vec4 info;
+
+void main() {
+ gl_Position = u_projection * (u_modelview * vec4(aPosition, 0.0, 1.0));
+
+ vec2 mv0 = u_modelview[0].xy;
+ vec2 mv1 = u_modelview[1].xy;
+ vec2 offset = aPosition - u_points.xy;
+ vec2 coord = vec2(dot(mv0, offset),
+ dot(mv1, offset));
+
+ // Original equation:
+ // VS | maxDist = length(end - start);
+ // VS | gradient = end - start;
+ // VS | gradientLength = length(gradient);
+ // FS | pos = frag_coord - start
+ // FS | proj = (dot(gradient, pos) / (gradientLength * gradientLength)) * gradient
+ // FS | offset = length(proj) / maxDist
+
+ // Simplified formula derivation:
+ // 1. Notice that maxDist = gradientLength:
+ // offset = length(proj) / gradientLength
+ // 2. Let gnorm = gradient / gradientLength, then:
+ // proj = (dot(gnorm * gradientLength, pos) / (gradientLength * gradientLength)) * (gnorm * gradientLength) =
+ // = dot(gnorm, pos) * gnorm
+ // 3. Since gnorm is unit length then:
+ // length(proj) = length(dot(gnorm, pos) * gnorm) = dot(gnorm, pos)
+ // 4. We can avoid the FS division by passing a scaled pos from the VS:
+ // offset = dot(gnorm, pos) / gradientLength = dot(gnorm, pos / gradientLength)
+ // 5. 1.0 / length(gradient) is inversesqrt(dot(gradient, gradient)) in GLSL
+ vec2 gradient = vec2(dot(mv0, u_points.zw),
+ dot(mv1, u_points.zw));
+ float rcp_gradient_length = inversesqrt(dot(gradient, gradient));
+
+ info = rcp_gradient_length * vec4(coord, gradient);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// linear_gradient.glsl
+
+#define MAX_COLOR_STOPS 6
+
+#ifdef GSK_LEGACY
+uniform int u_num_color_stops;
+#else
+uniform highp int u_num_color_stops; // Why? Because it works like this.
+#endif
+
+uniform float u_color_stops[MAX_COLOR_STOPS * 5];
+uniform bool u_repeat;
+
+_IN_ vec4 info;
+
+float get_offset(int index) {
+ // u_color_stops[5 * index] makes Intel Windows driver crash.
+ // See https://gitlab.gnome.org/GNOME/gtk/-/issues/3783
+ int base = 5 * index;
+ return u_color_stops[base];
+}
+
+vec4 get_color(int index) {
+ int base = 5 * index + 1;
+
+ return vec4(u_color_stops[base],
+ u_color_stops[base + 1],
+ u_color_stops[base + 2],
+ u_color_stops[base + 3]);
+}
+
+void main() {
+ float offset = dot(info.xy, info.zw);
+ float curr_offset;
+ float next_offset;
+
+ if (u_repeat) {
+ offset = fract(offset);
+ }
+
+ next_offset = get_offset(0);
+ if (offset < next_offset) {
+ gskSetOutputColor(gsk_scaled_premultiply(get_color(0), u_alpha));
+ return;
+ }
+
+ if (offset >= get_offset(u_num_color_stops - 1)) {
+ gskSetOutputColor(gsk_scaled_premultiply(get_color(u_num_color_stops - 1), u_alpha));
+ return;
+ }
+
+ for (int i = 0; i < MAX_COLOR_STOPS; i++) {
+ curr_offset = next_offset;
+ next_offset = get_offset(i + 1);
+
+ if (offset < next_offset) {
+ float f = (offset - curr_offset) / (next_offset - curr_offset);
+ vec4 curr_color = gsk_premultiply(get_color(i));
+ vec4 next_color = gsk_premultiply(get_color(i + 1));
+ vec4 color = mix(curr_color, next_color, f);
+ gskSetScaledOutputColor(color, u_alpha);
+ return;
+ }
+ }
+}
+
diff --git a/shaders/gtk4/108.shader_test b/shaders/gtk4/108.shader_test
new file mode 100644
index 0000000..ec4ce94
--- /dev/null
+++ b/shaders/gtk4/108.shader_test
@@ -0,0 +1,463 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// linear_gradient.glsl
+uniform vec4 u_points;
+
+_OUT_ vec4 info;
+
+void main() {
+ gl_Position = u_projection * (u_modelview * vec4(aPosition, 0.0, 1.0));
+
+ vec2 mv0 = u_modelview[0].xy;
+ vec2 mv1 = u_modelview[1].xy;
+ vec2 offset = aPosition - u_points.xy;
+ vec2 coord = vec2(dot(mv0, offset),
+ dot(mv1, offset));
+
+ // Original equation:
+ // VS | maxDist = length(end - start);
+ // VS | gradient = end - start;
+ // VS | gradientLength = length(gradient);
+ // FS | pos = frag_coord - start
+ // FS | proj = (dot(gradient, pos) / (gradientLength * gradientLength)) * gradient
+ // FS | offset = length(proj) / maxDist
+
+ // Simplified formula derivation:
+ // 1. Notice that maxDist = gradientLength:
+ // offset = length(proj) / gradientLength
+ // 2. Let gnorm = gradient / gradientLength, then:
+ // proj = (dot(gnorm * gradientLength, pos) / (gradientLength * gradientLength)) * (gnorm * gradientLength) =
+ // = dot(gnorm, pos) * gnorm
+ // 3. Since gnorm is unit length then:
+ // length(proj) = length(dot(gnorm, pos) * gnorm) = dot(gnorm, pos)
+ // 4. We can avoid the FS division by passing a scaled pos from the VS:
+ // offset = dot(gnorm, pos) / gradientLength = dot(gnorm, pos / gradientLength)
+ // 5. 1.0 / length(gradient) is inversesqrt(dot(gradient, gradient)) in GLSL
+ vec2 gradient = vec2(dot(mv0, u_points.zw),
+ dot(mv1, u_points.zw));
+ float rcp_gradient_length = inversesqrt(dot(gradient, gradient));
+
+ info = rcp_gradient_length * vec4(coord, gradient);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// linear_gradient.glsl
+
+#define MAX_COLOR_STOPS 6
+
+#ifdef GSK_LEGACY
+uniform int u_num_color_stops;
+#else
+uniform highp int u_num_color_stops; // Why? Because it works like this.
+#endif
+
+uniform float u_color_stops[MAX_COLOR_STOPS * 5];
+uniform bool u_repeat;
+
+_IN_ vec4 info;
+
+float get_offset(int index) {
+ // u_color_stops[5 * index] makes Intel Windows driver crash.
+ // See https://gitlab.gnome.org/GNOME/gtk/-/issues/3783
+ int base = 5 * index;
+ return u_color_stops[base];
+}
+
+vec4 get_color(int index) {
+ int base = 5 * index + 1;
+
+ return vec4(u_color_stops[base],
+ u_color_stops[base + 1],
+ u_color_stops[base + 2],
+ u_color_stops[base + 3]);
+}
+
+void main() {
+ float offset = dot(info.xy, info.zw);
+ float curr_offset;
+ float next_offset;
+
+ if (u_repeat) {
+ offset = fract(offset);
+ }
+
+ next_offset = get_offset(0);
+ if (offset < next_offset) {
+ gskSetOutputColor(gsk_scaled_premultiply(get_color(0), u_alpha));
+ return;
+ }
+
+ if (offset >= get_offset(u_num_color_stops - 1)) {
+ gskSetOutputColor(gsk_scaled_premultiply(get_color(u_num_color_stops - 1), u_alpha));
+ return;
+ }
+
+ for (int i = 0; i < MAX_COLOR_STOPS; i++) {
+ curr_offset = next_offset;
+ next_offset = get_offset(i + 1);
+
+ if (offset < next_offset) {
+ float f = (offset - curr_offset) / (next_offset - curr_offset);
+ vec4 curr_color = gsk_premultiply(get_color(i));
+ vec4 next_color = gsk_premultiply(get_color(i + 1));
+ vec4 color = mix(curr_color, next_color, f);
+ gskSetScaledOutputColor(color, u_alpha);
+ return;
+ }
+ }
+}
+
diff --git a/shaders/gtk4/111.shader_test b/shaders/gtk4/111.shader_test
new file mode 100644
index 0000000..42a65cf
--- /dev/null
+++ b/shaders/gtk4/111.shader_test
@@ -0,0 +1,392 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// outset_shadow.glsl
+
+uniform vec4[3] u_outline_rect;
+
+_OUT_ vec4 final_color;
+_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outline;
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ vUv = vec2(aUv.x, aUv.y);
+
+ final_color = gsk_scaled_premultiply(aColor, u_alpha);
+
+ GskRoundedRect outline = gsk_create_rect(u_outline_rect);
+ gsk_rounded_rect_transform(outline, u_modelview);
+ gsk_rounded_rect_encode(outline, transformed_outline);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// outset_shadow.glsl
+
+_IN_ vec4 final_color;
+_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outline;
+
+void main() {
+ vec2 frag = gsk_get_frag_coord();
+ float alpha = GskTexture(u_source, vUv).a;
+
+ alpha *= (1.0 - clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outline), frag), 0.0, 1.0));
+
+ gskSetScaledOutputColor(final_color, alpha);
+}
+
diff --git a/shaders/gtk4/114.shader_test b/shaders/gtk4/114.shader_test
new file mode 100644
index 0000000..3bbb8c5
--- /dev/null
+++ b/shaders/gtk4/114.shader_test
@@ -0,0 +1,392 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// outset_shadow.glsl
+
+uniform vec4[3] u_outline_rect;
+
+_OUT_ vec4 final_color;
+_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outline;
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ vUv = vec2(aUv.x, aUv.y);
+
+ final_color = gsk_scaled_premultiply(aColor, u_alpha);
+
+ GskRoundedRect outline = gsk_create_rect(u_outline_rect);
+ gsk_rounded_rect_transform(outline, u_modelview);
+ gsk_rounded_rect_encode(outline, transformed_outline);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// outset_shadow.glsl
+
+_IN_ vec4 final_color;
+_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outline;
+
+void main() {
+ vec2 frag = gsk_get_frag_coord();
+ float alpha = GskTexture(u_source, vUv).a;
+
+ alpha *= (1.0 - clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outline), frag), 0.0, 1.0));
+
+ gskSetScaledOutputColor(final_color, alpha);
+}
+
diff --git a/shaders/gtk4/117.shader_test b/shaders/gtk4/117.shader_test
new file mode 100644
index 0000000..c0cc165
--- /dev/null
+++ b/shaders/gtk4/117.shader_test
@@ -0,0 +1,390 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// outset_shadow.glsl
+
+uniform vec4[3] u_outline_rect;
+
+_OUT_ vec4 final_color;
+_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outline;
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ vUv = vec2(aUv.x, aUv.y);
+
+ final_color = gsk_scaled_premultiply(aColor, u_alpha);
+
+ GskRoundedRect outline = gsk_create_rect(u_outline_rect);
+ gsk_rounded_rect_transform(outline, u_modelview);
+ gsk_rounded_rect_encode(outline, transformed_outline);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// outset_shadow.glsl
+
+_IN_ vec4 final_color;
+_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outline;
+
+void main() {
+ vec2 frag = gsk_get_frag_coord();
+ float alpha = GskTexture(u_source, vUv).a;
+
+ alpha *= (1.0 - clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outline), frag), 0.0, 1.0));
+
+ gskSetScaledOutputColor(final_color, alpha);
+}
+
diff --git a/shaders/gtk4/12.shader_test b/shaders/gtk4/12.shader_test
new file mode 100644
index 0000000..fc62790
--- /dev/null
+++ b/shaders/gtk4/12.shader_test
@@ -0,0 +1,375 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// blit.glsl
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ vUv = vec2(aUv.x, aUv.y);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// blit.glsl
+
+void main() {
+ vec4 diffuse = GskTexture(u_source, vUv);
+
+ gskSetScaledOutputColor(diffuse, u_alpha);
+}
+
diff --git a/shaders/gtk4/120.shader_test b/shaders/gtk4/120.shader_test
new file mode 100644
index 0000000..e8d623d
--- /dev/null
+++ b/shaders/gtk4/120.shader_test
@@ -0,0 +1,445 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// radial_gradient.glsl
+
+uniform vec4 u_geometry;
+
+_OUT_ vec2 coord;
+
+void main() {
+ gl_Position = u_projection * (u_modelview * vec4(aPosition, 0.0, 1.0));
+
+ vec2 mv0 = u_modelview[0].xy;
+ vec2 mv1 = u_modelview[1].xy;
+ vec2 offset = aPosition - u_geometry.xy;
+ vec2 dir = vec2(dot(mv0, offset),
+ dot(mv1, offset));
+
+ coord = dir * u_geometry.zw;
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// radial_gradient.glsl
+
+#define MAX_COLOR_STOPS 6
+
+#ifdef GSK_LEGACY
+uniform int u_num_color_stops;
+#else
+uniform highp int u_num_color_stops;
+#endif
+
+uniform bool u_repeat;
+uniform vec2 u_range;
+uniform float u_color_stops[MAX_COLOR_STOPS * 5];
+
+_IN_ vec2 coord;
+
+float get_offset(int index) {
+ // u_color_stops[5 * index] makes Intel Windows driver crash.
+ // See https://gitlab.gnome.org/GNOME/gtk/-/issues/3783
+ int base = 5 * index;
+ return u_color_stops[base];
+}
+
+vec4 get_color(int index) {
+ int base = 5 * index + 1;
+
+ return vec4(u_color_stops[base],
+ u_color_stops[base + 1],
+ u_color_stops[base + 2],
+ u_color_stops[base + 3]);
+}
+
+void main() {
+ // Reverse scale
+ float offset = length(coord) * u_range.x + u_range.y;
+ float curr_offset;
+ float next_offset;
+
+ if (u_repeat) {
+ offset = fract(offset);
+ }
+
+ next_offset = get_offset(0);
+ if (offset < next_offset) {
+ gskSetOutputColor(gsk_scaled_premultiply(get_color(0), u_alpha));
+ return;
+ }
+
+ if (offset >= get_offset(u_num_color_stops - 1)) {
+ gskSetOutputColor(gsk_scaled_premultiply(get_color(u_num_color_stops - 1), u_alpha));
+ return;
+ }
+
+ for (int i = 0; i < MAX_COLOR_STOPS; i++) {
+ curr_offset = next_offset;
+ next_offset = get_offset(i + 1);
+
+ if (offset < next_offset) {
+ float f = (offset - curr_offset) / (next_offset - curr_offset);
+ vec4 curr_color = gsk_premultiply(get_color(i));
+ vec4 next_color = gsk_premultiply(get_color(i + 1));
+ vec4 color = mix(curr_color, next_color, f);
+ gskSetScaledOutputColor(color, u_alpha);
+ return;
+ }
+ }
+}
+
diff --git a/shaders/gtk4/123.shader_test b/shaders/gtk4/123.shader_test
new file mode 100644
index 0000000..bec58bb
--- /dev/null
+++ b/shaders/gtk4/123.shader_test
@@ -0,0 +1,445 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// radial_gradient.glsl
+
+uniform vec4 u_geometry;
+
+_OUT_ vec2 coord;
+
+void main() {
+ gl_Position = u_projection * (u_modelview * vec4(aPosition, 0.0, 1.0));
+
+ vec2 mv0 = u_modelview[0].xy;
+ vec2 mv1 = u_modelview[1].xy;
+ vec2 offset = aPosition - u_geometry.xy;
+ vec2 dir = vec2(dot(mv0, offset),
+ dot(mv1, offset));
+
+ coord = dir * u_geometry.zw;
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// radial_gradient.glsl
+
+#define MAX_COLOR_STOPS 6
+
+#ifdef GSK_LEGACY
+uniform int u_num_color_stops;
+#else
+uniform highp int u_num_color_stops;
+#endif
+
+uniform bool u_repeat;
+uniform vec2 u_range;
+uniform float u_color_stops[MAX_COLOR_STOPS * 5];
+
+_IN_ vec2 coord;
+
+float get_offset(int index) {
+ // u_color_stops[5 * index] makes Intel Windows driver crash.
+ // See https://gitlab.gnome.org/GNOME/gtk/-/issues/3783
+ int base = 5 * index;
+ return u_color_stops[base];
+}
+
+vec4 get_color(int index) {
+ int base = 5 * index + 1;
+
+ return vec4(u_color_stops[base],
+ u_color_stops[base + 1],
+ u_color_stops[base + 2],
+ u_color_stops[base + 3]);
+}
+
+void main() {
+ // Reverse scale
+ float offset = length(coord) * u_range.x + u_range.y;
+ float curr_offset;
+ float next_offset;
+
+ if (u_repeat) {
+ offset = fract(offset);
+ }
+
+ next_offset = get_offset(0);
+ if (offset < next_offset) {
+ gskSetOutputColor(gsk_scaled_premultiply(get_color(0), u_alpha));
+ return;
+ }
+
+ if (offset >= get_offset(u_num_color_stops - 1)) {
+ gskSetOutputColor(gsk_scaled_premultiply(get_color(u_num_color_stops - 1), u_alpha));
+ return;
+ }
+
+ for (int i = 0; i < MAX_COLOR_STOPS; i++) {
+ curr_offset = next_offset;
+ next_offset = get_offset(i + 1);
+
+ if (offset < next_offset) {
+ float f = (offset - curr_offset) / (next_offset - curr_offset);
+ vec4 curr_color = gsk_premultiply(get_color(i));
+ vec4 next_color = gsk_premultiply(get_color(i + 1));
+ vec4 color = mix(curr_color, next_color, f);
+ gskSetScaledOutputColor(color, u_alpha);
+ return;
+ }
+ }
+}
+
diff --git a/shaders/gtk4/126.shader_test b/shaders/gtk4/126.shader_test
new file mode 100644
index 0000000..f64432d
--- /dev/null
+++ b/shaders/gtk4/126.shader_test
@@ -0,0 +1,443 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// radial_gradient.glsl
+
+uniform vec4 u_geometry;
+
+_OUT_ vec2 coord;
+
+void main() {
+ gl_Position = u_projection * (u_modelview * vec4(aPosition, 0.0, 1.0));
+
+ vec2 mv0 = u_modelview[0].xy;
+ vec2 mv1 = u_modelview[1].xy;
+ vec2 offset = aPosition - u_geometry.xy;
+ vec2 dir = vec2(dot(mv0, offset),
+ dot(mv1, offset));
+
+ coord = dir * u_geometry.zw;
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// radial_gradient.glsl
+
+#define MAX_COLOR_STOPS 6
+
+#ifdef GSK_LEGACY
+uniform int u_num_color_stops;
+#else
+uniform highp int u_num_color_stops;
+#endif
+
+uniform bool u_repeat;
+uniform vec2 u_range;
+uniform float u_color_stops[MAX_COLOR_STOPS * 5];
+
+_IN_ vec2 coord;
+
+float get_offset(int index) {
+ // u_color_stops[5 * index] makes Intel Windows driver crash.
+ // See https://gitlab.gnome.org/GNOME/gtk/-/issues/3783
+ int base = 5 * index;
+ return u_color_stops[base];
+}
+
+vec4 get_color(int index) {
+ int base = 5 * index + 1;
+
+ return vec4(u_color_stops[base],
+ u_color_stops[base + 1],
+ u_color_stops[base + 2],
+ u_color_stops[base + 3]);
+}
+
+void main() {
+ // Reverse scale
+ float offset = length(coord) * u_range.x + u_range.y;
+ float curr_offset;
+ float next_offset;
+
+ if (u_repeat) {
+ offset = fract(offset);
+ }
+
+ next_offset = get_offset(0);
+ if (offset < next_offset) {
+ gskSetOutputColor(gsk_scaled_premultiply(get_color(0), u_alpha));
+ return;
+ }
+
+ if (offset >= get_offset(u_num_color_stops - 1)) {
+ gskSetOutputColor(gsk_scaled_premultiply(get_color(u_num_color_stops - 1), u_alpha));
+ return;
+ }
+
+ for (int i = 0; i < MAX_COLOR_STOPS; i++) {
+ curr_offset = next_offset;
+ next_offset = get_offset(i + 1);
+
+ if (offset < next_offset) {
+ float f = (offset - curr_offset) / (next_offset - curr_offset);
+ vec4 curr_color = gsk_premultiply(get_color(i));
+ vec4 next_color = gsk_premultiply(get_color(i + 1));
+ vec4 color = mix(curr_color, next_color, f);
+ gskSetScaledOutputColor(color, u_alpha);
+ return;
+ }
+ }
+}
+
diff --git a/shaders/gtk4/129.shader_test b/shaders/gtk4/129.shader_test
new file mode 100644
index 0000000..d5e3525
--- /dev/null
+++ b/shaders/gtk4/129.shader_test
@@ -0,0 +1,402 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// repeat.glsl
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ vUv = vec2(aUv.x, aUv.y);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// repeat.glsl
+
+uniform vec4 u_child_bounds;
+uniform vec4 u_texture_rect;
+
+float wrap(float f, float wrap_for) {
+ return mod(f, wrap_for);
+}
+
+/* We get the texture coordinates via vUv,
+ * but that might be on a texture atlas, so we need to do the
+ * wrapping ourselves.
+ */
+void main() {
+
+ /* We map the texture coordinate to [1;0], then wrap it and scale the result again */
+
+ float tw = u_texture_rect.z - u_texture_rect.x;
+ float th = u_texture_rect.w - u_texture_rect.y;
+
+ float mapped_x = (vUv.x - u_texture_rect.x) / tw;
+ float mapped_y = (vUv.y - u_texture_rect.y) / th;
+
+ float wrapped_x = wrap(u_child_bounds.x + mapped_x * u_child_bounds.z, 1.0);
+ float wrapped_y = wrap(u_child_bounds.y + mapped_y * u_child_bounds.w, 1.0);
+
+ vec2 tp;
+ tp.x = u_texture_rect.x + (wrapped_x * tw);
+ tp.y = u_texture_rect.y + (wrapped_y * th);
+
+ vec4 diffuse = GskTexture(u_source, tp);
+
+ gskSetScaledOutputColor(diffuse, u_alpha);
+}
+
diff --git a/shaders/gtk4/132.shader_test b/shaders/gtk4/132.shader_test
new file mode 100644
index 0000000..73e1911
--- /dev/null
+++ b/shaders/gtk4/132.shader_test
@@ -0,0 +1,402 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// repeat.glsl
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ vUv = vec2(aUv.x, aUv.y);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// repeat.glsl
+
+uniform vec4 u_child_bounds;
+uniform vec4 u_texture_rect;
+
+float wrap(float f, float wrap_for) {
+ return mod(f, wrap_for);
+}
+
+/* We get the texture coordinates via vUv,
+ * but that might be on a texture atlas, so we need to do the
+ * wrapping ourselves.
+ */
+void main() {
+
+ /* We map the texture coordinate to [1;0], then wrap it and scale the result again */
+
+ float tw = u_texture_rect.z - u_texture_rect.x;
+ float th = u_texture_rect.w - u_texture_rect.y;
+
+ float mapped_x = (vUv.x - u_texture_rect.x) / tw;
+ float mapped_y = (vUv.y - u_texture_rect.y) / th;
+
+ float wrapped_x = wrap(u_child_bounds.x + mapped_x * u_child_bounds.z, 1.0);
+ float wrapped_y = wrap(u_child_bounds.y + mapped_y * u_child_bounds.w, 1.0);
+
+ vec2 tp;
+ tp.x = u_texture_rect.x + (wrapped_x * tw);
+ tp.y = u_texture_rect.y + (wrapped_y * th);
+
+ vec4 diffuse = GskTexture(u_source, tp);
+
+ gskSetScaledOutputColor(diffuse, u_alpha);
+}
+
diff --git a/shaders/gtk4/135.shader_test b/shaders/gtk4/135.shader_test
new file mode 100644
index 0000000..87d53ab
--- /dev/null
+++ b/shaders/gtk4/135.shader_test
@@ -0,0 +1,400 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// repeat.glsl
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ vUv = vec2(aUv.x, aUv.y);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// repeat.glsl
+
+uniform vec4 u_child_bounds;
+uniform vec4 u_texture_rect;
+
+float wrap(float f, float wrap_for) {
+ return mod(f, wrap_for);
+}
+
+/* We get the texture coordinates via vUv,
+ * but that might be on a texture atlas, so we need to do the
+ * wrapping ourselves.
+ */
+void main() {
+
+ /* We map the texture coordinate to [1;0], then wrap it and scale the result again */
+
+ float tw = u_texture_rect.z - u_texture_rect.x;
+ float th = u_texture_rect.w - u_texture_rect.y;
+
+ float mapped_x = (vUv.x - u_texture_rect.x) / tw;
+ float mapped_y = (vUv.y - u_texture_rect.y) / th;
+
+ float wrapped_x = wrap(u_child_bounds.x + mapped_x * u_child_bounds.z, 1.0);
+ float wrapped_y = wrap(u_child_bounds.y + mapped_y * u_child_bounds.w, 1.0);
+
+ vec2 tp;
+ tp.x = u_texture_rect.x + (wrapped_x * tw);
+ tp.y = u_texture_rect.y + (wrapped_y * th);
+
+ vec4 diffuse = GskTexture(u_source, tp);
+
+ gskSetScaledOutputColor(diffuse, u_alpha);
+}
+
diff --git a/shaders/gtk4/138.shader_test b/shaders/gtk4/138.shader_test
new file mode 100644
index 0000000..8daf069
--- /dev/null
+++ b/shaders/gtk4/138.shader_test
@@ -0,0 +1,402 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// unblurred_outset_shadow.glsl
+
+uniform float u_spread;
+uniform vec2 u_offset;
+uniform vec4[3] u_outline_rect;
+
+_OUT_ vec4 final_color;
+_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
+_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ final_color = gsk_premultiply(aColor) * u_alpha;
+
+ GskRoundedRect inside = gsk_create_rect(u_outline_rect);
+ GskRoundedRect outside = gsk_rounded_rect_shrink(inside, vec4(- u_spread));
+
+ gsk_rounded_rect_offset(outside, u_offset);
+
+ gsk_rounded_rect_transform(outside, u_modelview);
+ gsk_rounded_rect_transform(inside, u_modelview);
+
+ gsk_rounded_rect_encode(outside, transformed_outside_outline);
+ gsk_rounded_rect_encode(inside, transformed_inside_outline);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// unblurred_outset_shadow.glsl
+_IN_ vec4 final_color;
+_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
+_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
+
+void main() {
+ vec2 frag = gsk_get_frag_coord();
+
+ float alpha = clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outside_outline), frag) -
+ gsk_rounded_rect_coverage(gsk_decode_rect(transformed_inside_outline), frag),
+ 0.0, 1.0);
+
+ gskSetScaledOutputColor(final_color, alpha);
+}
+
+
diff --git a/shaders/gtk4/141.shader_test b/shaders/gtk4/141.shader_test
new file mode 100644
index 0000000..736a291
--- /dev/null
+++ b/shaders/gtk4/141.shader_test
@@ -0,0 +1,402 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// unblurred_outset_shadow.glsl
+
+uniform float u_spread;
+uniform vec2 u_offset;
+uniform vec4[3] u_outline_rect;
+
+_OUT_ vec4 final_color;
+_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
+_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ final_color = gsk_premultiply(aColor) * u_alpha;
+
+ GskRoundedRect inside = gsk_create_rect(u_outline_rect);
+ GskRoundedRect outside = gsk_rounded_rect_shrink(inside, vec4(- u_spread));
+
+ gsk_rounded_rect_offset(outside, u_offset);
+
+ gsk_rounded_rect_transform(outside, u_modelview);
+ gsk_rounded_rect_transform(inside, u_modelview);
+
+ gsk_rounded_rect_encode(outside, transformed_outside_outline);
+ gsk_rounded_rect_encode(inside, transformed_inside_outline);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// unblurred_outset_shadow.glsl
+_IN_ vec4 final_color;
+_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
+_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
+
+void main() {
+ vec2 frag = gsk_get_frag_coord();
+
+ float alpha = clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outside_outline), frag) -
+ gsk_rounded_rect_coverage(gsk_decode_rect(transformed_inside_outline), frag),
+ 0.0, 1.0);
+
+ gskSetScaledOutputColor(final_color, alpha);
+}
+
+
diff --git a/shaders/gtk4/144.shader_test b/shaders/gtk4/144.shader_test
new file mode 100644
index 0000000..1fc4e32
--- /dev/null
+++ b/shaders/gtk4/144.shader_test
@@ -0,0 +1,400 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// unblurred_outset_shadow.glsl
+
+uniform float u_spread;
+uniform vec2 u_offset;
+uniform vec4[3] u_outline_rect;
+
+_OUT_ vec4 final_color;
+_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
+_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ final_color = gsk_premultiply(aColor) * u_alpha;
+
+ GskRoundedRect inside = gsk_create_rect(u_outline_rect);
+ GskRoundedRect outside = gsk_rounded_rect_shrink(inside, vec4(- u_spread));
+
+ gsk_rounded_rect_offset(outside, u_offset);
+
+ gsk_rounded_rect_transform(outside, u_modelview);
+ gsk_rounded_rect_transform(inside, u_modelview);
+
+ gsk_rounded_rect_encode(outside, transformed_outside_outline);
+ gsk_rounded_rect_encode(inside, transformed_inside_outline);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// unblurred_outset_shadow.glsl
+_IN_ vec4 final_color;
+_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
+_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
+
+void main() {
+ vec2 frag = gsk_get_frag_coord();
+
+ float alpha = clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outside_outline), frag) -
+ gsk_rounded_rect_coverage(gsk_decode_rect(transformed_inside_outline), frag),
+ 0.0, 1.0);
+
+ gskSetScaledOutputColor(final_color, alpha);
+}
+
+
diff --git a/shaders/gtk4/146.shader_test b/shaders/gtk4/146.shader_test
new file mode 100644
index 0000000..6d7a623
--- /dev/null
+++ b/shaders/gtk4/146.shader_test
@@ -0,0 +1,54 @@
+[require]
+GLSL >= 3.30
+
+[vertex shader]
+#version 330 core
+in vec4 a_position;
+in vec2 a_texcoord;
+out vec2 v_texcoord;
+void main()
+{
+ gl_Position = a_position;
+ v_texcoord = a_texcoord;
+}
+
+[fragment shader]
+#version 330 core
+uniform vec2 tex_scale0;
+uniform vec2 tex_scale1;
+uniform vec2 tex_scale2;
+uniform float width;
+uniform float height;
+uniform float poffset_x;
+uniform float poffset_y;
+uniform vec3 offset;
+uniform vec3 coeff1;
+uniform vec3 coeff2;
+uniform vec3 coeff3;
+uniform sampler2D Ytex, Utex, Vtex;
+
+layout (location = 0) out vec4 fragColor;
+
+vec3 yuv_to_rgb (vec3 val, vec3 offset, vec3 ycoeff, vec3 ucoeff, vec3 vcoeff) {
+ vec3 rgb;
+ val += offset;
+ rgb.r = dot(val, ycoeff);
+ rgb.g = dot(val, ucoeff);
+ rgb.b = dot(val, vcoeff);
+ return rgb;
+}
+
+
+in vec2 v_texcoord;
+void main (void) {
+vec2 texcoord;
+texcoord = v_texcoord;
+vec4 texel, rgba;
+texel.x = texture(Ytex, texcoord * tex_scale0).r;
+texel.y = texture(Utex, texcoord * tex_scale1).r;
+texel.z = texture(Vtex, texcoord * tex_scale2).r;
+rgba.rgb = yuv_to_rgb (texel.xyz, offset, coeff1, coeff2, coeff3);
+rgba.a = 1.0;
+fragColor=vec4(rgba.r,rgba.g,rgba.b,rgba.a);
+
+}
diff --git a/shaders/gtk4/15.shader_test b/shaders/gtk4/15.shader_test
new file mode 100644
index 0000000..d4b808f
--- /dev/null
+++ b/shaders/gtk4/15.shader_test
@@ -0,0 +1,375 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// blit.glsl
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ vUv = vec2(aUv.x, aUv.y);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// blit.glsl
+
+void main() {
+ vec4 diffuse = GskTexture(u_source, vUv);
+
+ gskSetScaledOutputColor(diffuse, u_alpha);
+}
+
diff --git a/shaders/gtk4/18.shader_test b/shaders/gtk4/18.shader_test
new file mode 100644
index 0000000..27dfe83
--- /dev/null
+++ b/shaders/gtk4/18.shader_test
@@ -0,0 +1,373 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// blit.glsl
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ vUv = vec2(aUv.x, aUv.y);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// blit.glsl
+
+void main() {
+ vec4 diffuse = GskTexture(u_source, vUv);
+
+ gskSetScaledOutputColor(diffuse, u_alpha);
+}
+
diff --git a/shaders/gtk4/21.shader_test b/shaders/gtk4/21.shader_test
new file mode 100644
index 0000000..790282d
--- /dev/null
+++ b/shaders/gtk4/21.shader_test
@@ -0,0 +1,417 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// blur.glsl
+
+uniform float u_blur_radius;
+uniform vec2 u_blur_size;
+uniform vec2 u_blur_dir;
+
+_OUT_ vec2 pixel_step;
+_OUT_ float pixels_per_side;
+_OUT_ vec3 initial_gaussian;
+
+const float PI = 3.14159265;
+const float RADIUS_MULTIPLIER = 2.0;
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ vUv = vec2(aUv.x, aUv.y);
+
+ pixel_step = (vec2(1.0) / u_blur_size) * u_blur_dir;
+ pixels_per_side = floor(u_blur_radius * RADIUS_MULTIPLIER / 2.0);
+
+ float sigma = u_blur_radius / 2.0; // *shrug*
+ initial_gaussian.x = 1.0 / (sqrt(2.0 * PI) * sigma);
+ initial_gaussian.y = exp(-0.5 / (sigma * sigma));
+ initial_gaussian.z = initial_gaussian.y * initial_gaussian.y;
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// blur.glsl
+
+_IN_ vec2 pixel_step;
+_IN_ float pixels_per_side;
+_IN_ vec3 initial_gaussian;
+
+// blur_radius 0 is NOT supported and MUST be caught before.
+
+// Partially from http://callumhay.blogspot.com/2010/09/gaussian-blur-shader-glsl.html
+void main() {
+ vec3 incrementalGaussian = initial_gaussian;
+
+ float coefficientSum = 0.0;
+ vec4 sum = GskTexture(u_source, vUv) * incrementalGaussian.x;
+ coefficientSum += incrementalGaussian.x;
+ incrementalGaussian.xy *= incrementalGaussian.yz;
+
+ vec2 p = pixel_step;
+ for (int i = 1; i <= int(pixels_per_side); i++) {
+ sum += GskTexture(u_source, vUv - p) * incrementalGaussian.x;
+ sum += GskTexture(u_source, vUv + p) * incrementalGaussian.x;
+
+ coefficientSum += 2.0 * incrementalGaussian.x;
+ incrementalGaussian.xy *= incrementalGaussian.yz;
+
+ p += pixel_step;
+ }
+
+ gskSetOutputColor(sum / coefficientSum);
+}
+
diff --git a/shaders/gtk4/24.shader_test b/shaders/gtk4/24.shader_test
new file mode 100644
index 0000000..fdcfd45
--- /dev/null
+++ b/shaders/gtk4/24.shader_test
@@ -0,0 +1,417 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// blur.glsl
+
+uniform float u_blur_radius;
+uniform vec2 u_blur_size;
+uniform vec2 u_blur_dir;
+
+_OUT_ vec2 pixel_step;
+_OUT_ float pixels_per_side;
+_OUT_ vec3 initial_gaussian;
+
+const float PI = 3.14159265;
+const float RADIUS_MULTIPLIER = 2.0;
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ vUv = vec2(aUv.x, aUv.y);
+
+ pixel_step = (vec2(1.0) / u_blur_size) * u_blur_dir;
+ pixels_per_side = floor(u_blur_radius * RADIUS_MULTIPLIER / 2.0);
+
+ float sigma = u_blur_radius / 2.0; // *shrug*
+ initial_gaussian.x = 1.0 / (sqrt(2.0 * PI) * sigma);
+ initial_gaussian.y = exp(-0.5 / (sigma * sigma));
+ initial_gaussian.z = initial_gaussian.y * initial_gaussian.y;
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// blur.glsl
+
+_IN_ vec2 pixel_step;
+_IN_ float pixels_per_side;
+_IN_ vec3 initial_gaussian;
+
+// blur_radius 0 is NOT supported and MUST be caught before.
+
+// Partially from http://callumhay.blogspot.com/2010/09/gaussian-blur-shader-glsl.html
+void main() {
+ vec3 incrementalGaussian = initial_gaussian;
+
+ float coefficientSum = 0.0;
+ vec4 sum = GskTexture(u_source, vUv) * incrementalGaussian.x;
+ coefficientSum += incrementalGaussian.x;
+ incrementalGaussian.xy *= incrementalGaussian.yz;
+
+ vec2 p = pixel_step;
+ for (int i = 1; i <= int(pixels_per_side); i++) {
+ sum += GskTexture(u_source, vUv - p) * incrementalGaussian.x;
+ sum += GskTexture(u_source, vUv + p) * incrementalGaussian.x;
+
+ coefficientSum += 2.0 * incrementalGaussian.x;
+ incrementalGaussian.xy *= incrementalGaussian.yz;
+
+ p += pixel_step;
+ }
+
+ gskSetOutputColor(sum / coefficientSum);
+}
+
diff --git a/shaders/gtk4/27.shader_test b/shaders/gtk4/27.shader_test
new file mode 100644
index 0000000..732dbc3
--- /dev/null
+++ b/shaders/gtk4/27.shader_test
@@ -0,0 +1,415 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// blur.glsl
+
+uniform float u_blur_radius;
+uniform vec2 u_blur_size;
+uniform vec2 u_blur_dir;
+
+_OUT_ vec2 pixel_step;
+_OUT_ float pixels_per_side;
+_OUT_ vec3 initial_gaussian;
+
+const float PI = 3.14159265;
+const float RADIUS_MULTIPLIER = 2.0;
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ vUv = vec2(aUv.x, aUv.y);
+
+ pixel_step = (vec2(1.0) / u_blur_size) * u_blur_dir;
+ pixels_per_side = floor(u_blur_radius * RADIUS_MULTIPLIER / 2.0);
+
+ float sigma = u_blur_radius / 2.0; // *shrug*
+ initial_gaussian.x = 1.0 / (sqrt(2.0 * PI) * sigma);
+ initial_gaussian.y = exp(-0.5 / (sigma * sigma));
+ initial_gaussian.z = initial_gaussian.y * initial_gaussian.y;
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// blur.glsl
+
+_IN_ vec2 pixel_step;
+_IN_ float pixels_per_side;
+_IN_ vec3 initial_gaussian;
+
+// blur_radius 0 is NOT supported and MUST be caught before.
+
+// Partially from http://callumhay.blogspot.com/2010/09/gaussian-blur-shader-glsl.html
+void main() {
+ vec3 incrementalGaussian = initial_gaussian;
+
+ float coefficientSum = 0.0;
+ vec4 sum = GskTexture(u_source, vUv) * incrementalGaussian.x;
+ coefficientSum += incrementalGaussian.x;
+ incrementalGaussian.xy *= incrementalGaussian.yz;
+
+ vec2 p = pixel_step;
+ for (int i = 1; i <= int(pixels_per_side); i++) {
+ sum += GskTexture(u_source, vUv - p) * incrementalGaussian.x;
+ sum += GskTexture(u_source, vUv + p) * incrementalGaussian.x;
+
+ coefficientSum += 2.0 * incrementalGaussian.x;
+ incrementalGaussian.xy *= incrementalGaussian.yz;
+
+ p += pixel_step;
+ }
+
+ gskSetOutputColor(sum / coefficientSum);
+}
+
diff --git a/shaders/gtk4/3.shader_test b/shaders/gtk4/3.shader_test
new file mode 100644
index 0000000..60736a5
--- /dev/null
+++ b/shaders/gtk4/3.shader_test
@@ -0,0 +1,672 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// blend.glsl
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ vUv = vec2(aUv.x, aUv.y);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// blend.glsl
+
+uniform int u_mode;
+uniform sampler2D u_source2;
+
+float
+combine (float source, float backdrop)
+{
+ return source + backdrop * (1.0 - source);
+}
+
+vec4
+composite (vec4 Cs, vec4 Cb, vec3 B)
+{
+ float ao = Cs.a + Cb.a * (1.0 - Cs.a);
+ vec3 Co = (Cs.a*(1.0 - Cb.a)*Cs.rgb + Cs.a*Cb.a*B + (1.0 - Cs.a)*Cb.a*Cb.rgb) / ao;
+ return vec4(Co, ao);
+}
+
+vec4
+normal (vec4 Cs, vec4 Cb)
+{
+ return composite (Cs, Cb, Cs.rgb);
+}
+
+vec4
+multiply (vec4 Cs, vec4 Cb)
+{
+ return composite (Cs, Cb, Cs.rgb * Cb.rgb);
+}
+
+vec4
+difference (vec4 Cs, vec4 Cb)
+{
+ return composite (Cs, Cb, abs(Cs.rgb - Cb.rgb));
+}
+
+vec4
+screen (vec4 Cs, vec4 Cb)
+{
+ return composite (Cs, Cb, Cs.rgb + Cb.rgb - Cs.rgb * Cb.rgb);
+}
+
+float
+hard_light (float source, float backdrop)
+{
+ if (source <= 0.5)
+ return 2.0 * backdrop * source;
+ else
+ return 2.0 * (backdrop + source - backdrop * source) - 1.0;
+}
+
+vec4
+hard_light (vec4 Cs, vec4 Cb)
+{
+ vec3 B = vec3 (hard_light (Cs.r, Cb.r),
+ hard_light (Cs.g, Cb.g),
+ hard_light (Cs.b, Cb.b));
+ return composite (Cs, Cb, B);
+}
+
+float
+soft_light (float source, float backdrop)
+{
+ float db;
+
+ if (backdrop <= 0.25)
+ db = ((16.0 * backdrop - 12.0) * backdrop + 4.0) * backdrop;
+ else
+ db = sqrt (backdrop);
+
+ if (source <= 0.5)
+ return backdrop - (1.0 - 2.0 * source) * backdrop * (1.0 - backdrop);
+ else
+ return backdrop + (2.0 * source - 1.0) * (db - backdrop);
+}
+
+vec4
+soft_light (vec4 Cs, vec4 Cb)
+{
+ vec3 B = vec3 (soft_light (Cs.r, Cb.r),
+ soft_light (Cs.g, Cb.g),
+ soft_light (Cs.b, Cb.b));
+ return composite (Cs, Cb, B);
+}
+
+vec4
+overlay (vec4 Cs, vec4 Cb)
+{
+ vec3 B = vec3 (hard_light (Cb.r, Cs.r),
+ hard_light (Cb.g, Cs.g),
+ hard_light (Cb.b, Cs.b));
+ return composite (Cs, Cb, B);
+}
+
+vec4
+darken (vec4 Cs, vec4 Cb)
+{
+ vec3 B = min (Cs.rgb, Cb.rgb);
+ return composite (Cs, Cb, B);
+}
+
+vec4
+lighten (vec4 Cs, vec4 Cb)
+{
+ vec3 B = max (Cs.rgb, Cb.rgb);
+ return composite (Cs, Cb, B);
+}
+
+float
+color_dodge (float source, float backdrop)
+{
+ return (source == 1.0) ? source : min (backdrop / (1.0 - source), 1.0);
+}
+
+vec4
+color_dodge (vec4 Cs, vec4 Cb)
+{
+ vec3 B = vec3 (color_dodge (Cs.r, Cb.r),
+ color_dodge (Cs.g, Cb.g),
+ color_dodge (Cs.b, Cb.b));
+ return composite (Cs, Cb, B);
+}
+
+
+float
+color_burn (float source, float backdrop)
+{
+ return (source == 0.0) ? source : max ((1.0 - ((1.0 - backdrop) / source)), 0.0);
+}
+
+vec4
+color_burn (vec4 Cs, vec4 Cb)
+{
+ vec3 B = vec3 (color_burn (Cs.r, Cb.r),
+ color_burn (Cs.g, Cb.g),
+ color_burn (Cs.b, Cb.b));
+ return composite (Cs, Cb, B);
+}
+
+vec4
+exclusion (vec4 Cs, vec4 Cb)
+{
+ vec3 B = Cb.rgb + Cs.rgb - 2.0 * Cb.rgb * Cs.rgb;
+ return composite (Cs, Cb, B);
+}
+
+float
+lum (vec3 c)
+{
+ return 0.3 * c.r + 0.59 * c.g + 0.11 * c.b;
+}
+
+vec3
+clip_color (vec3 c)
+{
+ float l = lum (c);
+ float n = min (c.r, min (c.g, c.b));
+ float x = max (c.r, max (c.g, c.b));
+ if (n < 0.0) c = l + (((c - l) * l) / (l - n));
+ if (x > 1.0) c = l + (((c - l) * (1.0 - l)) / (x - l));
+ return c;
+}
+
+vec3
+set_lum (vec3 c, float l)
+{
+ float d = l - lum (c);
+ return clip_color (vec3 (c.r + d, c.g + d, c.b + d));
+}
+
+float
+sat (vec3 c)
+{
+ return max (c.r, max (c.g, c.b)) - min (c.r, min (c.g, c.b));
+}
+
+vec3
+set_sat (vec3 c, float s)
+{
+ float cmin = min (c.r, min (c.g, c.b));
+ float cmax = max (c.r, max (c.g, c.b));
+ vec3 res;
+
+ if (cmax == cmin)
+ res = vec3 (0, 0, 0);
+ else
+ {
+ if (c.r == cmax)
+ {
+ if (c.g == cmin)
+ {
+ res.b = ((c.b - cmin) * s) / (cmax - cmin);
+ res.g = 0.0;
+ }
+ else
+ {
+ res.g = ((c.g - cmin) * s) / (cmax - cmin);
+ res.b = 0.0;
+ }
+ res.r = s;
+ }
+ else if (c.g == cmax)
+ {
+ if (c.r == cmin)
+ {
+ res.b = ((c.b - cmin) * s) / (cmax - cmin);
+ res.r = 0.0;
+ }
+ else
+ {
+ res.r = ((c.r - cmin) * s) / (cmax - cmin);
+ res.b = 0.0;
+ }
+ res.g = s;
+ }
+ else
+ {
+ if (c.r == cmin)
+ {
+ res.g = ((c.g - cmin) * s) / (cmax - cmin);
+ res.r = 0.0;
+ }
+ else
+ {
+ res.r = ((c.r - cmin) * s) / (cmax - cmin);
+ res.g = 0.0;
+ }
+ res.b = s;
+ }
+ }
+ return res;
+}
+
+vec4
+color (vec4 Cs, vec4 Cb)
+{
+ vec3 B = set_lum (Cs.rgb, lum (Cb.rgb));
+ return composite (Cs, Cb, B);
+}
+
+vec4
+hue (vec4 Cs, vec4 Cb)
+{
+ vec3 B = set_lum (set_sat (Cs.rgb, sat (Cb.rgb)), lum (Cb.rgb));
+ return composite (Cs, Cb, B);
+}
+
+vec4
+saturation (vec4 Cs, vec4 Cb)
+{
+ vec3 B = set_lum (set_sat (Cb.rgb, sat (Cs.rgb)), lum (Cb.rgb));
+ return composite (Cs, Cb, B);
+}
+
+vec4
+luminosity (vec4 Cs, vec4 Cb)
+{
+ vec3 B = set_lum (Cb.rgb, lum (Cs.rgb));
+ return composite (Cs, Cb, B);
+}
+
+void main() {
+ vec4 bottom_color = GskTexture(u_source, vUv);
+ vec4 top_color = GskTexture(u_source2, vUv);
+
+ vec4 result;
+ if (u_mode == 0)
+ result = normal(top_color, bottom_color);
+ else if (u_mode == 1)
+ result = multiply(top_color, bottom_color);
+ else if (u_mode == 2)
+ result = screen(top_color, bottom_color);
+ else if (u_mode == 3)
+ result = overlay(top_color, bottom_color);
+ else if (u_mode == 4)
+ result = darken(top_color, bottom_color);
+ else if (u_mode == 5)
+ result = lighten(top_color, bottom_color);
+ else if (u_mode == 6)
+ result = color_dodge(top_color, bottom_color);
+ else if (u_mode == 7)
+ result = color_burn(top_color, bottom_color);
+ else if (u_mode == 8)
+ result = hard_light(top_color, bottom_color);
+ else if (u_mode == 9)
+ result = soft_light(top_color, bottom_color);
+ else if (u_mode == 10)
+ result = difference(top_color, bottom_color);
+ else if (u_mode == 11)
+ result = exclusion(top_color, bottom_color);
+ else if (u_mode == 12)
+ result = color(top_color, bottom_color);
+ else if (u_mode == 13)
+ result = hue(top_color, bottom_color);
+ else if (u_mode == 14)
+ result = saturation(top_color, bottom_color);
+ else if (u_mode == 15)
+ result = luminosity(top_color, bottom_color);
+ else
+ discard;
+
+ gskSetScaledOutputColor(result, u_alpha);
+}
+
diff --git a/shaders/gtk4/30.shader_test b/shaders/gtk4/30.shader_test
new file mode 100644
index 0000000..77f5b88
--- /dev/null
+++ b/shaders/gtk4/30.shader_test
@@ -0,0 +1,401 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// border.glsl
+
+uniform vec4 u_widths;
+uniform vec4[3] u_outline_rect;
+
+_OUT_ vec4 final_color;
+_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
+_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ final_color = gsk_scaled_premultiply(aColor, u_alpha);
+
+ GskRoundedRect outside = gsk_create_rect(u_outline_rect);
+ GskRoundedRect inside = gsk_rounded_rect_shrink (outside, u_widths);
+
+ gsk_rounded_rect_transform(outside, u_modelview);
+ gsk_rounded_rect_transform(inside, u_modelview);
+
+ gsk_rounded_rect_encode(outside, transformed_outside_outline);
+ gsk_rounded_rect_encode(inside, transformed_inside_outline);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// border.glsl
+
+uniform vec4[3] u_outline_rect;
+
+_IN_ vec4 final_color;
+_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
+_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
+
+void main() {
+ vec2 frag = gsk_get_frag_coord();
+
+ float alpha = clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outside_outline), frag) -
+ gsk_rounded_rect_coverage(gsk_decode_rect(transformed_inside_outline), frag),
+ 0.0, 1.0);
+
+ gskSetScaledOutputColor(final_color, alpha);
+}
+
diff --git a/shaders/gtk4/33.shader_test b/shaders/gtk4/33.shader_test
new file mode 100644
index 0000000..85bf20b
--- /dev/null
+++ b/shaders/gtk4/33.shader_test
@@ -0,0 +1,401 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// border.glsl
+
+uniform vec4 u_widths;
+uniform vec4[3] u_outline_rect;
+
+_OUT_ vec4 final_color;
+_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
+_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ final_color = gsk_scaled_premultiply(aColor, u_alpha);
+
+ GskRoundedRect outside = gsk_create_rect(u_outline_rect);
+ GskRoundedRect inside = gsk_rounded_rect_shrink (outside, u_widths);
+
+ gsk_rounded_rect_transform(outside, u_modelview);
+ gsk_rounded_rect_transform(inside, u_modelview);
+
+ gsk_rounded_rect_encode(outside, transformed_outside_outline);
+ gsk_rounded_rect_encode(inside, transformed_inside_outline);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// border.glsl
+
+uniform vec4[3] u_outline_rect;
+
+_IN_ vec4 final_color;
+_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
+_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
+
+void main() {
+ vec2 frag = gsk_get_frag_coord();
+
+ float alpha = clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outside_outline), frag) -
+ gsk_rounded_rect_coverage(gsk_decode_rect(transformed_inside_outline), frag),
+ 0.0, 1.0);
+
+ gskSetScaledOutputColor(final_color, alpha);
+}
+
diff --git a/shaders/gtk4/36.shader_test b/shaders/gtk4/36.shader_test
new file mode 100644
index 0000000..a878b1c
--- /dev/null
+++ b/shaders/gtk4/36.shader_test
@@ -0,0 +1,399 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// border.glsl
+
+uniform vec4 u_widths;
+uniform vec4[3] u_outline_rect;
+
+_OUT_ vec4 final_color;
+_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
+_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ final_color = gsk_scaled_premultiply(aColor, u_alpha);
+
+ GskRoundedRect outside = gsk_create_rect(u_outline_rect);
+ GskRoundedRect inside = gsk_rounded_rect_shrink (outside, u_widths);
+
+ gsk_rounded_rect_transform(outside, u_modelview);
+ gsk_rounded_rect_transform(inside, u_modelview);
+
+ gsk_rounded_rect_encode(outside, transformed_outside_outline);
+ gsk_rounded_rect_encode(inside, transformed_inside_outline);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// border.glsl
+
+uniform vec4[3] u_outline_rect;
+
+_IN_ vec4 final_color;
+_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
+_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
+
+void main() {
+ vec2 frag = gsk_get_frag_coord();
+
+ float alpha = clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outside_outline), frag) -
+ gsk_rounded_rect_coverage(gsk_decode_rect(transformed_inside_outline), frag),
+ 0.0, 1.0);
+
+ gskSetScaledOutputColor(final_color, alpha);
+}
+
diff --git a/shaders/gtk4/39.shader_test b/shaders/gtk4/39.shader_test
new file mode 100644
index 0000000..b28d6e1
--- /dev/null
+++ b/shaders/gtk4/39.shader_test
@@ -0,0 +1,378 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// color.glsl
+
+_OUT_ vec4 final_color;
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ final_color = gsk_scaled_premultiply(aColor, u_alpha);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// color.glsl
+
+_IN_ vec4 final_color;
+
+void main() {
+ gskSetOutputColor(final_color);
+}
+
+
diff --git a/shaders/gtk4/42.shader_test b/shaders/gtk4/42.shader_test
new file mode 100644
index 0000000..68074ec
--- /dev/null
+++ b/shaders/gtk4/42.shader_test
@@ -0,0 +1,378 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// color.glsl
+
+_OUT_ vec4 final_color;
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ final_color = gsk_scaled_premultiply(aColor, u_alpha);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// color.glsl
+
+_IN_ vec4 final_color;
+
+void main() {
+ gskSetOutputColor(final_color);
+}
+
+
diff --git a/shaders/gtk4/45.shader_test b/shaders/gtk4/45.shader_test
new file mode 100644
index 0000000..6d75cdf
--- /dev/null
+++ b/shaders/gtk4/45.shader_test
@@ -0,0 +1,376 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// color.glsl
+
+_OUT_ vec4 final_color;
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ final_color = gsk_scaled_premultiply(aColor, u_alpha);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// color.glsl
+
+_IN_ vec4 final_color;
+
+void main() {
+ gskSetOutputColor(final_color);
+}
+
+
diff --git a/shaders/gtk4/48.shader_test b/shaders/gtk4/48.shader_test
new file mode 100644
index 0000000..fa63ae3
--- /dev/null
+++ b/shaders/gtk4/48.shader_test
@@ -0,0 +1,391 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// coloring.glsl
+
+_OUT_ vec4 final_color;
+_OUT_ float use_color;
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ vUv = vec2(aUv.x, aUv.y);
+
+ // We use this shader for both plain glyphs (used as mask)
+ // and color glpyhs (used as source). The renderer sets
+ // aColor to vec4(-1) for color glyhs.
+ if (distance(aColor,vec4(-1)) < 0.1)
+ use_color = 0.0;
+ else
+ use_color = 1.0;
+
+ final_color = gsk_scaled_premultiply(aColor, u_alpha);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// coloring.glsl
+
+_IN_ vec4 final_color;
+_IN_ float use_color;
+
+void main() {
+ vec4 diffuse = GskTexture(u_source, vUv);
+
+ gskSetOutputColor(mix(diffuse * u_alpha, final_color * diffuse.a, use_color));
+}
+
diff --git a/shaders/gtk4/51.shader_test b/shaders/gtk4/51.shader_test
new file mode 100644
index 0000000..58b3a04
--- /dev/null
+++ b/shaders/gtk4/51.shader_test
@@ -0,0 +1,391 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// coloring.glsl
+
+_OUT_ vec4 final_color;
+_OUT_ float use_color;
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ vUv = vec2(aUv.x, aUv.y);
+
+ // We use this shader for both plain glyphs (used as mask)
+ // and color glpyhs (used as source). The renderer sets
+ // aColor to vec4(-1) for color glyhs.
+ if (distance(aColor,vec4(-1)) < 0.1)
+ use_color = 0.0;
+ else
+ use_color = 1.0;
+
+ final_color = gsk_scaled_premultiply(aColor, u_alpha);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// coloring.glsl
+
+_IN_ vec4 final_color;
+_IN_ float use_color;
+
+void main() {
+ vec4 diffuse = GskTexture(u_source, vUv);
+
+ gskSetOutputColor(mix(diffuse * u_alpha, final_color * diffuse.a, use_color));
+}
+
diff --git a/shaders/gtk4/54.shader_test b/shaders/gtk4/54.shader_test
new file mode 100644
index 0000000..c62fc1c
--- /dev/null
+++ b/shaders/gtk4/54.shader_test
@@ -0,0 +1,389 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// coloring.glsl
+
+_OUT_ vec4 final_color;
+_OUT_ float use_color;
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ vUv = vec2(aUv.x, aUv.y);
+
+ // We use this shader for both plain glyphs (used as mask)
+ // and color glpyhs (used as source). The renderer sets
+ // aColor to vec4(-1) for color glyhs.
+ if (distance(aColor,vec4(-1)) < 0.1)
+ use_color = 0.0;
+ else
+ use_color = 1.0;
+
+ final_color = gsk_scaled_premultiply(aColor, u_alpha);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// coloring.glsl
+
+_IN_ vec4 final_color;
+_IN_ float use_color;
+
+void main() {
+ vec4 diffuse = GskTexture(u_source, vUv);
+
+ gskSetOutputColor(mix(diffuse * u_alpha, final_color * diffuse.a, use_color));
+}
+
diff --git a/shaders/gtk4/57.shader_test b/shaders/gtk4/57.shader_test
new file mode 100644
index 0000000..241bdf0
--- /dev/null
+++ b/shaders/gtk4/57.shader_test
@@ -0,0 +1,385 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// color_matrix.glsl
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ vUv = vec2(aUv.x, aUv.y);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// color_matrix.glsl
+
+uniform mat4 u_color_matrix;
+uniform vec4 u_color_offset;
+
+void main() {
+ vec4 color = GskTexture(u_source, vUv);
+
+ // Un-premultilpy
+ if (color.a != 0.0)
+ color.rgb /= color.a;
+
+ color = u_color_matrix * color + u_color_offset;
+ color = clamp(color, 0.0, 1.0);
+
+ gskSetOutputColor(gsk_scaled_premultiply(color, u_alpha));
+}
+
diff --git a/shaders/gtk4/6.shader_test b/shaders/gtk4/6.shader_test
new file mode 100644
index 0000000..16287d8
--- /dev/null
+++ b/shaders/gtk4/6.shader_test
@@ -0,0 +1,672 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// blend.glsl
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ vUv = vec2(aUv.x, aUv.y);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// blend.glsl
+
+uniform int u_mode;
+uniform sampler2D u_source2;
+
+float
+combine (float source, float backdrop)
+{
+ return source + backdrop * (1.0 - source);
+}
+
+vec4
+composite (vec4 Cs, vec4 Cb, vec3 B)
+{
+ float ao = Cs.a + Cb.a * (1.0 - Cs.a);
+ vec3 Co = (Cs.a*(1.0 - Cb.a)*Cs.rgb + Cs.a*Cb.a*B + (1.0 - Cs.a)*Cb.a*Cb.rgb) / ao;
+ return vec4(Co, ao);
+}
+
+vec4
+normal (vec4 Cs, vec4 Cb)
+{
+ return composite (Cs, Cb, Cs.rgb);
+}
+
+vec4
+multiply (vec4 Cs, vec4 Cb)
+{
+ return composite (Cs, Cb, Cs.rgb * Cb.rgb);
+}
+
+vec4
+difference (vec4 Cs, vec4 Cb)
+{
+ return composite (Cs, Cb, abs(Cs.rgb - Cb.rgb));
+}
+
+vec4
+screen (vec4 Cs, vec4 Cb)
+{
+ return composite (Cs, Cb, Cs.rgb + Cb.rgb - Cs.rgb * Cb.rgb);
+}
+
+float
+hard_light (float source, float backdrop)
+{
+ if (source <= 0.5)
+ return 2.0 * backdrop * source;
+ else
+ return 2.0 * (backdrop + source - backdrop * source) - 1.0;
+}
+
+vec4
+hard_light (vec4 Cs, vec4 Cb)
+{
+ vec3 B = vec3 (hard_light (Cs.r, Cb.r),
+ hard_light (Cs.g, Cb.g),
+ hard_light (Cs.b, Cb.b));
+ return composite (Cs, Cb, B);
+}
+
+float
+soft_light (float source, float backdrop)
+{
+ float db;
+
+ if (backdrop <= 0.25)
+ db = ((16.0 * backdrop - 12.0) * backdrop + 4.0) * backdrop;
+ else
+ db = sqrt (backdrop);
+
+ if (source <= 0.5)
+ return backdrop - (1.0 - 2.0 * source) * backdrop * (1.0 - backdrop);
+ else
+ return backdrop + (2.0 * source - 1.0) * (db - backdrop);
+}
+
+vec4
+soft_light (vec4 Cs, vec4 Cb)
+{
+ vec3 B = vec3 (soft_light (Cs.r, Cb.r),
+ soft_light (Cs.g, Cb.g),
+ soft_light (Cs.b, Cb.b));
+ return composite (Cs, Cb, B);
+}
+
+vec4
+overlay (vec4 Cs, vec4 Cb)
+{
+ vec3 B = vec3 (hard_light (Cb.r, Cs.r),
+ hard_light (Cb.g, Cs.g),
+ hard_light (Cb.b, Cs.b));
+ return composite (Cs, Cb, B);
+}
+
+vec4
+darken (vec4 Cs, vec4 Cb)
+{
+ vec3 B = min (Cs.rgb, Cb.rgb);
+ return composite (Cs, Cb, B);
+}
+
+vec4
+lighten (vec4 Cs, vec4 Cb)
+{
+ vec3 B = max (Cs.rgb, Cb.rgb);
+ return composite (Cs, Cb, B);
+}
+
+float
+color_dodge (float source, float backdrop)
+{
+ return (source == 1.0) ? source : min (backdrop / (1.0 - source), 1.0);
+}
+
+vec4
+color_dodge (vec4 Cs, vec4 Cb)
+{
+ vec3 B = vec3 (color_dodge (Cs.r, Cb.r),
+ color_dodge (Cs.g, Cb.g),
+ color_dodge (Cs.b, Cb.b));
+ return composite (Cs, Cb, B);
+}
+
+
+float
+color_burn (float source, float backdrop)
+{
+ return (source == 0.0) ? source : max ((1.0 - ((1.0 - backdrop) / source)), 0.0);
+}
+
+vec4
+color_burn (vec4 Cs, vec4 Cb)
+{
+ vec3 B = vec3 (color_burn (Cs.r, Cb.r),
+ color_burn (Cs.g, Cb.g),
+ color_burn (Cs.b, Cb.b));
+ return composite (Cs, Cb, B);
+}
+
+vec4
+exclusion (vec4 Cs, vec4 Cb)
+{
+ vec3 B = Cb.rgb + Cs.rgb - 2.0 * Cb.rgb * Cs.rgb;
+ return composite (Cs, Cb, B);
+}
+
+float
+lum (vec3 c)
+{
+ return 0.3 * c.r + 0.59 * c.g + 0.11 * c.b;
+}
+
+vec3
+clip_color (vec3 c)
+{
+ float l = lum (c);
+ float n = min (c.r, min (c.g, c.b));
+ float x = max (c.r, max (c.g, c.b));
+ if (n < 0.0) c = l + (((c - l) * l) / (l - n));
+ if (x > 1.0) c = l + (((c - l) * (1.0 - l)) / (x - l));
+ return c;
+}
+
+vec3
+set_lum (vec3 c, float l)
+{
+ float d = l - lum (c);
+ return clip_color (vec3 (c.r + d, c.g + d, c.b + d));
+}
+
+float
+sat (vec3 c)
+{
+ return max (c.r, max (c.g, c.b)) - min (c.r, min (c.g, c.b));
+}
+
+vec3
+set_sat (vec3 c, float s)
+{
+ float cmin = min (c.r, min (c.g, c.b));
+ float cmax = max (c.r, max (c.g, c.b));
+ vec3 res;
+
+ if (cmax == cmin)
+ res = vec3 (0, 0, 0);
+ else
+ {
+ if (c.r == cmax)
+ {
+ if (c.g == cmin)
+ {
+ res.b = ((c.b - cmin) * s) / (cmax - cmin);
+ res.g = 0.0;
+ }
+ else
+ {
+ res.g = ((c.g - cmin) * s) / (cmax - cmin);
+ res.b = 0.0;
+ }
+ res.r = s;
+ }
+ else if (c.g == cmax)
+ {
+ if (c.r == cmin)
+ {
+ res.b = ((c.b - cmin) * s) / (cmax - cmin);
+ res.r = 0.0;
+ }
+ else
+ {
+ res.r = ((c.r - cmin) * s) / (cmax - cmin);
+ res.b = 0.0;
+ }
+ res.g = s;
+ }
+ else
+ {
+ if (c.r == cmin)
+ {
+ res.g = ((c.g - cmin) * s) / (cmax - cmin);
+ res.r = 0.0;
+ }
+ else
+ {
+ res.r = ((c.r - cmin) * s) / (cmax - cmin);
+ res.g = 0.0;
+ }
+ res.b = s;
+ }
+ }
+ return res;
+}
+
+vec4
+color (vec4 Cs, vec4 Cb)
+{
+ vec3 B = set_lum (Cs.rgb, lum (Cb.rgb));
+ return composite (Cs, Cb, B);
+}
+
+vec4
+hue (vec4 Cs, vec4 Cb)
+{
+ vec3 B = set_lum (set_sat (Cs.rgb, sat (Cb.rgb)), lum (Cb.rgb));
+ return composite (Cs, Cb, B);
+}
+
+vec4
+saturation (vec4 Cs, vec4 Cb)
+{
+ vec3 B = set_lum (set_sat (Cb.rgb, sat (Cs.rgb)), lum (Cb.rgb));
+ return composite (Cs, Cb, B);
+}
+
+vec4
+luminosity (vec4 Cs, vec4 Cb)
+{
+ vec3 B = set_lum (Cb.rgb, lum (Cs.rgb));
+ return composite (Cs, Cb, B);
+}
+
+void main() {
+ vec4 bottom_color = GskTexture(u_source, vUv);
+ vec4 top_color = GskTexture(u_source2, vUv);
+
+ vec4 result;
+ if (u_mode == 0)
+ result = normal(top_color, bottom_color);
+ else if (u_mode == 1)
+ result = multiply(top_color, bottom_color);
+ else if (u_mode == 2)
+ result = screen(top_color, bottom_color);
+ else if (u_mode == 3)
+ result = overlay(top_color, bottom_color);
+ else if (u_mode == 4)
+ result = darken(top_color, bottom_color);
+ else if (u_mode == 5)
+ result = lighten(top_color, bottom_color);
+ else if (u_mode == 6)
+ result = color_dodge(top_color, bottom_color);
+ else if (u_mode == 7)
+ result = color_burn(top_color, bottom_color);
+ else if (u_mode == 8)
+ result = hard_light(top_color, bottom_color);
+ else if (u_mode == 9)
+ result = soft_light(top_color, bottom_color);
+ else if (u_mode == 10)
+ result = difference(top_color, bottom_color);
+ else if (u_mode == 11)
+ result = exclusion(top_color, bottom_color);
+ else if (u_mode == 12)
+ result = color(top_color, bottom_color);
+ else if (u_mode == 13)
+ result = hue(top_color, bottom_color);
+ else if (u_mode == 14)
+ result = saturation(top_color, bottom_color);
+ else if (u_mode == 15)
+ result = luminosity(top_color, bottom_color);
+ else
+ discard;
+
+ gskSetScaledOutputColor(result, u_alpha);
+}
+
diff --git a/shaders/gtk4/60.shader_test b/shaders/gtk4/60.shader_test
new file mode 100644
index 0000000..851bdfd
--- /dev/null
+++ b/shaders/gtk4/60.shader_test
@@ -0,0 +1,385 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// color_matrix.glsl
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ vUv = vec2(aUv.x, aUv.y);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// color_matrix.glsl
+
+uniform mat4 u_color_matrix;
+uniform vec4 u_color_offset;
+
+void main() {
+ vec4 color = GskTexture(u_source, vUv);
+
+ // Un-premultilpy
+ if (color.a != 0.0)
+ color.rgb /= color.a;
+
+ color = u_color_matrix * color + u_color_offset;
+ color = clamp(color, 0.0, 1.0);
+
+ gskSetOutputColor(gsk_scaled_premultiply(color, u_alpha));
+}
+
diff --git a/shaders/gtk4/63.shader_test b/shaders/gtk4/63.shader_test
new file mode 100644
index 0000000..346ec78
--- /dev/null
+++ b/shaders/gtk4/63.shader_test
@@ -0,0 +1,383 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// color_matrix.glsl
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ vUv = vec2(aUv.x, aUv.y);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// color_matrix.glsl
+
+uniform mat4 u_color_matrix;
+uniform vec4 u_color_offset;
+
+void main() {
+ vec4 color = GskTexture(u_source, vUv);
+
+ // Un-premultilpy
+ if (color.a != 0.0)
+ color.rgb /= color.a;
+
+ color = u_color_matrix * color + u_color_offset;
+ color = clamp(color, 0.0, 1.0);
+
+ gskSetOutputColor(gsk_scaled_premultiply(color, u_alpha));
+}
+
diff --git a/shaders/gtk4/66.shader_test b/shaders/gtk4/66.shader_test
new file mode 100644
index 0000000..71cbdaf
--- /dev/null
+++ b/shaders/gtk4/66.shader_test
@@ -0,0 +1,443 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// conic_gradient.glsl
+
+uniform vec4 u_geometry;
+
+_OUT_ vec2 coord;
+
+void main() {
+ gl_Position = u_projection * (u_modelview * vec4(aPosition, 0.0, 1.0));
+
+ vec2 mv0 = u_modelview[0].xy;
+ vec2 mv1 = u_modelview[1].xy;
+ vec2 offset = aPosition - u_geometry.xy;
+
+ coord = vec2(dot(mv0, offset), dot(mv1, offset));
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// conic_gradient.glsl
+
+#define MAX_COLOR_STOPS 6
+
+#ifdef GSK_LEGACY
+uniform int u_num_color_stops;
+#else
+uniform highp int u_num_color_stops; // Why? Because it works like this.
+#endif
+
+uniform vec4 u_geometry;
+uniform float u_color_stops[MAX_COLOR_STOPS * 5];
+
+_IN_ vec2 coord;
+
+float get_offset(int index) {
+ // u_color_stops[5 * index] makes Intel Windows driver crash.
+ // See https://gitlab.gnome.org/GNOME/gtk/-/issues/3783
+ int base = 5 * index;
+ return u_color_stops[base];
+}
+
+vec4 get_color(int index) {
+ int base = 5 * index + 1;
+
+ return vec4(u_color_stops[base],
+ u_color_stops[base + 1],
+ u_color_stops[base + 2],
+ u_color_stops[base + 3]);
+}
+
+void main() {
+ // direction of point in range [-PI, PI]
+ vec2 pos = floor(coord);
+ float angle = atan(pos.y, pos.x);
+
+ // fract() does the modulo here, so now we have progress
+ // into the current conic
+ float offset = fract(angle * u_geometry.z + u_geometry.w);
+ float curr_offset;
+ float next_offset;
+
+ next_offset = get_offset(0);
+ if (offset < next_offset) {
+ gskSetOutputColor(gsk_scaled_premultiply(get_color(0), u_alpha));
+ return;
+ }
+
+ if (offset >= get_offset(u_num_color_stops - 1)) {
+ gskSetOutputColor(gsk_scaled_premultiply(get_color(u_num_color_stops - 1), u_alpha));
+ return;
+ }
+
+ for (int i = 0; i < MAX_COLOR_STOPS; i++) {
+ curr_offset = next_offset;
+ next_offset = get_offset(i + 1);
+
+ if (offset < next_offset) {
+ float f = (offset - curr_offset) / (next_offset - curr_offset);
+ vec4 curr_color = gsk_premultiply(get_color(i));
+ vec4 next_color = gsk_premultiply(get_color(i + 1));
+ vec4 color = mix(curr_color, next_color, f);
+ gskSetScaledOutputColor(color, u_alpha);
+ return;
+ }
+ }
+}
+
diff --git a/shaders/gtk4/69.shader_test b/shaders/gtk4/69.shader_test
new file mode 100644
index 0000000..bad2c31
--- /dev/null
+++ b/shaders/gtk4/69.shader_test
@@ -0,0 +1,443 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// conic_gradient.glsl
+
+uniform vec4 u_geometry;
+
+_OUT_ vec2 coord;
+
+void main() {
+ gl_Position = u_projection * (u_modelview * vec4(aPosition, 0.0, 1.0));
+
+ vec2 mv0 = u_modelview[0].xy;
+ vec2 mv1 = u_modelview[1].xy;
+ vec2 offset = aPosition - u_geometry.xy;
+
+ coord = vec2(dot(mv0, offset), dot(mv1, offset));
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// conic_gradient.glsl
+
+#define MAX_COLOR_STOPS 6
+
+#ifdef GSK_LEGACY
+uniform int u_num_color_stops;
+#else
+uniform highp int u_num_color_stops; // Why? Because it works like this.
+#endif
+
+uniform vec4 u_geometry;
+uniform float u_color_stops[MAX_COLOR_STOPS * 5];
+
+_IN_ vec2 coord;
+
+float get_offset(int index) {
+ // u_color_stops[5 * index] makes Intel Windows driver crash.
+ // See https://gitlab.gnome.org/GNOME/gtk/-/issues/3783
+ int base = 5 * index;
+ return u_color_stops[base];
+}
+
+vec4 get_color(int index) {
+ int base = 5 * index + 1;
+
+ return vec4(u_color_stops[base],
+ u_color_stops[base + 1],
+ u_color_stops[base + 2],
+ u_color_stops[base + 3]);
+}
+
+void main() {
+ // direction of point in range [-PI, PI]
+ vec2 pos = floor(coord);
+ float angle = atan(pos.y, pos.x);
+
+ // fract() does the modulo here, so now we have progress
+ // into the current conic
+ float offset = fract(angle * u_geometry.z + u_geometry.w);
+ float curr_offset;
+ float next_offset;
+
+ next_offset = get_offset(0);
+ if (offset < next_offset) {
+ gskSetOutputColor(gsk_scaled_premultiply(get_color(0), u_alpha));
+ return;
+ }
+
+ if (offset >= get_offset(u_num_color_stops - 1)) {
+ gskSetOutputColor(gsk_scaled_premultiply(get_color(u_num_color_stops - 1), u_alpha));
+ return;
+ }
+
+ for (int i = 0; i < MAX_COLOR_STOPS; i++) {
+ curr_offset = next_offset;
+ next_offset = get_offset(i + 1);
+
+ if (offset < next_offset) {
+ float f = (offset - curr_offset) / (next_offset - curr_offset);
+ vec4 curr_color = gsk_premultiply(get_color(i));
+ vec4 next_color = gsk_premultiply(get_color(i + 1));
+ vec4 color = mix(curr_color, next_color, f);
+ gskSetScaledOutputColor(color, u_alpha);
+ return;
+ }
+ }
+}
+
diff --git a/shaders/gtk4/72.shader_test b/shaders/gtk4/72.shader_test
new file mode 100644
index 0000000..a0a382d
--- /dev/null
+++ b/shaders/gtk4/72.shader_test
@@ -0,0 +1,441 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// conic_gradient.glsl
+
+uniform vec4 u_geometry;
+
+_OUT_ vec2 coord;
+
+void main() {
+ gl_Position = u_projection * (u_modelview * vec4(aPosition, 0.0, 1.0));
+
+ vec2 mv0 = u_modelview[0].xy;
+ vec2 mv1 = u_modelview[1].xy;
+ vec2 offset = aPosition - u_geometry.xy;
+
+ coord = vec2(dot(mv0, offset), dot(mv1, offset));
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// conic_gradient.glsl
+
+#define MAX_COLOR_STOPS 6
+
+#ifdef GSK_LEGACY
+uniform int u_num_color_stops;
+#else
+uniform highp int u_num_color_stops; // Why? Because it works like this.
+#endif
+
+uniform vec4 u_geometry;
+uniform float u_color_stops[MAX_COLOR_STOPS * 5];
+
+_IN_ vec2 coord;
+
+float get_offset(int index) {
+ // u_color_stops[5 * index] makes Intel Windows driver crash.
+ // See https://gitlab.gnome.org/GNOME/gtk/-/issues/3783
+ int base = 5 * index;
+ return u_color_stops[base];
+}
+
+vec4 get_color(int index) {
+ int base = 5 * index + 1;
+
+ return vec4(u_color_stops[base],
+ u_color_stops[base + 1],
+ u_color_stops[base + 2],
+ u_color_stops[base + 3]);
+}
+
+void main() {
+ // direction of point in range [-PI, PI]
+ vec2 pos = floor(coord);
+ float angle = atan(pos.y, pos.x);
+
+ // fract() does the modulo here, so now we have progress
+ // into the current conic
+ float offset = fract(angle * u_geometry.z + u_geometry.w);
+ float curr_offset;
+ float next_offset;
+
+ next_offset = get_offset(0);
+ if (offset < next_offset) {
+ gskSetOutputColor(gsk_scaled_premultiply(get_color(0), u_alpha));
+ return;
+ }
+
+ if (offset >= get_offset(u_num_color_stops - 1)) {
+ gskSetOutputColor(gsk_scaled_premultiply(get_color(u_num_color_stops - 1), u_alpha));
+ return;
+ }
+
+ for (int i = 0; i < MAX_COLOR_STOPS; i++) {
+ curr_offset = next_offset;
+ next_offset = get_offset(i + 1);
+
+ if (offset < next_offset) {
+ float f = (offset - curr_offset) / (next_offset - curr_offset);
+ vec4 curr_color = gsk_premultiply(get_color(i));
+ vec4 next_color = gsk_premultiply(get_color(i + 1));
+ vec4 color = mix(curr_color, next_color, f);
+ gskSetScaledOutputColor(color, u_alpha);
+ return;
+ }
+ }
+}
+
diff --git a/shaders/gtk4/75.shader_test b/shaders/gtk4/75.shader_test
new file mode 100644
index 0000000..d1529bc
--- /dev/null
+++ b/shaders/gtk4/75.shader_test
@@ -0,0 +1,382 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// cross_fade.glsl
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ vUv = vec2(aUv.x, aUv.y);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// cross_fade.glsl
+
+uniform float u_progress;
+uniform sampler2D u_source2;
+
+void main() {
+ vec4 source1 = GskTexture(u_source, vUv); // start child
+ vec4 source2 = GskTexture(u_source2, vUv); // end child
+
+ float p_start = (1.0 - u_progress) * u_alpha;
+ float p_end = u_progress * u_alpha;
+ vec4 color = (p_start * source1) + (p_end * source2);
+ gskSetOutputColor(color);
+}
+
diff --git a/shaders/gtk4/78.shader_test b/shaders/gtk4/78.shader_test
new file mode 100644
index 0000000..d9ccdb8
--- /dev/null
+++ b/shaders/gtk4/78.shader_test
@@ -0,0 +1,382 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// cross_fade.glsl
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ vUv = vec2(aUv.x, aUv.y);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// cross_fade.glsl
+
+uniform float u_progress;
+uniform sampler2D u_source2;
+
+void main() {
+ vec4 source1 = GskTexture(u_source, vUv); // start child
+ vec4 source2 = GskTexture(u_source2, vUv); // end child
+
+ float p_start = (1.0 - u_progress) * u_alpha;
+ float p_end = u_progress * u_alpha;
+ vec4 color = (p_start * source1) + (p_end * source2);
+ gskSetOutputColor(color);
+}
+
diff --git a/shaders/gtk4/81.shader_test b/shaders/gtk4/81.shader_test
new file mode 100644
index 0000000..284dab6
--- /dev/null
+++ b/shaders/gtk4/81.shader_test
@@ -0,0 +1,380 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// cross_fade.glsl
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ vUv = vec2(aUv.x, aUv.y);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// cross_fade.glsl
+
+uniform float u_progress;
+uniform sampler2D u_source2;
+
+void main() {
+ vec4 source1 = GskTexture(u_source, vUv); // start child
+ vec4 source2 = GskTexture(u_source2, vUv); // end child
+
+ float p_start = (1.0 - u_progress) * u_alpha;
+ float p_end = u_progress * u_alpha;
+ vec4 color = (p_start * source1) + (p_end * source2);
+ gskSetOutputColor(color);
+}
+
diff --git a/shaders/gtk4/84.shader_test b/shaders/gtk4/84.shader_test
new file mode 100644
index 0000000..af9464a
--- /dev/null
+++ b/shaders/gtk4/84.shader_test
@@ -0,0 +1,405 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// filled_border.glsl
+
+uniform vec4 u_widths;
+uniform vec4[3] u_outline_rect;
+
+_OUT_ vec4 outer_color;
+_OUT_ vec4 inner_color;
+_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
+_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ outer_color = gsk_scaled_premultiply(aColor, u_alpha);
+ inner_color = gsk_scaled_premultiply(aColor2, u_alpha);
+
+ GskRoundedRect outside = gsk_create_rect(u_outline_rect);
+ GskRoundedRect inside = gsk_rounded_rect_shrink (outside, u_widths);
+
+ gsk_rounded_rect_transform(outside, u_modelview);
+ gsk_rounded_rect_transform(inside, u_modelview);
+
+ gsk_rounded_rect_encode(outside, transformed_outside_outline);
+ gsk_rounded_rect_encode(inside, transformed_inside_outline);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// filled_border.glsl
+
+uniform vec4[3] u_outline_rect;
+
+_IN_ vec4 outer_color;
+_IN_ vec4 inner_color;
+_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
+_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
+
+void main() {
+ vec2 frag = gsk_get_frag_coord();
+ float outer_coverage = gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outside_outline), frag);
+ float inner_coverage = gsk_rounded_rect_coverage(gsk_decode_rect(transformed_inside_outline), frag);
+
+ float alpha = clamp(outer_coverage - inner_coverage, 0.0, 1.0);
+ float alpha2 = clamp(inner_coverage, 0.0, 1.0);
+
+ gskSetOutputColor((outer_color * alpha) + (inner_color * alpha2));
+}
+
diff --git a/shaders/gtk4/87.shader_test b/shaders/gtk4/87.shader_test
new file mode 100644
index 0000000..abba338
--- /dev/null
+++ b/shaders/gtk4/87.shader_test
@@ -0,0 +1,405 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// filled_border.glsl
+
+uniform vec4 u_widths;
+uniform vec4[3] u_outline_rect;
+
+_OUT_ vec4 outer_color;
+_OUT_ vec4 inner_color;
+_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
+_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ outer_color = gsk_scaled_premultiply(aColor, u_alpha);
+ inner_color = gsk_scaled_premultiply(aColor2, u_alpha);
+
+ GskRoundedRect outside = gsk_create_rect(u_outline_rect);
+ GskRoundedRect inside = gsk_rounded_rect_shrink (outside, u_widths);
+
+ gsk_rounded_rect_transform(outside, u_modelview);
+ gsk_rounded_rect_transform(inside, u_modelview);
+
+ gsk_rounded_rect_encode(outside, transformed_outside_outline);
+ gsk_rounded_rect_encode(inside, transformed_inside_outline);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// filled_border.glsl
+
+uniform vec4[3] u_outline_rect;
+
+_IN_ vec4 outer_color;
+_IN_ vec4 inner_color;
+_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
+_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
+
+void main() {
+ vec2 frag = gsk_get_frag_coord();
+ float outer_coverage = gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outside_outline), frag);
+ float inner_coverage = gsk_rounded_rect_coverage(gsk_decode_rect(transformed_inside_outline), frag);
+
+ float alpha = clamp(outer_coverage - inner_coverage, 0.0, 1.0);
+ float alpha2 = clamp(inner_coverage, 0.0, 1.0);
+
+ gskSetOutputColor((outer_color * alpha) + (inner_color * alpha2));
+}
+
diff --git a/shaders/gtk4/9.shader_test b/shaders/gtk4/9.shader_test
new file mode 100644
index 0000000..418a21e
--- /dev/null
+++ b/shaders/gtk4/9.shader_test
@@ -0,0 +1,670 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// blend.glsl
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ vUv = vec2(aUv.x, aUv.y);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// blend.glsl
+
+uniform int u_mode;
+uniform sampler2D u_source2;
+
+float
+combine (float source, float backdrop)
+{
+ return source + backdrop * (1.0 - source);
+}
+
+vec4
+composite (vec4 Cs, vec4 Cb, vec3 B)
+{
+ float ao = Cs.a + Cb.a * (1.0 - Cs.a);
+ vec3 Co = (Cs.a*(1.0 - Cb.a)*Cs.rgb + Cs.a*Cb.a*B + (1.0 - Cs.a)*Cb.a*Cb.rgb) / ao;
+ return vec4(Co, ao);
+}
+
+vec4
+normal (vec4 Cs, vec4 Cb)
+{
+ return composite (Cs, Cb, Cs.rgb);
+}
+
+vec4
+multiply (vec4 Cs, vec4 Cb)
+{
+ return composite (Cs, Cb, Cs.rgb * Cb.rgb);
+}
+
+vec4
+difference (vec4 Cs, vec4 Cb)
+{
+ return composite (Cs, Cb, abs(Cs.rgb - Cb.rgb));
+}
+
+vec4
+screen (vec4 Cs, vec4 Cb)
+{
+ return composite (Cs, Cb, Cs.rgb + Cb.rgb - Cs.rgb * Cb.rgb);
+}
+
+float
+hard_light (float source, float backdrop)
+{
+ if (source <= 0.5)
+ return 2.0 * backdrop * source;
+ else
+ return 2.0 * (backdrop + source - backdrop * source) - 1.0;
+}
+
+vec4
+hard_light (vec4 Cs, vec4 Cb)
+{
+ vec3 B = vec3 (hard_light (Cs.r, Cb.r),
+ hard_light (Cs.g, Cb.g),
+ hard_light (Cs.b, Cb.b));
+ return composite (Cs, Cb, B);
+}
+
+float
+soft_light (float source, float backdrop)
+{
+ float db;
+
+ if (backdrop <= 0.25)
+ db = ((16.0 * backdrop - 12.0) * backdrop + 4.0) * backdrop;
+ else
+ db = sqrt (backdrop);
+
+ if (source <= 0.5)
+ return backdrop - (1.0 - 2.0 * source) * backdrop * (1.0 - backdrop);
+ else
+ return backdrop + (2.0 * source - 1.0) * (db - backdrop);
+}
+
+vec4
+soft_light (vec4 Cs, vec4 Cb)
+{
+ vec3 B = vec3 (soft_light (Cs.r, Cb.r),
+ soft_light (Cs.g, Cb.g),
+ soft_light (Cs.b, Cb.b));
+ return composite (Cs, Cb, B);
+}
+
+vec4
+overlay (vec4 Cs, vec4 Cb)
+{
+ vec3 B = vec3 (hard_light (Cb.r, Cs.r),
+ hard_light (Cb.g, Cs.g),
+ hard_light (Cb.b, Cs.b));
+ return composite (Cs, Cb, B);
+}
+
+vec4
+darken (vec4 Cs, vec4 Cb)
+{
+ vec3 B = min (Cs.rgb, Cb.rgb);
+ return composite (Cs, Cb, B);
+}
+
+vec4
+lighten (vec4 Cs, vec4 Cb)
+{
+ vec3 B = max (Cs.rgb, Cb.rgb);
+ return composite (Cs, Cb, B);
+}
+
+float
+color_dodge (float source, float backdrop)
+{
+ return (source == 1.0) ? source : min (backdrop / (1.0 - source), 1.0);
+}
+
+vec4
+color_dodge (vec4 Cs, vec4 Cb)
+{
+ vec3 B = vec3 (color_dodge (Cs.r, Cb.r),
+ color_dodge (Cs.g, Cb.g),
+ color_dodge (Cs.b, Cb.b));
+ return composite (Cs, Cb, B);
+}
+
+
+float
+color_burn (float source, float backdrop)
+{
+ return (source == 0.0) ? source : max ((1.0 - ((1.0 - backdrop) / source)), 0.0);
+}
+
+vec4
+color_burn (vec4 Cs, vec4 Cb)
+{
+ vec3 B = vec3 (color_burn (Cs.r, Cb.r),
+ color_burn (Cs.g, Cb.g),
+ color_burn (Cs.b, Cb.b));
+ return composite (Cs, Cb, B);
+}
+
+vec4
+exclusion (vec4 Cs, vec4 Cb)
+{
+ vec3 B = Cb.rgb + Cs.rgb - 2.0 * Cb.rgb * Cs.rgb;
+ return composite (Cs, Cb, B);
+}
+
+float
+lum (vec3 c)
+{
+ return 0.3 * c.r + 0.59 * c.g + 0.11 * c.b;
+}
+
+vec3
+clip_color (vec3 c)
+{
+ float l = lum (c);
+ float n = min (c.r, min (c.g, c.b));
+ float x = max (c.r, max (c.g, c.b));
+ if (n < 0.0) c = l + (((c - l) * l) / (l - n));
+ if (x > 1.0) c = l + (((c - l) * (1.0 - l)) / (x - l));
+ return c;
+}
+
+vec3
+set_lum (vec3 c, float l)
+{
+ float d = l - lum (c);
+ return clip_color (vec3 (c.r + d, c.g + d, c.b + d));
+}
+
+float
+sat (vec3 c)
+{
+ return max (c.r, max (c.g, c.b)) - min (c.r, min (c.g, c.b));
+}
+
+vec3
+set_sat (vec3 c, float s)
+{
+ float cmin = min (c.r, min (c.g, c.b));
+ float cmax = max (c.r, max (c.g, c.b));
+ vec3 res;
+
+ if (cmax == cmin)
+ res = vec3 (0, 0, 0);
+ else
+ {
+ if (c.r == cmax)
+ {
+ if (c.g == cmin)
+ {
+ res.b = ((c.b - cmin) * s) / (cmax - cmin);
+ res.g = 0.0;
+ }
+ else
+ {
+ res.g = ((c.g - cmin) * s) / (cmax - cmin);
+ res.b = 0.0;
+ }
+ res.r = s;
+ }
+ else if (c.g == cmax)
+ {
+ if (c.r == cmin)
+ {
+ res.b = ((c.b - cmin) * s) / (cmax - cmin);
+ res.r = 0.0;
+ }
+ else
+ {
+ res.r = ((c.r - cmin) * s) / (cmax - cmin);
+ res.b = 0.0;
+ }
+ res.g = s;
+ }
+ else
+ {
+ if (c.r == cmin)
+ {
+ res.g = ((c.g - cmin) * s) / (cmax - cmin);
+ res.r = 0.0;
+ }
+ else
+ {
+ res.r = ((c.r - cmin) * s) / (cmax - cmin);
+ res.g = 0.0;
+ }
+ res.b = s;
+ }
+ }
+ return res;
+}
+
+vec4
+color (vec4 Cs, vec4 Cb)
+{
+ vec3 B = set_lum (Cs.rgb, lum (Cb.rgb));
+ return composite (Cs, Cb, B);
+}
+
+vec4
+hue (vec4 Cs, vec4 Cb)
+{
+ vec3 B = set_lum (set_sat (Cs.rgb, sat (Cb.rgb)), lum (Cb.rgb));
+ return composite (Cs, Cb, B);
+}
+
+vec4
+saturation (vec4 Cs, vec4 Cb)
+{
+ vec3 B = set_lum (set_sat (Cb.rgb, sat (Cs.rgb)), lum (Cb.rgb));
+ return composite (Cs, Cb, B);
+}
+
+vec4
+luminosity (vec4 Cs, vec4 Cb)
+{
+ vec3 B = set_lum (Cb.rgb, lum (Cs.rgb));
+ return composite (Cs, Cb, B);
+}
+
+void main() {
+ vec4 bottom_color = GskTexture(u_source, vUv);
+ vec4 top_color = GskTexture(u_source2, vUv);
+
+ vec4 result;
+ if (u_mode == 0)
+ result = normal(top_color, bottom_color);
+ else if (u_mode == 1)
+ result = multiply(top_color, bottom_color);
+ else if (u_mode == 2)
+ result = screen(top_color, bottom_color);
+ else if (u_mode == 3)
+ result = overlay(top_color, bottom_color);
+ else if (u_mode == 4)
+ result = darken(top_color, bottom_color);
+ else if (u_mode == 5)
+ result = lighten(top_color, bottom_color);
+ else if (u_mode == 6)
+ result = color_dodge(top_color, bottom_color);
+ else if (u_mode == 7)
+ result = color_burn(top_color, bottom_color);
+ else if (u_mode == 8)
+ result = hard_light(top_color, bottom_color);
+ else if (u_mode == 9)
+ result = soft_light(top_color, bottom_color);
+ else if (u_mode == 10)
+ result = difference(top_color, bottom_color);
+ else if (u_mode == 11)
+ result = exclusion(top_color, bottom_color);
+ else if (u_mode == 12)
+ result = color(top_color, bottom_color);
+ else if (u_mode == 13)
+ result = hue(top_color, bottom_color);
+ else if (u_mode == 14)
+ result = saturation(top_color, bottom_color);
+ else if (u_mode == 15)
+ result = luminosity(top_color, bottom_color);
+ else
+ discard;
+
+ gskSetScaledOutputColor(result, u_alpha);
+}
+
diff --git a/shaders/gtk4/90.shader_test b/shaders/gtk4/90.shader_test
new file mode 100644
index 0000000..8e820ba
--- /dev/null
+++ b/shaders/gtk4/90.shader_test
@@ -0,0 +1,403 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// filled_border.glsl
+
+uniform vec4 u_widths;
+uniform vec4[3] u_outline_rect;
+
+_OUT_ vec4 outer_color;
+_OUT_ vec4 inner_color;
+_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
+_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ outer_color = gsk_scaled_premultiply(aColor, u_alpha);
+ inner_color = gsk_scaled_premultiply(aColor2, u_alpha);
+
+ GskRoundedRect outside = gsk_create_rect(u_outline_rect);
+ GskRoundedRect inside = gsk_rounded_rect_shrink (outside, u_widths);
+
+ gsk_rounded_rect_transform(outside, u_modelview);
+ gsk_rounded_rect_transform(inside, u_modelview);
+
+ gsk_rounded_rect_encode(outside, transformed_outside_outline);
+ gsk_rounded_rect_encode(inside, transformed_inside_outline);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// filled_border.glsl
+
+uniform vec4[3] u_outline_rect;
+
+_IN_ vec4 outer_color;
+_IN_ vec4 inner_color;
+_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
+_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
+
+void main() {
+ vec2 frag = gsk_get_frag_coord();
+ float outer_coverage = gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outside_outline), frag);
+ float inner_coverage = gsk_rounded_rect_coverage(gsk_decode_rect(transformed_inside_outline), frag);
+
+ float alpha = clamp(outer_coverage - inner_coverage, 0.0, 1.0);
+ float alpha2 = clamp(inner_coverage, 0.0, 1.0);
+
+ gskSetOutputColor((outer_color * alpha) + (inner_color * alpha2));
+}
+
diff --git a/shaders/gtk4/93.shader_test b/shaders/gtk4/93.shader_test
new file mode 100644
index 0000000..91128de
--- /dev/null
+++ b/shaders/gtk4/93.shader_test
@@ -0,0 +1,402 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// inset_shadow.glsl
+
+uniform float u_spread;
+uniform vec2 u_offset;
+uniform vec4[3] u_outline_rect;
+
+_OUT_ vec4 final_color;
+_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
+_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ final_color = gsk_scaled_premultiply(aColor, u_alpha);
+
+ GskRoundedRect outside = gsk_create_rect(u_outline_rect);
+ GskRoundedRect inside = gsk_rounded_rect_shrink(outside, vec4(u_spread));
+
+ gsk_rounded_rect_offset(inside, u_offset);
+
+ gsk_rounded_rect_transform(outside, u_modelview);
+ gsk_rounded_rect_transform(inside, u_modelview);
+
+ gsk_rounded_rect_encode(outside, transformed_outside_outline);
+ gsk_rounded_rect_encode(inside, transformed_inside_outline);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define NO_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// inset_shadow.glsl
+
+_IN_ vec4 final_color;
+_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
+_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
+
+void main() {
+ vec2 frag = gsk_get_frag_coord();
+
+ float alpha = clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outside_outline), frag) -
+ gsk_rounded_rect_coverage(gsk_decode_rect(transformed_inside_outline), frag),
+ 0.0, 1.0);
+
+ gskSetScaledOutputColor(final_color, alpha);
+}
+
diff --git a/shaders/gtk4/96.shader_test b/shaders/gtk4/96.shader_test
new file mode 100644
index 0000000..453c171
--- /dev/null
+++ b/shaders/gtk4/96.shader_test
@@ -0,0 +1,402 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// inset_shadow.glsl
+
+uniform float u_spread;
+uniform vec2 u_offset;
+uniform vec4[3] u_outline_rect;
+
+_OUT_ vec4 final_color;
+_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
+_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ final_color = gsk_scaled_premultiply(aColor, u_alpha);
+
+ GskRoundedRect outside = gsk_create_rect(u_outline_rect);
+ GskRoundedRect inside = gsk_rounded_rect_shrink(outside, vec4(u_spread));
+
+ gsk_rounded_rect_offset(inside, u_offset);
+
+ gsk_rounded_rect_transform(outside, u_modelview);
+ gsk_rounded_rect_transform(inside, u_modelview);
+
+ gsk_rounded_rect_encode(outside, transformed_outside_outline);
+ gsk_rounded_rect_encode(inside, transformed_inside_outline);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#define RECT_CLIP 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// inset_shadow.glsl
+
+_IN_ vec4 final_color;
+_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
+_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
+
+void main() {
+ vec2 frag = gsk_get_frag_coord();
+
+ float alpha = clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outside_outline), frag) -
+ gsk_rounded_rect_coverage(gsk_decode_rect(transformed_inside_outline), frag),
+ 0.0, 1.0);
+
+ gskSetScaledOutputColor(final_color, alpha);
+}
+
diff --git a/shaders/gtk4/99.shader_test b/shaders/gtk4/99.shader_test
new file mode 100644
index 0000000..604eb93
--- /dev/null
+++ b/shaders/gtk4/99.shader_test
@@ -0,0 +1,400 @@
+[require]
+GLSL >= 1.50
+
+[vertex shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+attribute vec2 aPosition;
+attribute vec2 aUv;
+attribute vec4 aColor;
+attribute vec4 aColor2;
+_OUT_ vec2 vUv;
+#else
+_IN_ vec2 aPosition;
+_IN_ vec2 aUv;
+_IN_ vec4 aColor;
+_IN_ vec4 aColor2;
+_OUT_ vec2 vUv;
+#endif
+
+// amount is: top, right, bottom, left
+GskRoundedRect
+gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
+{
+ vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
+ vec4 new_corner_points1 = r.corner_points1;
+ vec4 new_corner_points2 = r.corner_points2;
+
+ if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy;
+ if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy;
+ if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
+ if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
+
+ return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
+}
+
+void
+gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
+{
+ r.bounds.xy += offset;
+ r.bounds.zw += offset;
+ r.corner_points1.xy += offset;
+ r.corner_points1.zw += offset;
+ r.corner_points2.xy += offset;
+ r.corner_points2.zw += offset;
+}
+
+void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
+{
+ r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
+ r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
+
+ r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy;
+ r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy;
+
+ r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy;
+ r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy;
+}
+
+#if defined(GSK_LEGACY)
+// Can't have out or inout array parameters...
+#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
+#else
+void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
+{
+#if defined(GSK_GLES)
+ out_r[0] = r.bounds;
+ out_r[1] = r.corner_points1;
+ out_r[2] = r.corner_points2;
+#else
+ out_r = r;
+#endif
+}
+
+#endif
+
+// inset_shadow.glsl
+
+uniform float u_spread;
+uniform vec2 u_offset;
+uniform vec4[3] u_outline_rect;
+
+_OUT_ vec4 final_color;
+_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
+_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ final_color = gsk_scaled_premultiply(aColor, u_alpha);
+
+ GskRoundedRect outside = gsk_create_rect(u_outline_rect);
+ GskRoundedRect inside = gsk_rounded_rect_shrink(outside, vec4(u_spread));
+
+ gsk_rounded_rect_offset(inside, u_offset);
+
+ gsk_rounded_rect_transform(outside, u_modelview);
+ gsk_rounded_rect_transform(inside, u_modelview);
+
+ gsk_rounded_rect_encode(outside, transformed_outside_outline);
+ gsk_rounded_rect_encode(inside, transformed_inside_outline);
+}
+
+// FRAGMENT_SHADER:
+[fragment shader]
+#version 150
+#define GSK_GL3 1
+#ifndef GSK_LEGACY
+precision highp float;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+#define _OUT_ varying
+#define _IN_ varying
+#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
+#else
+#define _OUT_ out
+#define _IN_ in
+#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
+#endif
+
+
+struct GskRoundedRect
+{
+ vec4 bounds; // Top left and bottom right
+ // Look, arrays can't be in structs if you want to return the struct
+ // from a function in gles or whatever. Just kill me.
+ vec4 corner_points1; // xy = top left, zw = top right
+ vec4 corner_points2; // xy = bottom right, zw = bottom left
+};
+
+// Transform from a C GskRoundedRect to what we need.
+GskRoundedRect
+gsk_create_rect(vec4[3] data)
+{
+ vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+ vec4 corner_points1 = vec4(bounds.xy + data[1].xy,
+ bounds.zy + vec2(data[1].zw * vec2(-1, 1)));
+ vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
+ bounds.xw + vec2(data[2].zw * vec2(1, -1)));
+
+ return GskRoundedRect(bounds, corner_points1, corner_points2);
+}
+
+vec4
+gsk_get_bounds(vec4[3] data)
+{
+ return vec4(data[0].xy, data[0].xy + data[0].zw);
+}
+
+vec4 gsk_premultiply(vec4 c) {
+ return vec4(c.rgb * c.a, c.a);
+}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
+uniform sampler2D u_source;
+uniform mat4 u_projection;
+uniform mat4 u_modelview;
+uniform float u_alpha;
+uniform vec4 u_viewport;
+uniform vec4[3] u_clip_rect;
+
+#if defined(GSK_LEGACY)
+_OUT_ vec4 outputColor;
+#elif !defined(GSK_GLES)
+_OUT_ vec4 outputColor;
+#endif
+
+_IN_ vec2 vUv;
+
+
+GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
+{
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return GskRoundedRect(r[0], r[1], r[2]);
+#else
+ return r;
+#endif
+}
+
+float
+gsk_ellipsis_dist (vec2 p, vec2 radius)
+{
+ if (radius == vec2(0, 0))
+ return 0.0;
+
+ vec2 p0 = p / radius;
+ vec2 p1 = 2.0 * p0 / radius;
+
+ return (dot(p0, p0) - 1.0) / length (p1);
+}
+
+float
+gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
+{
+ float d = gsk_ellipsis_dist (point - center, radius);
+ return clamp (0.5 - d, 0.0, 1.0);
+}
+
+float
+gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
+{
+ if (p.x < r.bounds.x || p.y < r.bounds.y ||
+ p.x >= r.bounds.z || p.y >= r.bounds.w)
+ return 0.0;
+
+ vec2 ref_tl = r.corner_points1.xy;
+ vec2 ref_tr = r.corner_points1.zw;
+ vec2 ref_br = r.corner_points2.xy;
+ vec2 ref_bl = r.corner_points2.zw;
+
+ if (p.x >= ref_tl.x && p.x >= ref_bl.x &&
+ p.x <= ref_tr.x && p.x <= ref_br.x)
+ return 1.0;
+
+ if (p.y >= ref_tl.y && p.y >= ref_tr.y &&
+ p.y <= ref_bl.y && p.y <= ref_br.y)
+ return 1.0;
+
+ vec2 rad_tl = r.corner_points1.xy - r.bounds.xy;
+ vec2 rad_tr = r.corner_points1.zw - r.bounds.zy;
+ vec2 rad_br = r.corner_points2.xy - r.bounds.zw;
+ vec2 rad_bl = r.corner_points2.zw - r.bounds.xw;
+
+ float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
+ float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
+ float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
+ float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
+
+ vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
+
+ bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y,
+ p.x > ref_tr.x && p.y < ref_tr.y,
+ p.x > ref_br.x && p.y > ref_br.y,
+ p.x < ref_bl.x && p.y > ref_bl.y);
+
+ return 1.0 - dot(vec4(is_out), corner_coverages);
+}
+
+float
+gsk_rect_coverage (vec4 r, vec2 p)
+{
+ if (p.x < r.x || p.y < r.y ||
+ p.x >= r.z || p.y >= r.w)
+ return 0.0;
+
+ return 1.0;
+}
+
+vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ return texture2D(sampler, texCoords);
+#else
+ return texture(sampler, texCoords);
+#endif
+}
+
+#ifdef GSK_GL3
+layout(origin_upper_left) in vec4 gl_FragCoord;
+#endif
+
+vec2 gsk_get_frag_coord() {
+ vec2 fc = gl_FragCoord.xy;
+
+#ifdef GSK_GL3
+ fc += u_viewport.xy;
+#else
+ fc.x += u_viewport.x;
+ fc.y = (u_viewport.y + u_viewport.w) - fc.y;
+#endif
+
+ return fc;
+}
+
+void gskSetOutputColor(vec4 color) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * coverage;
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+void gskSetScaledOutputColor(vec4 color, float alpha) {
+ vec4 result;
+
+#if defined(NO_CLIP)
+ result = color * alpha;
+#elif defined(RECT_CLIP)
+ float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#else
+ float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect),
+ gsk_get_frag_coord());
+ result = color * (alpha * coverage);
+#endif
+
+#if defined(GSK_GLES) || defined(GSK_LEGACY)
+ gl_FragColor = result;
+#else
+ outputColor = result;
+#endif
+}
+
+// inset_shadow.glsl
+
+_IN_ vec4 final_color;
+_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
+_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
+
+void main() {
+ vec2 frag = gsk_get_frag_coord();
+
+ float alpha = clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outside_outline), frag) -
+ gsk_rounded_rect_coverage(gsk_decode_rect(transformed_inside_outline), frag),
+ 0.0, 1.0);
+
+ gskSetScaledOutputColor(final_color, alpha);
+}
+