1 /* 2 Copyright (C) 2004, 2005, 2008 Nikolas Zimmermann <zimmermann (at) kde.org> 3 2004, 2005, 2006, 2007 Rob Buis <buis (at) kde.org> 4 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 "SVGParserUtilities.h" 29 #include "SVGSVGElement.h" 30 31 namespace WebCore { 32 33 SVGPreserveAspectRatio::SVGPreserveAspectRatio() 34 : m_align(SVG_PRESERVEASPECTRATIO_XMIDYMID) 35 , m_meetOrSlice(SVG_MEETORSLICE_MEET) 36 { 37 // FIXME: Should the two values default to UNKNOWN instead? 38 } 39 40 SVGPreserveAspectRatio::~SVGPreserveAspectRatio() 41 { 42 } 43 44 void SVGPreserveAspectRatio::setAlign(unsigned short align) 45 { 46 m_align = align; 47 } 48 49 unsigned short SVGPreserveAspectRatio::align() const 50 { 51 return m_align; 52 } 53 54 void SVGPreserveAspectRatio::setMeetOrSlice(unsigned short meetOrSlice) 55 { 56 m_meetOrSlice = meetOrSlice; 57 } 58 59 unsigned short SVGPreserveAspectRatio::meetOrSlice() const 60 { 61 return m_meetOrSlice; 62 } 63 64 SVGPreserveAspectRatio SVGPreserveAspectRatio::parsePreserveAspectRatio(const UChar*& currParam, const UChar* end, bool validate, bool& result) 65 { 66 SVGPreserveAspectRatio aspectRatio; 67 aspectRatio.m_align = SVG_PRESERVEASPECTRATIO_NONE; 68 aspectRatio.m_meetOrSlice = SVG_MEETORSLICE_MEET; 69 result = false; 70 71 if (!skipOptionalSpaces(currParam, end)) 72 goto bail_out; 73 74 if (*currParam == 'd') { 75 if (!skipString(currParam, end, "defer")) 76 goto bail_out; 77 // FIXME: We just ignore the "defer" here. 78 if (!skipOptionalSpaces(currParam, end)) 79 goto bail_out; 80 } 81 82 if (*currParam == 'n') { 83 if (!skipString(currParam, end, "none")) 84 goto bail_out; 85 skipOptionalSpaces(currParam, end); 86 } else if (*currParam == 'x') { 87 if ((end - currParam) < 8) 88 goto bail_out; 89 if (currParam[1] != 'M' || currParam[4] != 'Y' || currParam[5] != 'M') 90 goto bail_out; 91 if (currParam[2] == 'i') { 92 if (currParam[3] == 'n') { 93 if (currParam[6] == 'i') { 94 if (currParam[7] == 'n') 95 aspectRatio.m_align = SVG_PRESERVEASPECTRATIO_XMINYMIN; 96 else if (currParam[7] == 'd') 97 aspectRatio.m_align = SVG_PRESERVEASPECTRATIO_XMINYMID; 98 else 99 goto bail_out; 100 } else if (currParam[6] == 'a' && currParam[7] == 'x') 101 aspectRatio.m_align = SVG_PRESERVEASPECTRATIO_XMINYMAX; 102 else 103 goto bail_out; 104 } else if (currParam[3] == 'd') { 105 if (currParam[6] == 'i') { 106 if (currParam[7] == 'n') 107 aspectRatio.m_align = SVG_PRESERVEASPECTRATIO_XMIDYMIN; 108 else if (currParam[7] == 'd') 109 aspectRatio.m_align = SVG_PRESERVEASPECTRATIO_XMIDYMID; 110 else 111 goto bail_out; 112 } else if (currParam[6] == 'a' && currParam[7] == 'x') 113 aspectRatio.m_align = SVG_PRESERVEASPECTRATIO_XMIDYMAX; 114 else 115 goto bail_out; 116 } else 117 goto bail_out; 118 } else if (currParam[2] == 'a' && currParam[3] == 'x') { 119 if (currParam[6] == 'i') { 120 if (currParam[7] == 'n') 121 aspectRatio.m_align = SVG_PRESERVEASPECTRATIO_XMAXYMIN; 122 else if (currParam[7] == 'd') 123 aspectRatio.m_align = SVG_PRESERVEASPECTRATIO_XMAXYMID; 124 else 125 goto bail_out; 126 } else if (currParam[6] == 'a' && currParam[7] == 'x') 127 aspectRatio.m_align = SVG_PRESERVEASPECTRATIO_XMAXYMAX; 128 else 129 goto bail_out; 130 } else 131 goto bail_out; 132 currParam += 8; 133 skipOptionalSpaces(currParam, end); 134 } else 135 goto bail_out; 136 137 if (currParam < end) { 138 if (*currParam == 'm') { 139 if (!skipString(currParam, end, "meet")) 140 goto bail_out; 141 skipOptionalSpaces(currParam, end); 142 } else if (*currParam == 's') { 143 if (!skipString(currParam, end, "slice")) 144 goto bail_out; 145 skipOptionalSpaces(currParam, end); 146 if (aspectRatio.m_align != SVG_PRESERVEASPECTRATIO_NONE) 147 aspectRatio.m_meetOrSlice = SVG_MEETORSLICE_SLICE; 148 } 149 } 150 151 if (end != currParam && validate) { 152 bail_out: 153 // FIXME: Should the two values be set to UNKNOWN instead? 154 aspectRatio.m_align = SVG_PRESERVEASPECTRATIO_NONE; 155 aspectRatio.m_meetOrSlice = SVG_MEETORSLICE_MEET; 156 } else 157 result = true; 158 159 return aspectRatio; 160 } 161 162 void SVGPreserveAspectRatio::transformRect(FloatRect& destRect, FloatRect& srcRect) 163 { 164 FloatSize imageSize = srcRect.size(); 165 float origDestWidth = destRect.width(); 166 float origDestHeight = destRect.height(); 167 if (meetOrSlice() == SVGPreserveAspectRatio::SVG_MEETORSLICE_MEET) { 168 float widthToHeightMultiplier = srcRect.height() / srcRect.width(); 169 if (origDestHeight > (origDestWidth * widthToHeightMultiplier)) { 170 destRect.setHeight(origDestWidth * widthToHeightMultiplier); 171 switch (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.0f - destRect.height() / 2.0f); 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 } 183 } 184 if (origDestWidth > (origDestHeight / widthToHeightMultiplier)) { 185 destRect.setWidth(origDestHeight / widthToHeightMultiplier); 186 switch (align()) { 187 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN: 188 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: 189 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: 190 destRect.setX(destRect.x() + origDestWidth / 2.0f - destRect.width() / 2.0f); 191 break; 192 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN: 193 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: 194 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: 195 destRect.setX(destRect.x() + origDestWidth - destRect.width()); 196 break; 197 } 198 } 199 } else if (meetOrSlice() == SVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE) { 200 float widthToHeightMultiplier = srcRect.height() / srcRect.width(); 201 // if the destination height is less than the height of the image we'll be drawing 202 if (origDestHeight < (origDestWidth * widthToHeightMultiplier)) { 203 float destToSrcMultiplier = srcRect.width() / destRect.width(); 204 srcRect.setHeight(destRect.height() * destToSrcMultiplier); 205 switch (align()) { 206 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID: 207 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: 208 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: 209 srcRect.setY(destRect.y() + imageSize.height() / 2.0f - srcRect.height() / 2.0f); 210 break; 211 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX: 212 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: 213 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: 214 srcRect.setY(destRect.y() + imageSize.height() - srcRect.height()); 215 break; 216 } 217 } 218 // if the destination width is less than the width of the image we'll be drawing 219 if (origDestWidth < (origDestHeight / widthToHeightMultiplier)) { 220 float destToSrcMultiplier = srcRect.height() / destRect.height(); 221 srcRect.setWidth(destRect.width() * destToSrcMultiplier); 222 switch (align()) { 223 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN: 224 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: 225 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: 226 srcRect.setX(destRect.x() + imageSize.width() / 2.0f - srcRect.width() / 2.0f); 227 break; 228 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN: 229 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: 230 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: 231 srcRect.setX(destRect.x() + imageSize.width() - srcRect.width()); 232 break; 233 } 234 } 235 } 236 } 237 238 AffineTransform SVGPreserveAspectRatio::getCTM(double logicX, double logicY, 239 double logicWidth, double logicHeight, 240 double /*physX*/, double /*physY*/, 241 double physWidth, double physHeight) const 242 { 243 AffineTransform temp; 244 245 if (align() == SVG_PRESERVEASPECTRATIO_UNKNOWN) 246 return temp; 247 248 double vpar = logicWidth / logicHeight; 249 double svgar = physWidth / physHeight; 250 251 if (align() == SVG_PRESERVEASPECTRATIO_NONE) { 252 temp.scaleNonUniform(physWidth / logicWidth, physHeight / logicHeight); 253 temp.translate(-logicX, -logicY); 254 } else if ((vpar < svgar && (meetOrSlice() == SVG_MEETORSLICE_MEET)) || (vpar >= svgar && (meetOrSlice() == SVG_MEETORSLICE_SLICE))) { 255 temp.scaleNonUniform(physHeight / logicHeight, physHeight / logicHeight); 256 257 if (align() == SVG_PRESERVEASPECTRATIO_XMINYMIN || align() == SVG_PRESERVEASPECTRATIO_XMINYMID || align() == SVG_PRESERVEASPECTRATIO_XMINYMAX) 258 temp.translate(-logicX, -logicY); 259 else if (align() == SVG_PRESERVEASPECTRATIO_XMIDYMIN || align() == SVG_PRESERVEASPECTRATIO_XMIDYMID || align() == SVG_PRESERVEASPECTRATIO_XMIDYMAX) 260 temp.translate(-logicX - (logicWidth - physWidth * logicHeight / physHeight) / 2, -logicY); 261 else 262 temp.translate(-logicX - (logicWidth - physWidth * logicHeight / physHeight), -logicY); 263 } else { 264 temp.scaleNonUniform(physWidth / logicWidth, physWidth / logicWidth); 265 266 if (align() == SVG_PRESERVEASPECTRATIO_XMINYMIN || align() == SVG_PRESERVEASPECTRATIO_XMIDYMIN || align() == SVG_PRESERVEASPECTRATIO_XMAXYMIN) 267 temp.translate(-logicX, -logicY); 268 else if (align() == SVG_PRESERVEASPECTRATIO_XMINYMID || align() == SVG_PRESERVEASPECTRATIO_XMIDYMID || align() == SVG_PRESERVEASPECTRATIO_XMAXYMID) 269 temp.translate(-logicX, -logicY - (logicHeight - physHeight * logicWidth / physWidth) / 2); 270 else 271 temp.translate(-logicX, -logicY - (logicHeight - physHeight * logicWidth / physWidth)); 272 } 273 274 return temp; 275 } 276 277 String SVGPreserveAspectRatio::valueAsString() const 278 { 279 String result; 280 281 switch ((SVGPreserveAspectRatioType) align()) { 282 default: 283 case SVG_PRESERVEASPECTRATIO_NONE: 284 result = "none"; 285 break; 286 case SVG_PRESERVEASPECTRATIO_XMINYMIN: 287 result = "xMinYMin"; 288 break; 289 case SVG_PRESERVEASPECTRATIO_XMIDYMIN: 290 result = "xMidYMin"; 291 break; 292 case SVG_PRESERVEASPECTRATIO_XMAXYMIN: 293 result = "xMaxYMin"; 294 break; 295 case SVG_PRESERVEASPECTRATIO_XMINYMID: 296 result = "xMinYMid"; 297 break; 298 case SVG_PRESERVEASPECTRATIO_XMIDYMID: 299 result = "xMidYMid"; 300 break; 301 case SVG_PRESERVEASPECTRATIO_XMAXYMID: 302 result = "xMaxYMid"; 303 break; 304 case SVG_PRESERVEASPECTRATIO_XMINYMAX: 305 result = "xMinYMax"; 306 break; 307 case SVG_PRESERVEASPECTRATIO_XMIDYMAX: 308 result = "xMidYMax"; 309 break; 310 case SVG_PRESERVEASPECTRATIO_XMAXYMAX: 311 result = "xMaxYMax"; 312 break; 313 }; 314 315 switch ((SVGMeetOrSliceType) meetOrSlice()) { 316 default: 317 case SVG_MEETORSLICE_UNKNOWN: 318 break; 319 case SVG_MEETORSLICE_MEET: 320 result += " meet"; 321 break; 322 case SVG_MEETORSLICE_SLICE: 323 result += " slice"; 324 break; 325 }; 326 327 return result; 328 } 329 330 } 331 332 #endif // ENABLE(SVG) 333