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