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