1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2016 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkAndroidCodec.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkCodec.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkCodecAnimation.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkEncodedOrigin.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkAlphaType.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBitmap.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorType.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkData.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImage.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImageInfo.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkMatrix.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSize.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "tests/CodecPriv.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "tests/Test.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "tools/Resources.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "tools/ToolUtils.h"
28*c8dee2aaSAndroid Build Coastguard Worker
29*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
30*c8dee2aaSAndroid Build Coastguard Worker #include <cstring>
31*c8dee2aaSAndroid Build Coastguard Worker #include <initializer_list>
32*c8dee2aaSAndroid Build Coastguard Worker #include <memory>
33*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
34*c8dee2aaSAndroid Build Coastguard Worker #include <vector>
35*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(Codec_trunc,r)36*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_trunc, r) {
37*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkData> data(GetResourceAsData("images/box.gif"));
38*c8dee2aaSAndroid Build Coastguard Worker if (!data) {
39*c8dee2aaSAndroid Build Coastguard Worker return;
40*c8dee2aaSAndroid Build Coastguard Worker }
41*c8dee2aaSAndroid Build Coastguard Worker // See also Codec_GifTruncated2 in GifTest.cpp for this magic 23.
42*c8dee2aaSAndroid Build Coastguard Worker //
43*c8dee2aaSAndroid Build Coastguard Worker // TODO: just move this getFrameInfo call to Codec_GifTruncated2?
44*c8dee2aaSAndroid Build Coastguard Worker SkCodec::MakeFromData(SkData::MakeSubset(data.get(), 0, 23))->getFrameInfo();
45*c8dee2aaSAndroid Build Coastguard Worker }
46*c8dee2aaSAndroid Build Coastguard Worker
47*c8dee2aaSAndroid Build Coastguard Worker // 565 does not support alpha, but there is no reason for it not to support an
48*c8dee2aaSAndroid Build Coastguard Worker // animated image with a frame that has alpha but then blends onto an opaque
49*c8dee2aaSAndroid Build Coastguard Worker // frame making the result opaque. Test that we can decode such a frame.
DEF_TEST(Codec_565,r)50*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_565, r) {
51*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkData> data(GetResourceAsData("images/blendBG.webp"));
52*c8dee2aaSAndroid Build Coastguard Worker if (!data) {
53*c8dee2aaSAndroid Build Coastguard Worker return;
54*c8dee2aaSAndroid Build Coastguard Worker }
55*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(std::move(data)));
56*c8dee2aaSAndroid Build Coastguard Worker auto info = codec->getInfo().makeColorType(kRGB_565_SkColorType);
57*c8dee2aaSAndroid Build Coastguard Worker SkBitmap bm;
58*c8dee2aaSAndroid Build Coastguard Worker bm.allocPixels(info);
59*c8dee2aaSAndroid Build Coastguard Worker
60*c8dee2aaSAndroid Build Coastguard Worker SkCodec::Options options;
61*c8dee2aaSAndroid Build Coastguard Worker options.fFrameIndex = 1;
62*c8dee2aaSAndroid Build Coastguard Worker options.fPriorFrame = SkCodec::kNoFrame;
63*c8dee2aaSAndroid Build Coastguard Worker
64*c8dee2aaSAndroid Build Coastguard Worker const auto result = codec->getPixels(info, bm.getPixels(), bm.rowBytes(),
65*c8dee2aaSAndroid Build Coastguard Worker &options);
66*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, result == SkCodec::kSuccess);
67*c8dee2aaSAndroid Build Coastguard Worker }
68*c8dee2aaSAndroid Build Coastguard Worker
restore_previous(const SkCodec::FrameInfo & info)69*c8dee2aaSAndroid Build Coastguard Worker static bool restore_previous(const SkCodec::FrameInfo& info) {
70*c8dee2aaSAndroid Build Coastguard Worker return info.fDisposalMethod == SkCodecAnimation::DisposalMethod::kRestorePrevious;
71*c8dee2aaSAndroid Build Coastguard Worker }
72*c8dee2aaSAndroid Build Coastguard Worker
73*c8dee2aaSAndroid Build Coastguard Worker namespace {
to_string(bool boolean)74*c8dee2aaSAndroid Build Coastguard Worker SkString to_string(bool boolean) { return boolean ? SkString("true") : SkString("false"); }
to_string(SkCodecAnimation::Blend blend)75*c8dee2aaSAndroid Build Coastguard Worker SkString to_string(SkCodecAnimation::Blend blend) {
76*c8dee2aaSAndroid Build Coastguard Worker switch (blend) {
77*c8dee2aaSAndroid Build Coastguard Worker case SkCodecAnimation::Blend::kSrcOver:
78*c8dee2aaSAndroid Build Coastguard Worker return SkString("kSrcOver");
79*c8dee2aaSAndroid Build Coastguard Worker case SkCodecAnimation::Blend::kSrc:
80*c8dee2aaSAndroid Build Coastguard Worker return SkString("kSrc");
81*c8dee2aaSAndroid Build Coastguard Worker default:
82*c8dee2aaSAndroid Build Coastguard Worker return SkString();
83*c8dee2aaSAndroid Build Coastguard Worker }
84*c8dee2aaSAndroid Build Coastguard Worker }
to_string(SkIRect rect)85*c8dee2aaSAndroid Build Coastguard Worker SkString to_string(SkIRect rect) {
86*c8dee2aaSAndroid Build Coastguard Worker return SkStringPrintf("{ %i, %i, %i, %i }", rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
87*c8dee2aaSAndroid Build Coastguard Worker }
88*c8dee2aaSAndroid Build Coastguard Worker
89*c8dee2aaSAndroid Build Coastguard Worker template <typename T>
reporter_assert_equals(skiatest::Reporter * r,const char * name,int i,const char * prop,T expected,T actual)90*c8dee2aaSAndroid Build Coastguard Worker void reporter_assert_equals(skiatest::Reporter* r, const char* name, int i, const char* prop,
91*c8dee2aaSAndroid Build Coastguard Worker T expected, T actual) {
92*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, expected == actual, "%s's frame %i has wrong %s! expected:"
93*c8dee2aaSAndroid Build Coastguard Worker " %s\tactual: %s", name, i, prop, to_string(expected).c_str(),
94*c8dee2aaSAndroid Build Coastguard Worker to_string(actual).c_str());
95*c8dee2aaSAndroid Build Coastguard Worker }
96*c8dee2aaSAndroid Build Coastguard Worker } // namespace
97*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(Codec_frames,r)98*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(Codec_frames, r) {
99*c8dee2aaSAndroid Build Coastguard Worker constexpr int kNoFrame = SkCodec::kNoFrame;
100*c8dee2aaSAndroid Build Coastguard Worker constexpr SkAlphaType kOpaque = kOpaque_SkAlphaType;
101*c8dee2aaSAndroid Build Coastguard Worker constexpr SkAlphaType kUnpremul = kUnpremul_SkAlphaType;
102*c8dee2aaSAndroid Build Coastguard Worker constexpr SkCodecAnimation::DisposalMethod kKeep =
103*c8dee2aaSAndroid Build Coastguard Worker SkCodecAnimation::DisposalMethod::kKeep;
104*c8dee2aaSAndroid Build Coastguard Worker constexpr SkCodecAnimation::DisposalMethod kRestoreBG =
105*c8dee2aaSAndroid Build Coastguard Worker SkCodecAnimation::DisposalMethod::kRestoreBGColor;
106*c8dee2aaSAndroid Build Coastguard Worker constexpr SkCodecAnimation::DisposalMethod kRestorePrev =
107*c8dee2aaSAndroid Build Coastguard Worker SkCodecAnimation::DisposalMethod::kRestorePrevious;
108*c8dee2aaSAndroid Build Coastguard Worker constexpr auto kSrcOver = SkCodecAnimation::Blend::kSrcOver;
109*c8dee2aaSAndroid Build Coastguard Worker constexpr auto kSrc = SkCodecAnimation::Blend::kSrc;
110*c8dee2aaSAndroid Build Coastguard Worker
111*c8dee2aaSAndroid Build Coastguard Worker static const struct {
112*c8dee2aaSAndroid Build Coastguard Worker const char* fName;
113*c8dee2aaSAndroid Build Coastguard Worker int fFrameCount;
114*c8dee2aaSAndroid Build Coastguard Worker // One less than fFramecount, since the first frame is always
115*c8dee2aaSAndroid Build Coastguard Worker // independent.
116*c8dee2aaSAndroid Build Coastguard Worker std::vector<int> fRequiredFrames;
117*c8dee2aaSAndroid Build Coastguard Worker // Same, since the first frame should match getInfo
118*c8dee2aaSAndroid Build Coastguard Worker std::vector<SkAlphaType> fAlphas;
119*c8dee2aaSAndroid Build Coastguard Worker // The size of this one should match fFrameCount for animated, empty
120*c8dee2aaSAndroid Build Coastguard Worker // otherwise.
121*c8dee2aaSAndroid Build Coastguard Worker std::vector<int> fDurations;
122*c8dee2aaSAndroid Build Coastguard Worker int fRepetitionCount;
123*c8dee2aaSAndroid Build Coastguard Worker std::vector<SkCodecAnimation::DisposalMethod> fDisposalMethods;
124*c8dee2aaSAndroid Build Coastguard Worker std::vector<bool> fAlphaWithinBounds;
125*c8dee2aaSAndroid Build Coastguard Worker std::vector<SkCodecAnimation::Blend> fBlends;
126*c8dee2aaSAndroid Build Coastguard Worker std::vector<SkIRect> fFrameRects;
127*c8dee2aaSAndroid Build Coastguard Worker } gRecs[] = {
128*c8dee2aaSAndroid Build Coastguard Worker { "images/required.gif", 7,
129*c8dee2aaSAndroid Build Coastguard Worker { 0, 1, 2, 3, 4, 5 },
130*c8dee2aaSAndroid Build Coastguard Worker { kOpaque, kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul },
131*c8dee2aaSAndroid Build Coastguard Worker { 100, 100, 100, 100, 100, 100, 100 },
132*c8dee2aaSAndroid Build Coastguard Worker 0,
133*c8dee2aaSAndroid Build Coastguard Worker { kKeep, kRestoreBG, kKeep, kKeep, kKeep, kRestoreBG, kKeep },
134*c8dee2aaSAndroid Build Coastguard Worker { false, true, true, true, true, true, true },
135*c8dee2aaSAndroid Build Coastguard Worker { kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver,
136*c8dee2aaSAndroid Build Coastguard Worker kSrcOver },
137*c8dee2aaSAndroid Build Coastguard Worker { {0, 0, 100, 100}, {0, 0, 75, 75}, {0, 0, 50, 50}, {0, 0, 60, 60},
138*c8dee2aaSAndroid Build Coastguard Worker {0, 0, 100, 100}, {0, 0, 50, 50}, {0, 0, 75, 75}},
139*c8dee2aaSAndroid Build Coastguard Worker },
140*c8dee2aaSAndroid Build Coastguard Worker { "images/alphabetAnim.gif", 13,
141*c8dee2aaSAndroid Build Coastguard Worker { kNoFrame, 0, 0, 0, 0, 5, 6, kNoFrame, kNoFrame, 9, 10, 11 },
142*c8dee2aaSAndroid Build Coastguard Worker { kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul,
143*c8dee2aaSAndroid Build Coastguard Worker kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul },
144*c8dee2aaSAndroid Build Coastguard Worker { 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100 },
145*c8dee2aaSAndroid Build Coastguard Worker 0,
146*c8dee2aaSAndroid Build Coastguard Worker { kKeep, kRestorePrev, kRestorePrev, kRestorePrev, kRestorePrev,
147*c8dee2aaSAndroid Build Coastguard Worker kRestoreBG, kKeep, kRestoreBG, kRestoreBG, kKeep, kKeep,
148*c8dee2aaSAndroid Build Coastguard Worker kRestoreBG, kKeep },
149*c8dee2aaSAndroid Build Coastguard Worker { true, false, true, false, true, true, true, true, true, true, true, true, true },
150*c8dee2aaSAndroid Build Coastguard Worker { kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver,
151*c8dee2aaSAndroid Build Coastguard Worker kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver,
152*c8dee2aaSAndroid Build Coastguard Worker kSrcOver },
153*c8dee2aaSAndroid Build Coastguard Worker { {25, 25, 75, 75}, {25, 25, 75, 75}, {25, 25, 75, 75}, {37, 37, 62, 62},
154*c8dee2aaSAndroid Build Coastguard Worker {37, 37, 62, 62}, {25, 25, 75, 75}, {0, 0, 50, 50}, {0, 0, 100, 100},
155*c8dee2aaSAndroid Build Coastguard Worker {25, 25, 75, 75}, {25, 25, 75, 75}, {0, 0, 100, 100}, {25, 25, 75, 75},
156*c8dee2aaSAndroid Build Coastguard Worker {37, 37, 62, 62}},
157*c8dee2aaSAndroid Build Coastguard Worker },
158*c8dee2aaSAndroid Build Coastguard Worker { "images/randPixelsAnim2.gif", 4,
159*c8dee2aaSAndroid Build Coastguard Worker // required frames
160*c8dee2aaSAndroid Build Coastguard Worker { 0, 0, 1 },
161*c8dee2aaSAndroid Build Coastguard Worker // alphas
162*c8dee2aaSAndroid Build Coastguard Worker { kOpaque, kOpaque, kOpaque },
163*c8dee2aaSAndroid Build Coastguard Worker // durations
164*c8dee2aaSAndroid Build Coastguard Worker { 0, 1000, 170, 40 },
165*c8dee2aaSAndroid Build Coastguard Worker // repetition count
166*c8dee2aaSAndroid Build Coastguard Worker 0,
167*c8dee2aaSAndroid Build Coastguard Worker { kKeep, kKeep, kRestorePrev, kKeep },
168*c8dee2aaSAndroid Build Coastguard Worker { false, true, false, false },
169*c8dee2aaSAndroid Build Coastguard Worker { kSrcOver, kSrcOver, kSrcOver, kSrcOver },
170*c8dee2aaSAndroid Build Coastguard Worker { {0, 0, 8, 8}, {6, 6, 8, 8}, {4, 4, 8, 8}, {7, 0, 8, 8} },
171*c8dee2aaSAndroid Build Coastguard Worker },
172*c8dee2aaSAndroid Build Coastguard Worker { "images/randPixelsAnim.gif", 13,
173*c8dee2aaSAndroid Build Coastguard Worker // required frames
174*c8dee2aaSAndroid Build Coastguard Worker { 0, 1, 2, 3, 4, 3, 6, 7, 7, 7, 9, 9 },
175*c8dee2aaSAndroid Build Coastguard Worker { kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul,
176*c8dee2aaSAndroid Build Coastguard Worker kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul },
177*c8dee2aaSAndroid Build Coastguard Worker // durations
178*c8dee2aaSAndroid Build Coastguard Worker { 0, 1000, 170, 40, 220, 7770, 90, 90, 90, 90, 90, 90, 90 },
179*c8dee2aaSAndroid Build Coastguard Worker // repetition count
180*c8dee2aaSAndroid Build Coastguard Worker 0,
181*c8dee2aaSAndroid Build Coastguard Worker { kKeep, kKeep, kKeep, kKeep, kRestoreBG, kRestoreBG, kRestoreBG,
182*c8dee2aaSAndroid Build Coastguard Worker kRestoreBG, kRestorePrev, kRestoreBG, kRestorePrev, kRestorePrev,
183*c8dee2aaSAndroid Build Coastguard Worker kRestorePrev, },
184*c8dee2aaSAndroid Build Coastguard Worker { false, true, true, false, true, true, false, false, true, true, false, false,
185*c8dee2aaSAndroid Build Coastguard Worker true },
186*c8dee2aaSAndroid Build Coastguard Worker { kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver,
187*c8dee2aaSAndroid Build Coastguard Worker kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver,
188*c8dee2aaSAndroid Build Coastguard Worker kSrcOver },
189*c8dee2aaSAndroid Build Coastguard Worker { {4, 4, 12, 12}, {4, 4, 12, 12}, {4, 4, 12, 12}, {0, 0, 8, 8}, {8, 8, 16, 16},
190*c8dee2aaSAndroid Build Coastguard Worker {8, 8, 16, 16}, {8, 8, 16, 16}, {2, 2, 10, 10}, {7, 7, 15, 15}, {7, 7, 15, 15},
191*c8dee2aaSAndroid Build Coastguard Worker {7, 7, 15, 15}, {0, 0, 8, 8}, {14, 14, 16, 16} },
192*c8dee2aaSAndroid Build Coastguard Worker },
193*c8dee2aaSAndroid Build Coastguard Worker { "images/box.gif", 1, {}, {}, {}, SkCodec::kRepetitionCountInfinite, { kKeep }, {}, {}, {} },
194*c8dee2aaSAndroid Build Coastguard Worker { "images/color_wheel.gif", 1, {}, {}, {}, SkCodec::kRepetitionCountInfinite, { kKeep }, {}, {}, {} },
195*c8dee2aaSAndroid Build Coastguard Worker { "images/test640x479.gif", 4, { 0, 1, 2 },
196*c8dee2aaSAndroid Build Coastguard Worker { kOpaque, kOpaque, kOpaque },
197*c8dee2aaSAndroid Build Coastguard Worker { 200, 200, 200, 200 },
198*c8dee2aaSAndroid Build Coastguard Worker SkCodec::kRepetitionCountInfinite,
199*c8dee2aaSAndroid Build Coastguard Worker { kKeep, kKeep, kKeep, kKeep },
200*c8dee2aaSAndroid Build Coastguard Worker { false, true, true, true },
201*c8dee2aaSAndroid Build Coastguard Worker { kSrcOver, kSrcOver, kSrcOver, kSrcOver },
202*c8dee2aaSAndroid Build Coastguard Worker { {0, 0, 640, 479}, {0, 0, 640, 479}, {0, 0, 640, 479}, {0, 0, 640, 479} },
203*c8dee2aaSAndroid Build Coastguard Worker },
204*c8dee2aaSAndroid Build Coastguard Worker { "images/colorTables.gif", 2, { 0 }, { kOpaque }, { 1000, 1000 }, 5,
205*c8dee2aaSAndroid Build Coastguard Worker { kKeep, kKeep }, {false, true}, { kSrcOver, kSrcOver },
206*c8dee2aaSAndroid Build Coastguard Worker { {0, 0, 640, 400}, {0, 0, 640, 200}},
207*c8dee2aaSAndroid Build Coastguard Worker },
208*c8dee2aaSAndroid Build Coastguard Worker
209*c8dee2aaSAndroid Build Coastguard Worker { "images/arrow.png", 1, {}, {}, {}, 0, {}, {}, {}, {} },
210*c8dee2aaSAndroid Build Coastguard Worker { "images/google_chrome.ico", 1, {}, {}, {}, 0, {}, {}, {}, {} },
211*c8dee2aaSAndroid Build Coastguard Worker { "images/brickwork-texture.jpg", 1, {}, {}, {}, 0, {}, {}, {}, {} },
212*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32))
213*c8dee2aaSAndroid Build Coastguard Worker { "images/dng_with_preview.dng", 1, {}, {}, {}, 0, {}, {}, {}, {} },
214*c8dee2aaSAndroid Build Coastguard Worker #endif
215*c8dee2aaSAndroid Build Coastguard Worker { "images/mandrill.wbmp", 1, {}, {}, {}, 0, {}, {}, {}, {} },
216*c8dee2aaSAndroid Build Coastguard Worker { "images/randPixels.bmp", 1, {}, {}, {}, 0, {}, {}, {}, {} },
217*c8dee2aaSAndroid Build Coastguard Worker { "images/yellow_rose.webp", 1, {}, {}, {}, 0, {}, {}, {}, {} },
218*c8dee2aaSAndroid Build Coastguard Worker { "images/stoplight.webp", 3, { 0, 1 }, { kOpaque, kOpaque },
219*c8dee2aaSAndroid Build Coastguard Worker { 1000, 500, 1000 }, SkCodec::kRepetitionCountInfinite,
220*c8dee2aaSAndroid Build Coastguard Worker { kKeep, kKeep, kKeep }, {false, false, false},
221*c8dee2aaSAndroid Build Coastguard Worker {kSrcOver, kSrcOver, kSrcOver},
222*c8dee2aaSAndroid Build Coastguard Worker { {0, 0, 11, 29}, {2, 10, 9, 27}, {2, 2, 9, 18}},
223*c8dee2aaSAndroid Build Coastguard Worker },
224*c8dee2aaSAndroid Build Coastguard Worker { "images/blendBG.webp", 7,
225*c8dee2aaSAndroid Build Coastguard Worker { 0, kNoFrame, kNoFrame, kNoFrame, 4, 4 },
226*c8dee2aaSAndroid Build Coastguard Worker { kOpaque, kOpaque, kUnpremul, kOpaque, kUnpremul, kUnpremul },
227*c8dee2aaSAndroid Build Coastguard Worker { 525, 500, 525, 437, 609, 729, 444 },
228*c8dee2aaSAndroid Build Coastguard Worker 6,
229*c8dee2aaSAndroid Build Coastguard Worker { kKeep, kKeep, kKeep, kKeep, kKeep, kKeep, kKeep },
230*c8dee2aaSAndroid Build Coastguard Worker { false, true, false, true, false, true, true },
231*c8dee2aaSAndroid Build Coastguard Worker { kSrc, kSrcOver, kSrc, kSrc, kSrc, kSrc, kSrc },
232*c8dee2aaSAndroid Build Coastguard Worker { {0, 0, 200, 200}, {0, 0, 200, 200}, {0, 0, 200, 200}, {0, 0, 200, 200},
233*c8dee2aaSAndroid Build Coastguard Worker {0, 0, 200, 200}, {100, 100, 200, 200}, {100, 100, 200, 200} },
234*c8dee2aaSAndroid Build Coastguard Worker },
235*c8dee2aaSAndroid Build Coastguard Worker { "images/required.webp", 7,
236*c8dee2aaSAndroid Build Coastguard Worker { 0, 1, 1, kNoFrame, 4, 4 },
237*c8dee2aaSAndroid Build Coastguard Worker { kOpaque, kUnpremul, kUnpremul, kOpaque, kOpaque, kOpaque },
238*c8dee2aaSAndroid Build Coastguard Worker { 100, 100, 100, 100, 100, 100, 100 },
239*c8dee2aaSAndroid Build Coastguard Worker 0,
240*c8dee2aaSAndroid Build Coastguard Worker { kKeep, kRestoreBG, kKeep, kKeep, kKeep, kRestoreBG, kKeep },
241*c8dee2aaSAndroid Build Coastguard Worker { false, false, false, false, false, false, false },
242*c8dee2aaSAndroid Build Coastguard Worker { kSrc, kSrcOver, kSrcOver, kSrcOver, kSrc, kSrcOver,
243*c8dee2aaSAndroid Build Coastguard Worker kSrcOver },
244*c8dee2aaSAndroid Build Coastguard Worker { {0, 0, 100, 100}, {0, 0, 75, 75}, {0, 0, 50, 50}, {0, 0, 60, 60},
245*c8dee2aaSAndroid Build Coastguard Worker {0, 0, 100, 100}, {0, 0, 50, 50}, {0, 0, 75, 75}},
246*c8dee2aaSAndroid Build Coastguard Worker },
247*c8dee2aaSAndroid Build Coastguard Worker };
248*c8dee2aaSAndroid Build Coastguard Worker
249*c8dee2aaSAndroid Build Coastguard Worker for (const auto& rec : gRecs) {
250*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkData> data(GetResourceAsData(rec.fName));
251*c8dee2aaSAndroid Build Coastguard Worker if (!data) {
252*c8dee2aaSAndroid Build Coastguard Worker // Useful error statement, but sometimes people run tests without
253*c8dee2aaSAndroid Build Coastguard Worker // resources, and they do not want to see these messages.
254*c8dee2aaSAndroid Build Coastguard Worker //ERRORF(r, "Missing resources? Could not find '%s'", rec.fName);
255*c8dee2aaSAndroid Build Coastguard Worker continue;
256*c8dee2aaSAndroid Build Coastguard Worker }
257*c8dee2aaSAndroid Build Coastguard Worker
258*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(data));
259*c8dee2aaSAndroid Build Coastguard Worker if (!codec) {
260*c8dee2aaSAndroid Build Coastguard Worker ERRORF(r, "Failed to create an SkCodec from '%s'", rec.fName);
261*c8dee2aaSAndroid Build Coastguard Worker continue;
262*c8dee2aaSAndroid Build Coastguard Worker }
263*c8dee2aaSAndroid Build Coastguard Worker
264*c8dee2aaSAndroid Build Coastguard Worker {
265*c8dee2aaSAndroid Build Coastguard Worker SkCodec::FrameInfo frameInfo;
266*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, !codec->getFrameInfo(0, &frameInfo));
267*c8dee2aaSAndroid Build Coastguard Worker }
268*c8dee2aaSAndroid Build Coastguard Worker
269*c8dee2aaSAndroid Build Coastguard Worker const int expected = rec.fFrameCount;
270*c8dee2aaSAndroid Build Coastguard Worker if (rec.fRequiredFrames.size() + 1 != static_cast<size_t>(expected)) {
271*c8dee2aaSAndroid Build Coastguard Worker ERRORF(r, "'%s' has wrong number entries in fRequiredFrames; expected: %i\tactual: %zu",
272*c8dee2aaSAndroid Build Coastguard Worker rec.fName, expected - 1, rec.fRequiredFrames.size());
273*c8dee2aaSAndroid Build Coastguard Worker continue;
274*c8dee2aaSAndroid Build Coastguard Worker }
275*c8dee2aaSAndroid Build Coastguard Worker
276*c8dee2aaSAndroid Build Coastguard Worker if (expected > 1) {
277*c8dee2aaSAndroid Build Coastguard Worker if (rec.fDurations.size() != static_cast<size_t>(expected)) {
278*c8dee2aaSAndroid Build Coastguard Worker ERRORF(r, "'%s' has wrong number entries in fDurations; expected: %i\tactual: %zu",
279*c8dee2aaSAndroid Build Coastguard Worker rec.fName, expected, rec.fDurations.size());
280*c8dee2aaSAndroid Build Coastguard Worker continue;
281*c8dee2aaSAndroid Build Coastguard Worker }
282*c8dee2aaSAndroid Build Coastguard Worker
283*c8dee2aaSAndroid Build Coastguard Worker if (rec.fAlphas.size() + 1 != static_cast<size_t>(expected)) {
284*c8dee2aaSAndroid Build Coastguard Worker ERRORF(r, "'%s' has wrong number entries in fAlphas; expected: %i\tactual: %zu",
285*c8dee2aaSAndroid Build Coastguard Worker rec.fName, expected - 1, rec.fAlphas.size());
286*c8dee2aaSAndroid Build Coastguard Worker continue;
287*c8dee2aaSAndroid Build Coastguard Worker }
288*c8dee2aaSAndroid Build Coastguard Worker
289*c8dee2aaSAndroid Build Coastguard Worker if (rec.fDisposalMethods.size() != static_cast<size_t>(expected)) {
290*c8dee2aaSAndroid Build Coastguard Worker ERRORF(r, "'%s' has wrong number entries in fDisposalMethods; "
291*c8dee2aaSAndroid Build Coastguard Worker "expected %i\tactual: %zu",
292*c8dee2aaSAndroid Build Coastguard Worker rec.fName, expected, rec.fDisposalMethods.size());
293*c8dee2aaSAndroid Build Coastguard Worker continue;
294*c8dee2aaSAndroid Build Coastguard Worker }
295*c8dee2aaSAndroid Build Coastguard Worker }
296*c8dee2aaSAndroid Build Coastguard Worker
297*c8dee2aaSAndroid Build Coastguard Worker enum class TestMode {
298*c8dee2aaSAndroid Build Coastguard Worker kVector,
299*c8dee2aaSAndroid Build Coastguard Worker kIndividual,
300*c8dee2aaSAndroid Build Coastguard Worker };
301*c8dee2aaSAndroid Build Coastguard Worker
302*c8dee2aaSAndroid Build Coastguard Worker for (auto mode : { TestMode::kVector, TestMode::kIndividual }) {
303*c8dee2aaSAndroid Build Coastguard Worker // Re-create the codec to reset state and test parsing.
304*c8dee2aaSAndroid Build Coastguard Worker codec = SkCodec::MakeFromData(data);
305*c8dee2aaSAndroid Build Coastguard Worker
306*c8dee2aaSAndroid Build Coastguard Worker int frameCount;
307*c8dee2aaSAndroid Build Coastguard Worker std::vector<SkCodec::FrameInfo> frameInfos;
308*c8dee2aaSAndroid Build Coastguard Worker switch (mode) {
309*c8dee2aaSAndroid Build Coastguard Worker case TestMode::kVector:
310*c8dee2aaSAndroid Build Coastguard Worker frameInfos = codec->getFrameInfo();
311*c8dee2aaSAndroid Build Coastguard Worker // getFrameInfo returns empty set for non-animated.
312*c8dee2aaSAndroid Build Coastguard Worker frameCount = frameInfos.empty() ? 1 : frameInfos.size();
313*c8dee2aaSAndroid Build Coastguard Worker break;
314*c8dee2aaSAndroid Build Coastguard Worker case TestMode::kIndividual:
315*c8dee2aaSAndroid Build Coastguard Worker frameCount = codec->getFrameCount();
316*c8dee2aaSAndroid Build Coastguard Worker break;
317*c8dee2aaSAndroid Build Coastguard Worker }
318*c8dee2aaSAndroid Build Coastguard Worker
319*c8dee2aaSAndroid Build Coastguard Worker if (frameCount != expected) {
320*c8dee2aaSAndroid Build Coastguard Worker ERRORF(r, "'%s' expected frame count: %i\tactual: %i",
321*c8dee2aaSAndroid Build Coastguard Worker rec.fName, expected, frameCount);
322*c8dee2aaSAndroid Build Coastguard Worker continue;
323*c8dee2aaSAndroid Build Coastguard Worker }
324*c8dee2aaSAndroid Build Coastguard Worker
325*c8dee2aaSAndroid Build Coastguard Worker // Get the repetition count after the codec->getFrameInfo() or
326*c8dee2aaSAndroid Build Coastguard Worker // codec->getFrameCount() call above has walked to the end of the
327*c8dee2aaSAndroid Build Coastguard Worker // encoded image.
328*c8dee2aaSAndroid Build Coastguard Worker //
329*c8dee2aaSAndroid Build Coastguard Worker // At the file format level, GIF images can declare their
330*c8dee2aaSAndroid Build Coastguard Worker // repetition count multiple times and our codec goes with "last
331*c8dee2aaSAndroid Build Coastguard Worker // one wins". Furthermore, for single-frame (still) GIF images, a
332*c8dee2aaSAndroid Build Coastguard Worker // zero, positive or infinite repetition count are all equivalent
333*c8dee2aaSAndroid Build Coastguard Worker // in practice (in all cases, the pixels do not change over time),
334*c8dee2aaSAndroid Build Coastguard Worker // so the codec has some leeway in what to return for single-frame
335*c8dee2aaSAndroid Build Coastguard Worker // GIF images, but it cannot distinguish single-frame from
336*c8dee2aaSAndroid Build Coastguard Worker // multiple-frame GIFs until we count the number of frames (e.g.
337*c8dee2aaSAndroid Build Coastguard Worker // call getFrameInfo or getFrameCount).
338*c8dee2aaSAndroid Build Coastguard Worker const int repetitionCount = codec->getRepetitionCount();
339*c8dee2aaSAndroid Build Coastguard Worker if (repetitionCount != rec.fRepetitionCount) {
340*c8dee2aaSAndroid Build Coastguard Worker ERRORF(r, "%s repetition count does not match! expected: %i\tactual: %i",
341*c8dee2aaSAndroid Build Coastguard Worker rec.fName, rec.fRepetitionCount, repetitionCount);
342*c8dee2aaSAndroid Build Coastguard Worker }
343*c8dee2aaSAndroid Build Coastguard Worker
344*c8dee2aaSAndroid Build Coastguard Worker // From here on, we are only concerned with animated images.
345*c8dee2aaSAndroid Build Coastguard Worker if (1 == frameCount) {
346*c8dee2aaSAndroid Build Coastguard Worker continue;
347*c8dee2aaSAndroid Build Coastguard Worker }
348*c8dee2aaSAndroid Build Coastguard Worker
349*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < frameCount; i++) {
350*c8dee2aaSAndroid Build Coastguard Worker SkCodec::FrameInfo frameInfo;
351*c8dee2aaSAndroid Build Coastguard Worker switch (mode) {
352*c8dee2aaSAndroid Build Coastguard Worker case TestMode::kVector:
353*c8dee2aaSAndroid Build Coastguard Worker frameInfo = frameInfos[i];
354*c8dee2aaSAndroid Build Coastguard Worker break;
355*c8dee2aaSAndroid Build Coastguard Worker case TestMode::kIndividual:
356*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, codec->getFrameInfo(i, nullptr));
357*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, codec->getFrameInfo(i, &frameInfo));
358*c8dee2aaSAndroid Build Coastguard Worker break;
359*c8dee2aaSAndroid Build Coastguard Worker }
360*c8dee2aaSAndroid Build Coastguard Worker
361*c8dee2aaSAndroid Build Coastguard Worker if (rec.fDurations[i] != frameInfo.fDuration) {
362*c8dee2aaSAndroid Build Coastguard Worker ERRORF(r, "%s frame %i's durations do not match! expected: %i\tactual: %i",
363*c8dee2aaSAndroid Build Coastguard Worker rec.fName, i, rec.fDurations[i], frameInfo.fDuration);
364*c8dee2aaSAndroid Build Coastguard Worker }
365*c8dee2aaSAndroid Build Coastguard Worker
366*c8dee2aaSAndroid Build Coastguard Worker auto to_string = [](SkAlphaType alpha) {
367*c8dee2aaSAndroid Build Coastguard Worker switch (alpha) {
368*c8dee2aaSAndroid Build Coastguard Worker case kUnpremul_SkAlphaType:
369*c8dee2aaSAndroid Build Coastguard Worker return "unpremul";
370*c8dee2aaSAndroid Build Coastguard Worker case kOpaque_SkAlphaType:
371*c8dee2aaSAndroid Build Coastguard Worker return "opaque";
372*c8dee2aaSAndroid Build Coastguard Worker default:
373*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(false);
374*c8dee2aaSAndroid Build Coastguard Worker return "unknown";
375*c8dee2aaSAndroid Build Coastguard Worker }
376*c8dee2aaSAndroid Build Coastguard Worker };
377*c8dee2aaSAndroid Build Coastguard Worker
378*c8dee2aaSAndroid Build Coastguard Worker auto expectedAlpha = 0 == i ? codec->getInfo().alphaType() : rec.fAlphas[i-1];
379*c8dee2aaSAndroid Build Coastguard Worker auto alpha = frameInfo.fAlphaType;
380*c8dee2aaSAndroid Build Coastguard Worker if (expectedAlpha != alpha) {
381*c8dee2aaSAndroid Build Coastguard Worker ERRORF(r, "%s's frame %i has wrong alpha type! expected: %s\tactual: %s",
382*c8dee2aaSAndroid Build Coastguard Worker rec.fName, i, to_string(expectedAlpha), to_string(alpha));
383*c8dee2aaSAndroid Build Coastguard Worker }
384*c8dee2aaSAndroid Build Coastguard Worker
385*c8dee2aaSAndroid Build Coastguard Worker if (0 == i) {
386*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, frameInfo.fRequiredFrame == SkCodec::kNoFrame);
387*c8dee2aaSAndroid Build Coastguard Worker } else if (rec.fRequiredFrames[i-1] != frameInfo.fRequiredFrame) {
388*c8dee2aaSAndroid Build Coastguard Worker ERRORF(r, "%s's frame %i has wrong dependency! expected: %i\tactual: %i",
389*c8dee2aaSAndroid Build Coastguard Worker rec.fName, i, rec.fRequiredFrames[i-1], frameInfo.fRequiredFrame);
390*c8dee2aaSAndroid Build Coastguard Worker }
391*c8dee2aaSAndroid Build Coastguard Worker
392*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, frameInfo.fDisposalMethod == rec.fDisposalMethods[i]);
393*c8dee2aaSAndroid Build Coastguard Worker
394*c8dee2aaSAndroid Build Coastguard Worker reporter_assert_equals<bool>(r, rec.fName, i, "alpha within bounds",
395*c8dee2aaSAndroid Build Coastguard Worker rec.fAlphaWithinBounds[i],
396*c8dee2aaSAndroid Build Coastguard Worker frameInfo.fHasAlphaWithinBounds);
397*c8dee2aaSAndroid Build Coastguard Worker
398*c8dee2aaSAndroid Build Coastguard Worker reporter_assert_equals(r, rec.fName, i, "blend mode", rec.fBlends[i],
399*c8dee2aaSAndroid Build Coastguard Worker frameInfo.fBlend);
400*c8dee2aaSAndroid Build Coastguard Worker
401*c8dee2aaSAndroid Build Coastguard Worker reporter_assert_equals(r, rec.fName, i, "frame rect", rec.fFrameRects[i],
402*c8dee2aaSAndroid Build Coastguard Worker frameInfo.fFrameRect);
403*c8dee2aaSAndroid Build Coastguard Worker }
404*c8dee2aaSAndroid Build Coastguard Worker
405*c8dee2aaSAndroid Build Coastguard Worker if (TestMode::kIndividual == mode) {
406*c8dee2aaSAndroid Build Coastguard Worker // No need to test decoding twice.
407*c8dee2aaSAndroid Build Coastguard Worker continue;
408*c8dee2aaSAndroid Build Coastguard Worker }
409*c8dee2aaSAndroid Build Coastguard Worker
410*c8dee2aaSAndroid Build Coastguard Worker // Compare decoding in multiple ways:
411*c8dee2aaSAndroid Build Coastguard Worker // - Start from scratch for each frame. |codec| will have to decode the required frame
412*c8dee2aaSAndroid Build Coastguard Worker // (and any it depends on) to decode. This is stored in |cachedFrames|.
413*c8dee2aaSAndroid Build Coastguard Worker // - Provide the frame that a frame depends on, so |codec| just has to blend.
414*c8dee2aaSAndroid Build Coastguard Worker // - Provide a frame after the required frame, which will be covered up by the newest
415*c8dee2aaSAndroid Build Coastguard Worker // frame.
416*c8dee2aaSAndroid Build Coastguard Worker // All should look the same.
417*c8dee2aaSAndroid Build Coastguard Worker std::vector<SkBitmap> cachedFrames(frameCount);
418*c8dee2aaSAndroid Build Coastguard Worker const auto info = codec->getInfo().makeColorType(kN32_SkColorType);
419*c8dee2aaSAndroid Build Coastguard Worker
420*c8dee2aaSAndroid Build Coastguard Worker auto decode = [&](SkBitmap* bm, int index, int cachedIndex) {
421*c8dee2aaSAndroid Build Coastguard Worker auto decodeInfo = info;
422*c8dee2aaSAndroid Build Coastguard Worker if (index > 0) {
423*c8dee2aaSAndroid Build Coastguard Worker decodeInfo = info.makeAlphaType(frameInfos[index].fAlphaType);
424*c8dee2aaSAndroid Build Coastguard Worker }
425*c8dee2aaSAndroid Build Coastguard Worker bm->allocPixels(decodeInfo);
426*c8dee2aaSAndroid Build Coastguard Worker if (cachedIndex != SkCodec::kNoFrame) {
427*c8dee2aaSAndroid Build Coastguard Worker // First copy the pixels from the cached frame
428*c8dee2aaSAndroid Build Coastguard Worker const bool success =
429*c8dee2aaSAndroid Build Coastguard Worker ToolUtils::copy_to(bm, kN32_SkColorType, cachedFrames[cachedIndex]);
430*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, success);
431*c8dee2aaSAndroid Build Coastguard Worker }
432*c8dee2aaSAndroid Build Coastguard Worker SkCodec::Options opts;
433*c8dee2aaSAndroid Build Coastguard Worker opts.fFrameIndex = index;
434*c8dee2aaSAndroid Build Coastguard Worker opts.fPriorFrame = cachedIndex;
435*c8dee2aaSAndroid Build Coastguard Worker const auto result = codec->getPixels(decodeInfo, bm->getPixels(), bm->rowBytes(),
436*c8dee2aaSAndroid Build Coastguard Worker &opts);
437*c8dee2aaSAndroid Build Coastguard Worker if (cachedIndex != SkCodec::kNoFrame &&
438*c8dee2aaSAndroid Build Coastguard Worker restore_previous(frameInfos[cachedIndex])) {
439*c8dee2aaSAndroid Build Coastguard Worker if (result == SkCodec::kInvalidParameters) {
440*c8dee2aaSAndroid Build Coastguard Worker return true;
441*c8dee2aaSAndroid Build Coastguard Worker }
442*c8dee2aaSAndroid Build Coastguard Worker ERRORF(r, "Using a kRestorePrevious frame as fPriorFrame should fail");
443*c8dee2aaSAndroid Build Coastguard Worker return false;
444*c8dee2aaSAndroid Build Coastguard Worker }
445*c8dee2aaSAndroid Build Coastguard Worker if (result != SkCodec::kSuccess) {
446*c8dee2aaSAndroid Build Coastguard Worker ERRORF(r, "Failed to decode frame %i from %s when providing prior frame %i, "
447*c8dee2aaSAndroid Build Coastguard Worker "error %i", index, rec.fName, cachedIndex, result);
448*c8dee2aaSAndroid Build Coastguard Worker }
449*c8dee2aaSAndroid Build Coastguard Worker return result == SkCodec::kSuccess;
450*c8dee2aaSAndroid Build Coastguard Worker };
451*c8dee2aaSAndroid Build Coastguard Worker
452*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < frameCount; i++) {
453*c8dee2aaSAndroid Build Coastguard Worker SkBitmap& cachedFrame = cachedFrames[i];
454*c8dee2aaSAndroid Build Coastguard Worker if (!decode(&cachedFrame, i, SkCodec::kNoFrame)) {
455*c8dee2aaSAndroid Build Coastguard Worker continue;
456*c8dee2aaSAndroid Build Coastguard Worker }
457*c8dee2aaSAndroid Build Coastguard Worker const auto reqFrame = frameInfos[i].fRequiredFrame;
458*c8dee2aaSAndroid Build Coastguard Worker if (reqFrame == SkCodec::kNoFrame) {
459*c8dee2aaSAndroid Build Coastguard Worker // Nothing to compare against.
460*c8dee2aaSAndroid Build Coastguard Worker continue;
461*c8dee2aaSAndroid Build Coastguard Worker }
462*c8dee2aaSAndroid Build Coastguard Worker for (int j = reqFrame; j < i; j++) {
463*c8dee2aaSAndroid Build Coastguard Worker SkBitmap frame;
464*c8dee2aaSAndroid Build Coastguard Worker if (restore_previous(frameInfos[j])) {
465*c8dee2aaSAndroid Build Coastguard Worker (void) decode(&frame, i, j);
466*c8dee2aaSAndroid Build Coastguard Worker continue;
467*c8dee2aaSAndroid Build Coastguard Worker }
468*c8dee2aaSAndroid Build Coastguard Worker if (!decode(&frame, i, j)) {
469*c8dee2aaSAndroid Build Coastguard Worker continue;
470*c8dee2aaSAndroid Build Coastguard Worker }
471*c8dee2aaSAndroid Build Coastguard Worker
472*c8dee2aaSAndroid Build Coastguard Worker // Now verify they're equal.
473*c8dee2aaSAndroid Build Coastguard Worker const size_t rowLen = info.bytesPerPixel() * info.width();
474*c8dee2aaSAndroid Build Coastguard Worker for (int y = 0; y < info.height(); y++) {
475*c8dee2aaSAndroid Build Coastguard Worker const void* cachedAddr = cachedFrame.getAddr(0, y);
476*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(cachedAddr != nullptr);
477*c8dee2aaSAndroid Build Coastguard Worker const void* addr = frame.getAddr(0, y);
478*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(addr != nullptr);
479*c8dee2aaSAndroid Build Coastguard Worker const bool lineMatches = memcmp(cachedAddr, addr, rowLen) == 0;
480*c8dee2aaSAndroid Build Coastguard Worker if (!lineMatches) {
481*c8dee2aaSAndroid Build Coastguard Worker SkString name = SkStringPrintf("cached_%i", i);
482*c8dee2aaSAndroid Build Coastguard Worker write_bm(name.c_str(), cachedFrame);
483*c8dee2aaSAndroid Build Coastguard Worker name = SkStringPrintf("frame_%i", i);
484*c8dee2aaSAndroid Build Coastguard Worker write_bm(name.c_str(), frame);
485*c8dee2aaSAndroid Build Coastguard Worker ERRORF(r, "%s's frame %i is different (starting from line %i) when "
486*c8dee2aaSAndroid Build Coastguard Worker "providing prior frame %i!", rec.fName, i, y, j);
487*c8dee2aaSAndroid Build Coastguard Worker break;
488*c8dee2aaSAndroid Build Coastguard Worker }
489*c8dee2aaSAndroid Build Coastguard Worker }
490*c8dee2aaSAndroid Build Coastguard Worker }
491*c8dee2aaSAndroid Build Coastguard Worker }
492*c8dee2aaSAndroid Build Coastguard Worker }
493*c8dee2aaSAndroid Build Coastguard Worker }
494*c8dee2aaSAndroid Build Coastguard Worker }
495*c8dee2aaSAndroid Build Coastguard Worker
496*c8dee2aaSAndroid Build Coastguard Worker // Verify that an image can be animated scaled down. These images have a
497*c8dee2aaSAndroid Build Coastguard Worker // kRestoreBG frame, so they are interesting to test. After decoding that
498*c8dee2aaSAndroid Build Coastguard Worker // frame, we have to erase its rectangle. The rectangle has to be adjusted
499*c8dee2aaSAndroid Build Coastguard Worker // based on the scaled size.
test_animated_AndroidCodec(skiatest::Reporter * r,const char * file)500*c8dee2aaSAndroid Build Coastguard Worker static void test_animated_AndroidCodec(skiatest::Reporter* r, const char* file) {
501*c8dee2aaSAndroid Build Coastguard Worker if (GetResourcePath().isEmpty()) {
502*c8dee2aaSAndroid Build Coastguard Worker return;
503*c8dee2aaSAndroid Build Coastguard Worker }
504*c8dee2aaSAndroid Build Coastguard Worker
505*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkData> data(GetResourceAsData(file));
506*c8dee2aaSAndroid Build Coastguard Worker if (!data) {
507*c8dee2aaSAndroid Build Coastguard Worker ERRORF(r, "Missing %s", file);
508*c8dee2aaSAndroid Build Coastguard Worker return;
509*c8dee2aaSAndroid Build Coastguard Worker }
510*c8dee2aaSAndroid Build Coastguard Worker
511*c8dee2aaSAndroid Build Coastguard Worker auto codec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(std::move(data)));
512*c8dee2aaSAndroid Build Coastguard Worker if (!codec) {
513*c8dee2aaSAndroid Build Coastguard Worker ERRORF(r, "Failed to decode %s", file);
514*c8dee2aaSAndroid Build Coastguard Worker return;
515*c8dee2aaSAndroid Build Coastguard Worker }
516*c8dee2aaSAndroid Build Coastguard Worker
517*c8dee2aaSAndroid Build Coastguard Worker auto info = codec->getInfo().makeAlphaType(kPremul_SkAlphaType);
518*c8dee2aaSAndroid Build Coastguard Worker
519*c8dee2aaSAndroid Build Coastguard Worker for (int sampleSize : { 8, 32, 100 }) {
520*c8dee2aaSAndroid Build Coastguard Worker auto dimensions = codec->codec()->getScaledDimensions(1.0f / sampleSize);
521*c8dee2aaSAndroid Build Coastguard Worker info = info.makeDimensions(dimensions);
522*c8dee2aaSAndroid Build Coastguard Worker SkBitmap bm;
523*c8dee2aaSAndroid Build Coastguard Worker bm.allocPixels(info);
524*c8dee2aaSAndroid Build Coastguard Worker
525*c8dee2aaSAndroid Build Coastguard Worker SkCodec::Options options;
526*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < codec->codec()->getFrameCount(); ++i) {
527*c8dee2aaSAndroid Build Coastguard Worker SkCodec::FrameInfo frameInfo;
528*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, codec->codec()->getFrameInfo(i, &frameInfo));
529*c8dee2aaSAndroid Build Coastguard Worker if (5 == i) {
530*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, frameInfo.fDisposalMethod
531*c8dee2aaSAndroid Build Coastguard Worker == SkCodecAnimation::DisposalMethod::kRestoreBGColor);
532*c8dee2aaSAndroid Build Coastguard Worker }
533*c8dee2aaSAndroid Build Coastguard Worker options.fFrameIndex = i;
534*c8dee2aaSAndroid Build Coastguard Worker options.fPriorFrame = i - 1;
535*c8dee2aaSAndroid Build Coastguard Worker info = info.makeAlphaType(frameInfo.fAlphaType);
536*c8dee2aaSAndroid Build Coastguard Worker
537*c8dee2aaSAndroid Build Coastguard Worker auto result = codec->codec()->getPixels(info, bm.getPixels(), bm.rowBytes(),
538*c8dee2aaSAndroid Build Coastguard Worker &options);
539*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, result == SkCodec::kSuccess);
540*c8dee2aaSAndroid Build Coastguard Worker
541*c8dee2aaSAndroid Build Coastguard Worker // Now compare to not using prior frame.
542*c8dee2aaSAndroid Build Coastguard Worker SkBitmap bm2;
543*c8dee2aaSAndroid Build Coastguard Worker bm2.allocPixels(info);
544*c8dee2aaSAndroid Build Coastguard Worker
545*c8dee2aaSAndroid Build Coastguard Worker options.fPriorFrame = SkCodec::kNoFrame;
546*c8dee2aaSAndroid Build Coastguard Worker result = codec->codec()->getPixels(info, bm2.getPixels(), bm2.rowBytes(),
547*c8dee2aaSAndroid Build Coastguard Worker &options);
548*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, result == SkCodec::kSuccess);
549*c8dee2aaSAndroid Build Coastguard Worker
550*c8dee2aaSAndroid Build Coastguard Worker for (int y = 0; y < info.height(); ++y) {
551*c8dee2aaSAndroid Build Coastguard Worker if (0 != memcmp(bm.getAddr32(0, y), bm2.getAddr32(0, y), info.minRowBytes())) {
552*c8dee2aaSAndroid Build Coastguard Worker ERRORF(r, "pixel mismatch for sample size %i, frame %i resulting in "
553*c8dee2aaSAndroid Build Coastguard Worker "dimensions %i x %i line %i\n",
554*c8dee2aaSAndroid Build Coastguard Worker sampleSize, i, info.width(), info.height(), y);
555*c8dee2aaSAndroid Build Coastguard Worker break;
556*c8dee2aaSAndroid Build Coastguard Worker }
557*c8dee2aaSAndroid Build Coastguard Worker }
558*c8dee2aaSAndroid Build Coastguard Worker }
559*c8dee2aaSAndroid Build Coastguard Worker }
560*c8dee2aaSAndroid Build Coastguard Worker }
561*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(AndroidCodec_animated,r)562*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(AndroidCodec_animated, r) {
563*c8dee2aaSAndroid Build Coastguard Worker test_animated_AndroidCodec(r, "images/required.webp");
564*c8dee2aaSAndroid Build Coastguard Worker }
565*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(AndroidCodec_animated_gif,r)566*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(AndroidCodec_animated_gif, r) {
567*c8dee2aaSAndroid Build Coastguard Worker test_animated_AndroidCodec(r, "images/required.gif");
568*c8dee2aaSAndroid Build Coastguard Worker }
569*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(EncodedOriginToMatrixTest,r)570*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(EncodedOriginToMatrixTest, r) {
571*c8dee2aaSAndroid Build Coastguard Worker // SkAnimCodecPlayer relies on the fact that these matrices are invertible.
572*c8dee2aaSAndroid Build Coastguard Worker for (auto origin : { kTopLeft_SkEncodedOrigin ,
573*c8dee2aaSAndroid Build Coastguard Worker kTopRight_SkEncodedOrigin ,
574*c8dee2aaSAndroid Build Coastguard Worker kBottomRight_SkEncodedOrigin ,
575*c8dee2aaSAndroid Build Coastguard Worker kBottomLeft_SkEncodedOrigin ,
576*c8dee2aaSAndroid Build Coastguard Worker kLeftTop_SkEncodedOrigin ,
577*c8dee2aaSAndroid Build Coastguard Worker kRightTop_SkEncodedOrigin ,
578*c8dee2aaSAndroid Build Coastguard Worker kRightBottom_SkEncodedOrigin ,
579*c8dee2aaSAndroid Build Coastguard Worker kLeftBottom_SkEncodedOrigin }) {
580*c8dee2aaSAndroid Build Coastguard Worker // Arbitrary output dimensions.
581*c8dee2aaSAndroid Build Coastguard Worker auto matrix = SkEncodedOriginToMatrix(origin, 100, 80);
582*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, matrix.invert(nullptr));
583*c8dee2aaSAndroid Build Coastguard Worker }
584*c8dee2aaSAndroid Build Coastguard Worker }
585*c8dee2aaSAndroid Build Coastguard Worker
586*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_ENABLE_SKOTTIE)
587*c8dee2aaSAndroid Build Coastguard Worker
588*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skresources/src/SkAnimCodecPlayer.h"
589*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(AnimCodecPlayer,r)590*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(AnimCodecPlayer, r) {
591*c8dee2aaSAndroid Build Coastguard Worker static constexpr struct {
592*c8dee2aaSAndroid Build Coastguard Worker const char* fFile;
593*c8dee2aaSAndroid Build Coastguard Worker uint32_t fDuration;
594*c8dee2aaSAndroid Build Coastguard Worker SkISize fSize;
595*c8dee2aaSAndroid Build Coastguard Worker } gTests[] = {
596*c8dee2aaSAndroid Build Coastguard Worker { "images/alphabetAnim.gif" , 1300, {100, 100} },
597*c8dee2aaSAndroid Build Coastguard Worker { "images/randPixels.gif" , 0, { 8, 8} },
598*c8dee2aaSAndroid Build Coastguard Worker { "images/randPixels.jpg" , 0, { 8, 8} },
599*c8dee2aaSAndroid Build Coastguard Worker { "images/randPixels.png" , 0, { 8, 8} },
600*c8dee2aaSAndroid Build Coastguard Worker { "images/stoplight.webp" , 2500, { 11, 29} },
601*c8dee2aaSAndroid Build Coastguard Worker { "images/stoplight_h.webp" , 2500, { 29, 11} },
602*c8dee2aaSAndroid Build Coastguard Worker { "images/orientation/1.webp", 0, {100, 80} },
603*c8dee2aaSAndroid Build Coastguard Worker { "images/orientation/2.webp", 0, {100, 80} },
604*c8dee2aaSAndroid Build Coastguard Worker { "images/orientation/3.webp", 0, {100, 80} },
605*c8dee2aaSAndroid Build Coastguard Worker { "images/orientation/4.webp", 0, {100, 80} },
606*c8dee2aaSAndroid Build Coastguard Worker { "images/orientation/5.webp", 0, {100, 80} },
607*c8dee2aaSAndroid Build Coastguard Worker { "images/orientation/6.webp", 0, {100, 80} },
608*c8dee2aaSAndroid Build Coastguard Worker { "images/orientation/7.webp", 0, {100, 80} },
609*c8dee2aaSAndroid Build Coastguard Worker { "images/orientation/8.webp", 0, {100, 80} },
610*c8dee2aaSAndroid Build Coastguard Worker };
611*c8dee2aaSAndroid Build Coastguard Worker
612*c8dee2aaSAndroid Build Coastguard Worker for (const auto& test : gTests) {
613*c8dee2aaSAndroid Build Coastguard Worker auto codec = SkCodec::MakeFromData(GetResourceAsData(test.fFile));
614*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, codec);
615*c8dee2aaSAndroid Build Coastguard Worker
616*c8dee2aaSAndroid Build Coastguard Worker auto player = std::make_unique<SkAnimCodecPlayer>(std::move(codec));
617*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, player->duration() == test.fDuration);
618*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, player->dimensions() == test.fSize);
619*c8dee2aaSAndroid Build Coastguard Worker
620*c8dee2aaSAndroid Build Coastguard Worker auto f0 = player->getFrame();
621*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, f0);
622*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, f0->bounds().size() == test.fSize,
623*c8dee2aaSAndroid Build Coastguard Worker "Mismatched size for initial frame of %s", test.fFile);
624*c8dee2aaSAndroid Build Coastguard Worker
625*c8dee2aaSAndroid Build Coastguard Worker player->seek(500);
626*c8dee2aaSAndroid Build Coastguard Worker auto f1 = player->getFrame();
627*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, f1);
628*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, f1->bounds().size() == test.fSize,
629*c8dee2aaSAndroid Build Coastguard Worker "Mismatched size for frame at 500 ms of %s", test.fFile);
630*c8dee2aaSAndroid Build Coastguard Worker }
631*c8dee2aaSAndroid Build Coastguard Worker }
632*c8dee2aaSAndroid Build Coastguard Worker
633*c8dee2aaSAndroid Build Coastguard Worker #endif
634