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