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 #include "core/svg/SVGPathBlender.h"
     22 
     23 #include "core/platform/animation/AnimationUtilities.h"
     24 #include "core/svg/SVGPathSeg.h"
     25 #include "core/svg/SVGPathSource.h"
     26 #include "wtf/TemporaryChange.h"
     27 
     28 namespace WebCore {
     29 
     30 SVGPathBlender::SVGPathBlender()
     31     : m_fromSource(0)
     32     , m_toSource(0)
     33     , m_consumer(0)
     34     , m_progress(0)
     35     , m_addTypesCount(0)
     36     , m_isInFirstHalfOfAnimation(false)
     37 {
     38 }
     39 
     40 // Helper functions
     41 static inline FloatPoint blendFloatPoint(const FloatPoint& a, const FloatPoint& b, float progress)
     42 {
     43     return FloatPoint(blend(a.x(), b.x(), progress), blend(a.y(), b.y(), progress));
     44 }
     45 
     46 float SVGPathBlender::blendAnimatedDimensonalFloat(float from, float to, FloatBlendMode blendMode)
     47 {
     48     if (m_addTypesCount) {
     49         ASSERT(m_fromMode == m_toMode);
     50         return from + to * m_addTypesCount;
     51     }
     52 
     53     if (m_fromMode == m_toMode)
     54         return blend(from, to, m_progress);
     55 
     56     float fromValue = blendMode == BlendHorizontal ? m_fromCurrentPoint.x() : m_fromCurrentPoint.y();
     57     float toValue = blendMode == BlendHorizontal ? m_toCurrentPoint.x() : m_toCurrentPoint.y();
     58 
     59     // Transform toY to the coordinate mode of fromY
     60     float animValue = blend(from, m_fromMode == AbsoluteCoordinates ? to + toValue : to - toValue, m_progress);
     61 
     62     if (m_isInFirstHalfOfAnimation)
     63         return animValue;
     64 
     65     // Transform the animated point to the coordinate mode, needed for the current progress.
     66     float currentValue = blend(fromValue, toValue, m_progress);
     67     return m_toMode == AbsoluteCoordinates ? animValue + currentValue : animValue - currentValue;
     68 }
     69 
     70 FloatPoint SVGPathBlender::blendAnimatedFloatPoint(const FloatPoint& fromPoint, const FloatPoint& toPoint)
     71 {
     72     if (m_addTypesCount) {
     73         ASSERT(m_fromMode == m_toMode);
     74         FloatPoint repeatedToPoint = toPoint;
     75         repeatedToPoint.scale(m_addTypesCount, m_addTypesCount);
     76         return fromPoint + repeatedToPoint;
     77     }
     78 
     79     if (m_fromMode == m_toMode)
     80         return blendFloatPoint(fromPoint, toPoint, m_progress);
     81 
     82     // Transform toPoint to the coordinate mode of fromPoint
     83     FloatPoint animatedPoint = toPoint;
     84     if (m_fromMode == AbsoluteCoordinates)
     85         animatedPoint += m_toCurrentPoint;
     86     else
     87         animatedPoint.move(-m_toCurrentPoint.x(), -m_toCurrentPoint.y());
     88 
     89     animatedPoint = blendFloatPoint(fromPoint, animatedPoint, m_progress);
     90 
     91     if (m_isInFirstHalfOfAnimation)
     92         return animatedPoint;
     93 
     94     // Transform the animated point to the coordinate mode, needed for the current progress.
     95     FloatPoint currentPoint = blendFloatPoint(m_fromCurrentPoint, m_toCurrentPoint, m_progress);
     96     if (m_toMode == AbsoluteCoordinates)
     97         return animatedPoint + currentPoint;
     98 
     99     animatedPoint.move(-currentPoint.x(), -currentPoint.y());
    100     return animatedPoint;
    101 }
    102 
    103 bool SVGPathBlender::blendMoveToSegment()
    104 {
    105     FloatPoint fromTargetPoint;
    106     FloatPoint toTargetPoint;
    107     if ((m_fromSource->hasMoreData() && !m_fromSource->parseMoveToSegment(fromTargetPoint))
    108         || !m_toSource->parseMoveToSegment(toTargetPoint))
    109         return false;
    110 
    111     m_consumer->moveTo(blendAnimatedFloatPoint(fromTargetPoint, toTargetPoint), false, m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
    112     m_fromCurrentPoint = m_fromMode == AbsoluteCoordinates ? fromTargetPoint : m_fromCurrentPoint + fromTargetPoint;
    113     m_toCurrentPoint = m_toMode == AbsoluteCoordinates ? toTargetPoint : m_toCurrentPoint + toTargetPoint;
    114     return true;
    115 }
    116 
    117 bool SVGPathBlender::blendLineToSegment()
    118 {
    119     FloatPoint fromTargetPoint;
    120     FloatPoint toTargetPoint;
    121     if ((m_fromSource->hasMoreData() && !m_fromSource->parseLineToSegment(fromTargetPoint))
    122         || !m_toSource->parseLineToSegment(toTargetPoint))
    123         return false;
    124 
    125     m_consumer->lineTo(blendAnimatedFloatPoint(fromTargetPoint, toTargetPoint), m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
    126     m_fromCurrentPoint = m_fromMode == AbsoluteCoordinates ? fromTargetPoint : m_fromCurrentPoint + fromTargetPoint;
    127     m_toCurrentPoint = m_toMode == AbsoluteCoordinates ? toTargetPoint : m_toCurrentPoint + toTargetPoint;
    128     return true;
    129 }
    130 
    131 bool SVGPathBlender::blendLineToHorizontalSegment()
    132 {
    133     float fromX = 0;
    134     float toX = 0;
    135     if ((m_fromSource->hasMoreData() && !m_fromSource->parseLineToHorizontalSegment(fromX))
    136         || !m_toSource->parseLineToHorizontalSegment(toX))
    137         return false;
    138 
    139     m_consumer->lineToHorizontal(blendAnimatedDimensonalFloat(fromX, toX, BlendHorizontal), m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
    140     m_fromCurrentPoint.setX(m_fromMode == AbsoluteCoordinates ? fromX : m_fromCurrentPoint.x() + fromX);
    141     m_toCurrentPoint.setX(m_toMode == AbsoluteCoordinates ? toX : m_toCurrentPoint.x() + toX);
    142     return true;
    143 }
    144 
    145 bool SVGPathBlender::blendLineToVerticalSegment()
    146 {
    147     float fromY = 0;
    148     float toY = 0;
    149     if ((m_fromSource->hasMoreData() && !m_fromSource->parseLineToVerticalSegment(fromY))
    150         || !m_toSource->parseLineToVerticalSegment(toY))
    151         return false;
    152 
    153     m_consumer->lineToVertical(blendAnimatedDimensonalFloat(fromY, toY, BlendVertical), m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
    154     m_fromCurrentPoint.setY(m_fromMode == AbsoluteCoordinates ? fromY : m_fromCurrentPoint.y() + fromY);
    155     m_toCurrentPoint.setY(m_toMode == AbsoluteCoordinates ? toY : m_toCurrentPoint.y() + toY);
    156     return true;
    157 }
    158 
    159 bool SVGPathBlender::blendCurveToCubicSegment()
    160 {
    161     FloatPoint fromTargetPoint;
    162     FloatPoint fromPoint1;
    163     FloatPoint fromPoint2;
    164     FloatPoint toTargetPoint;
    165     FloatPoint toPoint1;
    166     FloatPoint toPoint2;
    167     if ((m_fromSource->hasMoreData() && !m_fromSource->parseCurveToCubicSegment(fromPoint1, fromPoint2, fromTargetPoint))
    168         || !m_toSource->parseCurveToCubicSegment(toPoint1, toPoint2, toTargetPoint))
    169         return false;
    170 
    171     m_consumer->curveToCubic(blendAnimatedFloatPoint(fromPoint1, toPoint1),
    172                              blendAnimatedFloatPoint(fromPoint2, toPoint2),
    173                              blendAnimatedFloatPoint(fromTargetPoint, toTargetPoint),
    174                              m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
    175     m_fromCurrentPoint = m_fromMode == AbsoluteCoordinates ? fromTargetPoint : m_fromCurrentPoint + fromTargetPoint;
    176     m_toCurrentPoint = m_toMode == AbsoluteCoordinates ? toTargetPoint : m_toCurrentPoint + toTargetPoint;
    177     return true;
    178 }
    179 
    180 bool SVGPathBlender::blendCurveToCubicSmoothSegment()
    181 {
    182     FloatPoint fromTargetPoint;
    183     FloatPoint fromPoint2;
    184     FloatPoint toTargetPoint;
    185     FloatPoint toPoint2;
    186     if ((m_fromSource->hasMoreData() && !m_fromSource->parseCurveToCubicSmoothSegment(fromPoint2, fromTargetPoint))
    187         || !m_toSource->parseCurveToCubicSmoothSegment(toPoint2, toTargetPoint))
    188         return false;
    189 
    190     m_consumer->curveToCubicSmooth(blendAnimatedFloatPoint(fromPoint2, toPoint2),
    191                                    blendAnimatedFloatPoint(fromTargetPoint, toTargetPoint),
    192                                    m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
    193     m_fromCurrentPoint = m_fromMode == AbsoluteCoordinates ? fromTargetPoint : m_fromCurrentPoint + fromTargetPoint;
    194     m_toCurrentPoint = m_toMode == AbsoluteCoordinates ? toTargetPoint : m_toCurrentPoint + toTargetPoint;
    195     return true;
    196 }
    197 
    198 bool SVGPathBlender::blendCurveToQuadraticSegment()
    199 {
    200     FloatPoint fromTargetPoint;
    201     FloatPoint fromPoint1;
    202     FloatPoint toTargetPoint;
    203     FloatPoint toPoint1;
    204     if ((m_fromSource->hasMoreData() && !m_fromSource->parseCurveToQuadraticSegment(fromPoint1, fromTargetPoint))
    205         || !m_toSource->parseCurveToQuadraticSegment(toPoint1, toTargetPoint))
    206         return false;
    207 
    208     m_consumer->curveToQuadratic(blendAnimatedFloatPoint(fromPoint1, toPoint1),
    209                                  blendAnimatedFloatPoint(fromTargetPoint, toTargetPoint),
    210                                  m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
    211     m_fromCurrentPoint = m_fromMode == AbsoluteCoordinates ? fromTargetPoint : m_fromCurrentPoint + fromTargetPoint;
    212     m_toCurrentPoint = m_toMode == AbsoluteCoordinates ? toTargetPoint : m_toCurrentPoint + toTargetPoint;
    213     return true;
    214 }
    215 
    216 bool SVGPathBlender::blendCurveToQuadraticSmoothSegment()
    217 {
    218     FloatPoint fromTargetPoint;
    219     FloatPoint toTargetPoint;
    220     if ((m_fromSource->hasMoreData() && !m_fromSource->parseCurveToQuadraticSmoothSegment(fromTargetPoint))
    221         || !m_toSource->parseCurveToQuadraticSmoothSegment(toTargetPoint))
    222         return false;
    223 
    224     m_consumer->curveToQuadraticSmooth(blendAnimatedFloatPoint(fromTargetPoint, toTargetPoint), m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
    225     m_fromCurrentPoint = m_fromMode == AbsoluteCoordinates ? fromTargetPoint : m_fromCurrentPoint + fromTargetPoint;
    226     m_toCurrentPoint = m_toMode == AbsoluteCoordinates ? toTargetPoint : m_toCurrentPoint + toTargetPoint;
    227     return true;
    228 }
    229 
    230 bool SVGPathBlender::blendArcToSegment()
    231 {
    232     float fromRx = 0;
    233     float fromRy = 0;
    234     float fromAngle = 0;
    235     bool fromLargeArc = false;
    236     bool fromSweep = false;
    237     FloatPoint fromTargetPoint;
    238     float toRx = 0;
    239     float toRy = 0;
    240     float toAngle = 0;
    241     bool toLargeArc = false;
    242     bool toSweep = false;
    243     FloatPoint toTargetPoint;
    244     if ((m_fromSource->hasMoreData() && !m_fromSource->parseArcToSegment(fromRx, fromRy, fromAngle, fromLargeArc, fromSweep, fromTargetPoint))
    245         || !m_toSource->parseArcToSegment(toRx, toRy, toAngle, toLargeArc, toSweep, toTargetPoint))
    246         return false;
    247 
    248     if (m_addTypesCount) {
    249         ASSERT(m_fromMode == m_toMode);
    250         FloatPoint scaledToTargetPoint = toTargetPoint;
    251         scaledToTargetPoint.scale(m_addTypesCount, m_addTypesCount);
    252         m_consumer->arcTo(fromRx + toRx * m_addTypesCount,
    253                           fromRy + toRy * m_addTypesCount,
    254                           fromAngle + toAngle * m_addTypesCount,
    255                           fromLargeArc || toLargeArc,
    256                           fromSweep || toSweep,
    257                           fromTargetPoint + scaledToTargetPoint,
    258                           m_fromMode);
    259     } else {
    260         m_consumer->arcTo(blend(fromRx, toRx, m_progress),
    261                           blend(fromRy, toRy, m_progress),
    262                           blend(fromAngle, toAngle, m_progress),
    263                           m_isInFirstHalfOfAnimation ? fromLargeArc : toLargeArc,
    264                           m_isInFirstHalfOfAnimation ? fromSweep : toSweep,
    265                           blendAnimatedFloatPoint(fromTargetPoint, toTargetPoint),
    266                           m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
    267     }
    268     m_fromCurrentPoint = m_fromMode == AbsoluteCoordinates ? fromTargetPoint : m_fromCurrentPoint + fromTargetPoint;
    269     m_toCurrentPoint = m_toMode == AbsoluteCoordinates ? toTargetPoint : m_toCurrentPoint + toTargetPoint;
    270     return true;
    271 }
    272 
    273 static inline PathCoordinateMode coordinateModeOfCommand(const SVGPathSegType& type)
    274 {
    275     if (type < PathSegMoveToAbs)
    276         return AbsoluteCoordinates;
    277 
    278     // Odd number = relative command
    279     if (type % 2)
    280         return RelativeCoordinates;
    281 
    282     return AbsoluteCoordinates;
    283 }
    284 
    285 static inline bool isSegmentEqual(const SVGPathSegType& fromType, const SVGPathSegType& toType, const PathCoordinateMode& fromMode, const PathCoordinateMode& toMode)
    286 {
    287     if (fromType == toType && (fromType == PathSegUnknown || fromType == PathSegClosePath))
    288         return true;
    289 
    290     unsigned short from = fromType;
    291     unsigned short to = toType;
    292     if (fromMode == toMode)
    293         return from == to;
    294     if (fromMode == AbsoluteCoordinates)
    295         return from == to - 1;
    296     return to == from - 1;
    297 }
    298 
    299 bool SVGPathBlender::addAnimatedPath(SVGPathSource* fromSource, SVGPathSource* toSource, SVGPathConsumer* consumer, unsigned repeatCount)
    300 {
    301     TemporaryChange<unsigned> change(m_addTypesCount, repeatCount);
    302     return blendAnimatedPath(0, fromSource, toSource, consumer);
    303 }
    304 
    305 bool SVGPathBlender::blendAnimatedPath(float progress, SVGPathSource* fromSource, SVGPathSource* toSource, SVGPathConsumer* consumer)
    306 {
    307     ASSERT(fromSource);
    308     ASSERT(toSource);
    309     ASSERT(consumer);
    310     m_fromSource = fromSource;
    311     m_toSource = toSource;
    312     m_consumer = consumer;
    313     m_isInFirstHalfOfAnimation = progress < 0.5f;
    314     m_progress = progress;
    315 
    316     bool fromSourceHadData = m_fromSource->hasMoreData();
    317     while (m_toSource->hasMoreData()) {
    318         SVGPathSegType fromCommand;
    319         SVGPathSegType toCommand;
    320         if ((fromSourceHadData && !m_fromSource->parseSVGSegmentType(fromCommand)) || !m_toSource->parseSVGSegmentType(toCommand))
    321             return false;
    322 
    323         m_toMode = coordinateModeOfCommand(toCommand);
    324         m_fromMode = fromSourceHadData ? coordinateModeOfCommand(fromCommand) : m_toMode;
    325         if (m_fromMode != m_toMode && m_addTypesCount)
    326             return false;
    327 
    328         if (fromSourceHadData && !isSegmentEqual(fromCommand, toCommand, m_fromMode, m_toMode))
    329             return false;
    330 
    331         switch (toCommand) {
    332         case PathSegMoveToRel:
    333         case PathSegMoveToAbs:
    334             if (!blendMoveToSegment())
    335                 return false;
    336             break;
    337         case PathSegLineToRel:
    338         case PathSegLineToAbs:
    339             if (!blendLineToSegment())
    340                 return false;
    341             break;
    342         case PathSegLineToHorizontalRel:
    343         case PathSegLineToHorizontalAbs:
    344             if (!blendLineToHorizontalSegment())
    345                 return false;
    346             break;
    347         case PathSegLineToVerticalRel:
    348         case PathSegLineToVerticalAbs:
    349             if (!blendLineToVerticalSegment())
    350                 return false;
    351             break;
    352         case PathSegClosePath:
    353             m_consumer->closePath();
    354             break;
    355         case PathSegCurveToCubicRel:
    356         case PathSegCurveToCubicAbs:
    357             if (!blendCurveToCubicSegment())
    358                 return false;
    359             break;
    360         case PathSegCurveToCubicSmoothRel:
    361         case PathSegCurveToCubicSmoothAbs:
    362             if (!blendCurveToCubicSmoothSegment())
    363                 return false;
    364             break;
    365         case PathSegCurveToQuadraticRel:
    366         case PathSegCurveToQuadraticAbs:
    367             if (!blendCurveToQuadraticSegment())
    368                 return false;
    369             break;
    370         case PathSegCurveToQuadraticSmoothRel:
    371         case PathSegCurveToQuadraticSmoothAbs:
    372             if (!blendCurveToQuadraticSmoothSegment())
    373                 return false;
    374             break;
    375         case PathSegArcRel:
    376         case PathSegArcAbs:
    377             if (!blendArcToSegment())
    378                 return false;
    379             break;
    380         case PathSegUnknown:
    381             return false;
    382         }
    383 
    384         if (!fromSourceHadData)
    385             continue;
    386         if (m_fromSource->hasMoreData() != m_toSource->hasMoreData())
    387             return false;
    388         if (!m_fromSource->hasMoreData() || !m_toSource->hasMoreData())
    389             return true;
    390     }
    391 
    392     return true;
    393 }
    394 
    395 void SVGPathBlender::cleanup()
    396 {
    397     ASSERT(m_toSource);
    398     ASSERT(m_fromSource);
    399     ASSERT(m_consumer);
    400 
    401     m_consumer->cleanup();
    402     m_toSource = 0;
    403     m_fromSource = 0;
    404     m_consumer = 0;
    405     m_fromCurrentPoint = FloatPoint();
    406     m_toCurrentPoint = FloatPoint();
    407 }
    408 
    409 }
    410