1/* 2 * Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org> 3 * Copyright (C) 2004, 2005, 2008 Rob Buis <buis@kde.org> 4 * Copyright (C) 2005, 2007 Eric Seidel <eric@webkit.org> 5 * Copyright (C) 2009 Google, Inc. 6 * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> 7 * Copyright (C) Research In Motion Limited 2010. All rights reserved. 8 * Copyright (C) 2009 Jeff Schiller <codedread@gmail.com> 9 * Copyright (C) 2011 Renata Hodovan <reni@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#include "RenderSVGPath.h" 30 31#include "SVGPathElement.h" 32#include "SVGSubpathData.h" 33 34namespace WebCore { 35 36RenderSVGPath::RenderSVGPath(SVGGraphicsElement& element, PassRef<RenderStyle> style) 37 : RenderSVGShape(element, WTF::move(style)) 38{ 39} 40 41RenderSVGPath::~RenderSVGPath() 42{ 43} 44 45void RenderSVGPath::updateShapeFromElement() 46{ 47 RenderSVGShape::updateShapeFromElement(); 48 updateZeroLengthSubpaths(); 49 50 m_strokeBoundingBox = calculateUpdatedStrokeBoundingBox(); 51} 52 53FloatRect RenderSVGPath::calculateUpdatedStrokeBoundingBox() const 54{ 55 FloatRect strokeBoundingBox = m_strokeBoundingBox; 56 57 if (style().svgStyle().hasStroke()) { 58 // FIXME: zero-length subpaths do not respect vector-effect = non-scaling-stroke. 59 float strokeWidth = this->strokeWidth(); 60 for (size_t i = 0; i < m_zeroLengthLinecapLocations.size(); ++i) 61 strokeBoundingBox.unite(zeroLengthSubpathRect(m_zeroLengthLinecapLocations[i], strokeWidth)); 62 } 63 64 return strokeBoundingBox; 65} 66 67static void useStrokeStyleToFill(GraphicsContext* context) 68{ 69 if (Gradient* gradient = context->strokeGradient()) 70 context->setFillGradient(gradient); 71 else if (Pattern* pattern = context->strokePattern()) 72 context->setFillPattern(pattern); 73 else 74 context->setFillColor(context->strokeColor(), context->strokeColorSpace()); 75} 76 77void RenderSVGPath::strokeShape(GraphicsContext* context) const 78{ 79 if (!style().svgStyle().hasVisibleStroke()) 80 return; 81 82 RenderSVGShape::strokeShape(context); 83 84 if (m_zeroLengthLinecapLocations.isEmpty()) 85 return; 86 87 Path* usePath; 88 AffineTransform nonScalingTransform; 89 90 if (hasNonScalingStroke()) 91 nonScalingTransform = nonScalingStrokeTransform(); 92 93 GraphicsContextStateSaver stateSaver(*context, true); 94 useStrokeStyleToFill(context); 95 for (size_t i = 0; i < m_zeroLengthLinecapLocations.size(); ++i) { 96 usePath = zeroLengthLinecapPath(m_zeroLengthLinecapLocations[i]); 97 if (hasNonScalingStroke()) 98 usePath = nonScalingStrokePath(usePath, nonScalingTransform); 99 context->fillPath(*usePath); 100 } 101} 102 103bool RenderSVGPath::shapeDependentStrokeContains(const FloatPoint& point) 104{ 105 if (RenderSVGShape::shapeDependentStrokeContains(point)) 106 return true; 107 108 const SVGRenderStyle& svgStyle = style().svgStyle(); 109 for (size_t i = 0; i < m_zeroLengthLinecapLocations.size(); ++i) { 110 ASSERT(svgStyle.hasStroke()); 111 float strokeWidth = this->strokeWidth(); 112 if (svgStyle.capStyle() == SquareCap) { 113 if (zeroLengthSubpathRect(m_zeroLengthLinecapLocations[i], strokeWidth).contains(point)) 114 return true; 115 } else { 116 ASSERT(svgStyle.capStyle() == RoundCap); 117 FloatPoint radiusVector(point.x() - m_zeroLengthLinecapLocations[i].x(), point.y() - m_zeroLengthLinecapLocations[i].y()); 118 if (radiusVector.lengthSquared() < strokeWidth * strokeWidth * .25f) 119 return true; 120 } 121 } 122 return false; 123} 124 125bool RenderSVGPath::shouldStrokeZeroLengthSubpath() const 126{ 127 // Spec(11.4): Any zero length subpath shall not be stroked if the "stroke-linecap" property has a value of butt 128 // but shall be stroked if the "stroke-linecap" property has a value of round or square 129 return style().svgStyle().hasStroke() && style().svgStyle().capStyle() != ButtCap; 130} 131 132Path* RenderSVGPath::zeroLengthLinecapPath(const FloatPoint& linecapPosition) const 133{ 134 DEPRECATED_DEFINE_STATIC_LOCAL(Path, tempPath, ()); 135 136 tempPath.clear(); 137 if (style().svgStyle().capStyle() == SquareCap) 138 tempPath.addRect(zeroLengthSubpathRect(linecapPosition, this->strokeWidth())); 139 else 140 tempPath.addEllipse(zeroLengthSubpathRect(linecapPosition, this->strokeWidth())); 141 142 return &tempPath; 143} 144 145FloatRect RenderSVGPath::zeroLengthSubpathRect(const FloatPoint& linecapPosition, float strokeWidth) const 146{ 147 return FloatRect(linecapPosition.x() - strokeWidth / 2, linecapPosition.y() - strokeWidth / 2, strokeWidth, strokeWidth); 148} 149 150void RenderSVGPath::updateZeroLengthSubpaths() 151{ 152 m_zeroLengthLinecapLocations.clear(); 153 154 if (!strokeWidth() || !shouldStrokeZeroLengthSubpath()) 155 return; 156 157 SVGSubpathData subpathData(m_zeroLengthLinecapLocations); 158 path().apply(&subpathData, SVGSubpathData::updateFromPathElement); 159 subpathData.pathIsDone(); 160} 161 162bool RenderSVGPath::isRenderingDisabled() const 163{ 164 // For a polygon, polyline or path, rendering is disabled if there is no path data. 165 // No path data is possible in the case of a missing or empty 'd' or 'points' attribute. 166 return path().isEmpty(); 167} 168 169} 170