Skip to content

Commit

Permalink
Add SkottieTextPropertyValue to skottie rendering pipeline.
Browse files Browse the repository at this point in the history
Why: ChromeOS needs text in its animations, and the text is
dynamic.

This just adds a class that will get plumbed through the
rendering pipeline in Chromium. It will get serialized and
used in subsequent CLs.

Bug: b:217271404

Cq-Include-Trybots: luci.chrome.try:linux-chromeos-chrome
Change-Id: I641ba35248a5c7c6213e84b4ad9549da0f9bc71d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3472552
Reviewed-by: Florin Malita <fmalita@chromium.org>
Commit-Queue: Eric Sum <esum@google.com>
Cr-Commit-Position: refs/heads/main@{#973141}
  • Loading branch information
esum26 authored and Chromium LUCI CQ committed Feb 18, 2022
1 parent 79f1199 commit 2825a6e
Show file tree
Hide file tree
Showing 10 changed files with 253 additions and 32 deletions.
1 change: 1 addition & 0 deletions cc/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,7 @@ cc_test("cc_unittests") {
"paint/skottie_mru_resource_provider_unittest.cc",
"paint/skottie_resource_metadata_unittest.cc",
"paint/skottie_serialization_history_unittest.cc",
"paint/skottie_text_property_value_unittest.cc",
"paint/skottie_transfer_cache_entry_unittest.cc",
"paint/skottie_wrapper_unittest.cc",
]
Expand Down
2 changes: 2 additions & 0 deletions cc/paint/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ cc_component("paint") {
"skottie_resource_metadata.h",
"skottie_serialization_history.cc",
"skottie_serialization_history.h",
"skottie_text_property_value.cc",
"skottie_text_property_value.h",
"skottie_transfer_cache_entry.cc",
"skottie_transfer_cache_entry.h",
"skottie_wrapper.cc",
Expand Down
7 changes: 5 additions & 2 deletions cc/paint/paint_op_buffer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -705,9 +705,12 @@ size_t DrawSkottieOp::Serialize(const PaintOp* base_op,
helper.Write(op->skottie);

SkottieFrameDataMap images_to_serialize = op->images;
// TODO(esum): Serialize/deserialize the |text_map| and use it in
// DrawSkottieOp::Raster().
SkottieTextPropertyValueMap text_map_to_serialize = op->text_map;
if (options.skottie_serialization_history) {
options.skottie_serialization_history->FilterNewSkottieFrameImages(
*op->skottie, images_to_serialize);
options.skottie_serialization_history->FilterNewSkottieFrameState(
*op->skottie, images_to_serialize, text_map_to_serialize);
}

// Write number of images in the map first so that we know how many images to
Expand Down
2 changes: 2 additions & 0 deletions cc/paint/paint_op_buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "cc/paint/skottie_color_map.h"
#include "cc/paint/skottie_frame_data.h"
#include "cc/paint/skottie_resource_metadata.h"
#include "cc/paint/skottie_text_property_value.h"
#include "cc/paint/skottie_wrapper.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/skia/include/core/SkColor.h"
Expand Down Expand Up @@ -841,6 +842,7 @@ class CC_PAINT_EXPORT DrawSkottieOp final : public PaintOp {
SkottieFrameDataMap images;
// Node name hashes and corresponding colors to use for dynamic coloration.
SkottieColorMap color_map;
SkottieTextPropertyValueMap text_map;

private:
SkottieWrapper::FrameDataFetchResult GetImageAssetForRaster(
Expand Down
43 changes: 37 additions & 6 deletions cc/paint/skottie_serialization_history.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ bool SkottieSerializationHistory::SkottieFrameDataId::operator!=(
}

SkottieSerializationHistory::SkottieWrapperHistory::SkottieWrapperHistory(
const SkottieFrameDataMap& initial_images) {
const SkottieFrameDataMap& initial_images,
const SkottieTextPropertyValueMap& initial_text_map)
: accumulated_text_map_(initial_text_map) {
for (const auto& image_asset_pair : initial_images) {
DVLOG(1) << "Received initial image for asset " << image_asset_pair.first;
last_frame_data_per_asset_.emplace(
Expand All @@ -51,9 +53,16 @@ SkottieSerializationHistory::SkottieWrapperHistory::operator=(
SkottieSerializationHistory::SkottieWrapperHistory::~SkottieWrapperHistory() =
default;

void SkottieSerializationHistory::SkottieWrapperHistory::FilterNewState(
SkottieFrameDataMap& images,
SkottieTextPropertyValueMap& text_map) {
++current_sequence_id_;
FilterNewFrameImages(images);
FilterNewTextPropertyValues(text_map);
}

void SkottieSerializationHistory::SkottieWrapperHistory::FilterNewFrameImages(
SkottieFrameDataMap& images) {
++current_sequence_id_;
auto images_iter = images.begin();
while (images_iter != images.end()) {
const SkottieResourceIdHash& asset_id = images_iter->first;
Expand All @@ -76,24 +85,46 @@ void SkottieSerializationHistory::SkottieWrapperHistory::FilterNewFrameImages(
}
}

void SkottieSerializationHistory::SkottieWrapperHistory::
FilterNewTextPropertyValues(SkottieTextPropertyValueMap& text_map_in) {
auto text_map_in_iter = text_map_in.begin();
while (text_map_in_iter != text_map_in.end()) {
const SkottieResourceIdHash& node = text_map_in_iter->first;
const SkottieTextPropertyValue& new_text_property_val =
text_map_in_iter->second;
auto [accumulated_iter, is_new_insertion] =
accumulated_text_map_.insert(*text_map_in_iter);
SkottieTextPropertyValue& old_text_property_val = accumulated_iter->second;
if (!is_new_insertion && old_text_property_val == new_text_property_val) {
DVLOG(4) << "No update to text property value for node" << node;
text_map_in_iter = text_map_in.erase(text_map_in_iter);
} else {
DVLOG(1) << "New text available for node " << node;
old_text_property_val = new_text_property_val;
++text_map_in_iter;
}
}
}

SkottieSerializationHistory::SkottieSerializationHistory(int purge_period)
: purge_period_(purge_period) {}

SkottieSerializationHistory::~SkottieSerializationHistory() = default;

void SkottieSerializationHistory::FilterNewSkottieFrameImages(
void SkottieSerializationHistory::FilterNewSkottieFrameState(
const SkottieWrapper& skottie,
SkottieFrameDataMap& images) {
SkottieFrameDataMap& images,
SkottieTextPropertyValueMap& text_map) {
DCHECK(skottie.is_valid());
base::AutoLock lock(mutex_);
auto [result_iterator, is_new_insertion] =
history_per_animation_.try_emplace(skottie.id(), images);
history_per_animation_.try_emplace(skottie.id(), images, text_map);
if (is_new_insertion) {
DVLOG(1) << "Encountered new SkottieWrapper with id " << skottie.id()
<< " and " << images.size() << " images";
} else {
SkottieWrapperHistory& skottie_history_found = result_iterator->second;
skottie_history_found.FilterNewFrameImages(images);
skottie_history_found.FilterNewState(images, text_map);
}
}

Expand Down
24 changes: 16 additions & 8 deletions cc/paint/skottie_serialization_history.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "cc/paint/paint_image.h"
#include "cc/paint/skottie_frame_data.h"
#include "cc/paint/skottie_resource_metadata.h"
#include "cc/paint/skottie_text_property_value.h"

namespace cc {

Expand Down Expand Up @@ -46,12 +47,13 @@ class CC_PAINT_EXPORT SkottieSerializationHistory {
delete;
~SkottieSerializationHistory();

// Given the set of |images| in the |skottie| animation's new frame, filter
// out the image assets that whose contents have not changed since the last
// frame they appeared in. If an image asset *has* changed, it is kept in the
// |images| map and the history is updated internally.
void FilterNewSkottieFrameImages(const SkottieWrapper& skottie,
SkottieFrameDataMap& images);
// Given the set of |images| and the |text_map| in the |skottie| animation's
// new frame, filter out the entries whose contents have not changed since the
// last frame. If an entry *has* changed, it is kept in its corresponding
// output argument and the history is updated internally.
void FilterNewSkottieFrameState(const SkottieWrapper& skottie,
SkottieFrameDataMap& images,
SkottieTextPropertyValueMap& text_map);

// Purges the history of any Skottie animations that have been inactive for
// a while. Although an animation's history is rather light-weight, this is
Expand Down Expand Up @@ -85,12 +87,14 @@ class CC_PAINT_EXPORT SkottieSerializationHistory {
public:
static constexpr int kInitialSequenceId = 1;

explicit SkottieWrapperHistory(const SkottieFrameDataMap& initial_images);
SkottieWrapperHistory(const SkottieFrameDataMap& initial_images,
const SkottieTextPropertyValueMap& initial_text_map);
SkottieWrapperHistory(const SkottieWrapperHistory& other);
SkottieWrapperHistory& operator=(const SkottieWrapperHistory& other);
~SkottieWrapperHistory();

void FilterNewFrameImages(SkottieFrameDataMap& images);
void FilterNewState(SkottieFrameDataMap& images,
SkottieTextPropertyValueMap& text_map);

// The "sequence_id" is incremented each time the caller tries to update an
// animation's history, regardless of whether its history is ultimately
Expand All @@ -105,10 +109,14 @@ class CC_PAINT_EXPORT SkottieSerializationHistory {
}

private:
void FilterNewFrameImages(SkottieFrameDataMap& images);
void FilterNewTextPropertyValues(SkottieTextPropertyValueMap& text_map);

int current_sequence_id_ = kInitialSequenceId;
int sequence_id_at_last_purge_check_ = kInitialSequenceId;
base::flat_map<SkottieResourceIdHash, SkottieFrameDataId>
last_frame_data_per_asset_;
SkottieTextPropertyValueMap accumulated_text_map_;
};

base::Lock mutex_;
Expand Down
71 changes: 55 additions & 16 deletions cc/paint/skottie_serialization_history_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "cc/paint/paint_flags.h"
#include "cc/paint/skottie_frame_data.h"
#include "cc/paint/skottie_resource_metadata.h"
#include "cc/paint/skottie_text_property_value.h"
#include "cc/paint/skottie_wrapper.h"
#include "cc/test/lottie_test_data.h"
#include "cc/test/skia_common.h"
Expand All @@ -30,6 +31,8 @@ class SkottieSerializationHistoryTest : public ::testing::Test {
SkottieSerializationHistoryTest() : history_(kTestPurgePeriod) {}

SkottieSerializationHistory history_;
SkottieFrameDataMap empty_images;
SkottieTextPropertyValueMap empty_text_map;
};

TEST_F(SkottieSerializationHistoryTest, FilterNewSkottieFrameImages) {
Expand All @@ -46,7 +49,7 @@ TEST_F(SkottieSerializationHistoryTest, FilterNewSkottieFrameImages) {
{HashSkottieResourceId("asset_b"),
{image_2, PaintFlags::FilterQuality::kMedium}},
};
history_.FilterNewSkottieFrameImages(*skottie, images);
history_.FilterNewSkottieFrameState(*skottie, images, empty_text_map);
EXPECT_THAT(
images,
UnorderedElementsAre(
Expand All @@ -62,7 +65,7 @@ TEST_F(SkottieSerializationHistoryTest, FilterNewSkottieFrameImages) {
{HashSkottieResourceId("asset_b"),
{image_2, PaintFlags::FilterQuality::kMedium}},
};
history_.FilterNewSkottieFrameImages(*skottie, images);
history_.FilterNewSkottieFrameState(*skottie, images, empty_text_map);
EXPECT_THAT(
images,
UnorderedElementsAre(Pair(
Expand All @@ -75,17 +78,53 @@ TEST_F(SkottieSerializationHistoryTest, FilterNewSkottieFrameImages) {
{HashSkottieResourceId("asset_b"),
{image_4, PaintFlags::FilterQuality::kMedium}},
};
history_.FilterNewSkottieFrameImages(*skottie, images);
history_.FilterNewSkottieFrameState(*skottie, images, empty_text_map);
EXPECT_THAT(
images,
UnorderedElementsAre(Pair(
HashSkottieResourceId("asset_b"),
SkottieFrameData({image_4, PaintFlags::FilterQuality::kMedium}))));

history_.FilterNewSkottieFrameImages(*skottie, images);
history_.FilterNewSkottieFrameState(*skottie, images, empty_text_map);
EXPECT_THAT(images, IsEmpty());
}

TEST_F(SkottieSerializationHistoryTest, FilterNewSkottieFrameText) {
auto skottie = CreateSkottie(gfx::Size(10, 10), 1);

SkottieTextPropertyValueMap text_map = {
{HashSkottieResourceId("node_a"), SkottieTextPropertyValue("test_1a")},
{HashSkottieResourceId("node_b"), SkottieTextPropertyValue("test_1b")},
};
history_.FilterNewSkottieFrameState(*skottie, empty_images, text_map);
EXPECT_THAT(text_map,
UnorderedElementsAre(Pair(HashSkottieResourceId("node_a"),
SkottieTextPropertyValue("test_1a")),
Pair(HashSkottieResourceId("node_b"),
SkottieTextPropertyValue("test_1b"))));

text_map = {
{HashSkottieResourceId("node_a"), SkottieTextPropertyValue("test_2a")},
{HashSkottieResourceId("node_b"), SkottieTextPropertyValue("test_1b")},
};
history_.FilterNewSkottieFrameState(*skottie, empty_images, text_map);
EXPECT_THAT(text_map,
UnorderedElementsAre(Pair(HashSkottieResourceId("node_a"),
SkottieTextPropertyValue("test_2a"))));

text_map = {
{HashSkottieResourceId("node_a"), SkottieTextPropertyValue("test_2a")},
{HashSkottieResourceId("node_b"), SkottieTextPropertyValue("test_2b")},
};
history_.FilterNewSkottieFrameState(*skottie, empty_images, text_map);
EXPECT_THAT(text_map,
UnorderedElementsAre(Pair(HashSkottieResourceId("node_b"),
SkottieTextPropertyValue("test_2b"))));

history_.FilterNewSkottieFrameState(*skottie, empty_images, text_map);
EXPECT_THAT(text_map, IsEmpty());
}

TEST_F(SkottieSerializationHistoryTest,
FilterNewSkottieFrameImagesTakesQualityIntoAccount) {
auto skottie = CreateSkottieFromString(
Expand All @@ -99,15 +138,15 @@ TEST_F(SkottieSerializationHistoryTest,
{HashSkottieResourceId("asset_b"),
{image_2, PaintFlags::FilterQuality::kMedium}},
};
history_.FilterNewSkottieFrameImages(*skottie, images);
history_.FilterNewSkottieFrameState(*skottie, images, empty_text_map);

images = {
{HashSkottieResourceId("asset_a"),
{image_1, PaintFlags::FilterQuality::kHigh}},
{HashSkottieResourceId("asset_b"),
{image_2, PaintFlags::FilterQuality::kMedium}},
};
history_.FilterNewSkottieFrameImages(*skottie, images);
history_.FilterNewSkottieFrameState(*skottie, images, empty_text_map);
EXPECT_THAT(
images,
UnorderedElementsAre(
Expand All @@ -116,7 +155,7 @@ TEST_F(SkottieSerializationHistoryTest,
}

TEST_F(SkottieSerializationHistoryTest,
FilterNewSkottieFrameImagesMultipleAnimations) {
FilterNewSkottieFrameStateMultipleAnimations) {
auto skottie_1 = CreateSkottieFromString(
CreateCustomLottieDataWith2Assets("asset_1a", "asset_1b"));
auto skottie_2 = CreateSkottieFromString(
Expand All @@ -138,8 +177,8 @@ TEST_F(SkottieSerializationHistoryTest,
{HashSkottieResourceId("asset_2b"),
{image_2, PaintFlags::FilterQuality::kMedium}},
};
history_.FilterNewSkottieFrameImages(*skottie_1, images_1);
history_.FilterNewSkottieFrameImages(*skottie_2, images_2);
history_.FilterNewSkottieFrameState(*skottie_1, images_1, empty_text_map);
history_.FilterNewSkottieFrameState(*skottie_2, images_2, empty_text_map);
EXPECT_THAT(
images_2,
UnorderedElementsAre(
Expand All @@ -161,8 +200,8 @@ TEST_F(SkottieSerializationHistoryTest,
{HashSkottieResourceId("asset_2b"),
{image_2, PaintFlags::FilterQuality::kMedium}},
};
history_.FilterNewSkottieFrameImages(*skottie_1, images_1);
history_.FilterNewSkottieFrameImages(*skottie_2, images_2);
history_.FilterNewSkottieFrameState(*skottie_1, images_1, empty_text_map);
history_.FilterNewSkottieFrameState(*skottie_2, images_2, empty_text_map);
EXPECT_THAT(
images_2,
UnorderedElementsAre(Pair(
Expand Down Expand Up @@ -190,18 +229,18 @@ TEST_F(SkottieSerializationHistoryTest, RequestInactiveAnimationsPurge) {
{HashSkottieResourceId("asset_2b"),
{image_2, PaintFlags::FilterQuality::kMedium}},
};
history_.FilterNewSkottieFrameImages(*skottie_1, images_1);
history_.FilterNewSkottieFrameImages(*skottie_2, images_2);
history_.FilterNewSkottieFrameState(*skottie_1, images_1, empty_text_map);
history_.FilterNewSkottieFrameState(*skottie_2, images_2, empty_text_map);

history_.RequestInactiveAnimationsPurge();
history_.FilterNewSkottieFrameImages(*skottie_1, images_1);
history_.FilterNewSkottieFrameState(*skottie_1, images_1, empty_text_map);

// Only |skottie_2| should be purged here since |skottie_1| was updated after
// the first purge.
history_.RequestInactiveAnimationsPurge();

history_.FilterNewSkottieFrameImages(*skottie_1, images_1);
history_.FilterNewSkottieFrameImages(*skottie_2, images_2);
history_.FilterNewSkottieFrameState(*skottie_1, images_1, empty_text_map);
history_.FilterNewSkottieFrameState(*skottie_2, images_2, empty_text_map);
EXPECT_THAT(images_1, IsEmpty());
// History for |skottie_2| should start again.
EXPECT_THAT(
Expand Down
43 changes: 43 additions & 0 deletions cc/paint/skottie_text_property_value.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright 2022 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 "cc/paint/skottie_text_property_value.h"

#include <utility>

#include "base/hash/hash.h"

namespace cc {

SkottieTextPropertyValue::SkottieTextPropertyValue(std::string text) {
SetText(std::move(text));
}

SkottieTextPropertyValue::SkottieTextPropertyValue(
const SkottieTextPropertyValue& other) = default;

SkottieTextPropertyValue& SkottieTextPropertyValue::operator=(
const SkottieTextPropertyValue& other) = default;

SkottieTextPropertyValue::~SkottieTextPropertyValue() = default;

bool SkottieTextPropertyValue::operator==(
const SkottieTextPropertyValue& other) const {
return text_hash_ == other.text_hash_;
}

bool SkottieTextPropertyValue::operator!=(
const SkottieTextPropertyValue& other) const {
return !(*this == other);
}

void SkottieTextPropertyValue::SetText(std::string text) {
size_t incoming_text_hash = base::FastHash(text);
if (incoming_text_hash == text_hash_)
return;
text_hash_ = incoming_text_hash;
text_ = base::RefCountedString::TakeString(&text);
}

} // namespace cc
Loading

0 comments on commit 2825a6e

Please sign in to comment.