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