Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 38921ca

Browse files
fmalitaSkia Commit-Bot
authored andcommitted
Reland "[skottie] Add image sampling and transform options"
This reverts commit b81842a. Reason for revert: reland with fixes Original change's description: > Revert "[skottie] Add image sampling and transform options" > > This reverts commit 2f24405. > > Reason for revert: broke Win/shared > > Original change's description: > > [skottie] Add image sampling and transform options > > > > Expand the SkImageAsset API to support controlling sampling options and > > pass an additional transform. > > > > Bug: skia:10944, skia:10942 > > Change-Id: I7bad0b2ab58ed40fe4b425de0eb6970a4c7d7117 > > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/340097 > > Commit-Queue: Florin Malita <[email protected]> > > Commit-Queue: Florin Malita <[email protected]> > > Reviewed-by: Mike Reed <[email protected]> > > [email protected],[email protected],[email protected],[email protected] > > Change-Id: I59d4161356ffdc20588f1bd3beb33c54e44807a2 > No-Presubmit: true > No-Tree-Checks: true > No-Try: true > Bug: skia:10944 > Bug: skia:10942 > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/340619 > Reviewed-by: Florin Malita <[email protected]> > Commit-Queue: Florin Malita <[email protected]> [email protected],[email protected],[email protected],[email protected] # Not skipping CQ checks because this is a reland. Bug: skia:10944 Bug: skia:10942 Change-Id: I91892f4db6366ceb07d1a49a7bc54da17cea5399 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/340657 Reviewed-by: Mike Reed <[email protected]> Reviewed-by: Brian Osman <[email protected]> Commit-Queue: Florin Malita <[email protected]> Commit-Queue: Florin Malita <[email protected]>
1 parent 5f9ba69 commit 38921ca

File tree

9 files changed

+230
-26
lines changed

9 files changed

+230
-26
lines changed

include/core/SkSamplingOptions.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,15 @@ struct SK_API SkSamplingOptions {
6666
, cubic(c) {}
6767

6868
explicit SkSamplingOptions(SkFilterQuality);
69+
70+
bool operator==(const SkSamplingOptions& other) const {
71+
return useCubic == other.useCubic
72+
&& cubic.B == other.cubic.B
73+
&& cubic.C == other.cubic.C
74+
&& filter == other.filter
75+
&& mipmap == other.mipmap;
76+
}
77+
bool operator!=(const SkSamplingOptions& other) const { return !(*this == other); }
6978
};
7079

7180
#endif

modules/skottie/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ if (skia_enable_skottie) {
5555
sources = [
5656
"src/SkottieTest.cpp",
5757
"tests/AudioLayer.cpp",
58+
"tests/Image.cpp",
5859
"tests/Keyframe.cpp",
5960
"tests/Text.cpp",
6061
]

modules/skottie/src/layers/FootageLayer.cpp

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,14 @@ namespace internal {
1717

1818
namespace {
1919

20-
SkMatrix image_matrix(const sk_sp<SkImage>& image, const SkISize& dest_size) {
21-
return image ? SkMatrix::MakeRectToRect(SkRect::Make(image->bounds()),
22-
SkRect::Make(dest_size),
23-
SkMatrix::kCenter_ScaleToFit)
24-
: SkMatrix::I();
20+
SkMatrix image_matrix(const ImageAsset::FrameData& frame_data, const SkISize& dest_size) {
21+
if (!frame_data.image) {
22+
return SkMatrix::I();
23+
}
24+
25+
return frame_data.matrix * SkMatrix::MakeRectToRect(SkRect::Make(frame_data.image->bounds()),
26+
SkRect::Make(dest_size),
27+
SkMatrix::kCenter_ScaleToFit);
2528
}
2629

2730
class FootageAnimator final : public Animator {
@@ -45,10 +48,15 @@ class FootageAnimator final : public Animator {
4548
return false;
4649
}
4750

48-
auto frame = fAsset->getFrame((t + fTimeBias) * fTimeScale);
49-
if (frame != fImageNode->getImage()) {
50-
fImageTransformNode->setMatrix(image_matrix(frame, fAssetSize));
51-
fImageNode->setImage(std::move(frame));
51+
auto frame_data = fAsset->getFrameData((t + fTimeBias) * fTimeScale);
52+
const auto m = image_matrix(frame_data, fAssetSize);
53+
if (frame_data.image != fImageNode->getImage() ||
54+
frame_data.sampling != fImageNode->getSamplingOptions() ||
55+
m != fImageTransformNode->getMatrix()) {
56+
57+
fImageNode->setImage(std::move(frame_data.image));
58+
fImageNode->setSamplingOptions(frame_data.sampling);
59+
fImageTransformNode->setMatrix(m);
5260
return true;
5361
}
5462

@@ -102,7 +110,6 @@ sk_sp<sksg::RenderNode> AnimationBuilder::attachFootageAsset(const skjson::Objec
102110
SkASSERT(asset_info->fAsset);
103111

104112
auto image_node = sksg::Image::Make(nullptr);
105-
image_node->setQuality(kMedium_SkFilterQuality);
106113

107114
// Optional image transform (mapping the intrinsic image size to declared asset size).
108115
sk_sp<sksg::Matrix<SkMatrix>> image_transform;
@@ -121,17 +128,19 @@ sk_sp<sksg::RenderNode> AnimationBuilder::attachFootageAsset(const skjson::Objec
121128
1 / fFrameRate));
122129
} else {
123130
// No animator needed, resolve the (only) frame upfront.
124-
auto frame = asset_info->fAsset->getFrame(0);
125-
if (!frame) {
131+
auto frame_data = asset_info->fAsset->getFrameData(0);
132+
if (!frame_data.image) {
126133
this->log(Logger::Level::kError, nullptr, "Could not load single-frame image asset.");
127134
return nullptr;
128135
}
129136

130-
if (frame->bounds().size() != asset_info->fSize) {
131-
image_transform = sksg::Matrix<SkMatrix>::Make(image_matrix(frame, asset_info->fSize));
137+
const auto m = image_matrix(frame_data, asset_info->fSize);
138+
if (!m.isIdentity()) {
139+
image_transform = sksg::Matrix<SkMatrix>::Make(m);
132140
}
133141

134-
image_node->setImage(std::move(frame));
142+
image_node->setImage(std::move(frame_data.image));
143+
image_node->setSamplingOptions(frame_data.sampling);
135144
}
136145

137146
// Image layers are sized explicitly.

modules/skottie/tests/Image.cpp

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/*
2+
* Copyright 2020 Google Inc.
3+
*
4+
* Use of this source code is governed by a BSD-style license that can be
5+
* found in the LICENSE file.
6+
*/
7+
8+
#include "include/core/SkSurface.h"
9+
#include "modules/skottie/include/Skottie.h"
10+
#include "tests/Test.h"
11+
12+
using namespace skottie;
13+
14+
DEF_TEST(Skottie_Image_CustomTransform, r) {
15+
static constexpr char json[] =
16+
R"({
17+
"v": "5.2.1",
18+
"w": 100,
19+
"h": 100,
20+
"fr": 10,
21+
"ip": 0,
22+
"op": 100,
23+
"assets": [{
24+
"id": "img_0",
25+
"p" : "img_0.png",
26+
"u" : "images/",
27+
"w" : 100,
28+
"h" : 50
29+
}],
30+
"layers": [
31+
{
32+
"ip": 0,
33+
"op": 100,
34+
"ty": 2,
35+
"refId": "img_0",
36+
"ks": {
37+
"p": { "a": 0, "k": [0,25] }
38+
}
39+
}
40+
]
41+
})";
42+
43+
SkMemoryStream stream(json, strlen(json));
44+
45+
static const struct TestData {
46+
float t;
47+
SkMatrix m;
48+
SkColor c[5]; // expected color samples at center/L/T/R/B
49+
} tests[] {
50+
{ 0, SkMatrix::I(),
51+
{0xffff0000, 0xffff0000, 0xff00ff00, 0xffff0000, 0xff00ff00}},
52+
{ 1, SkMatrix::Translate(50,25) * SkMatrix::Scale(.5f,.5f) * SkMatrix::Translate(-50,-25),
53+
{0xffff0000, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00}},
54+
{ 2, SkMatrix::Translate(-50, 0),
55+
{0xff00ff00, 0xffff0000, 0xff00ff00, 0xff00ff00, 0xff00ff00}},
56+
{ 3, SkMatrix::Translate(0, -25),
57+
{0xff00ff00, 0xff00ff00, 0xffff0000, 0xff00ff00, 0xff00ff00}},
58+
{ 4, SkMatrix::Translate(50, 0),
59+
{0xffff0000, 0xff00ff00, 0xff00ff00, 0xffff0000, 0xff00ff00}},
60+
{ 5, SkMatrix::Translate(0, 25),
61+
{0xffff0000, 0xffff0000, 0xff00ff00, 0xffff0000, 0xffff0000}},
62+
};
63+
64+
class TestImageAsset final : public ImageAsset {
65+
public:
66+
TestImageAsset(const TestData* tst, skiatest::Reporter* r)
67+
: fTest(tst)
68+
, fReporter(r) {
69+
70+
auto surf = SkSurface::MakeRasterN32Premul(200, 100);
71+
surf->getCanvas()->drawColor(0xffff0000);
72+
fImage = surf->makeImageSnapshot();
73+
}
74+
75+
private:
76+
bool isMultiFrame() override { return true; }
77+
78+
FrameData getFrameData(float t) override {
79+
REPORTER_ASSERT(fReporter, t == fTest->t);
80+
81+
return { fImage, SkSamplingOptions(), fTest++->m };
82+
}
83+
84+
sk_sp<SkImage> fImage;
85+
const TestData* fTest;
86+
skiatest::Reporter* fReporter;
87+
};
88+
89+
class TestResourceProvider final : public ResourceProvider {
90+
public:
91+
TestResourceProvider(const TestData* tst, skiatest::Reporter* r)
92+
: fTest(tst)
93+
, fReporter(r) {}
94+
95+
private:
96+
sk_sp<ImageAsset> loadImageAsset(const char[], const char[], const char[]) const override {
97+
return sk_make_sp<TestImageAsset>(fTest, fReporter);
98+
}
99+
100+
const TestData* fTest;
101+
skiatest::Reporter* fReporter;
102+
};
103+
104+
auto anim = Animation::Builder()
105+
.setResourceProvider(sk_make_sp<TestResourceProvider>(tests, r))
106+
.make(&stream);
107+
108+
REPORTER_ASSERT(r, anim);
109+
110+
static constexpr SkSize render_size{100, 100};
111+
auto surf = SkSurface::MakeRasterN32Premul(render_size.width(), render_size.height());
112+
auto rect = SkRect::MakeSize(render_size);
113+
114+
SkPixmap pmap;
115+
surf->peekPixels(&pmap);
116+
117+
for (const auto& tst : tests) {
118+
surf->getCanvas()->clear(0xff00ff00);
119+
anim->seekFrameTime(tst.t);
120+
anim->render(surf->getCanvas(), &rect);
121+
122+
REPORTER_ASSERT(r,
123+
tst.c[0] == pmap.getColor(render_size.width() / 2, render_size.height() / 2));
124+
REPORTER_ASSERT(r,
125+
tst.c[1] == pmap.getColor(1 , render_size.height() / 2));
126+
REPORTER_ASSERT(r,
127+
tst.c[2] == pmap.getColor(render_size.width() / 2, 1));
128+
REPORTER_ASSERT(r,
129+
tst.c[3] == pmap.getColor(render_size.width() - 1, render_size.height() / 2));
130+
REPORTER_ASSERT(r,
131+
tst.c[4] == pmap.getColor(render_size.width() /2 , render_size.height() - 1));
132+
}
133+
}

modules/skresources/BUILD.gn

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ static_library("skresources") {
1414
public_configs = [ ":public_config" ]
1515
public = skia_skresources_public
1616
sources = skia_skresources_sources
17-
configs += [ "../../:skia_private" ]
17+
configs += [
18+
"../../:skia_private",
19+
"../../:skia_library",
20+
]
1821
deps = [
1922
"../..:skia",
2023
"../../experimental/ffmpeg:video_decoder",

modules/skresources/include/SkResources.h

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
#define SkResources_DEFINED
1010

1111
#include "include/core/SkData.h"
12+
#include "include/core/SkMatrix.h"
1213
#include "include/core/SkRefCnt.h"
14+
#include "include/core/SkSamplingOptions.h"
1315
#include "include/core/SkString.h"
1416
#include "include/core/SkTypeface.h"
1517
#include "include/core/SkTypes.h"
@@ -34,17 +36,41 @@ class SK_API ImageAsset : public SkRefCnt {
3436
virtual bool isMultiFrame() = 0;
3537

3638
/**
39+
* DEPRECATED: override getFrameData() instead.
40+
*
3741
* Returns the SkImage for a given frame.
3842
*
39-
* If the image asset is static, getImage() is only called once, at animation load time.
43+
* If the image asset is static, getFrame() is only called once, at animation load time.
44+
* Otherwise, this gets invoked every time the animation time is adjusted (on every seek).
45+
*
46+
* Embedders should cache and serve the same SkImage whenever possible, for efficiency.
47+
*
48+
* @param t Frame time code, in seconds, relative to the image layer timeline origin
49+
* (in-point).
50+
*/
51+
virtual sk_sp<SkImage> getFrame(float t);
52+
53+
struct FrameData {
54+
// SkImage payload.
55+
sk_sp<SkImage> image;
56+
// Resampling parameters.
57+
SkSamplingOptions sampling;
58+
// Additional image transform to be applied before AE scaling rules.
59+
SkMatrix matrix = SkMatrix::I();
60+
};
61+
62+
/**
63+
* Returns the payload for a given frame.
64+
*
65+
* If the image asset is static, getFrameData() is only called once, at animation load time.
4066
* Otherwise, this gets invoked every time the animation time is adjusted (on every seek).
4167
*
4268
* Embedders should cache and serve the same SkImage whenever possible, for efficiency.
4369
*
4470
* @param t Frame time code, in seconds, relative to the image layer timeline origin
4571
* (in-point).
4672
*/
47-
virtual sk_sp<SkImage> getFrame(float t) = 0;
73+
virtual FrameData getFrameData(float t);
4874
};
4975

5076
class MultiFrameImageAsset final : public ImageAsset {

modules/skresources/src/SkResources.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,19 @@ class VideoAsset final : public ImageAsset {
8888

8989
} // namespace
9090

91+
sk_sp<SkImage> ImageAsset::getFrame(float t) {
92+
return nullptr;
93+
}
94+
95+
ImageAsset::FrameData ImageAsset::getFrameData(float t) {
96+
// legacy behavior
97+
return {
98+
this->getFrame(t),
99+
SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNearest),
100+
SkMatrix::I(),
101+
};
102+
}
103+
91104
sk_sp<MultiFrameImageAsset> MultiFrameImageAsset::Make(sk_sp<SkData> data, bool predecode) {
92105
if (auto codec = SkCodec::MakeFromData(std::move(data))) {
93106
return sk_sp<MultiFrameImageAsset>(

modules/sksg/include/SkSGImage.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
#include "modules/sksg/include/SkSGRenderNode.h"
1212

13-
#include "include/core/SkFilterQuality.h"
13+
#include "include/core/SkSamplingOptions.h"
1414

1515
class SkImage;
1616

@@ -26,9 +26,9 @@ class Image final : public RenderNode {
2626
return sk_sp<Image>(new Image(std::move(image)));
2727
}
2828

29-
SG_ATTRIBUTE(Image, sk_sp<SkImage> , fImage )
30-
SG_ATTRIBUTE(Quality , SkFilterQuality, fQuality )
31-
SG_ATTRIBUTE(AntiAlias, bool , fAntiAlias)
29+
SG_ATTRIBUTE(Image , sk_sp<SkImage> , fImage )
30+
SG_ATTRIBUTE(SamplingOptions, SkSamplingOptions, fSamplingOptions)
31+
SG_ATTRIBUTE(AntiAlias , bool , fAntiAlias )
3232

3333
protected:
3434
explicit Image(sk_sp<SkImage>);
@@ -39,9 +39,9 @@ class Image final : public RenderNode {
3939
SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
4040

4141
private:
42-
sk_sp<SkImage> fImage;
43-
SkFilterQuality fQuality = kNone_SkFilterQuality;
44-
bool fAntiAlias = true;
42+
SkSamplingOptions fSamplingOptions;
43+
sk_sp<SkImage> fImage;
44+
bool fAntiAlias = true;
4545

4646
using INHERITED = RenderNode;
4747
};

modules/sksg/src/SkSGImage.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,19 @@ void Image::onRender(SkCanvas* canvas, const RenderContext* ctx) const {
1919
return;
2020
}
2121

22+
// Ignoring cubic params and trilerp for now.
23+
// TODO: convert to drawImage(sampling options) when available.
24+
auto legacy_quality = [](const SkSamplingOptions& sampling) {
25+
return
26+
sampling.useCubic ? SkFilterQuality::kHigh_SkFilterQuality :
27+
sampling.filter == SkFilterMode::kNearest ? SkFilterQuality::kNone_SkFilterQuality :
28+
sampling.mipmap == SkMipmapMode::kNone ? SkFilterQuality::kLow_SkFilterQuality :
29+
SkFilterQuality::kMedium_SkFilterQuality;
30+
};
31+
2232
SkPaint paint;
2333
paint.setAntiAlias(fAntiAlias);
24-
paint.setFilterQuality(fQuality);
34+
paint.setFilterQuality(legacy_quality(fSamplingOptions));
2535

2636
sksg::RenderNode::ScopedRenderContext local_ctx(canvas, ctx);
2737
if (ctx) {

0 commit comments

Comments
 (0)