2 * Copyright (C) 2004, 2005, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
4 * Copyright (C) 2010 Dirk Schulze <krit@webkit.org>
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.
23 #include "core/svg/SVGPreserveAspectRatio.h"
25 #include "bindings/core/v8/ExceptionState.h"
26 #include "bindings/core/v8/ExceptionStatePlaceholder.h"
27 #include "core/dom/ExceptionCode.h"
28 #include "core/svg/SVGAnimationElement.h"
29 #include "core/svg/SVGParserUtilities.h"
30 #include "platform/geometry/FloatRect.h"
31 #include "platform/transforms/AffineTransform.h"
32 #include "wtf/text/WTFString.h"
36 SVGPreserveAspectRatio::SVGPreserveAspectRatio()
41 void SVGPreserveAspectRatio::setDefault()
43 m_align
= SVG_PRESERVEASPECTRATIO_XMIDYMID
;
44 m_meetOrSlice
= SVG_MEETORSLICE_MEET
;
47 PassRefPtrWillBeRawPtr
<SVGPreserveAspectRatio
> SVGPreserveAspectRatio::clone() const
49 RefPtrWillBeRawPtr
<SVGPreserveAspectRatio
> preserveAspectRatio
= create();
51 preserveAspectRatio
->m_align
= m_align
;
52 preserveAspectRatio
->m_meetOrSlice
= m_meetOrSlice
;
54 return preserveAspectRatio
.release();
57 template<typename CharType
>
58 bool SVGPreserveAspectRatio::parseInternal(const CharType
*& ptr
, const CharType
* end
, bool validate
)
60 SVGPreserveAspectRatioType align
= SVG_PRESERVEASPECTRATIO_XMIDYMID
;
61 SVGMeetOrSliceType meetOrSlice
= SVG_MEETORSLICE_MEET
;
64 setMeetOrSlice(meetOrSlice
);
66 if (!skipOptionalSVGSpaces(ptr
, end
))
70 if (!skipString(ptr
, end
, "none"))
72 align
= SVG_PRESERVEASPECTRATIO_NONE
;
73 skipOptionalSVGSpaces(ptr
, end
);
74 } else if (*ptr
== 'x') {
77 if (ptr
[1] != 'M' || ptr
[4] != 'Y' || ptr
[5] != 'M')
83 align
= SVG_PRESERVEASPECTRATIO_XMINYMIN
;
84 else if (ptr
[7] == 'd')
85 align
= SVG_PRESERVEASPECTRATIO_XMINYMID
;
88 } else if (ptr
[6] == 'a' && ptr
[7] == 'x') {
89 align
= SVG_PRESERVEASPECTRATIO_XMINYMAX
;
93 } else if (ptr
[3] == 'd') {
96 align
= SVG_PRESERVEASPECTRATIO_XMIDYMIN
;
97 else if (ptr
[7] == 'd')
98 align
= SVG_PRESERVEASPECTRATIO_XMIDYMID
;
101 } else if (ptr
[6] == 'a' && ptr
[7] == 'x') {
102 align
= SVG_PRESERVEASPECTRATIO_XMIDYMAX
;
109 } else if (ptr
[2] == 'a' && ptr
[3] == 'x') {
112 align
= SVG_PRESERVEASPECTRATIO_XMAXYMIN
;
113 else if (ptr
[7] == 'd')
114 align
= SVG_PRESERVEASPECTRATIO_XMAXYMID
;
117 } else if (ptr
[6] == 'a' && ptr
[7] == 'x') {
118 align
= SVG_PRESERVEASPECTRATIO_XMAXYMAX
;
126 skipOptionalSVGSpaces(ptr
, end
);
133 if (!skipString(ptr
, end
, "meet"))
135 skipOptionalSVGSpaces(ptr
, end
);
136 } else if (*ptr
== 's') {
137 if (!skipString(ptr
, end
, "slice"))
139 skipOptionalSVGSpaces(ptr
, end
);
140 if (align
!= SVG_PRESERVEASPECTRATIO_NONE
)
141 meetOrSlice
= SVG_MEETORSLICE_SLICE
;
145 if (end
!= ptr
&& validate
)
149 setMeetOrSlice(meetOrSlice
);
154 void SVGPreserveAspectRatio::setValueAsString(const String
& string
, ExceptionState
& exceptionState
)
158 if (string
.isEmpty())
162 if (string
.is8Bit()) {
163 const LChar
* ptr
= string
.characters8();
164 const LChar
* end
= ptr
+ string
.length();
165 valid
= parseInternal(ptr
, end
, true);
167 const UChar
* ptr
= string
.characters16();
168 const UChar
* end
= ptr
+ string
.length();
169 valid
= parseInternal(ptr
, end
, true);
173 exceptionState
.throwDOMException(SyntaxError
, "The value provided ('" + string
+ "') is invalid.");
177 bool SVGPreserveAspectRatio::parse(const LChar
*& ptr
, const LChar
* end
, bool validate
)
179 return parseInternal(ptr
, end
, validate
);
182 bool SVGPreserveAspectRatio::parse(const UChar
*& ptr
, const UChar
* end
, bool validate
)
184 return parseInternal(ptr
, end
, validate
);
187 void SVGPreserveAspectRatio::transformRect(FloatRect
& destRect
, FloatRect
& srcRect
)
189 if (m_align
== SVG_PRESERVEASPECTRATIO_NONE
)
192 FloatSize imageSize
= srcRect
.size();
193 float origDestWidth
= destRect
.width();
194 float origDestHeight
= destRect
.height();
195 switch (m_meetOrSlice
) {
196 case SVGPreserveAspectRatio::SVG_MEETORSLICE_UNKNOWN
:
198 case SVGPreserveAspectRatio::SVG_MEETORSLICE_MEET
: {
199 float widthToHeightMultiplier
= srcRect
.height() / srcRect
.width();
200 if (origDestHeight
> origDestWidth
* widthToHeightMultiplier
) {
201 destRect
.setHeight(origDestWidth
* widthToHeightMultiplier
);
203 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID
:
204 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID
:
205 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID
:
206 destRect
.setY(destRect
.y() + origDestHeight
/ 2 - destRect
.height() / 2);
208 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX
:
209 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX
:
210 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX
:
211 destRect
.setY(destRect
.y() + origDestHeight
- destRect
.height());
217 if (origDestWidth
> origDestHeight
/ widthToHeightMultiplier
) {
218 destRect
.setWidth(origDestHeight
/ widthToHeightMultiplier
);
220 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN
:
221 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID
:
222 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX
:
223 destRect
.setX(destRect
.x() + origDestWidth
/ 2 - destRect
.width() / 2);
225 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN
:
226 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID
:
227 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX
:
228 destRect
.setX(destRect
.x() + origDestWidth
- destRect
.width());
236 case SVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE
: {
237 float widthToHeightMultiplier
= srcRect
.height() / srcRect
.width();
238 // if the destination height is less than the height of the image we'll be drawing
239 if (origDestHeight
< origDestWidth
* widthToHeightMultiplier
) {
240 float destToSrcMultiplier
= srcRect
.width() / destRect
.width();
241 srcRect
.setHeight(destRect
.height() * destToSrcMultiplier
);
243 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID
:
244 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID
:
245 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID
:
246 srcRect
.setY(srcRect
.y() + imageSize
.height() / 2 - srcRect
.height() / 2);
248 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX
:
249 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX
:
250 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX
:
251 srcRect
.setY(srcRect
.y() + imageSize
.height() - srcRect
.height());
257 // if the destination width is less than the width of the image we'll be drawing
258 if (origDestWidth
< origDestHeight
/ widthToHeightMultiplier
) {
259 float destToSrcMultiplier
= srcRect
.height() / destRect
.height();
260 srcRect
.setWidth(destRect
.width() * destToSrcMultiplier
);
262 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN
:
263 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID
:
264 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX
:
265 srcRect
.setX(srcRect
.x() + imageSize
.width() / 2 - srcRect
.width() / 2);
267 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN
:
268 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID
:
269 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX
:
270 srcRect
.setX(srcRect
.x() + imageSize
.width() - srcRect
.width());
281 AffineTransform
SVGPreserveAspectRatio::getCTM(float logicalX
, float logicalY
, float logicalWidth
, float logicalHeight
, float physicalWidth
, float physicalHeight
) const
283 ASSERT(logicalWidth
);
284 ASSERT(logicalHeight
);
285 ASSERT(physicalWidth
);
286 ASSERT(physicalHeight
);
288 AffineTransform transform
;
289 if (m_align
== SVG_PRESERVEASPECTRATIO_UNKNOWN
)
292 double extendedLogicalX
= logicalX
;
293 double extendedLogicalY
= logicalY
;
294 double extendedLogicalWidth
= logicalWidth
;
295 double extendedLogicalHeight
= logicalHeight
;
296 double extendedPhysicalWidth
= physicalWidth
;
297 double extendedPhysicalHeight
= physicalHeight
;
298 double logicalRatio
= extendedLogicalWidth
/ extendedLogicalHeight
;
299 double physicalRatio
= extendedPhysicalWidth
/ extendedPhysicalHeight
;
301 if (m_align
== SVG_PRESERVEASPECTRATIO_NONE
) {
302 transform
.scaleNonUniform(extendedPhysicalWidth
/ extendedLogicalWidth
, extendedPhysicalHeight
/ extendedLogicalHeight
);
303 transform
.translate(-extendedLogicalX
, -extendedLogicalY
);
307 if ((logicalRatio
< physicalRatio
&& (m_meetOrSlice
== SVG_MEETORSLICE_MEET
)) || (logicalRatio
>= physicalRatio
&& (m_meetOrSlice
== SVG_MEETORSLICE_SLICE
))) {
308 transform
.scaleNonUniform(extendedPhysicalHeight
/ extendedLogicalHeight
, extendedPhysicalHeight
/ extendedLogicalHeight
);
310 if (m_align
== SVG_PRESERVEASPECTRATIO_XMINYMIN
|| m_align
== SVG_PRESERVEASPECTRATIO_XMINYMID
|| m_align
== SVG_PRESERVEASPECTRATIO_XMINYMAX
)
311 transform
.translate(-extendedLogicalX
, -extendedLogicalY
);
312 else if (m_align
== SVG_PRESERVEASPECTRATIO_XMIDYMIN
|| m_align
== SVG_PRESERVEASPECTRATIO_XMIDYMID
|| m_align
== SVG_PRESERVEASPECTRATIO_XMIDYMAX
)
313 transform
.translate(-extendedLogicalX
- (extendedLogicalWidth
- extendedPhysicalWidth
* extendedLogicalHeight
/ extendedPhysicalHeight
) / 2, -extendedLogicalY
);
315 transform
.translate(-extendedLogicalX
- (extendedLogicalWidth
- extendedPhysicalWidth
* extendedLogicalHeight
/ extendedPhysicalHeight
), -extendedLogicalY
);
320 transform
.scaleNonUniform(extendedPhysicalWidth
/ extendedLogicalWidth
, extendedPhysicalWidth
/ extendedLogicalWidth
);
322 if (m_align
== SVG_PRESERVEASPECTRATIO_XMINYMIN
|| m_align
== SVG_PRESERVEASPECTRATIO_XMIDYMIN
|| m_align
== SVG_PRESERVEASPECTRATIO_XMAXYMIN
)
323 transform
.translate(-extendedLogicalX
, -extendedLogicalY
);
324 else if (m_align
== SVG_PRESERVEASPECTRATIO_XMINYMID
|| m_align
== SVG_PRESERVEASPECTRATIO_XMIDYMID
|| m_align
== SVG_PRESERVEASPECTRATIO_XMAXYMID
)
325 transform
.translate(-extendedLogicalX
, -extendedLogicalY
- (extendedLogicalHeight
- extendedPhysicalHeight
* extendedLogicalWidth
/ extendedPhysicalWidth
) / 2);
327 transform
.translate(-extendedLogicalX
, -extendedLogicalY
- (extendedLogicalHeight
- extendedPhysicalHeight
* extendedLogicalWidth
/ extendedPhysicalWidth
));
332 String
SVGPreserveAspectRatio::valueAsString() const
337 case SVG_PRESERVEASPECTRATIO_NONE
:
340 case SVG_PRESERVEASPECTRATIO_XMINYMIN
:
341 alignType
= "xMinYMin";
343 case SVG_PRESERVEASPECTRATIO_XMIDYMIN
:
344 alignType
= "xMidYMin";
346 case SVG_PRESERVEASPECTRATIO_XMAXYMIN
:
347 alignType
= "xMaxYMin";
349 case SVG_PRESERVEASPECTRATIO_XMINYMID
:
350 alignType
= "xMinYMid";
352 case SVG_PRESERVEASPECTRATIO_XMIDYMID
:
353 alignType
= "xMidYMid";
355 case SVG_PRESERVEASPECTRATIO_XMAXYMID
:
356 alignType
= "xMaxYMid";
358 case SVG_PRESERVEASPECTRATIO_XMINYMAX
:
359 alignType
= "xMinYMax";
361 case SVG_PRESERVEASPECTRATIO_XMIDYMAX
:
362 alignType
= "xMidYMax";
364 case SVG_PRESERVEASPECTRATIO_XMAXYMAX
:
365 alignType
= "xMaxYMax";
367 case SVG_PRESERVEASPECTRATIO_UNKNOWN
:
368 alignType
= "unknown";
372 switch (m_meetOrSlice
) {
374 case SVG_MEETORSLICE_UNKNOWN
:
376 case SVG_MEETORSLICE_MEET
:
377 return alignType
+ " meet";
378 case SVG_MEETORSLICE_SLICE
:
379 return alignType
+ " slice";
383 void SVGPreserveAspectRatio::add(PassRefPtrWillBeRawPtr
<SVGPropertyBase
> other
, SVGElement
*)
385 ASSERT_NOT_REACHED();
388 void SVGPreserveAspectRatio::calculateAnimatedValue(SVGAnimationElement
* animationElement
, float percentage
, unsigned repeatCount
, PassRefPtrWillBeRawPtr
<SVGPropertyBase
> fromValue
, PassRefPtrWillBeRawPtr
<SVGPropertyBase
> toValue
, PassRefPtrWillBeRawPtr
<SVGPropertyBase
>, SVGElement
*)
390 ASSERT(animationElement
);
393 animationElement
->animateDiscreteType(percentage
, false, true, useToValue
);
395 RefPtrWillBeRawPtr
<SVGPreserveAspectRatio
> preserveAspectRatioToUse
= useToValue
? toSVGPreserveAspectRatio(toValue
) : toSVGPreserveAspectRatio(fromValue
);
397 m_align
= preserveAspectRatioToUse
->m_align
;
398 m_meetOrSlice
= preserveAspectRatioToUse
->m_meetOrSlice
;
401 float SVGPreserveAspectRatio::calculateDistance(PassRefPtrWillBeRawPtr
<SVGPropertyBase
> toValue
, SVGElement
* contextElement
)
403 // No paced animations for SVGPreserveAspectRatio.