src/hbcore/effects/hbeffectscale.cpp
changeset 0 16d8024aca5e
child 1 f7ac710697a9
equal deleted inserted replaced
-1:000000000000 0:16d8024aca5e
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (developer.feedback@nokia.com)
       
     6 **
       
     7 ** This file is part of the HbCore module of the UI Extensions for Mobile.
       
     8 **
       
     9 ** GNU Lesser General Public License Usage
       
    10 ** This file may be used under the terms of the GNU Lesser General Public
       
    11 ** License version 2.1 as published by the Free Software Foundation and
       
    12 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
       
    13 ** Please review the following information to ensure the GNU Lesser General
       
    14 ** Public License version 2.1 requirements will be met:
       
    15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    16 **
       
    17 ** In addition, as a special exception, Nokia gives you certain additional
       
    18 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    20 **
       
    21 ** If you have questions regarding the use of this file, please contact
       
    22 ** Nokia at developer.feedback@nokia.com.
       
    23 **
       
    24 ****************************************************************************/
       
    25 #include "hbeffectscale_p.h"
       
    26 #include "hbeffectanimation_p.h"
       
    27 #include "hbeffectutils_p.h"
       
    28 #include "hbeffectgroup_p.h"
       
    29 #include "hbeffectscaleanimation_p.h"
       
    30 #include "hbeffectdef_p.h"
       
    31 #include <QGraphicsItem>
       
    32 #include <QtDebug>
       
    33 #include <QVariantAnimation>
       
    34 
       
    35 HbEffectScaleAnimation::HbEffectScaleAnimation(
       
    36     HbEffectGroup *group,
       
    37     HbEffectScale *effect,
       
    38     int duration ) :
       
    39         HbEffectAnimation(),
       
    40         mGroup(group),
       
    41         mEffect(effect),
       
    42         mCenter(0),
       
    43         mCurrentScaling(1.0)
       
    44 {
       
    45     setDuration(duration);
       
    46 }
       
    47 
       
    48 void HbEffectScaleAnimation::setCenter(qreal center)
       
    49 {
       
    50     mCenter = center;
       
    51 }
       
    52 
       
    53 void HbEffectScaleAnimation::handleAnimationUpdate(const QVariant &value)
       
    54 {
       
    55     if (mGroup->isRunning()) {
       
    56         mCurrentScaling = qVariantValue<qreal>(value);
       
    57         // Prevent negative scale factors, they crash QT.
       
    58         if (mCurrentScaling < 0) {
       
    59             mCurrentScaling = 0;
       
    60         }
       
    61         mGroup->updateItemTransform();
       
    62     }
       
    63 }
       
    64 
       
    65 void HbEffectScaleAnimation::handleAnimationFinish()
       
    66 {
       
    67     // This informs the effect group when both scale_x and scale_y have finished
       
    68     mEffect->handleAnimationFinished();
       
    69 }
       
    70 
       
    71 // -----------------------------------------------------------------------------
       
    72 
       
    73 // HbEffectScale
       
    74 
       
    75 HbEffectScale::HbEffectScale(
       
    76     const QList<HbEffectFxmlParamData> &data,
       
    77     QGraphicsItem *item,
       
    78     HbEffectGroup *group) :
       
    79         HbEffectAbstract(0, item, group),
       
    80         mAnimationX(0),
       
    81         mAnimationY(0)
       
    82 {
       
    83     int durationX = 0;
       
    84     int durationY = 0;
       
    85     QEasingCurve curveX = QEasingCurve::Linear;
       
    86     QEasingCurve curveY = QEasingCurve::Linear;
       
    87 
       
    88     // Default values if not defined in the data. Value 1.0f means the item's "identity" i.e. its normal size.
       
    89     mStartXValue = 1.0f;
       
    90     mEndXValue = 1.0f;
       
    91     mStartYValue = 1.0f;
       
    92     mEndYValue = 1.0f;
       
    93     mCenterXValue = 0;
       
    94     mCenterYValue = 0;
       
    95 
       
    96     const HbEffectFxmlParamData* paramX = 0;
       
    97     const HbEffectFxmlParamData* paramY = 0;
       
    98 
       
    99     Q_FOREACH(const HbEffectFxmlParamData &param, data) {
       
   100 	    if (param.name() == FXML_KEYWORD_SCALE_X) {
       
   101             mKeyFrameListX = param.keyFrames();
       
   102 			// <start> for scale_x
       
   103 			mStartWidth =  param.getAttribute(FXML_KEYWORD_START);
       
   104 			mStartWidthRef = param.startRef();
       
   105 			// <end> for scale_x
       
   106 			mEndWidth = param.getAttribute(FXML_KEYWORD_END);
       
   107 			mEndWidthRef = param.endRef();
       
   108 			// duration and curvepath
       
   109             HbEffectUtils::resolveFxmlDuration(durationX, param);
       
   110             HbEffectUtils::resolveFxmlCurveShape(curveX, param);
       
   111 
       
   112             // Only if "start ref" and "end ref" are not used, read parameter's "ref" attribute
       
   113             if (mStartWidthRef.isEmpty() && mEndWidthRef.isEmpty()) {
       
   114                 mParamRefX = param.getAttribute(FXML_PARAM_REF);
       
   115             }
       
   116 
       
   117             paramX = &param;
       
   118 	    }
       
   119 	    else if (param.name() == FXML_KEYWORD_SCALE_Y) {
       
   120             mKeyFrameListY = param.keyFrames();
       
   121 			// <start> for scale_y
       
   122 			mStartHeight =  param.getAttribute(FXML_KEYWORD_START);
       
   123 			mStartHeightRef = param.startRef();
       
   124 			// <end> for scale_y
       
   125 			mEndHeight = param.getAttribute(FXML_KEYWORD_END);
       
   126 			mEndHeightRef = param.endRef();
       
   127             HbEffectUtils::resolveFxmlDuration(durationY, param);
       
   128             HbEffectUtils::resolveFxmlCurveShape(curveY, param);
       
   129 
       
   130             // Only if "start ref" and "end ref" are not used, read parameter's "ref" attribute
       
   131             if (mStartHeightRef.isEmpty() && mEndHeightRef.isEmpty()) {
       
   132                 mParamRefY = param.getAttribute(FXML_PARAM_REF);
       
   133             }
       
   134 
       
   135             paramY = &param;
       
   136 	    }
       
   137 	    else if (param.name() == FXML_KEYWORD_SCALE_ORIGIN_X) {
       
   138             mCenterX = param.getValue();
       
   139             mCenterXRef = param.getAttribute(FXML_PARAM_REF);
       
   140         }                        
       
   141 	    else if (param.name() == FXML_KEYWORD_SCALE_ORIGIN_Y) {
       
   142             mCenterY = param.getValue();
       
   143             mCenterYRef = param.getAttribute(FXML_PARAM_REF);
       
   144 	    }
       
   145     }
       
   146 
       
   147     // Validate references. If start and end references are used, at least one of them must be a visual reference.
       
   148     if ((mStartWidthRef.isEmpty() || mStartWidthRef.startsWith(FXML_VISUAL) ||
       
   149          mEndWidthRef.isEmpty() || mEndWidthRef.startsWith(FXML_VISUAL)) &&
       
   150         (mStartHeightRef.isEmpty() || mStartHeightRef.startsWith(FXML_VISUAL) ||
       
   151          mEndHeightRef.isEmpty() || mEndHeightRef.startsWith(FXML_VISUAL))) {
       
   152         // references ok
       
   153     } else {
       
   154         // Invalid references - disable effect
       
   155         durationX = 0;
       
   156         durationY = 0;
       
   157 
       
   158         mParamRefX.clear();
       
   159         mStartWidthRef.clear();
       
   160         mStartWidth.clear();
       
   161         mEndWidthRef.clear();
       
   162         mEndWidth.clear();
       
   163 
       
   164         mParamRefY.clear();
       
   165         mStartHeightRef.clear();
       
   166         mStartHeight.clear();
       
   167         mEndHeightRef.clear();
       
   168         mEndHeight.clear();
       
   169 
       
   170         mCenterX.clear();
       
   171         mCenterXRef.clear();
       
   172         mCenterY.clear();
       
   173         mCenterYRef.clear();
       
   174     }
       
   175 
       
   176     if (durationX > 0) {
       
   177         mAnimationX = new HbEffectScaleAnimation(group, this, durationX);
       
   178         mAnimationX->addLooping(paramX, group);
       
   179         mAnimationX->mCurve = curveX;
       
   180     }
       
   181     // Create at least one animation even if both durations are zero for handling animation finish easily.
       
   182     if (durationY > 0 || !mAnimationX) {
       
   183         mAnimationY = new HbEffectScaleAnimation(group, this, durationY);
       
   184         mAnimationY->addLooping(paramY, group);
       
   185         mAnimationY->mCurve = curveY;
       
   186     }
       
   187 }
       
   188 
       
   189 
       
   190 /* deletes the effect and rewinds the animation so that next animation can star,
       
   191     rewind is necessary as the general use case is that the effect is restarted before 
       
   192     being complete, in which case the rewind would transform the item and prepare for
       
   193     the next effect.
       
   194 */
       
   195 HbEffectScale::~HbEffectScale()
       
   196 {
       
   197     if (mAnimationX) {
       
   198         mAnimationX->stop();
       
   199         // Applying the cancel policy so that if dynamically new effect is started HbEffect::start then this finishes
       
   200         // and the new one starts.
       
   201         // This will set the endstate for the object correctly when destroying effect
       
   202         delete mAnimationX;
       
   203     }
       
   204     if (mAnimationY) {
       
   205         mAnimationY->stop();
       
   206         delete mAnimationY;
       
   207     }
       
   208 }
       
   209 
       
   210 QString HbEffectScale::name() const
       
   211 {
       
   212     return HB_EFFECT_NAME_SCALE;
       
   213 }
       
   214 
       
   215 void HbEffectScale::init()
       
   216 {
       
   217     QSizeF orgSize = item()->boundingRect().size();
       
   218 
       
   219     // Resolve Dynamic attributes at this point
       
   220     bool valueOk;
       
   221     qreal value;
       
   222 
       
   223     const QRectF &extRect = group()->extRect();
       
   224 
       
   225     // start width
       
   226     if (!mStartWidth.isEmpty()) {
       
   227         value = HbEffectUtils::resolveFxmlRef(
       
   228             mStartWidthRef, mStartWidth, &valueOk, item(), HbEffectUtils::Size, extRect);
       
   229         if (valueOk) {
       
   230             mStartXValue = value;
       
   231         }
       
   232     }
       
   233     // start height
       
   234     if (!mStartHeight.isEmpty()) {
       
   235         value = HbEffectUtils::resolveFxmlRef(mStartHeightRef, mStartHeight, &valueOk, item(), HbEffectUtils::Size, extRect);
       
   236         if (valueOk) {
       
   237             mStartYValue = value;
       
   238         }
       
   239     }
       
   240 
       
   241     // end width
       
   242     if (!mEndWidth.isEmpty()) {
       
   243         value = HbEffectUtils::resolveFxmlRef(
       
   244             mEndWidthRef, mEndWidth, &valueOk, item(), HbEffectUtils::Size, extRect);
       
   245 
       
   246         if (valueOk) {
       
   247             mEndXValue = value;
       
   248         }
       
   249     }
       
   250     // end height
       
   251     if (!mEndHeight.isEmpty()) {
       
   252         value = HbEffectUtils::resolveFxmlRef(
       
   253             mEndHeightRef, mEndHeight, &valueOk, item(), HbEffectUtils::Size, extRect);
       
   254 
       
   255         if (valueOk) {
       
   256             mEndYValue = value;
       
   257         }
       
   258     }
       
   259 
       
   260     // CenterX
       
   261     value = HbEffectUtils::resolveFxmlRef(
       
   262         mCenterXRef, mCenterX, &valueOk, item(), HbEffectUtils::CenterMappedToTargetRect, extRect);
       
   263     
       
   264     if (valueOk) {
       
   265         mCenterXValue = value;
       
   266     }
       
   267     // CenterY
       
   268     value = HbEffectUtils::resolveFxmlRef(
       
   269         mCenterYRef, mCenterY, &valueOk, item(), HbEffectUtils::CenterMappedToTargetRect, extRect);
       
   270 
       
   271     if (valueOk) {
       
   272         mCenterYValue = value;
       
   273     }
       
   274 
       
   275     // QT's scale animations do not handle 0 value correctly so change it here.
       
   276     if (HbEffectUtils::fuzzyIsNull(mStartXValue)) {
       
   277         mStartXValue = 0.00002;
       
   278     }
       
   279     if (HbEffectUtils::fuzzyIsNull(mStartYValue)) {
       
   280         mStartYValue = 0.00002;
       
   281     }
       
   282     if (HbEffectUtils::fuzzyIsNull(mEndXValue)) {
       
   283         mEndXValue = 0.00002;
       
   284     }
       
   285     if (HbEffectUtils::fuzzyIsNull(mEndYValue)) {
       
   286         mEndYValue = 0.00002;
       
   287     }
       
   288 
       
   289     if (mAnimationX) {
       
   290         // Start and end values are relative to item's size so value 1 means normal size
       
   291         mAnimationX->setStartValue(QVariant(mStartXValue));
       
   292         mAnimationX->setEndValue(QVariant(mEndXValue));
       
   293         // Center value is in pixels
       
   294         mAnimationX->setCenter(mCenterXValue);
       
   295 
       
   296         bool startEndRefUsed = !mStartWidthRef.isEmpty() && !mEndWidthRef.isEmpty();
       
   297 
       
   298         qreal paramRefValueX = 0.0;
       
   299         
       
   300         // Resolve SCALE_X parameter's "ref" value only if that's needed
       
   301         if (!startEndRefUsed) {
       
   302             paramRefValueX = HbEffectUtils::resolveFxmlRef(
       
   303                 mParamRefX, "1", &valueOk, item(), HbEffectUtils::Size, extRect);
       
   304         }
       
   305 
       
   306         // Set keyframes in animation
       
   307         foreach(const HbKeyFrame &kf, mKeyFrameListX) {
       
   308 
       
   309             // If start and end references are used,
       
   310             // value at given step is (1-c)*startX + c*endX where c is the keyframe coefficient value
       
   311             if (startEndRefUsed) {
       
   312                 value = (1 - kf.val) * mStartXValue + kf.val * mEndXValue;
       
   313             }
       
   314             // Otherwise c defines the value
       
   315             else {
       
   316                 value = kf.val;
       
   317                 // Multiply keyframe's value with parameter's "ref" if it is defined
       
   318                 if (valueOk) {
       
   319                     value *= paramRefValueX;
       
   320                 }
       
   321             }
       
   322 
       
   323             mAnimationX->setKeyValueAt(kf.pos, QVariant(value));
       
   324 
       
   325             // Update also stored start and end values if keyframe refers to those
       
   326             if (HbEffectUtils::fuzzyIsNull(kf.pos)) {
       
   327                 mStartXValue = value;
       
   328             } else if (HbEffectUtils::fuzzyIsOneOrGreater(kf.pos)) {
       
   329                 mEndXValue = value;
       
   330             }
       
   331         }
       
   332     }
       
   333     if (mAnimationY) {
       
   334         // Start and end values are relative to item's size so value 1 means normal size
       
   335         mAnimationY->setStartValue(QVariant(mStartYValue));
       
   336         mAnimationY->setEndValue(QVariant(mEndYValue));
       
   337         // Center value is in pixels
       
   338         mAnimationY->setCenter(mCenterYValue);
       
   339 
       
   340         bool startEndRefUsed = !mStartHeightRef.isEmpty() && !mEndHeightRef.isEmpty();
       
   341 
       
   342         qreal paramRefValueY = 0.0;
       
   343         // Resolve SCALE_Y parameter's "ref" value only if that's needed
       
   344         if (!startEndRefUsed) {
       
   345             paramRefValueY = HbEffectUtils::resolveFxmlRef(
       
   346                 mParamRefY, "1", &valueOk, item(), HbEffectUtils::Size, extRect);
       
   347         }
       
   348 
       
   349         // Set keyframes in animation
       
   350         foreach(const HbKeyFrame &kf, mKeyFrameListY) {
       
   351             // If start and end references are used,
       
   352             // value at given step is (1-c)*startY + c*endY where c is the keyframe coefficient value
       
   353             if (startEndRefUsed) {
       
   354                 value = (1 - kf.val) * mStartYValue + kf.val * mEndYValue;
       
   355             }
       
   356             // Otherwise c defines the value
       
   357             else {
       
   358                 value = kf.val;
       
   359                 // Multiply keyframe's value with parameter's "ref" if it is defined
       
   360                 if (valueOk) {
       
   361                     value *= paramRefValueY;
       
   362                 }
       
   363             }
       
   364 
       
   365             mAnimationY->setKeyValueAt(kf.pos, QVariant(value));
       
   366 
       
   367             // Update also stored start and end values if keyframe refers to those
       
   368             if (HbEffectUtils::fuzzyIsNull(kf.pos)) {
       
   369                 mStartYValue = value;
       
   370             } else if (HbEffectUtils::fuzzyIsOneOrGreater(kf.pos)) {
       
   371                 mEndYValue = value;
       
   372             }
       
   373         }
       
   374     }
       
   375 }
       
   376 
       
   377 void HbEffectScale::setStartState(QTransform &transform)
       
   378 {
       
   379     // Set the start state
       
   380     updateItemTransform(transform);
       
   381 }
       
   382 
       
   383 /* Starts the scale effect animation. If the effect is already started,
       
   384 the result is undefined.
       
   385 */
       
   386 void HbEffectScale::start()
       
   387 {
       
   388     if (mAnimationX) {
       
   389         mAnimationX->stop(); // rewind the animation before starting again
       
   390         mAnimationX->setCurrentTime(0);
       
   391         mAnimationX->mFinished = false;
       
   392         mAnimationX->start();
       
   393     }
       
   394     if (mAnimationY) {
       
   395         mAnimationY->stop(); // rewind the animation before starting again
       
   396         mAnimationY->setCurrentTime(0);
       
   397         mAnimationY->mFinished = false;
       
   398         mAnimationY->start();
       
   399     }
       
   400 }
       
   401 
       
   402 /* Cancels the effect animation and sets the animation end state immediately.
       
   403 */
       
   404 void HbEffectScale::cancel(QTransform &transform, bool itemIsValid)
       
   405 {
       
   406     Q_UNUSED(itemIsValid)
       
   407 
       
   408     if (mAnimationX) {
       
   409         mAnimationX->stop();
       
   410     }
       
   411     if (mAnimationY) {
       
   412         mAnimationY->stop();
       
   413     }
       
   414 
       
   415     // This will set the endstate for the object correctly when cancelling effect
       
   416     QTransform newTransform;
       
   417     newTransform.translate(mCenterXValue, mCenterYValue);
       
   418     newTransform.scale(mEndXValue, mEndYValue);
       
   419     newTransform.translate(-mCenterXValue, -mCenterYValue);
       
   420     // Combine the new transform with the existing one
       
   421     transform *= newTransform;
       
   422 }
       
   423 
       
   424 void HbEffectScale::updateItemTransform(QTransform &transform)
       
   425 {
       
   426     QTransform newTransform;
       
   427     // Handle centering for scaling the matrix
       
   428     newTransform.translate(mCenterXValue, mCenterYValue);
       
   429     
       
   430     // Get the current scaling factor from animation or use 1.0 if animation does not exist
       
   431     qreal currentScalingX = mAnimationX ? mAnimationX->mCurrentScaling : 1.0;
       
   432     qreal currentScalingY = mAnimationY ? mAnimationY->mCurrentScaling : 1.0;
       
   433 
       
   434     // Scale the transformation matrix to reach the new scaling factor.
       
   435     newTransform.scale(currentScalingX, currentScalingY);
       
   436     // Centering must be canceled after the scaling has been done in the matrix
       
   437     newTransform.translate(-mCenterXValue, -mCenterYValue);
       
   438     // Combine the new transform with the existing one
       
   439     transform *= newTransform;
       
   440 }
       
   441 
       
   442 void HbEffectScale::handleAnimationFinished()
       
   443 {
       
   444     bool allFinished = true;
       
   445 
       
   446     if (mAnimationX && !mAnimationX->mFinished) {
       
   447         allFinished = false;
       
   448     }
       
   449     if (mAnimationY && !mAnimationY->mFinished) {
       
   450         allFinished = false;
       
   451     }
       
   452 
       
   453     // Inform the effect group that the whole scale effect has finished (both scaleX and scaleY)
       
   454     if (allFinished) {
       
   455         group()->effectFinished();
       
   456     }
       
   457 }
       
   458 
       
   459 void HbEffectScale::pause()
       
   460 {
       
   461     if (mAnimationX) {
       
   462         mAnimationX->pause();
       
   463     }
       
   464     if (mAnimationY) {
       
   465         mAnimationY->pause();
       
   466     }
       
   467 }
       
   468 
       
   469 void HbEffectScale::resume()
       
   470 {
       
   471     if (mAnimationX) {
       
   472         mAnimationX->resume();
       
   473     }
       
   474     if (mAnimationY) {
       
   475         mAnimationY->resume();
       
   476     }
       
   477 }