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/rendering/svg/SVGSubpathData.h" 33 #include "platform/graphics/GraphicsContextStateSaver.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