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  * Copyright (C) 2011 Renata Hodovan <reni (at) webkit.org>
     10  * Copyright (C) 2011 University of Szeged
     11  *
     12  * This library is free software; you can redistribute it and/or
     13  * modify it under the terms of the GNU Library General Public
     14  * License as published by the Free Software Foundation; either
     15  * version 2 of the License, or (at your option) any later version.
     16  *
     17  * This library is distributed in the hope that it will be useful,
     18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     20  * Library General Public License for more details.
     21  *
     22  * You should have received a copy of the GNU Library General Public License
     23  * along with this library; see the file COPYING.LIB.  If not, write to
     24  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     25  * Boston, MA 02110-1301, USA.
     26  */
     27 
     28 #include "config.h"
     29 
     30 #include "core/rendering/svg/RenderSVGShape.h"
     31 
     32 #include "core/platform/graphics/FloatPoint.h"
     33 #include "core/platform/graphics/GraphicsContextStateSaver.h"
     34 #include "core/rendering/HitTestRequest.h"
     35 #include "core/rendering/LayoutRepainter.h"
     36 #include "core/rendering/PointerEventsHitRules.h"
     37 #include "core/rendering/svg/RenderSVGResourceMarker.h"
     38 #include "core/rendering/svg/RenderSVGResourceSolidColor.h"
     39 #include "core/rendering/svg/SVGPathData.h"
     40 #include "core/rendering/svg/SVGRenderingContext.h"
     41 #include "core/rendering/svg/SVGResources.h"
     42 #include "core/rendering/svg/SVGResourcesCache.h"
     43 #include "core/svg/SVGGraphicsElement.h"
     44 #include "wtf/MathExtras.h"
     45 
     46 namespace WebCore {
     47 
     48 RenderSVGShape::RenderSVGShape(SVGGraphicsElement* node)
     49     : RenderSVGModelObject(node)
     50     , m_needsBoundariesUpdate(false) // Default is false, the cached rects are empty from the beginning.
     51     , m_needsShapeUpdate(true) // Default is true, so we grab a Path object once from SVGGraphicsElement.
     52     , m_needsTransformUpdate(true) // Default is true, so we grab a AffineTransform object once from SVGGraphicsElement.
     53 {
     54 }
     55 
     56 RenderSVGShape::~RenderSVGShape()
     57 {
     58 }
     59 
     60 void RenderSVGShape::updateShapeFromElement()
     61 {
     62     m_path.clear();
     63     m_path = adoptPtr(new Path);
     64     ASSERT(RenderSVGShape::isEmpty());
     65 
     66     SVGGraphicsElement* element = toSVGGraphicsElement(node());
     67     updatePathFromGraphicsElement(element, path());
     68     processMarkerPositions();
     69 
     70     m_fillBoundingBox = calculateObjectBoundingBox();
     71     m_strokeBoundingBox = calculateStrokeBoundingBox();
     72 }
     73 
     74 bool RenderSVGShape::isEmpty() const
     75 {
     76     return path().isEmpty();
     77 }
     78 
     79 void RenderSVGShape::fillShape(GraphicsContext* context) const
     80 {
     81     context->fillPath(path());
     82 }
     83 
     84 void RenderSVGShape::strokeShape(GraphicsContext* context) const
     85 {
     86     ASSERT(m_path);
     87     Path* usePath = m_path.get();
     88 
     89     if (hasNonScalingStroke())
     90         usePath = nonScalingStrokePath(usePath, nonScalingStrokeTransform());
     91 
     92     context->strokePath(*usePath);
     93 }
     94 
     95 bool RenderSVGShape::shapeDependentStrokeContains(const FloatPoint& point)
     96 {
     97     ASSERT(m_path);
     98     StrokeData strokeData;
     99     SVGRenderSupport::applyStrokeStyleToStrokeData(&strokeData, style(), this);
    100 
    101     if (hasNonScalingStroke()) {
    102         AffineTransform nonScalingTransform = nonScalingStrokeTransform();
    103         Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform);
    104 
    105         return usePath->strokeContains(nonScalingTransform.mapPoint(point), strokeData);
    106     }
    107 
    108     return m_path->strokeContains(point, strokeData);
    109 }
    110 
    111 bool RenderSVGShape::shapeDependentFillContains(const FloatPoint& point, const WindRule fillRule) const
    112 {
    113     return path().contains(point, fillRule);
    114 }
    115 
    116 bool RenderSVGShape::fillContains(const FloatPoint& point, bool requiresFill, const WindRule fillRule)
    117 {
    118     if (!m_fillBoundingBox.contains(point))
    119         return false;
    120 
    121     StyleColor fallbackColor;
    122     if (requiresFill && !RenderSVGResource::fillPaintingResource(this, style(), fallbackColor))
    123         return false;
    124 
    125     return shapeDependentFillContains(point, fillRule);
    126 }
    127 
    128 bool RenderSVGShape::strokeContains(const FloatPoint& point, bool requiresStroke)
    129 {
    130     if (!strokeBoundingBox().contains(point))
    131         return false;
    132 
    133     StyleColor fallbackColor;
    134     if (requiresStroke && !RenderSVGResource::strokePaintingResource(this, style(), fallbackColor))
    135         return false;
    136 
    137     return shapeDependentStrokeContains(point);
    138 }
    139 
    140 void RenderSVGShape::layout()
    141 {
    142     StackStats::LayoutCheckPoint layoutCheckPoint;
    143     LayoutRepainter repainter(*this, SVGRenderSupport::checkForSVGRepaintDuringLayout(this) && selfNeedsLayout());
    144     SVGGraphicsElement* element = toSVGGraphicsElement(node());
    145 
    146     bool updateCachedBoundariesInParents = false;
    147 
    148     if (m_needsShapeUpdate || m_needsBoundariesUpdate) {
    149         updateShapeFromElement();
    150         m_needsShapeUpdate = false;
    151         updateRepaintBoundingBox();
    152         m_needsBoundariesUpdate = false;
    153         updateCachedBoundariesInParents = true;
    154     }
    155 
    156     if (m_needsTransformUpdate) {
    157         m_localTransform = element->animatedLocalTransform();
    158         m_needsTransformUpdate = false;
    159         updateCachedBoundariesInParents = true;
    160     }
    161 
    162     // Invalidate all resources of this client if our layout changed.
    163     if (everHadLayout() && selfNeedsLayout())
    164         SVGResourcesCache::clientLayoutChanged(this);
    165 
    166     // If our bounds changed, notify the parents.
    167     if (updateCachedBoundariesInParents)
    168         RenderSVGModelObject::setNeedsBoundariesUpdate();
    169 
    170     repainter.repaintAfterLayout();
    171     clearNeedsLayout();
    172 }
    173 
    174 Path* RenderSVGShape::nonScalingStrokePath(const Path* path, const AffineTransform& strokeTransform) const
    175 {
    176     DEFINE_STATIC_LOCAL(Path, tempPath, ());
    177 
    178     tempPath = *path;
    179     tempPath.transform(strokeTransform);
    180 
    181     return &tempPath;
    182 }
    183 
    184 bool RenderSVGShape::setupNonScalingStrokeContext(AffineTransform& strokeTransform, GraphicsContextStateSaver& stateSaver)
    185 {
    186     if (!strokeTransform.isInvertible())
    187         return false;
    188 
    189     stateSaver.save();
    190     stateSaver.context()->concatCTM(strokeTransform.inverse());
    191     return true;
    192 }
    193 
    194 AffineTransform RenderSVGShape::nonScalingStrokeTransform() const
    195 {
    196     SVGGraphicsElement* element = toSVGGraphicsElement(node());
    197     return element->getScreenCTM(SVGLocatable::DisallowStyleUpdate);
    198 }
    199 
    200 bool RenderSVGShape::shouldGenerateMarkerPositions() const
    201 {
    202     if (!style()->svgStyle()->hasMarkers())
    203         return false;
    204 
    205     SVGGraphicsElement* element = toSVGGraphicsElement(node());
    206     if (!element->supportsMarkers())
    207         return false;
    208 
    209     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
    210     if (!resources)
    211         return false;
    212 
    213     return resources->markerStart() || resources->markerMid() || resources->markerEnd();
    214 }
    215 
    216 void RenderSVGShape::fillShape(RenderStyle* style, GraphicsContext* context)
    217 {
    218     StyleColor fallbackColor;
    219     if (RenderSVGResource* fillPaintingResource = RenderSVGResource::fillPaintingResource(this, style, fallbackColor)) {
    220         if (fillPaintingResource->applyResource(this, style, context, ApplyToFillMode))
    221             fillPaintingResource->postApplyResource(this, context, ApplyToFillMode, 0, this);
    222         else if (fallbackColor.isValid()) {
    223             RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
    224             fallbackResource->setColor(fallbackColor.color());
    225             if (fallbackResource->applyResource(this, style, context, ApplyToFillMode))
    226                 fallbackResource->postApplyResource(this, context, ApplyToFillMode, 0, this);
    227         }
    228     }
    229 }
    230 
    231 void RenderSVGShape::strokeShape(RenderStyle* style, GraphicsContext* context)
    232 {
    233     StyleColor fallbackColor;
    234     if (RenderSVGResource* strokePaintingResource = RenderSVGResource::strokePaintingResource(this, style, fallbackColor)) {
    235         if (strokePaintingResource->applyResource(this, style, context, ApplyToStrokeMode))
    236             strokePaintingResource->postApplyResource(this, context, ApplyToStrokeMode, 0, this);
    237         else if (fallbackColor.isValid()) {
    238             RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
    239             fallbackResource->setColor(fallbackColor.color());
    240             if (fallbackResource->applyResource(this, style, context, ApplyToStrokeMode))
    241                 fallbackResource->postApplyResource(this, context, ApplyToStrokeMode, 0, this);
    242         }
    243     }
    244 }
    245 
    246 void RenderSVGShape::fillAndStrokeShape(GraphicsContext* context)
    247 {
    248     RenderStyle* style = this->style();
    249 
    250     fillShape(style, context);
    251 
    252     if (!style->svgStyle()->hasVisibleStroke())
    253         return;
    254 
    255     GraphicsContextStateSaver stateSaver(*context, false);
    256 
    257     if (hasNonScalingStroke()) {
    258         AffineTransform nonScalingTransform = nonScalingStrokeTransform();
    259         if (!setupNonScalingStrokeContext(nonScalingTransform, stateSaver))
    260             return;
    261     }
    262 
    263     strokeShape(style, context);
    264 }
    265 
    266 void RenderSVGShape::paint(PaintInfo& paintInfo, const LayoutPoint&)
    267 {
    268     ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
    269 
    270     if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN || isEmpty())
    271         return;
    272     FloatRect boundingBox = repaintRectInLocalCoordinates();
    273     if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(boundingBox, m_localTransform, paintInfo))
    274         return;
    275 
    276     PaintInfo childPaintInfo(paintInfo);
    277     bool drawsOutline = style()->outlineWidth() && (childPaintInfo.phase == PaintPhaseOutline || childPaintInfo.phase == PaintPhaseSelfOutline);
    278     if (drawsOutline || childPaintInfo.phase == PaintPhaseForeground) {
    279         GraphicsContextStateSaver stateSaver(*childPaintInfo.context);
    280         childPaintInfo.applyTransform(m_localTransform);
    281 
    282         if (childPaintInfo.phase == PaintPhaseForeground) {
    283             SVGRenderingContext renderingContext(this, childPaintInfo);
    284 
    285             if (renderingContext.isRenderingPrepared()) {
    286                 const SVGRenderStyle* svgStyle = style()->svgStyle();
    287                 if (svgStyle->shapeRendering() == SR_CRISPEDGES)
    288                     childPaintInfo.context->setShouldAntialias(false);
    289 
    290                 fillAndStrokeShape(childPaintInfo.context);
    291                 if (!m_markerPositions.isEmpty())
    292                     drawMarkers(childPaintInfo);
    293             }
    294         }
    295 
    296         if (drawsOutline)
    297             paintOutline(childPaintInfo, IntRect(boundingBox));
    298     }
    299 }
    300 
    301 // This method is called from inside paintOutline() since we call paintOutline()
    302 // while transformed to our coord system, return local coords
    303 void RenderSVGShape::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint&, const RenderLayerModelObject*)
    304 {
    305     IntRect rect = enclosingIntRect(repaintRectInLocalCoordinates());
    306     if (!rect.isEmpty())
    307         rects.append(rect);
    308 }
    309 
    310 bool RenderSVGShape::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
    311 {
    312     // We only draw in the forground phase, so we only hit-test then.
    313     if (hitTestAction != HitTestForeground)
    314         return false;
    315 
    316     FloatPoint localPoint = m_localTransform.inverse().mapPoint(pointInParent);
    317 
    318     if (!SVGRenderSupport::pointInClippingArea(this, localPoint))
    319         return false;
    320 
    321     PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_PATH_HITTESTING, request, style()->pointerEvents());
    322     bool isVisible = (style()->visibility() == VISIBLE);
    323     if (isVisible || !hitRules.requireVisible) {
    324         const SVGRenderStyle* svgStyle = style()->svgStyle();
    325         WindRule fillRule = svgStyle->fillRule();
    326         if (request.svgClipContent())
    327             fillRule = svgStyle->clipRule();
    328         if ((hitRules.canHitStroke && (svgStyle->hasStroke() || !hitRules.requireStroke) && strokeContains(localPoint, hitRules.requireStroke))
    329             || (hitRules.canHitFill && (svgStyle->hasFill() || !hitRules.requireFill) && fillContains(localPoint, hitRules.requireFill, fillRule))) {
    330             updateHitTestResult(result, roundedLayoutPoint(localPoint));
    331             return true;
    332         }
    333     }
    334     return false;
    335 }
    336 
    337 static inline RenderSVGResourceMarker* markerForType(SVGMarkerType type, RenderSVGResourceMarker* markerStart, RenderSVGResourceMarker* markerMid, RenderSVGResourceMarker* markerEnd)
    338 {
    339     switch (type) {
    340     case StartMarker:
    341         return markerStart;
    342     case MidMarker:
    343         return markerMid;
    344     case EndMarker:
    345         return markerEnd;
    346     }
    347 
    348     ASSERT_NOT_REACHED();
    349     return 0;
    350 }
    351 
    352 FloatRect RenderSVGShape::markerRect(float strokeWidth) const
    353 {
    354     ASSERT(!m_markerPositions.isEmpty());
    355 
    356     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
    357     ASSERT(resources);
    358 
    359     RenderSVGResourceMarker* markerStart = resources->markerStart();
    360     RenderSVGResourceMarker* markerMid = resources->markerMid();
    361     RenderSVGResourceMarker* markerEnd = resources->markerEnd();
    362     ASSERT(markerStart || markerMid || markerEnd);
    363 
    364     FloatRect boundaries;
    365     unsigned size = m_markerPositions.size();
    366     for (unsigned i = 0; i < size; ++i) {
    367         if (RenderSVGResourceMarker* marker = markerForType(m_markerPositions[i].type, markerStart, markerMid, markerEnd))
    368             boundaries.unite(marker->markerBoundaries(marker->markerTransformation(m_markerPositions[i].origin, m_markerPositions[i].angle, strokeWidth)));
    369     }
    370     return boundaries;
    371 }
    372 
    373 FloatRect RenderSVGShape::calculateObjectBoundingBox() const
    374 {
    375     return path().boundingRect();
    376 }
    377 
    378 FloatRect RenderSVGShape::calculateStrokeBoundingBox() const
    379 {
    380     ASSERT(m_path);
    381     FloatRect strokeBoundingBox = m_fillBoundingBox;
    382 
    383     if (style()->svgStyle()->hasStroke()) {
    384         StrokeData strokeData;
    385         SVGRenderSupport::applyStrokeStyleToStrokeData(&strokeData, style(), this);
    386         if (hasNonScalingStroke()) {
    387             AffineTransform nonScalingTransform = nonScalingStrokeTransform();
    388             if (nonScalingTransform.isInvertible()) {
    389                 Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform);
    390                 FloatRect strokeBoundingRect = usePath->strokeBoundingRect(strokeData);
    391                 strokeBoundingRect = nonScalingTransform.inverse().mapRect(strokeBoundingRect);
    392                 strokeBoundingBox.unite(strokeBoundingRect);
    393             }
    394         } else {
    395             strokeBoundingBox.unite(path().strokeBoundingRect(strokeData));
    396         }
    397     }
    398 
    399     if (!m_markerPositions.isEmpty())
    400         strokeBoundingBox.unite(markerRect(strokeWidth()));
    401 
    402     return strokeBoundingBox;
    403 }
    404 
    405 void RenderSVGShape::updateRepaintBoundingBox()
    406 {
    407     m_repaintBoundingBox = strokeBoundingBox();
    408     SVGRenderSupport::intersectRepaintRectWithResources(this, m_repaintBoundingBox);
    409 }
    410 
    411 float RenderSVGShape::strokeWidth() const
    412 {
    413     SVGElement* svgElement = toSVGElement(node());
    414     SVGLengthContext lengthContext(svgElement);
    415     return style()->svgStyle()->strokeWidth().value(lengthContext);
    416 }
    417 
    418 bool RenderSVGShape::hasSmoothStroke() const
    419 {
    420     const SVGRenderStyle* svgStyle = style()->svgStyle();
    421     return svgStyle->strokeDashArray().isEmpty()
    422         && svgStyle->strokeMiterLimit() == svgStyle->initialStrokeMiterLimit()
    423         && svgStyle->joinStyle() == svgStyle->initialJoinStyle()
    424         && svgStyle->capStyle() == svgStyle->initialCapStyle();
    425 }
    426 
    427 void RenderSVGShape::drawMarkers(PaintInfo& paintInfo)
    428 {
    429     ASSERT(!m_markerPositions.isEmpty());
    430 
    431     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
    432     if (!resources)
    433         return;
    434 
    435     RenderSVGResourceMarker* markerStart = resources->markerStart();
    436     RenderSVGResourceMarker* markerMid = resources->markerMid();
    437     RenderSVGResourceMarker* markerEnd = resources->markerEnd();
    438     if (!markerStart && !markerMid && !markerEnd)
    439         return;
    440 
    441     float strokeWidth = this->strokeWidth();
    442     unsigned size = m_markerPositions.size();
    443     for (unsigned i = 0; i < size; ++i) {
    444         if (RenderSVGResourceMarker* marker = markerForType(m_markerPositions[i].type, markerStart, markerMid, markerEnd))
    445             marker->draw(paintInfo, marker->markerTransformation(m_markerPositions[i].origin, m_markerPositions[i].angle, strokeWidth));
    446     }
    447 }
    448 
    449 void RenderSVGShape::processMarkerPositions()
    450 {
    451     m_markerPositions.clear();
    452 
    453     if (!shouldGenerateMarkerPositions())
    454         return;
    455 
    456     ASSERT(m_path);
    457 
    458     SVGMarkerData markerData(m_markerPositions);
    459     m_path->apply(&markerData, SVGMarkerData::updateFromPathElement);
    460     markerData.pathIsDone();
    461 }
    462 
    463 }
    464