Bug 1606771 - Fix the middle area of border-image nine-patches. r=gw
authorNicolas Silva <nsilva@mozilla.com>
Thu, 23 Jan 2020 14:26:32 +0000
changeset 511475 834d49acd3c02a7e5ae75d0a4d01a55e69c8fdba
parent 511474 6b61867f80b123f11aa3e9252f0ac005a12787c0
child 511476 fcea11e2d25a45bb7cfe6c2187c30a17ca5e4c4e
push id37048
push userrmaries@mozilla.com
push dateThu, 23 Jan 2020 21:42:24 +0000
treeherdermozilla-central@fb6b61e49217 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgw
bugs1606771
milestone74.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1606771 - Fix the middle area of border-image nine-patches. r=gw Unlike the border areas that only nead their own dimensions, the middle area of a border-image determines its repetition parameter based on the size of the borders. A new flag is provided for the brush_image shader to know whether to use the segment's own rect or look at the borders when computing the pattern's size. Differential Revision: https://phabricator.services.mozilla.com/D59675
gfx/wr/webrender/res/brush.glsl
gfx/wr/webrender/res/brush_image.glsl
gfx/wr/webrender/src/border.rs
gfx/wr/webrender/src/gpu_types.rs
--- a/gfx/wr/webrender/res/brush.glsl
+++ b/gfx/wr/webrender/res/brush.glsl
@@ -109,17 +109,18 @@ void multi_brush_vs(
 #define VECS_PER_SEGMENT                    2
 
 #define BRUSH_FLAG_PERSPECTIVE_INTERPOLATION    1
 #define BRUSH_FLAG_SEGMENT_RELATIVE             2
 #define BRUSH_FLAG_SEGMENT_REPEAT_X             4
 #define BRUSH_FLAG_SEGMENT_REPEAT_Y             8
 #define BRUSH_FLAG_SEGMENT_REPEAT_X_ROUND      16
 #define BRUSH_FLAG_SEGMENT_REPEAT_Y_ROUND      32
-#define BRUSH_FLAG_TEXEL_RECT                  64
+#define BRUSH_FLAG_SEGMENT_NINEPATCH_MIDDLE    64
+#define BRUSH_FLAG_TEXEL_RECT                 128
 
 #define INVALID_SEGMENT_INDEX                   0xffff
 
 void main(void) {
     // Load the brush instance from vertex attributes.
     Instance instance = decode_instance_attributes();
     int edge_flags = instance.flags & 0xff;
     int brush_flags = (instance.flags >> 8) & 0xff;
--- a/gfx/wr/webrender/res/brush_image.glsl
+++ b/gfx/wr/webrender/res/brush_image.glsl
@@ -99,23 +99,49 @@ void image_brush_vs(
         // If the extra data is a texel rect, modify the UVs.
         if ((brush_flags & BRUSH_FLAG_TEXEL_RECT) != 0) {
 
             vec2 uv_size = res.uv_rect.p1 - res.uv_rect.p0;
             uv0 = res.uv_rect.p0 + segment_data.xy * uv_size;
             uv1 = res.uv_rect.p0 + segment_data.zw * uv_size;
 
             #ifdef WR_FEATURE_REPETITION
+            // The repetition parameters for the middle area of a nine-patch are based
+            // on the size of the border segments rather than the middle segment itself,
+            // taking top and left by default, falling back to bottom and right when a
+            // size is empty.
+            // TODO(bug 1609893): Move this logic of the CPU as well as other sources of
+            // branchiness in this shader.
+            if ((brush_flags & BRUSH_FLAG_SEGMENT_NINEPATCH_MIDDLE) != 0) {
+                dx = segment_data.x;
+                dy = segment_data.y;
+                stretch_size.x = segment_rect.p0.x - prim_rect.p0.x;
+                stretch_size.y = segment_rect.p0.y - prim_rect.p0.y;
+                float epsilon = 0.001;
+                if (dx < epsilon || stretch_size.x < epsilon) {
+                    dx = 1.0 - segment_data.z;
+                    stretch_size.x = prim_rect.p0.x + prim_rect.size.x
+                        - segment_rect.p0.x - segment_rect.size.x;
+                }
+                if (dy < epsilon || stretch_size.y < epsilon) {
+                    dy = 1.0 - segment_data.w;
+                    stretch_size.y = prim_rect.p0.y + prim_rect.size.y
+                        - segment_rect.p0.y - segment_rect.size.y;
+                }
+            }
+
+            vec2 original_stretch_size = stretch_size;
             if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_X) != 0) {
-              stretch_size.x = local_rect.size.y / dy * dx;
+              stretch_size.x = original_stretch_size.y / dy * dx;
             }
             if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_Y) != 0) {
-              stretch_size.y = local_rect.size.x / dx * dy;
+              stretch_size.y = original_stretch_size.x / dx * dy;
             }
             #endif
+
         } else {
             #ifdef WR_FEATURE_REPETITION
             if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_X) != 0) {
                 stretch_size.x = dx;
             }
             if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_Y) != 0) {
                 stretch_size.y = dy;
             }
--- a/gfx/wr/webrender/src/border.rs
+++ b/gfx/wr/webrender/src/border.rs
@@ -1344,27 +1344,29 @@ impl NinePatchDescriptor {
         );
         let br_inner = br_outer - vec2(self.widths.right, self.widths.bottom);
 
         fn add_segment(
             segments: &mut Vec<BrushSegment>,
             rect: LayoutRect,
             uv_rect: TexelRect,
             repeat_horizontal: RepeatMode,
-            repeat_vertical: RepeatMode
+            repeat_vertical: RepeatMode,
+            extra_flags: BrushFlags,
         ) {
             if uv_rect.uv1.x < uv_rect.uv0.x || uv_rect.uv1.y < uv_rect.uv0.y {
                 return;
             }
 
             // Use segment relative interpolation for all
             // instances in this primitive.
             let mut brush_flags =
                 BrushFlags::SEGMENT_RELATIVE |
-                BrushFlags::SEGMENT_TEXEL_RECT;
+                BrushFlags::SEGMENT_TEXEL_RECT |
+                extra_flags;
 
             // Enable repeat modes on the segment.
             if repeat_horizontal == RepeatMode::Repeat {
                 brush_flags |= BrushFlags::SEGMENT_REPEAT_X;
             } else if repeat_horizontal == RepeatMode::Round {
                 brush_flags |= BrushFlags::SEGMENT_REPEAT_X | BrushFlags::SEGMENT_REPEAT_X_ROUND;
             }
 
@@ -1394,84 +1396,93 @@ impl NinePatchDescriptor {
         let mut segments = Vec::new();
 
         // Top left
         add_segment(
             &mut segments,
             LayoutRect::from_floats(tl_outer.x, tl_outer.y, tl_inner.x, tl_inner.y),
             TexelRect::new(px0, py0, px1, py1),
             RepeatMode::Stretch,
-            RepeatMode::Stretch
+            RepeatMode::Stretch,
+            BrushFlags::empty(),
         );
         // Top right
         add_segment(
             &mut segments,
             LayoutRect::from_floats(tr_inner.x, tr_outer.y, tr_outer.x, tr_inner.y),
             TexelRect::new(px2, py0, px3, py1),
             RepeatMode::Stretch,
-            RepeatMode::Stretch
+            RepeatMode::Stretch,
+            BrushFlags::empty(),
         );
         // Bottom right
         add_segment(
             &mut segments,
             LayoutRect::from_floats(br_inner.x, br_inner.y, br_outer.x, br_outer.y),
             TexelRect::new(px2, py2, px3, py3),
             RepeatMode::Stretch,
-            RepeatMode::Stretch
+            RepeatMode::Stretch,
+            BrushFlags::empty(),
         );
         // Bottom left
         add_segment(
             &mut segments,
             LayoutRect::from_floats(bl_outer.x, bl_inner.y, bl_inner.x, bl_outer.y),
             TexelRect::new(px0, py2, px1, py3),
             RepeatMode::Stretch,
-            RepeatMode::Stretch
+            RepeatMode::Stretch,
+            BrushFlags::empty(),
         );
 
         // Center
         if self.fill {
             add_segment(
                 &mut segments,
                 LayoutRect::from_floats(tl_inner.x, tl_inner.y, tr_inner.x, bl_inner.y),
                 TexelRect::new(px1, py1, px2, py2),
                 self.repeat_horizontal,
-                self.repeat_vertical
+                self.repeat_vertical,
+                BrushFlags::SEGMENT_NINEPATCH_MIDDLE,
             );
         }
 
         // Add edge segments.
 
         // Top
         add_segment(
             &mut segments,
             LayoutRect::from_floats(tl_inner.x, tl_outer.y, tr_inner.x, tl_inner.y),
             TexelRect::new(px1, py0, px2, py1),
             self.repeat_horizontal,
             RepeatMode::Stretch,
+            BrushFlags::empty(),
         );
         // Bottom
         add_segment(
             &mut segments,
             LayoutRect::from_floats(bl_inner.x, bl_inner.y, br_inner.x, bl_outer.y),
             TexelRect::new(px1, py2, px2, py3),
             self.repeat_horizontal,
             RepeatMode::Stretch,
+            BrushFlags::empty(),
         );
         // Left
         add_segment(
             &mut segments,
             LayoutRect::from_floats(tl_outer.x, tl_inner.y, tl_inner.x, bl_inner.y),
             TexelRect::new(px0, py1, px1, py2),
             RepeatMode::Stretch,
             self.repeat_vertical,
+            BrushFlags::empty(),
         );
         // Right
         add_segment(
             &mut segments,
             LayoutRect::from_floats(tr_inner.x, tr_inner.y, br_outer.x, br_inner.y),
             TexelRect::new(px2, py1, px3, py2),
             RepeatMode::Stretch,
             self.repeat_vertical,
+            BrushFlags::empty(),
         );
 
         segments
     }
 }
--- a/gfx/wr/webrender/src/gpu_types.rs
+++ b/gfx/wr/webrender/src/gpu_types.rs
@@ -406,22 +406,24 @@ bitflags! {
         const PERSPECTIVE_INTERPOLATION = 1;
         /// Do interpolation relative to segment rect,
         /// rather than primitive rect.
         const SEGMENT_RELATIVE = 2;
         /// Repeat UVs horizontally.
         const SEGMENT_REPEAT_X = 4;
         /// Repeat UVs vertically.
         const SEGMENT_REPEAT_Y = 8;
-        /// Horizontally follow border-image-repeat: round
+        /// Horizontally follow border-image-repeat: round.
         const SEGMENT_REPEAT_X_ROUND = 16;
-        /// Vorizontally follow border-image-repeat: round
+        /// Vertically follow border-image-repeat: round.
         const SEGMENT_REPEAT_Y_ROUND = 32;
+        /// Middle (fill) area of a border-image-repeat.
+        const SEGMENT_NINEPATCH_MIDDLE = 64;
         /// The extra segment data is a texel rect.
-        const SEGMENT_TEXEL_RECT = 64;
+        const SEGMENT_TEXEL_RECT = 128;
     }
 }
 
 /// Convenience structure to encode into PrimitiveInstanceData.
 pub struct BrushInstance {
     pub prim_header_index: PrimitiveHeaderIndex,
     pub render_task_address: RenderTaskAddress,
     pub clip_task_address: RenderTaskAddress,