Home | History | Annotate | Download | only in svg
      1 /*
      2  * Copyright (C) Research In Motion Limited 2010, 2011. All rights reserved.
      3  *
      4  * This library is free software; you can redistribute it and/or
      5  * modify it under the terms of the GNU Library General Public
      6  * License as published by the Free Software Foundation; either
      7  * version 2 of the License, or (at your option) any later version.
      8  *
      9  * This library is distributed in the hope that it will be useful,
     10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12  * Library General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU Library General Public License
     15  * along with this library; see the file COPYING.LIB.  If not, write to
     16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     17  * Boston, MA 02110-1301, USA.
     18  */
     19 
     20 #include "config.h"
     21 
     22 #if ENABLE(SVG)
     23 #include "SVGPathBlender.h"
     24 
     25 #include "SVGPathSeg.h"
     26 
     27 namespace WebCore {
     28 
     29 SVGPathBlender::SVGPathBlender()
     30     : m_fromSource(0)
     31     , m_toSource(0)
     32     , m_consumer(0)
     33     , m_progress(0)
     34 {
     35 }
     36 
     37 // Helper functions
     38 static inline FloatPoint blendFloatPoint(const FloatPoint& a, const FloatPoint& b, float progress)
     39 {
     40     return FloatPoint((b.x() - a.x()) * progress + a.x(), (b.y() - a.y()) * progress + a.y());
     41 }
     42 
     43 static inline float blendAnimatedFloat(float from, float to, float progress)
     44 {
     45     return (to - from) * progress + from;
     46 }
     47 
     48 float SVGPathBlender::blendAnimatedDimensonalFloat(float from, float to, FloatBlendMode blendMode)
     49 {
     50     if (m_fromMode == m_toMode)
     51         return blendAnimatedFloat(from, to, m_progress);
     52 
     53     float fromValue = blendMode == BlendHorizontal ? m_fromCurrentPoint.x() : m_fromCurrentPoint.y();
     54     float toValue = blendMode == BlendHorizontal ? m_toCurrentPoint.x() : m_toCurrentPoint.y();
     55 
     56     // Transform toY to the coordinate mode of fromY
     57     float animValue = blendAnimatedFloat(from, m_fromMode == AbsoluteCoordinates ? to + toValue : to - toValue, m_progress);
     58 
     59     if (m_isInFirstHalfOfAnimation)
     60         return animValue;
     61 
     62     // Transform the animated point to the coordinate mode, needed for the current progress.
     63     float currentValue = blendAnimatedFloat(fromValue, toValue, m_progress);
     64     return m_toMode == AbsoluteCoordinates ? animValue + currentValue : animValue - currentValue;
     65 }
     66 
     67 FloatPoint SVGPathBlender::blendAnimatedFloatPoint(const FloatPoint& fromPoint, const FloatPoint& toPoint)
     68 {
     69     if (m_fromMode == m_toMode)
     70         return blendFloatPoint(fromPoint, toPoint, m_progress);
     71 
     72     // Transform toPoint to the coordinate mode of fromPoint
     73     FloatPoint animatedPoint = toPoint;
     74     if (m_fromMode == AbsoluteCoordinates)
     75         animatedPoint += m_toCurrentPoint;
     76     else
     77         animatedPoint.move(-m_toCurrentPoint.x(), -m_toCurrentPoint.y());
     78 
     79     animatedPoint = blendFloatPoint(fromPoint, animatedPoint, m_progress);
     80 
     81     if (m_isInFirstHalfOfAnimation)
     82         return animatedPoint;
     83 
     84     // Transform the animated point to the coordinate mode, needed for the current progress.
     85     FloatPoint currentPoint = blendFloatPoint(m_fromCurrentPoint, m_toCurrentPoint, m_progress);
     86     if (m_toMode == AbsoluteCoordinates)
     87         return animatedPoint + currentPoint;
     88 
     89     animatedPoint.move(-currentPoint.x(), -currentPoint.y());
     90     return animatedPoint;
     91 }
     92 
     93 bool SVGPathBlender::blendMoveToSegment()
     94 {
     95     FloatPoint fromTargetPoint;
     96     FloatPoint toTargetPoint;
     97     if (!m_fromSource->parseMoveToSegment(fromTargetPoint)
     98         || !m_toSource->parseMoveToSegment(toTargetPoint))
     99         return false;
    100 
    101     m_consumer->moveTo(blendAnimatedFloatPoint(fromTargetPoint, toTargetPoint), false, m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
    102     m_fromCurrentPoint = m_fromMode == AbsoluteCoordinates ? fromTargetPoint : m_fromCurrentPoint + fromTargetPoint;
    103     m_toCurrentPoint = m_toMode == AbsoluteCoordinates ? toTargetPoint : m_toCurrentPoint + toTargetPoint;
    104     return true;
    105 }
    106 
    107 bool SVGPathBlender::blendLineToSegment()
    108 {
    109     FloatPoint fromTargetPoint;
    110     FloatPoint toTargetPoint;
    111     if (!m_fromSource->parseLineToSegment(fromTargetPoint)
    112         || !m_toSource->parseLineToSegment(toTargetPoint))
    113         return false;
    114 
    115     m_consumer->lineTo(blendAnimatedFloatPoint(fromTargetPoint, toTargetPoint), m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
    116     m_fromCurrentPoint = m_fromMode == AbsoluteCoordinates ? fromTargetPoint : m_fromCurrentPoint + fromTargetPoint;
    117     m_toCurrentPoint = m_toMode == AbsoluteCoordinates ? toTargetPoint : m_toCurrentPoint + toTargetPoint;
    118     return true;
    119 }
    120 
    121 bool SVGPathBlender::blendLineToHorizontalSegment()
    122 {
    123     float fromX;
    124     float toX;
    125     if (!m_fromSource->parseLineToHorizontalSegment(fromX)
    126         || !m_toSource->parseLineToHorizontalSegment(toX))
    127         return false;
    128 
    129     m_consumer->lineToHorizontal(blendAnimatedDimensonalFloat(fromX, toX, BlendHorizontal), m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
    130     m_fromCurrentPoint.setX(m_fromMode == AbsoluteCoordinates ? fromX : m_fromCurrentPoint.x() + fromX);
    131     m_toCurrentPoint.setX(m_toMode == AbsoluteCoordinates ? toX : m_toCurrentPoint.x() + toX);
    132     return true;
    133 }
    134 
    135 bool SVGPathBlender::blendLineToVerticalSegment()
    136 {
    137     float fromY;
    138     float toY;
    139     if (!m_fromSource->parseLineToVerticalSegment(fromY)
    140         || !m_toSource->parseLineToVerticalSegment(toY))
    141         return false;
    142 
    143     m_consumer->lineToVertical(blendAnimatedDimensonalFloat(fromY, toY, BlendVertical), m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
    144     m_fromCurrentPoint.setY(m_fromMode == AbsoluteCoordinates ? fromY : m_fromCurrentPoint.y() + fromY);
    145     m_toCurrentPoint.setY(m_toMode == AbsoluteCoordinates ? toY : m_toCurrentPoint.y() + toY);
    146     return true;
    147 }
    148 
    149 bool SVGPathBlender::blendCurveToCubicSegment()
    150 {
    151     FloatPoint fromTargetPoint;
    152     FloatPoint fromPoint1;
    153     FloatPoint fromPoint2;
    154     FloatPoint toTargetPoint;
    155     FloatPoint toPoint1;
    156     FloatPoint toPoint2;
    157     if (!m_fromSource->parseCurveToCubicSegment(fromPoint1, fromPoint2, fromTargetPoint)
    158         || !m_toSource->parseCurveToCubicSegment(toPoint1, toPoint2, toTargetPoint))
    159         return false;
    160 
    161     m_consumer->curveToCubic(blendAnimatedFloatPoint(fromPoint1, toPoint1),
    162                              blendAnimatedFloatPoint(fromPoint2, toPoint2),
    163                              blendAnimatedFloatPoint(fromTargetPoint, toTargetPoint),
    164                              m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
    165     m_fromCurrentPoint = m_fromMode == AbsoluteCoordinates ? fromTargetPoint : m_fromCurrentPoint + fromTargetPoint;
    166     m_toCurrentPoint = m_toMode == AbsoluteCoordinates ? toTargetPoint : m_toCurrentPoint + toTargetPoint;
    167     return true;
    168 }
    169 
    170 bool SVGPathBlender::blendCurveToCubicSmoothSegment()
    171 {
    172     FloatPoint fromTargetPoint;
    173     FloatPoint fromPoint2;
    174     FloatPoint toTargetPoint;
    175     FloatPoint toPoint2;
    176     if (!m_fromSource->parseCurveToCubicSmoothSegment(fromPoint2, fromTargetPoint)
    177         || !m_toSource->parseCurveToCubicSmoothSegment(toPoint2, toTargetPoint))
    178         return false;
    179 
    180     m_consumer->curveToCubicSmooth(blendAnimatedFloatPoint(fromPoint2, toPoint2),
    181                                    blendAnimatedFloatPoint(fromTargetPoint, toTargetPoint),
    182                                    m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
    183     m_fromCurrentPoint = m_fromMode == AbsoluteCoordinates ? fromTargetPoint : m_fromCurrentPoint + fromTargetPoint;
    184     m_toCurrentPoint = m_toMode == AbsoluteCoordinates ? toTargetPoint : m_toCurrentPoint + toTargetPoint;
    185     return true;
    186 }
    187 
    188 bool SVGPathBlender::blendCurveToQuadraticSegment()
    189 {
    190     FloatPoint fromTargetPoint;
    191     FloatPoint fromPoint1;
    192     FloatPoint toTargetPoint;
    193     FloatPoint toPoint1;
    194     if (!m_fromSource->parseCurveToQuadraticSegment(fromPoint1, fromTargetPoint)
    195         || !m_toSource->parseCurveToQuadraticSegment(toPoint1, toTargetPoint))
    196         return false;
    197 
    198     m_consumer->curveToQuadratic(blendAnimatedFloatPoint(fromPoint1, toPoint1),
    199                                  blendAnimatedFloatPoint(fromTargetPoint, toTargetPoint),
    200                                  m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
    201     m_fromCurrentPoint = m_fromMode == AbsoluteCoordinates ? fromTargetPoint : m_fromCurrentPoint + fromTargetPoint;
    202     m_toCurrentPoint = m_toMode == AbsoluteCoordinates ? toTargetPoint : m_toCurrentPoint + toTargetPoint;
    203     return true;
    204 }
    205 
    206 bool SVGPathBlender::blendCurveToQuadraticSmoothSegment()
    207 {
    208     FloatPoint fromTargetPoint;
    209     FloatPoint toTargetPoint;
    210     if (!m_fromSource->parseCurveToQuadraticSmoothSegment(fromTargetPoint)
    211         || !m_toSource->parseCurveToQuadraticSmoothSegment(toTargetPoint))
    212         return false;
    213 
    214     m_consumer->curveToQuadraticSmooth(blendAnimatedFloatPoint(fromTargetPoint, toTargetPoint), m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
    215     m_fromCurrentPoint = m_fromMode == AbsoluteCoordinates ? fromTargetPoint : m_fromCurrentPoint + fromTargetPoint;
    216     m_toCurrentPoint = m_toMode == AbsoluteCoordinates ? toTargetPoint : m_toCurrentPoint + toTargetPoint;
    217     return true;
    218 }
    219 
    220 bool SVGPathBlender::blendArcToSegment()
    221 {
    222     float fromRx;
    223     float fromRy;
    224     float fromAngle;
    225     bool fromLargeArc;
    226     bool fromSweep;
    227     FloatPoint fromTargetPoint;
    228     float toRx;
    229     float toRy;
    230     float toAngle;
    231     bool toLargeArc;
    232     bool toSweep;
    233     FloatPoint toTargetPoint;
    234     if (!m_fromSource->parseArcToSegment(fromRx, fromRy, fromAngle, fromLargeArc, fromSweep, fromTargetPoint)
    235         || !m_toSource->parseArcToSegment(toRx, toRy, toAngle, toLargeArc, toSweep, toTargetPoint))
    236         return false;
    237 
    238     m_consumer->arcTo(blendAnimatedFloat(fromRx, toRx, m_progress),
    239                       blendAnimatedFloat(fromRy, toRy, m_progress),
    240                       blendAnimatedFloat(fromAngle, toAngle, m_progress),
    241                       m_isInFirstHalfOfAnimation ? fromLargeArc : toLargeArc,
    242                       m_isInFirstHalfOfAnimation ? fromSweep : toSweep,
    243                       blendAnimatedFloatPoint(fromTargetPoint, toTargetPoint),
    244                       m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
    245     m_fromCurrentPoint = m_fromMode == AbsoluteCoordinates ? fromTargetPoint : m_fromCurrentPoint + fromTargetPoint;
    246     m_toCurrentPoint = m_toMode == AbsoluteCoordinates ? toTargetPoint : m_toCurrentPoint + toTargetPoint;
    247     return true;
    248 }
    249 
    250 static inline PathCoordinateMode coordinateModeOfCommand(const SVGPathSegType& type)
    251 {
    252     if (type < PathSegMoveToAbs)
    253         return AbsoluteCoordinates;
    254 
    255     // Odd number = relative command
    256     if (type % 2)
    257         return RelativeCoordinates;
    258 
    259     return AbsoluteCoordinates;
    260 }
    261 
    262 static inline bool isSegmentEqual(const SVGPathSegType& fromType, const SVGPathSegType& toType, const PathCoordinateMode& fromMode, const PathCoordinateMode& toMode)
    263 {
    264     if (fromType == toType && (fromType == PathSegUnknown || fromType == PathSegClosePath))
    265         return true;
    266 
    267     unsigned short from = fromType;
    268     unsigned short to = toType;
    269     if (fromMode == toMode)
    270         return from == to;
    271     if (fromMode == AbsoluteCoordinates)
    272         return from == to - 1;
    273     return to == from - 1;
    274 }
    275 
    276 bool SVGPathBlender::blendAnimatedPath(float progress, SVGPathSource* fromSource, SVGPathSource* toSource, SVGPathConsumer* consumer)
    277 {
    278     ASSERT(fromSource);
    279     ASSERT(toSource);
    280     ASSERT(consumer);
    281     m_fromSource = fromSource;
    282     m_toSource = toSource;
    283     m_consumer = consumer;
    284     m_isInFirstHalfOfAnimation = progress < 0.5f;
    285 
    286     m_progress = progress;
    287     while (true) {
    288         SVGPathSegType fromCommand;
    289         SVGPathSegType toCommand;
    290         if (!m_fromSource->parseSVGSegmentType(fromCommand) || !m_toSource->parseSVGSegmentType(toCommand))
    291             return false;
    292 
    293         m_fromMode = coordinateModeOfCommand(fromCommand);
    294         m_toMode = coordinateModeOfCommand(toCommand);
    295         if (!isSegmentEqual(fromCommand, toCommand, m_fromMode, m_toMode))
    296             return false;
    297 
    298         switch (fromCommand) {
    299         case PathSegMoveToRel:
    300         case PathSegMoveToAbs:
    301             if (!blendMoveToSegment())
    302                 return false;
    303             break;
    304         case PathSegLineToRel:
    305         case PathSegLineToAbs:
    306             if (!blendLineToSegment())
    307                 return false;
    308             break;
    309         case PathSegLineToHorizontalRel:
    310         case PathSegLineToHorizontalAbs:
    311             if (!blendLineToHorizontalSegment())
    312                 return false;
    313             break;
    314         case PathSegLineToVerticalRel:
    315         case PathSegLineToVerticalAbs:
    316             if (!blendLineToVerticalSegment())
    317                 return false;
    318             break;
    319         case PathSegClosePath:
    320             m_consumer->closePath();
    321             break;
    322         case PathSegCurveToCubicRel:
    323         case PathSegCurveToCubicAbs:
    324             if (!blendCurveToCubicSegment())
    325                 return false;
    326             break;
    327         case PathSegCurveToCubicSmoothRel:
    328         case PathSegCurveToCubicSmoothAbs:
    329             if (!blendCurveToCubicSmoothSegment())
    330                 return false;
    331             break;
    332         case PathSegCurveToQuadraticRel:
    333         case PathSegCurveToQuadraticAbs:
    334             if (!blendCurveToQuadraticSegment())
    335                 return false;
    336             break;
    337         case PathSegCurveToQuadraticSmoothRel:
    338         case PathSegCurveToQuadraticSmoothAbs:
    339             if (!blendCurveToQuadraticSmoothSegment())
    340                 return false;
    341             break;
    342         case PathSegArcRel:
    343         case PathSegArcAbs:
    344             if (!blendArcToSegment())
    345                 return false;
    346             break;
    347         default:
    348             return false;
    349         }
    350         if (m_fromSource->hasMoreData() != m_toSource->hasMoreData())
    351             return false;
    352         if (!m_fromSource->hasMoreData() || !m_toSource->hasMoreData())
    353             break;
    354     }
    355     return true;
    356 }
    357 
    358 void SVGPathBlender::cleanup()
    359 {
    360     ASSERT(m_toSource);
    361     ASSERT(m_fromSource);
    362     ASSERT(m_consumer);
    363 
    364     m_consumer->cleanup();
    365     m_toSource = 0;
    366     m_fromSource = 0;
    367     m_consumer = 0;
    368     m_fromCurrentPoint = FloatPoint();
    369     m_toCurrentPoint = FloatPoint();
    370 }
    371 
    372 }
    373 
    374 #endif // ENABLE(SVG)
    375