Home | History | Annotate | Download | only in graphics
      1 /*
      2  * Copyright (C) 2006 Nikolas Zimmermann <zimmermann (at) kde.org>
      3  *               2007 Rob Buis <buis (at) kde.org>
      4  *               2008 Dirk Schulze <krit (at) 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 
     45 namespace WebCore {
     46 
     47 SVGPaintServer::SVGPaintServer()
     48 {
     49 }
     50 
     51 SVGPaintServer::~SVGPaintServer()
     52 {
     53 }
     54 
     55 TextStream& operator<<(TextStream& ts, const SVGPaintServer& paintServer)
     56 {
     57     return paintServer.externalRepresentation(ts);
     58 }
     59 
     60 SVGPaintServer* 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 
     69 SVGPaintServerSolid* SVGPaintServer::sharedSolidPaintServer()
     70 {
     71     static SVGPaintServerSolid* _sharedSolidPaintServer = SVGPaintServerSolid::create().releaseRef();
     72 
     73     return _sharedSolidPaintServer;
     74 }
     75 
     76 SVGPaintServer* 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 
    117 SVGPaintServer* 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 
    154 void applyStrokeStyleToContext(GraphicsContext* context, const 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 
    167 bool SVGPaintServer::setup(GraphicsContext*& context, const RenderObject* object, SVGPaintTargetType type, bool isPaintingText) const
    168 {
    169     return setup(context, object, object ? object->style() : 0, type, isPaintingText);
    170 }
    171 
    172 void SVGPaintServer::draw(GraphicsContext*& context, const RenderObject* path, SVGPaintTargetType type) const
    173 {
    174     if (!setup(context, path, type))
    175         return;
    176 
    177     renderPath(context, path, type);
    178     teardown(context, path, type);
    179 }
    180 
    181 void SVGPaintServer::renderPath(GraphicsContext*& context, const RenderObject* path, SVGPaintTargetType type) const
    182 {
    183     const SVGRenderStyle* style = path ? path->style()->svgStyle() : 0;
    184 
    185     if ((type & ApplyToFillTargetType) && (!style || style->hasFill()))
    186         context->fillPath();
    187 
    188     if ((type & ApplyToStrokeTargetType) && (!style || style->hasStroke()))
    189         context->strokePath();
    190 }
    191 
    192 #if PLATFORM(SKIA) && !PLATFORM(ANDROID)
    193 void SVGPaintServer::teardown(GraphicsContext*& context, const RenderObject*, SVGPaintTargetType, bool) const
    194 {
    195     // FIXME: Move this into the GraphicsContext
    196     // WebKit implicitly expects us to reset the path.
    197     // For example in fillAndStrokePath() of RenderPath.cpp the path is
    198     // added back to the context after filling. This is because internally it
    199     // calls CGContextFillPath() which closes the path.
    200     context->beginPath();
    201     context->platformContext()->setFillShader(0);
    202     context->platformContext()->setStrokeShader(0);
    203 }
    204 #else
    205 void SVGPaintServer::teardown(GraphicsContext*&, const RenderObject*, SVGPaintTargetType, bool) const
    206 {
    207 }
    208 #endif
    209 
    210 DashArray dashArrayFromRenderingStyle(const RenderStyle* style, RenderStyle* rootStyle)
    211 {
    212     DashArray array;
    213 
    214     CSSValueList* dashes = style->svgStyle()->strokeDashArray();
    215     if (dashes) {
    216         CSSPrimitiveValue* dash = 0;
    217         unsigned long len = dashes->length();
    218         for (unsigned long i = 0; i < len; i++) {
    219             dash = static_cast<CSSPrimitiveValue*>(dashes->itemWithoutBoundsCheck(i));
    220             if (!dash)
    221                 continue;
    222 
    223             array.append((float) dash->computeLengthFloat(const_cast<RenderStyle*>(style), rootStyle));
    224         }
    225     }
    226 
    227     return array;
    228 }
    229 
    230 } // namespace WebCore
    231 
    232 #endif
    233