gsk: Normalize rects in GLSL
authorMatthias Clasen <mclasen@redhat.com>
Sat, 15 Apr 2023 20:50:34 +0000 (22:50 +0200)
committerMatthias Clasen <mclasen@redhat.com>
Mon, 1 May 2023 17:43:33 +0000 (13:43 -0400)
Our coverage computation only works for well-behaved
rects and rounded rects. But our modelview transform
might flip x or y around, causing things to fail.

Add functions to normalize rects and rounded rects,
and use it whenever we transform a rounded rect in GLSL.

gsk/gl/resources/border.glsl
gsk/gl/resources/filled_border.glsl
gsk/gl/resources/inset_shadow.glsl
gsk/gl/resources/outset_shadow.glsl
gsk/gl/resources/preamble.fs.glsl
gsk/gl/resources/preamble.glsl

index b8653bad499a8868b3f17cc1e30c2fd2ca10fb77..0fa3203887adfe8c106eb46c4d35ba87bd89f937 100644 (file)
@@ -19,6 +19,9 @@ void main() {
   gsk_rounded_rect_transform(outside, u_modelview);
   gsk_rounded_rect_transform(inside, u_modelview);
 
+  gsk_rounded_rect_normalize(outside);
+  gsk_rounded_rect_normalize(inside);
+
   gsk_rounded_rect_encode(outside, transformed_outside_outline);
   gsk_rounded_rect_encode(inside, transformed_inside_outline);
 }
@@ -34,10 +37,9 @@ _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);
+  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);
 
   gskSetScaledOutputColor(final_color, alpha);
 }
index d503dd05005b39bed846e9071fe402186f0d3b57..23af3aa89c605f814019397f09da2a83745eea57 100644 (file)
@@ -21,6 +21,9 @@ void main() {
   gsk_rounded_rect_transform(outside, u_modelview);
   gsk_rounded_rect_transform(inside, u_modelview);
 
+  gsk_rounded_rect_normalize(outside);
+  gsk_rounded_rect_normalize(inside);
+
   gsk_rounded_rect_encode(outside, transformed_outside_outline);
   gsk_rounded_rect_encode(inside, transformed_inside_outline);
 }
index f052a08580b0b5e38cfc83b52fd644a0254db8cd..83d4287f130c7dab1af094299e7d30723a1eb139 100644 (file)
@@ -22,6 +22,9 @@ void main() {
   gsk_rounded_rect_transform(outside, u_modelview);
   gsk_rounded_rect_transform(inside, u_modelview);
 
+  gsk_rounded_rect_normalize(outside);
+  gsk_rounded_rect_normalize(inside);
+
   gsk_rounded_rect_encode(outside, transformed_outside_outline);
   gsk_rounded_rect_encode(inside, transformed_inside_outline);
 }
index 44b05aa15234bc25d3ec2d47a6a48c7039a81dd1..fac86c0093c9ec49e0aa4bddbfca834ba640a205 100644 (file)
@@ -15,6 +15,7 @@ void main() {
 
   GskRoundedRect outline = gsk_create_rect(u_outline_rect);
   gsk_rounded_rect_transform(outline, u_modelview);
+  gsk_rounded_rect_normalize(outline);
   gsk_rounded_rect_encode(outline, transformed_outline);
 }
 
index c1c5a954eec3a3c50bd8953623f82a1f87d86e6d..0c366bd8476ea01c909f0d52b0eb1f659b7d72cc 100644 (file)
@@ -16,11 +16,15 @@ _IN_ vec2 vUv;
 
 GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
 {
+  GskRoundedRect rect;
 #if defined(GSK_GLES) || defined(GSK_LEGACY)
-  return GskRoundedRect(r[0], r[1], r[2]);
+  rect = GskRoundedRect(r[0], r[1], r[2]);
 #else
-  return r;
+  rect = r;
 #endif
+  gsk_rounded_rect_normalize (rect);
+
+  return rect;
 }
 
 float
index 5f5d2b780a172e6d5702f5cd1b65769324ce28b1..46bebc14e0477fde28db8914e6056c2decb384b3 100644 (file)
@@ -22,6 +22,55 @@ struct GskRoundedRect
   vec4 corner_points2; // xy = bottom right, zw = bottom left
 };
 
+void gsk_rounded_rect_normalize(inout GskRoundedRect r)
+{
+  if (r.bounds.x > r.bounds.z)
+    {
+      float t = r.bounds.x;
+      r.bounds.x = r.bounds.z;
+      r.bounds.z = t;
+
+      vec2 c = r.corner_points1.xy;
+      r.corner_points1.xy = r.corner_points1.zw;
+      r.corner_points1.zw = c;
+
+      c = r.corner_points2.xy;
+      r.corner_points2.xy = r.corner_points2.zw;
+      r.corner_points2.zw = c;
+    }
+
+  if (r.bounds.y > r.bounds.w)
+    {
+      float t = r.bounds.y;
+      r.bounds.y = r.bounds.w;
+      r.bounds.w = t;
+
+      vec2 c = r.corner_points1.xy;
+      r.corner_points1.xy = r.corner_points2.xy;
+      r.corner_points2.xy = c;
+
+      c = r.corner_points1.zw;
+      r.corner_points1.zw = r.corner_points2.zw;
+      r.corner_points2.zw = c;
+    }
+}
+
+void gsk_bounds_normalize (inout vec4 bounds)
+{
+  if (bounds.x > bounds.z)
+    {
+      float t = bounds.x;
+      bounds.x = bounds.z;
+      bounds.z = t;
+    }
+  if (bounds.y > bounds.w)
+    {
+      float t = bounds.y;
+      bounds.y = bounds.w;
+      bounds.w = t;
+    }
+}
+
 // Transform from a C GskRoundedRect to what we need.
 GskRoundedRect
 gsk_create_rect(vec4[3] data)
@@ -33,13 +82,21 @@ gsk_create_rect(vec4[3] data)
   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);
+  GskRoundedRect rect = GskRoundedRect(bounds, corner_points1, corner_points2);
+
+  gsk_rounded_rect_normalize (rect);
+
+  return rect;
 }
 
 vec4
 gsk_get_bounds(vec4[3] data)
 {
-  return vec4(data[0].xy, data[0].xy + data[0].zw);
+  vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
+
+  gsk_bounds_normalize (bounds);
+
+  return bounds;
 }
 
 vec4 gsk_premultiply(vec4 c) {