Home | History | Annotate | Download | only in svg
      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