ambient_animation_attribution_transformer.cc 7.3 KB

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