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>
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.
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.
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.
25 #include "SVGPreserveAspectRatio.h"
27 #include "AffineTransform.h"
28 #include "FloatRect.h"
29 #include "SVGParserUtilities.h"
30 #include <wtf/text/StringConcatenate.h>
34 SVGPreserveAspectRatio::SVGPreserveAspectRatio()
35 : m_align(SVG_PRESERVEASPECTRATIO_XMIDYMID)
36 , m_meetOrSlice(SVG_MEETORSLICE_MEET)
40 void SVGPreserveAspectRatio::setAlign(unsigned short align, ExceptionCode& ec)
42 if (align == SVG_PRESERVEASPECTRATIO_UNKNOWN || align > SVG_PRESERVEASPECTRATIO_XMAXYMAX) {
43 ec = NOT_SUPPORTED_ERR;
47 m_align = static_cast<SVGPreserveAspectRatioType>(align);
50 void SVGPreserveAspectRatio::setMeetOrSlice(unsigned short meetOrSlice, ExceptionCode& ec)
52 if (meetOrSlice == SVG_MEETORSLICE_UNKNOWN || meetOrSlice > SVG_MEETORSLICE_SLICE) {
53 ec = NOT_SUPPORTED_ERR;
57 m_meetOrSlice = static_cast<SVGMeetOrSliceType>(meetOrSlice);
60 SVGPreserveAspectRatio SVGPreserveAspectRatio::parsePreserveAspectRatio(const UChar*& currParam, const UChar* end, bool validate, bool& result)
62 SVGPreserveAspectRatio aspectRatio;
63 aspectRatio.m_align = SVG_PRESERVEASPECTRATIO_NONE;
64 aspectRatio.m_meetOrSlice = SVG_MEETORSLICE_MEET;
67 // FIXME: Rewrite this parser, without gotos!
68 if (!skipOptionalSpaces(currParam, end))
71 if (*currParam == 'd') {
72 if (!skipString(currParam, end, "defer"))
74 // FIXME: We just ignore the "defer" here.
75 if (!skipOptionalSpaces(currParam, end))
79 if (*currParam == 'n') {
80 if (!skipString(currParam, end, "none"))
82 skipOptionalSpaces(currParam, end);
83 } else if (*currParam == 'x') {
84 if ((end - currParam) < 8)
86 if (currParam[1] != 'M' || currParam[4] != 'Y' || currParam[5] != 'M')
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;
97 } else if (currParam[6] == 'a' && currParam[7] == 'x')
98 aspectRatio.m_align = SVG_PRESERVEASPECTRATIO_XMINYMAX;
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;
109 } else if (currParam[6] == 'a' && currParam[7] == 'x')
110 aspectRatio.m_align = SVG_PRESERVEASPECTRATIO_XMIDYMAX;
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;
123 } else if (currParam[6] == 'a' && currParam[7] == 'x')
124 aspectRatio.m_align = SVG_PRESERVEASPECTRATIO_XMAXYMAX;
130 skipOptionalSpaces(currParam, end);
134 if (currParam < end) {
135 if (*currParam == 'm') {
136 if (!skipString(currParam, end, "meet"))
138 skipOptionalSpaces(currParam, end);
139 } else if (*currParam == 's') {
140 if (!skipString(currParam, end, "slice"))
142 skipOptionalSpaces(currParam, end);
143 if (aspectRatio.m_align != SVG_PRESERVEASPECTRATIO_NONE)
144 aspectRatio.m_meetOrSlice = SVG_MEETORSLICE_SLICE;
148 if (end != currParam && validate) {
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;
159 void SVGPreserveAspectRatio::transformRect(FloatRect& destRect, FloatRect& srcRect)
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:
167 case SVGPreserveAspectRatio::SVG_MEETORSLICE_MEET:
169 float widthToHeightMultiplier = srcRect.height() / srcRect.width();
170 if (origDestHeight > origDestWidth * widthToHeightMultiplier) {
171 destRect.setHeight(origDestWidth * widthToHeightMultiplier);
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);
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());
187 if (origDestWidth > origDestHeight / widthToHeightMultiplier) {
188 destRect.setWidth(origDestHeight / widthToHeightMultiplier);
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);
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());
206 case SVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE:
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);
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);
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());
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);
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);
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());
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
255 AffineTransform transform;
256 if (m_align == SVG_PRESERVEASPECTRATIO_UNKNOWN)
259 double logicalRatio = logicWidth / logicHeight;
260 double physRatio = physWidth / physHeight;
262 if (m_align == SVG_PRESERVEASPECTRATIO_NONE) {
263 transform.scaleNonUniform(physWidth / logicWidth, physHeight / logicHeight);
264 transform.translate(-logicX, -logicY);
268 if ((logicalRatio < physRatio && (m_meetOrSlice == SVG_MEETORSLICE_MEET)) || (logicalRatio >= physRatio && (m_meetOrSlice == SVG_MEETORSLICE_SLICE))) {
269 transform.scaleNonUniform(physHeight / logicHeight, physHeight / logicHeight);
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);
276 transform.translate(-logicX - (logicWidth - physWidth * logicHeight / physHeight), -logicY);
281 transform.scaleNonUniform(physWidth / logicWidth, physWidth / logicWidth);
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);
288 transform.translate(-logicX, -logicY - (logicHeight - physHeight * logicWidth / physWidth));
293 String SVGPreserveAspectRatio::valueAsString() const
298 case SVG_PRESERVEASPECTRATIO_NONE:
301 case SVG_PRESERVEASPECTRATIO_XMINYMIN:
302 alignType = "xMinYMin";
304 case SVG_PRESERVEASPECTRATIO_XMIDYMIN:
305 alignType = "xMidYMin";
307 case SVG_PRESERVEASPECTRATIO_XMAXYMIN:
308 alignType = "xMaxYMin";
310 case SVG_PRESERVEASPECTRATIO_XMINYMID:
311 alignType = "xMinYMid";
313 case SVG_PRESERVEASPECTRATIO_XMIDYMID:
314 alignType = "xMidYMid";
316 case SVG_PRESERVEASPECTRATIO_XMAXYMID:
317 alignType = "xMaxYMid";
319 case SVG_PRESERVEASPECTRATIO_XMINYMAX:
320 alignType = "xMinYMax";
322 case SVG_PRESERVEASPECTRATIO_XMIDYMAX:
323 alignType = "xMidYMax";
325 case SVG_PRESERVEASPECTRATIO_XMAXYMAX:
326 alignType = "xMaxYMax";
328 case SVG_PRESERVEASPECTRATIO_UNKNOWN:
329 alignType = "unknown";
333 switch (m_meetOrSlice) {
335 case SVG_MEETORSLICE_UNKNOWN:
337 case SVG_MEETORSLICE_MEET:
338 return makeString(alignType, " meet");
339 case SVG_MEETORSLICE_SLICE:
340 return makeString(alignType, " slice");
346 #endif // ENABLE(SVG)