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::ScaleComponent(gfx::Vector3dF* scale) const {
    118   *scale = gfx::Vector3dF(1.f, 1.f, 1.f);
    119   bool has_scale_component = false;
    120   for (size_t i = 0; i < operations_.size(); ++i) {
    121     switch (operations_[i].type) {
    122       case TransformOperation::TransformOperationIdentity:
    123       case TransformOperation::TransformOperationTranslate:
    124         continue;
    125       case TransformOperation::TransformOperationMatrix:
    126         if (!operations_[i].matrix.IsIdentityOrTranslation())
    127           return false;
    128         continue;
    129       case TransformOperation::TransformOperationRotate:
    130       case TransformOperation::TransformOperationSkew:
    131       case TransformOperation::TransformOperationPerspective:
    132         return false;
    133       case TransformOperation::TransformOperationScale:
    134         if (has_scale_component)
    135           return false;
    136         has_scale_component = true;
    137         scale->Scale(operations_[i].scale.x,
    138                      operations_[i].scale.y,
    139                      operations_[i].scale.z);
    140     }
    141   }
    142   return true;
    143 }
    144 
    145 bool TransformOperations::MatchesTypes(const TransformOperations& other) const {
    146   if (IsIdentity() || other.IsIdentity())
    147     return true;
    148 
    149   if (operations_.size() != other.operations_.size())
    150     return false;
    151 
    152   for (size_t i = 0; i < operations_.size(); ++i) {
    153     if (operations_[i].type != other.operations_[i].type
    154       && !operations_[i].IsIdentity()
    155       && !other.operations_[i].IsIdentity())
    156       return false;
    157   }
    158 
    159   return true;
    160 }
    161 
    162 bool TransformOperations::CanBlendWith(
    163     const TransformOperations& other) const {
    164   gfx::Transform dummy;
    165   return BlendInternal(other, 0.5, &dummy);
    166 }
    167 
    168 void TransformOperations::AppendTranslate(SkMScalar x,
    169                                           SkMScalar y,
    170                                           SkMScalar z) {
    171   TransformOperation to_add;
    172   to_add.matrix.Translate3d(x, y, z);
    173   to_add.type = TransformOperation::TransformOperationTranslate;
    174   to_add.translate.x = x;
    175   to_add.translate.y = y;
    176   to_add.translate.z = z;
    177   operations_.push_back(to_add);
    178   decomposed_transform_dirty_ = true;
    179 }
    180 
    181 void TransformOperations::AppendRotate(SkMScalar x,
    182                                        SkMScalar y,
    183                                        SkMScalar z,
    184                                        SkMScalar degrees) {
    185   TransformOperation to_add;
    186   to_add.matrix.RotateAbout(gfx::Vector3dF(x, y, z), degrees);
    187   to_add.type = TransformOperation::TransformOperationRotate;
    188   to_add.rotate.axis.x = x;
    189   to_add.rotate.axis.y = y;
    190   to_add.rotate.axis.z = z;
    191   to_add.rotate.angle = degrees;
    192   operations_.push_back(to_add);
    193   decomposed_transform_dirty_ = true;
    194 }
    195 
    196 void TransformOperations::AppendScale(SkMScalar x, SkMScalar y, SkMScalar z) {
    197   TransformOperation to_add;
    198   to_add.matrix.Scale3d(x, y, z);
    199   to_add.type = TransformOperation::TransformOperationScale;
    200   to_add.scale.x = x;
    201   to_add.scale.y = y;
    202   to_add.scale.z = z;
    203   operations_.push_back(to_add);
    204   decomposed_transform_dirty_ = true;
    205 }
    206 
    207 void TransformOperations::AppendSkew(SkMScalar x, SkMScalar y) {
    208   TransformOperation to_add;
    209   to_add.matrix.SkewX(x);
    210   to_add.matrix.SkewY(y);
    211   to_add.type = TransformOperation::TransformOperationSkew;
    212   to_add.skew.x = x;
    213   to_add.skew.y = y;
    214   operations_.push_back(to_add);
    215   decomposed_transform_dirty_ = true;
    216 }
    217 
    218 void TransformOperations::AppendPerspective(SkMScalar depth) {
    219   TransformOperation to_add;
    220   to_add.matrix.ApplyPerspectiveDepth(depth);
    221   to_add.type = TransformOperation::TransformOperationPerspective;
    222   to_add.perspective_depth = depth;
    223   operations_.push_back(to_add);
    224   decomposed_transform_dirty_ = true;
    225 }
    226 
    227 void TransformOperations::AppendMatrix(const gfx::Transform& matrix) {
    228   TransformOperation to_add;
    229   to_add.matrix = matrix;
    230   to_add.type = TransformOperation::TransformOperationMatrix;
    231   operations_.push_back(to_add);
    232   decomposed_transform_dirty_ = true;
    233 }
    234 
    235 void TransformOperations::AppendIdentity() {
    236   operations_.push_back(TransformOperation());
    237 }
    238 
    239 bool TransformOperations::IsIdentity() const {
    240   for (size_t i = 0; i < operations_.size(); ++i) {
    241     if (!operations_[i].IsIdentity())
    242       return false;
    243   }
    244   return true;
    245 }
    246 
    247 bool TransformOperations::BlendInternal(const TransformOperations& from,
    248                                         SkMScalar progress,
    249                                         gfx::Transform* result) const {
    250   bool from_identity = from.IsIdentity();
    251   bool to_identity = IsIdentity();
    252   if (from_identity && to_identity)
    253     return true;
    254 
    255   if (MatchesTypes(from)) {
    256     size_t num_operations =
    257         std::max(from_identity ? 0 : from.operations_.size(),
    258                  to_identity ? 0 : operations_.size());
    259     for (size_t i = 0; i < num_operations; ++i) {
    260       gfx::Transform blended;
    261       if (!TransformOperation::BlendTransformOperations(
    262           from_identity ? 0 : &from.operations_[i],
    263           to_identity ? 0 : &operations_[i],
    264           progress,
    265           &blended))
    266           return false;
    267       result->PreconcatTransform(blended);
    268     }
    269     return true;
    270   }
    271 
    272   if (!ComputeDecomposedTransform() || !from.ComputeDecomposedTransform())
    273     return false;
    274 
    275   gfx::DecomposedTransform to_return;
    276   if (!gfx::BlendDecomposedTransforms(&to_return,
    277                                       *decomposed_transform_.get(),
    278                                       *from.decomposed_transform_.get(),
    279                                       progress))
    280     return false;
    281 
    282   *result = ComposeTransform(to_return);
    283   return true;
    284 }
    285 
    286 bool TransformOperations::ComputeDecomposedTransform() const {
    287   if (decomposed_transform_dirty_) {
    288     if (!decomposed_transform_)
    289       decomposed_transform_.reset(new gfx::DecomposedTransform());
    290     gfx::Transform transform = Apply();
    291     if (!gfx::DecomposeTransform(decomposed_transform_.get(), transform))
    292       return false;
    293     decomposed_transform_dirty_ = false;
    294   }
    295   return true;
    296 }
    297 
    298 }  // namespace cc
    299