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