aboutsummaryrefslogtreecommitdiffhomepage
path: root/tests/AnimatedImageTest.cpp
diff options
context:
space:
mode:
authorGravatar Leon Scroggins III <scroggo@google.com>2018-05-23 16:15:09 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2018-05-23 20:49:42 +0000
commit4aafb3a8d12015067fae1301c2f5951f398eb25b (patch)
tree99f3f8476851cc141d35cd5ec944866207e1315c /tests/AnimatedImageTest.cpp
parent1530f390087e48bd88981eeee69086ef9a52c243 (diff)
Alternate between two SkBitmaps in SkAnimatedImage
Bug: 78866720 The client in Android calls newPictureSnapshot, which results in copying the mutable SkBitmap into a newly allocated one in each frame. Avoid this by calling SkMakeImageFromRasterBitmap with kNever_SkCopyPixelsMode. Make SkAnimatedImage copy on write, by copying before decoding if the bitmap's pixel ref is not unique. Android's AnimatedImageDrawable's current architecture only decodes one frame in advance, so it will never need to perform the copy on write. This will save one bitmap allocation per GIF frame. Add a test to verify that copy on write works as expected. Change-Id: I87eb6e84089096cd2d618b91fb627fc58677e66a Reviewed-on: https://skia-review.googlesource.com/129841 Reviewed-by: Leon Scroggins <scroggo@google.com> Commit-Queue: Leon Scroggins <scroggo@google.com> Auto-Submit: Leon Scroggins <scroggo@google.com>
Diffstat (limited to 'tests/AnimatedImageTest.cpp')
-rw-r--r--tests/AnimatedImageTest.cpp115
1 files changed, 97 insertions, 18 deletions
diff --git a/tests/AnimatedImageTest.cpp b/tests/AnimatedImageTest.cpp
index 6c7b0e5a55..13ea80804c 100644
--- a/tests/AnimatedImageTest.cpp
+++ b/tests/AnimatedImageTest.cpp
@@ -72,6 +72,102 @@ DEF_TEST(AnimatedImage_scaled, r) {
}
}
+static bool compare_bitmaps(skiatest::Reporter* r,
+ const char* file,
+ int expectedFrame,
+ const SkBitmap& expectedBm,
+ const SkBitmap& actualBm) {
+ REPORTER_ASSERT(r, expectedBm.colorType() == actualBm.colorType());
+ REPORTER_ASSERT(r, expectedBm.dimensions() == actualBm.dimensions());
+ for (int i = 0; i < actualBm.width(); ++i)
+ for (int j = 0; j < actualBm.height(); ++j) {
+ SkColor expected = SkUnPreMultiply::PMColorToColor(*expectedBm.getAddr32(i, j));
+ SkColor actual = SkUnPreMultiply::PMColorToColor(*actualBm .getAddr32(i, j));
+ if (expected != actual) {
+ ERRORF(r, "frame %i of %s does not match at pixel %i, %i!"
+ " expected %x\tactual: %x",
+ expectedFrame, file, i, j, expected, actual);
+ SkString expected_name = SkStringPrintf("expected_%c", '0' + expectedFrame);
+ SkString actual_name = SkStringPrintf("actual_%c", '0' + expectedFrame);
+ write_bm(expected_name.c_str(), expectedBm);
+ write_bm(actual_name.c_str(), actualBm);
+ return false;
+ }
+ }
+ return true;
+}
+
+DEF_TEST(AnimatedImage_copyOnWrite, r) {
+ if (GetResourcePath().isEmpty()) {
+ return;
+ }
+ for (const char* file : { "images/alphabetAnim.gif",
+ "images/colorTables.gif",
+ "images/webp-animated.webp",
+ "images/required.webp",
+ }) {
+ auto data = GetResourceAsData(file);
+ if (!data) {
+ ERRORF(r, "Could not get %s", file);
+ continue;
+ }
+
+ auto codec = SkCodec::MakeFromData(data);
+ if (!codec) {
+ ERRORF(r, "Could not create codec for %s", file);
+ continue;
+ }
+
+ const auto imageInfo = codec->getInfo().makeAlphaType(kPremul_SkAlphaType);
+ const int frameCount = codec->getFrameCount();
+ auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
+ if (!androidCodec) {
+ ERRORF(r, "Could not create androidCodec for %s", file);
+ continue;
+ }
+
+ auto animatedImage = SkAnimatedImage::Make(std::move(androidCodec));
+ if (!animatedImage) {
+ ERRORF(r, "Could not create animated image for %s", file);
+ continue;
+ }
+ animatedImage->setRepetitionCount(0);
+
+ std::vector<SkBitmap> expected(frameCount);
+ std::vector<sk_sp<SkPicture>> pictures(frameCount);
+ for (int i = 0; i < frameCount; i++) {
+ SkBitmap& bm = expected[i];
+ bm.allocPixels(imageInfo);
+ bm.eraseColor(SK_ColorTRANSPARENT);
+ SkCanvas canvas(bm);
+
+ pictures[i].reset(animatedImage->newPictureSnapshot());
+ canvas.drawPicture(pictures[i]);
+
+ const auto duration = animatedImage->decodeNextFrame();
+ // We're attempting to decode i + 1, so decodeNextFrame will return
+ // kFinished if that is the last frame (or we attempt to decode one
+ // more).
+ if (i >= frameCount - 2) {
+ REPORTER_ASSERT(r, duration == SkAnimatedImage::kFinished);
+ } else {
+ REPORTER_ASSERT(r, duration != SkAnimatedImage::kFinished);
+ }
+ }
+
+ for (int i = 0; i < frameCount; i++) {
+ SkBitmap test;
+ test.allocPixels(imageInfo);
+ test.eraseColor(SK_ColorTRANSPARENT);
+ SkCanvas canvas(test);
+
+ canvas.drawPicture(pictures[i]);
+
+ compare_bitmaps(r, file, i, expected[i], test);
+ }
+ }
+}
+
DEF_TEST(AnimatedImage, r) {
if (GetResourcePath().isEmpty()) {
return;
@@ -147,24 +243,7 @@ DEF_TEST(AnimatedImage, r) {
animatedImage->draw(&c);
const SkBitmap& frame = frames[expectedFrame];
- REPORTER_ASSERT(r, frame.colorType() == test.colorType());
- REPORTER_ASSERT(r, frame.dimensions() == test.dimensions());
- for (int i = 0; i < test.width(); ++i)
- for (int j = 0; j < test.height(); ++j) {
- SkColor expected = SkUnPreMultiply::PMColorToColor(*frame.getAddr32(i, j));
- SkColor actual = SkUnPreMultiply::PMColorToColor(*test .getAddr32(i, j));
- if (expected != actual) {
- ERRORF(r, "frame %i of %s does not match at pixel %i, %i!"
- " expected %x\tactual: %x",
- expectedFrame, file, i, j, expected, actual);
- SkString expected_name = SkStringPrintf("expected_%c", '0' + expectedFrame);
- SkString actual_name = SkStringPrintf("actual_%c", '0' + expectedFrame);
- write_bm(expected_name.c_str(), frame);
- write_bm(actual_name.c_str(), test);
- return false;
- }
- }
- return true;
+ return compare_bitmaps(r, file, expectedFrame, frame, test);
};
REPORTER_ASSERT(r, animatedImage->currentFrameDuration() == frameInfos[0].fDuration);