1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
|
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/ambient/ui/ambient_animation_attribution_transformer.h"
#include <string>
#include <utility>
#include "ash/utility/lottie_util.h"
#include "base/check.h"
#include "base/logging.h"
#include "cc/paint/skottie_resource_metadata.h"
#include "cc/paint/skottie_text_property_value.h"
#include "cc/paint/skottie_transform_property_value.h"
#include "cc/paint/skottie_wrapper.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/vector2d.h"
#include "ui/lottie/animation.h"
#include "ui/views/controls/animated_image_view.h"
namespace ash {
namespace {
// Amount of padding there should be from the bottom-right of the
// AnimatedImageView to the bottom-right of the attribution text box.
constexpr gfx::Vector2d kTextBoxPaddingDip = gfx::Vector2d(24, 24);
} // namespace
// This translates between 2 coordinate systems. The first coordinate system
// (the one translating from) is the views coordinate system where the origin
// is the top-left of the view. In practice, the typical case looks like this:
//
// Animation
// +-----------------------------------------------+
// | |
// |(0, 0) View |
// +-----------------------------------------------|
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// |-------------------------------------------+ |
// | Attribution Text| |
// |-------------------------------------------+ |
// | |
// +-----------------------------------------------+
// | |
// | |
// +-----------------------------------------------+
//
// Note in the above, the animation's width matches the view's width, and its
// height exceeds that of the view (the upper and lower parts of the animation
// get cropped out). The origin is the top-left of the view, so the top-left of
// the animation has coordinates (0, <some negative number>). The attribution
// text box's bottom right corner has coordinates
// (view_width - 24, view_height - 24).
//
// The second set of coordinates (the one translating to) is the original
// animation's coordinate system. "Original" here refers to the
// coordinates baked into the Lottie file. Visually, it looks the same as the
// picture above, except:
// * The origin is the top-left of the animation.
// * The animation's width/height are those of the original animation (baked
// into the Lottie file), as opposed to those of the "scaled" animation that
// was scaled to reflect the view's bounds/dimensions.
//
// Note that although the typical case is illustrated above, the implementation
// was written generically to account for all cases.
void AmbientAnimationAttributionTransformer::TransformTextBox(
views::AnimatedImageView& animated_image_view) {
gfx::Transform view_to_animation_transform;
// 1) Change the origin from the top-left of the view to the top-left of the
// scaled animation.
DCHECK(!animated_image_view.GetImageBounds().IsEmpty());
gfx::Vector2d scaled_animation_origin_offset =
animated_image_view.GetImageBounds().origin().OffsetFromOrigin();
view_to_animation_transform.Translate(-scaled_animation_origin_offset);
// 2) Reset the coordinates from the "scaled" animation dimensions (scaled to
// fit the view) to the original animation dimensions baked into the Lottie
// file.
lottie::Animation* animation = animated_image_view.animated_image();
DCHECK(animation);
gfx::Size original_animation_size = animation->GetOriginalSize();
gfx::Size scaled_animation_size = animated_image_view.GetImageBounds().size();
view_to_animation_transform.PostScale(
static_cast<float>(original_animation_size.width()) /
scaled_animation_size.width(),
static_cast<float>(original_animation_size.height()) /
scaled_animation_size.height());
// Apply transformation to the bottom-right corner of the text box. The
// bottom-right corner is arbitrary here and is just used as a point of
// reference when building the final transformed text box's coordinates.
gfx::Rect view_bounds = animated_image_view.GetContentsBounds();
DCHECK(!view_bounds.IsEmpty())
<< "AnimatedImageView's content bounds must be initialized before "
"transforming the text box.";
gfx::Point text_box_bottom_right = view_to_animation_transform.MapPoint(
view_bounds.bottom_right() - kTextBoxPaddingDip);
// In the majority of cases, the bottom-right of the text box will already be
// within the boundaries of the original animation. There are some corner
// cases though (ex: fitting a landscape animation file to portrait view)
// where the bottom-right will be outside the animation's boundaries. In these
// cases, clamp the text box's coordinates to the bottom-right of the
// animation, or the text box will ultimately not be rendered.
text_box_bottom_right.SetToMin(
gfx::Rect(original_animation_size).bottom_right());
for (const std::string& text_node_name :
animation->skottie()->GetTextNodeNames()) {
if (!IsCustomizableLottieId(text_node_name)) {
DVLOG(4) << "Ignoring non-attribution text node";
continue;
}
cc::SkottieResourceIdHash attribution_node_id =
cc::HashSkottieResourceId(text_node_name);
DCHECK(animation->text_map().contains(attribution_node_id));
cc::SkottieTextPropertyValue& attribution_val =
animation->text_map().at(attribution_node_id);
// Text box's height stays the same as what's specified in the lottie file.
gfx::RectF new_text_box = attribution_val.box();
new_text_box.set_width(text_box_bottom_right.x());
new_text_box.set_origin(
gfx::PointF(0, text_box_bottom_right.y() - new_text_box.height()));
// One final transform: The text box's coordinates must be relative to the
// text attribution layer's "position" in the animation file (an arbitrary
// point specified in Adobe After-Effects when the animation is built). It
// is effectively the "local origin" for the text box, and may be different
// for each attribution node in the animation.
DCHECK(animation->skottie()->GetCurrentTransformPropertyValues().contains(
attribution_node_id));
gfx::Transform attribution_layer_shift;
attribution_layer_shift.Translate(-animation->skottie()
->GetCurrentTransformPropertyValues()
.at(attribution_node_id)
.position.OffsetFromOrigin());
attribution_val.set_box(attribution_layer_shift.MapRect(new_text_box));
}
}
} // namespace ash
|