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