1635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project/*
2635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * Copyright (c) 2008, Google Inc. All rights reserved.
3635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project *
4635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * Redistribution and use in source and binary forms, with or without
5635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * modification, are permitted provided that the following conditions are
6635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * met:
7635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project *
8635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project *     * Redistributions of source code must retain the above copyright
9635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * notice, this list of conditions and the following disclaimer.
10635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project *     * Redistributions in binary form must reproduce the above
11635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * copyright notice, this list of conditions and the following disclaimer
12635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * in the documentation and/or other materials provided with the
13635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * distribution.
14635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project *     * Neither the name of Google Inc. nor the names of its
15635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * contributors may be used to endorse or promote products derived from
16635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * this software without specific prior written permission.
17635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project *
18635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project */
30635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
31635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project#include "config.h"
32635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project#include "Gradient.h"
33635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
34635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project#include "CSSParser.h"
35635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project#include "GraphicsContext.h"
36635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
37635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project#include "SkGradientShader.h"
38635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project#include "SkiaUtils.h"
39635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
40635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Projectnamespace WebCore {
41635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
42635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Projectvoid Gradient::platformDestroy()
43635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project{
44635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    if (m_gradient)
45bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen        SkSafeUnref(m_gradient);
46635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    m_gradient = 0;
47635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project}
48635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
49635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Projectstatic inline U8CPU F2B(float x)
50635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project{
51635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    return static_cast<int>(x * 255);
52635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project}
53635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
54635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Projectstatic SkColor makeSkColor(float a, float r, float g, float b)
55635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project{
56635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    return SkColorSetARGB(F2B(a), F2B(r), F2B(g), F2B(b));
57635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project}
58635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
59635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project// Determine the total number of stops needed, including pseudo-stops at the
60635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project// ends as necessary.
61635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Projectstatic size_t totalStopsNeeded(const Gradient::ColorStop* stopData, size_t count)
62635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project{
635f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian    // N.B.:  The tests in this function should kept in sync with the ones in
645f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian    // fillStops(), or badness happens.
65635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    const Gradient::ColorStop* stop = stopData;
66635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    size_t countUsed = count;
67635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    if (count < 1 || stop->stop > 0.0)
68635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        countUsed++;
69635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    stop += count - 1;
705f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian    if (count < 1 || stop->stop < 1.0)
71635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        countUsed++;
72635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    return countUsed;
73635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project}
74635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
75635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project// Collect sorted stop position and color information into the pos and colors
76635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project// buffers, ensuring stops at both 0.0 and 1.0.  The buffers must be large
77635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project// enough to hold information for all stops, including the new endpoints if
78635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project// stops at 0.0 and 1.0 aren't already included.
79635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Projectstatic void fillStops(const Gradient::ColorStop* stopData,
80635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project                       size_t count, SkScalar* pos, SkColor* colors)
81635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project{
82635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    const Gradient::ColorStop* stop = stopData;
83635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    size_t start = 0;
84635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    if (count < 1) {
85635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        // A gradient with no stops must be transparent black.
86635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        pos[0] = WebCoreFloatToSkScalar(0.0);
87635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        colors[0] = makeSkColor(0.0, 0.0, 0.0, 0.0);
88635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        start = 1;
89635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    } else if (stop->stop > 0.0) {
90635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        // Copy the first stop to 0.0. The first stop position may have a slight
91635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        // rounding error, but we don't care in this float comparison, since
92635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        // 0.0 comes through cleanly and people aren't likely to want a gradient
93635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        // with a stop at (0 + epsilon).
94635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        pos[0] = WebCoreFloatToSkScalar(0.0);
95635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        colors[0] = makeSkColor(stop->alpha, stop->red, stop->green, stop->blue);
96635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        start = 1;
97635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    }
98635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
99635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    for (size_t i = start; i < start + count; i++) {
100635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        pos[i] = WebCoreFloatToSkScalar(stop->stop);
101635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        colors[i] = makeSkColor(stop->alpha, stop->red, stop->green, stop->blue);
102635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        ++stop;
103635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    }
104635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
105635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    // Copy the last stop to 1.0 if needed.  See comment above about this float
106635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    // comparison.
107635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    if (count < 1 || (--stop)->stop < 1.0) {
108635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        pos[start + count] = WebCoreFloatToSkScalar(1.0);
109635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        colors[start + count] = colors[start + count - 1];
110635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    }
111635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project}
112635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
113635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source ProjectSkShader* Gradient::platformGradient()
114635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project{
115635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    if (m_gradient)
116635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        return m_gradient;
117635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
11881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch    sortStopsIfNecessary();
11981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch    ASSERT(m_stopsSorted);
12081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch
121635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    size_t countUsed = totalStopsNeeded(m_stops.data(), m_stops.size());
122635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    ASSERT(countUsed >= 2);
123635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    ASSERT(countUsed >= m_stops.size());
124635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
125635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    // FIXME: Why is all this manual pointer math needed?!
126635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    SkAutoMalloc storage(countUsed * (sizeof(SkColor) + sizeof(SkScalar)));
127635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    SkColor* colors = (SkColor*)storage.get();
128635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    SkScalar* pos = (SkScalar*)(colors + countUsed);
129635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
130635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    fillStops(m_stops.data(), m_stops.size(), pos, colors);
131635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
1328f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian    SkShader::TileMode tile = SkShader::kClamp_TileMode;
1338f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian    switch (m_spreadMethod) {
1348f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian    case SpreadMethodReflect:
1358f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian        tile = SkShader::kMirror_TileMode;
1368f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian        break;
1378f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian    case SpreadMethodRepeat:
1388f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian        tile = SkShader::kRepeat_TileMode;
1398f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian        break;
1408f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian    case SpreadMethodPad:
1418f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian        tile = SkShader::kClamp_TileMode;
1428f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian        break;
1438f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian    }
1448f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian
145635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    if (m_radial) {
146231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block        // Since the two-point radial gradient is slower than the plain radial,
147231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block        // only use it if we have to.
148e458d70a0d18538346f41b503114c9ebe6b2ce12Leon Clarke        if (m_p0 == m_p1 && m_r0 <= 0.0f) {
149231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block            // The radius we give to Skia must be positive (and non-zero).  If
150231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block            // we're given a zero radius, just ask for a very small radius so
151231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block            // Skia will still return an object.
152231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block            SkScalar radius = m_r1 > 0 ? WebCoreFloatToSkScalar(m_r1) : SK_ScalarMin;
153231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block            m_gradient = SkGradientShader::CreateRadial(m_p1, radius, colors, pos, static_cast<int>(countUsed), tile);
154e458d70a0d18538346f41b503114c9ebe6b2ce12Leon Clarke        } else {
155e458d70a0d18538346f41b503114c9ebe6b2ce12Leon Clarke            // The radii we give to Skia must be positive.  If we're given a
156e458d70a0d18538346f41b503114c9ebe6b2ce12Leon Clarke            // negative radius, ask for zero instead.
157e458d70a0d18538346f41b503114c9ebe6b2ce12Leon Clarke            SkScalar radius0 = m_r0 >= 0.0f ? WebCoreFloatToSkScalar(m_r0) : 0;
158e458d70a0d18538346f41b503114c9ebe6b2ce12Leon Clarke            SkScalar radius1 = m_r1 >= 0.0f ? WebCoreFloatToSkScalar(m_r1) : 0;
159e458d70a0d18538346f41b503114c9ebe6b2ce12Leon Clarke            m_gradient = SkGradientShader::CreateTwoPointRadial(m_p0, radius0, m_p1, radius1, colors, pos, static_cast<int>(countUsed), tile);
160231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block        }
16181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch
16281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch        if (aspectRatio() != 1) {
16381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch            // CSS3 elliptical gradients: apply the elliptical scaling at the
16481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch            // gradient center point.
16581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch            m_gradientSpaceTransformation.translate(m_p0.x(), m_p0.y());
16681bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch            m_gradientSpaceTransformation.scale(1, 1 / aspectRatio());
16781bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch            m_gradientSpaceTransformation.translate(-m_p0.x(), -m_p0.y());
16881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch            ASSERT(m_p0 == m_p1);
16981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch        }
170635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    } else {
171635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project        SkPoint pts[2] = { m_p0, m_p1 };
17281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch        m_gradient = SkGradientShader::CreateLinear(pts, colors, pos, static_cast<int>(countUsed), tile);
173635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    }
174635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
1750bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    ASSERT(m_gradient);
1768f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian    SkMatrix matrix = m_gradientSpaceTransformation;
1778f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian    m_gradient->setLocalMatrix(matrix);
178635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    return m_gradient;
179635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project}
180635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
181635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Projectvoid Gradient::fill(GraphicsContext* context, const FloatRect& rect)
182635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project{
183635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    context->setFillGradient(this);
184635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project    context->fillRect(rect);
185635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project}
186635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project
1878a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Blockvoid Gradient::setPlatformGradientSpaceTransform(const AffineTransform& matrix)
1880bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch{
1890bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    if (m_gradient)
1900bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch        m_gradient->setLocalMatrix(m_gradientSpaceTransformation);
1910bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch}
1920bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch
193635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project} // namespace WebCore
194