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/RenderSVGPath.h"
     31 
     32 #include "core/platform/graphics/GraphicsContextStateSaver.h"
     33 #include "core/rendering/svg/SVGSubpathData.h"
     34 
     35 namespace WebCore {
     36 
     37 RenderSVGPath::RenderSVGPath(SVGGraphicsElement* node)
     38     : RenderSVGShape(node)
     39 {
     40 }
     41 
     42 RenderSVGPath::~RenderSVGPath()
     43 {
     44 }
     45 
     46 void RenderSVGPath::updateShapeFromElement()
     47 {
     48     RenderSVGShape::updateShapeFromElement();
     49     updateZeroLengthSubpaths();
     50 
     51     m_strokeBoundingBox = calculateUpdatedStrokeBoundingBox();
     52 }
     53 
     54 FloatRect RenderSVGPath::calculateUpdatedStrokeBoundingBox() const
     55 {
     56     FloatRect strokeBoundingBox = m_strokeBoundingBox;
     57 
     58     if (style()->svgStyle()->hasStroke()) {
     59         // FIXME: zero-length subpaths do not respect vector-effect = non-scaling-stroke.
     60         float strokeWidth = this->strokeWidth();
     61         for (size_t i = 0; i < m_zeroLengthLinecapLocations.size(); ++i)
     62             strokeBoundingBox.unite(zeroLengthSubpathRect(m_zeroLengthLinecapLocations[i], strokeWidth));
     63     }
     64 
     65     return strokeBoundingBox;
     66 }
     67 
     68 static void useStrokeStyleToFill(GraphicsContext* context)
     69 {
     70     if (Gradient* gradient = context->strokeGradient())
     71         context->setFillGradient(gradient);
     72     else if (Pattern* pattern = context->strokePattern())
     73         context->setFillPattern(pattern);
     74     else
     75         context->setFillColor(context->strokeColor());
     76 }
     77 
     78 void RenderSVGPath::strokeShape(GraphicsContext* context) const
     79 {
     80     if (!style()->svgStyle()->hasVisibleStroke())
     81         return;
     82 
     83     RenderSVGShape::strokeShape(context);
     84 
     85     if (m_zeroLengthLinecapLocations.isEmpty())
     86         return;
     87 
     88     Path* usePath;
     89     AffineTransform nonScalingTransform;
     90 
     91     if (hasNonScalingStroke())
     92         nonScalingTransform = nonScalingStrokeTransform();
     93 
     94     GraphicsContextStateSaver stateSaver(*context, true);
     95     useStrokeStyleToFill(context);
     96     for (size_t i = 0; i < m_zeroLengthLinecapLocations.size(); ++i) {
     97         usePath = zeroLengthLinecapPath(m_zeroLengthLinecapLocations[i]);
     98         if (hasNonScalingStroke())
     99             usePath = nonScalingStrokePath(usePath, nonScalingTransform);
    100         context->fillPath(*usePath);
    101     }
    102 }
    103 
    104 bool RenderSVGPath::shapeDependentStrokeContains(const FloatPoint& point)
    105 {
    106     if (RenderSVGShape::shapeDependentStrokeContains(point))
    107         return true;
    108 
    109     const SVGRenderStyle* svgStyle = style()->svgStyle();
    110     for (size_t i = 0; i < m_zeroLengthLinecapLocations.size(); ++i) {
    111         ASSERT(svgStyle->hasStroke());
    112         float strokeWidth = this->strokeWidth();
    113         if (svgStyle->capStyle() == SquareCap) {
    114             if (zeroLengthSubpathRect(m_zeroLengthLinecapLocations[i], strokeWidth).contains(point))
    115                 return true;
    116         } else {
    117             ASSERT(svgStyle->capStyle() == RoundCap);
    118             FloatPoint radiusVector(point.x() - m_zeroLengthLinecapLocations[i].x(), point.y() -  m_zeroLengthLinecapLocations[i].y());
    119             if (radiusVector.lengthSquared() < strokeWidth * strokeWidth * .25f)
    120                 return true;
    121         }
    122     }
    123     return false;
    124 }
    125 
    126 bool RenderSVGPath::shouldStrokeZeroLengthSubpath() const
    127 {
    128     // Spec(11.4): Any zero length subpath shall not be stroked if the "stroke-linecap" property has a value of butt
    129     // but shall be stroked if the "stroke-linecap" property has a value of round or square
    130     return style()->svgStyle()->hasStroke() && style()->svgStyle()->capStyle() != ButtCap;
    131 }
    132 
    133 Path* RenderSVGPath::zeroLengthLinecapPath(const FloatPoint& linecapPosition) const
    134 {
    135     DEFINE_STATIC_LOCAL(Path, tempPath, ());
    136 
    137     tempPath.clear();
    138     if (style()->svgStyle()->capStyle() == SquareCap)
    139         tempPath.addRect(zeroLengthSubpathRect(linecapPosition, this->strokeWidth()));
    140     else
    141         tempPath.addEllipse(zeroLengthSubpathRect(linecapPosition, this->strokeWidth()));
    142 
    143     return &tempPath;
    144 }
    145 
    146 FloatRect RenderSVGPath::zeroLengthSubpathRect(const FloatPoint& linecapPosition, float strokeWidth) const
    147 {
    148     return FloatRect(linecapPosition.x() - strokeWidth / 2, linecapPosition.y() - strokeWidth / 2, strokeWidth, strokeWidth);
    149 }
    150 
    151 void RenderSVGPath::updateZeroLengthSubpaths()
    152 {
    153     m_zeroLengthLinecapLocations.clear();
    154 
    155     if (!strokeWidth() || !shouldStrokeZeroLengthSubpath())
    156         return;
    157 
    158     SVGSubpathData subpathData(m_zeroLengthLinecapLocations);
    159     path().apply(&subpathData, SVGSubpathData::updateFromPathElement);
    160     subpathData.pathIsDone();
    161 }
    162 
    163 }
    164