Bug 1606771 - Implement border-image-repeat: round in WebRender. r=jrmuizel,kvark
authorNicolas Silva <nsilva@mozilla.com>
Thu, 23 Jan 2020 14:24:11 +0000
changeset 511474 6b61867f80b123f11aa3e9252f0ac005a12787c0
parent 511473 8b8a272428800614fcbc975c1ed93b663950ba89
child 511475 834d49acd3c02a7e5ae75d0a4d01a55e69c8fdba
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)
reviewersjrmuizel, kvark
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 - Implement border-image-repeat: round in WebRender. r=jrmuizel,kvark border-image-repeat: Round is equivalent to Repeat with the pattern size adjusted to fill the area with a whole number of repetitions. This is done by adjusting the segment's stretch_size in the shader so that it fits a whole number of times in the segment's size. Differential Revision: https://phabricator.services.mozilla.com/D59370
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
layout/painting/nsCSSRendering.cpp
--- a/gfx/wr/webrender/res/brush.glsl
+++ b/gfx/wr/webrender/res/brush.glsl
@@ -107,17 +107,19 @@ 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_TEXEL_RECT                  16
+#define BRUSH_FLAG_SEGMENT_REPEAT_X_ROUND      16
+#define BRUSH_FLAG_SEGMENT_REPEAT_Y_ROUND      32
+#define BRUSH_FLAG_TEXEL_RECT                  64
 
 #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
@@ -98,30 +98,45 @@ 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
             if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_X) != 0) {
               stretch_size.x = local_rect.size.y / dy * dx;
             }
             if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_Y) != 0) {
               stretch_size.y = local_rect.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;
             }
+            #endif
         }
+
+        #ifdef WR_FEATURE_REPETITION
+        if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_X_ROUND) != 0) {
+            float nx = max(1.0, round(segment_rect.size.x / stretch_size.x));
+            stretch_size.x = segment_rect.size.x / nx;
+        }
+        if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_Y_ROUND) != 0) {
+            float ny = max(1.0, round(segment_rect.size.y / stretch_size.y));
+            stretch_size.y = segment_rect.size.y / ny;
+        }
+        #endif
     }
 
     float perspective_interpolate = (brush_flags & BRUSH_FLAG_PERSPECTIVE_INTERPOLATION) != 0 ? 1.0 : 0.0;
     V_LAYER = res.layer;
     V_PERSPECTIVE = perspective_interpolate;
 
     // Handle case where the UV coords are inverted (e.g. from an
     // external image).
--- a/gfx/wr/webrender/src/border.rs
+++ b/gfx/wr/webrender/src/border.rs
@@ -1346,48 +1346,53 @@ impl NinePatchDescriptor {
 
         fn add_segment(
             segments: &mut Vec<BrushSegment>,
             rect: LayoutRect,
             uv_rect: TexelRect,
             repeat_horizontal: RepeatMode,
             repeat_vertical: RepeatMode
         ) {
-            if uv_rect.uv1.x > uv_rect.uv0.x &&
-               uv_rect.uv1.y > uv_rect.uv0.y {
+            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;
+            // Use segment relative interpolation for all
+            // instances in this primitive.
+            let mut brush_flags =
+                BrushFlags::SEGMENT_RELATIVE |
+                BrushFlags::SEGMENT_TEXEL_RECT;
+
+            // 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;
+            }
 
-                // Enable repeat modes on the segment.
-                if repeat_horizontal == RepeatMode::Repeat {
-                    brush_flags |= BrushFlags::SEGMENT_REPEAT_X;
-                }
-                if repeat_vertical == RepeatMode::Repeat {
-                    brush_flags |= BrushFlags::SEGMENT_REPEAT_Y;
-                }
+            if repeat_vertical == RepeatMode::Repeat {
+                brush_flags |= BrushFlags::SEGMENT_REPEAT_Y;
+            } else if repeat_vertical == RepeatMode::Round {
+                brush_flags |= BrushFlags::SEGMENT_REPEAT_Y | BrushFlags::SEGMENT_REPEAT_Y_ROUND;
+            }
 
-                let segment = BrushSegment::new(
-                    rect,
-                    true,
-                    EdgeAaSegmentMask::empty(),
-                    [
-                        uv_rect.uv0.x,
-                        uv_rect.uv0.y,
-                        uv_rect.uv1.x,
-                        uv_rect.uv1.y,
-                    ],
-                    brush_flags,
-                );
+            let segment = BrushSegment::new(
+                rect,
+                true,
+                EdgeAaSegmentMask::empty(),
+                [
+                    uv_rect.uv0.x,
+                    uv_rect.uv0.y,
+                    uv_rect.uv1.x,
+                    uv_rect.uv1.y,
+                ],
+                brush_flags,
+            );
 
-                segments.push(segment);
-            }
+            segments.push(segment);
         }
 
         // Build the list of image segments
         let mut segments = Vec::new();
 
         // Top left
         add_segment(
             &mut segments,
--- a/gfx/wr/webrender/src/gpu_types.rs
+++ b/gfx/wr/webrender/src/gpu_types.rs
@@ -398,26 +398,30 @@ impl From<SplitCompositeInstance> for Pr
 bitflags! {
     /// Flags that define how the common brush shader
     /// code should process this instance.
     #[cfg_attr(feature = "capture", derive(Serialize))]
     #[cfg_attr(feature = "replay", derive(Deserialize))]
     #[derive(MallocSizeOf)]
     pub struct BrushFlags: u8 {
         /// Apply perspective interpolation to UVs
-        const PERSPECTIVE_INTERPOLATION = 0x1;
+        const PERSPECTIVE_INTERPOLATION = 1;
         /// Do interpolation relative to segment rect,
         /// rather than primitive rect.
-        const SEGMENT_RELATIVE = 0x2;
+        const SEGMENT_RELATIVE = 2;
         /// Repeat UVs horizontally.
-        const SEGMENT_REPEAT_X = 0x4;
+        const SEGMENT_REPEAT_X = 4;
         /// Repeat UVs vertically.
-        const SEGMENT_REPEAT_Y = 0x8;
+        const SEGMENT_REPEAT_Y = 8;
+        /// Horizontally follow border-image-repeat: round
+        const SEGMENT_REPEAT_X_ROUND = 16;
+        /// Vorizontally follow border-image-repeat: round
+        const SEGMENT_REPEAT_Y_ROUND = 32;
         /// The extra segment data is a texel rect.
-        const SEGMENT_TEXEL_RECT = 0x10;
+        const SEGMENT_TEXEL_RECT = 64;
     }
 }
 
 /// Convenience structure to encode into PrimitiveInstanceData.
 pub struct BrushInstance {
     pub prim_header_index: PrimitiveHeaderIndex,
     pub render_task_address: RenderTaskAddress,
     pub clip_task_address: RenderTaskAddress,
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -710,19 +710,17 @@ ImgDrawResult nsCSSRendering::CreateWebR
   }
 
   // Next we try image and gradient borders. Gradients are not supported at
   // this very moment.
   if (type != eStyleImageType_Image) {
     return ImgDrawResult::NOT_SUPPORTED;
   }
 
-  if (aStyleBorder.mBorderImageRepeatH == StyleBorderImageRepeat::Round ||
-      aStyleBorder.mBorderImageRepeatH == StyleBorderImageRepeat::Space ||
-      aStyleBorder.mBorderImageRepeatV == StyleBorderImageRepeat::Round ||
+  if (aStyleBorder.mBorderImageRepeatH == StyleBorderImageRepeat::Space ||
       aStyleBorder.mBorderImageRepeatV == StyleBorderImageRepeat::Space) {
     return ImgDrawResult::NOT_SUPPORTED;
   }
 
   uint32_t flags = 0;
   if (aDisplayListBuilder->IsPaintingToWindow()) {
     flags |= nsImageRenderer::FLAG_PAINTING_TO_WINDOW;
   }