1 /* 2 * Copyright (C) 2004, 2005, 2008 Nikolas Zimmermann <zimmermann (at) kde.org> 3 * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis (at) kde.org> 4 * Copyright (C) 2007 Eric Seidel <eric (at) webkit.org> 5 * Copyright (C) 2008 Apple Inc. All rights reserved. 6 * Copyright (C) Research In Motion Limited 2012. All rights reserved. 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 #include "core/svg/SVGTransformList.h" 27 28 #include "core/SVGNames.h" 29 #include "core/svg/SVGAnimateTransformElement.h" 30 #include "core/svg/SVGAnimatedNumber.h" 31 #include "core/svg/SVGParserUtilities.h" 32 #include "core/svg/SVGTransformDistance.h" 33 #include "wtf/text/StringBuilder.h" 34 #include "wtf/text/WTFString.h" 35 36 namespace WebCore { 37 38 inline PassRefPtr<SVGTransformList> toSVGTransformList(PassRefPtr<SVGPropertyBase> passBase) 39 { 40 RefPtr<SVGPropertyBase> base = passBase; 41 ASSERT(base->type() == SVGTransformList::classType()); 42 return static_pointer_cast<SVGTransformList>(base.release()); 43 } 44 45 SVGTransformList::SVGTransformList() 46 { 47 } 48 49 SVGTransformList::~SVGTransformList() 50 { 51 } 52 53 PassRefPtr<SVGTransform> SVGTransformList::consolidate() 54 { 55 AffineTransform matrix; 56 if (!concatenate(matrix)) 57 return SVGTransform::create(); 58 59 RefPtr<SVGTransform> transform = SVGTransform::create(matrix); 60 clear(); 61 return appendItem(transform); 62 } 63 64 bool SVGTransformList::concatenate(AffineTransform& result) const 65 { 66 if (isEmpty()) 67 return false; 68 69 ConstIterator it = begin(); 70 ConstIterator itEnd = end(); 71 for (; it != itEnd; ++it) 72 result *= it->matrix(); 73 74 return true; 75 } 76 77 PassRefPtr<SVGTransformList> SVGTransformList::clone() 78 { 79 RefPtr<SVGTransformList> svgTransformList = SVGTransformList::create(); 80 svgTransformList->deepCopy(this); 81 return svgTransformList.release(); 82 } 83 84 namespace { 85 86 template<typename CharType> 87 int parseTransformParamList(const CharType*& ptr, const CharType* end, float* values, int required, int optional) 88 { 89 int parsedParams = 0; 90 int maxPossibleParams = required + optional; 91 92 bool trailingDelimiter = false; 93 94 skipOptionalSVGSpaces(ptr, end); 95 while (parsedParams < maxPossibleParams) { 96 if (!parseNumber(ptr, end, values[parsedParams], DisallowWhitespace)) 97 break; 98 99 ++parsedParams; 100 101 if (skipOptionalSVGSpaces(ptr, end) && *ptr == ',') { 102 ++ptr; 103 skipOptionalSVGSpaces(ptr, end); 104 105 trailingDelimiter = true; 106 } else { 107 trailingDelimiter = false; 108 } 109 } 110 111 if (trailingDelimiter || !(parsedParams == required || parsedParams == maxPossibleParams)) 112 return -1; 113 114 return parsedParams; 115 } 116 117 // These should be kept in sync with enum SVGTransformType 118 static const int requiredValuesForType[] = {0, 6, 1, 1, 1, 1, 1}; 119 static const int optionalValuesForType[] = {0, 0, 1, 1, 2, 0, 0}; 120 121 template<typename CharType> 122 PassRefPtr<SVGTransform> parseTransformOfType(unsigned type, const CharType*& ptr, const CharType* end) 123 { 124 if (type == SVG_TRANSFORM_UNKNOWN) 125 return nullptr; 126 127 int valueCount = 0; 128 float values[] = {0, 0, 0, 0, 0, 0}; 129 if ((valueCount = parseTransformParamList(ptr, end, values, requiredValuesForType[type], optionalValuesForType[type])) < 0) { 130 return nullptr; 131 } 132 133 RefPtr<SVGTransform> transform = SVGTransform::create(); 134 135 switch (type) { 136 case SVG_TRANSFORM_SKEWX: 137 transform->setSkewX(values[0]); 138 break; 139 case SVG_TRANSFORM_SKEWY: 140 transform->setSkewY(values[0]); 141 break; 142 case SVG_TRANSFORM_SCALE: 143 if (valueCount == 1) // Spec: if only one param given, assume uniform scaling 144 transform->setScale(values[0], values[0]); 145 else 146 transform->setScale(values[0], values[1]); 147 break; 148 case SVG_TRANSFORM_TRANSLATE: 149 if (valueCount == 1) // Spec: if only one param given, assume 2nd param to be 0 150 transform->setTranslate(values[0], 0); 151 else 152 transform->setTranslate(values[0], values[1]); 153 break; 154 case SVG_TRANSFORM_ROTATE: 155 if (valueCount == 1) 156 transform->setRotate(values[0], 0, 0); 157 else 158 transform->setRotate(values[0], values[1], values[2]); 159 break; 160 case SVG_TRANSFORM_MATRIX: 161 transform->setMatrix(AffineTransform(values[0], values[1], values[2], values[3], values[4], values[5])); 162 break; 163 } 164 165 return transform.release(); 166 } 167 168 } 169 170 template<typename CharType> 171 bool SVGTransformList::parseInternal(const CharType*& ptr, const CharType* end) 172 { 173 clear(); 174 175 bool delimParsed = false; 176 while (ptr < end) { 177 delimParsed = false; 178 SVGTransformType transformType = SVG_TRANSFORM_UNKNOWN; 179 skipOptionalSVGSpaces(ptr, end); 180 181 if (!parseAndSkipTransformType(ptr, end, transformType)) 182 return false; 183 184 if (!skipOptionalSVGSpaces(ptr, end) || *ptr != '(') 185 return false; 186 ptr++; 187 188 RefPtr<SVGTransform> transform = parseTransformOfType(transformType, ptr, end); 189 if (!transform) 190 return false; 191 192 if (!skipOptionalSVGSpaces(ptr, end) || *ptr != ')') 193 return false; 194 ptr++; 195 196 append(transform.release()); 197 198 skipOptionalSVGSpaces(ptr, end); 199 if (ptr < end && *ptr == ',') { 200 delimParsed = true; 201 ++ptr; 202 skipOptionalSVGSpaces(ptr, end); 203 } 204 } 205 206 return !delimParsed; 207 } 208 209 bool SVGTransformList::parse(const UChar*& ptr, const UChar* end) 210 { 211 return parseInternal(ptr, end); 212 } 213 214 bool SVGTransformList::parse(const LChar*& ptr, const LChar* end) 215 { 216 return parseInternal(ptr, end); 217 } 218 219 String SVGTransformList::valueAsString() const 220 { 221 StringBuilder builder; 222 223 ConstIterator it = begin(); 224 ConstIterator itEnd = end(); 225 while (it != itEnd) { 226 builder.append(it->valueAsString()); 227 ++it; 228 if (it != itEnd) 229 builder.append(' '); 230 } 231 232 return builder.toString(); 233 } 234 235 void SVGTransformList::setValueAsString(const String& value, ExceptionState& exceptionState) 236 { 237 if (value.isEmpty()) { 238 clear(); 239 return; 240 } 241 242 bool valid = false; 243 if (value.is8Bit()) { 244 const LChar* ptr = value.characters8(); 245 const LChar* end = ptr + value.length(); 246 valid = parse(ptr, end); 247 } else { 248 const UChar* ptr = value.characters16(); 249 const UChar* end = ptr + value.length(); 250 valid = parse(ptr, end); 251 } 252 253 if (!valid) { 254 clear(); 255 exceptionState.throwDOMException(SyntaxError, "Problem parsing transform list=\""+value+"\""); 256 } 257 } 258 259 PassRefPtr<SVGPropertyBase> SVGTransformList::cloneForAnimation(const String& value) const 260 { 261 ASSERT_NOT_REACHED(); 262 return nullptr; 263 } 264 265 PassRefPtr<SVGTransformList> SVGTransformList::create(SVGTransformType transformType, const String& value) 266 { 267 RefPtr<SVGTransform> transform; 268 if (value.isEmpty()) { 269 } else if (value.is8Bit()) { 270 const LChar* ptr = value.characters8(); 271 const LChar* end = ptr + value.length(); 272 transform = parseTransformOfType(transformType, ptr, end); 273 } else { 274 const UChar* ptr = value.characters16(); 275 const UChar* end = ptr + value.length(); 276 transform = parseTransformOfType(transformType, ptr, end); 277 } 278 279 RefPtr<SVGTransformList> svgTransformList = SVGTransformList::create(); 280 if (transform) 281 svgTransformList->append(transform); 282 return svgTransformList.release(); 283 } 284 285 void SVGTransformList::add(PassRefPtrWillBeRawPtr<SVGPropertyBase> other, SVGElement* contextElement) 286 { 287 if (isEmpty()) 288 return; 289 290 RefPtr<SVGTransformList> otherList = toSVGTransformList(other); 291 if (length() != otherList->length()) 292 return; 293 294 ASSERT(length() == 1); 295 RefPtr<SVGTransform> fromTransform = at(0); 296 RefPtr<SVGTransform> toTransform = otherList->at(0); 297 298 ASSERT(fromTransform->transformType() == toTransform->transformType()); 299 clear(); 300 append(SVGTransformDistance::addSVGTransforms(fromTransform, toTransform)); 301 } 302 303 void SVGTransformList::calculateAnimatedValue(SVGAnimationElement* animationElement, float percentage, unsigned repeatCount, PassRefPtr<SVGPropertyBase> fromValue, PassRefPtr<SVGPropertyBase> toValue, PassRefPtr<SVGPropertyBase> toAtEndOfDurationValue, SVGElement* contextElement) 304 { 305 ASSERT(animationElement); 306 bool isToAnimation = animationElement->animationMode() == ToAnimation; 307 308 // Spec: To animations provide specific functionality to get a smooth change from the underlying value to the 309 // to attribute value, which conflicts mathematically with the requirement for additive transform animations 310 // to be post-multiplied. As a consequence, in SVG 1.1 the behavior of to animations for animateTransform is undefined 311 // FIXME: This is not taken into account yet. 312 RefPtr<SVGTransformList> fromList = isToAnimation ? this : toSVGTransformList(fromValue); 313 RefPtr<SVGTransformList> toList = toSVGTransformList(toValue); 314 RefPtr<SVGTransformList> toAtEndOfDurationList = toSVGTransformList(toAtEndOfDurationValue); 315 316 size_t toListSize = toList->length(); 317 if (!toListSize) 318 return; 319 320 // Get a reference to the from value before potentially cleaning it out (in the case of a To animation.) 321 RefPtr<SVGTransform> toTransform = toList->at(0); 322 RefPtr<SVGTransform> effectiveFrom; 323 // If there's an existing 'from'/underlying value of the same type use that, else use a "zero transform". 324 if (fromList->length() && fromList->at(0)->transformType() == toTransform->transformType()) 325 effectiveFrom = fromList->at(0); 326 else 327 effectiveFrom = SVGTransform::create(toTransform->transformType(), SVGTransform::ConstructZeroTransform); 328 329 // Never resize the animatedTransformList to the toList size, instead either clear the list or append to it. 330 if (!isEmpty() && (!animationElement->isAdditive() || isToAnimation)) 331 clear(); 332 333 RefPtr<SVGTransform> currentTransform = SVGTransformDistance(effectiveFrom, toTransform).scaledDistance(percentage).addToSVGTransform(effectiveFrom); 334 if (animationElement->isAccumulated() && repeatCount) { 335 RefPtr<SVGTransform> effectiveToAtEnd = !toAtEndOfDurationList->isEmpty() ? toAtEndOfDurationList->at(0) : SVGTransform::create(toTransform->transformType(), SVGTransform::ConstructZeroTransform); 336 append(SVGTransformDistance::addSVGTransforms(currentTransform, effectiveToAtEnd, repeatCount)); 337 } else { 338 append(currentTransform); 339 } 340 } 341 342 float SVGTransformList::calculateDistance(PassRefPtr<SVGPropertyBase> toValue, SVGElement*) 343 { 344 // FIXME: This is not correct in all cases. The spec demands that each component (translate x and y for example) 345 // is paced separately. To implement this we need to treat each component as individual animation everywhere. 346 347 RefPtr<SVGTransformList> toList = toSVGTransformList(toValue); 348 if (isEmpty() || length() != toList->length()) 349 return -1; 350 351 ASSERT(length() == 1); 352 if (at(0)->transformType() == toList->at(0)->transformType()) 353 return -1; 354 355 // Spec: http://www.w3.org/TR/SVG/animate.html#complexDistances 356 // Paced animations assume a notion of distance between the various animation values defined by the to, from, by and values attributes. 357 // Distance is defined only for scalar types (such as <length>), colors and the subset of transformation types that are supported by animateTransform. 358 return SVGTransformDistance(at(0), toList->at(0)).distance(); 359 } 360 361 } 362