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__ */