| OLD | NEW |
| 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 Loading... |
| 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 |
| OLD | NEW |