Home | History | Annotate | Download | only in rendering
      1 /*
      2     Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann (at) kde.org>
      3                   2004, 2005, 2008 Rob Buis <buis (at) kde.org>
      4                   2005, 2007 Eric Seidel <eric (at) webkit.org>
      5                   2009 Google, Inc.
      6                   2009 Dirk Schulze <krit (at) webkit.org>
      7 
      8     This library is free software; you can redistribute it and/or
      9     modify it under the terms of the GNU Library General Public
     10     License as published by the Free Software Foundation; either
     11     version 2 of the License, or (at your option) any later version.
     12 
     13     This library is distributed in the hope that it will be useful,
     14     but WITHOUT ANY WARRANTY; without even the implied warranty of
     15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     16     Library General Public License for more details.
     17 
     18     You should have received a copy of the GNU Library General Public License
     19     aint with this library; see the file COPYING.LIB.  If not, write to
     20     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     21     Boston, MA 02110-1301, USA.
     22 */
     23 
     24 #include "config.h"
     25 
     26 #if ENABLE(SVG)
     27 #include "RenderPath.h"
     28 
     29 #include "FloatPoint.h"
     30 #include "FloatQuad.h"
     31 #include "GraphicsContext.h"
     32 #include "PointerEventsHitRules.h"
     33 #include "RenderSVGContainer.h"
     34 #include "StrokeStyleApplier.h"
     35 #include "SVGPaintServer.h"
     36 #include "SVGRenderSupport.h"
     37 #include "SVGResourceFilter.h"
     38 #include "SVGResourceMarker.h"
     39 #include "SVGResourceMasker.h"
     40 #include "SVGStyledTransformableElement.h"
     41 #include "SVGTransformList.h"
     42 #include "SVGURIReference.h"
     43 #include <wtf/MathExtras.h>
     44 
     45 namespace WebCore {
     46 
     47 class BoundingRectStrokeStyleApplier : public StrokeStyleApplier {
     48 public:
     49     BoundingRectStrokeStyleApplier(const RenderObject* object, RenderStyle* style)
     50         : m_object(object)
     51         , m_style(style)
     52     {
     53         ASSERT(style);
     54         ASSERT(object);
     55     }
     56 
     57     void strokeStyle(GraphicsContext* gc)
     58     {
     59         applyStrokeStyleToContext(gc, m_style, m_object);
     60     }
     61 
     62 private:
     63     const RenderObject* m_object;
     64     RenderStyle* m_style;
     65 };
     66 
     67 RenderPath::RenderPath(SVGStyledTransformableElement* node)
     68     : RenderSVGModelObject(node)
     69 {
     70 }
     71 
     72 const AffineTransform& RenderPath::localToParentTransform() const
     73 {
     74     return m_localTransform;
     75 }
     76 
     77 AffineTransform RenderPath::localTransform() const
     78 {
     79     return m_localTransform;
     80 }
     81 
     82 bool RenderPath::fillContains(const FloatPoint& point, bool requiresFill) const
     83 {
     84     if (m_path.isEmpty())
     85         return false;
     86 
     87     if (requiresFill && !SVGPaintServer::fillPaintServer(style(), this))
     88         return false;
     89 
     90     return m_path.contains(point, style()->svgStyle()->fillRule());
     91 }
     92 
     93 bool RenderPath::strokeContains(const FloatPoint& point, bool requiresStroke) const
     94 {
     95     if (m_path.isEmpty())
     96         return false;
     97 
     98     if (requiresStroke && !SVGPaintServer::strokePaintServer(style(), this))
     99         return false;
    100 
    101     BoundingRectStrokeStyleApplier strokeStyle(this, style());
    102     return m_path.strokeContains(&strokeStyle, point);
    103 }
    104 
    105 FloatRect RenderPath::objectBoundingBox() const
    106 {
    107     if (m_path.isEmpty())
    108         return FloatRect();
    109 
    110     if (m_cachedLocalFillBBox.isEmpty())
    111         m_cachedLocalFillBBox = m_path.boundingRect();
    112 
    113     return m_cachedLocalFillBBox;
    114 }
    115 
    116 FloatRect RenderPath::strokeBoundingBox() const
    117 {
    118     if (m_path.isEmpty())
    119         return FloatRect();
    120 
    121     if (!m_cachedLocalStrokeBBox.isEmpty())
    122         return m_cachedLocalStrokeBBox;
    123 
    124     m_cachedLocalStrokeBBox = objectBoundingBox();
    125     if (style()->svgStyle()->hasStroke()) {
    126         BoundingRectStrokeStyleApplier strokeStyle(this, style());
    127         m_cachedLocalStrokeBBox.unite(m_path.strokeBoundingRect(&strokeStyle));
    128     }
    129 
    130     return m_cachedLocalStrokeBBox;
    131 }
    132 
    133 FloatRect RenderPath::markerBoundingBox() const
    134 {
    135     if (m_path.isEmpty())
    136         return FloatRect();
    137 
    138     if (m_cachedLocalMarkerBBox.isEmpty())
    139         calculateMarkerBoundsIfNeeded();
    140 
    141     return m_cachedLocalMarkerBBox;
    142 }
    143 
    144 FloatRect RenderPath::repaintRectInLocalCoordinates() const
    145 {
    146     if (m_path.isEmpty())
    147         return FloatRect();
    148 
    149     // If we already have a cached repaint rect, return that
    150     if (!m_cachedLocalRepaintRect.isEmpty())
    151         return m_cachedLocalRepaintRect;
    152 
    153     // FIXME: We need to be careful here. We assume that there is no filter,
    154     // clipper, marker or masker if the rects are empty.
    155     FloatRect rect = filterBoundingBoxForRenderer(this);
    156     if (!rect.isEmpty())
    157         m_cachedLocalRepaintRect = rect;
    158     else {
    159         m_cachedLocalRepaintRect = strokeBoundingBox();
    160         m_cachedLocalRepaintRect.unite(markerBoundingBox());
    161     }
    162 
    163     rect = clipperBoundingBoxForRenderer(this);
    164     if (!rect.isEmpty())
    165         m_cachedLocalRepaintRect.intersect(rect);
    166 
    167     rect = maskerBoundingBoxForRenderer(this);
    168     if (!rect.isEmpty())
    169         m_cachedLocalRepaintRect.intersect(rect);
    170 
    171     style()->svgStyle()->inflateForShadow(m_cachedLocalRepaintRect);
    172 
    173     return m_cachedLocalRepaintRect;
    174 }
    175 
    176 void RenderPath::setPath(const Path& newPath)
    177 {
    178     m_path = newPath;
    179     m_cachedLocalRepaintRect = FloatRect();
    180     m_cachedLocalStrokeBBox = FloatRect();
    181     m_cachedLocalFillBBox = FloatRect();
    182     m_cachedLocalMarkerBBox = FloatRect();
    183 }
    184 
    185 void RenderPath::layout()
    186 {
    187     LayoutRepainter repainter(*this, checkForRepaintDuringLayout() && selfNeedsLayout());
    188 
    189     SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node());
    190     m_localTransform = element->animatedLocalTransform();
    191     setPath(element->toPathData());
    192 
    193     repainter.repaintAfterLayout();
    194     setNeedsLayout(false);
    195 }
    196 
    197 static inline void fillAndStrokePath(const Path& path, GraphicsContext* context, RenderStyle* style, RenderPath* object)
    198 {
    199     context->beginPath();
    200 
    201     SVGPaintServer* fillPaintServer = SVGPaintServer::fillPaintServer(style, object);
    202     if (fillPaintServer) {
    203         context->addPath(path);
    204         fillPaintServer->draw(context, object, ApplyToFillTargetType);
    205     }
    206 
    207     SVGPaintServer* strokePaintServer = SVGPaintServer::strokePaintServer(style, object);
    208     if (strokePaintServer) {
    209         context->addPath(path); // path is cleared when filled.
    210         strokePaintServer->draw(context, object, ApplyToStrokeTargetType);
    211     }
    212 }
    213 
    214 void RenderPath::paint(PaintInfo& paintInfo, int, int)
    215 {
    216     if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN || m_path.isEmpty())
    217         return;
    218 
    219     FloatRect boundingBox = repaintRectInLocalCoordinates();
    220     FloatRect nonLocalBoundingBox = m_localTransform.mapRect(boundingBox);
    221     // FIXME: The empty rect check is to deal with incorrect initial clip in renderSubtreeToImage
    222     // unfortunately fixing that problem is fairly complex unless we were willing to just futz the
    223     // rect to something "close enough"
    224     if (!nonLocalBoundingBox.intersects(paintInfo.rect) && !paintInfo.rect.isEmpty())
    225         return;
    226 
    227     PaintInfo childPaintInfo(paintInfo);
    228     childPaintInfo.context->save();
    229     applyTransformToPaintInfo(childPaintInfo, m_localTransform);
    230     SVGResourceFilter* filter = 0;
    231 
    232     if (childPaintInfo.phase == PaintPhaseForeground) {
    233         PaintInfo savedInfo(childPaintInfo);
    234 
    235         if (prepareToRenderSVGContent(this, childPaintInfo, boundingBox, filter)) {
    236             if (style()->svgStyle()->shapeRendering() == SR_CRISPEDGES)
    237                 childPaintInfo.context->setShouldAntialias(false);
    238             fillAndStrokePath(m_path, childPaintInfo.context, style(), this);
    239 
    240             if (static_cast<SVGStyledElement*>(node())->supportsMarkers())
    241                 m_markerLayoutInfo.drawMarkers(childPaintInfo);
    242         }
    243         finishRenderSVGContent(this, childPaintInfo, filter, savedInfo.context);
    244     }
    245 
    246     if ((childPaintInfo.phase == PaintPhaseOutline || childPaintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth())
    247         paintOutline(childPaintInfo.context, static_cast<int>(boundingBox.x()), static_cast<int>(boundingBox.y()),
    248             static_cast<int>(boundingBox.width()), static_cast<int>(boundingBox.height()), style());
    249 
    250     childPaintInfo.context->restore();
    251 }
    252 
    253 // This method is called from inside paintOutline() since we call paintOutline()
    254 // while transformed to our coord system, return local coords
    255 void RenderPath::addFocusRingRects(Vector<IntRect>& rects, int, int)
    256 {
    257     IntRect rect = enclosingIntRect(repaintRectInLocalCoordinates());
    258     if (!rect.isEmpty())
    259         rects.append(rect);
    260 }
    261 
    262 bool RenderPath::nodeAtFloatPoint(const HitTestRequest&, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
    263 {
    264     // We only draw in the forground phase, so we only hit-test then.
    265     if (hitTestAction != HitTestForeground)
    266         return false;
    267 
    268     FloatPoint localPoint = m_localTransform.inverse().mapPoint(pointInParent);
    269 
    270     PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_PATH_HITTESTING, style()->pointerEvents());
    271 
    272     bool isVisible = (style()->visibility() == VISIBLE);
    273     if (isVisible || !hitRules.requireVisible) {
    274         if ((hitRules.canHitStroke && (style()->svgStyle()->hasStroke() || !hitRules.requireStroke) && strokeContains(localPoint, hitRules.requireStroke))
    275             || (hitRules.canHitFill && (style()->svgStyle()->hasFill() || !hitRules.requireFill) && fillContains(localPoint, hitRules.requireFill))) {
    276             updateHitTestResult(result, roundedIntPoint(localPoint));
    277             return true;
    278         }
    279     }
    280 
    281     return false;
    282 }
    283 
    284 void RenderPath::calculateMarkerBoundsIfNeeded() const
    285 {
    286     Document* doc = document();
    287 
    288     SVGElement* svgElement = static_cast<SVGElement*>(node());
    289     ASSERT(svgElement && svgElement->document());
    290     if (!svgElement->isStyled())
    291         return;
    292 
    293     SVGStyledElement* styledElement = static_cast<SVGStyledElement*>(svgElement);
    294     if (!styledElement->supportsMarkers())
    295         return;
    296 
    297     const SVGRenderStyle* svgStyle = style()->svgStyle();
    298     AtomicString startMarkerId(svgStyle->startMarker());
    299     AtomicString midMarkerId(svgStyle->midMarker());
    300     AtomicString endMarkerId(svgStyle->endMarker());
    301 
    302     SVGResourceMarker* startMarker = getMarkerById(doc, startMarkerId, this);
    303     SVGResourceMarker* midMarker = getMarkerById(doc, midMarkerId, this);
    304     SVGResourceMarker* endMarker = getMarkerById(doc, endMarkerId, this);
    305 
    306     if (!startMarker && !startMarkerId.isEmpty())
    307         svgElement->document()->accessSVGExtensions()->addPendingResource(startMarkerId, styledElement);
    308     else if (startMarker)
    309         startMarker->addClient(styledElement);
    310 
    311     if (!midMarker && !midMarkerId.isEmpty())
    312         svgElement->document()->accessSVGExtensions()->addPendingResource(midMarkerId, styledElement);
    313     else if (midMarker)
    314         midMarker->addClient(styledElement);
    315 
    316     if (!endMarker && !endMarkerId.isEmpty())
    317         svgElement->document()->accessSVGExtensions()->addPendingResource(endMarkerId, styledElement);
    318     else if (endMarker)
    319         endMarker->addClient(styledElement);
    320 
    321     if (!startMarker && !midMarker && !endMarker)
    322         return;
    323 
    324     float strokeWidth = SVGRenderStyle::cssPrimitiveToLength(this, svgStyle->strokeWidth(), 1.0f);
    325     m_cachedLocalMarkerBBox = m_markerLayoutInfo.calculateBoundaries(startMarker, midMarker, endMarker, strokeWidth, m_path);
    326 }
    327 
    328 }
    329 
    330 #endif // ENABLE(SVG)
    331