OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2004, 2005, 2008 Nikolas Zimmermann <zimmermann@kde.org> | 2 * Copyright (C) 2004, 2005 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 String aaa(ptr, end-ptr); |
| 130 if ((valueCount = parseTransformParamList(ptr, end, values, requiredValuesFo
rType[type], optionalValuesForType[type])) < 0) { |
| 131 return 0; |
| 132 } |
| 133 |
| 134 RefPtr<SVGTransform> transform = SVGTransform::create(); |
| 135 |
| 136 switch (type) { |
| 137 case SVG_TRANSFORM_SKEWX: |
| 138 transform->setSkewX(values[0]); |
| 139 break; |
| 140 case SVG_TRANSFORM_SKEWY: |
| 141 transform->setSkewY(values[0]); |
| 142 break; |
| 143 case SVG_TRANSFORM_SCALE: |
| 144 if (valueCount == 1) // Spec: if only one param given, assume uniform sc
aling |
| 145 transform->setScale(values[0], values[0]); |
| 146 else |
| 147 transform->setScale(values[0], values[1]); |
| 148 break; |
| 149 case SVG_TRANSFORM_TRANSLATE: |
| 150 if (valueCount == 1) // Spec: if only one param given, assume 2nd param
to be 0 |
| 151 transform->setTranslate(values[0], 0); |
| 152 else |
| 153 transform->setTranslate(values[0], values[1]); |
| 154 break; |
| 155 case SVG_TRANSFORM_ROTATE: |
| 156 if (valueCount == 1) |
| 157 transform->setRotate(values[0], 0, 0); |
| 158 else |
| 159 transform->setRotate(values[0], values[1], values[2]); |
| 160 break; |
| 161 case SVG_TRANSFORM_MATRIX: |
| 162 transform->setMatrix(AffineTransform(values[0], values[1], values[2], va
lues[3], values[4], values[5])); |
| 163 break; |
| 164 } |
| 165 |
| 166 return transform.release(); |
| 167 } |
| 168 |
| 169 } |
| 170 |
| 171 template<typename CharType> |
| 172 bool SVGTransformList::parseInternal(const CharType*& ptr, const CharType* end) |
| 173 { |
| 174 String(ptr, end-ptr).show(); |
| 175 |
| 176 clear(); |
| 177 |
| 178 bool delimParsed = false; |
| 179 while (ptr < end) { |
| 180 delimParsed = false; |
| 181 SVGTransformType transformType = SVG_TRANSFORM_UNKNOWN; |
| 182 skipOptionalSVGSpaces(ptr, end); |
| 183 |
| 184 if (!parseAndSkipTransformType(ptr, end, transformType)) |
| 185 return false; |
| 186 |
| 187 if (!skipOptionalSVGSpaces(ptr, end) || *ptr != '(') |
| 188 return false; |
| 189 ptr++; |
| 190 |
| 191 RefPtr<SVGTransform> transform = parseTransformOfType(transformType, ptr
, end); |
| 192 if (!transform) |
| 193 return false; |
| 194 |
| 195 if (!skipOptionalSVGSpaces(ptr, end) || *ptr != ')') |
| 196 return false; |
| 197 ptr++; |
| 198 |
| 199 append(transform.release()); |
| 200 |
| 201 skipOptionalSVGSpaces(ptr, end); |
| 202 if (ptr < end && *ptr == ',') { |
| 203 delimParsed = true; |
| 204 ++ptr; |
| 205 } |
| 206 skipOptionalSVGSpaces(ptr, end); |
| 207 } |
| 208 |
| 209 return !delimParsed; |
| 210 } |
| 211 |
| 212 bool SVGTransformList::parse(const UChar*& ptr, const UChar* end) |
| 213 { |
| 214 return parseInternal(ptr, end); |
| 215 } |
| 216 |
| 217 bool SVGTransformList::parse(const LChar*& ptr, const LChar* end) |
| 218 { |
| 219 return parseInternal(ptr, end); |
| 220 } |
| 221 |
60 String SVGTransformList::valueAsString() const | 222 String SVGTransformList::valueAsString() const |
61 { | 223 { |
62 StringBuilder builder; | 224 StringBuilder builder; |
63 unsigned size = this->size(); | 225 |
64 for (unsigned i = 0; i < size; ++i) { | 226 ConstIterator it = begin(); |
65 if (i > 0) | 227 ConstIterator itEnd = end(); |
| 228 if (it != itEnd) { |
| 229 builder.append(it->valueAsString()); |
| 230 ++it; |
| 231 |
| 232 for (; it != itEnd; ++it) { |
66 builder.append(' '); | 233 builder.append(' '); |
67 | 234 builder.append(it->valueAsString()); |
68 builder.append(at(i).valueAsString()); | 235 } |
69 } | 236 } |
70 | 237 |
71 return builder.toString(); | 238 return builder.toString(); |
72 } | 239 } |
73 | 240 |
74 void SVGTransformList::parse(const String& transform) | 241 void SVGTransformList::setValueAsString(const String& value, ExceptionState& exc
eptionState) |
75 { | 242 { |
76 if (transform.isEmpty()) { | 243 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(); | 244 clear(); |
82 } else if (transform.is8Bit()) { | 245 return; |
83 const LChar* ptr = transform.characters8(); | 246 } |
84 const LChar* end = ptr + transform.length(); | 247 |
85 if (!parseTransformAttribute(*this, ptr, end)) | 248 bool valid = false; |
86 clear(); | 249 if (value.is8Bit()) { |
| 250 const LChar* ptr = value.characters8(); |
| 251 const LChar* end = ptr + value.length(); |
| 252 valid = parse(ptr, end); |
87 } else { | 253 } else { |
88 const UChar* ptr = transform.characters16(); | 254 const UChar* ptr = value.characters16(); |
89 const UChar* end = ptr + transform.length(); | 255 const UChar* end = ptr + value.length(); |
90 if (!parseTransformAttribute(*this, ptr, end)) | 256 valid = parse(ptr, end); |
91 clear(); | 257 } |
92 } | 258 |
93 } | 259 if (!valid) { |
94 | 260 clear(); |
95 } // namespace WebCore | 261 exceptionState.throwDOMException(SyntaxError, "Problem parsing transform
list=\""+value+"\""); |
| 262 } |
| 263 } |
| 264 |
| 265 PassRefPtr<NewSVGPropertyBase> SVGTransformList::cloneForAnimation(const String&
value) const |
| 266 { |
| 267 ASSERT_NOT_REACHED(); |
| 268 } |
| 269 |
| 270 PassRefPtr<SVGTransformList> SVGTransformList::create(SVGTransformType transform
Type, const String& value) |
| 271 { |
| 272 RefPtr<SVGTransform> transform; |
| 273 if (value.isEmpty()) { |
| 274 } else if (value.is8Bit()) { |
| 275 const LChar* ptr = value.characters8(); |
| 276 const LChar* end = ptr + value.length(); |
| 277 transform = parseTransformOfType(transformType, ptr, end); |
| 278 } else { |
| 279 const UChar* ptr = value.characters16(); |
| 280 const UChar* end = ptr + value.length(); |
| 281 transform = parseTransformOfType(transformType, ptr, end); |
| 282 } |
| 283 |
| 284 RefPtr<SVGTransformList> svgTransformList = SVGTransformList::create(); |
| 285 if (transform) |
| 286 svgTransformList->append(transform); |
| 287 return svgTransformList.release(); |
| 288 } |
| 289 |
| 290 void SVGTransformList::add(PassRefPtr<NewSVGPropertyBase> other, SVGElement* con
textElement) |
| 291 { |
| 292 if (isEmpty()) |
| 293 return; |
| 294 |
| 295 RefPtr<SVGTransformList> otherList = toSVGTransformList(other); |
| 296 if (numberOfItems() != otherList->numberOfItems()) |
| 297 return; |
| 298 |
| 299 ASSERT(numberOfItems() == 1); |
| 300 RefPtr<SVGTransform> fromTransform = at(0); |
| 301 RefPtr<SVGTransform> toTransform = otherList->at(0); |
| 302 |
| 303 ASSERT(fromTransform->transformType() == toTransform->transformType()); |
| 304 clear(); |
| 305 append(SVGTransformDistance::addSVGTransforms(fromTransform, toTransform)); |
| 306 } |
| 307 |
| 308 void SVGTransformList::calculateAnimatedValue(SVGAnimationElement* animationElem
ent, float percentage, unsigned repeatCount, PassRefPtr<NewSVGPropertyBase> from
Value, PassRefPtr<NewSVGPropertyBase> toValue, PassRefPtr<NewSVGPropertyBase> to
AtEndOfDurationValue, SVGElement* contextElement) |
| 309 { |
| 310 ASSERT(animationElement); |
| 311 bool isToAnimation = animationElement->animationMode() == ToAnimation; |
| 312 |
| 313 // Spec: To animations provide specific functionality to get a smooth change
from the underlying value to the |
| 314 // ‘to’ attribute value, which conflicts mathematically with the requirement
for additive transform animations |
| 315 // to be post-multiplied. As a consequence, in SVG 1.1 the behavior of to an
imations for ‘animateTransform’ is undefined |
| 316 // FIXME: This is not taken into account yet. |
| 317 RefPtr<SVGTransformList> fromList = isToAnimation ? this : toSVGTransformLis
t(fromValue); |
| 318 RefPtr<SVGTransformList> toList = toSVGTransformList(toValue); |
| 319 RefPtr<SVGTransformList> toAtEndOfDurationList = toSVGTransformList(toAtEndO
fDurationValue); |
| 320 |
| 321 size_t fromListSize = fromList->numberOfItems(); |
| 322 size_t toListSize = toList->numberOfItems(); |
| 323 |
| 324 if (!toListSize) |
| 325 return; |
| 326 |
| 327 // Never resize the animatedTransformList to the toList size, instead either
clear the list or append to it. |
| 328 if (!isEmpty() && !animationElement->isAdditive()) |
| 329 clear(); |
| 330 |
| 331 RefPtr<SVGTransform> toTransform = toList->at(0); |
| 332 RefPtr<SVGTransform> effectiveFrom = fromListSize ? fromList->at(0) : SVGTra
nsform::create(toTransform->transformType(), SVGTransform::ConstructZeroTransfor
m); |
| 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->transformTy
pe(), SVGTransform::ConstructZeroTransform); |
| 336 append(SVGTransformDistance::addSVGTransforms(currentTransform, effectiv
eToAtEnd, repeatCount)); |
| 337 } else { |
| 338 append(currentTransform); |
| 339 } |
| 340 } |
| 341 |
| 342 float SVGTransformList::calculateDistance(PassRefPtr<NewSVGPropertyBase> toValue
, SVGElement*) |
| 343 { |
| 344 // FIXME: This is not correct in all cases. The spec demands that each compo
nent (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() || numberOfItems() != toList->numberOfItems()) |
| 349 return -1; |
| 350 |
| 351 ASSERT(numberOfItems() == 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 animatio
n 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 } |
OLD | NEW |