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