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