Chromium Code Reviews| 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> |
|
pdr.
2014/02/18 00:53:57
We lost a 2008 for Niko
kouhei (in TOK)
2014/02/18 02:09:15
Done.
| |
| 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); | |
|
fs
2014/02/17 14:14:31
Nit: Not used/needed.
kouhei (in TOK)
2014/02/18 02:09:15
Done.
| |
| 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(); | |
|
fs
2014/02/17 14:14:31
Nit: Drop.
kouhei (in TOK)
2014/02/18 02:09:15
Done.
| |
| 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); | |
|
fs
2014/02/17 14:14:31
Could also be moved into the if-block, since it's
kouhei (in TOK)
2014/02/18 02:09:15
Done.
| |
| 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) { | |
|
fs
2014/02/17 14:14:31
Alternatively:
while (it != itEnd) {
builder.ap
kouhei (in TOK)
2014/02/18 02:09:15
Done.
| |
| 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 |