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