OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2004, 2005, 2008 Nikolas Zimmermann <zimmermann@kde.org> | 2 * Copyright (C) 2004, 2005, 2008 Nikolas Zimmermann <zimmermann@kde.org> |
3 * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org> | 3 * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org> |
| 4 * Copyright (C) 2007 Eric Seidel <eric@webkit.org> |
| 5 * Copyright (C) 2008 Apple Inc. All rights reserved. |
| 6 * Copyright (C) Research In Motion Limited 2012. All rights reserved. |
4 * | 7 * |
5 * This library is free software; you can redistribute it and/or | 8 * This library is free software; you can redistribute it and/or |
6 * modify it under the terms of the GNU Library General Public | 9 * modify it under the terms of the GNU Library General Public |
7 * License as published by the Free Software Foundation; either | 10 * License as published by the Free Software Foundation; either |
8 * version 2 of the License, or (at your option) any later version. | 11 * version 2 of the License, or (at your option) any later version. |
9 * | 12 * |
10 * This library is distributed in the hope that it will be useful, | 13 * This library is distributed in the hope that it will be useful, |
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 * Library General Public License for more details. | 16 * Library General Public License for more details. |
14 * | 17 * |
15 * You should have received a copy of the GNU Library General Public License | 18 * You should have received a copy of the GNU Library General Public License |
16 * along with this library; see the file COPYING.LIB. If not, write to | 19 * along with this library; see the file COPYING.LIB. If not, write to |
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | 20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
18 * Boston, MA 02110-1301, USA. | 21 * Boston, MA 02110-1301, USA. |
19 */ | 22 */ |
20 | 23 |
21 #include "config.h" | 24 #include "config.h" |
| 25 |
22 #include "core/svg/SVGTransformList.h" | 26 #include "core/svg/SVGTransformList.h" |
23 | 27 |
| 28 #include "SVGNames.h" |
| 29 #include "core/svg/SVGAnimateTransformElement.h" |
| 30 #include "core/svg/SVGAnimatedNumber.h" |
24 #include "core/svg/SVGParserUtilities.h" | 31 #include "core/svg/SVGParserUtilities.h" |
25 #include "core/svg/SVGSVGElement.h" | 32 #include "core/svg/SVGTransformDistance.h" |
26 #include "platform/transforms/AffineTransform.h" | |
27 #include "wtf/text/StringBuilder.h" | 33 #include "wtf/text/StringBuilder.h" |
| 34 #include "wtf/text/WTFString.h" |
28 | 35 |
29 namespace WebCore { | 36 namespace WebCore { |
30 | 37 |
31 SVGTransform SVGTransformList::createSVGTransformFromMatrix(const SVGMatrix& mat
rix) const | 38 inline PassRefPtr<SVGTransformList> toSVGTransformList(PassRefPtr<NewSVGProperty
Base> passBase) |
32 { | 39 { |
33 return SVGSVGElement::createSVGTransformFromMatrix(matrix); | 40 RefPtr<NewSVGPropertyBase> base = passBase; |
34 } | 41 ASSERT(base->type() == SVGTransformList::classType()); |
35 | 42 return static_pointer_cast<SVGTransformList>(base.release()); |
36 SVGTransform SVGTransformList::consolidate() | 43 } |
| 44 |
| 45 SVGTransformList::SVGTransformList() |
| 46 { |
| 47 } |
| 48 |
| 49 SVGTransformList::~SVGTransformList() |
| 50 { |
| 51 } |
| 52 |
| 53 PassRefPtr<SVGTransform> SVGTransformList::consolidate() |
37 { | 54 { |
38 AffineTransform matrix; | 55 AffineTransform matrix; |
39 if (!concatenate(matrix)) | 56 if (!concatenate(matrix)) |
40 return SVGTransform(); | 57 return SVGTransform::create(); |
41 | 58 |
42 SVGTransform transform(matrix); | 59 RefPtr<SVGTransform> transform = SVGTransform::create(matrix); |
43 clear(); | 60 clear(); |
44 append(transform); | 61 return appendItem(transform); |
45 return transform; | |
46 } | 62 } |
47 | 63 |
48 bool SVGTransformList::concatenate(AffineTransform& result) const | 64 bool SVGTransformList::concatenate(AffineTransform& result) const |
49 { | 65 { |
50 unsigned size = this->size(); | 66 if (isEmpty()) |
51 if (!size) | |
52 return false; | 67 return false; |
53 | 68 |
54 for (unsigned i = 0; i < size; ++i) | 69 ConstIterator it = begin(); |
55 result *= at(i).matrix(); | 70 ConstIterator itEnd = end(); |
| 71 for (; it != itEnd; ++it) |
| 72 result *= it->matrix(); |
56 | 73 |
57 return true; | 74 return true; |
58 } | 75 } |
59 | 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* va
lues, 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], false)) |
| 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 == maxPo
ssibleParams)) |
| 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*& pt
r, const CharType* end) |
| 123 { |
| 124 if (type == SVG_TRANSFORM_UNKNOWN) |
| 125 return 0; |
| 126 |
| 127 int valueCount = 0; |
| 128 float values[] = {0, 0, 0, 0, 0, 0}; |
| 129 if ((valueCount = parseTransformParamList(ptr, end, values, requiredValuesFo
rType[type], optionalValuesForType[type])) < 0) { |
| 130 return 0; |
| 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 sc
aling |
| 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], va
lues[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 |
60 String SVGTransformList::valueAsString() const | 219 String SVGTransformList::valueAsString() const |
61 { | 220 { |
62 StringBuilder builder; | 221 StringBuilder builder; |
63 unsigned size = this->size(); | 222 |
64 for (unsigned i = 0; i < size; ++i) { | 223 ConstIterator it = begin(); |
65 if (i > 0) | 224 ConstIterator itEnd = end(); |
| 225 while (it != itEnd) { |
| 226 builder.append(it->valueAsString()); |
| 227 ++it; |
| 228 if (it != itEnd) |
66 builder.append(' '); | 229 builder.append(' '); |
67 | |
68 builder.append(at(i).valueAsString()); | |
69 } | 230 } |
70 | 231 |
71 return builder.toString(); | 232 return builder.toString(); |
72 } | 233 } |
73 | 234 |
74 void SVGTransformList::parse(const String& transform) | 235 void SVGTransformList::setValueAsString(const String& value, ExceptionState& exc
eptionState) |
75 { | 236 { |
76 if (transform.isEmpty()) { | 237 if (value.isEmpty()) { |
77 // FIXME: The parseTransformAttribute function secretly calls clear() | |
78 // based on a |mode| parameter. We should study whether we should | |
79 // remove the |mode| parameter and force callers to call clear() | |
80 // themselves. | |
81 clear(); | 238 clear(); |
82 } else if (transform.is8Bit()) { | 239 return; |
83 const LChar* ptr = transform.characters8(); | 240 } |
84 const LChar* end = ptr + transform.length(); | 241 |
85 if (!parseTransformAttribute(*this, ptr, end)) | 242 bool valid = false; |
86 clear(); | 243 if (value.is8Bit()) { |
| 244 const LChar* ptr = value.characters8(); |
| 245 const LChar* end = ptr + value.length(); |
| 246 valid = parse(ptr, end); |
87 } else { | 247 } else { |
88 const UChar* ptr = transform.characters16(); | 248 const UChar* ptr = value.characters16(); |
89 const UChar* end = ptr + transform.length(); | 249 const UChar* end = ptr + value.length(); |
90 if (!parseTransformAttribute(*this, ptr, end)) | 250 valid = parse(ptr, end); |
91 clear(); | 251 } |
92 } | 252 |
93 } | 253 if (!valid) { |
94 | 254 clear(); |
95 } // namespace WebCore | 255 exceptionState.throwDOMException(SyntaxError, "Problem parsing transform
list=\""+value+"\""); |
| 256 } |
| 257 } |
| 258 |
| 259 PassRefPtr<NewSVGPropertyBase> SVGTransformList::cloneForAnimation(const String&
value) const |
| 260 { |
| 261 ASSERT_NOT_REACHED(); |
| 262 return 0; |
| 263 } |
| 264 |
| 265 PassRefPtr<SVGTransformList> SVGTransformList::create(SVGTransformType transform
Type, 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(PassRefPtr<NewSVGPropertyBase> other, SVGElement* con
textElement) |
| 286 { |
| 287 if (isEmpty()) |
| 288 return; |
| 289 |
| 290 RefPtr<SVGTransformList> otherList = toSVGTransformList(other); |
| 291 if (numberOfItems() != otherList->numberOfItems()) |
| 292 return; |
| 293 |
| 294 ASSERT(numberOfItems() == 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* animationElem
ent, float percentage, unsigned repeatCount, PassRefPtr<NewSVGPropertyBase> from
Value, PassRefPtr<NewSVGPropertyBase> toValue, PassRefPtr<NewSVGPropertyBase> to
AtEndOfDurationValue, 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 an
imations for ‘animateTransform’ is undefined |
| 311 // FIXME: This is not taken into account yet. |
| 312 RefPtr<SVGTransformList> fromList = isToAnimation ? this : toSVGTransformLis
t(fromValue); |
| 313 RefPtr<SVGTransformList> toList = toSVGTransformList(toValue); |
| 314 RefPtr<SVGTransformList> toAtEndOfDurationList = toSVGTransformList(toAtEndO
fDurationValue); |
| 315 |
| 316 size_t fromListSize = fromList->numberOfItems(); |
| 317 size_t toListSize = toList->numberOfItems(); |
| 318 |
| 319 if (!toListSize) |
| 320 return; |
| 321 |
| 322 // Never resize the animatedTransformList to the toList size, instead either
clear the list or append to it. |
| 323 if (!isEmpty() && !animationElement->isAdditive()) |
| 324 clear(); |
| 325 |
| 326 RefPtr<SVGTransform> toTransform = toList->at(0); |
| 327 RefPtr<SVGTransform> effectiveFrom = fromListSize ? fromList->at(0) : SVGTra
nsform::create(toTransform->transformType(), SVGTransform::ConstructZeroTransfor
m); |
| 328 RefPtr<SVGTransform> currentTransform = SVGTransformDistance(effectiveFrom,
toTransform).scaledDistance(percentage).addToSVGTransform(effectiveFrom); |
| 329 if (animationElement->isAccumulated() && repeatCount) { |
| 330 RefPtr<SVGTransform> effectiveToAtEnd = !toAtEndOfDurationList->isEmpty(
) ? toAtEndOfDurationList->at(0) : SVGTransform::create(toTransform->transformTy
pe(), SVGTransform::ConstructZeroTransform); |
| 331 append(SVGTransformDistance::addSVGTransforms(currentTransform, effectiv
eToAtEnd, repeatCount)); |
| 332 } else { |
| 333 append(currentTransform); |
| 334 } |
| 335 } |
| 336 |
| 337 float SVGTransformList::calculateDistance(PassRefPtr<NewSVGPropertyBase> toValue
, SVGElement*) |
| 338 { |
| 339 // FIXME: This is not correct in all cases. The spec demands that each compo
nent (translate x and y for example) |
| 340 // is paced separately. To implement this we need to treat each component as
individual animation everywhere. |
| 341 |
| 342 RefPtr<SVGTransformList> toList = toSVGTransformList(toValue); |
| 343 if (isEmpty() || numberOfItems() != toList->numberOfItems()) |
| 344 return -1; |
| 345 |
| 346 ASSERT(numberOfItems() == 1); |
| 347 if (at(0)->transformType() == toList->at(0)->transformType()) |
| 348 return -1; |
| 349 |
| 350 // Spec: http://www.w3.org/TR/SVG/animate.html#complexDistances |
| 351 // Paced animations assume a notion of distance between the various animatio
n values defined by the ‘to’, ‘from’, ‘by’ and ‘values’ attributes. |
| 352 // Distance is defined only for scalar types (such as <length>), colors and
the subset of transformation types that are supported by ‘animateTransform’. |
| 353 return SVGTransformDistance(at(0), toList->at(0)).distance(); |
| 354 } |
| 355 |
| 356 } |
OLD | NEW |