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

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

Issue 2785203002: Split Gradient impl into separate classes (Closed)
Patch Set: anonymous subclasses 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
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
76 Gradient::~Gradient() {}
77 50
78 static inline bool compareStops(const Gradient::ColorStop& a, 51 static inline bool compareStops(const Gradient::ColorStop& a,
79 const Gradient::ColorStop& b) { 52 const Gradient::ColorStop& b) {
80 return a.stop < b.stop; 53 return a.stop < b.stop;
81 } 54 }
82 55
83 void Gradient::addColorStop(const Gradient::ColorStop& stop) { 56 void Gradient::addColorStop(const Gradient::ColorStop& stop) {
84 if (m_stops.isEmpty()) { 57 if (m_stops.isEmpty()) {
85 m_stopsSorted = true; 58 m_stopsSorted = true;
86 } else { 59 } else {
(...skipping 24 matching lines...) Expand all
111 // FIXME: This would be more at home as Color::operator SkColor. 84 // FIXME: This would be more at home as Color::operator SkColor.
112 static inline SkColor makeSkColor(const Color& c) { 85 static inline SkColor makeSkColor(const Color& c) {
113 return SkColorSetARGB(c.alpha(), c.red(), c.green(), c.blue()); 86 return SkColorSetARGB(c.alpha(), c.red(), c.green(), c.blue());
114 } 87 }
115 88
116 // Collect sorted stop position and color information into the pos and colors 89 // Collect sorted stop position and color information into the pos and colors
117 // buffers, ensuring stops at both 0.0 and 1.0. 90 // buffers, ensuring stops at both 0.0 and 1.0.
118 // TODO(fmalita): theoretically Skia should provide the same 0.0/1.0 padding 91 // 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; 92 // (making this logic redundant), but in practice there are rendering diffs;
120 // investigate. 93 // investigate.
121 static void fillStops(const Vector<Gradient::ColorStop, 2>& stops, 94 void Gradient::fillSkiaStops(ColorBuffer& colors, OffsetBuffer& pos) const {
122 ColorStopOffsetVector& pos, 95 if (m_stops.isEmpty()) {
123 ColorStopColorVector& colors) {
124 if (stops.isEmpty()) {
125 // A gradient with no stops must be transparent black. 96 // A gradient with no stops must be transparent black.
126 pos.push_back(WebCoreFloatToSkScalar(0)); 97 pos.push_back(WebCoreFloatToSkScalar(0));
127 colors.push_back(SK_ColorTRANSPARENT); 98 colors.push_back(SK_ColorTRANSPARENT);
128 } else if (stops.front().stop > 0) { 99 } else if (m_stops.front().stop > 0) {
129 // Copy the first stop to 0.0. The first stop position may have a slight 100 // 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 101 // 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 102 // 0.0 comes through cleanly and people aren't likely to want a gradient
132 // with a stop at (0 + epsilon). 103 // with a stop at (0 + epsilon).
133 pos.push_back(WebCoreFloatToSkScalar(0)); 104 pos.push_back(WebCoreFloatToSkScalar(0));
134 colors.push_back(makeSkColor(stops.front().color)); 105 colors.push_back(makeSkColor(m_stops.front().color));
135 } 106 }
136 107
137 for (const auto& stop : stops) { 108 for (const auto& stop : m_stops) {
138 pos.push_back(WebCoreFloatToSkScalar(stop.stop)); 109 pos.push_back(WebCoreFloatToSkScalar(stop.stop));
139 colors.push_back(makeSkColor(stop.color)); 110 colors.push_back(makeSkColor(stop.color));
140 } 111 }
141 112
142 // Copy the last stop to 1.0 if needed. See comment above about this float 113 // Copy the last stop to 1.0 if needed. See comment above about this float
143 // comparison. 114 // comparison.
144 DCHECK(!pos.isEmpty()); 115 DCHECK(!pos.isEmpty());
145 if (pos.back() < 1) { 116 if (pos.back() < 1) {
146 pos.push_back(WebCoreFloatToSkScalar(1)); 117 pos.push_back(WebCoreFloatToSkScalar(1));
147 colors.push_back(colors.back()); 118 colors.push_back(colors.back());
148 } 119 }
149 } 120 }
150 121
151 sk_sp<PaintShader> Gradient::createShader(const SkMatrix& localMatrix) { 122 sk_sp<PaintShader> Gradient::createShaderInternal(const SkMatrix& localMatrix) {
152 sortStopsIfNecessary(); 123 sortStopsIfNecessary();
153 ASSERT(m_stopsSorted); 124 DCHECK(m_stopsSorted);
154 125
155 ColorStopOffsetVector pos; 126 ColorBuffer colors;
156 ColorStopColorVector colors; 127 colors.reserveCapacity(m_stops.size());
128 OffsetBuffer pos;
157 pos.reserveCapacity(m_stops.size()); 129 pos.reserveCapacity(m_stops.size());
158 colors.reserveCapacity(m_stops.size());
159 130
160 fillStops(m_stops, pos, colors); 131 fillSkiaStops(colors, pos);
161 DCHECK_GE(pos.size(), 2ul); 132 DCHECK_GE(colors.size(), 2ul);
162 DCHECK_EQ(pos.size(), colors.size()); 133 DCHECK_EQ(pos.size(), colors.size());
163 134
164 SkShader::TileMode tile = SkShader::kClamp_TileMode; 135 SkShader::TileMode tile = SkShader::kClamp_TileMode;
165 switch (m_spreadMethod) { 136 switch (m_spreadMethod) {
166 case SpreadMethodReflect: 137 case SpreadMethodReflect:
167 tile = SkShader::kMirror_TileMode; 138 tile = SkShader::kMirror_TileMode;
168 break; 139 break;
169 case SpreadMethodRepeat: 140 case SpreadMethodRepeat:
170 tile = SkShader::kRepeat_TileMode; 141 tile = SkShader::kRepeat_TileMode;
171 break; 142 break;
172 case SpreadMethodPad: 143 case SpreadMethodPad:
173 tile = SkShader::kClamp_TileMode; 144 tile = SkShader::kClamp_TileMode;
174 break; 145 break;
175 } 146 }
176 147
177 sk_sp<SkShader> shader;
178 uint32_t flags = m_colorInterpolation == ColorInterpolation::Premultiplied 148 uint32_t flags = m_colorInterpolation == ColorInterpolation::Premultiplied
179 ? SkGradientShader::kInterpolateColorsInPremul_Flag 149 ? SkGradientShader::kInterpolateColorsInPremul_Flag
180 : 0; 150 : 0;
181 if (m_radial) { 151 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) { 152 if (!shader) {
216 // use last color, since our "geometry" was degenerate (e.g. radius==0) 153 // use last color, since our "geometry" was degenerate (e.g. radius==0)
217 shader = SkShader::MakeColorShader(colors.back()); 154 shader = SkShader::MakeColorShader(colors.back());
218 } 155 }
219 156
220 return WrapSkShader(shader); 157 return WrapSkShader(std::move(shader));
221 } 158 }
222 159
223 void Gradient::applyToFlags(PaintFlags& flags, const SkMatrix& localMatrix) { 160 void Gradient::applyToFlags(PaintFlags& flags, const SkMatrix& localMatrix) {
224 if (!m_cachedShader || localMatrix != m_cachedShader->getLocalMatrix()) 161 if (!m_cachedShader || localMatrix != m_cachedShader->getLocalMatrix())
225 m_cachedShader = createShader(localMatrix); 162 m_cachedShader = createShaderInternal(localMatrix);
226 163
227 flags.setShader(m_cachedShader); 164 flags.setShader(m_cachedShader);
228 165
229 // Legacy behavior: gradients are always dithered. 166 // Legacy behavior: gradients are always dithered.
230 flags.setDither(true); 167 flags.setDither(true);
231 } 168 }
232 169
170 namespace {
171
172 class LinearGradient final : public Gradient {
173 public:
174 LinearGradient(const FloatPoint& p0,
175 const FloatPoint& p1,
176 GradientSpreadMethod spreadMethod,
177 ColorInterpolation interpolation)
178 : Gradient(Type::Linear, spreadMethod, interpolation),
179 m_p0(p0),
180 m_p1(p1) {}
181
182 protected:
183 sk_sp<SkShader> createShader(const ColorBuffer& colors,
184 const OffsetBuffer& pos,
185 SkShader::TileMode tileMode,
186 uint32_t flags,
187 const SkMatrix& localMatrix) const override {
188 SkPoint pts[2] = {m_p0.data(), m_p1.data()};
189 return SkGradientShader::MakeLinear(pts, colors.data(), pos.data(),
190 static_cast<int>(colors.size()),
191 tileMode, flags, &localMatrix);
192 }
193
194 private:
195 FloatPoint m_p0;
196 FloatPoint m_p1;
197 };
198
199 class RadialGradient final : public Gradient {
200 public:
201 RadialGradient(const FloatPoint& p0,
202 float r0,
203 const FloatPoint& p1,
204 float r1,
205 float aspectRatio,
206 GradientSpreadMethod spreadMethod,
207 ColorInterpolation interpolation)
208 : Gradient(Type::Radial, spreadMethod, interpolation),
209 m_p0(p0),
210 m_p1(p1),
211 m_r0(r0),
212 m_r1(r1),
213 m_aspectRatio(aspectRatio) {}
214
215 protected:
216 sk_sp<SkShader> createShader(const ColorBuffer& colors,
217 const OffsetBuffer& pos,
218 SkShader::TileMode tileMode,
219 uint32_t flags,
220 const SkMatrix& localMatrix) const override {
221 SkTCopyOnFirstWrite<SkMatrix> adjustedLocalMatrix(localMatrix);
222 if (m_aspectRatio != 1) {
223 // CSS3 elliptical gradients: apply the elliptical scaling at the
224 // gradient center point.
225 DCHECK(m_p0 == m_p1);
226 adjustedLocalMatrix.writable()->preScale(1, 1 / m_aspectRatio, m_p0.x(),
227 m_p0.y());
228 }
229
230 // Since the two-point radial gradient is slower than the plain radial,
231 // only use it if we have to.
232 if (m_p0 == m_p1 && m_r0 <= 0.0f) {
233 return SkGradientShader::MakeRadial(m_p1.data(), m_r1, colors.data(),
234 pos.data(),
235 static_cast<int>(colors.size()),
236 tileMode, flags, adjustedLocalMatrix);
237 }
238
239 // The radii we give to Skia must be positive. If we're given a
240 // negative radius, ask for zero instead.
241 const SkScalar radius0 = std::max(WebCoreFloatToSkScalar(m_r0), 0.0f);
242 const SkScalar radius1 = std::max(WebCoreFloatToSkScalar(m_r1), 0.0f);
243 return SkGradientShader::MakeTwoPointConical(
244 m_p0.data(), radius0, m_p1.data(), radius1, colors.data(), pos.data(),
245 static_cast<int>(colors.size()), tileMode, flags, adjustedLocalMatrix);
246 }
247
248 private:
249 FloatPoint m_p0;
250 FloatPoint m_p1;
251 float m_r0;
252 float m_r1;
253 float m_aspectRatio; // For elliptical gradient, width / height.
254 };
255
256 } // anonymous ns
257
258 PassRefPtr<Gradient> Gradient::createLinear(const FloatPoint& p0,
259 const FloatPoint& p1,
260 GradientSpreadMethod spreadMethod,
261 ColorInterpolation interpolation) {
262 return adoptRef(new LinearGradient(p0, p1, spreadMethod, interpolation));
263 }
264
265 PassRefPtr<Gradient> Gradient::createRadial(const FloatPoint& p0,
266 float r0,
267 const FloatPoint& p1,
268 float r1,
269 float aspectRatio,
270 GradientSpreadMethod spreadMethod,
271 ColorInterpolation interpolation) {
272 return adoptRef(new RadialGradient(p0, r0, p1, r1, aspectRatio, spreadMethod,
273 interpolation));
274 }
275
233 } // namespace blink 276 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698