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 30#include "core/rendering/svg/RenderSVGPath.h" 31 32#include "core/rendering/svg/SVGSubpathData.h" 33#include "platform/graphics/GraphicsContextStateSaver.h" 34 35namespace WebCore { 36 37RenderSVGPath::RenderSVGPath(SVGGraphicsElement* node) 38 : RenderSVGShape(node) 39{ 40} 41 42RenderSVGPath::~RenderSVGPath() 43{ 44} 45 46void RenderSVGPath::updateShapeFromElement() 47{ 48 RenderSVGShape::updateShapeFromElement(); 49 updateZeroLengthSubpaths(); 50 51 m_strokeBoundingBox = calculateUpdatedStrokeBoundingBox(); 52} 53 54FloatRect 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 68static 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 78void 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 104bool 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 126bool 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 133Path* 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 146FloatRect RenderSVGPath::zeroLengthSubpathRect(const FloatPoint& linecapPosition, float strokeWidth) const 147{ 148 return FloatRect(linecapPosition.x() - strokeWidth / 2, linecapPosition.y() - strokeWidth / 2, strokeWidth, strokeWidth); 149} 150 151void 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