AnimatedImageTest.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. /*
  2. * Copyright 2018 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. #include "include/android/SkAnimatedImage.h"
  8. #include "include/codec/SkAndroidCodec.h"
  9. #include "include/codec/SkCodec.h"
  10. #include "include/core/SkBitmap.h"
  11. #include "include/core/SkCanvas.h"
  12. #include "include/core/SkColor.h"
  13. #include "include/core/SkData.h"
  14. #include "include/core/SkImageInfo.h"
  15. #include "include/core/SkPicture.h"
  16. #include "include/core/SkRefCnt.h"
  17. #include "include/core/SkSize.h"
  18. #include "include/core/SkString.h"
  19. #include "include/core/SkTypes.h"
  20. #include "include/core/SkUnPreMultiply.h"
  21. #include "tests/CodecPriv.h"
  22. #include "tests/Test.h"
  23. #include "tools/Resources.h"
  24. #include "tools/ToolUtils.h"
  25. #include <initializer_list>
  26. #include <memory>
  27. #include <utility>
  28. #include <vector>
  29. DEF_TEST(AnimatedImage_scaled, r) {
  30. if (GetResourcePath().isEmpty()) {
  31. return;
  32. }
  33. const char* file = "images/alphabetAnim.gif";
  34. auto data = GetResourceAsData(file);
  35. if (!data) {
  36. ERRORF(r, "Could not get %s", file);
  37. return;
  38. }
  39. auto codec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(data));
  40. if (!codec) {
  41. ERRORF(r, "Could not create codec for %s", file);
  42. return;
  43. }
  44. // Force the drawable follow its special case that requires scaling.
  45. auto info = codec->getInfo();
  46. info = info.makeWH(info.width() - 5, info.height() - 5);
  47. auto rect = info.bounds();
  48. auto image = SkAnimatedImage::Make(std::move(codec), info, rect, nullptr);
  49. if (!image) {
  50. ERRORF(r, "Failed to create animated image for %s", file);
  51. return;
  52. }
  53. // Clear a bitmap to non-transparent and draw to it. pixels that are transparent
  54. // in the image should not replace the original non-transparent color.
  55. SkBitmap bm;
  56. bm.allocPixels(SkImageInfo::MakeN32Premul(info.width(), info.height()));
  57. bm.eraseColor(SK_ColorBLUE);
  58. SkCanvas canvas(bm);
  59. image->draw(&canvas);
  60. for (int i = 0; i < info.width(); ++i)
  61. for (int j = 0; j < info.height(); ++j) {
  62. if (*bm.getAddr32(i, j) == SK_ColorTRANSPARENT) {
  63. ERRORF(r, "Erased color underneath!");
  64. return;
  65. }
  66. }
  67. }
  68. static bool compare_bitmaps(skiatest::Reporter* r,
  69. const char* file,
  70. int expectedFrame,
  71. const SkBitmap& expectedBm,
  72. const SkBitmap& actualBm) {
  73. REPORTER_ASSERT(r, expectedBm.colorType() == actualBm.colorType());
  74. REPORTER_ASSERT(r, expectedBm.dimensions() == actualBm.dimensions());
  75. for (int i = 0; i < actualBm.width(); ++i)
  76. for (int j = 0; j < actualBm.height(); ++j) {
  77. SkColor expected = SkUnPreMultiply::PMColorToColor(*expectedBm.getAddr32(i, j));
  78. SkColor actual = SkUnPreMultiply::PMColorToColor(*actualBm .getAddr32(i, j));
  79. if (expected != actual) {
  80. ERRORF(r, "frame %i of %s does not match at pixel %i, %i!"
  81. " expected %x\tactual: %x",
  82. expectedFrame, file, i, j, expected, actual);
  83. SkString expected_name = SkStringPrintf("expected_%c", '0' + expectedFrame);
  84. SkString actual_name = SkStringPrintf("actual_%c", '0' + expectedFrame);
  85. write_bm(expected_name.c_str(), expectedBm);
  86. write_bm(actual_name.c_str(), actualBm);
  87. return false;
  88. }
  89. }
  90. return true;
  91. }
  92. DEF_TEST(AnimatedImage_copyOnWrite, r) {
  93. if (GetResourcePath().isEmpty()) {
  94. return;
  95. }
  96. for (const char* file : { "images/alphabetAnim.gif",
  97. "images/colorTables.gif",
  98. "images/webp-animated.webp",
  99. "images/required.webp",
  100. }) {
  101. auto data = GetResourceAsData(file);
  102. if (!data) {
  103. ERRORF(r, "Could not get %s", file);
  104. continue;
  105. }
  106. auto codec = SkCodec::MakeFromData(data);
  107. if (!codec) {
  108. ERRORF(r, "Could not create codec for %s", file);
  109. continue;
  110. }
  111. const auto imageInfo = codec->getInfo().makeAlphaType(kPremul_SkAlphaType);
  112. const int frameCount = codec->getFrameCount();
  113. auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
  114. if (!androidCodec) {
  115. ERRORF(r, "Could not create androidCodec for %s", file);
  116. continue;
  117. }
  118. auto animatedImage = SkAnimatedImage::Make(std::move(androidCodec));
  119. if (!animatedImage) {
  120. ERRORF(r, "Could not create animated image for %s", file);
  121. continue;
  122. }
  123. animatedImage->setRepetitionCount(0);
  124. std::vector<SkBitmap> expected(frameCount);
  125. std::vector<sk_sp<SkPicture>> pictures(frameCount);
  126. for (int i = 0; i < frameCount; i++) {
  127. SkBitmap& bm = expected[i];
  128. bm.allocPixels(imageInfo);
  129. bm.eraseColor(SK_ColorTRANSPARENT);
  130. SkCanvas canvas(bm);
  131. pictures[i].reset(animatedImage->newPictureSnapshot());
  132. canvas.drawPicture(pictures[i]);
  133. const auto duration = animatedImage->decodeNextFrame();
  134. // We're attempting to decode i + 1, so decodeNextFrame will return
  135. // kFinished if that is the last frame (or we attempt to decode one
  136. // more).
  137. if (i >= frameCount - 2) {
  138. REPORTER_ASSERT(r, duration == SkAnimatedImage::kFinished);
  139. } else {
  140. REPORTER_ASSERT(r, duration != SkAnimatedImage::kFinished);
  141. }
  142. }
  143. for (int i = 0; i < frameCount; i++) {
  144. SkBitmap test;
  145. test.allocPixels(imageInfo);
  146. test.eraseColor(SK_ColorTRANSPARENT);
  147. SkCanvas canvas(test);
  148. canvas.drawPicture(pictures[i]);
  149. compare_bitmaps(r, file, i, expected[i], test);
  150. }
  151. }
  152. }
  153. DEF_TEST(AnimatedImage, r) {
  154. if (GetResourcePath().isEmpty()) {
  155. return;
  156. }
  157. for (const char* file : { "images/alphabetAnim.gif",
  158. "images/colorTables.gif",
  159. "images/webp-animated.webp",
  160. "images/required.webp",
  161. }) {
  162. auto data = GetResourceAsData(file);
  163. if (!data) {
  164. ERRORF(r, "Could not get %s", file);
  165. continue;
  166. }
  167. auto codec = SkCodec::MakeFromData(data);
  168. if (!codec) {
  169. ERRORF(r, "Could not create codec for %s", file);
  170. continue;
  171. }
  172. const int defaultRepetitionCount = codec->getRepetitionCount();
  173. std::vector<SkCodec::FrameInfo> frameInfos = codec->getFrameInfo();
  174. std::vector<SkBitmap> frames(frameInfos.size());
  175. // Used down below for our test image.
  176. const auto imageInfo = codec->getInfo().makeAlphaType(kPremul_SkAlphaType);
  177. for (size_t i = 0; i < frameInfos.size(); ++i) {
  178. auto info = codec->getInfo().makeAlphaType(frameInfos[i].fAlphaType);
  179. auto& bm = frames[i];
  180. SkCodec::Options options;
  181. options.fFrameIndex = (int) i;
  182. options.fPriorFrame = frameInfos[i].fRequiredFrame;
  183. if (options.fPriorFrame == SkCodec::kNoFrame) {
  184. bm.allocPixels(info);
  185. bm.eraseColor(0);
  186. } else {
  187. const SkBitmap& priorFrame = frames[options.fPriorFrame];
  188. if (!ToolUtils::copy_to(&bm, priorFrame.colorType(), priorFrame)) {
  189. ERRORF(r, "Failed to copy %s frame %i", file, options.fPriorFrame);
  190. options.fPriorFrame = SkCodec::kNoFrame;
  191. }
  192. REPORTER_ASSERT(r, bm.setAlphaType(frameInfos[i].fAlphaType));
  193. }
  194. auto result = codec->getPixels(info, bm.getPixels(), bm.rowBytes(), &options);
  195. if (result != SkCodec::kSuccess) {
  196. ERRORF(r, "error in %s frame %zu: %s", file, i, SkCodec::ResultToString(result));
  197. }
  198. }
  199. auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
  200. if (!androidCodec) {
  201. ERRORF(r, "Could not create androidCodec for %s", file);
  202. continue;
  203. }
  204. auto animatedImage = SkAnimatedImage::Make(std::move(androidCodec));
  205. if (!animatedImage) {
  206. ERRORF(r, "Could not create animated image for %s", file);
  207. continue;
  208. }
  209. REPORTER_ASSERT(r, defaultRepetitionCount == animatedImage->getRepetitionCount());
  210. auto testDraw = [r, &frames, &imageInfo, file](const sk_sp<SkAnimatedImage>& animatedImage,
  211. int expectedFrame) {
  212. SkBitmap test;
  213. test.allocPixels(imageInfo);
  214. test.eraseColor(0);
  215. SkCanvas c(test);
  216. animatedImage->draw(&c);
  217. const SkBitmap& frame = frames[expectedFrame];
  218. return compare_bitmaps(r, file, expectedFrame, frame, test);
  219. };
  220. REPORTER_ASSERT(r, animatedImage->currentFrameDuration() == frameInfos[0].fDuration);
  221. if (!testDraw(animatedImage, 0)) {
  222. ERRORF(r, "Did not start with frame 0");
  223. continue;
  224. }
  225. // Start at an arbitrary time.
  226. bool failed = false;
  227. for (size_t i = 1; i < frameInfos.size(); ++i) {
  228. const int frameTime = animatedImage->decodeNextFrame();
  229. REPORTER_ASSERT(r, frameTime == animatedImage->currentFrameDuration());
  230. if (i == frameInfos.size() - 1 && defaultRepetitionCount == 0) {
  231. REPORTER_ASSERT(r, frameTime == SkAnimatedImage::kFinished);
  232. REPORTER_ASSERT(r, animatedImage->isFinished());
  233. } else {
  234. REPORTER_ASSERT(r, frameTime == frameInfos[i].fDuration);
  235. REPORTER_ASSERT(r, !animatedImage->isFinished());
  236. }
  237. if (!testDraw(animatedImage, i)) {
  238. ERRORF(r, "Did not update to %i properly", i);
  239. failed = true;
  240. break;
  241. }
  242. }
  243. if (failed) {
  244. continue;
  245. }
  246. animatedImage->reset();
  247. REPORTER_ASSERT(r, !animatedImage->isFinished());
  248. if (!testDraw(animatedImage, 0)) {
  249. ERRORF(r, "reset failed");
  250. continue;
  251. }
  252. // Test reset from all the frames.
  253. // j is the frame to call reset on.
  254. for (int j = 0; j < (int) frameInfos.size(); ++j) {
  255. if (failed) {
  256. break;
  257. }
  258. // i is the frame to decode.
  259. for (int i = 0; i <= j; ++i) {
  260. if (i == j) {
  261. animatedImage->reset();
  262. if (!testDraw(animatedImage, 0)) {
  263. ERRORF(r, "reset failed for image %s from frame %i",
  264. file, i);
  265. failed = true;
  266. break;
  267. }
  268. } else if (i != 0) {
  269. animatedImage->decodeNextFrame();
  270. if (!testDraw(animatedImage, i)) {
  271. ERRORF(r, "failed to match frame %i in %s on iteration %i",
  272. i, file, j);
  273. failed = true;
  274. break;
  275. }
  276. }
  277. }
  278. }
  279. if (failed) {
  280. continue;
  281. }
  282. for (int loopCount : { 0, 1, 2, 5 }) {
  283. animatedImage = SkAnimatedImage::Make(SkAndroidCodec::MakeFromCodec(
  284. SkCodec::MakeFromData(data)));
  285. animatedImage->setRepetitionCount(loopCount);
  286. REPORTER_ASSERT(r, animatedImage->getRepetitionCount() == loopCount);
  287. for (int loops = 0; loops <= loopCount; loops++) {
  288. if (failed) {
  289. break;
  290. }
  291. REPORTER_ASSERT(r, !animatedImage->isFinished());
  292. for (size_t i = 1; i <= frameInfos.size(); ++i) {
  293. const int frameTime = animatedImage->decodeNextFrame();
  294. if (frameTime == SkAnimatedImage::kFinished) {
  295. if (loops != loopCount) {
  296. ERRORF(r, "%s animation stopped early: loops: %i\tloopCount: %i",
  297. file, loops, loopCount);
  298. failed = true;
  299. }
  300. if (i != frameInfos.size() - 1) {
  301. ERRORF(r, "%s animation stopped early: i: %i\tsize: %i",
  302. file, i, frameInfos.size());
  303. failed = true;
  304. }
  305. break;
  306. }
  307. }
  308. }
  309. if (!animatedImage->isFinished()) {
  310. ERRORF(r, "%s animation should have finished with specified loop count (%i)",
  311. file, loopCount);
  312. }
  313. }
  314. }
  315. }