1 /* 2 * Copyright (C) 2004, 2005, 2008 Nikolas Zimmermann <zimmermann (at) kde.org> 3 * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis (at) kde.org> 4 * Copyright (C) 2010 Dirk Schulze <krit (at) 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 34 SVGPreserveAspectRatio::SVGPreserveAspectRatio() 35 : m_align(SVG_PRESERVEASPECTRATIO_XMIDYMID) 36 , m_meetOrSlice(SVG_MEETORSLICE_MEET) 37 { 38 } 39 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 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 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 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 250 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 290 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