Bug 1401653 - fixup webrender text-decoration bindings. r?jrmuizel draft
authorAlexis Beingessner <a.beingessner@gmail.com>
Tue, 24 Oct 2017 12:44:38 -0400
changeset 686223 da5ff20293a405bfa802820c7508ffb1382c0509
parent 686074 dfb54d604158f5605fb07f41751e36bfef641a2f
child 737326 d1abf54a4d664d0785d729fd22fc314f63cbe70b
push id86125
push userbmo:a.beingessner@gmail.com
push dateWed, 25 Oct 2017 15:44:27 +0000
reviewersjrmuizel
bugs1401653
milestone58.0a1
Bug 1401653 - fixup webrender text-decoration bindings. r?jrmuizel This does 3 things that were all a bit too intermixed to split out cleanly: 1. Teaches TextDrawTarget to handle rectangular clips (while also completely forbidding other ones). This is necessary to handle how gecko "overdraws" decorations with clips to create the illusion of continuous lines when they're actually made out of multiple lines, possibly from different display items with different lines. Previously gecko *was* handing these clips to TextDrawTarget to use these clips, but we were just ignoring them. This is also necessary work to support partial glyphs natively (which apply rectangular clips to glyphs). Also note that this currently causes a bug in webrender if combined with zero-blur shadows, but it's not a regression since we already mishandle clipped decorations. I will work on fixing this upstream. 2. Changes the intermediate representation of lines from the old webrender format to a rect-based one. This is in preperation for webrender adopting that format in a future update. 3. Changes the way wavy lines are processed, correcting some errors in the old wavy line bindings that lead to them being positioned incorrectly. Also introduces a wavyLineThickness property that the will be required in a future webrender update. Wavy lines are unlike any other line, so it's ultimately desirable to distinguish them. The net result of these changes is that a companion upstream change (webrender#1923) will make decoration rendering nearly identical to gecko, and much nicer. However the clipped shadows issue will need to be seperately resolved before actually closing this issue. MozReview-Commit-ID: 6O2wLA6bU3C
gfx/webrender_bindings/WebRenderAPI.cpp
gfx/webrender_bindings/WebRenderAPI.h
gfx/webrender_bindings/src/bindings.rs
gfx/webrender_bindings/webrender_ffi_generated.h
layout/generic/TextDrawTarget.h
layout/painting/nsCSSRendering.cpp
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -1107,35 +1107,19 @@ DisplayListBuilder::PushText(const wr::L
                   aGlyphOptions);
 }
 
 void
 DisplayListBuilder::PushLine(const wr::LayoutRect& aClip,
                              bool aIsBackfaceVisible,
                              const wr::Line& aLine)
 {
- wr_dp_push_line(mWrState, aClip, aIsBackfaceVisible, aLine.baseline, aLine.start, aLine.end,
-                 aLine.orientation, aLine.width, aLine.color, aLine.style);
-
-/* TODO(Gankro): remove this
-  LayoutRect rect;
-  if (aLine.orientation == wr::LineOrientation::Horizontal) {
-    rect.origin.x = aLine.start;
-    rect.origin.y = aLine.baseline;
-    rect.size.width = aLine.end - aLine.start;
-    rect.size.height = aLine.width;
-  } else {
-    rect.origin.x = aLine.baseline;
-    rect.origin.y = aLine.start;
-    rect.size.width = aLine.width;
-    rect.size.height = aLine.end - aLine.start;
-  }
-
-  PushRect(rect, aClip, aLine.color);
-*/
+ wr_dp_push_line(mWrState, &aClip, aIsBackfaceVisible,
+                 &aLine.bounds, aLine.wavyLineThickness, aLine.orientation,
+                 &aLine.color, aLine.style);
 }
 
 void
 DisplayListBuilder::PushShadow(const wr::LayoutRect& aRect,
                                const wr::LayoutRect& aClip,
                                bool aIsBackfaceVisible,
                                const wr::Shadow& aShadow)
 {
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -37,22 +37,20 @@ namespace wr {
 
 class DisplayListBuilder;
 class RendererOGL;
 class RendererEvent;
 
 // This isn't part of WR's API, but we define it here to simplify layout's
 // logic and data plumbing.
 struct Line {
-  float baseline;
-  float start;
-  float end;
-  float width;
+  wr::LayoutRect bounds;
+  float wavyLineThickness;
+  wr::LineOrientation orientation;
   wr::ColorF color;
-  wr::LineOrientation orientation;
   wr::LineStyle style;
 };
 
 /// Updates to retained resources such as images and fonts, applied within the
 /// same transaction.
 class ResourceUpdateQueue {
 public:
   ResourceUpdateQueue();
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -1520,38 +1520,48 @@ pub extern "C" fn wr_dp_push_shadow(stat
 pub extern "C" fn wr_dp_pop_all_shadows(state: &mut WrState) {
     debug_assert!(unsafe { is_in_main_thread() });
 
     state.frame_builder.dl_builder.pop_all_shadows();
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_line(state: &mut WrState,
-                                  clip: LayoutRect,
+                                  clip: &LayoutRect,
                                   is_backface_visible: bool,
-                                  baseline: f32,
-                                  start: f32,
-                                  end: f32,
+                                  bounds: &LayoutRect,
+                                  _wavy_line_thickness: f32,
                                   orientation: LineOrientation,
-                                  width: f32,
-                                  color: ColorF,
+                                  color: &ColorF,
                                   style: LineStyle) {
     debug_assert!(unsafe { is_in_main_thread() });
 
-    let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(LayoutRect::zero(), clip.into());
+    // TODO: remove this once WR is updated to just check the bounds.
+    let (baseline, start, end, width) = match orientation {
+        LineOrientation::Horizontal => (bounds.origin.y,
+                                        bounds.origin.x,
+                                        bounds.origin.x + bounds.size.width,
+                                        bounds.size.height),
+        LineOrientation::Vertical => (bounds.origin.x,
+                                      bounds.origin.y,
+                                      bounds.origin.y + bounds.size.height,
+                                      bounds.size.width),
+    };
+
+    let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(*bounds, (*clip).into());
     prim_info.is_backface_visible = is_backface_visible;
     state.frame_builder
          .dl_builder
          .push_line(&prim_info,
                     baseline,
                     start,
                     end,
                     orientation,
                     width,
-                    color,
+                    *color,
                     style);
 
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_border(state: &mut WrState,
                                     rect: LayoutRect,
                                     clip: LayoutRect,
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -1,13 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-/* Generated with cbindgen:0.1.26 */
+/* Generated with cbindgen:0.1.25 */
 
 /* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen.
  * To generate this file:
  *   1. Get the latest cbindgen using `cargo install --force cbindgen`
  *      a. Alternatively, you can clone `https://github.com/rlhunt/cbindgen` and use a tagged release
  *   2. Run `rustup run nightly cbindgen toolkit/library/rust/ --crate webrender_bindings -o gfx/webrender_bindings/webrender_ffi_generated.h`
  */
 
@@ -314,27 +314,16 @@ struct WrOpacityProperty {
   float opacity;
 
   bool operator==(const WrOpacityProperty& aOther) const {
     return id == aOther.id &&
            opacity == aOther.opacity;
   }
 };
 
-// A 3d transform stored as a 4 by 4 matrix in row-major order in memory.
-//
-// Transforms can be parametrized over the source and destination units, to describe a
-// transformation from a space to another.
-// For example, `TypedTransform3D<f32, WordSpace, ScreenSpace>::transform_point3d`
-// takes a `TypedPoint3D<f32, WordSpace>` and returns a `TypedPoint3D<f32, ScreenSpace>`.
-//
-// Transforms expose a set of convenience methods for pre- and post-transformations.
-// A pre-transformation corresponds to adding an operation that is applied before
-// the rest of the transformation, while a post-transformation adds an operation
-// that is applied after.
 struct TypedTransform3D_f32__LayoutPixel__LayoutPixel {
   float m11;
   float m12;
   float m13;
   float m14;
   float m21;
   float m22;
   float m23;
@@ -407,17 +396,16 @@ struct ColorF {
   bool operator==(const ColorF& aOther) const {
     return r == aOther.r &&
            g == aOther.g &&
            b == aOther.b &&
            a == aOther.a;
   }
 };
 
-// A 2d Point tagged with a unit.
 struct TypedPoint2D_f32__LayerPixel {
   float x;
   float y;
 
   bool operator==(const TypedPoint2D_f32__LayerPixel& aOther) const {
     return x == aOther.x &&
            y == aOther.y;
   }
@@ -577,17 +565,16 @@ struct NinePatchDescriptor {
 
   bool operator==(const NinePatchDescriptor& aOther) const {
     return width == aOther.width &&
            height == aOther.height &&
            slice == aOther.slice;
   }
 };
 
-// A 2d Vector tagged with a unit.
 struct TypedVector2D_f32__LayerPixel {
   float x;
   float y;
 
   bool operator==(const TypedVector2D_f32__LayerPixel& aOther) const {
     return x == aOther.x &&
            y == aOther.y;
   }
@@ -658,17 +645,16 @@ struct ByteSlice {
   size_t len;
 
   bool operator==(const ByteSlice& aOther) const {
     return buffer == aOther.buffer &&
            len == aOther.len;
   }
 };
 
-// A 2d Point tagged with a unit.
 struct TypedPoint2D_u16__Tiles {
   uint16_t x;
   uint16_t y;
 
   bool operator==(const TypedPoint2D_u16__Tiles& aOther) const {
     return x == aOther.x &&
            y == aOther.y;
   }
@@ -1121,24 +1107,22 @@ void wr_dp_push_image(WrState *aState,
                       LayoutSize aStretchSize,
                       LayoutSize aTileSpacing,
                       ImageRendering aImageRendering,
                       WrImageKey aKey)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_line(WrState *aState,
-                     LayoutRect aClip,
+                     const LayoutRect *aClip,
                      bool aIsBackfaceVisible,
-                     float aBaseline,
-                     float aStart,
-                     float aEnd,
+                     const LayoutRect *aBounds,
+                     float aWavyLineThickness,
                      LineOrientation aOrientation,
-                     float aWidth,
-                     ColorF aColor,
+                     const ColorF *aColor,
                      LineStyle aStyle)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_linear_gradient(WrState *aState,
                                 LayoutRect aRect,
                                 LayoutRect aClip,
                                 bool aIsBackfaceVisible,
--- a/layout/generic/TextDrawTarget.h
+++ b/layout/generic/TextDrawTarget.h
@@ -133,16 +133,28 @@ public:
     glyphOptions.render_mode = wr::ToFontRenderMode(aOptions.mAntialiasMode, GetPermitSubpixelAA());
 
     mManager->WrBridge()->PushGlyphs(mBuilder, glyphs, aFont,
                                      color, mSc, mBoundsRect, mClipRect,
                                      mBackfaceVisible, &glyphOptions);
   }
 
   void
+  PushClipRect(const Rect &aRect) override {
+    auto rect = mSc.ToRelativeLayoutRect(LayoutDeviceRect::FromUnknownRect(aRect));
+    auto clipId = mBuilder.DefineClip(Nothing(), Nothing(), rect);
+    mBuilder.PushClip(clipId);
+  }
+
+  void
+  PopClip() override {
+    mBuilder.PopClip();
+  }
+
+  void
   AppendShadow(const wr::Shadow& aShadow)
   {
     mBuilder.PushShadow(mBoundsRect, mClipRect, mBackfaceVisible, aShadow);
     mHasShadows = true;
   }
 
   void
   TerminateShadows()
@@ -156,67 +168,101 @@ public:
   void
   AppendSelectionRect(const LayoutDeviceRect& aRect, const Color& aColor)
   {
     auto rect = wr::ToLayoutRect(aRect);
     auto color = wr::ToColorF(aColor);
     mBuilder.PushRect(rect, mClipRect, mBackfaceVisible, color);
   }
 
+
+  // This function is basically designed to slide into the decoration drawing
+  // code of nsCSSRendering with minimum disruption, to minimize the
+  // chances of implementation drift. As such, it mostly looks like a call
+  // to a skia-style StrokeLine method: two end-points, with a thickness
+  // and style. Notably the end-points are *centered* in the block direction,
+  // even though webrender wants a rect-like representation, where the points
+  // are on corners.
+  //
+  // So we mangle the format here in a single centralized place, where neither
+  // webrender nor nsCSSRendering has to care about this mismatch.
+  //
+  // NOTE: we assume the points are axis-aligned, and aStart should be used
+  // as the top-left corner of the rect.
   void
   AppendDecoration(const Point& aStart,
                    const Point& aEnd,
                    const float aThickness,
                    const bool aVertical,
                    const Color& aColor,
                    const uint8_t aStyle)
   {
-    wr::Line decoration;
+    auto pos = LayoutDevicePoint::FromUnknownPoint(aStart);
+    LayoutDeviceSize size;
 
-    // This function is basically designed to slide into the decoration drawing
-    // code of nsCSSRendering with minimum disruption, to minimize the
-    // chances of implementation drift. As such, it mostly looks like a call
-    // to a skia-style StrokeLine method: two end-points, with a thickness
-    // and style. Notably the end-points are *centered* in the block direction,
-    // even though webrender wants a rect-like representation, where the points
-    // are on corners.
-    //
-    // So we mangle the format here in a single centralized place, where neither
-    // webrender nor nsCSSRendering has to care about this mismatch.
-    decoration.baseline = (aVertical ? aStart.x : aStart.y) - aThickness / 2;
-    decoration.start = aVertical ? aStart.y : aStart.x;
-    decoration.end = aVertical ? aEnd.y : aEnd.x;
-    decoration.width = aThickness;
+    if (aVertical) {
+      pos.x -= aThickness / 2; // adjust from center to corner
+      size = LayoutDeviceSize(aThickness, aEnd.y - aStart.y);
+    } else {
+      pos.y -= aThickness / 2; // adjust from center to corner
+      size = LayoutDeviceSize(aEnd.x - aStart.x, aThickness);
+    }
+
+    wr::Line decoration;
+    decoration.bounds = mSc.ToRelativeLayoutRect(LayoutDeviceRect(pos, size));
+    decoration.wavyLineThickness = 0; // dummy value, unused
     decoration.color = wr::ToColorF(aColor);
     decoration.orientation = aVertical
       ? wr::LineOrientation::Vertical
       : wr::LineOrientation::Horizontal;
 
     switch (aStyle) {
       case NS_STYLE_TEXT_DECORATION_STYLE_SOLID:
         decoration.style = wr::LineStyle::Solid;
         break;
       case NS_STYLE_TEXT_DECORATION_STYLE_DOTTED:
         decoration.style = wr::LineStyle::Dotted;
         break;
       case NS_STYLE_TEXT_DECORATION_STYLE_DASHED:
         decoration.style = wr::LineStyle::Dashed;
         break;
+      // Wavy lines should go through AppendWavyDecoration
       case NS_STYLE_TEXT_DECORATION_STYLE_WAVY:
-        decoration.style = wr::LineStyle::Wavy;
-        break;
       // Double lines should be lowered to two solid lines
       case NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE:
       default:
         MOZ_CRASH("TextDrawTarget received unsupported line style");
     }
 
     mBuilder.PushLine(mClipRect, mBackfaceVisible, decoration);
   }
 
+  // Seperated out from AppendDecoration because Wavy Lines are completely
+  // different, and trying to merge the concept is more of a mess than it's
+  // worth.
+  void
+  AppendWavyDecoration(const Rect& aBounds,
+                       const float aThickness,
+                       const bool aVertical,
+                       const Color& aColor)
+  {
+    wr::Line decoration;
+
+    decoration.bounds = mSc.ToRelativeLayoutRect(
+      LayoutDeviceRect::FromUnknownRect(aBounds));
+    decoration.wavyLineThickness = aThickness;
+    decoration.color = wr::ToColorF(aColor);
+    decoration.orientation = aVertical
+      ? wr::LineOrientation::Vertical
+      : wr::LineOrientation::Horizontal;
+    decoration.style = wr::LineStyle::Wavy;
+
+    mBuilder.PushLine(mClipRect, mBackfaceVisible, decoration);
+  }
+
 private:
   // Whether anything unsupported was encountered. Currently:
   //
   // * Synthetic bold/italics
   // * SVG fonts
   // * Unserializable fonts
   // * Tofu glyphs
   // * Pratial ligatures
@@ -367,30 +413,24 @@ public:
   }
 
   bool Draw3DTransformedSurface(SourceSurface* aSurface,
                                 const Matrix4x4& aMatrix) override {
     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
   }
 
   void PushClip(const Path *aPath) override {
-    // Fine to pretend we do this
-  }
-
-  void PushClipRect(const Rect &aRect) override {
-    // Fine to pretend we do this
+    MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
   }
 
   void PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount) override {
     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
   }
 
-  void PopClip() override {
-    // Fine to pretend we do this
-  }
+
 
   void PushLayer(bool aOpaque, Float aOpacity,
                          SourceSurface* aMask,
                          const Matrix& aMaskTransform,
                          const IntRect& aBounds,
                          bool aCopyBackground) override {
     // Fine to pretend we do this
   }
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -4073,54 +4073,53 @@ nsCSSRendering::PaintDecorationLine(nsIF
        *  5. Goes up to top of the area at 45 degrees.
        *  6. Slides to right horizontaly.
        *  7. Repeat from 2 until reached to right-most edge of the area.
        *
        * In the vertical case, swap horizontal and vertical coordinates and
        * directions in the above description.
        */
 
-      // TODO(gankro)
-
       Float& rectICoord = aParams.vertical ? rect.y : rect.x;
       Float& rectISize = aParams.vertical ? rect.height : rect.width;
       const Float rectBSize = aParams.vertical ? rect.width : rect.height;
 
       const Float adv = rectBSize - lineThickness;
       const Float flatLengthAtVertex =
         std::max((lineThickness - 1.0) * 2.0, 1.0);
 
       // Align the start of wavy lines to the nearest ancestor block.
       const Float cycleLength = 2 * (adv + flatLengthAtVertex);
       rect = ExpandPaintingRectForDecorationLine(aFrame, aParams.style, rect,
                                                  aParams.icoordInFrame,
                                                  cycleLength, aParams.vertical);
+
+      if (textDrawer) {
+        // Undo attempted centering
+        Float& rectBCoord = aParams.vertical ? rect.x : rect.y;
+        rectBCoord -= lineThickness / 2;
+
+        textDrawer->AppendWavyDecoration(rect, lineThickness,
+                                         aParams.vertical, color);
+        return;
+      }
+
       // figure out if we can trim whole cycles from the left and right edges
       // of the line, to try and avoid creating an unnecessarily long and
-      // complex path
+      // complex path (but don't do this for webrender, )
       const Float dirtyRectICoord = aParams.vertical ? aParams.dirtyRect.y
                                                      : aParams.dirtyRect.x;
       int32_t skipCycles = floor((dirtyRectICoord - rectICoord) / cycleLength);
       if (skipCycles > 0) {
         rectICoord += skipCycles * cycleLength;
         rectISize -= skipCycles * cycleLength;
       }
 
       rectICoord += lineThickness / 2.0;
 
-      if (textDrawer) {
-        Point p1 = rect.TopLeft();
-        Point p2 = aParams.vertical ? rect.BottomLeft() : rect.TopRight();
-
-        textDrawer->AppendDecoration(
-          p1, p2, adv, aParams.vertical, color,
-          NS_STYLE_TEXT_DECORATION_STYLE_WAVY);
-        return;
-      }
-
       Point pt(rect.TopLeft());
       Float& ptICoord = aParams.vertical ? pt.y : pt.x;
       Float& ptBCoord = aParams.vertical ? pt.x : pt.y;
       if (aParams.vertical) {
         ptBCoord += adv;
       }
       Float iCoordLimit = ptICoord + rectISize + lineThickness;