604dd0c2ad04873f778740c5bbcd9ed75b7e1a3f
[WebKit-https.git] / WebCore / html / CanvasGradient.cpp
1 /*
2  * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "CanvasGradient.h"
28
29 #include "cssparser.h"
30
31 #if __APPLE__
32 #include <ApplicationServices/ApplicationServices.h>
33 #endif
34
35 namespace WebCore {
36
37 CanvasGradient::CanvasGradient(const FloatPoint& p0, const FloatPoint& p1)
38     : m_radial(false), m_p0(p0), m_p1(p1), m_stopsSorted(false), m_lastStop(0)
39 #if __APPLE__
40     , m_shading(0)
41 #endif
42 {
43 }
44
45 CanvasGradient::CanvasGradient(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1)
46     : m_radial(true), m_p0(p0), m_p1(p1), m_r0(r0), m_r1(r1), m_stopsSorted(false), m_lastStop(0)
47 #if __APPLE__
48     , m_shading(0)
49 #endif
50 {
51 }
52
53 CanvasGradient::~CanvasGradient()
54 {
55 #if __APPLE__
56     CGShadingRelease(m_shading);
57 #endif
58 }
59
60 void CanvasGradient::addColorStop(float value, const String& color)
61 {
62     RGBA32 rgba = CSSParser::parseColor(color);
63     m_stops.append(ColorStop(value,
64         ((rgba >> 16) & 0xFF) / 255.0,
65         ((rgba >> 8) & 0xFF) / 255.0,
66         (rgba & 0xFF) / 255.0,
67         ((rgba >> 24) & 0xFF) / 255.0));
68
69     m_stopsSorted = false;
70
71 #if __APPLE__
72     CGShadingRelease(m_shading);
73     m_shading = 0;
74 #endif
75 }
76
77 #if __APPLE__
78
79 static void gradientCallback(void* info, const CGFloat* in, CGFloat* out)
80 {
81     float r, g, b, a;
82     static_cast<CanvasGradient*>(info)->getColor(*in, &r, &g, &b, &a);
83     out[0] = r;
84     out[1] = g;
85     out[2] = b;
86     out[3] = a;
87 }
88
89 CGShadingRef CanvasGradient::platformShading()
90 {
91     if (m_shading)
92         return m_shading;
93
94     const CGFloat intervalRanges[2] = { 0, 1 };
95     const CGFloat colorComponentRanges[4 * 2] = { 0, 1, 0, 1, 0, 1, 0, 1 };
96     const CGFunctionCallbacks gradientCallbacks = { 0, gradientCallback, 0 };
97     CGFunctionRef colorFunction = CGFunctionCreate(this, 1, intervalRanges, 4, colorComponentRanges, &gradientCallbacks);
98
99     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
100
101     if (m_radial)
102         m_shading = CGShadingCreateRadial(colorSpace, m_p0, m_r0, m_p1, m_r1, colorFunction, true, true);
103     else
104         m_shading = CGShadingCreateAxial(colorSpace, m_p0, m_p1, colorFunction, true, true);
105
106     CGColorSpaceRelease(colorSpace);
107     CGFunctionRelease(colorFunction);
108
109     return m_shading;
110 }
111
112 #endif
113
114 void CanvasGradient::getColor(float value, float* r, float* g, float* b, float* a)
115 {
116     ASSERT(value >= 0);
117     ASSERT(value <= 1);
118
119     if (value <= 0) {
120         *r = m_stops.first().red;
121         *g = m_stops.first().green;
122         *b = m_stops.first().blue;
123         *a = m_stops.first().alpha;
124         return;
125     }
126     if (value >= 1) {
127         *r = m_stops.last().red;
128         *g = m_stops.last().green;
129         *b = m_stops.last().blue;
130         *a = m_stops.last().alpha;
131         return;
132     }
133
134     // Find stop before and stop after and interpolate.
135     int stop = findStop(value);
136     const ColorStop& lastStop = m_stops[stop];    
137     const ColorStop& nextStop = m_stops[stop + 1];
138     float stopFraction = (value - lastStop.stop) / (nextStop.stop - lastStop.stop);
139     *r = lastStop.red + (nextStop.red - lastStop.red) * stopFraction;
140     *g = lastStop.green + (nextStop.green - lastStop.green) * stopFraction;
141     *b = lastStop.blue + (nextStop.blue - lastStop.blue) * stopFraction;
142     *a = lastStop.alpha + (nextStop.alpha - lastStop.alpha) * stopFraction;
143 }
144
145 static int compareStops(const void* a, const void* b)
146 {
147     float as = static_cast<const CanvasGradient::ColorStop*>(a)->stop;
148     float bs = static_cast<const CanvasGradient::ColorStop*>(b)->stop;
149
150     if (as > bs)
151         return 1;
152     if (as < bs)
153         return -1;
154     return 0;
155 }
156
157 int CanvasGradient::findStop(float value) const
158 {
159     if (!m_stopsSorted) {
160         bool addZeroStop = true;
161         bool addOneStop = true;
162         if (m_stops.size()) {
163             qsort(m_stops.data(), m_stops.size(), sizeof(ColorStop), compareStops);
164             addZeroStop = m_stops.first().stop != 0;
165             addOneStop = m_stops.last().stop != 1;
166         }
167         if (addZeroStop)
168             m_stops.insert(0, ColorStop(0, 0, 0, 0, 1));
169         if (addOneStop)
170             m_stops.append(ColorStop(1, 0, 0, 0, 1));
171         m_stopsSorted = true;
172     }
173
174     ASSERT(value >= 0);
175     ASSERT(value <= 1);
176
177     int numStops = m_stops.size();
178     ASSERT(numStops >= 2);
179     ASSERT(m_lastStop < numStops - 1);
180
181     int i = m_lastStop;
182     if (value < m_stops[i].stop)
183         i = 1;
184     else
185         i = m_lastStop + 1;
186
187     for (; i < numStops - 1; ++i)
188         if (value < m_stops[i].stop)
189             break;
190
191     m_lastStop = i - 1;
192     return m_lastStop;
193 }
194
195 } //namespace