• 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 "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