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/rendering/GraphicsContextAnnotator.h"
     33 #include "core/rendering/HitTestRequest.h"
     34 #include "core/rendering/LayoutRectRecorder.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 "platform/geometry/FloatPoint.h"
     45 #include "platform/graphics/GraphicsContextStateSaver.h"
     46 #include "wtf/MathExtras.h"
     47 
     48 namespace WebCore {
     49 
     50 RenderSVGShape::RenderSVGShape(SVGGraphicsElement* node)
     51     : RenderSVGModelObject(node)
     52     , m_needsBoundariesUpdate(false) // Default is false, the cached rects are empty from the beginning.
     53     , m_needsShapeUpdate(true) // Default is true, so we grab a Path object once from SVGGraphicsElement.
     54     , m_needsTransformUpdate(true) // Default is true, so we grab a AffineTransform object once from SVGGraphicsElement.
     55 {
     56 }
     57 
     58 RenderSVGShape::~RenderSVGShape()
     59 {
     60 }
     61 
     62 void RenderSVGShape::updateShapeFromElement()
     63 {
     64     m_path.clear();
     65     m_path = adoptPtr(new Path);
     66     ASSERT(RenderSVGShape::isEmpty());
     67 
     68     updatePathFromGraphicsElement(toSVGGraphicsElement(element()), path());
     69     processMarkerPositions();
     70 
     71     m_fillBoundingBox = calculateObjectBoundingBox();
     72     m_strokeBoundingBox = calculateStrokeBoundingBox();
     73 }
     74 
     75 bool RenderSVGShape::isEmpty() const
     76 {
     77     return path().isEmpty();
     78 }
     79 
     80 void RenderSVGShape::fillShape(GraphicsContext* context) const
     81 {
     82     context->fillPath(path());
     83 }
     84 
     85 void RenderSVGShape::strokeShape(GraphicsContext* context) const
     86 {
     87     ASSERT(m_path);
     88     Path* usePath = m_path.get();
     89 
     90     if (hasNonScalingStroke())
     91         usePath = nonScalingStrokePath(usePath, nonScalingStrokeTransform());
     92 
     93     context->strokePath(*usePath);
     94 }
     95 
     96 bool RenderSVGShape::shapeDependentStrokeContains(const FloatPoint& point)
     97 {
     98     ASSERT(m_path);
     99     StrokeData strokeData;
    100     SVGRenderSupport::applyStrokeStyleToStrokeData(&strokeData, style(), this);
    101 
    102     if (hasNonScalingStroke()) {
    103         AffineTransform nonScalingTransform = nonScalingStrokeTransform();
    104         Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform);
    105 
    106         return usePath->strokeContains(nonScalingTransform.mapPoint(point), strokeData);
    107     }
    108 
    109     return m_path->strokeContains(point, strokeData);
    110 }
    111 
    112 bool RenderSVGShape::shapeDependentFillContains(const FloatPoint& point, const WindRule fillRule) const
    113 {
    114     return path().contains(point, fillRule);
    115 }
    116 
    117 bool RenderSVGShape::fillContains(const FloatPoint& point, bool requiresFill, const WindRule fillRule)
    118 {
    119     if (!m_fillBoundingBox.contains(point))
    120         return false;
    121 
    122     Color fallbackColor;
    123     if (requiresFill && !RenderSVGResource::fillPaintingResource(this, style(), fallbackColor))
    124         return false;
    125 
    126     return shapeDependentFillContains(point, fillRule);
    127 }
    128 
    129 bool RenderSVGShape::strokeContains(const FloatPoint& point, bool requiresStroke)
    130 {
    131     if (!strokeBoundingBox().contains(point))
    132         return false;
    133 
    134     Color fallbackColor;
    135     if (requiresStroke && !RenderSVGResource::strokePaintingResource(this, style(), fallbackColor))
    136         return false;
    137 
    138     return shapeDependentStrokeContains(point);
    139 }
    140 
    141 void RenderSVGShape::layout()
    142 {
    143     LayoutRectRecorder recorder(*this);
    144     LayoutRepainter repainter(*this, SVGRenderSupport::checkForSVGRepaintDuringLayout(this) && selfNeedsLayout());
    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 =  toSVGGraphicsElement(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     return toSVGGraphicsElement(element())->getScreenCTM(SVGGraphicsElement::DisallowStyleUpdate);
    197 }
    198 
    199 bool RenderSVGShape::shouldGenerateMarkerPositions() const
    200 {
    201     if (!style()->svgStyle()->hasMarkers())
    202         return false;
    203 
    204     if (!toSVGGraphicsElement(element())->supportsMarkers())
    205         return false;
    206 
    207     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
    208     if (!resources)
    209         return false;
    210 
    211     return resources->markerStart() || resources->markerMid() || resources->markerEnd();
    212 }
    213 
    214 void RenderSVGShape::fillShape(RenderStyle* style, GraphicsContext* context)
    215 {
    216     Color fallbackColor;
    217     if (RenderSVGResource* fillPaintingResource = RenderSVGResource::fillPaintingResource(this, style, fallbackColor)) {
    218         if (fillPaintingResource->applyResource(this, style, context, ApplyToFillMode))
    219             fillPaintingResource->postApplyResource(this, context, ApplyToFillMode, 0, this);
    220         else if (fallbackColor.isValid()) {
    221             RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
    222             fallbackResource->setColor(fallbackColor);
    223             if (fallbackResource->applyResource(this, style, context, ApplyToFillMode))
    224                 fallbackResource->postApplyResource(this, context, ApplyToFillMode, 0, this);
    225         }
    226     }
    227 }
    228 
    229 void RenderSVGShape::strokeShape(RenderStyle* style, GraphicsContext* context)
    230 {
    231     Color fallbackColor;
    232     if (RenderSVGResource* strokePaintingResource = RenderSVGResource::strokePaintingResource(this, style, fallbackColor)) {
    233         if (strokePaintingResource->applyResource(this, style, context, ApplyToStrokeMode))
    234             strokePaintingResource->postApplyResource(this, context, ApplyToStrokeMode, 0, this);
    235         else if (fallbackColor.isValid()) {
    236             RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
    237             fallbackResource->setColor(fallbackColor);
    238             if (fallbackResource->applyResource(this, style, context, ApplyToStrokeMode))
    239                 fallbackResource->postApplyResource(this, context, ApplyToStrokeMode, 0, this);
    240         }
    241     }
    242 }
    243 
    244 void RenderSVGShape::paint(PaintInfo& paintInfo, const LayoutPoint&)
    245 {
    246     ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
    247 
    248     if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN || isEmpty())
    249         return;
    250     FloatRect boundingBox = repaintRectInLocalCoordinates();
    251     if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(boundingBox, m_localTransform, paintInfo))
    252         return;
    253 
    254     PaintInfo childPaintInfo(paintInfo);
    255     bool drawsOutline = style()->outlineWidth() && (childPaintInfo.phase == PaintPhaseOutline || childPaintInfo.phase == PaintPhaseSelfOutline);
    256     if (drawsOutline || childPaintInfo.phase == PaintPhaseForeground) {
    257         GraphicsContextStateSaver stateSaver(*childPaintInfo.context);
    258         childPaintInfo.applyTransform(m_localTransform);
    259 
    260         if (childPaintInfo.phase == PaintPhaseForeground) {
    261             SVGRenderingContext renderingContext(this, childPaintInfo);
    262 
    263             if (renderingContext.isRenderingPrepared()) {
    264                 const SVGRenderStyle* svgStyle = style()->svgStyle();
    265                 if (svgStyle->shapeRendering() == SR_CRISPEDGES)
    266                     childPaintInfo.context->setShouldAntialias(false);
    267 
    268                 for (int i = 0; i < 3; i++) {
    269                     switch (svgStyle->paintOrderType(i)) {
    270                     case PT_FILL:
    271                         fillShape(this->style(), childPaintInfo.context);
    272                         break;
    273                     case PT_STROKE:
    274                         if (svgStyle->hasVisibleStroke()) {
    275                             GraphicsContextStateSaver stateSaver(*childPaintInfo.context, false);
    276                             AffineTransform nonScalingTransform;
    277 
    278                             if (hasNonScalingStroke()) {
    279                                 AffineTransform nonScalingTransform = nonScalingStrokeTransform();
    280                                 if (!setupNonScalingStrokeContext(nonScalingTransform, stateSaver))
    281                                     return;
    282                             }
    283 
    284                             strokeShape(this->style(), childPaintInfo.context);
    285                         }
    286                         break;
    287                     case PT_MARKERS:
    288                         if (!m_markerPositions.isEmpty())
    289                             drawMarkers(childPaintInfo);
    290                         break;
    291                     default:
    292                         ASSERT_NOT_REACHED();
    293                         break;
    294                     }
    295                 }
    296             }
    297         }
    298 
    299         if (drawsOutline)
    300             paintOutline(childPaintInfo, IntRect(boundingBox));
    301     }
    302 }
    303 
    304 // This method is called from inside paintOutline() since we call paintOutline()
    305 // while transformed to our coord system, return local coords
    306 void RenderSVGShape::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint&, const RenderLayerModelObject*)
    307 {
    308     IntRect rect = enclosingIntRect(repaintRectInLocalCoordinates());
    309     if (!rect.isEmpty())
    310         rects.append(rect);
    311 }
    312 
    313 bool RenderSVGShape::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
    314 {
    315     // We only draw in the foreground phase, so we only hit-test then.
    316     if (hitTestAction != HitTestForeground)
    317         return false;
    318 
    319     FloatPoint localPoint = m_localTransform.inverse().mapPoint(pointInParent);
    320 
    321     if (!SVGRenderSupport::pointInClippingArea(this, localPoint))
    322         return false;
    323 
    324     PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_GEOMETRY_HITTESTING, request, style()->pointerEvents());
    325     if (nodeAtFloatPointInternal(request, localPoint, hitRules)) {
    326         updateHitTestResult(result, roundedLayoutPoint(localPoint));
    327         return true;
    328     }
    329 
    330     return false;
    331 }
    332 
    333 bool RenderSVGShape::nodeAtFloatPointInternal(const HitTestRequest& request, const FloatPoint& localPoint, PointerEventsHitRules hitRules)
    334 {
    335     bool isVisible = (style()->visibility() == VISIBLE);
    336     if (isVisible || !hitRules.requireVisible) {
    337         const SVGRenderStyle* svgStyle = style()->svgStyle();
    338         WindRule fillRule = svgStyle->fillRule();
    339         if (request.svgClipContent())
    340             fillRule = svgStyle->clipRule();
    341         if ((hitRules.canHitBoundingBox && objectBoundingBox().contains(localPoint))
    342             || (hitRules.canHitStroke && (svgStyle->hasStroke() || !hitRules.requireStroke) && strokeContains(localPoint, hitRules.requireStroke))
    343             || (hitRules.canHitFill && (svgStyle->hasFill() || !hitRules.requireFill) && fillContains(localPoint, hitRules.requireFill, fillRule)))
    344             return true;
    345     }
    346     return false;
    347 }
    348 
    349 static inline RenderSVGResourceMarker* markerForType(SVGMarkerType type, RenderSVGResourceMarker* markerStart, RenderSVGResourceMarker* markerMid, RenderSVGResourceMarker* markerEnd)
    350 {
    351     switch (type) {
    352     case StartMarker:
    353         return markerStart;
    354     case MidMarker:
    355         return markerMid;
    356     case EndMarker:
    357         return markerEnd;
    358     }
    359 
    360     ASSERT_NOT_REACHED();
    361     return 0;
    362 }
    363 
    364 FloatRect RenderSVGShape::markerRect(float strokeWidth) const
    365 {
    366     ASSERT(!m_markerPositions.isEmpty());
    367 
    368     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
    369     ASSERT(resources);
    370 
    371     RenderSVGResourceMarker* markerStart = resources->markerStart();
    372     RenderSVGResourceMarker* markerMid = resources->markerMid();
    373     RenderSVGResourceMarker* markerEnd = resources->markerEnd();
    374     ASSERT(markerStart || markerMid || markerEnd);
    375 
    376     FloatRect boundaries;
    377     unsigned size = m_markerPositions.size();
    378     for (unsigned i = 0; i < size; ++i) {
    379         if (RenderSVGResourceMarker* marker = markerForType(m_markerPositions[i].type, markerStart, markerMid, markerEnd))
    380             boundaries.unite(marker->markerBoundaries(marker->markerTransformation(m_markerPositions[i].origin, m_markerPositions[i].angle, strokeWidth)));
    381     }
    382     return boundaries;
    383 }
    384 
    385 FloatRect RenderSVGShape::calculateObjectBoundingBox() const
    386 {
    387     return path().boundingRect();
    388 }
    389 
    390 FloatRect RenderSVGShape::calculateStrokeBoundingBox() const
    391 {
    392     ASSERT(m_path);
    393     FloatRect strokeBoundingBox = m_fillBoundingBox;
    394 
    395     if (style()->svgStyle()->hasStroke()) {
    396         StrokeData strokeData;
    397         SVGRenderSupport::applyStrokeStyleToStrokeData(&strokeData, style(), this);
    398         if (hasNonScalingStroke()) {
    399             AffineTransform nonScalingTransform = nonScalingStrokeTransform();
    400             if (nonScalingTransform.isInvertible()) {
    401                 Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform);
    402                 FloatRect strokeBoundingRect = usePath->strokeBoundingRect(strokeData);
    403                 strokeBoundingRect = nonScalingTransform.inverse().mapRect(strokeBoundingRect);
    404                 strokeBoundingBox.unite(strokeBoundingRect);
    405             }
    406         } else {
    407             strokeBoundingBox.unite(path().strokeBoundingRect(strokeData));
    408         }
    409     }
    410 
    411     if (!m_markerPositions.isEmpty())
    412         strokeBoundingBox.unite(markerRect(strokeWidth()));
    413 
    414     return strokeBoundingBox;
    415 }
    416 
    417 void RenderSVGShape::updateRepaintBoundingBox()
    418 {
    419     m_repaintBoundingBox = strokeBoundingBox();
    420     if (strokeWidth() < 1.0f)
    421         m_repaintBoundingBox.inflate(1);
    422     SVGRenderSupport::intersectRepaintRectWithResources(this, m_repaintBoundingBox);
    423 }
    424 
    425 float RenderSVGShape::strokeWidth() const
    426 {
    427     SVGLengthContext lengthContext(element());
    428     return style()->svgStyle()->strokeWidth().value(lengthContext);
    429 }
    430 
    431 bool RenderSVGShape::hasSmoothStroke() const
    432 {
    433     const SVGRenderStyle* svgStyle = style()->svgStyle();
    434     return svgStyle->strokeDashArray().isEmpty()
    435         && svgStyle->strokeMiterLimit() == svgStyle->initialStrokeMiterLimit()
    436         && svgStyle->joinStyle() == svgStyle->initialJoinStyle()
    437         && svgStyle->capStyle() == svgStyle->initialCapStyle();
    438 }
    439 
    440 void RenderSVGShape::drawMarkers(PaintInfo& paintInfo)
    441 {
    442     ASSERT(!m_markerPositions.isEmpty());
    443 
    444     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
    445     if (!resources)
    446         return;
    447 
    448     RenderSVGResourceMarker* markerStart = resources->markerStart();
    449     RenderSVGResourceMarker* markerMid = resources->markerMid();
    450     RenderSVGResourceMarker* markerEnd = resources->markerEnd();
    451     if (!markerStart && !markerMid && !markerEnd)
    452         return;
    453 
    454     float strokeWidth = this->strokeWidth();
    455     unsigned size = m_markerPositions.size();
    456     for (unsigned i = 0; i < size; ++i) {
    457         if (RenderSVGResourceMarker* marker = markerForType(m_markerPositions[i].type, markerStart, markerMid, markerEnd))
    458             marker->draw(paintInfo, marker->markerTransformation(m_markerPositions[i].origin, m_markerPositions[i].angle, strokeWidth));
    459     }
    460 }
    461 
    462 void RenderSVGShape::processMarkerPositions()
    463 {
    464     m_markerPositions.clear();
    465 
    466     if (!shouldGenerateMarkerPositions())
    467         return;
    468 
    469     ASSERT(m_path);
    470 
    471     SVGMarkerData markerData(m_markerPositions);
    472     m_path->apply(&markerData, SVGMarkerData::updateFromPathElement);
    473     markerData.pathIsDone();
    474 }
    475 
    476 }
    477