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 #include "core/svg/SVGPreserveAspectRatio.h" 24 25 #include "bindings/v8/ExceptionState.h" 26 #include "core/dom/ExceptionCode.h" 27 #include "core/platform/graphics/FloatRect.h" 28 #include "core/platform/graphics/transforms/AffineTransform.h" 29 #include "core/svg/SVGParserUtilities.h" 30 #include "wtf/text/WTFString.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, ExceptionState& es) 41 { 42 if (align == SVG_PRESERVEASPECTRATIO_UNKNOWN || align > SVG_PRESERVEASPECTRATIO_XMAXYMAX) { 43 es.throwDOMException(NotSupportedError); 44 return; 45 } 46 47 m_align = static_cast<SVGPreserveAspectRatioType>(align); 48 } 49 50 void SVGPreserveAspectRatio::setMeetOrSlice(unsigned short meetOrSlice, ExceptionState& es) 51 { 52 if (meetOrSlice == SVG_MEETORSLICE_UNKNOWN || meetOrSlice > SVG_MEETORSLICE_SLICE) { 53 es.throwDOMException(NotSupportedError); 54 return; 55 } 56 57 m_meetOrSlice = static_cast<SVGMeetOrSliceType>(meetOrSlice); 58 } 59 60 template<typename CharType> 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 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 174 bool SVGPreserveAspectRatio::parse(const LChar*& ptr, const LChar* end, bool validate) 175 { 176 return parseInternal(ptr, end, validate); 177 } 178 179 bool SVGPreserveAspectRatio::parse(const UChar*& ptr, const UChar* end, bool validate) 180 { 181 return parseInternal(ptr, end, validate); 182 } 183 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 278 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 324 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