OSDN Git Service

Merge WebKit at r71558: Initial merge by git.
[android-x86/external-webkit.git] / WebCore / 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
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
34 SVGPreserveAspectRatio::SVGPreserveAspectRatio()
35     : m_align(SVG_PRESERVEASPECTRATIO_XMIDYMID)
36     , m_meetOrSlice(SVG_MEETORSLICE_MEET)
37 {
38 }
39
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
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
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 bail_out;
70
71     if (*currParam == 'd') {
72         if (!skipString(currParam, end, "defer"))
73             goto bail_out;
74         // FIXME: We just ignore the "defer" here.
75         if (!skipOptionalSpaces(currParam, end))
76             goto bail_out;
77     }
78
79     if (*currParam == 'n') {
80         if (!skipString(currParam, end, "none"))
81             goto bail_out;
82         skipOptionalSpaces(currParam, end);
83     } else if (*currParam == 'x') {
84         if ((end - currParam) < 8)
85             goto bail_out;
86         if (currParam[1] != 'M' || currParam[4] != 'Y' || currParam[5] != 'M')
87             goto bail_out;
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 bail_out;
97                 } else if (currParam[6] == 'a' && currParam[7] == 'x')
98                      aspectRatio.m_align = SVG_PRESERVEASPECTRATIO_XMINYMAX;
99                 else
100                      goto bail_out;
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 bail_out;
109                 } else if (currParam[6] == 'a' && currParam[7] == 'x')
110                     aspectRatio.m_align = SVG_PRESERVEASPECTRATIO_XMIDYMAX;
111                 else
112                     goto bail_out;
113             } else
114                 goto bail_out;
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 bail_out;
123             } else if (currParam[6] == 'a' && currParam[7] == 'x')
124                 aspectRatio.m_align = SVG_PRESERVEASPECTRATIO_XMAXYMAX;
125             else
126                 goto bail_out;
127         } else
128             goto bail_out;
129         currParam += 8;
130         skipOptionalSpaces(currParam, end);
131     } else
132         goto bail_out;
133
134     if (currParam < end) {
135         if (*currParam == 'm') {
136             if (!skipString(currParam, end, "meet"))
137                 goto bail_out;
138             skipOptionalSpaces(currParam, end);
139         } else if (*currParam == 's') {
140             if (!skipString(currParam, end, "slice"))
141                 goto bail_out;
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 bail_out:
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
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     {
169         float widthToHeightMultiplier = srcRect.height() / srcRect.width();
170         if (origDestHeight > origDestWidth * widthToHeightMultiplier) {
171             destRect.setHeight(origDestWidth * widthToHeightMultiplier);
172             switch (m_align) {
173             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID:
174             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
175             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
176                 destRect.setY(destRect.y() + origDestHeight / 2 - destRect.height() / 2);
177                 break;
178             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX:
179             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
180             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
181                 destRect.setY(destRect.y() + origDestHeight - destRect.height());
182                 break;
183             default:
184                 break;
185             }
186         }
187         if (origDestWidth > origDestHeight / widthToHeightMultiplier) {
188             destRect.setWidth(origDestHeight / widthToHeightMultiplier);
189             switch (m_align) {
190             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN:
191             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
192             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
193                 destRect.setX(destRect.x() + origDestWidth / 2 - destRect.width() / 2);
194                 break;
195             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN:
196             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
197             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
198                 destRect.setX(destRect.x() + origDestWidth - destRect.width());
199                 break;
200             default:
201                 break;
202             }
203         }
204         break;
205     }
206     case SVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE:
207     {
208         float widthToHeightMultiplier = srcRect.height() / srcRect.width();
209         // if the destination height is less than the height of the image we'll be drawing
210         if (origDestHeight < origDestWidth * widthToHeightMultiplier) {
211             float destToSrcMultiplier = srcRect.width() / destRect.width();
212             srcRect.setHeight(destRect.height() * destToSrcMultiplier);
213             switch (m_align) {
214             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID:
215             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
216             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
217                 srcRect.setY(destRect.y() + imageSize.height() / 2 - srcRect.height() / 2);
218                 break;
219             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX:
220             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
221             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
222                 srcRect.setY(destRect.y() + imageSize.height() - srcRect.height());
223                 break;
224             default:
225                 break;
226             }
227         }
228         // if the destination width is less than the width of the image we'll be drawing
229         if (origDestWidth < origDestHeight / widthToHeightMultiplier) {
230             float destToSrcMultiplier = srcRect.height() / destRect.height();
231             srcRect.setWidth(destRect.width() * destToSrcMultiplier);
232             switch (m_align) {
233             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN:
234             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID:
235             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX:
236                 srcRect.setX(destRect.x() + imageSize.width() / 2 - srcRect.width() / 2);
237                 break;
238             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN:
239             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID:
240             case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX:
241                 srcRect.setX(destRect.x() + imageSize.width() - srcRect.width());
242                 break;
243             default:
244                 break;
245             }
246         }
247         break;
248     }
249     }
250 }
251
252 // FIXME: We should use floats here, like everywhere else!
253 AffineTransform SVGPreserveAspectRatio::getCTM(double logicX, double logicY, double logicWidth, double logicHeight, double physWidth, double physHeight) const
254 {
255     AffineTransform transform;
256     if (m_align == SVG_PRESERVEASPECTRATIO_UNKNOWN)
257         return transform;
258
259     double logicalRatio = logicWidth / logicHeight;
260     double physRatio = physWidth / physHeight;
261
262     if (m_align == SVG_PRESERVEASPECTRATIO_NONE) {
263         transform.scaleNonUniform(physWidth / logicWidth, physHeight / logicHeight);
264         transform.translate(-logicX, -logicY);
265         return transform;
266     }
267
268     if ((logicalRatio < physRatio && (m_meetOrSlice == SVG_MEETORSLICE_MEET)) || (logicalRatio >= physRatio && (m_meetOrSlice == SVG_MEETORSLICE_SLICE))) {
269         transform.scaleNonUniform(physHeight / logicHeight, physHeight / logicHeight);
270
271         if (m_align == SVG_PRESERVEASPECTRATIO_XMINYMIN || m_align == SVG_PRESERVEASPECTRATIO_XMINYMID || m_align == SVG_PRESERVEASPECTRATIO_XMINYMAX)
272             transform.translate(-logicX, -logicY);
273         else if (m_align == SVG_PRESERVEASPECTRATIO_XMIDYMIN || m_align == SVG_PRESERVEASPECTRATIO_XMIDYMID || m_align == SVG_PRESERVEASPECTRATIO_XMIDYMAX)
274             transform.translate(-logicX - (logicWidth - physWidth * logicHeight / physHeight) / 2, -logicY);
275         else
276             transform.translate(-logicX - (logicWidth - physWidth * logicHeight / physHeight), -logicY);
277         
278         return transform;
279     }
280
281     transform.scaleNonUniform(physWidth / logicWidth, physWidth / logicWidth);
282
283     if (m_align == SVG_PRESERVEASPECTRATIO_XMINYMIN || m_align == SVG_PRESERVEASPECTRATIO_XMIDYMIN || m_align == SVG_PRESERVEASPECTRATIO_XMAXYMIN)
284         transform.translate(-logicX, -logicY);
285     else if (m_align == SVG_PRESERVEASPECTRATIO_XMINYMID || m_align == SVG_PRESERVEASPECTRATIO_XMIDYMID || m_align == SVG_PRESERVEASPECTRATIO_XMAXYMID)
286         transform.translate(-logicX, -logicY - (logicHeight - physHeight * logicWidth / physWidth) / 2);
287     else
288         transform.translate(-logicX, -logicY - (logicHeight - physHeight * logicWidth / physWidth));
289
290     return transform;
291 }
292
293 String SVGPreserveAspectRatio::valueAsString() const
294 {
295     String alignType;
296
297     switch (m_align) {
298     case SVG_PRESERVEASPECTRATIO_NONE:
299         alignType = "none";
300         break;
301     case SVG_PRESERVEASPECTRATIO_XMINYMIN:
302         alignType = "xMinYMin";
303         break;
304     case SVG_PRESERVEASPECTRATIO_XMIDYMIN:
305         alignType = "xMidYMin";
306         break;
307     case SVG_PRESERVEASPECTRATIO_XMAXYMIN:
308         alignType = "xMaxYMin";
309         break;
310     case SVG_PRESERVEASPECTRATIO_XMINYMID:
311         alignType = "xMinYMid";
312         break;
313     case SVG_PRESERVEASPECTRATIO_XMIDYMID:
314         alignType = "xMidYMid";
315         break;
316     case SVG_PRESERVEASPECTRATIO_XMAXYMID:
317         alignType = "xMaxYMid";
318         break;
319     case SVG_PRESERVEASPECTRATIO_XMINYMAX:
320         alignType = "xMinYMax";
321         break;
322     case SVG_PRESERVEASPECTRATIO_XMIDYMAX:
323         alignType = "xMidYMax";
324         break;
325     case SVG_PRESERVEASPECTRATIO_XMAXYMAX:
326         alignType = "xMaxYMax";
327         break;
328     case SVG_PRESERVEASPECTRATIO_UNKNOWN:
329         alignType = "unknown";
330         break;
331     };
332
333     switch (m_meetOrSlice) {
334     default:
335     case SVG_MEETORSLICE_UNKNOWN:
336         return alignType;
337     case SVG_MEETORSLICE_MEET:
338         return makeString(alignType, " meet");
339     case SVG_MEETORSLICE_SLICE:
340         return makeString(alignType, " slice");
341     };
342 }
343
344 }
345
346 #endif // ENABLE(SVG)