1 /*
2 * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
3 * 2008 Eric Seidel <eric@webkit.org>
4 * 2008 Dirk Schulze <krit@webkit.org>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
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.
26 */
27
28 #include "config.h"
29
30 #if ENABLE(SVG)
31 #include "SVGPaintServerGradient.h"
32
33 #include "FloatConversion.h"
34 #include "GraphicsContext.h"
35 #include "ImageBuffer.h"
36 #include "RenderObject.h"
37 #include "SVGGradientElement.h"
38 #include "SVGPaintServerLinearGradient.h"
39 #include "SVGPaintServerRadialGradient.h"
40 #include "SVGRenderSupport.h"
41 #include "SVGRenderTreeAsText.h"
42
43 #if PLATFORM(CG)
44 #include <wtf/MathExtras.h>
45 #include <wtf/RetainPtr.h>
46 #endif
47
48 using namespace std;
49
50 namespace WebCore {
51
operator <<(TextStream & ts,GradientSpreadMethod m)52 static TextStream& operator<<(TextStream& ts, GradientSpreadMethod m)
53 {
54 switch (m) {
55 case SpreadMethodPad:
56 ts << "PAD"; break;
57 case SpreadMethodRepeat:
58 ts << "REPEAT"; break;
59 case SpreadMethodReflect:
60 ts << "REFLECT"; break;
61 }
62
63 return ts;
64 }
65
operator <<(TextStream & ts,const Vector<SVGGradientStop> & l)66 static TextStream& operator<<(TextStream& ts, const Vector<SVGGradientStop>& l)
67 {
68 ts << "[";
69 for (Vector<SVGGradientStop>::const_iterator it = l.begin(); it != l.end(); ++it) {
70 ts << "(" << it->first << "," << it->second << ")";
71 if (it + 1 != l.end())
72 ts << ", ";
73 }
74 ts << "]";
75 return ts;
76 }
77
SVGPaintServerGradient(const SVGGradientElement * owner)78 SVGPaintServerGradient::SVGPaintServerGradient(const SVGGradientElement* owner)
79 : m_spreadMethod(SpreadMethodPad)
80 , m_boundingBoxMode(true)
81 , m_ownerElement(owner)
82
83 #if PLATFORM(CG)
84 , m_savedContext(0)
85 , m_imageBuffer(0)
86 #endif
87 {
88 ASSERT(owner);
89 }
90
~SVGPaintServerGradient()91 SVGPaintServerGradient::~SVGPaintServerGradient()
92 {
93 }
94
gradient() const95 Gradient* SVGPaintServerGradient::gradient() const
96 {
97 return m_gradient.get();
98 }
99
setGradient(PassRefPtr<Gradient> gradient)100 void SVGPaintServerGradient::setGradient(PassRefPtr<Gradient> gradient)
101 {
102 m_gradient = gradient;
103 }
104
spreadMethod() const105 GradientSpreadMethod SVGPaintServerGradient::spreadMethod() const
106 {
107 return m_spreadMethod;
108 }
109
setGradientSpreadMethod(const GradientSpreadMethod & method)110 void SVGPaintServerGradient::setGradientSpreadMethod(const GradientSpreadMethod& method)
111 {
112 m_spreadMethod = method;
113 }
114
boundingBoxMode() const115 bool SVGPaintServerGradient::boundingBoxMode() const
116 {
117 return m_boundingBoxMode;
118 }
119
setBoundingBoxMode(bool mode)120 void SVGPaintServerGradient::setBoundingBoxMode(bool mode)
121 {
122 m_boundingBoxMode = mode;
123 }
124
gradientTransform() const125 TransformationMatrix SVGPaintServerGradient::gradientTransform() const
126 {
127 return m_gradientTransform;
128 }
129
setGradientTransform(const TransformationMatrix & transform)130 void SVGPaintServerGradient::setGradientTransform(const TransformationMatrix& transform)
131 {
132 m_gradientTransform = transform;
133 }
134
135 #if PLATFORM(CG)
136 // Helper function for text painting in CG
137 // This Cg specific code should move to GraphicsContext and Font* in a next step.
findTextRootObject(const RenderObject * start)138 static inline const RenderObject* findTextRootObject(const RenderObject* start)
139 {
140 while (start && !start->isSVGText())
141 start = start->parent();
142 ASSERT(start);
143 ASSERT(start->isSVGText());
144
145 return start;
146 }
147
createMaskAndSwapContextForTextGradient(GraphicsContext * & context,GraphicsContext * & savedContext,OwnPtr<ImageBuffer> & imageBuffer,const RenderObject * object)148 static inline bool createMaskAndSwapContextForTextGradient(
149 GraphicsContext*& context, GraphicsContext*& savedContext,
150 OwnPtr<ImageBuffer>& imageBuffer, const RenderObject* object)
151 {
152 FloatRect maskBBox = const_cast<RenderObject*>(findTextRootObject(object))->relativeBBox(false);
153 IntRect maskRect = enclosingIntRect(object->absoluteTransform().mapRect(maskBBox));
154
155 IntSize maskSize(maskRect.width(), maskRect.height());
156 clampImageBufferSizeToViewport(object->document()->renderer(), maskSize);
157
158 auto_ptr<ImageBuffer> maskImage = ImageBuffer::create(maskSize, false);
159
160 if (!maskImage.get())
161 return false;
162
163 GraphicsContext* maskImageContext = maskImage->context();
164
165 maskImageContext->save();
166 maskImageContext->translate(-maskRect.x(), -maskRect.y());
167 maskImageContext->concatCTM(object->absoluteTransform());
168
169 imageBuffer.set(maskImage.release());
170 savedContext = context;
171
172 context = maskImageContext;
173
174 return true;
175 }
176
clipToTextMask(GraphicsContext * context,OwnPtr<ImageBuffer> & imageBuffer,const RenderObject * object,const SVGPaintServerGradient * gradientServer)177 static inline void clipToTextMask(GraphicsContext* context,
178 OwnPtr<ImageBuffer>& imageBuffer, const RenderObject* object,
179 const SVGPaintServerGradient* gradientServer)
180 {
181 FloatRect maskBBox = const_cast<RenderObject*>(findTextRootObject(object))->relativeBBox(false);
182
183 // Fixup transformations to be able to clip to mask
184 TransformationMatrix transform = object->absoluteTransform();
185 FloatRect textBoundary = transform.mapRect(maskBBox);
186
187 IntSize maskSize(lroundf(textBoundary.width()), lroundf(textBoundary.height()));
188 clampImageBufferSizeToViewport(object->document()->renderer(), maskSize);
189 textBoundary.setSize(textBoundary.size().shrunkTo(maskSize));
190
191 // Clip current context to mask image (gradient)
192 context->concatCTM(transform.inverse());
193 context->clipToImageBuffer(textBoundary, imageBuffer.get());
194 context->concatCTM(transform);
195
196 if (gradientServer->boundingBoxMode()) {
197 context->translate(maskBBox.x(), maskBBox.y());
198 context->scale(FloatSize(maskBBox.width(), maskBBox.height()));
199 }
200 context->concatCTM(gradientServer->gradientTransform());
201 }
202 #endif
203
setup(GraphicsContext * & context,const RenderObject * object,SVGPaintTargetType type,bool isPaintingText) const204 bool SVGPaintServerGradient::setup(GraphicsContext*& context, const RenderObject* object, SVGPaintTargetType type, bool isPaintingText) const
205 {
206 m_ownerElement->buildGradient();
207
208 const SVGRenderStyle* style = object->style()->svgStyle();
209 bool isFilled = (type & ApplyToFillTargetType) && style->hasFill();
210 bool isStroked = (type & ApplyToStrokeTargetType) && style->hasStroke();
211
212 ASSERT(isFilled && !isStroked || !isFilled && isStroked);
213
214 context->save();
215
216 if (isPaintingText) {
217 #if PLATFORM(CG)
218 if (!createMaskAndSwapContextForTextGradient(context, m_savedContext, m_imageBuffer, object)) {
219 context->restore();
220 return false;
221 }
222 #endif
223 context->setTextDrawingMode(isFilled ? cTextFill : cTextStroke);
224 }
225
226 if (isFilled) {
227 context->setAlpha(style->fillOpacity());
228 context->setFillGradient(m_gradient);
229 context->setFillRule(style->fillRule());
230 }
231 if (isStroked) {
232 context->setAlpha(style->strokeOpacity());
233 context->setStrokeGradient(m_gradient);
234 applyStrokeStyleToContext(context, object->style(), object);
235 }
236
237 if (boundingBoxMode() && !isPaintingText) {
238 FloatRect bbox = object->relativeBBox(false);
239 // Don't use gradientes for 1d objects like horizontal/vertical
240 // lines or rectangles without width or height.
241 if (bbox.width() == 0 || bbox.height() == 0) {
242 Color color(0, 0, 0);
243 context->setStrokeColor(color);
244 return true;
245 }
246 context->translate(bbox.x(), bbox.y());
247 context->scale(FloatSize(bbox.width(), bbox.height()));
248
249 // With scaling the context, the strokeThickness is scaled too. We have to
250 // undo this.
251 float strokeThickness = std::max((context->strokeThickness() / ((bbox.width() + bbox.height()) / 2) - 0.001f), 0.f);
252 context->setStrokeThickness(strokeThickness);
253 }
254 context->concatCTM(gradientTransform());
255 context->setSpreadMethod(spreadMethod());
256
257 return true;
258 }
259
teardown(GraphicsContext * & context,const RenderObject * object,SVGPaintTargetType,bool isPaintingText) const260 void SVGPaintServerGradient::teardown(GraphicsContext*& context, const RenderObject* object, SVGPaintTargetType, bool isPaintingText) const
261 {
262 #if PLATFORM(CG)
263 // renderPath() is not used when painting text, so we paint the gradient during teardown()
264 if (isPaintingText && m_savedContext) {
265 // Restore on-screen drawing context
266 context = m_savedContext;
267 m_savedContext = 0;
268
269 clipToTextMask(context, m_imageBuffer, object, this);
270
271 // finally fill the text clip with the shading
272 CGContextDrawShading(context->platformContext(), m_gradient->platformGradient());
273
274 m_imageBuffer.clear(); // we're done with our text mask buffer
275 }
276 #endif
277 context->restore();
278 }
279
externalRepresentation(TextStream & ts) const280 TextStream& SVGPaintServerGradient::externalRepresentation(TextStream& ts) const
281 {
282 // Gradients/patterns aren't setup, until they are used for painting. Work around that fact.
283 m_ownerElement->buildGradient();
284
285 // abstract, don't stream type
286 ts << "[stops=" << gradientStops() << "]";
287 if (spreadMethod() != SpreadMethodPad)
288 ts << "[method=" << spreadMethod() << "]";
289 if (!boundingBoxMode())
290 ts << " [bounding box mode=" << boundingBoxMode() << "]";
291 if (!gradientTransform().isIdentity())
292 ts << " [transform=" << gradientTransform() << "]";
293
294 return ts;
295 }
296
297 } // namespace WebCore
298
299 #endif
300