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