Home | History | Annotate | Download | only in animation
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "cc/animation/transform_operations.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "ui/gfx/animation/tween.h"
     10 #include "ui/gfx/box_f.h"
     11 #include "ui/gfx/transform_util.h"
     12 #include "ui/gfx/vector3d_f.h"
     13 
     14 namespace cc {
     15 
     16 TransformOperations::TransformOperations()
     17     : decomposed_transform_dirty_(true) {
     18 }
     19 
     20 TransformOperations::TransformOperations(const TransformOperations& other) {
     21   operations_ = other.operations_;
     22   decomposed_transform_dirty_ = other.decomposed_transform_dirty_;
     23   if (!decomposed_transform_dirty_) {
     24     decomposed_transform_.reset(
     25         new gfx::DecomposedTransform(*other.decomposed_transform_.get()));
     26   }
     27 }
     28 
     29 TransformOperations::~TransformOperations() {
     30 }
     31 
     32 gfx::Transform TransformOperations::Apply() const {
     33   gfx::Transform to_return;
     34   for (size_t i = 0; i < operations_.size(); ++i)
     35     to_return.PreconcatTransform(operations_[i].matrix);
     36   return to_return;
     37 }
     38 
     39 gfx::Transform TransformOperations::Blend(const TransformOperations& from,
     40                                           SkMScalar progress) const {
     41   gfx::Transform to_return;
     42   BlendInternal(from, progress, &to_return);
     43   return to_return;
     44 }
     45 
     46 bool TransformOperations::BlendedBoundsForBox(const gfx::BoxF& box,
     47                                               const TransformOperations& from,
     48                                               SkMScalar min_progress,
     49                                               SkMScalar max_progress,
     50                                               gfx::BoxF* bounds) const {
     51   *bounds = box;
     52 
     53   bool from_identity = from.IsIdentity();
     54   bool to_identity = IsIdentity();
     55   if (from_identity && to_identity)
     56     return true;
     57 
     58   if (!MatchesTypes(from))
     59     return false;
     60 
     61   size_t num_operations =
     62       std::max(from_identity ? 0 : from.operations_.size(),
     63                to_identity ? 0 : operations_.size());
     64 
     65   // Because we are squashing all of the matrices together when applying
     66   // them to the animation, we must apply them in reverse order when
     67   // not squashing them.
     68   for (int i = num_operations - 1; i >= 0; --i) {
     69     gfx::BoxF bounds_for_operation;
     70     const TransformOperation* from_op =
     71         from_identity ? NULL : &from.operations_[i];
     72     const TransformOperation* to_op = to_identity ? NULL : &operations_[i];
     73     if (!TransformOperation::BlendedBoundsForBox(*bounds,
     74                                                  from_op,
     75                                                  to_op,
     76                                                  min_progress,
     77                                                  max_progress,
     78                                                  &bounds_for_operation))
     79       return false;
     80     *bounds = bounds_for_operation;
     81   }
     82 
     83   return true;
     84 }
     85 
     86 bool TransformOperations::AffectsScale() const {
     87   for (size_t i = 0; i < operations_.size(); ++i) {
     88     if (operations_[i].type == TransformOperation::TransformOperationScale)
     89       return true;
     90     if (operations_[i].type == TransformOperation::TransformOperationMatrix &&
     91         !operations_[i].matrix.IsIdentityOrTranslation())
     92       return true;
     93   }
     94   return false;
     95 }
     96 
     97 bool TransformOperations::IsTranslation() const {
     98   for (size_t i = 0; i < operations_.size(); ++i) {
     99     switch (operations_[i].type) {
    100       case TransformOperation::TransformOperationIdentity:
    101       case TransformOperation::TransformOperationTranslate:
    102         continue;
    103       case TransformOperation::TransformOperationMatrix:
    104         if (!operations_[i].matrix.IsIdentityOrTranslation())
    105           return false;
    106         continue;
    107       case TransformOperation::TransformOperationRotate:
    108       case TransformOperation::TransformOperationScale:
    109       case TransformOperation::TransformOperationSkew:
    110       case TransformOperation::TransformOperationPerspective:
    111         return false;
    112     }
    113   }
    114   return true;
    115 }
    116 
    117 bool TransformOperations::MaximumScale(const TransformOperations& from,
    118                                        SkMScalar min_progress,
    119                                        SkMScalar max_progress,
    120                                        float* max_scale) const {
    121   if (!MatchesTypes(from))
    122     return false;
    123 
    124   gfx::Vector3dF from_scale;
    125   gfx::Vector3dF to_scale;
    126 
    127   if (!from.ScaleComponent(&from_scale) || !ScaleComponent(&to_scale))
    128     return false;
    129 
    130   gfx::Vector3dF scale_at_min_progress(
    131       std::abs(gfx::Tween::FloatValueBetween(
    132           min_progress, from_scale.x(), to_scale.x())),
    133       std::abs(gfx::Tween::FloatValueBetween(
    134           min_progress, from_scale.y(), to_scale.y())),
    135       std::abs(gfx::Tween::FloatValueBetween(
    136           min_progress, from_scale.z(), to_scale.z())));
    137   gfx::Vector3dF scale_at_max_progress(
    138       std::abs(gfx::Tween::FloatValueBetween(
    139           max_progress, from_scale.x(), to_scale.x())),
    140       std::abs(gfx::Tween::FloatValueBetween(
    141           max_progress, from_scale.y(), to_scale.y())),
    142       std::abs(gfx::Tween::FloatValueBetween(
    143           max_progress, from_scale.z(), to_scale.z())));
    144 
    145   gfx::Vector3dF max_scale_3d = scale_at_min_progress;
    146   max_scale_3d.SetToMax(scale_at_max_progress);
    147   *max_scale =
    148       std::max(max_scale_3d.x(), std::max(max_scale_3d.y(), max_scale_3d.z()));
    149   return true;
    150 }
    151 
    152 bool TransformOperations::ScaleComponent(gfx::Vector3dF* scale) const {
    153   *scale = gfx::Vector3dF(1.f, 1.f, 1.f);
    154   bool has_scale_component = false;
    155   for (size_t i = 0; i < operations_.size(); ++i) {
    156     switch (operations_[i].type) {
    157       case TransformOperation::TransformOperationIdentity:
    158       case TransformOperation::TransformOperationTranslate:
    159         continue;
    160       case TransformOperation::TransformOperationMatrix:
    161         if (!operations_[i].matrix.IsIdentityOrTranslation())
    162           return false;
    163         continue;
    164       case TransformOperation::TransformOperationRotate:
    165       case TransformOperation::TransformOperationSkew:
    166       case TransformOperation::TransformOperationPerspective:
    167         return false;
    168       case TransformOperation::TransformOperationScale:
    169         if (has_scale_component)
    170           return false;
    171         has_scale_component = true;
    172         scale->Scale(operations_[i].scale.x,
    173                      operations_[i].scale.y,
    174                      operations_[i].scale.z);
    175     }
    176   }
    177   return true;
    178 }
    179 
    180 bool TransformOperations::MatchesTypes(const TransformOperations& other) const {
    181   if (IsIdentity() || other.IsIdentity())
    182     return true;
    183 
    184   if (operations_.size() != other.operations_.size())
    185     return false;
    186 
    187   for (size_t i = 0; i < operations_.size(); ++i) {
    188     if (operations_[i].type != other.operations_[i].type
    189       && !operations_[i].IsIdentity()
    190       && !other.operations_[i].IsIdentity())
    191       return false;
    192   }
    193 
    194   return true;
    195 }
    196 
    197 bool TransformOperations::CanBlendWith(
    198     const TransformOperations& other) const {
    199   gfx::Transform dummy;
    200   return BlendInternal(other, 0.5, &dummy);
    201 }
    202 
    203 void TransformOperations::AppendTranslate(SkMScalar x,
    204                                           SkMScalar y,
    205                                           SkMScalar z) {
    206   TransformOperation to_add;
    207   to_add.matrix.Translate3d(x, y, z);
    208   to_add.type = TransformOperation::TransformOperationTranslate;
    209   to_add.translate.x = x;
    210   to_add.translate.y = y;
    211   to_add.translate.z = z;
    212   operations_.push_back(to_add);
    213   decomposed_transform_dirty_ = true;
    214 }
    215 
    216 void TransformOperations::AppendRotate(SkMScalar x,
    217                                        SkMScalar y,
    218                                        SkMScalar z,
    219                                        SkMScalar degrees) {
    220   TransformOperation to_add;
    221   to_add.matrix.RotateAbout(gfx::Vector3dF(x, y, z), degrees);
    222   to_add.type = TransformOperation::TransformOperationRotate;
    223   to_add.rotate.axis.x = x;
    224   to_add.rotate.axis.y = y;
    225   to_add.rotate.axis.z = z;
    226   to_add.rotate.angle = degrees;
    227   operations_.push_back(to_add);
    228   decomposed_transform_dirty_ = true;
    229 }
    230 
    231 void TransformOperations::AppendScale(SkMScalar x, SkMScalar y, SkMScalar z) {
    232   TransformOperation to_add;
    233   to_add.matrix.Scale3d(x, y, z);
    234   to_add.type = TransformOperation::TransformOperationScale;
    235   to_add.scale.x = x;
    236   to_add.scale.y = y;
    237   to_add.scale.z = z;
    238   operations_.push_back(to_add);
    239   decomposed_transform_dirty_ = true;
    240 }
    241 
    242 void TransformOperations::AppendSkew(SkMScalar x, SkMScalar y) {
    243   TransformOperation to_add;
    244   to_add.matrix.SkewX(x);
    245   to_add.matrix.SkewY(y);
    246   to_add.type = TransformOperation::TransformOperationSkew;
    247   to_add.skew.x = x;
    248   to_add.skew.y = y;
    249   operations_.push_back(to_add);
    250   decomposed_transform_dirty_ = true;
    251 }
    252 
    253 void TransformOperations::AppendPerspective(SkMScalar depth) {
    254   TransformOperation to_add;
    255   to_add.matrix.ApplyPerspectiveDepth(depth);
    256   to_add.type = TransformOperation::TransformOperationPerspective;
    257   to_add.perspective_depth = depth;
    258   operations_.push_back(to_add);
    259   decomposed_transform_dirty_ = true;
    260 }
    261 
    262 void TransformOperations::AppendMatrix(const gfx::Transform& matrix) {
    263   TransformOperation to_add;
    264   to_add.matrix = matrix;
    265   to_add.type = TransformOperation::TransformOperationMatrix;
    266   operations_.push_back(to_add);
    267   decomposed_transform_dirty_ = true;
    268 }
    269 
    270 void TransformOperations::AppendIdentity() {
    271   operations_.push_back(TransformOperation());
    272 }
    273 
    274 bool TransformOperations::IsIdentity() const {
    275   for (size_t i = 0; i < operations_.size(); ++i) {
    276     if (!operations_[i].IsIdentity())
    277       return false;
    278   }
    279   return true;
    280 }
    281 
    282 bool TransformOperations::BlendInternal(const TransformOperations& from,
    283                                         SkMScalar progress,
    284                                         gfx::Transform* result) const {
    285   bool from_identity = from.IsIdentity();
    286   bool to_identity = IsIdentity();
    287   if (from_identity && to_identity)
    288     return true;
    289 
    290   if (MatchesTypes(from)) {
    291     size_t num_operations =
    292         std::max(from_identity ? 0 : from.operations_.size(),
    293                  to_identity ? 0 : operations_.size());
    294     for (size_t i = 0; i < num_operations; ++i) {
    295       gfx::Transform blended;
    296       if (!TransformOperation::BlendTransformOperations(
    297           from_identity ? 0 : &from.operations_[i],
    298           to_identity ? 0 : &operations_[i],
    299           progress,
    300           &blended))
    301           return false;
    302       result->PreconcatTransform(blended);
    303     }
    304     return true;
    305   }
    306 
    307   if (!ComputeDecomposedTransform() || !from.ComputeDecomposedTransform())
    308     return false;
    309 
    310   gfx::DecomposedTransform to_return;
    311   if (!gfx::BlendDecomposedTransforms(&to_return,
    312                                       *decomposed_transform_.get(),
    313                                       *from.decomposed_transform_.get(),
    314                                       progress))
    315     return false;
    316 
    317   *result = ComposeTransform(to_return);
    318   return true;
    319 }
    320 
    321 bool TransformOperations::ComputeDecomposedTransform() const {
    322   if (decomposed_transform_dirty_) {
    323     if (!decomposed_transform_)
    324       decomposed_transform_.reset(new gfx::DecomposedTransform());
    325     gfx::Transform transform = Apply();
    326     if (!gfx::DecomposeTransform(decomposed_transform_.get(), transform))
    327       return false;
    328     decomposed_transform_dirty_ = false;
    329   }
    330   return true;
    331 }
    332 
    333 }  // namespace cc
    334