• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/v8/ExceptionState.h"
26 #include "core/dom/ExceptionCode.h"
27 #include "core/svg/SVGParserUtilities.h"
28 #include "platform/geometry/FloatRect.h"
29 #include "platform/transforms/AffineTransform.h"
30 #include "wtf/text/WTFString.h"
31 
32 namespace WebCore {
33 
SVGPreserveAspectRatio()34 SVGPreserveAspectRatio::SVGPreserveAspectRatio()
35     : m_align(SVG_PRESERVEASPECTRATIO_XMIDYMID)
36     , m_meetOrSlice(SVG_MEETORSLICE_MEET)
37 {
38 }
39 
setAlign(unsigned short align,ExceptionState & exceptionState)40 void SVGPreserveAspectRatio::setAlign(unsigned short align, ExceptionState& exceptionState)
41 {
42     if (align == SVG_PRESERVEASPECTRATIO_UNKNOWN || align > SVG_PRESERVEASPECTRATIO_XMAXYMAX) {
43         exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError);
44         return;
45     }
46 
47     m_align = static_cast<SVGPreserveAspectRatioType>(align);
48 }
49 
setMeetOrSlice(unsigned short meetOrSlice,ExceptionState & exceptionState)50 void SVGPreserveAspectRatio::setMeetOrSlice(unsigned short meetOrSlice, ExceptionState& exceptionState)
51 {
52     if (meetOrSlice == SVG_MEETORSLICE_UNKNOWN || meetOrSlice > SVG_MEETORSLICE_SLICE) {
53         exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError);
54         return;
55     }
56 
57     m_meetOrSlice = static_cast<SVGMeetOrSliceType>(meetOrSlice);
58 }
59 
60 template<typename CharType>
parseInternal(const CharType * & ptr,const CharType * end,bool validate)61 bool SVGPreserveAspectRatio::parseInternal(const CharType*& ptr, const CharType* end, bool validate)
62 {
63     // FIXME: Rewrite this parser, without gotos!
64     if (!skipOptionalSVGSpaces(ptr, end))
65         goto bailOut;
66 
67     if (*ptr == 'd') {
68         if (!skipString(ptr, end, "defer"))
69             goto bailOut;
70 
71         // FIXME: We just ignore the "defer" here.
72         if (ptr == end)
73             return true;
74 
75         if (!skipOptionalSVGSpaces(ptr, end))
76             goto bailOut;
77     }
78 
79     if (*ptr == 'n') {
80         if (!skipString(ptr, end, "none"))
81             goto bailOut;
82         m_align = SVG_PRESERVEASPECTRATIO_NONE;
83         skipOptionalSVGSpaces(ptr, end);
84     } else if (*ptr == 'x') {
85         if ((end - ptr) < 8)
86             goto bailOut;
87         if (ptr[1] != 'M' || ptr[4] != 'Y' || ptr[5] != 'M')
88             goto bailOut;
89         if (ptr[2] == 'i') {
90             if (ptr[3] == 'n') {
91                 if (ptr[6] == 'i') {
92                     if (ptr[7] == 'n')
93                         m_align = SVG_PRESERVEASPECTRATIO_XMINYMIN;
94                     else if (ptr[7] == 'd')
95                         m_align = SVG_PRESERVEASPECTRATIO_XMINYMID;
96                     else
97                         goto bailOut;
98                 } else if (ptr[6] == 'a' && ptr[7] == 'x')
99                      m_align = SVG_PRESERVEASPECTRATIO_XMINYMAX;
100                 else
101                      goto bailOut;
102             } else if (ptr[3] == 'd') {
103                 if (ptr[6] == 'i') {
104                     if (ptr[7] == 'n')
105                         m_align = SVG_PRESERVEASPECTRATIO_XMIDYMIN;
106                     else if (ptr[7] == 'd')
107                         m_align = SVG_PRESERVEASPECTRATIO_XMIDYMID;
108                     else
109                         goto bailOut;
110                 } else if (ptr[6] == 'a' && ptr[7] == 'x')
111                     m_align = SVG_PRESERVEASPECTRATIO_XMIDYMAX;
112                 else
113                     goto bailOut;
114             } else
115                 goto bailOut;
116         } else if (ptr[2] == 'a' && ptr[3] == 'x') {
117             if (ptr[6] == 'i') {
118                 if (ptr[7] == 'n')
119                     m_align = SVG_PRESERVEASPECTRATIO_XMAXYMIN;
120                 else if (ptr[7] == 'd')
121                     m_align = SVG_PRESERVEASPECTRATIO_XMAXYMID;
122                 else
123                     goto bailOut;
124             } else if (ptr[6] == 'a' && ptr[7] == 'x')
125                 m_align = SVG_PRESERVEASPECTRATIO_XMAXYMAX;
126             else
127                 goto bailOut;
128         } else
129             goto bailOut;
130         ptr += 8;
131         skipOptionalSVGSpaces(ptr, end);
132     } else
133         goto bailOut;
134 
135     if (ptr < end) {
136         if (*ptr == 'm') {
137             if (!skipString(ptr, end, "meet"))
138                 goto bailOut;
139             skipOptionalSVGSpaces(ptr, end);
140         } else if (*ptr == 's') {
141             if (!skipString(ptr, end, "slice"))
142                 goto bailOut;
143             skipOptionalSVGSpaces(ptr, end);
144             if (m_align != SVG_PRESERVEASPECTRATIO_NONE)
145                 m_meetOrSlice = SVG_MEETORSLICE_SLICE;
146         }
147     }
148 
149     if (end != ptr && validate) {
150 bailOut:
151         m_align = SVG_PRESERVEASPECTRATIO_XMIDYMID;
152         m_meetOrSlice = SVG_MEETORSLICE_MEET;
153         return false;
154     }
155     return true;
156 }
157 
parse(const String & string)158 void SVGPreserveAspectRatio::parse(const String& string)
159 {
160     if (string.isEmpty()) {
161         const LChar* ptr = 0;
162         parseInternal(ptr, ptr, true);
163     } else if (string.is8Bit()) {
164         const LChar* ptr = string.characters8();
165         const LChar* end = ptr + string.length();
166         parseInternal(ptr, end, true);
167     } else {
168         const UChar* ptr = string.characters16();
169         const UChar* end = ptr + string.length();
170         parseInternal(ptr, end, true);
171     }
172 }
173 
parse(const LChar * & ptr,const LChar * end,bool validate)174 bool SVGPreserveAspectRatio::parse(const LChar*& ptr, const LChar* end, bool validate)
175 {
176     return parseInternal(ptr, end, validate);
177 }
178 
parse(const UChar * & ptr,const UChar * end,bool validate)179 bool SVGPreserveAspectRatio::parse(const UChar*& ptr, const UChar* end, bool validate)
180 {
181     return parseInternal(ptr, end, validate);
182 }
183 
transformRect(FloatRect & destRect,FloatRect & srcRect)184 void SVGPreserveAspectRatio::transformRect(FloatRect& destRect, FloatRect& srcRect)
185 {
186     if (m_align == SVG_PRESERVEASPECTRATIO_NONE)
187         return;
188 
189     FloatSize imageSize = srcRect.size();
190     float origDestWidth = destRect.width();
191     float origDestHeight = destRect.height();
192     switch (m_meetOrSlice) {
193     case SVGPreserveAspectRatio::SVG_MEETORSLICE_UNKNOWN:
194         break;
195     case SVGPreserveAspectRatio::SVG_MEETORSLICE_MEET: {
196         float widthToHeightMultiplier = srcRect.height() / srcRect.width();
197         if (origDestHeight > origDestWidth * widthToHeightMultiplier) {
198             destRect.setHeight(origDestWidth * widthToHeightMultiplier);
199             switch (m_align) {
200             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID:
201             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
202             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
203                 destRect.setY(destRect.y() + origDestHeight / 2 - destRect.height() / 2);
204                 break;
205             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX:
206             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
207             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
208                 destRect.setY(destRect.y() + origDestHeight - destRect.height());
209                 break;
210             default:
211                 break;
212             }
213         }
214         if (origDestWidth > origDestHeight / widthToHeightMultiplier) {
215             destRect.setWidth(origDestHeight / widthToHeightMultiplier);
216             switch (m_align) {
217             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN:
218             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
219             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
220                 destRect.setX(destRect.x() + origDestWidth / 2 - destRect.width() / 2);
221                 break;
222             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN:
223             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
224             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
225                 destRect.setX(destRect.x() + origDestWidth - destRect.width());
226                 break;
227             default:
228                 break;
229             }
230         }
231         break;
232     }
233     case SVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE: {
234         float widthToHeightMultiplier = srcRect.height() / srcRect.width();
235         // if the destination height is less than the height of the image we'll be drawing
236         if (origDestHeight < origDestWidth * widthToHeightMultiplier) {
237             float destToSrcMultiplier = srcRect.width() / destRect.width();
238             srcRect.setHeight(destRect.height() * destToSrcMultiplier);
239             switch (m_align) {
240             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID:
241             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
242             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
243                 srcRect.setY(srcRect.y() + imageSize.height() / 2 - srcRect.height() / 2);
244                 break;
245             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX:
246             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
247             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
248                 srcRect.setY(srcRect.y() + imageSize.height() - srcRect.height());
249                 break;
250             default:
251                 break;
252             }
253         }
254         // if the destination width is less than the width of the image we'll be drawing
255         if (origDestWidth < origDestHeight / widthToHeightMultiplier) {
256             float destToSrcMultiplier = srcRect.height() / destRect.height();
257             srcRect.setWidth(destRect.width() * destToSrcMultiplier);
258             switch (m_align) {
259             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN:
260             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
261             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
262                 srcRect.setX(srcRect.x() + imageSize.width() / 2 - srcRect.width() / 2);
263                 break;
264             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN:
265             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
266             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
267                 srcRect.setX(srcRect.x() + imageSize.width() - srcRect.width());
268                 break;
269             default:
270                 break;
271             }
272         }
273         break;
274     }
275     }
276 }
277 
getCTM(float logicalX,float logicalY,float logicalWidth,float logicalHeight,float physicalWidth,float physicalHeight) const278 AffineTransform SVGPreserveAspectRatio::getCTM(float logicalX, float logicalY, float logicalWidth, float logicalHeight, float physicalWidth, float physicalHeight) const
279 {
280     AffineTransform transform;
281     if (m_align == SVG_PRESERVEASPECTRATIO_UNKNOWN)
282         return transform;
283 
284     double extendedLogicalX = logicalX;
285     double extendedLogicalY = logicalY;
286     double extendedLogicalWidth = logicalWidth;
287     double extendedLogicalHeight = logicalHeight;
288     double extendedPhysicalWidth = physicalWidth;
289     double extendedPhysicalHeight = physicalHeight;
290     double logicalRatio = extendedLogicalWidth / extendedLogicalHeight;
291     double physicalRatio = extendedPhysicalWidth / extendedPhysicalHeight;
292 
293     if (m_align == SVG_PRESERVEASPECTRATIO_NONE) {
294         transform.scaleNonUniform(extendedPhysicalWidth / extendedLogicalWidth, extendedPhysicalHeight / extendedLogicalHeight);
295         transform.translate(-extendedLogicalX, -extendedLogicalY);
296         return transform;
297     }
298 
299     if ((logicalRatio < physicalRatio && (m_meetOrSlice == SVG_MEETORSLICE_MEET)) || (logicalRatio >= physicalRatio && (m_meetOrSlice == SVG_MEETORSLICE_SLICE))) {
300         transform.scaleNonUniform(extendedPhysicalHeight / extendedLogicalHeight, extendedPhysicalHeight / extendedLogicalHeight);
301 
302         if (m_align == SVG_PRESERVEASPECTRATIO_XMINYMIN || m_align == SVG_PRESERVEASPECTRATIO_XMINYMID || m_align == SVG_PRESERVEASPECTRATIO_XMINYMAX)
303             transform.translate(-extendedLogicalX, -extendedLogicalY);
304         else if (m_align == SVG_PRESERVEASPECTRATIO_XMIDYMIN || m_align == SVG_PRESERVEASPECTRATIO_XMIDYMID || m_align == SVG_PRESERVEASPECTRATIO_XMIDYMAX)
305             transform.translate(-extendedLogicalX - (extendedLogicalWidth - extendedPhysicalWidth * extendedLogicalHeight / extendedPhysicalHeight) / 2, -extendedLogicalY);
306         else
307             transform.translate(-extendedLogicalX - (extendedLogicalWidth - extendedPhysicalWidth * extendedLogicalHeight / extendedPhysicalHeight), -extendedLogicalY);
308 
309         return transform;
310     }
311 
312     transform.scaleNonUniform(extendedPhysicalWidth / extendedLogicalWidth, extendedPhysicalWidth / extendedLogicalWidth);
313 
314     if (m_align == SVG_PRESERVEASPECTRATIO_XMINYMIN || m_align == SVG_PRESERVEASPECTRATIO_XMIDYMIN || m_align == SVG_PRESERVEASPECTRATIO_XMAXYMIN)
315         transform.translate(-extendedLogicalX, -extendedLogicalY);
316     else if (m_align == SVG_PRESERVEASPECTRATIO_XMINYMID || m_align == SVG_PRESERVEASPECTRATIO_XMIDYMID || m_align == SVG_PRESERVEASPECTRATIO_XMAXYMID)
317         transform.translate(-extendedLogicalX, -extendedLogicalY - (extendedLogicalHeight - extendedPhysicalHeight * extendedLogicalWidth / extendedPhysicalWidth) / 2);
318     else
319         transform.translate(-extendedLogicalX, -extendedLogicalY - (extendedLogicalHeight - extendedPhysicalHeight * extendedLogicalWidth / extendedPhysicalWidth));
320 
321     return transform;
322 }
323 
valueAsString() const324 String SVGPreserveAspectRatio::valueAsString() const
325 {
326     String alignType;
327 
328     switch (m_align) {
329     case SVG_PRESERVEASPECTRATIO_NONE:
330         alignType = "none";
331         break;
332     case SVG_PRESERVEASPECTRATIO_XMINYMIN:
333         alignType = "xMinYMin";
334         break;
335     case SVG_PRESERVEASPECTRATIO_XMIDYMIN:
336         alignType = "xMidYMin";
337         break;
338     case SVG_PRESERVEASPECTRATIO_XMAXYMIN:
339         alignType = "xMaxYMin";
340         break;
341     case SVG_PRESERVEASPECTRATIO_XMINYMID:
342         alignType = "xMinYMid";
343         break;
344     case SVG_PRESERVEASPECTRATIO_XMIDYMID:
345         alignType = "xMidYMid";
346         break;
347     case SVG_PRESERVEASPECTRATIO_XMAXYMID:
348         alignType = "xMaxYMid";
349         break;
350     case SVG_PRESERVEASPECTRATIO_XMINYMAX:
351         alignType = "xMinYMax";
352         break;
353     case SVG_PRESERVEASPECTRATIO_XMIDYMAX:
354         alignType = "xMidYMax";
355         break;
356     case SVG_PRESERVEASPECTRATIO_XMAXYMAX:
357         alignType = "xMaxYMax";
358         break;
359     case SVG_PRESERVEASPECTRATIO_UNKNOWN:
360         alignType = "unknown";
361         break;
362     };
363 
364     switch (m_meetOrSlice) {
365     default:
366     case SVG_MEETORSLICE_UNKNOWN:
367         return alignType;
368     case SVG_MEETORSLICE_MEET:
369         return alignType + " meet";
370     case SVG_MEETORSLICE_SLICE:
371         return alignType + " slice";
372     }
373 }
374 
375 }
376