Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / svg / SVGPreserveAspectRatio.cpp
1 /*
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>
5  *
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.
10  *
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.
15  *
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.
20  */
21
22 #include "config.h"
23 #include "core/svg/SVGPreserveAspectRatio.h"
24
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"
33
34 namespace blink {
35
36 SVGPreserveAspectRatio::SVGPreserveAspectRatio()
37 {
38     setDefault();
39 }
40
41 void SVGPreserveAspectRatio::setDefault()
42 {
43     m_align = SVG_PRESERVEASPECTRATIO_XMIDYMID;
44     m_meetOrSlice = SVG_MEETORSLICE_MEET;
45 }
46
47 PassRefPtr<SVGPreserveAspectRatio> SVGPreserveAspectRatio::clone() const
48 {
49     RefPtr<SVGPreserveAspectRatio> preserveAspectRatio = create();
50
51     preserveAspectRatio->m_align = m_align;
52     preserveAspectRatio->m_meetOrSlice = m_meetOrSlice;
53
54     return preserveAspectRatio.release();
55 }
56
57 template<typename CharType>
58 bool SVGPreserveAspectRatio::parseInternal(const CharType*& ptr, const CharType* end, bool validate)
59 {
60     SVGPreserveAspectRatioType align = SVG_PRESERVEASPECTRATIO_XMIDYMID;
61     SVGMeetOrSliceType meetOrSlice = SVG_MEETORSLICE_MEET;
62
63     setAlign(align);
64     setMeetOrSlice(meetOrSlice);
65
66     if (!skipOptionalSVGSpaces(ptr, end))
67         return false;
68
69     if (*ptr == 'd') {
70         if (!skipString(ptr, end, "defer"))
71             return false;
72
73         // FIXME: We just ignore the "defer" here.
74         if (ptr == end)
75             return true;
76
77         if (!skipOptionalSVGSpaces(ptr, end))
78             return false;
79     }
80
81     if (*ptr == 'n') {
82         if (!skipString(ptr, end, "none"))
83             return false;
84         align = SVG_PRESERVEASPECTRATIO_NONE;
85         skipOptionalSVGSpaces(ptr, end);
86     } else if (*ptr == 'x') {
87         if ((end - ptr) < 8)
88             return false;
89         if (ptr[1] != 'M' || ptr[4] != 'Y' || ptr[5] != 'M')
90             return false;
91         if (ptr[2] == 'i') {
92             if (ptr[3] == 'n') {
93                 if (ptr[6] == 'i') {
94                     if (ptr[7] == 'n')
95                         align = SVG_PRESERVEASPECTRATIO_XMINYMIN;
96                     else if (ptr[7] == 'd')
97                         align = SVG_PRESERVEASPECTRATIO_XMINYMID;
98                     else
99                         return false;
100                 } else if (ptr[6] == 'a' && ptr[7] == 'x') {
101                     align = SVG_PRESERVEASPECTRATIO_XMINYMAX;
102                 } else {
103                     return false;
104                 }
105             } else if (ptr[3] == 'd') {
106                 if (ptr[6] == 'i') {
107                     if (ptr[7] == 'n')
108                         align = SVG_PRESERVEASPECTRATIO_XMIDYMIN;
109                     else if (ptr[7] == 'd')
110                         align = SVG_PRESERVEASPECTRATIO_XMIDYMID;
111                     else
112                         return false;
113                 } else if (ptr[6] == 'a' && ptr[7] == 'x') {
114                     align = SVG_PRESERVEASPECTRATIO_XMIDYMAX;
115                 } else {
116                     return false;
117                 }
118             } else {
119                 return false;
120             }
121         } else if (ptr[2] == 'a' && ptr[3] == 'x') {
122             if (ptr[6] == 'i') {
123                 if (ptr[7] == 'n')
124                     align = SVG_PRESERVEASPECTRATIO_XMAXYMIN;
125                 else if (ptr[7] == 'd')
126                     align = SVG_PRESERVEASPECTRATIO_XMAXYMID;
127                 else
128                     return false;
129             } else if (ptr[6] == 'a' && ptr[7] == 'x') {
130                 align = SVG_PRESERVEASPECTRATIO_XMAXYMAX;
131             } else {
132                 return false;
133             }
134         } else {
135             return false;
136         }
137         ptr += 8;
138         skipOptionalSVGSpaces(ptr, end);
139     } else {
140         return false;
141     }
142
143     if (ptr < end) {
144         if (*ptr == 'm') {
145             if (!skipString(ptr, end, "meet"))
146                 return false;
147             skipOptionalSVGSpaces(ptr, end);
148         } else if (*ptr == 's') {
149             if (!skipString(ptr, end, "slice"))
150                 return false;
151             skipOptionalSVGSpaces(ptr, end);
152             if (align != SVG_PRESERVEASPECTRATIO_NONE)
153                 meetOrSlice = SVG_MEETORSLICE_SLICE;
154         }
155     }
156
157     if (end != ptr && validate)
158         return false;
159
160     setAlign(align);
161     setMeetOrSlice(meetOrSlice);
162
163     return true;
164 }
165
166 void SVGPreserveAspectRatio::setValueAsString(const String& string, ExceptionState& exceptionState)
167 {
168     setDefault();
169
170     if (string.isEmpty())
171         return;
172
173     bool valid = false;
174     if (string.is8Bit()) {
175         const LChar* ptr = string.characters8();
176         const LChar* end = ptr + string.length();
177         valid = parseInternal(ptr, end, true);
178     } else {
179         const UChar* ptr = string.characters16();
180         const UChar* end = ptr + string.length();
181         valid = parseInternal(ptr, end, true);
182     }
183
184     if (!valid) {
185         exceptionState.throwDOMException(SyntaxError, "The value provided ('" + string + "') is invalid.");
186     }
187 }
188
189 bool SVGPreserveAspectRatio::parse(const LChar*& ptr, const LChar* end, bool validate)
190 {
191     return parseInternal(ptr, end, validate);
192 }
193
194 bool SVGPreserveAspectRatio::parse(const UChar*& ptr, const UChar* end, bool validate)
195 {
196     return parseInternal(ptr, end, validate);
197 }
198
199 void SVGPreserveAspectRatio::transformRect(FloatRect& destRect, FloatRect& srcRect)
200 {
201     if (m_align == SVG_PRESERVEASPECTRATIO_NONE)
202         return;
203
204     FloatSize imageSize = srcRect.size();
205     float origDestWidth = destRect.width();
206     float origDestHeight = destRect.height();
207     switch (m_meetOrSlice) {
208     case SVGPreserveAspectRatio::SVG_MEETORSLICE_UNKNOWN:
209         break;
210     case SVGPreserveAspectRatio::SVG_MEETORSLICE_MEET: {
211         float widthToHeightMultiplier = srcRect.height() / srcRect.width();
212         if (origDestHeight > origDestWidth * widthToHeightMultiplier) {
213             destRect.setHeight(origDestWidth * widthToHeightMultiplier);
214             switch (m_align) {
215             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID:
216             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
217             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
218                 destRect.setY(destRect.y() + origDestHeight / 2 - destRect.height() / 2);
219                 break;
220             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX:
221             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
222             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
223                 destRect.setY(destRect.y() + origDestHeight - destRect.height());
224                 break;
225             default:
226                 break;
227             }
228         }
229         if (origDestWidth > origDestHeight / widthToHeightMultiplier) {
230             destRect.setWidth(origDestHeight / widthToHeightMultiplier);
231             switch (m_align) {
232             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN:
233             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
234             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
235                 destRect.setX(destRect.x() + origDestWidth / 2 - destRect.width() / 2);
236                 break;
237             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN:
238             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
239             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
240                 destRect.setX(destRect.x() + origDestWidth - destRect.width());
241                 break;
242             default:
243                 break;
244             }
245         }
246         break;
247     }
248     case SVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE: {
249         float widthToHeightMultiplier = srcRect.height() / srcRect.width();
250         // if the destination height is less than the height of the image we'll be drawing
251         if (origDestHeight < origDestWidth * widthToHeightMultiplier) {
252             float destToSrcMultiplier = srcRect.width() / destRect.width();
253             srcRect.setHeight(destRect.height() * destToSrcMultiplier);
254             switch (m_align) {
255             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID:
256             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
257             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
258                 srcRect.setY(srcRect.y() + imageSize.height() / 2 - srcRect.height() / 2);
259                 break;
260             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX:
261             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
262             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
263                 srcRect.setY(srcRect.y() + imageSize.height() - srcRect.height());
264                 break;
265             default:
266                 break;
267             }
268         }
269         // if the destination width is less than the width of the image we'll be drawing
270         if (origDestWidth < origDestHeight / widthToHeightMultiplier) {
271             float destToSrcMultiplier = srcRect.height() / destRect.height();
272             srcRect.setWidth(destRect.width() * destToSrcMultiplier);
273             switch (m_align) {
274             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN:
275             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
276             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
277                 srcRect.setX(srcRect.x() + imageSize.width() / 2 - srcRect.width() / 2);
278                 break;
279             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN:
280             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
281             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
282                 srcRect.setX(srcRect.x() + imageSize.width() - srcRect.width());
283                 break;
284             default:
285                 break;
286             }
287         }
288         break;
289     }
290     }
291 }
292
293 AffineTransform SVGPreserveAspectRatio::getCTM(float logicalX, float logicalY, float logicalWidth, float logicalHeight, float physicalWidth, float physicalHeight) const
294 {
295     ASSERT(logicalWidth);
296     ASSERT(logicalHeight);
297     ASSERT(physicalWidth);
298     ASSERT(physicalHeight);
299
300     AffineTransform transform;
301     if (m_align == SVG_PRESERVEASPECTRATIO_UNKNOWN)
302         return transform;
303
304     double extendedLogicalX = logicalX;
305     double extendedLogicalY = logicalY;
306     double extendedLogicalWidth = logicalWidth;
307     double extendedLogicalHeight = logicalHeight;
308     double extendedPhysicalWidth = physicalWidth;
309     double extendedPhysicalHeight = physicalHeight;
310     double logicalRatio = extendedLogicalWidth / extendedLogicalHeight;
311     double physicalRatio = extendedPhysicalWidth / extendedPhysicalHeight;
312
313     if (m_align == SVG_PRESERVEASPECTRATIO_NONE) {
314         transform.scaleNonUniform(extendedPhysicalWidth / extendedLogicalWidth, extendedPhysicalHeight / extendedLogicalHeight);
315         transform.translate(-extendedLogicalX, -extendedLogicalY);
316         return transform;
317     }
318
319     if ((logicalRatio < physicalRatio && (m_meetOrSlice == SVG_MEETORSLICE_MEET)) || (logicalRatio >= physicalRatio && (m_meetOrSlice == SVG_MEETORSLICE_SLICE))) {
320         transform.scaleNonUniform(extendedPhysicalHeight / extendedLogicalHeight, extendedPhysicalHeight / extendedLogicalHeight);
321
322         if (m_align == SVG_PRESERVEASPECTRATIO_XMINYMIN || m_align == SVG_PRESERVEASPECTRATIO_XMINYMID || m_align == SVG_PRESERVEASPECTRATIO_XMINYMAX)
323             transform.translate(-extendedLogicalX, -extendedLogicalY);
324         else if (m_align == SVG_PRESERVEASPECTRATIO_XMIDYMIN || m_align == SVG_PRESERVEASPECTRATIO_XMIDYMID || m_align == SVG_PRESERVEASPECTRATIO_XMIDYMAX)
325             transform.translate(-extendedLogicalX - (extendedLogicalWidth - extendedPhysicalWidth * extendedLogicalHeight / extendedPhysicalHeight) / 2, -extendedLogicalY);
326         else
327             transform.translate(-extendedLogicalX - (extendedLogicalWidth - extendedPhysicalWidth * extendedLogicalHeight / extendedPhysicalHeight), -extendedLogicalY);
328
329         return transform;
330     }
331
332     transform.scaleNonUniform(extendedPhysicalWidth / extendedLogicalWidth, extendedPhysicalWidth / extendedLogicalWidth);
333
334     if (m_align == SVG_PRESERVEASPECTRATIO_XMINYMIN || m_align == SVG_PRESERVEASPECTRATIO_XMIDYMIN || m_align == SVG_PRESERVEASPECTRATIO_XMAXYMIN)
335         transform.translate(-extendedLogicalX, -extendedLogicalY);
336     else if (m_align == SVG_PRESERVEASPECTRATIO_XMINYMID || m_align == SVG_PRESERVEASPECTRATIO_XMIDYMID || m_align == SVG_PRESERVEASPECTRATIO_XMAXYMID)
337         transform.translate(-extendedLogicalX, -extendedLogicalY - (extendedLogicalHeight - extendedPhysicalHeight * extendedLogicalWidth / extendedPhysicalWidth) / 2);
338     else
339         transform.translate(-extendedLogicalX, -extendedLogicalY - (extendedLogicalHeight - extendedPhysicalHeight * extendedLogicalWidth / extendedPhysicalWidth));
340
341     return transform;
342 }
343
344 String SVGPreserveAspectRatio::valueAsString() const
345 {
346     String alignType;
347
348     switch (m_align) {
349     case SVG_PRESERVEASPECTRATIO_NONE:
350         alignType = "none";
351         break;
352     case SVG_PRESERVEASPECTRATIO_XMINYMIN:
353         alignType = "xMinYMin";
354         break;
355     case SVG_PRESERVEASPECTRATIO_XMIDYMIN:
356         alignType = "xMidYMin";
357         break;
358     case SVG_PRESERVEASPECTRATIO_XMAXYMIN:
359         alignType = "xMaxYMin";
360         break;
361     case SVG_PRESERVEASPECTRATIO_XMINYMID:
362         alignType = "xMinYMid";
363         break;
364     case SVG_PRESERVEASPECTRATIO_XMIDYMID:
365         alignType = "xMidYMid";
366         break;
367     case SVG_PRESERVEASPECTRATIO_XMAXYMID:
368         alignType = "xMaxYMid";
369         break;
370     case SVG_PRESERVEASPECTRATIO_XMINYMAX:
371         alignType = "xMinYMax";
372         break;
373     case SVG_PRESERVEASPECTRATIO_XMIDYMAX:
374         alignType = "xMidYMax";
375         break;
376     case SVG_PRESERVEASPECTRATIO_XMAXYMAX:
377         alignType = "xMaxYMax";
378         break;
379     case SVG_PRESERVEASPECTRATIO_UNKNOWN:
380         alignType = "unknown";
381         break;
382     };
383
384     switch (m_meetOrSlice) {
385     default:
386     case SVG_MEETORSLICE_UNKNOWN:
387         return alignType;
388     case SVG_MEETORSLICE_MEET:
389         return alignType + " meet";
390     case SVG_MEETORSLICE_SLICE:
391         return alignType + " slice";
392     }
393 }
394
395 void SVGPreserveAspectRatio::add(PassRefPtrWillBeRawPtr<SVGPropertyBase> other, SVGElement*)
396 {
397     ASSERT_NOT_REACHED();
398 }
399
400 void SVGPreserveAspectRatio::calculateAnimatedValue(SVGAnimationElement* animationElement, float percentage, unsigned repeatCount, PassRefPtr<SVGPropertyBase> fromValue, PassRefPtr<SVGPropertyBase> toValue, PassRefPtr<SVGPropertyBase>, SVGElement*)
401 {
402     ASSERT(animationElement);
403
404     bool useToValue;
405     animationElement->animateDiscreteType(percentage, false, true, useToValue);
406
407     RefPtr<SVGPreserveAspectRatio> preserveAspectRatioToUse = useToValue ? toSVGPreserveAspectRatio(toValue) : toSVGPreserveAspectRatio(fromValue);
408
409     m_align = preserveAspectRatioToUse->m_align;
410     m_meetOrSlice = preserveAspectRatioToUse->m_meetOrSlice;
411 }
412
413 float SVGPreserveAspectRatio::calculateDistance(PassRefPtr<SVGPropertyBase> toValue, SVGElement* contextElement)
414 {
415     // No paced animations for SVGPreserveAspectRatio.
416     return -1;
417 }
418
419 }