2 * Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
4 * Copyright (C) 2007 Apple Inc. All rights reserved.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
24 #include "core/svg/SVGLength.h"
26 #include "bindings/core/v8/ExceptionState.h"
27 #include "core/SVGNames.h"
28 #include "core/css/CSSPrimitiveValue.h"
29 #include "core/dom/ExceptionCode.h"
30 #include "core/svg/SVGAnimationElement.h"
31 #include "core/svg/SVGParserUtilities.h"
32 #include "platform/animation/AnimationUtilities.h"
33 #include "wtf/MathExtras.h"
34 #include "wtf/text/WTFString.h"
40 inline const char* lengthTypeToString(SVGLengthType type
)
43 case LengthTypeUnknown
:
44 case LengthTypeNumber
:
46 case LengthTypePercentage
:
74 template<typename CharType
>
75 SVGLengthType
stringToLengthType(const CharType
*& ptr
, const CharType
* end
)
78 return LengthTypeNumber
;
80 SVGLengthType type
= LengthTypeUnknown
;
81 const CharType firstChar
= *ptr
++;
83 if (firstChar
== '%') {
84 type
= LengthTypePercentage
;
85 } else if (isHTMLSpace
<CharType
>(firstChar
)) {
86 type
= LengthTypeNumber
;
87 } else if (ptr
< end
) {
88 const CharType secondChar
= *ptr
++;
90 if (firstChar
== 'p') {
91 if (secondChar
== 'x')
93 if (secondChar
== 't')
95 if (secondChar
== 'c')
97 } else if (firstChar
== 'e') {
98 if (secondChar
== 'm')
100 if (secondChar
== 'x')
101 type
= LengthTypeEXS
;
102 } else if (firstChar
== 'r') {
103 if (secondChar
== 'e' && ptr
< end
) {
104 const CharType thirdChar
= *ptr
++;
105 if (thirdChar
== 'm')
106 type
= LengthTypeREMS
;
108 } else if (firstChar
== 'c') {
109 if (secondChar
== 'h')
110 type
= LengthTypeCHS
;
111 if (secondChar
== 'm')
113 } else if (firstChar
== 'm' && secondChar
== 'm') {
115 } else if (firstChar
== 'i' && secondChar
== 'n') {
117 } else if (isHTMLSpace
<CharType
>(firstChar
) && isHTMLSpace
<CharType
>(secondChar
)) {
118 type
= LengthTypeNumber
;
122 if (!skipOptionalSVGSpaces(ptr
, end
))
125 return LengthTypeUnknown
;
130 SVGLength::SVGLength(SVGLengthMode mode
)
131 : SVGPropertyBase(classType())
132 , m_valueInSpecifiedUnits(0)
133 , m_unitMode(static_cast<unsigned>(mode
))
134 , m_unitType(LengthTypeNumber
)
136 ASSERT(unitMode() == mode
);
139 SVGLength::SVGLength(const SVGLength
& o
)
140 : SVGPropertyBase(classType())
141 , m_valueInSpecifiedUnits(o
.m_valueInSpecifiedUnits
)
142 , m_unitMode(o
.m_unitMode
)
143 , m_unitType(o
.m_unitType
)
147 PassRefPtrWillBeRawPtr
<SVGLength
> SVGLength::clone() const
149 return adoptRefWillBeNoop(new SVGLength(*this));
152 PassRefPtrWillBeRawPtr
<SVGPropertyBase
> SVGLength::cloneForAnimation(const String
& value
) const
154 RefPtrWillBeRawPtr
<SVGLength
> length
= create();
156 length
->m_unitMode
= m_unitMode
;
157 length
->m_unitType
= m_unitType
;
159 TrackExceptionState exceptionState
;
160 length
->setValueAsString(value
, exceptionState
);
161 if (exceptionState
.hadException()) {
162 length
->m_unitType
= LengthTypeNumber
;
163 length
->m_valueInSpecifiedUnits
= 0;
166 return length
.release();
169 bool SVGLength::operator==(const SVGLength
& other
) const
171 return m_unitMode
== other
.m_unitMode
172 && m_unitType
== other
.m_unitType
173 && m_valueInSpecifiedUnits
== other
.m_valueInSpecifiedUnits
;
176 float SVGLength::value(const SVGLengthContext
& context
) const
178 return context
.convertValueToUserUnits(m_valueInSpecifiedUnits
, unitMode(), unitType());
181 void SVGLength::setValue(float value
, const SVGLengthContext
& context
)
183 m_valueInSpecifiedUnits
= context
.convertValueFromUserUnits(value
, unitMode(), unitType());
186 void SVGLength::setUnitType(SVGLengthType type
)
188 ASSERT(type
!= LengthTypeUnknown
&& type
<= LengthTypeCHS
);
192 float SVGLength::valueAsPercentage() const
194 // LengthTypePercentage is represented with 100% = 100.0. Good for accuracy but could eventually be changed.
195 if (m_unitType
== LengthTypePercentage
) {
196 // Note: This division is a source of floating point inaccuracy.
197 return m_valueInSpecifiedUnits
/ 100;
200 return m_valueInSpecifiedUnits
;
203 float SVGLength::valueAsPercentage100() const
205 // LengthTypePercentage is represented with 100% = 100.0. Good for accuracy but could eventually be changed.
206 if (m_unitType
== LengthTypePercentage
)
207 return m_valueInSpecifiedUnits
;
209 return m_valueInSpecifiedUnits
* 100;
212 float SVGLength::scaleByPercentage(float input
) const
214 float result
= input
* m_valueInSpecifiedUnits
;
215 if (m_unitType
== LengthTypePercentage
) {
216 // Delaying division by 100 as long as possible since it introduces floating point errors.
217 result
= result
/ 100;
222 template<typename CharType
>
223 static bool parseValueInternal(const String
& string
, float& convertedNumber
, SVGLengthType
& type
)
225 const CharType
* ptr
= string
.getCharacters
<CharType
>();
226 const CharType
* end
= ptr
+ string
.length();
228 if (!parseNumber(ptr
, end
, convertedNumber
, AllowLeadingWhitespace
))
231 type
= stringToLengthType(ptr
, end
);
233 if (type
== LengthTypeUnknown
)
239 void SVGLength::setValueAsString(const String
& string
, ExceptionState
& exceptionState
)
241 if (string
.isEmpty()) {
242 m_unitType
= LengthTypeNumber
;
243 m_valueInSpecifiedUnits
= 0;
247 float convertedNumber
= 0;
248 SVGLengthType type
= LengthTypeUnknown
;
250 bool success
= string
.is8Bit() ?
251 parseValueInternal
<LChar
>(string
, convertedNumber
, type
) :
252 parseValueInternal
<UChar
>(string
, convertedNumber
, type
);
255 exceptionState
.throwDOMException(SyntaxError
, "The value provided ('" + string
+ "') is invalid.");
260 m_valueInSpecifiedUnits
= convertedNumber
;
263 String
SVGLength::valueAsString() const
265 return String::number(m_valueInSpecifiedUnits
) + lengthTypeToString(unitType());
268 void SVGLength::newValueSpecifiedUnits(SVGLengthType type
, float value
)
271 m_valueInSpecifiedUnits
= value
;
274 void SVGLength::convertToSpecifiedUnits(SVGLengthType type
, const SVGLengthContext
& context
)
276 ASSERT(type
!= LengthTypeUnknown
&& type
<= LengthTypeCHS
);
278 float valueInUserUnits
= value(context
);
280 setValue(valueInUserUnits
, context
);
283 SVGLengthMode
SVGLength::lengthModeForAnimatedLengthAttribute(const QualifiedName
& attrName
)
285 typedef HashMap
<QualifiedName
, SVGLengthMode
> LengthModeForLengthAttributeMap
;
286 DEFINE_STATIC_LOCAL(LengthModeForLengthAttributeMap
, s_lengthModeMap
, ());
288 if (s_lengthModeMap
.isEmpty()) {
289 s_lengthModeMap
.set(SVGNames::xAttr
, SVGLengthMode::Width
);
290 s_lengthModeMap
.set(SVGNames::yAttr
, SVGLengthMode::Height
);
291 s_lengthModeMap
.set(SVGNames::cxAttr
, SVGLengthMode::Width
);
292 s_lengthModeMap
.set(SVGNames::cyAttr
, SVGLengthMode::Height
);
293 s_lengthModeMap
.set(SVGNames::dxAttr
, SVGLengthMode::Width
);
294 s_lengthModeMap
.set(SVGNames::dyAttr
, SVGLengthMode::Height
);
295 s_lengthModeMap
.set(SVGNames::fxAttr
, SVGLengthMode::Width
);
296 s_lengthModeMap
.set(SVGNames::fyAttr
, SVGLengthMode::Height
);
297 s_lengthModeMap
.set(SVGNames::rAttr
, SVGLengthMode::Other
);
298 s_lengthModeMap
.set(SVGNames::rxAttr
, SVGLengthMode::Width
);
299 s_lengthModeMap
.set(SVGNames::ryAttr
, SVGLengthMode::Height
);
300 s_lengthModeMap
.set(SVGNames::widthAttr
, SVGLengthMode::Width
);
301 s_lengthModeMap
.set(SVGNames::heightAttr
, SVGLengthMode::Height
);
302 s_lengthModeMap
.set(SVGNames::x1Attr
, SVGLengthMode::Width
);
303 s_lengthModeMap
.set(SVGNames::x2Attr
, SVGLengthMode::Width
);
304 s_lengthModeMap
.set(SVGNames::y1Attr
, SVGLengthMode::Height
);
305 s_lengthModeMap
.set(SVGNames::y2Attr
, SVGLengthMode::Height
);
306 s_lengthModeMap
.set(SVGNames::refXAttr
, SVGLengthMode::Width
);
307 s_lengthModeMap
.set(SVGNames::refYAttr
, SVGLengthMode::Height
);
308 s_lengthModeMap
.set(SVGNames::markerWidthAttr
, SVGLengthMode::Width
);
309 s_lengthModeMap
.set(SVGNames::markerHeightAttr
, SVGLengthMode::Height
);
310 s_lengthModeMap
.set(SVGNames::textLengthAttr
, SVGLengthMode::Width
);
311 s_lengthModeMap
.set(SVGNames::startOffsetAttr
, SVGLengthMode::Width
);
314 if (s_lengthModeMap
.contains(attrName
))
315 return s_lengthModeMap
.get(attrName
);
317 return SVGLengthMode::Other
;
320 PassRefPtrWillBeRawPtr
<SVGLength
> SVGLength::blend(PassRefPtrWillBeRawPtr
<SVGLength
> passFrom
, float progress
) const
322 RefPtrWillBeRawPtr
<SVGLength
> from
= passFrom
;
324 SVGLengthType toType
= unitType();
325 SVGLengthType fromType
= from
->unitType();
326 if ((from
->isZero() && isZero())
327 || fromType
== LengthTypeUnknown
328 || toType
== LengthTypeUnknown
329 || (!from
->isZero() && fromType
!= LengthTypePercentage
&& toType
== LengthTypePercentage
)
330 || (!isZero() && fromType
== LengthTypePercentage
&& toType
!= LengthTypePercentage
)
331 || (!from
->isZero() && !isZero() && (fromType
== LengthTypeEMS
|| fromType
== LengthTypeEXS
|| fromType
== LengthTypeREMS
|| fromType
== LengthTypeCHS
) && fromType
!= toType
))
334 RefPtrWillBeRawPtr
<SVGLength
> length
= create();
336 if (fromType
== LengthTypePercentage
|| toType
== LengthTypePercentage
) {
337 float fromPercent
= from
->valueAsPercentage100();
338 float toPercent
= valueAsPercentage100();
339 length
->newValueSpecifiedUnits(LengthTypePercentage
, blink::blend(fromPercent
, toPercent
, progress
));
343 if (fromType
== toType
|| from
->isZero() || isZero() || fromType
== LengthTypeEMS
|| fromType
== LengthTypeEXS
|| fromType
== LengthTypeREMS
|| fromType
== LengthTypeCHS
) {
344 float fromValue
= from
->valueInSpecifiedUnits();
345 float toValue
= valueInSpecifiedUnits();
347 length
->newValueSpecifiedUnits(fromType
, blink::blend(fromValue
, toValue
, progress
));
349 length
->newValueSpecifiedUnits(toType
, blink::blend(fromValue
, toValue
, progress
));
353 ASSERT(!isRelative());
354 ASSERT(!from
->isRelative());
356 SVGLengthContext
nonRelativeLengthContext(0);
357 float fromValueInUserUnits
= nonRelativeLengthContext
.convertValueToUserUnits(from
->valueInSpecifiedUnits(), from
->unitMode(), fromType
);
358 float fromValue
= nonRelativeLengthContext
.convertValueFromUserUnits(fromValueInUserUnits
, unitMode(), toType
);
360 float toValue
= valueInSpecifiedUnits();
361 length
->newValueSpecifiedUnits(toType
, blink::blend(fromValue
, toValue
, progress
));
365 void SVGLength::add(PassRefPtrWillBeRawPtr
<SVGPropertyBase
> other
, SVGElement
* contextElement
)
367 SVGLengthContext
lengthContext(contextElement
);
368 setValue(value(lengthContext
) + toSVGLength(other
)->value(lengthContext
), lengthContext
);
371 void SVGLength::calculateAnimatedValue(SVGAnimationElement
* animationElement
, float percentage
, unsigned repeatCount
, PassRefPtrWillBeRawPtr
<SVGPropertyBase
> fromValue
, PassRefPtrWillBeRawPtr
<SVGPropertyBase
> toValue
, PassRefPtrWillBeRawPtr
<SVGPropertyBase
> toAtEndOfDurationValue
, SVGElement
* contextElement
)
373 RefPtrWillBeRawPtr
<SVGLength
> fromLength
= toSVGLength(fromValue
);
374 RefPtrWillBeRawPtr
<SVGLength
> toLength
= toSVGLength(toValue
);
375 RefPtrWillBeRawPtr
<SVGLength
> toAtEndOfDurationLength
= toSVGLength(toAtEndOfDurationValue
);
377 SVGLengthContext
lengthContext(contextElement
);
378 float animatedNumber
= value(lengthContext
);
379 animationElement
->animateAdditiveNumber(percentage
, repeatCount
, fromLength
->value(lengthContext
), toLength
->value(lengthContext
), toAtEndOfDurationLength
->value(lengthContext
), animatedNumber
);
381 ASSERT(unitMode() == lengthModeForAnimatedLengthAttribute(animationElement
->attributeName()));
382 m_unitType
= percentage
< 0.5 ? fromLength
->unitType() : toLength
->unitType();
383 setValue(animatedNumber
, lengthContext
);
386 float SVGLength::calculateDistance(PassRefPtrWillBeRawPtr
<SVGPropertyBase
> toValue
, SVGElement
* contextElement
)
388 SVGLengthContext
lengthContext(contextElement
);
389 RefPtrWillBeRawPtr
<SVGLength
> toLength
= toSVGLength(toValue
);
391 return fabsf(toLength
->value(lengthContext
) - value(lengthContext
));