Commit f45bb828 authored by Malay Keshav's avatar Malay Keshav Committed by Commit Bot

Implement a thread safe wrapper for Skottie

This patch implements a thread safe wrapper for the core skottie
animation object. We want to share the same animation resource file for
multiple SkiaVectorAnimation objects. This would allow us to do that.

This is also needed to implement the Phase 2 and Phase 3 of
rasterization where the rasterization would be handled by different
threads.

go/cros-skottie

Bug: 890512, 890224
Change-Id: I40371e58985ea4db5661f28a2ee8b31aab354a03
Component: Skia vector animation, skottie
Reviewed-on: https://chromium-review.googlesource.com/c/1253071Reviewed-by: default avatarScott Violet <sky@chromium.org>
Commit-Queue: Malay Keshav <malaykeshav@chromium.org>
Cr-Commit-Position: refs/heads/master@{#596118}
parent e4453a95
......@@ -224,6 +224,8 @@ jumbo_component("gfx") {
"skia_vector_animation.cc",
"skia_vector_animation.h",
"skia_vector_animation_observer.h",
"skottie_wrapper.cc",
"skottie_wrapper.h",
]
}
......
......@@ -13,6 +13,7 @@
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/skia_util.h"
#include "ui/gfx/skia_vector_animation_observer.h"
#include "ui/gfx/skottie_wrapper.h"
namespace gfx {
......@@ -68,15 +69,8 @@ double SkiaVectorAnimation::TimerControl::GetNormalizedEndOffset() const {
return end_offset_.InMillisecondsF() * progress_per_millisecond_;
}
SkiaVectorAnimation::SkiaVectorAnimation(
const scoped_refptr<base::RefCountedMemory>& data_stream) {
TRACE_EVENT0("ui", "SkiaVectorAnimation Parse");
SkMemoryStream sk_stream(data_stream->front(), data_stream->size());
animation_ = skottie::Animation::Make(&sk_stream);
}
SkiaVectorAnimation::SkiaVectorAnimation(std::unique_ptr<SkMemoryStream> stream)
: animation_(skottie::Animation::Make(stream.get())) {}
SkiaVectorAnimation::SkiaVectorAnimation(scoped_refptr<SkottieWrapper> skottie)
: skottie_(skottie) {}
SkiaVectorAnimation::~SkiaVectorAnimation() {}
......@@ -88,13 +82,13 @@ void SkiaVectorAnimation::SetAnimationObserver(
base::TimeDelta SkiaVectorAnimation::GetAnimationDuration() const {
return base::TimeDelta::FromMilliseconds(
std::floor(SkScalarToFloat(animation_->duration()) * 1000.f));
std::floor(SkScalarToFloat(skottie_->duration()) * 1000.f));
}
gfx::Size SkiaVectorAnimation::GetOriginalSize() const {
#if DCHECK_IS_ON()
// The size should have no fractional component.
gfx::SizeF float_size = gfx::SkSizeToSizeF(animation_->size());
gfx::SizeF float_size = gfx::SkSizeToSizeF(skottie_->size());
gfx::Size rounded_size = gfx::ToRoundedSize(float_size);
float height_diff = std::abs(float_size.height() - rounded_size.height());
......@@ -103,7 +97,7 @@ gfx::Size SkiaVectorAnimation::GetOriginalSize() const {
DCHECK_LE(height_diff, std::numeric_limits<float>::epsilon());
DCHECK_LE(width_diff, std::numeric_limits<float>::epsilon());
#endif
return gfx::ToRoundedSize(gfx::SkSizeToSizeF(animation_->size()));
return gfx::ToRoundedSize(gfx::SkSizeToSizeF(skottie_->size()));
}
void SkiaVectorAnimation::Start(Style style) {
......@@ -158,8 +152,7 @@ float SkiaVectorAnimation::GetCurrentProgress() const {
} else {
// It may be that the timer hasn't been initialized which may happen if
// the animation was paused while it was in |kScheculePlay| state.
return scheduled_start_offset_.InMillisecondsF() /
animation_->duration();
return scheduled_start_offset_.InMillisecondsF() / skottie_->duration();
}
case PlayState::kSchedulePlay:
case PlayState::kPlaying:
......@@ -214,17 +207,16 @@ void SkiaVectorAnimation::PaintFrame(gfx::Canvas* canvas,
DCHECK_GE(t, 0.f);
DCHECK_LE(t, 1.f);
animation_->seek(t);
float scale = canvas->UndoDeviceScaleFactor();
gfx::Size pixel_size = gfx::ScaleToRoundedSize(size, scale);
SkBitmap bitmap;
bitmap.allocN32Pixels(std::round(size.width() * scale),
std::round(size.height() * scale), false);
bitmap.allocN32Pixels(std::round(pixel_size.width()),
std::round(pixel_size.height()), false);
SkCanvas skcanvas(bitmap);
skcanvas.clear(SK_ColorTRANSPARENT);
SkRect dst = SkRect::MakeXYWH(0, 0, std::round(size.width() * scale),
std::round(size.height() * scale));
animation_->render(&skcanvas, &dst);
skottie_->Draw(&skcanvas, t, pixel_size);
canvas->DrawImageInt(gfx::ImageSkia::CreateFrom1xBitmap(bitmap), 0, 0);
}
......
......@@ -20,6 +20,7 @@ namespace gfx {
class Canvas;
class SkiaVectorAnimationTest;
class SkiaVectorAnimationObserver;
class SkottieWrapper;
// This class is a wrapper over the Skia object for lottie vector graphic
// animations. It has its own timeline manager for the animation controls. The
......@@ -80,9 +81,7 @@ class GFX_EXPORT SkiaVectorAnimation {
kLoop // Same as LINEAR, except the animation repeats after it ends.
};
explicit SkiaVectorAnimation(
const scoped_refptr<base::RefCountedMemory>& data_stream);
explicit SkiaVectorAnimation(std::unique_ptr<SkMemoryStream> stream);
explicit SkiaVectorAnimation(scoped_refptr<SkottieWrapper> skottie);
~SkiaVectorAnimation();
void SetAnimationObserver(SkiaVectorAnimationObserver* Observer);
......@@ -138,6 +137,9 @@ class GFX_EXPORT SkiaVectorAnimation {
// paint.
void PaintFrame(gfx::Canvas* canvas, float t, const gfx::Size& size);
// Returns the skottie object that contins the animation data.
scoped_refptr<SkottieWrapper> skottie() const { return skottie_; }
private:
friend class SkiaVectorAnimationTest;
......@@ -229,7 +231,7 @@ class GFX_EXPORT SkiaVectorAnimation {
SkiaVectorAnimationObserver* observer_ = nullptr;
sk_sp<skottie::Animation> animation_;
scoped_refptr<SkottieWrapper> skottie_;
DISALLOW_COPY_AND_ASSIGN(SkiaVectorAnimation);
};
......
......@@ -15,6 +15,7 @@
#include "third_party/skia/include/core/SkStream.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/skia_vector_animation_observer.h"
#include "ui/gfx/skottie_wrapper.h"
namespace gfx {
namespace {
......@@ -101,6 +102,9 @@ class SkiaVectorAnimationTest : public testing::Test {
void SetUp() override {
canvas_.reset(new gfx::Canvas(gfx::Size(kAnimationWidth, kAnimationHeight),
1.f, false));
skottie_ = base::MakeRefCounted<SkottieWrapper>(
std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
animation_ = std::make_unique<SkiaVectorAnimation>(skottie_);
}
void TearDown() override { animation_.reset(nullptr); }
......@@ -186,6 +190,7 @@ class SkiaVectorAnimationTest : public testing::Test {
protected:
std::unique_ptr<SkiaVectorAnimation> animation_;
scoped_refptr<SkottieWrapper> skottie_;
private:
std::unique_ptr<gfx::Canvas> canvas_;
......@@ -197,15 +202,17 @@ class SkiaVectorAnimationTest : public testing::Test {
TEST_F(SkiaVectorAnimationTest, InitializationAndLoadingData) {
auto bytes = base::MakeRefCounted<base::RefCountedBytes>(
std::vector<unsigned char>(kData, kData + std::strlen(kData)));
animation_ = std::make_unique<SkiaVectorAnimation>(bytes.get());
skottie_ = base::MakeRefCounted<SkottieWrapper>(bytes.get());
animation_ = std::make_unique<SkiaVectorAnimation>(skottie_);
EXPECT_FLOAT_EQ(animation_->GetOriginalSize().width(), kAnimationWidth);
EXPECT_FLOAT_EQ(animation_->GetOriginalSize().height(), kAnimationHeight);
EXPECT_FLOAT_EQ(animation_->GetAnimationDuration().InSecondsF(),
kAnimationDuration);
EXPECT_TRUE(IsStopped());
animation_ = std::make_unique<SkiaVectorAnimation>(
skottie_ = base::MakeRefCounted<SkottieWrapper>(
std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
animation_ = std::make_unique<SkiaVectorAnimation>(skottie_);
EXPECT_FLOAT_EQ(animation_->GetOriginalSize().width(), kAnimationWidth);
EXPECT_FLOAT_EQ(animation_->GetOriginalSize().height(), kAnimationHeight);
EXPECT_FLOAT_EQ(animation_->GetAnimationDuration().InSecondsF(),
......@@ -215,8 +222,6 @@ TEST_F(SkiaVectorAnimationTest, InitializationAndLoadingData) {
TEST_F(SkiaVectorAnimationTest, PlayLinearAnimation) {
TestAnimationObserver observer;
animation_ = std::make_unique<SkiaVectorAnimation>(
std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
animation_->SetAnimationObserver(&observer);
// Advance clock by 300 milliseconds.
......@@ -256,8 +261,6 @@ TEST_F(SkiaVectorAnimationTest, PlayLinearAnimation) {
TEST_F(SkiaVectorAnimationTest, StopLinearAnimation) {
TestAnimationObserver observer;
animation_ = std::make_unique<SkiaVectorAnimation>(
std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
animation_->SetAnimationObserver(&observer);
// Advance clock by 300 milliseconds.
......@@ -285,8 +288,6 @@ TEST_F(SkiaVectorAnimationTest, PlaySubsectionOfLinearAnimation) {
TestAnimationObserver observer;
animation_ = std::make_unique<SkiaVectorAnimation>(
std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
animation_->SetAnimationObserver(&observer);
// Advance clock by 300 milliseconds.
......@@ -344,8 +345,6 @@ TEST_F(SkiaVectorAnimationTest, PausingLinearAnimation) {
const int start_time_ms = 400;
const int duration_ms = 1000;
const float total_duration_ms = kAnimationDuration * 1000.f;
animation_ = std::make_unique<SkiaVectorAnimation>(
std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
TestAnimationObserver observer;
animation_->SetAnimationObserver(&observer);
......@@ -390,8 +389,6 @@ TEST_F(SkiaVectorAnimationTest, PausingLinearAnimation) {
TEST_F(SkiaVectorAnimationTest, PlayLoopAnimation) {
TestAnimationObserver observer;
animation_ = std::make_unique<SkiaVectorAnimation>(
std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
animation_->SetAnimationObserver(&observer);
// Advance clock by 300 milliseconds.
......@@ -436,8 +433,6 @@ TEST_F(SkiaVectorAnimationTest, PlaySubsectionOfLoopAnimation) {
const int duration_ms = 1000;
const float total_duration_ms = kAnimationDuration * 1000.f;
animation_ = std::make_unique<SkiaVectorAnimation>(
std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
TestAnimationObserver observer;
animation_->SetAnimationObserver(&observer);
......@@ -498,8 +493,6 @@ TEST_F(SkiaVectorAnimationTest, PausingLoopAnimation) {
const int start_time_ms = 400;
const int duration_ms = 1000;
const float total_duration_ms = kAnimationDuration * 1000.f;
animation_ = std::make_unique<SkiaVectorAnimation>(
std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
TestAnimationObserver observer;
animation_->SetAnimationObserver(&observer);
......@@ -550,8 +543,6 @@ TEST_F(SkiaVectorAnimationTest, PausingLoopAnimation) {
}
TEST_F(SkiaVectorAnimationTest, PlayThrobbingAnimation) {
animation_ = std::make_unique<SkiaVectorAnimation>(
std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
TestAnimationObserver observer;
animation_->SetAnimationObserver(&observer);
......@@ -607,8 +598,6 @@ TEST_F(SkiaVectorAnimationTest, PlaySubsectionOfThrobbingAnimation) {
const int duration_ms = 1000;
const float total_duration_ms = kAnimationDuration * 1000.f;
animation_ = std::make_unique<SkiaVectorAnimation>(
std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
TestAnimationObserver observer;
animation_->SetAnimationObserver(&observer);
......@@ -688,8 +677,6 @@ TEST_F(SkiaVectorAnimationTest, PausingThrobbingAnimation) {
const int start_time_ms = 400;
const int duration_ms = 1000;
const float total_duration_ms = kAnimationDuration * 1000.f;
animation_ = std::make_unique<SkiaVectorAnimation>(
std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
AdvanceClock(200);
......@@ -782,8 +769,6 @@ TEST_F(SkiaVectorAnimationTest, PauseBeforePlay) {
// Test to see if the race condition is handled correctly. It may happen that
// we pause the video before it even starts playing.
animation_ = std::make_unique<SkiaVectorAnimation>(
std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
TestAnimationObserver observer;
animation_->SetAnimationObserver(&observer);
......@@ -814,8 +799,6 @@ TEST_F(SkiaVectorAnimationTest, PauseBeforePlay) {
TEST_F(SkiaVectorAnimationTest, PaintTest) {
std::unique_ptr<gfx::Canvas> canvas(new gfx::Canvas(
gfx::Size(kAnimationWidth, kAnimationHeight), 1.f, false));
animation_ = std::make_unique<SkiaVectorAnimation>(
std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
// Advance clock by 300 milliseconds.
AdvanceClock(300);
......
// Copyright 2018 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/skottie_wrapper.h"
#include "base/memory/ref_counted_memory.h"
#include "base/trace_event/trace_event.h"
#include "third_party/skia/include/core/SkRect.h"
#include "third_party/skia/include/core/SkStream.h"
#include "ui/gfx/geometry/size.h"
namespace gfx {
SkottieWrapper::SkottieWrapper(
const scoped_refptr<base::RefCountedMemory>& data_stream) {
TRACE_EVENT0("ui", "SkottieWrapper Parse");
SkMemoryStream sk_stream(data_stream->front(), data_stream->size());
animation_ = skottie::Animation::Make(&sk_stream);
}
SkottieWrapper::SkottieWrapper(std::unique_ptr<SkMemoryStream> stream)
: animation_(skottie::Animation::Make(stream.get())) {}
SkottieWrapper::~SkottieWrapper() {}
void SkottieWrapper::Draw(SkCanvas* canvas, float t, const gfx::Size& size) {
SkRect dst = SkRect::MakeXYWH(0, 0, size.width(), size.height());
{
base::AutoLock lock(lock_);
animation_->seek(t);
animation_->render(canvas, &dst);
}
}
} // namespace gfx
// Copyright 2018 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.
#ifndef UI_GFX_SKOTTIE_WRAPPER_H_
#define UI_GFX_SKOTTIE_WRAPPER_H_
#include <memory>
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/synchronization/lock.h"
#include "third_party/skia/modules/skottie/include/Skottie.h"
#include "ui/gfx/gfx_export.h"
class SkCanvas;
class SkMemoryStream;
namespace base {
class RefCountedMemory;
} // namespace base
namespace gfx {
class Size;
// A wrapper over Skia's Skottie object that can be shared by multiple
// SkiaVectorAnimation objects. This class is thread safe when performing a draw
// on an SkCanvas.
class GFX_EXPORT SkottieWrapper
: public base::RefCountedThreadSafe<SkottieWrapper> {
public:
explicit SkottieWrapper(
const scoped_refptr<base::RefCountedMemory>& data_stream);
explicit SkottieWrapper(std::unique_ptr<SkMemoryStream> stream);
// A thread safe call that will draw an image of size |size| for the frame at
// normalized time instant |t| onto the |canvas|.
void Draw(SkCanvas* canvas, float t, const Size& size);
float duration() const { return animation_->duration(); }
SkSize size() const { return animation_->size(); }
private:
friend class base::RefCountedThreadSafe<SkottieWrapper>;
~SkottieWrapper();
base::Lock lock_;
sk_sp<skottie::Animation> animation_;
DISALLOW_COPY_AND_ASSIGN(SkottieWrapper);
};
} // namespace gfx
#endif // UI_GFX_SKOTTIE_WRAPPER_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment