• 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 
24 #if ENABLE(SVG)
25 #include "SVGPreserveAspectRatio.h"
26 
27 #include "AffineTransform.h"
28 #include "FloatRect.h"
29 #include "SVGParserUtilities.h"
30 #include <wtf/text/StringConcatenate.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,ExceptionCode & ec)40 void SVGPreserveAspectRatio::setAlign(unsigned short align, ExceptionCode& ec)
41 {
42     if (align == SVG_PRESERVEASPECTRATIO_UNKNOWN || align > SVG_PRESERVEASPECTRATIO_XMAXYMAX) {
43         ec = NOT_SUPPORTED_ERR;
44         return;
45     }
46 
47     m_align = static_cast<SVGPreserveAspectRatioType>(align);
48 }
49 
setMeetOrSlice(unsigned short meetOrSlice,ExceptionCode & ec)50 void SVGPreserveAspectRatio::setMeetOrSlice(unsigned short meetOrSlice, ExceptionCode& ec)
51 {
52     if (meetOrSlice == SVG_MEETORSLICE_UNKNOWN || meetOrSlice > SVG_MEETORSLICE_SLICE) {
53         ec = NOT_SUPPORTED_ERR;
54         return;
55     }
56 
57     m_meetOrSlice = static_cast<SVGMeetOrSliceType>(meetOrSlice);
58 }
59 
parsePreserveAspectRatio(const UChar * & currParam,const UChar * end,bool validate,bool & result)60 SVGPreserveAspectRatio SVGPreserveAspectRatio::parsePreserveAspectRatio(const UChar*& currParam, const UChar* end, bool validate, bool& result)
61 {
62     SVGPreserveAspectRatio aspectRatio;
63     aspectRatio.m_align = SVG_PRESERVEASPECTRATIO_NONE;
64     aspectRatio.m_meetOrSlice = SVG_MEETORSLICE_MEET;
65     result = false;
66 
67     // FIXME: Rewrite this parser, without gotos!
68     if (!skipOptionalSpaces(currParam, end))
69         goto bailOut;
70 
71     if (*currParam == 'd') {
72         if (!skipString(currParam, end, "defer"))
73             goto bailOut;
74         // FIXME: We just ignore the "defer" here.
75         if (!skipOptionalSpaces(currParam, end))
76             goto bailOut;
77     }
78 
79     if (*currParam == 'n') {
80         if (!skipString(currParam, end, "none"))
81             goto bailOut;
82         skipOptionalSpaces(currParam, end);
83     } else if (*currParam == 'x') {
84         if ((end - currParam) < 8)
85             goto bailOut;
86         if (currParam[1] != 'M' || currParam[4] != 'Y' || currParam[5] != 'M')
87             goto bailOut;
88         if (currParam[2] == 'i') {
89             if (currParam[3] == 'n') {
90                 if (currParam[6] == 'i') {
91                     if (currParam[7] == 'n')
92                         aspectRatio.m_align = SVG_PRESERVEASPECTRATIO_XMINYMIN;
93                     else if (currParam[7] == 'd')
94                         aspectRatio.m_align = SVG_PRESERVEASPECTRATIO_XMINYMID;
95                     else
96                         goto bailOut;
97                 } else if (currParam[6] == 'a' && currParam[7] == 'x')
98                      aspectRatio.m_align = SVG_PRESERVEASPECTRATIO_XMINYMAX;
99                 else
100                      goto bailOut;
101              } else if (currParam[3] == 'd') {
102                 if (currParam[6] == 'i') {
103                     if (currParam[7] == 'n')
104                         aspectRatio.m_align = SVG_PRESERVEASPECTRATIO_XMIDYMIN;
105                     else if (currParam[7] == 'd')
106                         aspectRatio.m_align = SVG_PRESERVEASPECTRATIO_XMIDYMID;
107                     else
108                         goto bailOut;
109                 } else if (currParam[6] == 'a' && currParam[7] == 'x')
110                     aspectRatio.m_align = SVG_PRESERVEASPECTRATIO_XMIDYMAX;
111                 else
112                     goto bailOut;
113             } else
114                 goto bailOut;
115         } else if (currParam[2] == 'a' && currParam[3] == 'x') {
116             if (currParam[6] == 'i') {
117                 if (currParam[7] == 'n')
118                     aspectRatio.m_align = SVG_PRESERVEASPECTRATIO_XMAXYMIN;
119                 else if (currParam[7] == 'd')
120                     aspectRatio.m_align = SVG_PRESERVEASPECTRATIO_XMAXYMID;
121                 else
122                     goto bailOut;
123             } else if (currParam[6] == 'a' && currParam[7] == 'x')
124                 aspectRatio.m_align = SVG_PRESERVEASPECTRATIO_XMAXYMAX;
125             else
126                 goto bailOut;
127         } else
128             goto bailOut;
129         currParam += 8;
130         skipOptionalSpaces(currParam, end);
131     } else
132         goto bailOut;
133 
134     if (currParam < end) {
135         if (*currParam == 'm') {
136             if (!skipString(currParam, end, "meet"))
137                 goto bailOut;
138             skipOptionalSpaces(currParam, end);
139         } else if (*currParam == 's') {
140             if (!skipString(currParam, end, "slice"))
141                 goto bailOut;
142             skipOptionalSpaces(currParam, end);
143             if (aspectRatio.m_align != SVG_PRESERVEASPECTRATIO_NONE)
144                 aspectRatio.m_meetOrSlice = SVG_MEETORSLICE_SLICE;
145         }
146     }
147 
148     if (end != currParam && validate) {
149 bailOut:
150         // FIXME: Should the two values be set to UNKNOWN instead?
151         aspectRatio.m_align = SVG_PRESERVEASPECTRATIO_NONE;
152         aspectRatio.m_meetOrSlice = SVG_MEETORSLICE_MEET;
153     } else
154         result = true;
155 
156     return aspectRatio;
157 }
158 
transformRect(FloatRect & destRect,FloatRect & srcRect)159 void SVGPreserveAspectRatio::transformRect(FloatRect& destRect, FloatRect& srcRect)
160 {
161     FloatSize imageSize = srcRect.size();
162     float origDestWidth = destRect.width();
163     float origDestHeight = destRect.height();
164     switch (m_meetOrSlice) {
165     case SVGPreserveAspectRatio::SVG_MEETORSLICE_UNKNOWN:
166         break;
167     case SVGPreserveAspectRatio::SVG_MEETORSLICE_MEET: {
168         float widthToHeightMultiplier = srcRect.height() / srcRect.width();
169         if (origDestHeight > origDestWidth * widthToHeightMultiplier) {
170             destRect.setHeight(origDestWidth * widthToHeightMultiplier);
171             switch (m_align) {
172             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID:
173             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
174             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
175                 destRect.setY(destRect.y() + origDestHeight / 2 - destRect.height() / 2);
176                 break;
177             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX:
178             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
179             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
180                 destRect.setY(destRect.y() + origDestHeight - destRect.height());
181                 break;
182             default:
183                 break;
184             }
185         }
186         if (origDestWidth > origDestHeight / widthToHeightMultiplier) {
187             destRect.setWidth(origDestHeight / widthToHeightMultiplier);
188             switch (m_align) {
189             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN:
190             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
191             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
192                 destRect.setX(destRect.x() + origDestWidth / 2 - destRect.width() / 2);
193                 break;
194             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN:
195             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
196             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
197                 destRect.setX(destRect.x() + origDestWidth - destRect.width());
198                 break;
199             default:
200                 break;
201             }
202         }
203         break;
204     }
205     case SVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE: {
206         float widthToHeightMultiplier = srcRect.height() / srcRect.width();
207         // if the destination height is less than the height of the image we'll be drawing
208         if (origDestHeight < origDestWidth * widthToHeightMultiplier) {
209             float destToSrcMultiplier = srcRect.width() / destRect.width();
210             srcRect.setHeight(destRect.height() * destToSrcMultiplier);
211             switch (m_align) {
212             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID:
213             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
214             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
215                 srcRect.setY(destRect.y() + imageSize.height() / 2 - srcRect.height() / 2);
216                 break;
217             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX:
218             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
219             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
220                 srcRect.setY(destRect.y() + imageSize.height() - srcRect.height());
221                 break;
222             default:
223                 break;
224             }
225         }
226         // if the destination width is less than the width of the image we'll be drawing
227         if (origDestWidth < origDestHeight / widthToHeightMultiplier) {
228             float destToSrcMultiplier = srcRect.height() / destRect.height();
229             srcRect.setWidth(destRect.width() * destToSrcMultiplier);
230             switch (m_align) {
231             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN:
232             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
233             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
234                 srcRect.setX(destRect.x() + imageSize.width() / 2 - srcRect.width() / 2);
235                 break;
236             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN:
237             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
238             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
239                 srcRect.setX(destRect.x() + imageSize.width() - srcRect.width());
240                 break;
241             default:
242                 break;
243             }
244         }
245         break;
246     }
247     }
248 }
249 
getCTM(float logicX,float logicY,float logicWidth,float logicHeight,float physWidth,float physHeight) const250 AffineTransform SVGPreserveAspectRatio::getCTM(float logicX, float logicY, float logicWidth, float logicHeight, float physWidth, float physHeight) const
251 {
252     AffineTransform transform;
253     if (m_align == SVG_PRESERVEASPECTRATIO_UNKNOWN)
254         return transform;
255 
256     float logicalRatio = logicWidth / logicHeight;
257     float physRatio = physWidth / physHeight;
258 
259     if (m_align == SVG_PRESERVEASPECTRATIO_NONE) {
260         transform.scaleNonUniform(physWidth / logicWidth, physHeight / logicHeight);
261         transform.translate(-logicX, -logicY);
262         return transform;
263     }
264 
265     if ((logicalRatio < physRatio && (m_meetOrSlice == SVG_MEETORSLICE_MEET)) || (logicalRatio >= physRatio && (m_meetOrSlice == SVG_MEETORSLICE_SLICE))) {
266         transform.scaleNonUniform(physHeight / logicHeight, physHeight / logicHeight);
267 
268         if (m_align == SVG_PRESERVEASPECTRATIO_XMINYMIN || m_align == SVG_PRESERVEASPECTRATIO_XMINYMID || m_align == SVG_PRESERVEASPECTRATIO_XMINYMAX)
269             transform.translate(-logicX, -logicY);
270         else if (m_align == SVG_PRESERVEASPECTRATIO_XMIDYMIN || m_align == SVG_PRESERVEASPECTRATIO_XMIDYMID || m_align == SVG_PRESERVEASPECTRATIO_XMIDYMAX)
271             transform.translate(-logicX - (logicWidth - physWidth * logicHeight / physHeight) / 2, -logicY);
272         else
273             transform.translate(-logicX - (logicWidth - physWidth * logicHeight / physHeight), -logicY);
274 
275         return transform;
276     }
277 
278     transform.scaleNonUniform(physWidth / logicWidth, physWidth / logicWidth);
279 
280     if (m_align == SVG_PRESERVEASPECTRATIO_XMINYMIN || m_align == SVG_PRESERVEASPECTRATIO_XMIDYMIN || m_align == SVG_PRESERVEASPECTRATIO_XMAXYMIN)
281         transform.translate(-logicX, -logicY);
282     else if (m_align == SVG_PRESERVEASPECTRATIO_XMINYMID || m_align == SVG_PRESERVEASPECTRATIO_XMIDYMID || m_align == SVG_PRESERVEASPECTRATIO_XMAXYMID)
283         transform.translate(-logicX, -logicY - (logicHeight - physHeight * logicWidth / physWidth) / 2);
284     else
285         transform.translate(-logicX, -logicY - (logicHeight - physHeight * logicWidth / physWidth));
286 
287     return transform;
288 }
289 
valueAsString() const290 String SVGPreserveAspectRatio::valueAsString() const
291 {
292     String alignType;
293 
294     switch (m_align) {
295     case SVG_PRESERVEASPECTRATIO_NONE:
296         alignType = "none";
297         break;
298     case SVG_PRESERVEASPECTRATIO_XMINYMIN:
299         alignType = "xMinYMin";
300         break;
301     case SVG_PRESERVEASPECTRATIO_XMIDYMIN:
302         alignType = "xMidYMin";
303         break;
304     case SVG_PRESERVEASPECTRATIO_XMAXYMIN:
305         alignType = "xMaxYMin";
306         break;
307     case SVG_PRESERVEASPECTRATIO_XMINYMID:
308         alignType = "xMinYMid";
309         break;
310     case SVG_PRESERVEASPECTRATIO_XMIDYMID:
311         alignType = "xMidYMid";
312         break;
313     case SVG_PRESERVEASPECTRATIO_XMAXYMID:
314         alignType = "xMaxYMid";
315         break;
316     case SVG_PRESERVEASPECTRATIO_XMINYMAX:
317         alignType = "xMinYMax";
318         break;
319     case SVG_PRESERVEASPECTRATIO_XMIDYMAX:
320         alignType = "xMidYMax";
321         break;
322     case SVG_PRESERVEASPECTRATIO_XMAXYMAX:
323         alignType = "xMaxYMax";
324         break;
325     case SVG_PRESERVEASPECTRATIO_UNKNOWN:
326         alignType = "unknown";
327         break;
328     };
329 
330     switch (m_meetOrSlice) {
331     default:
332     case SVG_MEETORSLICE_UNKNOWN:
333         return alignType;
334     case SVG_MEETORSLICE_MEET:
335         return makeString(alignType, " meet");
336     case SVG_MEETORSLICE_SLICE:
337         return makeString(alignType, " slice");
338     };
339 }
340 
341 }
342 
343 #endif // ENABLE(SVG)
344