SVGPaintServer.cpp revision d0825bca7fe65beaee391d30da42e937db621564
1/*
2 * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
3 *               2007 Rob Buis <buis@kde.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 "SVGPaintServer.h"
32
33#include "GraphicsContext.h"
34#include "NodeRenderStyle.h"
35#include "RenderObject.h"
36#include "RenderStyle.h"
37#include "SVGPaintServerSolid.h"
38#include "SVGStyledElement.h"
39#include "SVGURIReference.h"
40
41#if PLATFORM(SKIA) && !PLATFORM(ANDROID)
42#include "PlatformContextSkia.h"
43#endif
44
45namespace WebCore {
46
47SVGPaintServer::SVGPaintServer()
48{
49}
50
51SVGPaintServer::~SVGPaintServer()
52{
53}
54
55TextStream& operator<<(TextStream& ts, const SVGPaintServer& paintServer)
56{
57    return paintServer.externalRepresentation(ts);
58}
59
60SVGPaintServer* getPaintServerById(Document* document, const AtomicString& id, const RenderObject* object)
61{
62    SVGResource* resource = getResourceById(document, id, object);
63    if (resource && resource->isPaintServer())
64        return static_cast<SVGPaintServer*>(resource);
65
66    return 0;
67}
68
69SVGPaintServerSolid* SVGPaintServer::sharedSolidPaintServer()
70{
71    static SVGPaintServerSolid* _sharedSolidPaintServer = SVGPaintServerSolid::create().releaseRef();
72
73    return _sharedSolidPaintServer;
74}
75
76SVGPaintServer* SVGPaintServer::fillPaintServer(const RenderStyle* style, const RenderObject* item)
77{
78    if (!style->svgStyle()->hasFill())
79        return 0;
80
81    SVGPaint* fill = style->svgStyle()->fillPaint();
82
83    SVGPaintServer* fillPaintServer = 0;
84    SVGPaint::SVGPaintType paintType = fill->paintType();
85    if (paintType == SVGPaint::SVG_PAINTTYPE_URI ||
86        paintType == SVGPaint::SVG_PAINTTYPE_URI_RGBCOLOR) {
87        AtomicString id(SVGURIReference::getTarget(fill->uri()));
88        fillPaintServer = getPaintServerById(item->document(), id, item);
89
90        SVGElement* svgElement = static_cast<SVGElement*>(item->node());
91        ASSERT(svgElement && svgElement->document() && svgElement->isStyled());
92
93        if (item->isRenderPath() && fillPaintServer)
94            fillPaintServer->addClient(static_cast<SVGStyledElement*>(svgElement));
95        else if (!fillPaintServer && paintType == SVGPaint::SVG_PAINTTYPE_URI)
96            svgElement->document()->accessSVGExtensions()->addPendingResource(id, static_cast<SVGStyledElement*>(svgElement));
97    }
98    if (paintType != SVGPaint::SVG_PAINTTYPE_URI && !fillPaintServer) {
99        fillPaintServer = sharedSolidPaintServer();
100        SVGPaintServerSolid* fillPaintServerSolid = static_cast<SVGPaintServerSolid*>(fillPaintServer);
101        if (paintType == SVGPaint::SVG_PAINTTYPE_CURRENTCOLOR)
102            fillPaintServerSolid->setColor(style->color());
103        else
104            fillPaintServerSolid->setColor(fill->color());
105        // FIXME: Ideally invalid colors would never get set on the RenderStyle and this could turn into an ASSERT
106        if (!fillPaintServerSolid->color().isValid())
107            fillPaintServer = 0;
108    }
109    if (!fillPaintServer) {
110        // default value (black), see bug 11017
111        fillPaintServer = sharedSolidPaintServer();
112        static_cast<SVGPaintServerSolid*>(fillPaintServer)->setColor(Color::black);
113    }
114    return fillPaintServer;
115}
116
117SVGPaintServer* SVGPaintServer::strokePaintServer(const RenderStyle* style, const RenderObject* item)
118{
119    if (!style->svgStyle()->hasStroke())
120        return 0;
121
122    SVGPaint* stroke = style->svgStyle()->strokePaint();
123
124    SVGPaintServer* strokePaintServer = 0;
125    SVGPaint::SVGPaintType paintType = stroke->paintType();
126    if (paintType == SVGPaint::SVG_PAINTTYPE_URI ||
127        paintType == SVGPaint::SVG_PAINTTYPE_URI_RGBCOLOR) {
128        AtomicString id(SVGURIReference::getTarget(stroke->uri()));
129        strokePaintServer = getPaintServerById(item->document(), id, item);
130
131        SVGElement* svgElement = static_cast<SVGElement*>(item->node());
132        ASSERT(svgElement && svgElement->document() && svgElement->isStyled());
133
134        if (item->isRenderPath() && strokePaintServer)
135            strokePaintServer->addClient(static_cast<SVGStyledElement*>(svgElement));
136        else if (!strokePaintServer && paintType == SVGPaint::SVG_PAINTTYPE_URI)
137            svgElement->document()->accessSVGExtensions()->addPendingResource(id, static_cast<SVGStyledElement*>(svgElement));
138    }
139    if (paintType != SVGPaint::SVG_PAINTTYPE_URI && !strokePaintServer) {
140        strokePaintServer = sharedSolidPaintServer();
141        SVGPaintServerSolid* strokePaintServerSolid = static_cast<SVGPaintServerSolid*>(strokePaintServer);
142        if (paintType == SVGPaint::SVG_PAINTTYPE_CURRENTCOLOR)
143            strokePaintServerSolid->setColor(style->color());
144        else
145            strokePaintServerSolid->setColor(stroke->color());
146        // FIXME: Ideally invalid colors would never get set on the RenderStyle and this could turn into an ASSERT
147        if (!strokePaintServerSolid->color().isValid())
148            strokePaintServer = 0;
149    }
150
151    return strokePaintServer;
152}
153
154void applyStrokeStyleToContext(GraphicsContext* context, RenderStyle* style, const RenderObject* object)
155{
156    context->setStrokeThickness(SVGRenderStyle::cssPrimitiveToLength(object, style->svgStyle()->strokeWidth(), 1.0f));
157    context->setLineCap(style->svgStyle()->capStyle());
158    context->setLineJoin(style->svgStyle()->joinStyle());
159    if (style->svgStyle()->joinStyle() == MiterJoin)
160        context->setMiterLimit(style->svgStyle()->strokeMiterLimit());
161
162    const DashArray& dashes = dashArrayFromRenderingStyle(object->style(), object->document()->documentElement()->renderStyle());
163    float dashOffset = SVGRenderStyle::cssPrimitiveToLength(object, style->svgStyle()->strokeDashOffset(), 0.0f);
164    context->setLineDash(dashes, dashOffset);
165}
166
167void SVGPaintServer::draw(GraphicsContext*& context, const RenderObject* path, SVGPaintTargetType type) const
168{
169    if (!setup(context, path, type))
170        return;
171
172    renderPath(context, path, type);
173    teardown(context, path, type);
174}
175
176void SVGPaintServer::renderPath(GraphicsContext*& context, const RenderObject* path, SVGPaintTargetType type) const
177{
178    const SVGRenderStyle* style = path ? path->style()->svgStyle() : 0;
179
180    if ((type & ApplyToFillTargetType) && (!style || style->hasFill()))
181        context->fillPath();
182
183    if ((type & ApplyToStrokeTargetType) && (!style || style->hasStroke()))
184        context->strokePath();
185}
186
187#if PLATFORM(SKIA) && !PLATFORM(ANDROID)
188void SVGPaintServer::teardown(GraphicsContext*& context, const RenderObject*, SVGPaintTargetType, bool) const
189{
190    // FIXME: Move this into the GraphicsContext
191    // WebKit implicitly expects us to reset the path.
192    // For example in fillAndStrokePath() of RenderPath.cpp the path is
193    // added back to the context after filling. This is because internally it
194    // calls CGContextFillPath() which closes the path.
195    context->beginPath();
196    context->platformContext()->setFillShader(0);
197    context->platformContext()->setStrokeShader(0);
198}
199#else
200void SVGPaintServer::teardown(GraphicsContext*&, const RenderObject*, SVGPaintTargetType, bool) const
201{
202}
203#endif
204
205DashArray dashArrayFromRenderingStyle(const RenderStyle* style, RenderStyle* rootStyle)
206{
207    DashArray array;
208
209    CSSValueList* dashes = style->svgStyle()->strokeDashArray();
210    if (dashes) {
211        CSSPrimitiveValue* dash = 0;
212        unsigned long len = dashes->length();
213        for (unsigned long i = 0; i < len; i++) {
214            dash = static_cast<CSSPrimitiveValue*>(dashes->itemWithoutBoundsCheck(i));
215            if (!dash)
216                continue;
217
218            array.append((float) dash->computeLengthFloat(const_cast<RenderStyle*>(style), rootStyle));
219        }
220    }
221
222    return array;
223}
224
225} // namespace WebCore
226
227#endif
228