779a7af0005ae010b923be649374a38d69226776
[WebKit-https.git] / WebCore / kcanvas / device / qt / KRenderingPaintServerGradientQt.cpp
1 /*
2     Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <wildfox@kde.org>
3                   2004, 2005, 2006 Rob Buis <buis@kde.org>
4
5     This file is part of the KDE project
6
7     This library is free software; you can redistribute it and/or
8     modify it under the terms of the GNU Library General Public
9     License as published by the Free Software Foundation; either
10     version 2 of the License, or (at your option) any later version.
11
12     This library is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15     Library General Public License for more details.
16
17     You should have received a copy of the GNU General Public License
18     along with this program; see the file COPYING.LIB. If not, write to
19     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20     Boston, MA 02110-1301, USA.
21 */
22
23 #include "config.h"
24
25 #include <math.h>
26 #include <QPointF>
27
28 #include "RenderStyle.h"
29 #include "AffineTransform.h"
30 #include "KRenderingDeviceQt.h"
31 #include "KCanvasRenderingStyle.h"
32 #include "KRenderingFillPainter.h"
33 #include "KRenderingStrokePainter.h"
34 #include "KRenderingPaintServerGradientQt.h"
35
36 namespace WebCore {
37
38 void fill_color_array(QGradient& gradient, const Vector<KCGradientStop>& stops, float opacity)
39 {
40     for (unsigned i = 0; i < stops.size(); ++i) {
41         float offset = stops[i].first;
42         Color color = stops[i].second;
43         
44         QColor c(color.red(), color.green(), color.blue());
45         c.setAlpha(int(color.alpha() * opacity));
46
47         gradient.setColorAt(offset, c);
48     }
49 }
50
51 // KRenderingPaintServerLinearGradientQt
52 KRenderingPaintServerLinearGradientQt::KRenderingPaintServerLinearGradientQt()
53     : KRenderingPaintServerLinearGradient()
54     , KRenderingPaintServerQt()
55 {
56 }
57
58 void KRenderingPaintServerLinearGradientQt::renderPath(KRenderingDeviceContext* context, const RenderPath* path, KCPaintTargetType type) const
59 {
60     RenderStyle* renderStyle = path->style();
61     KRenderingDeviceContextQt* qtContext = static_cast<KRenderingDeviceContextQt*>(context);
62
63     if ((type & APPLY_TO_FILL) && KSVGPainterFactory::isFilled(renderStyle))
64         qtContext->fillPath();
65
66     if ((type & APPLY_TO_STROKE) && KSVGPainterFactory::isStroked(renderStyle))
67         qtContext->strokePath();
68 }
69
70 bool KRenderingPaintServerLinearGradientQt::setup(KRenderingDeviceContext* context, const RenderObject* object, KCPaintTargetType type) const
71 {
72     KRenderingDeviceContextQt* qtContext = static_cast<KRenderingDeviceContextQt*>(context);
73     Q_ASSERT(qtContext != 0);
74
75     if (listener())
76         listener()->resourceNotification();
77
78     RenderStyle* renderStyle = object->style();
79
80     double x1, x2, y1, y2;
81     if (boundingBoxMode()) {
82         QRectF bbox = qtContext->pathBBox();
83         x1 = double(bbox.left()) + (double(gradientStart().x() / 100.0) * double(bbox.width()));
84         y1 = double(bbox.top()) + (double(gradientStart().y() / 100.0) * double(bbox.height()));
85         x2 = double(bbox.left()) + (double(gradientEnd().x() / 100.0)  * double(bbox.width()));
86         y2 = double(bbox.top()) + (double(gradientEnd().y() / 100.0) * double(bbox.height()));
87     } else {
88         x1 = gradientStart().x();
89         y1 = gradientStart().y();
90         x2 = gradientEnd().x();
91         y2 = gradientEnd().y();
92     }
93
94     qtContext->painter().setPen(Qt::NoPen);
95     qtContext->painter().setBrush(Qt::NoBrush);
96
97     QLinearGradient gradient(QPointF(x1, y1), QPointF(x2, y2));
98     if (spreadMethod() == SPREADMETHOD_REPEAT)
99         gradient.setSpread(QGradient::RepeatSpread);
100     else if (spreadMethod() == SPREADMETHOD_REFLECT)
101         gradient.setSpread(QGradient::ReflectSpread);
102     else
103         gradient.setSpread(QGradient::PadSpread);
104
105     double opacity = 1.0;
106
107     // TODO: Gradient transform + opacity fixes! 
108
109     if ((type & APPLY_TO_FILL) && KSVGPainterFactory::isFilled(renderStyle)) {
110         KRenderingFillPainter fillPainter = KSVGPainterFactory::fillPainter(renderStyle, object);
111         fill_color_array(gradient, gradientStops(), opacity);
112
113         QBrush brush(gradient);
114
115         qtContext->painter().setBrush(brush);
116         qtContext->setFillRule(fillPainter.fillRule());
117     }
118
119     if ((type & APPLY_TO_STROKE) && KSVGPainterFactory::isStroked(renderStyle)) {
120         KRenderingStrokePainter strokePainter = KSVGPainterFactory::strokePainter(renderStyle, object);
121         fill_color_array(gradient, gradientStops(), opacity);
122
123         QPen pen;
124         QBrush brush(gradient);
125
126         setPenProperties(strokePainter, pen);
127         pen.setBrush(brush);
128
129         qtContext->painter().setPen(pen);
130     }
131
132     return true;
133 }
134
135 void KRenderingPaintServerLinearGradientQt::teardown(KRenderingDeviceContext*, const RenderObject*, KCPaintTargetType) const
136 {
137 }
138
139 void KRenderingPaintServerLinearGradientQt::draw(KRenderingDeviceContext* context, const RenderPath* path, KCPaintTargetType type) const
140 {
141     if (!setup(context, path, type))
142         return;
143
144     renderPath(context, path, type);
145     teardown(context, path, type);
146 }
147
148 // KRenderingPaintServerRadialGradientQt
149 KRenderingPaintServerRadialGradientQt::KRenderingPaintServerRadialGradientQt()
150     : KRenderingPaintServerRadialGradient()
151     , KRenderingPaintServerQt()
152 {
153 }
154
155 bool KRenderingPaintServerRadialGradientQt::setup(KRenderingDeviceContext* context, const RenderObject* object, KCPaintTargetType type) const
156 {
157     KRenderingDeviceContextQt* qtContext = static_cast<KRenderingDeviceContextQt*>(context);
158     Q_ASSERT(qtContext != 0);
159
160     if (listener())
161         listener()->resourceNotification();
162
163     RenderStyle* renderStyle = object->style();
164
165     qtContext->painter().setPen(Qt::NoPen);
166     qtContext->painter().setBrush(Qt::NoBrush);
167     QMatrix mat = qtContext->ctm();
168
169     double cx, fx, cy, fy, r;
170     if (boundingBoxMode()) {
171         QRectF bbox = qtContext->pathBBox();
172         cx = double(bbox.left()) + (double(gradientCenter().x() / 100.0) * double(bbox.width()));
173         cy = double(bbox.top()) + (double(gradientCenter().y() / 100.0) * double(bbox.height()));
174         fx = double(bbox.left()) + (double(gradientFocal().x() / 100.0) * double(bbox.width())) - cx;
175         fy = double(bbox.top()) + (double(gradientFocal().y() / 100.0) * double(bbox.height())) - cy;
176         r = double(gradientRadius() / 100.0) * (sqrt(pow(bbox.width(), 2) + pow(bbox.height(), 2)));
177
178         float width = bbox.width();
179         float height = bbox.height();
180
181         int diff = int(width - height); // allow slight tolerance
182         if (!(diff > -2 && diff < 2)) {
183             // make elliptical or circular depending on bbox aspect ratio
184             float ratioX = (width / height);
185             float ratioY = (height / width);
186             mat.scale((width > height) ? 1 : ratioX, (width > height) ? ratioY : 1);
187         }
188     } else {
189         cx = gradientCenter().x();
190         cy = gradientCenter().y();
191
192         fx = gradientFocal().x();
193         fy = gradientFocal().y();
194
195         fx -= cx;
196         fy -= cy;
197
198         r = gradientRadius();
199     }
200
201     if (sqrt(fx * fx + fy * fy) > r) {
202         // Spec: If (fx, fy) lies outside the circle defined by (cx, cy) and r, set (fx, fy)
203         // to the point of intersection of the line through (fx, fy) and the circle.
204         double angle = atan2(fy, fx);
205         fx = int(cos(angle) * r) - 1;
206         fy = int(sin(angle) * r) - 1;
207     }
208
209     QRadialGradient gradient(QPointF(cx, cy), gradientRadius(), QPointF(fx + cx, fy + cy));
210     if (spreadMethod() == SPREADMETHOD_REPEAT)
211         gradient.setSpread(QGradient::RepeatSpread);
212     else if (spreadMethod() == SPREADMETHOD_REFLECT)
213         gradient.setSpread(QGradient::ReflectSpread);
214     else
215         gradient.setSpread(QGradient::PadSpread);
216
217     double opacity = 1.0;
218
219     // TODO: Gradient transform + opacity fixes! 
220
221     // AffineTransform gradientTrans = gradientTransform();
222     // gradientTrans.map(cx, cy, &cx, &cy);
223     // qtContext->painter().setMatrix(mat);
224
225     if ((type & APPLY_TO_FILL) && KSVGPainterFactory::isFilled(renderStyle)) {
226         KRenderingFillPainter fillPainter = KSVGPainterFactory::fillPainter(renderStyle, object);
227         fill_color_array(gradient, gradientStops(), opacity);
228
229         QBrush brush(gradient);
230
231         qtContext->painter().setBrush(brush);
232         qtContext->setFillRule(fillPainter.fillRule());
233     }
234
235     if ((type & APPLY_TO_STROKE) && KSVGPainterFactory::isStroked(renderStyle)) {
236         KRenderingStrokePainter strokePainter = KSVGPainterFactory::strokePainter(renderStyle, object);
237         fill_color_array(gradient, gradientStops(), opacity);
238
239         QPen pen;
240         QBrush brush(gradient);
241
242         setPenProperties(strokePainter, pen);
243         pen.setBrush(brush);
244
245         qtContext->painter().setPen(pen);
246     }
247
248     return true;
249 }
250
251 void KRenderingPaintServerRadialGradientQt::draw(KRenderingDeviceContext* context, const RenderPath* path, KCPaintTargetType type) const
252 {
253     if (!setup(context, path, type))
254         return;
255
256     renderPath(context, path, type);
257     teardown(context, path, type);
258 }
259
260 void KRenderingPaintServerRadialGradientQt::teardown(KRenderingDeviceContext*, const RenderObject*, KCPaintTargetType) const
261 {
262 }
263
264 void KRenderingPaintServerRadialGradientQt::renderPath(KRenderingDeviceContext* context, const RenderPath* path, KCPaintTargetType type) const
265 {
266     RenderStyle* renderStyle = path->style();
267     KRenderingDeviceContextQt* qtContext = static_cast<KRenderingDeviceContextQt*>(context);
268
269     if ((type & APPLY_TO_FILL) && KSVGPainterFactory::isFilled(renderStyle))
270         qtContext->fillPath();
271
272     if ((type & APPLY_TO_STROKE) && KSVGPainterFactory::isStroked(renderStyle))
273         qtContext->strokePath();
274 }
275
276 }
277
278 // vim:ts=4:noet