1 /* 2 Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann (at) kde.org> 3 2004, 2005 Rob Buis <buis (at) kde.org> 4 Copyright (C) 2007 Eric Seidel <eric (at) webkit.org> 5 6 This file is part of the WebKit project 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 along 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 "SVGPathSegList.h" 28 29 #include "FloatPoint.h" 30 #include "Path.h" 31 #include "PathTraversalState.h" 32 #include "SVGPathSegArc.h" 33 #include "SVGPathSegClosePath.h" 34 #include "SVGPathSegMoveto.h" 35 #include "SVGPathSegLineto.h" 36 #include "SVGPathSegLinetoHorizontal.h" 37 #include "SVGPathSegLinetoVertical.h" 38 #include "SVGPathSegCurvetoCubic.h" 39 #include "SVGPathSegCurvetoCubicSmooth.h" 40 #include "SVGPathSegCurvetoQuadratic.h" 41 #include "SVGPathSegCurvetoQuadraticSmooth.h" 42 43 namespace WebCore { 44 45 SVGPathSegList::SVGPathSegList(const QualifiedName& attributeName) 46 : SVGList<RefPtr<SVGPathSeg> >(attributeName) 47 { 48 } 49 50 SVGPathSegList::~SVGPathSegList() 51 { 52 } 53 54 unsigned SVGPathSegList::getPathSegAtLength(double, ExceptionCode& ec) 55 { 56 // FIXME : to be useful this will need to support non-normalized SVGPathSegLists 57 int len = numberOfItems(); 58 // FIXME: Eventually this will likely move to a "path applier"-like model, until then PathTraversalState is less useful as we could just use locals 59 PathTraversalState traversalState(PathTraversalState::TraversalSegmentAtLength); 60 for (int i = 0; i < len; ++i) { 61 SVGPathSeg* segment = getItem(i, ec).get(); 62 if (ec) 63 return 0; 64 float segmentLength = 0; 65 switch (segment->pathSegType()) { 66 case SVGPathSeg::PATHSEG_MOVETO_ABS: 67 { 68 SVGPathSegMovetoAbs* moveTo = static_cast<SVGPathSegMovetoAbs*>(segment); 69 segmentLength = traversalState.moveTo(FloatPoint(moveTo->x(), moveTo->y())); 70 break; 71 } 72 case SVGPathSeg::PATHSEG_LINETO_ABS: 73 { 74 SVGPathSegLinetoAbs* lineTo = static_cast<SVGPathSegLinetoAbs*>(segment); 75 segmentLength = traversalState.lineTo(FloatPoint(lineTo->x(), lineTo->y())); 76 break; 77 } 78 case SVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS: 79 { 80 SVGPathSegCurvetoCubicAbs* curveTo = static_cast<SVGPathSegCurvetoCubicAbs*>(segment); 81 segmentLength = traversalState.cubicBezierTo(FloatPoint(curveTo->x1(), curveTo->y1()), 82 FloatPoint(curveTo->x2(), curveTo->y2()), 83 FloatPoint(curveTo->x(), curveTo->y())); 84 break; 85 } 86 case SVGPathSeg::PATHSEG_CLOSEPATH: 87 segmentLength = traversalState.closeSubpath(); 88 break; 89 default: 90 ASSERT(false); // FIXME: This only works with normalized/processed path data. 91 break; 92 } 93 traversalState.m_totalLength += segmentLength; 94 if ((traversalState.m_action == PathTraversalState::TraversalSegmentAtLength) 95 && (traversalState.m_totalLength > traversalState.m_desiredLength)) { 96 return traversalState.m_segmentIndex; 97 } 98 traversalState.m_segmentIndex++; 99 } 100 101 return 0; // The SVG spec is unclear as to what to return when the distance is not on the path 102 } 103 104 Path SVGPathSegList::toPathData() 105 { 106 // FIXME : This should also support non-normalized PathSegLists 107 Path pathData; 108 int len = numberOfItems(); 109 ExceptionCode ec = 0; 110 for (int i = 0; i < len; ++i) { 111 SVGPathSeg* segment = getItem(i, ec).get(); 112 if (ec) 113 return Path(); 114 switch (segment->pathSegType()) { 115 case SVGPathSeg::PATHSEG_MOVETO_ABS: 116 { 117 SVGPathSegMovetoAbs* moveTo = static_cast<SVGPathSegMovetoAbs*>(segment); 118 pathData.moveTo(FloatPoint(moveTo->x(), moveTo->y())); 119 break; 120 } 121 case SVGPathSeg::PATHSEG_LINETO_ABS: 122 { 123 SVGPathSegLinetoAbs* lineTo = static_cast<SVGPathSegLinetoAbs*>(segment); 124 pathData.addLineTo(FloatPoint(lineTo->x(), lineTo->y())); 125 break; 126 } 127 case SVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS: 128 { 129 SVGPathSegCurvetoCubicAbs* curveTo = static_cast<SVGPathSegCurvetoCubicAbs*>(segment); 130 pathData.addBezierCurveTo(FloatPoint(curveTo->x1(), curveTo->y1()), 131 FloatPoint(curveTo->x2(), curveTo->y2()), 132 FloatPoint(curveTo->x(), curveTo->y())); 133 break; 134 } 135 case SVGPathSeg::PATHSEG_CLOSEPATH: 136 pathData.closeSubpath(); 137 break; 138 default: 139 ASSERT(false); // FIXME: This only works with normalized/processed path data. 140 break; 141 } 142 } 143 144 return pathData; 145 } 146 147 static inline float blendFunc(float from, float to, float progress) 148 { 149 return (to - from) * progress + from; 150 } 151 152 #define BLENDPATHSEG1(class, attr1) \ 153 class::create(blendFunc(static_cast<class*>(from)->attr1(), static_cast<class*>(to)->attr1(), progress)) 154 155 #define BLENDPATHSEG2(class, attr1, attr2) \ 156 class::create(blendFunc(static_cast<class*>(from)->attr1(), static_cast<class*>(to)->attr1(), progress), \ 157 blendFunc(static_cast<class*>(from)->attr2(), static_cast<class*>(to)->attr2(), progress)) 158 159 #define BLENDPATHSEG4(class, attr1, attr2, attr3, attr4) \ 160 class::create(blendFunc(static_cast<class*>(from)->attr1(), static_cast<class*>(to)->attr1(), progress), \ 161 blendFunc(static_cast<class*>(from)->attr2(), static_cast<class*>(to)->attr2(), progress), \ 162 blendFunc(static_cast<class*>(from)->attr3(), static_cast<class*>(to)->attr3(), progress), \ 163 blendFunc(static_cast<class*>(from)->attr4(), static_cast<class*>(to)->attr4(), progress)) 164 165 #define BLENDPATHSEG6(class, attr1, attr2, attr3, attr4, attr5, attr6) \ 166 class::create(blendFunc(static_cast<class*>(from)->attr1(), static_cast<class*>(to)->attr1(), progress), \ 167 blendFunc(static_cast<class*>(from)->attr2(), static_cast<class*>(to)->attr2(), progress), \ 168 blendFunc(static_cast<class*>(from)->attr3(), static_cast<class*>(to)->attr3(), progress), \ 169 blendFunc(static_cast<class*>(from)->attr4(), static_cast<class*>(to)->attr4(), progress), \ 170 blendFunc(static_cast<class*>(from)->attr5(), static_cast<class*>(to)->attr5(), progress), \ 171 blendFunc(static_cast<class*>(from)->attr6(), static_cast<class*>(to)->attr6(), progress)) 172 173 #define BLENDPATHSEG7(class, attr1, attr2, attr3, attr4, attr5, bool1, bool2) \ 174 class::create(blendFunc(static_cast<class*>(from)->attr1(), static_cast<class*>(to)->attr1(), progress), \ 175 blendFunc(static_cast<class*>(from)->attr2(), static_cast<class*>(to)->attr2(), progress), \ 176 blendFunc(static_cast<class*>(from)->attr3(), static_cast<class*>(to)->attr3(), progress), \ 177 blendFunc(static_cast<class*>(from)->attr4(), static_cast<class*>(to)->attr4(), progress), \ 178 blendFunc(static_cast<class*>(from)->attr5(), static_cast<class*>(to)->attr5(), progress), \ 179 static_cast<bool>(blendFunc(static_cast<class*>(from)->bool1(), static_cast<class*>(to)->bool1(), progress)), \ 180 static_cast<bool>(blendFunc(static_cast<class*>(from)->bool2(), static_cast<class*>(to)->bool2(), progress))) 181 182 PassRefPtr<SVGPathSegList> SVGPathSegList::createAnimated(const SVGPathSegList* fromList, const SVGPathSegList* toList, float progress) 183 { 184 unsigned itemCount = fromList->numberOfItems(); 185 if (!itemCount || itemCount != toList->numberOfItems()) 186 return 0; 187 RefPtr<SVGPathSegList> result = create(fromList->associatedAttributeName()); 188 ExceptionCode ec = 0; 189 for (unsigned n = 0; n < itemCount; ++n) { 190 SVGPathSeg* from = fromList->getItem(n, ec).get(); 191 if (ec) 192 return 0; 193 SVGPathSeg* to = toList->getItem(n, ec).get(); 194 if (ec) 195 return 0; 196 if (from->pathSegType() == SVGPathSeg::PATHSEG_UNKNOWN || from->pathSegType() != to->pathSegType()) 197 return 0; 198 RefPtr<SVGPathSeg> segment = 0; 199 switch (static_cast<SVGPathSeg::SVGPathSegType>(from->pathSegType())) { 200 case SVGPathSeg::PATHSEG_CLOSEPATH: 201 segment = SVGPathSegClosePath::create(); 202 break; 203 case SVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS: 204 segment = BLENDPATHSEG1(SVGPathSegLinetoHorizontalAbs, x); 205 break; 206 case SVGPathSeg::PATHSEG_LINETO_HORIZONTAL_REL: 207 segment = BLENDPATHSEG1(SVGPathSegLinetoHorizontalRel, x); 208 break; 209 case SVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS: 210 segment = BLENDPATHSEG1(SVGPathSegLinetoVerticalAbs, y); 211 break; 212 case SVGPathSeg::PATHSEG_LINETO_VERTICAL_REL: 213 segment = BLENDPATHSEG1(SVGPathSegLinetoVerticalRel, y); 214 break; 215 case SVGPathSeg::PATHSEG_MOVETO_ABS: 216 segment = BLENDPATHSEG2(SVGPathSegMovetoAbs, x, y); 217 break; 218 case SVGPathSeg::PATHSEG_MOVETO_REL: 219 segment = BLENDPATHSEG2(SVGPathSegMovetoRel, x, y); 220 break; 221 case SVGPathSeg::PATHSEG_LINETO_ABS: 222 segment = BLENDPATHSEG2(SVGPathSegLinetoAbs, x, y); 223 break; 224 case SVGPathSeg::PATHSEG_LINETO_REL: 225 segment = BLENDPATHSEG2(SVGPathSegLinetoRel, x, y); 226 break; 227 case SVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS: 228 segment = BLENDPATHSEG6(SVGPathSegCurvetoCubicAbs, x, y, x1, y1, x2, y2); 229 break; 230 case SVGPathSeg::PATHSEG_CURVETO_CUBIC_REL: 231 segment = BLENDPATHSEG6(SVGPathSegCurvetoCubicRel, x, y, x1, y1, x2, y2); 232 break; 233 case SVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS: 234 segment = BLENDPATHSEG4(SVGPathSegCurvetoCubicSmoothAbs, x, y, x2, y2); 235 break; 236 case SVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL: 237 segment = BLENDPATHSEG4(SVGPathSegCurvetoCubicSmoothRel, x, y, x2, y2); 238 break; 239 case SVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS: 240 segment = BLENDPATHSEG4(SVGPathSegCurvetoQuadraticAbs, x, y, x1, y1); 241 break; 242 case SVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL: 243 segment = BLENDPATHSEG4(SVGPathSegCurvetoQuadraticRel, x, y, x1, y1); 244 break; 245 case SVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS: 246 segment = BLENDPATHSEG2(SVGPathSegCurvetoQuadraticSmoothAbs, x, y); 247 break; 248 case SVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL: 249 segment = BLENDPATHSEG2(SVGPathSegCurvetoQuadraticSmoothRel, x, y); 250 break; 251 case SVGPathSeg::PATHSEG_ARC_ABS: 252 segment = BLENDPATHSEG7(SVGPathSegArcAbs, x, y, r1, r2, angle, largeArcFlag, sweepFlag); 253 break; 254 case SVGPathSeg::PATHSEG_ARC_REL: 255 segment = BLENDPATHSEG7(SVGPathSegArcRel, x, y, r1, r2, angle, largeArcFlag, sweepFlag); 256 break; 257 case SVGPathSeg::PATHSEG_UNKNOWN: 258 ASSERT_NOT_REACHED(); 259 } 260 result->appendItem(segment, ec); 261 if (ec) 262 return 0; 263 } 264 return result.release(); 265 } 266 267 } 268 269 #endif // ENABLE(SVG) 270