tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsCSSRenderingGradients.h (8344B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef nsCSSRenderingGradients_h__
      8 #define nsCSSRenderingGradients_h__
      9 
     10 #include "Units.h"
     11 #include "gfxRect.h"
     12 #include "gfxUtils.h"
     13 #include "mozilla/gfx/2D.h"
     14 #include "mozilla/webrender/webrender_ffi.h"
     15 #include "nsStyleStruct.h"
     16 
     17 class gfxPattern;
     18 
     19 namespace mozilla {
     20 
     21 namespace layers {
     22 class StackingContextHelper;
     23 }  // namespace layers
     24 
     25 namespace wr {
     26 class DisplayListBuilder;
     27 }  // namespace wr
     28 
     29 // A resolved color stop, with a specific position along the gradient line and
     30 // a color.
     31 struct ColorStop {
     32  ColorStop() : mPosition(0), mIsMidpoint(false) {}
     33  ColorStop(double aPosition, bool aIsMidPoint,
     34            const StyleAbsoluteColor& aColor)
     35      : mPosition(aPosition), mIsMidpoint(aIsMidPoint), mColor(aColor) {}
     36  double mPosition;  // along the gradient line; 0=start, 1=end
     37  bool mIsMidpoint;
     38  StyleAbsoluteColor mColor;
     39 };
     40 
     41 template <class T>
     42 class MOZ_STACK_CLASS ColorStopInterpolator {
     43 public:
     44  ColorStopInterpolator(
     45      const nsTArray<ColorStop>& aStops,
     46      const StyleColorInterpolationMethod& aStyleColorInterpolationMethod,
     47      bool aExtend)
     48      : mStyleColorInterpolationMethod(aStyleColorInterpolationMethod),
     49        mStops(aStops),
     50        mExtend(aExtend) {}
     51 
     52  void CreateStops() {
     53    // This loop intentionally iterates extra stops at the beginning and end
     54    // if extending was requested, or in the degenerate case where only one
     55    // color stop was specified.
     56    const bool extend = mExtend || mStops.Length() == 1;
     57    const uint32_t iterStops = mStops.Length() - 1 + (extend ? 2 : 0);
     58    for (uint32_t i = 0; i < iterStops; i++) {
     59      auto thisindex = extend ? (i == 0 ? 0 : i - 1) : i;
     60      auto nextindex =
     61          extend && (i == iterStops - 1 || i == 0) ? thisindex : thisindex + 1;
     62      const auto& start = mStops[thisindex];
     63      const auto& end = mStops[nextindex];
     64      float startPosition = start.mPosition;
     65      float endPosition = end.mPosition;
     66      // For CSS non-repeating gradients with longer hue specified, we have to
     67      // pretend there is a stop beyond the last stop, and one before the first.
     68      // This is never the case on SVG gradients as they only use shorter hue.
     69      //
     70      // See https://bugzilla.mozilla.org/show_bug.cgi?id=1885716 for more info.
     71      uint32_t extraStops = 0;
     72      if (extend) {
     73        // If we're extending, we just need a single new stop, which will
     74        // duplicate the end being extended; do not create interpolated stops
     75        // within in the extension area!
     76        if (i == 0) {
     77          startPosition = std::min(startPosition, 0.0f);
     78          extraStops = 1;
     79        }
     80        if (i == iterStops - 1) {
     81          endPosition = std::max(endPosition, 1.0f);
     82          extraStops = 1;
     83        }
     84      }
     85      if (!extraStops) {
     86        // Within the actual gradient range, figure out how many extra stops
     87        // to use for this section of the gradient.
     88        extraStops = (uint32_t)(floor(endPosition * kFullRangeExtraStops) -
     89                                floor(startPosition * kFullRangeExtraStops));
     90        extraStops = std::clamp(extraStops, 1U, kFullRangeExtraStops);
     91      }
     92      float step = 1.0f / (float)extraStops;
     93      for (uint32_t extraStop = 0; extraStop <= extraStops; extraStop++) {
     94        auto progress = (float)extraStop * step;
     95        auto position =
     96            startPosition + progress * (endPosition - startPosition);
     97        StyleAbsoluteColor color =
     98            Servo_InterpolateColor(mStyleColorInterpolationMethod,
     99                                   &start.mColor, &end.mColor, progress);
    100        static_cast<T*>(this)->CreateStop(float(position),
    101                                          gfx::ToDeviceColor(color));
    102      }
    103    }
    104  }
    105 
    106 protected:
    107  StyleColorInterpolationMethod mStyleColorInterpolationMethod;
    108  const nsTArray<ColorStop>& mStops;
    109  // This indicates that we want to extend the endPosition on the last stop,
    110  // which only matters if this is a CSS non-repeating gradient with
    111  // StyleHueInterpolationMethod::Longer (only valid for hsl/hwb/lch/oklch).
    112  bool mExtend;
    113 
    114  // This could be made tunable, but at 1.0/128 the error is largely
    115  // irrelevant, as WebRender re-encodes it to 128 pairs of stops.
    116  //
    117  // Note that we don't attempt to place the positions of these stops
    118  // precisely at intervals, we just add this many extra stops across the
    119  // range where it is convenient.
    120  inline static const uint32_t kFullRangeExtraStops = 128;
    121 };
    122 
    123 class nsCSSGradientRenderer final {
    124 public:
    125  /**
    126   * Prepare a nsCSSGradientRenderer for a gradient for an element.
    127   * aIntrinsicSize - the size of the source gradient.
    128   */
    129  static nsCSSGradientRenderer Create(nsPresContext* aPresContext,
    130                                      ComputedStyle* aComputedStyle,
    131                                      const StyleGradient& aGradient,
    132                                      const nsSize& aIntrinsiceSize);
    133 
    134  /**
    135   * Draw the gradient to aContext
    136   * aDest - where the first tile of gradient is
    137   * aFill - the area to be filled with tiles of aDest
    138   * aSrc - the area of the gradient that will fill aDest
    139   * aRepeatSize - the distance from the origin of a tile
    140   *               to the next origin of a tile
    141   * aDirtyRect - pixels outside of this area may be skipped
    142   */
    143  void Paint(gfxContext& aContext, const nsRect& aDest, const nsRect& aFill,
    144             const nsSize& aRepeatSize, const mozilla::CSSIntRect& aSrc,
    145             const nsRect& aDirtyRect, float aOpacity = 1.0);
    146 
    147  /**
    148   * Collect the gradient parameters
    149   */
    150  void BuildWebRenderParameters(float aOpacity, wr::ExtendMode& aMode,
    151                                nsTArray<wr::GradientStop>& aStops,
    152                                LayoutDevicePoint& aLineStart,
    153                                LayoutDevicePoint& aLineEnd,
    154                                LayoutDeviceSize& aGradientRadius,
    155                                LayoutDevicePoint& aGradientCenter,
    156                                float& aGradientAngle);
    157 
    158  /**
    159   * Build display items for the gradient
    160   * aLayer - the layer to make this display item relative to
    161   * aDest - where the first tile of gradient is
    162   * aFill - the area to be filled with tiles of aDest
    163   * aRepeatSize - the distance from the origin of a tile
    164   *               to the next origin of a tile
    165   * aSrc - the area of the gradient that will fill aDest
    166   */
    167  void BuildWebRenderDisplayItems(wr::DisplayListBuilder& aBuilder,
    168                                  const layers::StackingContextHelper& aSc,
    169                                  const nsRect& aDest, const nsRect& aFill,
    170                                  const nsSize& aRepeatSize,
    171                                  const mozilla::CSSIntRect& aSrc,
    172                                  bool aIsBackfaceVisible,
    173                                  float aOpacity = 1.0);
    174 
    175 private:
    176  nsCSSGradientRenderer()
    177      : mPresContext(nullptr),
    178        mGradient(nullptr),
    179        mRadiusX(0.0),
    180        mRadiusY(0.0),
    181        mAngle(0.0) {}
    182 
    183  /**
    184   * Attempts to paint the tiles for a gradient by painting it once to an
    185   * offscreen surface and then painting that offscreen surface with
    186   * ExtendMode::Repeat to cover all tiles.
    187   *
    188   * Returns false if the optimization wasn't able to be used, in which case
    189   * a fallback should be used.
    190   */
    191  bool TryPaintTilesWithExtendMode(
    192      gfxContext& aContext, gfxPattern* aGradientPattern, nscoord aXStart,
    193      nscoord aYStart, const gfxRect& aDirtyAreaToFill, const nsRect& aDest,
    194      const nsSize& aRepeatSize, bool aForceRepeatToCoverTiles);
    195 
    196  nsPresContext* mPresContext;
    197  const StyleGradient* mGradient;
    198  nsTArray<ColorStop> mStops;
    199  gfxPoint mLineStart, mLineEnd;  // only for linear/radial gradients
    200  double mRadiusX, mRadiusY;      // only for radial gradients
    201  gfxPoint mCenter;               // only for conic gradients
    202  float mAngle;                   // only for conic gradients
    203 };
    204 
    205 }  // namespace mozilla
    206 
    207 #endif /* nsCSSRenderingGradients_h__ */