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 "SVGPreserveAspectRatioValue.h" |
24 | |
25 | #include "AffineTransform.h" |
26 | #include "FloatRect.h" |
27 | #include "SVGParserUtilities.h" |
28 | #include <wtf/text/StringView.h> |
29 | |
30 | namespace WebCore { |
31 | |
32 | SVGPreserveAspectRatioValue::SVGPreserveAspectRatioValue() |
33 | : m_align(SVG_PRESERVEASPECTRATIO_XMIDYMID) |
34 | , m_meetOrSlice(SVG_MEETORSLICE_MEET) |
35 | { |
36 | } |
37 | |
38 | SVGPreserveAspectRatioValue::SVGPreserveAspectRatioValue(const String& value) |
39 | { |
40 | parse(value); |
41 | } |
42 | |
43 | ExceptionOr<void> SVGPreserveAspectRatioValue::setAlign(unsigned short align) |
44 | { |
45 | if (align == SVG_PRESERVEASPECTRATIO_UNKNOWN || align > SVG_PRESERVEASPECTRATIO_XMAXYMAX) |
46 | return Exception { NotSupportedError }; |
47 | |
48 | m_align = static_cast<SVGPreserveAspectRatioType>(align); |
49 | return { }; |
50 | } |
51 | |
52 | ExceptionOr<void> SVGPreserveAspectRatioValue::setMeetOrSlice(unsigned short meetOrSlice) |
53 | { |
54 | if (meetOrSlice == SVG_MEETORSLICE_UNKNOWN || meetOrSlice > SVG_MEETORSLICE_SLICE) |
55 | return Exception { NotSupportedError }; |
56 | |
57 | m_meetOrSlice = static_cast<SVGMeetOrSliceType>(meetOrSlice); |
58 | return { }; |
59 | } |
60 | |
61 | void SVGPreserveAspectRatioValue::parse(const String& value) |
62 | { |
63 | auto upconvertedCharacters = StringView(value).upconvertedCharacters(); |
64 | const UChar* begin = upconvertedCharacters; |
65 | parseInternal(begin, begin + value.length(), true); |
66 | } |
67 | |
68 | bool SVGPreserveAspectRatioValue::parse(const UChar*& currParam, const UChar* end, bool validate) |
69 | { |
70 | return parseInternal(currParam, end, validate); |
71 | } |
72 | |
73 | bool SVGPreserveAspectRatioValue::parseInternal(const UChar*& currParam, const UChar* end, bool validate) |
74 | { |
75 | SVGPreserveAspectRatioType align = SVG_PRESERVEASPECTRATIO_XMIDYMID; |
76 | SVGMeetOrSliceType meetOrSlice = SVG_MEETORSLICE_MEET; |
77 | |
78 | m_align = align; |
79 | m_meetOrSlice = meetOrSlice; |
80 | |
81 | if (!skipOptionalSVGSpaces(currParam, end)) |
82 | return false; |
83 | |
84 | if (*currParam == 'd') { |
85 | if (!skipString(currParam, end, "defer" )) { |
86 | LOG_ERROR("Skipped to parse except for *defer* value." ); |
87 | return false; |
88 | } |
89 | |
90 | // FIXME: We just ignore the "defer" here. |
91 | if (currParam == end) |
92 | return true; |
93 | |
94 | if (!skipOptionalSVGSpaces(currParam, end)) |
95 | return false; |
96 | } |
97 | |
98 | if (*currParam == 'n') { |
99 | if (!skipString(currParam, end, "none" )) { |
100 | LOG_ERROR("Skipped to parse except for *none* value." ); |
101 | return false; |
102 | } |
103 | align = SVG_PRESERVEASPECTRATIO_NONE; |
104 | skipOptionalSVGSpaces(currParam, end); |
105 | } else if (*currParam == 'x') { |
106 | if ((end - currParam) < 8) |
107 | return false; |
108 | if (currParam[1] != 'M' || currParam[4] != 'Y' || currParam[5] != 'M') |
109 | return false; |
110 | if (currParam[2] == 'i') { |
111 | if (currParam[3] == 'n') { |
112 | if (currParam[6] == 'i') { |
113 | if (currParam[7] == 'n') |
114 | align = SVG_PRESERVEASPECTRATIO_XMINYMIN; |
115 | else if (currParam[7] == 'd') |
116 | align = SVG_PRESERVEASPECTRATIO_XMINYMID; |
117 | else |
118 | return false; |
119 | } else if (currParam[6] == 'a' && currParam[7] == 'x') |
120 | align = SVG_PRESERVEASPECTRATIO_XMINYMAX; |
121 | else |
122 | return false; |
123 | } else if (currParam[3] == 'd') { |
124 | if (currParam[6] == 'i') { |
125 | if (currParam[7] == 'n') |
126 | align = SVG_PRESERVEASPECTRATIO_XMIDYMIN; |
127 | else if (currParam[7] == 'd') |
128 | align = SVG_PRESERVEASPECTRATIO_XMIDYMID; |
129 | else |
130 | return false; |
131 | } else if (currParam[6] == 'a' && currParam[7] == 'x') |
132 | align = SVG_PRESERVEASPECTRATIO_XMIDYMAX; |
133 | else |
134 | return false; |
135 | } else |
136 | return false; |
137 | } else if (currParam[2] == 'a' && currParam[3] == 'x') { |
138 | if (currParam[6] == 'i') { |
139 | if (currParam[7] == 'n') |
140 | align = SVG_PRESERVEASPECTRATIO_XMAXYMIN; |
141 | else if (currParam[7] == 'd') |
142 | align = SVG_PRESERVEASPECTRATIO_XMAXYMID; |
143 | else |
144 | return false; |
145 | } else if (currParam[6] == 'a' && currParam[7] == 'x') |
146 | align = SVG_PRESERVEASPECTRATIO_XMAXYMAX; |
147 | else |
148 | return false; |
149 | } else |
150 | return false; |
151 | currParam += 8; |
152 | skipOptionalSVGSpaces(currParam, end); |
153 | } else |
154 | return false; |
155 | |
156 | if (currParam < end) { |
157 | if (*currParam == 'm') { |
158 | if (!skipString(currParam, end, "meet" )) { |
159 | LOG_ERROR("Skipped to parse except for *meet* or *slice* value." ); |
160 | return false; |
161 | } |
162 | skipOptionalSVGSpaces(currParam, end); |
163 | } else if (*currParam == 's') { |
164 | if (!skipString(currParam, end, "slice" )) { |
165 | LOG_ERROR("Skipped to parse except for *meet* or *slice* value." ); |
166 | return false; |
167 | } |
168 | skipOptionalSVGSpaces(currParam, end); |
169 | if (align != SVG_PRESERVEASPECTRATIO_NONE) |
170 | meetOrSlice = SVG_MEETORSLICE_SLICE; |
171 | } |
172 | } |
173 | |
174 | if (end != currParam && validate) |
175 | return false; |
176 | |
177 | m_align = align; |
178 | m_meetOrSlice = meetOrSlice; |
179 | |
180 | return true; |
181 | } |
182 | |
183 | void SVGPreserveAspectRatioValue::transformRect(FloatRect& destRect, FloatRect& srcRect) const |
184 | { |
185 | if (m_align == SVG_PRESERVEASPECTRATIO_NONE) |
186 | return; |
187 | |
188 | FloatSize imageSize = srcRect.size(); |
189 | float origDestWidth = destRect.width(); |
190 | float origDestHeight = destRect.height(); |
191 | switch (m_meetOrSlice) { |
192 | case SVGPreserveAspectRatioValue::SVG_MEETORSLICE_UNKNOWN: |
193 | break; |
194 | case SVGPreserveAspectRatioValue::SVG_MEETORSLICE_MEET: { |
195 | float widthToHeightMultiplier = srcRect.height() / srcRect.width(); |
196 | if (origDestHeight > origDestWidth * widthToHeightMultiplier) { |
197 | destRect.setHeight(origDestWidth * widthToHeightMultiplier); |
198 | switch (m_align) { |
199 | case SVGPreserveAspectRatioValue::SVG_PRESERVEASPECTRATIO_XMINYMID: |
200 | case SVGPreserveAspectRatioValue::SVG_PRESERVEASPECTRATIO_XMIDYMID: |
201 | case SVGPreserveAspectRatioValue::SVG_PRESERVEASPECTRATIO_XMAXYMID: |
202 | destRect.setY(destRect.y() + origDestHeight / 2 - destRect.height() / 2); |
203 | break; |
204 | case SVGPreserveAspectRatioValue::SVG_PRESERVEASPECTRATIO_XMINYMAX: |
205 | case SVGPreserveAspectRatioValue::SVG_PRESERVEASPECTRATIO_XMIDYMAX: |
206 | case SVGPreserveAspectRatioValue::SVG_PRESERVEASPECTRATIO_XMAXYMAX: |
207 | destRect.setY(destRect.y() + origDestHeight - destRect.height()); |
208 | break; |
209 | default: |
210 | break; |
211 | } |
212 | } |
213 | if (origDestWidth > origDestHeight / widthToHeightMultiplier) { |
214 | destRect.setWidth(origDestHeight / widthToHeightMultiplier); |
215 | switch (m_align) { |
216 | case SVGPreserveAspectRatioValue::SVG_PRESERVEASPECTRATIO_XMIDYMIN: |
217 | case SVGPreserveAspectRatioValue::SVG_PRESERVEASPECTRATIO_XMIDYMID: |
218 | case SVGPreserveAspectRatioValue::SVG_PRESERVEASPECTRATIO_XMIDYMAX: |
219 | destRect.setX(destRect.x() + origDestWidth / 2 - destRect.width() / 2); |
220 | break; |
221 | case SVGPreserveAspectRatioValue::SVG_PRESERVEASPECTRATIO_XMAXYMIN: |
222 | case SVGPreserveAspectRatioValue::SVG_PRESERVEASPECTRATIO_XMAXYMID: |
223 | case SVGPreserveAspectRatioValue::SVG_PRESERVEASPECTRATIO_XMAXYMAX: |
224 | destRect.setX(destRect.x() + origDestWidth - destRect.width()); |
225 | break; |
226 | default: |
227 | break; |
228 | } |
229 | } |
230 | break; |
231 | } |
232 | case SVGPreserveAspectRatioValue::SVG_MEETORSLICE_SLICE: { |
233 | float widthToHeightMultiplier = srcRect.height() / srcRect.width(); |
234 | // if the destination height is less than the height of the image we'll be drawing |
235 | if (origDestHeight < origDestWidth * widthToHeightMultiplier) { |
236 | float destToSrcMultiplier = srcRect.width() / destRect.width(); |
237 | srcRect.setHeight(destRect.height() * destToSrcMultiplier); |
238 | switch (m_align) { |
239 | case SVGPreserveAspectRatioValue::SVG_PRESERVEASPECTRATIO_XMINYMID: |
240 | case SVGPreserveAspectRatioValue::SVG_PRESERVEASPECTRATIO_XMIDYMID: |
241 | case SVGPreserveAspectRatioValue::SVG_PRESERVEASPECTRATIO_XMAXYMID: |
242 | srcRect.setY(srcRect.y() + imageSize.height() / 2 - srcRect.height() / 2); |
243 | break; |
244 | case SVGPreserveAspectRatioValue::SVG_PRESERVEASPECTRATIO_XMINYMAX: |
245 | case SVGPreserveAspectRatioValue::SVG_PRESERVEASPECTRATIO_XMIDYMAX: |
246 | case SVGPreserveAspectRatioValue::SVG_PRESERVEASPECTRATIO_XMAXYMAX: |
247 | srcRect.setY(srcRect.y() + imageSize.height() - srcRect.height()); |
248 | break; |
249 | default: |
250 | break; |
251 | } |
252 | } |
253 | // if the destination width is less than the width of the image we'll be drawing |
254 | if (origDestWidth < origDestHeight / widthToHeightMultiplier) { |
255 | float destToSrcMultiplier = srcRect.height() / destRect.height(); |
256 | srcRect.setWidth(destRect.width() * destToSrcMultiplier); |
257 | switch (m_align) { |
258 | case SVGPreserveAspectRatioValue::SVG_PRESERVEASPECTRATIO_XMIDYMIN: |
259 | case SVGPreserveAspectRatioValue::SVG_PRESERVEASPECTRATIO_XMIDYMID: |
260 | case SVGPreserveAspectRatioValue::SVG_PRESERVEASPECTRATIO_XMIDYMAX: |
261 | srcRect.setX(srcRect.x() + imageSize.width() / 2 - srcRect.width() / 2); |
262 | break; |
263 | case SVGPreserveAspectRatioValue::SVG_PRESERVEASPECTRATIO_XMAXYMIN: |
264 | case SVGPreserveAspectRatioValue::SVG_PRESERVEASPECTRATIO_XMAXYMID: |
265 | case SVGPreserveAspectRatioValue::SVG_PRESERVEASPECTRATIO_XMAXYMAX: |
266 | srcRect.setX(srcRect.x() + imageSize.width() - srcRect.width()); |
267 | break; |
268 | default: |
269 | break; |
270 | } |
271 | } |
272 | break; |
273 | } |
274 | } |
275 | } |
276 | |
277 | AffineTransform SVGPreserveAspectRatioValue::getCTM(float logicalX, float logicalY, float logicalWidth, float logicalHeight, float physicalWidth, float physicalHeight) const |
278 | { |
279 | AffineTransform transform; |
280 | if (!logicalWidth || !logicalHeight || !physicalWidth || !physicalHeight) { |
281 | ASSERT_NOT_REACHED(); |
282 | return transform; |
283 | } |
284 | |
285 | if (m_align == SVG_PRESERVEASPECTRATIO_UNKNOWN) |
286 | return transform; |
287 | |
288 | double extendedLogicalX = logicalX; |
289 | double extendedLogicalY = logicalY; |
290 | double extendedLogicalWidth = logicalWidth; |
291 | double extendedLogicalHeight = logicalHeight; |
292 | double extendedPhysicalWidth = physicalWidth; |
293 | double extendedPhysicalHeight = physicalHeight; |
294 | double logicalRatio = extendedLogicalWidth / extendedLogicalHeight; |
295 | double physicalRatio = extendedPhysicalWidth / extendedPhysicalHeight; |
296 | |
297 | if (m_align == SVG_PRESERVEASPECTRATIO_NONE) { |
298 | transform.scaleNonUniform(extendedPhysicalWidth / extendedLogicalWidth, extendedPhysicalHeight / extendedLogicalHeight); |
299 | transform.translate(-extendedLogicalX, -extendedLogicalY); |
300 | return transform; |
301 | } |
302 | |
303 | if ((logicalRatio < physicalRatio && (m_meetOrSlice == SVG_MEETORSLICE_MEET)) || (logicalRatio >= physicalRatio && (m_meetOrSlice == SVG_MEETORSLICE_SLICE))) { |
304 | transform.scaleNonUniform(extendedPhysicalHeight / extendedLogicalHeight, extendedPhysicalHeight / extendedLogicalHeight); |
305 | |
306 | if (m_align == SVG_PRESERVEASPECTRATIO_XMINYMIN || m_align == SVG_PRESERVEASPECTRATIO_XMINYMID || m_align == SVG_PRESERVEASPECTRATIO_XMINYMAX) |
307 | transform.translate(-extendedLogicalX, -extendedLogicalY); |
308 | else if (m_align == SVG_PRESERVEASPECTRATIO_XMIDYMIN || m_align == SVG_PRESERVEASPECTRATIO_XMIDYMID || m_align == SVG_PRESERVEASPECTRATIO_XMIDYMAX) |
309 | transform.translate(-extendedLogicalX - (extendedLogicalWidth - extendedPhysicalWidth * extendedLogicalHeight / extendedPhysicalHeight) / 2, -extendedLogicalY); |
310 | else |
311 | transform.translate(-extendedLogicalX - (extendedLogicalWidth - extendedPhysicalWidth * extendedLogicalHeight / extendedPhysicalHeight), -extendedLogicalY); |
312 | |
313 | return transform; |
314 | } |
315 | |
316 | transform.scaleNonUniform(extendedPhysicalWidth / extendedLogicalWidth, extendedPhysicalWidth / extendedLogicalWidth); |
317 | |
318 | if (m_align == SVG_PRESERVEASPECTRATIO_XMINYMIN || m_align == SVG_PRESERVEASPECTRATIO_XMIDYMIN || m_align == SVG_PRESERVEASPECTRATIO_XMAXYMIN) |
319 | transform.translate(-extendedLogicalX, -extendedLogicalY); |
320 | else if (m_align == SVG_PRESERVEASPECTRATIO_XMINYMID || m_align == SVG_PRESERVEASPECTRATIO_XMIDYMID || m_align == SVG_PRESERVEASPECTRATIO_XMAXYMID) |
321 | transform.translate(-extendedLogicalX, -extendedLogicalY - (extendedLogicalHeight - extendedPhysicalHeight * extendedLogicalWidth / extendedPhysicalWidth) / 2); |
322 | else |
323 | transform.translate(-extendedLogicalX, -extendedLogicalY - (extendedLogicalHeight - extendedPhysicalHeight * extendedLogicalWidth / extendedPhysicalWidth)); |
324 | |
325 | return transform; |
326 | } |
327 | |
328 | String SVGPreserveAspectRatioValue::valueAsString() const |
329 | { |
330 | String alignType; |
331 | |
332 | switch (m_align) { |
333 | case SVG_PRESERVEASPECTRATIO_NONE: |
334 | alignType = "none" ; |
335 | break; |
336 | case SVG_PRESERVEASPECTRATIO_XMINYMIN: |
337 | alignType = "xMinYMin" ; |
338 | break; |
339 | case SVG_PRESERVEASPECTRATIO_XMIDYMIN: |
340 | alignType = "xMidYMin" ; |
341 | break; |
342 | case SVG_PRESERVEASPECTRATIO_XMAXYMIN: |
343 | alignType = "xMaxYMin" ; |
344 | break; |
345 | case SVG_PRESERVEASPECTRATIO_XMINYMID: |
346 | alignType = "xMinYMid" ; |
347 | break; |
348 | case SVG_PRESERVEASPECTRATIO_XMIDYMID: |
349 | alignType = "xMidYMid" ; |
350 | break; |
351 | case SVG_PRESERVEASPECTRATIO_XMAXYMID: |
352 | alignType = "xMaxYMid" ; |
353 | break; |
354 | case SVG_PRESERVEASPECTRATIO_XMINYMAX: |
355 | alignType = "xMinYMax" ; |
356 | break; |
357 | case SVG_PRESERVEASPECTRATIO_XMIDYMAX: |
358 | alignType = "xMidYMax" ; |
359 | break; |
360 | case SVG_PRESERVEASPECTRATIO_XMAXYMAX: |
361 | alignType = "xMaxYMax" ; |
362 | break; |
363 | case SVG_PRESERVEASPECTRATIO_UNKNOWN: |
364 | alignType = "unknown" ; |
365 | break; |
366 | }; |
367 | |
368 | switch (m_meetOrSlice) { |
369 | default: |
370 | case SVG_MEETORSLICE_UNKNOWN: |
371 | return alignType; |
372 | case SVG_MEETORSLICE_MEET: |
373 | return alignType + " meet" ; |
374 | case SVG_MEETORSLICE_SLICE: |
375 | return alignType + " slice" ; |
376 | } |
377 | } |
378 | |
379 | } |
380 | |