123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389 |
- // Copyright 2013 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "ui/gfx/geometry/transform_operations.h"
- #include <stddef.h>
- #include <algorithm>
- #include <utility>
- #include "ui/gfx/geometry/angle_conversions.h"
- #include "ui/gfx/geometry/box_f.h"
- #include "ui/gfx/geometry/transform_util.h"
- #include "ui/gfx/geometry/vector3d_f.h"
- namespace gfx {
- TransformOperations::TransformOperations() = default;
- TransformOperations::TransformOperations(const TransformOperations& other) {
- operations_ = other.operations_;
- }
- TransformOperations::~TransformOperations() = default;
- TransformOperations& TransformOperations::operator=(
- const TransformOperations& other) {
- operations_ = other.operations_;
- return *this;
- }
- Transform TransformOperations::Apply() const {
- return ApplyRemaining(0);
- }
- Transform TransformOperations::ApplyRemaining(size_t start) const {
- Transform to_return;
- for (size_t i = start; i < operations_.size(); i++) {
- to_return.PreconcatTransform(operations_[i].matrix);
- }
- return to_return;
- }
- // TODO(crbug.com/914397): Consolidate blink and cc implementations of transform
- // interpolation.
- TransformOperations TransformOperations::Blend(const TransformOperations& from,
- SkScalar progress) const {
- TransformOperations to_return;
- if (!BlendInternal(from, progress, &to_return)) {
- // If the matrices cannot be blended, fallback to discrete animation logic.
- // See https://drafts.csswg.org/css-transforms/#matrix-interpolation
- to_return = progress < 0.5 ? from : *this;
- }
- return to_return;
- }
- bool TransformOperations::BlendedBoundsForBox(const BoxF& box,
- const TransformOperations& from,
- SkScalar min_progress,
- SkScalar max_progress,
- BoxF* bounds) const {
- *bounds = box;
- bool from_identity = from.IsIdentity();
- bool to_identity = IsIdentity();
- if (from_identity && to_identity)
- return true;
- if (!MatchesTypes(from))
- return false;
- size_t num_operations = std::max(from_identity ? 0 : from.operations_.size(),
- to_identity ? 0 : operations_.size());
- // Because we are squashing all of the matrices together when applying
- // them to the animation, we must apply them in reverse order when
- // not squashing them.
- for (size_t i = 0; i < num_operations; ++i) {
- size_t operation_index = num_operations - 1 - i;
- BoxF bounds_for_operation;
- const TransformOperation* from_op =
- from_identity ? nullptr : &from.operations_[operation_index];
- const TransformOperation* to_op =
- to_identity ? nullptr : &operations_[operation_index];
- if (!TransformOperation::BlendedBoundsForBox(*bounds, from_op, to_op,
- min_progress, max_progress,
- &bounds_for_operation)) {
- return false;
- }
- *bounds = bounds_for_operation;
- }
- return true;
- }
- bool TransformOperations::PreservesAxisAlignment() const {
- for (auto& operation : operations_) {
- switch (operation.type) {
- case TransformOperation::TRANSFORM_OPERATION_IDENTITY:
- case TransformOperation::TRANSFORM_OPERATION_TRANSLATE:
- case TransformOperation::TRANSFORM_OPERATION_SCALE:
- continue;
- case TransformOperation::TRANSFORM_OPERATION_MATRIX:
- if (!operation.matrix.IsIdentity() &&
- !operation.matrix.IsScaleOrTranslation())
- return false;
- continue;
- case TransformOperation::TRANSFORM_OPERATION_ROTATE:
- case TransformOperation::TRANSFORM_OPERATION_SKEWX:
- case TransformOperation::TRANSFORM_OPERATION_SKEWY:
- case TransformOperation::TRANSFORM_OPERATION_SKEW:
- case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE:
- return false;
- }
- }
- return true;
- }
- bool TransformOperations::IsTranslation() const {
- for (auto& operation : operations_) {
- switch (operation.type) {
- case TransformOperation::TRANSFORM_OPERATION_IDENTITY:
- case TransformOperation::TRANSFORM_OPERATION_TRANSLATE:
- continue;
- case TransformOperation::TRANSFORM_OPERATION_MATRIX:
- if (!operation.matrix.IsIdentityOrTranslation())
- return false;
- continue;
- case TransformOperation::TRANSFORM_OPERATION_ROTATE:
- case TransformOperation::TRANSFORM_OPERATION_SCALE:
- case TransformOperation::TRANSFORM_OPERATION_SKEWX:
- case TransformOperation::TRANSFORM_OPERATION_SKEWY:
- case TransformOperation::TRANSFORM_OPERATION_SKEW:
- case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE:
- return false;
- }
- }
- return true;
- }
- static SkScalar TanDegrees(double degrees) {
- return SkDoubleToScalar(std::tan(DegToRad(degrees)));
- }
- bool TransformOperations::ScaleComponent(SkScalar* scale) const {
- SkScalar operations_scale = 1.f;
- for (auto& operation : operations_) {
- switch (operation.type) {
- case TransformOperation::TRANSFORM_OPERATION_IDENTITY:
- case TransformOperation::TRANSFORM_OPERATION_TRANSLATE:
- case TransformOperation::TRANSFORM_OPERATION_ROTATE:
- continue;
- case TransformOperation::TRANSFORM_OPERATION_MATRIX: {
- if (operation.matrix.HasPerspective())
- return false;
- Vector2dF scale_components =
- ComputeTransform2dScaleComponents(operation.matrix, 1.f);
- operations_scale *=
- std::max(scale_components.x(), scale_components.y());
- break;
- }
- case TransformOperation::TRANSFORM_OPERATION_SKEWX:
- case TransformOperation::TRANSFORM_OPERATION_SKEWY:
- case TransformOperation::TRANSFORM_OPERATION_SKEW: {
- SkScalar x_component = TanDegrees(operation.skew.x);
- SkScalar y_component = TanDegrees(operation.skew.y);
- SkScalar x_scale = std::sqrt(x_component * x_component + 1);
- SkScalar y_scale = std::sqrt(y_component * y_component + 1);
- operations_scale *= std::max(x_scale, y_scale);
- break;
- }
- case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE:
- return false;
- case TransformOperation::TRANSFORM_OPERATION_SCALE:
- operations_scale *= std::max(
- std::abs(operation.scale.x),
- std::max(std::abs(operation.scale.y), std::abs(operation.scale.z)));
- }
- }
- *scale = operations_scale;
- return true;
- }
- bool TransformOperations::MatchesTypes(const TransformOperations& other) const {
- if (operations_.size() == 0 || other.operations_.size() == 0)
- return true;
- if (operations_.size() != other.operations_.size())
- return false;
- for (size_t i = 0; i < operations_.size(); ++i) {
- if (operations_[i].type != other.operations_[i].type)
- return false;
- }
- return true;
- }
- size_t TransformOperations::MatchingPrefixLength(
- const TransformOperations& other) const {
- size_t num_operations =
- std::min(operations_.size(), other.operations_.size());
- for (size_t i = 0; i < num_operations; ++i) {
- if (operations_[i].type != other.operations_[i].type) {
- // Remaining operations in each operations list require matrix/matrix3d
- // interpolation.
- return i;
- }
- }
- // If the operations match to the length of the shorter list, then pad its
- // length with the matching identity operations.
- // https://drafts.csswg.org/css-transforms/#transform-function-lists
- return std::max(operations_.size(), other.operations_.size());
- }
- bool TransformOperations::CanBlendWith(const TransformOperations& other) const {
- TransformOperations dummy;
- return BlendInternal(other, 0.5, &dummy);
- }
- void TransformOperations::AppendTranslate(SkScalar x, SkScalar y, SkScalar z) {
- TransformOperation to_add;
- to_add.matrix.Translate3d(x, y, z);
- to_add.type = TransformOperation::TRANSFORM_OPERATION_TRANSLATE;
- to_add.translate.x = x;
- to_add.translate.y = y;
- to_add.translate.z = z;
- operations_.push_back(to_add);
- decomposed_transforms_.clear();
- }
- void TransformOperations::AppendRotate(SkScalar x,
- SkScalar y,
- SkScalar z,
- SkScalar degrees) {
- TransformOperation to_add;
- to_add.type = TransformOperation::TRANSFORM_OPERATION_ROTATE;
- to_add.rotate.axis.x = x;
- to_add.rotate.axis.y = y;
- to_add.rotate.axis.z = z;
- to_add.rotate.angle = degrees;
- to_add.Bake();
- operations_.push_back(to_add);
- decomposed_transforms_.clear();
- }
- void TransformOperations::AppendScale(SkScalar x, SkScalar y, SkScalar z) {
- TransformOperation to_add;
- to_add.type = TransformOperation::TRANSFORM_OPERATION_SCALE;
- to_add.scale.x = x;
- to_add.scale.y = y;
- to_add.scale.z = z;
- to_add.Bake();
- operations_.push_back(to_add);
- decomposed_transforms_.clear();
- }
- void TransformOperations::AppendSkewX(SkScalar x) {
- TransformOperation to_add;
- to_add.type = TransformOperation::TRANSFORM_OPERATION_SKEWX;
- to_add.skew.x = x;
- to_add.skew.y = 0;
- to_add.Bake();
- operations_.push_back(to_add);
- decomposed_transforms_.clear();
- }
- void TransformOperations::AppendSkewY(SkScalar y) {
- TransformOperation to_add;
- to_add.type = TransformOperation::TRANSFORM_OPERATION_SKEWY;
- to_add.skew.x = 0;
- to_add.skew.y = y;
- to_add.Bake();
- operations_.push_back(to_add);
- decomposed_transforms_.clear();
- }
- void TransformOperations::AppendSkew(SkScalar x, SkScalar y) {
- TransformOperation to_add;
- to_add.type = TransformOperation::TRANSFORM_OPERATION_SKEW;
- to_add.skew.x = x;
- to_add.skew.y = y;
- to_add.Bake();
- operations_.push_back(to_add);
- decomposed_transforms_.clear();
- }
- void TransformOperations::AppendPerspective(absl::optional<SkScalar> depth) {
- TransformOperation to_add;
- to_add.type = TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE;
- if (depth) {
- DCHECK_GE(*depth, 1.0f);
- to_add.perspective_m43 = -1.0f / *depth;
- } else {
- to_add.perspective_m43 = 0.0f;
- }
- to_add.Bake();
- operations_.push_back(to_add);
- decomposed_transforms_.clear();
- }
- void TransformOperations::AppendMatrix(const Transform& matrix) {
- TransformOperation to_add;
- to_add.matrix = matrix;
- to_add.type = TransformOperation::TRANSFORM_OPERATION_MATRIX;
- operations_.push_back(to_add);
- decomposed_transforms_.clear();
- }
- void TransformOperations::AppendIdentity() {
- operations_.emplace_back();
- }
- void TransformOperations::Append(const TransformOperation& operation) {
- operations_.push_back(operation);
- decomposed_transforms_.clear();
- }
- bool TransformOperations::IsIdentity() const {
- for (auto& operation : operations_) {
- if (!operation.IsIdentity())
- return false;
- }
- return true;
- }
- bool TransformOperations::ApproximatelyEqual(const TransformOperations& other,
- SkScalar tolerance) const {
- if (size() != other.size())
- return false;
- for (size_t i = 0; i < operations_.size(); ++i) {
- if (!operations_[i].ApproximatelyEqual(other.operations_[i], tolerance))
- return false;
- }
- return true;
- }
- bool TransformOperations::BlendInternal(const TransformOperations& from,
- SkScalar progress,
- TransformOperations* result) const {
- bool from_identity = from.IsIdentity();
- bool to_identity = IsIdentity();
- if (from_identity && to_identity)
- return true;
- size_t matching_prefix_length = MatchingPrefixLength(from);
- size_t from_size = from_identity ? 0 : from.operations_.size();
- size_t to_size = to_identity ? 0 : operations_.size();
- size_t num_operations = std::max(from_size, to_size);
- for (size_t i = 0; i < matching_prefix_length; ++i) {
- TransformOperation blended;
- if (!TransformOperation::BlendTransformOperations(
- i >= from_size ? nullptr : &from.operations_[i],
- i >= to_size ? nullptr : &operations_[i], progress, &blended)) {
- return false;
- }
- result->Append(blended);
- }
- if (matching_prefix_length < num_operations) {
- if (!ComputeDecomposedTransform(matching_prefix_length) ||
- !from.ComputeDecomposedTransform(matching_prefix_length)) {
- return false;
- }
- DecomposedTransform matrix_transform = BlendDecomposedTransforms(
- *decomposed_transforms_[matching_prefix_length].get(),
- *from.decomposed_transforms_[matching_prefix_length].get(), progress);
- result->AppendMatrix(ComposeTransform(matrix_transform));
- }
- return true;
- }
- bool TransformOperations::ComputeDecomposedTransform(
- size_t start_offset) const {
- auto it = decomposed_transforms_.find(start_offset);
- if (it == decomposed_transforms_.end()) {
- std::unique_ptr<DecomposedTransform> decomposed_transform =
- std::make_unique<DecomposedTransform>();
- Transform transform = ApplyRemaining(start_offset);
- if (!DecomposeTransform(decomposed_transform.get(), transform))
- return false;
- decomposed_transforms_[start_offset] = std::move(decomposed_transform);
- }
- return true;
- }
- } // namespace gfx
|