Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(140)

Side by Side Diff: third_party/WebKit/Source/platform/graphics/Gradient.cpp

Issue 2785203002: Split Gradient impl into separate classes (Closed)
Patch Set: review Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « third_party/WebKit/Source/platform/graphics/Gradient.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2006, 2007, 2008, 2010 Apple Inc. All rights reserved. 2 * Copyright (C) 2006, 2007, 2008, 2010 Apple Inc. All rights reserved.
3 * Copyright (C) 2007 Alp Toker <alp@atoker.com> 3 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
4 * Copyright (C) 2013 Google Inc. All rights reserved. 4 * Copyright (C) 2013 Google Inc. All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions 7 * modification, are permitted provided that the following conditions
8 * are met: 8 * are met:
9 * 1. Redistributions of source code must retain the above copyright 9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer. 10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright 11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the 12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution. 13 * documentation and/or other materials provided with the distribution.
14 * 14 *
15 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 15 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27 27
28 #include "platform/graphics/Gradient.h" 28 #include "platform/graphics/Gradient.h"
29 29
30 #include <algorithm>
30 #include "platform/geometry/FloatRect.h" 31 #include "platform/geometry/FloatRect.h"
31 #include "platform/graphics/GraphicsContext.h" 32 #include "platform/graphics/GraphicsContext.h"
32 #include "platform/graphics/paint/PaintShader.h" 33 #include "platform/graphics/paint/PaintShader.h"
33 #include "platform/graphics/skia/SkiaUtils.h" 34 #include "platform/graphics/skia/SkiaUtils.h"
34 #include "third_party/skia/include/core/SkColor.h" 35 #include "third_party/skia/include/core/SkColor.h"
35 #include "third_party/skia/include/core/SkMatrix.h" 36 #include "third_party/skia/include/core/SkMatrix.h"
36 #include "third_party/skia/include/core/SkShader.h" 37 #include "third_party/skia/include/core/SkShader.h"
38 #include "third_party/skia/include/core/SkTLazy.h"
37 #include "third_party/skia/include/effects/SkGradientShader.h" 39 #include "third_party/skia/include/effects/SkGradientShader.h"
38 #include <algorithm>
39
40 typedef Vector<SkScalar, 8> ColorStopOffsetVector;
41 typedef Vector<SkColor, 8> ColorStopColorVector;
42 40
43 namespace blink { 41 namespace blink {
44 42
45 Gradient::Gradient(const FloatPoint& p0, 43 Gradient::Gradient(Type type,
46 const FloatPoint& p1,
47 GradientSpreadMethod spreadMethod, 44 GradientSpreadMethod spreadMethod,
48 ColorInterpolation interpolation) 45 ColorInterpolation interpolation)
49 : m_p0(p0), 46 : m_type(type),
50 m_p1(p1),
51 m_r0(0),
52 m_r1(0),
53 m_aspectRatio(1),
54 m_radial(false),
55 m_stopsSorted(false),
56 m_spreadMethod(spreadMethod), 47 m_spreadMethod(spreadMethod),
57 m_colorInterpolation(interpolation) {} 48 m_colorInterpolation(interpolation),
58 49 m_stopsSorted(true) {}
59 Gradient::Gradient(const FloatPoint& p0,
60 float r0,
61 const FloatPoint& p1,
62 float r1,
63 float aspectRatio,
64 GradientSpreadMethod spreadMethod,
65 ColorInterpolation interpolation)
66 : m_p0(p0),
67 m_p1(p1),
68 m_r0(r0),
69 m_r1(r1),
70 m_aspectRatio(aspectRatio),
71 m_radial(true),
72 m_stopsSorted(false),
73 m_spreadMethod(spreadMethod),
74 m_colorInterpolation(interpolation) {}
75 50
76 Gradient::~Gradient() {} 51 Gradient::~Gradient() {}
77 52
78 static inline bool compareStops(const Gradient::ColorStop& a, 53 static inline bool compareStops(const Gradient::ColorStop& a,
79 const Gradient::ColorStop& b) { 54 const Gradient::ColorStop& b) {
80 return a.stop < b.stop; 55 return a.stop < b.stop;
81 } 56 }
82 57
83 void Gradient::addColorStop(const Gradient::ColorStop& stop) { 58 void Gradient::addColorStop(const Gradient::ColorStop& stop) {
84 if (m_stops.isEmpty()) { 59 if (m_stops.isEmpty()) {
(...skipping 26 matching lines...) Expand all
111 // FIXME: This would be more at home as Color::operator SkColor. 86 // FIXME: This would be more at home as Color::operator SkColor.
112 static inline SkColor makeSkColor(const Color& c) { 87 static inline SkColor makeSkColor(const Color& c) {
113 return SkColorSetARGB(c.alpha(), c.red(), c.green(), c.blue()); 88 return SkColorSetARGB(c.alpha(), c.red(), c.green(), c.blue());
114 } 89 }
115 90
116 // Collect sorted stop position and color information into the pos and colors 91 // Collect sorted stop position and color information into the pos and colors
117 // buffers, ensuring stops at both 0.0 and 1.0. 92 // buffers, ensuring stops at both 0.0 and 1.0.
118 // TODO(fmalita): theoretically Skia should provide the same 0.0/1.0 padding 93 // TODO(fmalita): theoretically Skia should provide the same 0.0/1.0 padding
119 // (making this logic redundant), but in practice there are rendering diffs; 94 // (making this logic redundant), but in practice there are rendering diffs;
120 // investigate. 95 // investigate.
121 static void fillStops(const Vector<Gradient::ColorStop, 2>& stops, 96 void Gradient::fillSkiaStops(ColorBuffer& colors, OffsetBuffer& pos) const {
122 ColorStopOffsetVector& pos, 97 if (m_stops.isEmpty()) {
123 ColorStopColorVector& colors) {
124 if (stops.isEmpty()) {
125 // A gradient with no stops must be transparent black. 98 // A gradient with no stops must be transparent black.
126 pos.push_back(WebCoreFloatToSkScalar(0)); 99 pos.push_back(WebCoreFloatToSkScalar(0));
127 colors.push_back(SK_ColorTRANSPARENT); 100 colors.push_back(SK_ColorTRANSPARENT);
128 } else if (stops.front().stop > 0) { 101 } else if (m_stops.front().stop > 0) {
129 // Copy the first stop to 0.0. The first stop position may have a slight 102 // Copy the first stop to 0.0. The first stop position may have a slight
130 // rounding error, but we don't care in this float comparison, since 103 // rounding error, but we don't care in this float comparison, since
131 // 0.0 comes through cleanly and people aren't likely to want a gradient 104 // 0.0 comes through cleanly and people aren't likely to want a gradient
132 // with a stop at (0 + epsilon). 105 // with a stop at (0 + epsilon).
133 pos.push_back(WebCoreFloatToSkScalar(0)); 106 pos.push_back(WebCoreFloatToSkScalar(0));
134 colors.push_back(makeSkColor(stops.front().color)); 107 colors.push_back(makeSkColor(m_stops.front().color));
135 } 108 }
136 109
137 for (const auto& stop : stops) { 110 for (const auto& stop : m_stops) {
138 pos.push_back(WebCoreFloatToSkScalar(stop.stop)); 111 pos.push_back(WebCoreFloatToSkScalar(stop.stop));
139 colors.push_back(makeSkColor(stop.color)); 112 colors.push_back(makeSkColor(stop.color));
140 } 113 }
141 114
142 // Copy the last stop to 1.0 if needed. See comment above about this float 115 // Copy the last stop to 1.0 if needed. See comment above about this float
143 // comparison. 116 // comparison.
144 DCHECK(!pos.isEmpty()); 117 DCHECK(!pos.isEmpty());
145 if (pos.back() < 1) { 118 if (pos.back() < 1) {
146 pos.push_back(WebCoreFloatToSkScalar(1)); 119 pos.push_back(WebCoreFloatToSkScalar(1));
147 colors.push_back(colors.back()); 120 colors.push_back(colors.back());
148 } 121 }
149 } 122 }
150 123
151 sk_sp<PaintShader> Gradient::createShader(const SkMatrix& localMatrix) { 124 sk_sp<PaintShader> Gradient::createShaderInternal(const SkMatrix& localMatrix) {
152 sortStopsIfNecessary(); 125 sortStopsIfNecessary();
153 ASSERT(m_stopsSorted); 126 DCHECK(m_stopsSorted);
154 127
155 ColorStopOffsetVector pos; 128 ColorBuffer colors;
156 ColorStopColorVector colors; 129 colors.reserveCapacity(m_stops.size());
130 OffsetBuffer pos;
157 pos.reserveCapacity(m_stops.size()); 131 pos.reserveCapacity(m_stops.size());
158 colors.reserveCapacity(m_stops.size());
159 132
160 fillStops(m_stops, pos, colors); 133 fillSkiaStops(colors, pos);
161 DCHECK_GE(pos.size(), 2ul); 134 DCHECK_GE(colors.size(), 2ul);
162 DCHECK_EQ(pos.size(), colors.size()); 135 DCHECK_EQ(pos.size(), colors.size());
163 136
164 SkShader::TileMode tile = SkShader::kClamp_TileMode; 137 SkShader::TileMode tile = SkShader::kClamp_TileMode;
165 switch (m_spreadMethod) { 138 switch (m_spreadMethod) {
166 case SpreadMethodReflect: 139 case SpreadMethodReflect:
167 tile = SkShader::kMirror_TileMode; 140 tile = SkShader::kMirror_TileMode;
168 break; 141 break;
169 case SpreadMethodRepeat: 142 case SpreadMethodRepeat:
170 tile = SkShader::kRepeat_TileMode; 143 tile = SkShader::kRepeat_TileMode;
171 break; 144 break;
172 case SpreadMethodPad: 145 case SpreadMethodPad:
173 tile = SkShader::kClamp_TileMode; 146 tile = SkShader::kClamp_TileMode;
174 break; 147 break;
175 } 148 }
176 149
177 sk_sp<SkShader> shader;
178 uint32_t flags = m_colorInterpolation == ColorInterpolation::Premultiplied 150 uint32_t flags = m_colorInterpolation == ColorInterpolation::Premultiplied
179 ? SkGradientShader::kInterpolateColorsInPremul_Flag 151 ? SkGradientShader::kInterpolateColorsInPremul_Flag
180 : 0; 152 : 0;
181 if (m_radial) { 153 sk_sp<SkShader> shader = createShader(colors, pos, tile, flags, localMatrix);
182 SkMatrix adjustedLocalMatrix = localMatrix;
183
184 if (m_aspectRatio != 1) {
185 // CSS3 elliptical gradients: apply the elliptical scaling at the
186 // gradient center point.
187 adjustedLocalMatrix.preTranslate(m_p0.x(), m_p0.y());
188 adjustedLocalMatrix.preScale(1, 1 / m_aspectRatio);
189 adjustedLocalMatrix.preTranslate(-m_p0.x(), -m_p0.y());
190 ASSERT(m_p0 == m_p1);
191 }
192
193 // Since the two-point radial gradient is slower than the plain radial,
194 // only use it if we have to.
195 if (m_p0 == m_p1 && m_r0 <= 0.0f) {
196 shader = SkGradientShader::MakeRadial(
197 m_p1.data(), m_r1, colors.data(), pos.data(),
198 static_cast<int>(colors.size()), tile, flags, &adjustedLocalMatrix);
199 } else {
200 // The radii we give to Skia must be positive. If we're given a
201 // negative radius, ask for zero instead.
202 SkScalar radius0 = m_r0 >= 0.0f ? WebCoreFloatToSkScalar(m_r0) : 0;
203 SkScalar radius1 = m_r1 >= 0.0f ? WebCoreFloatToSkScalar(m_r1) : 0;
204 shader = SkGradientShader::MakeTwoPointConical(
205 m_p0.data(), radius0, m_p1.data(), radius1, colors.data(), pos.data(),
206 static_cast<int>(colors.size()), tile, flags, &adjustedLocalMatrix);
207 }
208 } else {
209 SkPoint pts[2] = {m_p0.data(), m_p1.data()};
210 shader = SkGradientShader::MakeLinear(pts, colors.data(), pos.data(),
211 static_cast<int>(colors.size()), tile,
212 flags, &localMatrix);
213 }
214
215 if (!shader) { 154 if (!shader) {
216 // use last color, since our "geometry" was degenerate (e.g. radius==0) 155 // use last color, since our "geometry" was degenerate (e.g. radius==0)
217 shader = SkShader::MakeColorShader(colors.back()); 156 shader = SkShader::MakeColorShader(colors.back());
218 } 157 }
219 158
220 return WrapSkShader(shader); 159 return WrapSkShader(std::move(shader));
221 } 160 }
222 161
223 void Gradient::applyToFlags(PaintFlags& flags, const SkMatrix& localMatrix) { 162 void Gradient::applyToFlags(PaintFlags& flags, const SkMatrix& localMatrix) {
224 if (!m_cachedShader || localMatrix != m_cachedShader->getLocalMatrix()) 163 if (!m_cachedShader || localMatrix != m_cachedShader->getLocalMatrix())
225 m_cachedShader = createShader(localMatrix); 164 m_cachedShader = createShaderInternal(localMatrix);
226 165
227 flags.setShader(m_cachedShader); 166 flags.setShader(m_cachedShader);
228 167
229 // Legacy behavior: gradients are always dithered. 168 // Legacy behavior: gradients are always dithered.
230 flags.setDither(true); 169 flags.setDither(true);
231 } 170 }
232 171
172 namespace {
173
174 class LinearGradient final : public Gradient {
175 public:
176 LinearGradient(const FloatPoint& p0,
177 const FloatPoint& p1,
178 GradientSpreadMethod spreadMethod,
179 ColorInterpolation interpolation)
180 : Gradient(Type::Linear, spreadMethod, interpolation),
181 m_p0(p0),
182 m_p1(p1) {}
183
184 protected:
185 sk_sp<SkShader> createShader(const ColorBuffer& colors,
186 const OffsetBuffer& pos,
187 SkShader::TileMode tileMode,
188 uint32_t flags,
189 const SkMatrix& localMatrix) const override {
190 SkPoint pts[2] = {m_p0.data(), m_p1.data()};
191 return SkGradientShader::MakeLinear(pts, colors.data(), pos.data(),
192 static_cast<int>(colors.size()),
193 tileMode, flags, &localMatrix);
194 }
195
196 private:
197 FloatPoint m_p0;
198 FloatPoint m_p1;
199 };
200
201 class RadialGradient final : public Gradient {
202 public:
203 RadialGradient(const FloatPoint& p0,
204 float r0,
205 const FloatPoint& p1,
206 float r1,
207 float aspectRatio,
208 GradientSpreadMethod spreadMethod,
209 ColorInterpolation interpolation)
210 : Gradient(Type::Radial, spreadMethod, interpolation),
211 m_p0(p0),
212 m_p1(p1),
213 m_r0(r0),
214 m_r1(r1),
215 m_aspectRatio(aspectRatio) {}
216
217 protected:
218 sk_sp<SkShader> createShader(const ColorBuffer& colors,
219 const OffsetBuffer& pos,
220 SkShader::TileMode tileMode,
221 uint32_t flags,
222 const SkMatrix& localMatrix) const override {
223 SkTCopyOnFirstWrite<SkMatrix> adjustedLocalMatrix(localMatrix);
224 if (m_aspectRatio != 1) {
225 // CSS3 elliptical gradients: apply the elliptical scaling at the
226 // gradient center point.
227 DCHECK(m_p0 == m_p1);
228 adjustedLocalMatrix.writable()->preScale(1, 1 / m_aspectRatio, m_p0.x(),
229 m_p0.y());
230 }
231
232 // Since the two-point radial gradient is slower than the plain radial,
233 // only use it if we have to.
234 if (m_p0 == m_p1 && m_r0 <= 0.0f) {
235 return SkGradientShader::MakeRadial(m_p1.data(), m_r1, colors.data(),
236 pos.data(),
237 static_cast<int>(colors.size()),
238 tileMode, flags, adjustedLocalMatrix);
239 }
240
241 // The radii we give to Skia must be positive. If we're given a
242 // negative radius, ask for zero instead.
243 const SkScalar radius0 = std::max(WebCoreFloatToSkScalar(m_r0), 0.0f);
244 const SkScalar radius1 = std::max(WebCoreFloatToSkScalar(m_r1), 0.0f);
245 return SkGradientShader::MakeTwoPointConical(
246 m_p0.data(), radius0, m_p1.data(), radius1, colors.data(), pos.data(),
247 static_cast<int>(colors.size()), tileMode, flags, adjustedLocalMatrix);
248 }
249
250 private:
251 FloatPoint m_p0;
252 FloatPoint m_p1;
253 float m_r0;
254 float m_r1;
255 float m_aspectRatio; // For elliptical gradient, width / height.
256 };
257
258 } // anonymous ns
259
260 PassRefPtr<Gradient> Gradient::createLinear(const FloatPoint& p0,
261 const FloatPoint& p1,
262 GradientSpreadMethod spreadMethod,
263 ColorInterpolation interpolation) {
264 return adoptRef(new LinearGradient(p0, p1, spreadMethod, interpolation));
265 }
266
267 PassRefPtr<Gradient> Gradient::createRadial(const FloatPoint& p0,
268 float r0,
269 const FloatPoint& p1,
270 float r1,
271 float aspectRatio,
272 GradientSpreadMethod spreadMethod,
273 ColorInterpolation interpolation) {
274 return adoptRef(new RadialGradient(p0, r0, p1, r1, aspectRatio, spreadMethod,
275 interpolation));
276 }
277
233 } // namespace blink 278 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/platform/graphics/Gradient.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698