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