Home | History | Annotate | Download | only in svg
      1 /*
      2  * Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann (at) kde.org>
      3  * Copyright (C) 2004, 2005, 2008 Rob Buis <buis (at) kde.org>
      4  * Copyright (C) 2005, 2007 Eric Seidel <eric (at) webkit.org>
      5  * Copyright (C) 2009 Google, Inc.
      6  * Copyright (C) 2009 Dirk Schulze <krit (at) webkit.org>
      7  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
      8  * Copyright (C) 2009 Jeff Schiller <codedread (at) gmail.com>
      9  *
     10  * This library is free software; you can redistribute it and/or
     11  * modify it under the terms of the GNU Library General Public
     12  * License as published by the Free Software Foundation; either
     13  * version 2 of the License, or (at your option) any later version.
     14  *
     15  * This library is distributed in the hope that it will be useful,
     16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     18  * Library General Public License for more details.
     19  *
     20  * You should have received a copy of the GNU Library General Public License
     21  * along with this library; see the file COPYING.LIB.  If not, write to
     22  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     23  * Boston, MA 02110-1301, USA.
     24  */
     25 
     26 #include "config.h"
     27 
     28 #if ENABLE(SVG)
     29 #include "RenderSVGPath.h"
     30 
     31 #include "FloatPoint.h"
     32 #include "FloatQuad.h"
     33 #include "GraphicsContext.h"
     34 #include "HitTestRequest.h"
     35 #include "PointerEventsHitRules.h"
     36 #include "RenderSVGContainer.h"
     37 #include "RenderSVGResourceMarker.h"
     38 #include "RenderSVGResourceSolidColor.h"
     39 #include "SVGRenderSupport.h"
     40 #include "SVGResources.h"
     41 #include "SVGStyledTransformableElement.h"
     42 #include "SVGTransformList.h"
     43 #include "SVGURIReference.h"
     44 #include "StrokeStyleApplier.h"
     45 #include <wtf/MathExtras.h>
     46 
     47 namespace WebCore {
     48 
     49 class BoundingRectStrokeStyleApplier : public StrokeStyleApplier {
     50 public:
     51     BoundingRectStrokeStyleApplier(const RenderObject* object, RenderStyle* style)
     52         : m_object(object)
     53         , m_style(style)
     54     {
     55         ASSERT(style);
     56         ASSERT(object);
     57     }
     58 
     59     void strokeStyle(GraphicsContext* gc)
     60     {
     61         SVGRenderSupport::applyStrokeStyleToContext(gc, m_style, m_object);
     62     }
     63 
     64 private:
     65     const RenderObject* m_object;
     66     RenderStyle* m_style;
     67 };
     68 
     69 RenderSVGPath::RenderSVGPath(SVGStyledTransformableElement* node)
     70     : RenderSVGModelObject(node)
     71     , m_needsBoundariesUpdate(false) // default is false, the cached rects are empty from the beginning
     72     , m_needsPathUpdate(true) // default is true, so we grab a Path object once from SVGStyledTransformableElement
     73     , m_needsTransformUpdate(true) // default is true, so we grab a AffineTransform object once from SVGStyledTransformableElement
     74 {
     75 }
     76 
     77 RenderSVGPath::~RenderSVGPath()
     78 {
     79 }
     80 
     81 bool RenderSVGPath::fillContains(const FloatPoint& point, bool requiresFill, WindRule fillRule)
     82 {
     83     if (!m_fillBoundingBox.contains(point))
     84         return false;
     85 
     86     Color fallbackColor;
     87     if (requiresFill && !RenderSVGResource::fillPaintingResource(this, style(), fallbackColor))
     88         return false;
     89 
     90     return m_path.contains(point, fillRule);
     91 }
     92 
     93 bool RenderSVGPath::strokeContains(const FloatPoint& point, bool requiresStroke)
     94 {
     95     if (!m_strokeAndMarkerBoundingBox.contains(point))
     96         return false;
     97 
     98     Color fallbackColor;
     99     if (requiresStroke && !RenderSVGResource::strokePaintingResource(this, style(), fallbackColor))
    100         return false;
    101 
    102     BoundingRectStrokeStyleApplier strokeStyle(this, style());
    103     return m_path.strokeContains(&strokeStyle, point);
    104 }
    105 
    106 void RenderSVGPath::layout()
    107 {
    108     LayoutRepainter repainter(*this, checkForRepaintDuringLayout() && selfNeedsLayout());
    109     SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node());
    110 
    111     bool updateCachedBoundariesInParents = false;
    112 
    113     bool needsPathUpdate = m_needsPathUpdate;
    114     if (needsPathUpdate) {
    115         m_path.clear();
    116         element->toPathData(m_path);
    117         m_needsPathUpdate = false;
    118         updateCachedBoundariesInParents = true;
    119     }
    120 
    121     if (m_needsTransformUpdate) {
    122         m_localTransform = element->animatedLocalTransform();
    123         m_needsTransformUpdate = false;
    124         updateCachedBoundariesInParents = true;
    125     }
    126 
    127     if (m_needsBoundariesUpdate)
    128         updateCachedBoundariesInParents = true;
    129 
    130     // Invalidate all resources of this client if our layout changed.
    131     if (m_everHadLayout && selfNeedsLayout()) {
    132         SVGResourcesCache::clientLayoutChanged(this);
    133         m_markerLayoutInfo.clear();
    134     }
    135 
    136     // At this point LayoutRepainter already grabbed the old bounds,
    137     // recalculate them now so repaintAfterLayout() uses the new bounds.
    138     if (needsPathUpdate || m_needsBoundariesUpdate) {
    139         updateCachedBoundaries();
    140         m_needsBoundariesUpdate = false;
    141     }
    142 
    143     // If our bounds changed, notify the parents.
    144     if (updateCachedBoundariesInParents)
    145         RenderSVGModelObject::setNeedsBoundariesUpdate();
    146 
    147     repainter.repaintAfterLayout();
    148     setNeedsLayout(false);
    149 }
    150 
    151 void RenderSVGPath::fillAndStrokePath(GraphicsContext* context)
    152 {
    153     RenderStyle* style = this->style();
    154 
    155     Color fallbackColor;
    156     if (RenderSVGResource* fillPaintingResource = RenderSVGResource::fillPaintingResource(this, style, fallbackColor)) {
    157         if (fillPaintingResource->applyResource(this, style, context, ApplyToFillMode))
    158             fillPaintingResource->postApplyResource(this, context, ApplyToFillMode, &m_path);
    159         else if (fallbackColor.isValid()) {
    160             RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
    161             fallbackResource->setColor(fallbackColor);
    162             if (fallbackResource->applyResource(this, style, context, ApplyToFillMode))
    163                 fallbackResource->postApplyResource(this, context, ApplyToFillMode, &m_path);
    164         }
    165     }
    166 
    167     fallbackColor = Color();
    168     RenderSVGResource* strokePaintingResource = RenderSVGResource::strokePaintingResource(this, style, fallbackColor);
    169     if (!strokePaintingResource)
    170         return;
    171 
    172     Path path;
    173 
    174     bool nonScalingStroke = style->svgStyle()->vectorEffect() == VE_NON_SCALING_STROKE;
    175     bool restoreContext = false;
    176     if (nonScalingStroke) {
    177         SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node());
    178         AffineTransform nonScalingStrokeTransform = element->getScreenCTM(SVGLocatable::DisallowStyleUpdate);
    179         if (!nonScalingStrokeTransform.isInvertible())
    180             return;
    181 
    182         path = m_path;
    183         path.transform(nonScalingStrokeTransform);
    184 
    185         context->save();
    186         context->concatCTM(nonScalingStrokeTransform.inverse());
    187         restoreContext = true;
    188     }
    189 
    190     if (strokePaintingResource->applyResource(this, style, context, ApplyToStrokeMode))
    191         strokePaintingResource->postApplyResource(this, context, ApplyToStrokeMode, nonScalingStroke ? &path : &m_path);
    192     else if (fallbackColor.isValid()) {
    193         RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
    194         fallbackResource->setColor(fallbackColor);
    195         if (fallbackResource->applyResource(this, style, context, ApplyToStrokeMode))
    196             fallbackResource->postApplyResource(this, context, ApplyToStrokeMode, nonScalingStroke ? &path : &m_path);
    197     }
    198 
    199     if (restoreContext)
    200         context->restore();
    201 }
    202 
    203 void RenderSVGPath::paint(PaintInfo& paintInfo, int, int)
    204 {
    205     if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN || m_path.isEmpty())
    206         return;
    207 
    208     FloatRect boundingBox = repaintRectInLocalCoordinates();
    209     if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(boundingBox, m_localTransform, paintInfo))
    210         return;
    211 
    212     PaintInfo childPaintInfo(paintInfo);
    213     bool drawsOutline = style()->outlineWidth() && (childPaintInfo.phase == PaintPhaseOutline || childPaintInfo.phase == PaintPhaseSelfOutline);
    214     if (drawsOutline || childPaintInfo.phase == PaintPhaseForeground) {
    215         childPaintInfo.context->save();
    216         childPaintInfo.applyTransform(m_localTransform);
    217 
    218         if (childPaintInfo.phase == PaintPhaseForeground) {
    219             PaintInfo savedInfo(childPaintInfo);
    220 
    221             if (SVGRenderSupport::prepareToRenderSVGContent(this, childPaintInfo)) {
    222                 const SVGRenderStyle* svgStyle = style()->svgStyle();
    223                 if (svgStyle->shapeRendering() == SR_CRISPEDGES)
    224                     childPaintInfo.context->setShouldAntialias(false);
    225 
    226                 fillAndStrokePath(childPaintInfo.context);
    227 
    228                 if (svgStyle->hasMarkers())
    229                     m_markerLayoutInfo.drawMarkers(childPaintInfo);
    230             }
    231 
    232             SVGRenderSupport::finishRenderSVGContent(this, childPaintInfo, savedInfo.context);
    233         }
    234 
    235         if (drawsOutline)
    236             paintOutline(childPaintInfo.context, static_cast<int>(boundingBox.x()), static_cast<int>(boundingBox.y()),
    237                 static_cast<int>(boundingBox.width()), static_cast<int>(boundingBox.height()));
    238 
    239         childPaintInfo.context->restore();
    240     }
    241 }
    242 
    243 // This method is called from inside paintOutline() since we call paintOutline()
    244 // while transformed to our coord system, return local coords
    245 void RenderSVGPath::addFocusRingRects(Vector<IntRect>& rects, int, int)
    246 {
    247     IntRect rect = enclosingIntRect(repaintRectInLocalCoordinates());
    248     if (!rect.isEmpty())
    249         rects.append(rect);
    250 }
    251 
    252 bool RenderSVGPath::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
    253 {
    254     // We only draw in the forground phase, so we only hit-test then.
    255     if (hitTestAction != HitTestForeground)
    256         return false;
    257 
    258     FloatPoint localPoint = m_localTransform.inverse().mapPoint(pointInParent);
    259 
    260     if (!SVGRenderSupport::pointInClippingArea(this, localPoint))
    261         return false;
    262 
    263     PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_PATH_HITTESTING, request, style()->pointerEvents());
    264     bool isVisible = (style()->visibility() == VISIBLE);
    265     if (isVisible || !hitRules.requireVisible) {
    266         const SVGRenderStyle* svgStyle = style()->svgStyle();
    267         WindRule fillRule = svgStyle->fillRule();
    268         if (request.svgClipContent())
    269             fillRule = svgStyle->clipRule();
    270         if ((hitRules.canHitStroke && (svgStyle->hasStroke() || !hitRules.requireStroke) && strokeContains(localPoint, hitRules.requireStroke))
    271             || (hitRules.canHitFill && (svgStyle->hasFill() || !hitRules.requireFill) && fillContains(localPoint, hitRules.requireFill, fillRule))) {
    272             updateHitTestResult(result, roundedIntPoint(localPoint));
    273             return true;
    274         }
    275     }
    276     return false;
    277 }
    278 
    279 FloatRect RenderSVGPath::calculateMarkerBoundsIfNeeded()
    280 {
    281     SVGElement* svgElement = static_cast<SVGElement*>(node());
    282     ASSERT(svgElement && svgElement->document());
    283     if (!svgElement->isStyled())
    284         return FloatRect();
    285 
    286     SVGStyledElement* styledElement = static_cast<SVGStyledElement*>(svgElement);
    287     if (!styledElement->supportsMarkers())
    288         return FloatRect();
    289 
    290     const SVGRenderStyle* svgStyle = style()->svgStyle();
    291     ASSERT(svgStyle->hasMarkers());
    292 
    293     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
    294     if (!resources)
    295         return FloatRect();
    296 
    297     RenderSVGResourceMarker* markerStart = resources->markerStart();
    298     RenderSVGResourceMarker* markerMid = resources->markerMid();
    299     RenderSVGResourceMarker* markerEnd = resources->markerEnd();
    300     if (!markerStart && !markerMid && !markerEnd)
    301         return FloatRect();
    302 
    303     return m_markerLayoutInfo.calculateBoundaries(markerStart, markerMid, markerEnd, svgStyle->strokeWidth().value(svgElement), m_path);
    304 }
    305 
    306 void RenderSVGPath::updateCachedBoundaries()
    307 {
    308     if (m_path.isEmpty()) {
    309         m_fillBoundingBox = FloatRect();
    310         m_strokeAndMarkerBoundingBox = FloatRect();
    311         m_repaintBoundingBox = FloatRect();
    312         return;
    313     }
    314 
    315     // Cache _unclipped_ fill bounding box, used for calculations in resources
    316     m_fillBoundingBox = m_path.boundingRect();
    317 
    318     // Cache _unclipped_ stroke bounding box, used for calculations in resources (includes marker boundaries)
    319     m_strokeAndMarkerBoundingBox = m_fillBoundingBox;
    320 
    321     const SVGRenderStyle* svgStyle = style()->svgStyle();
    322     if (svgStyle->hasStroke()) {
    323         BoundingRectStrokeStyleApplier strokeStyle(this, style());
    324         m_strokeAndMarkerBoundingBox.unite(m_path.strokeBoundingRect(&strokeStyle));
    325     }
    326 
    327     if (svgStyle->hasMarkers()) {
    328         FloatRect markerBounds = calculateMarkerBoundsIfNeeded();
    329         if (!markerBounds.isEmpty())
    330             m_strokeAndMarkerBoundingBox.unite(markerBounds);
    331     }
    332 
    333     // Cache smallest possible repaint rectangle
    334     m_repaintBoundingBox = m_strokeAndMarkerBoundingBox;
    335     SVGRenderSupport::intersectRepaintRectWithResources(this, m_repaintBoundingBox);
    336 }
    337 
    338 }
    339 
    340 #endif // ENABLE(SVG)
    341