• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "RenderView.h"
38 #include "SVGGradientElement.h"
39 #include "SVGPaintServerLinearGradient.h"
40 #include "SVGPaintServerRadialGradient.h"
41 #include "SVGRenderSupport.h"
42 #include "SVGRenderTreeAsText.h"
43 
44 using namespace std;
45 
46 namespace WebCore {
47 
operator <<(TextStream & ts,GradientSpreadMethod m)48 static TextStream& operator<<(TextStream& ts, GradientSpreadMethod m)
49 {
50     switch (m) {
51         case SpreadMethodPad:
52             ts << "PAD"; break;
53         case SpreadMethodRepeat:
54             ts << "REPEAT"; break;
55         case SpreadMethodReflect:
56             ts << "REFLECT"; break;
57     }
58 
59     return ts;
60 }
61 
operator <<(TextStream & ts,const Vector<SVGGradientStop> & l)62 static TextStream& operator<<(TextStream& ts, const Vector<SVGGradientStop>& l)
63 {
64     ts << "[";
65     for (Vector<SVGGradientStop>::const_iterator it = l.begin(); it != l.end(); ++it) {
66         ts << "(" << it->first << "," << it->second << ")";
67         if (it + 1 != l.end())
68             ts << ", ";
69     }
70     ts << "]";
71     return ts;
72 }
73 
SVGPaintServerGradient(const SVGGradientElement * owner)74 SVGPaintServerGradient::SVGPaintServerGradient(const SVGGradientElement* owner)
75     : m_boundingBoxMode(true)
76     , m_ownerElement(owner)
77 #if PLATFORM(CG)
78     , m_savedContext(0)
79     , m_imageBuffer(0)
80 #endif
81 {
82     ASSERT(owner);
83 }
84 
~SVGPaintServerGradient()85 SVGPaintServerGradient::~SVGPaintServerGradient()
86 {
87 }
88 
gradient() const89 Gradient* SVGPaintServerGradient::gradient() const
90 {
91     return m_gradient.get();
92 }
93 
setGradient(PassRefPtr<Gradient> gradient)94 void SVGPaintServerGradient::setGradient(PassRefPtr<Gradient> gradient)
95 {
96     m_gradient = gradient;
97 }
98 
boundingBoxMode() const99 bool SVGPaintServerGradient::boundingBoxMode() const
100 {
101     return m_boundingBoxMode;
102 }
103 
setBoundingBoxMode(bool mode)104 void SVGPaintServerGradient::setBoundingBoxMode(bool mode)
105 {
106     m_boundingBoxMode = mode;
107 }
108 
gradientTransform() const109 TransformationMatrix SVGPaintServerGradient::gradientTransform() const
110 {
111     return m_gradientTransform;
112 }
113 
setGradientTransform(const TransformationMatrix & transform)114 void SVGPaintServerGradient::setGradientTransform(const TransformationMatrix& transform)
115 {
116     m_gradientTransform = transform;
117 }
118 
119 #if PLATFORM(CG)
findTextRootObject(const RenderObject * start)120 static inline const RenderObject* findTextRootObject(const RenderObject* start)
121 {
122     while (start && !start->isSVGText())
123         start = start->parent();
124     ASSERT(start);
125     ASSERT(start->isSVGText());
126 
127     return start;
128 }
129 
createMaskAndSwapContextForTextGradient(GraphicsContext * & context,GraphicsContext * & savedContext,OwnPtr<ImageBuffer> & imageBuffer,const RenderObject * object)130 static inline bool createMaskAndSwapContextForTextGradient(
131     GraphicsContext*& context, GraphicsContext*& savedContext,
132     OwnPtr<ImageBuffer>& imageBuffer, const RenderObject* object)
133 {
134     FloatRect maskBBox = const_cast<RenderObject*>(findTextRootObject(object))->objectBoundingBox();
135     IntRect maskRect = enclosingIntRect(object->absoluteTransform().mapRect(maskBBox));
136 
137     IntSize maskSize(maskRect.width(), maskRect.height());
138     clampImageBufferSizeToViewport(object->view()->frameView(), maskSize);
139 
140     OwnPtr<ImageBuffer> maskImage = ImageBuffer::create(maskSize);
141 
142     if (!maskImage)
143         return false;
144 
145     GraphicsContext* maskImageContext = maskImage->context();
146 
147     maskImageContext->save();
148     maskImageContext->translate(-maskRect.x(), -maskRect.y());
149     maskImageContext->concatCTM(object->absoluteTransform());
150 
151     imageBuffer.set(maskImage.release());
152     savedContext = context;
153 
154     context = maskImageContext;
155 
156     return true;
157 }
158 
clipToTextMask(GraphicsContext * context,OwnPtr<ImageBuffer> & imageBuffer,const RenderObject * object,const SVGPaintServerGradient * gradientServer)159 static inline TransformationMatrix clipToTextMask(GraphicsContext* context,
160     OwnPtr<ImageBuffer>& imageBuffer, const RenderObject* object,
161     const SVGPaintServerGradient* gradientServer)
162 {
163     FloatRect maskBBox = const_cast<RenderObject*>(findTextRootObject(object))->objectBoundingBox();
164 
165     // Fixup transformations to be able to clip to mask
166     TransformationMatrix transform = object->absoluteTransform();
167     FloatRect textBoundary = transform.mapRect(maskBBox);
168 
169     IntSize maskSize(lroundf(textBoundary.width()), lroundf(textBoundary.height()));
170     clampImageBufferSizeToViewport(object->view()->frameView(), maskSize);
171     textBoundary.setSize(textBoundary.size().shrunkTo(maskSize));
172 
173     // Clip current context to mask image (gradient)
174     context->concatCTM(transform.inverse());
175     context->clipToImageBuffer(textBoundary, imageBuffer.get());
176     context->concatCTM(transform);
177 
178     TransformationMatrix matrix;
179     if (gradientServer->boundingBoxMode()) {
180         matrix.translate(maskBBox.x(), maskBBox.y());
181         matrix.scaleNonUniform(maskBBox.width(), maskBBox.height());
182     }
183     matrix.multiply(gradientServer->gradientTransform());
184     return matrix;
185 }
186 #endif
187 
setup(GraphicsContext * & context,const RenderObject * object,SVGPaintTargetType type,bool isPaintingText) const188 bool SVGPaintServerGradient::setup(GraphicsContext*& context, const RenderObject* object, SVGPaintTargetType type, bool isPaintingText) const
189 {
190     m_ownerElement->buildGradient();
191 
192     const SVGRenderStyle* style = object->style()->svgStyle();
193     bool isFilled = (type & ApplyToFillTargetType) && style->hasFill();
194     bool isStroked = (type & ApplyToStrokeTargetType) && style->hasStroke();
195 
196     ASSERT(isFilled && !isStroked || !isFilled && isStroked);
197 
198     context->save();
199 
200     if (isPaintingText) {
201 #if PLATFORM(CG)
202         if (!createMaskAndSwapContextForTextGradient(context, m_savedContext, m_imageBuffer, object)) {
203             context->restore();
204             return false;
205         }
206 #endif
207         context->setTextDrawingMode(isFilled ? cTextFill : cTextStroke);
208     }
209 
210     if (isFilled) {
211         context->setAlpha(style->fillOpacity());
212         context->setFillGradient(m_gradient);
213         context->setFillRule(style->fillRule());
214     }
215     if (isStroked) {
216         context->setAlpha(style->strokeOpacity());
217         context->setStrokeGradient(m_gradient);
218         applyStrokeStyleToContext(context, object->style(), object);
219     }
220 
221     TransformationMatrix matrix;
222     // CG platforms will handle the gradient space transform for text in
223     // teardown, so we don't apply it here.  For non-CG platforms, we
224     // want the text bounding box applied to the gradient space transform now,
225     // so the gradient shader can use it.
226 #if PLATFORM(CG)
227     if (boundingBoxMode() && !isPaintingText) {
228 #else
229     if (boundingBoxMode()) {
230 #endif
231         FloatRect bbox = object->objectBoundingBox();
232         // Don't use gradients for 1d objects like horizontal/vertical
233         // lines or rectangles without width or height.
234         if (bbox.width() == 0 || bbox.height() == 0) {
235             Color color(0, 0, 0);
236             context->setStrokeColor(color);
237             return true;
238         }
239         matrix.translate(bbox.x(), bbox.y());
240         matrix.scaleNonUniform(bbox.width(), bbox.height());
241     }
242     matrix.multiply(gradientTransform());
243     m_gradient->setGradientSpaceTransform(matrix);
244 
245     return true;
246 }
247 
248 void SVGPaintServerGradient::teardown(GraphicsContext*& context, const RenderObject* object, SVGPaintTargetType, bool isPaintingText) const
249 {
250 #if PLATFORM(CG)
251     // renderPath() is not used when painting text, so we paint the gradient during teardown()
252     if (isPaintingText && m_savedContext) {
253 
254         // Restore on-screen drawing context
255         context = m_savedContext;
256         m_savedContext = 0;
257 
258         TransformationMatrix matrix = clipToTextMask(context, m_imageBuffer, object, this);
259         m_gradient->setGradientSpaceTransform(matrix);
260         context->setFillGradient(m_gradient);
261 
262         FloatRect maskBBox = const_cast<RenderObject*>(findTextRootObject(object))->objectBoundingBox();
263 
264         context->fillRect(maskBBox);
265 
266         m_imageBuffer.clear(); // we're done with our text mask buffer
267     }
268 #endif
269     context->restore();
270 }
271 
272 TextStream& SVGPaintServerGradient::externalRepresentation(TextStream& ts) const
273 {
274     // Gradients/patterns aren't setup, until they are used for painting. Work around that fact.
275     m_ownerElement->buildGradient();
276 
277     // abstract, don't stream type
278     ts  << "[stops=" << gradientStops() << "]";
279     if (m_gradient->spreadMethod() != SpreadMethodPad)
280         ts << "[method=" << m_gradient->spreadMethod() << "]";
281     if (!boundingBoxMode())
282         ts << " [bounding box mode=" << boundingBoxMode() << "]";
283     if (!gradientTransform().isIdentity())
284         ts << " [transform=" << gradientTransform() << "]";
285 
286     return ts;
287 }
288 
289 } // namespace WebCore
290 
291 #endif
292