CodecPartialTest.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  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. #include "include/codec/SkCodec.h"
  8. #include "include/core/SkBitmap.h"
  9. #include "include/core/SkData.h"
  10. #include "include/core/SkImageInfo.h"
  11. #include "include/core/SkRefCnt.h"
  12. #include "include/core/SkStream.h"
  13. #include "include/core/SkString.h"
  14. #include "include/core/SkTypes.h"
  15. #include "src/core/SkMakeUnique.h"
  16. #include "tests/CodecPriv.h"
  17. #include "tests/FakeStreams.h"
  18. #include "tests/Test.h"
  19. #include "tools/Resources.h"
  20. #include <cstring>
  21. #include <initializer_list>
  22. #include <memory>
  23. #include <utility>
  24. #include <vector>
  25. static SkImageInfo standardize_info(SkCodec* codec) {
  26. SkImageInfo defaultInfo = codec->getInfo();
  27. // Note: This drops the SkColorSpace, allowing the equality check between two
  28. // different codecs created from the same file to have the same SkImageInfo.
  29. return SkImageInfo::MakeN32Premul(defaultInfo.width(), defaultInfo.height());
  30. }
  31. static bool create_truth(sk_sp<SkData> data, SkBitmap* dst) {
  32. std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(std::move(data)));
  33. if (!codec) {
  34. return false;
  35. }
  36. const SkImageInfo info = standardize_info(codec.get());
  37. dst->allocPixels(info);
  38. return SkCodec::kSuccess == codec->getPixels(info, dst->getPixels(), dst->rowBytes());
  39. }
  40. static bool compare_bitmaps(skiatest::Reporter* r, const SkBitmap& bm1, const SkBitmap& bm2) {
  41. const SkImageInfo& info = bm1.info();
  42. if (info != bm2.info()) {
  43. ERRORF(r, "Bitmaps have different image infos!");
  44. return false;
  45. }
  46. const size_t rowBytes = info.minRowBytes();
  47. for (int i = 0; i < info.height(); i++) {
  48. if (memcmp(bm1.getAddr(0, i), bm2.getAddr(0, i), rowBytes)) {
  49. ERRORF(r, "Bitmaps have different pixels, starting on line %i!", i);
  50. return false;
  51. }
  52. }
  53. return true;
  54. }
  55. static void test_partial(skiatest::Reporter* r, const char* name, const sk_sp<SkData>& file,
  56. size_t minBytes, size_t increment) {
  57. SkBitmap truth;
  58. if (!create_truth(file, &truth)) {
  59. ERRORF(r, "Failed to decode %s\n", name);
  60. return;
  61. }
  62. // Now decode part of the file
  63. HaltingStream* stream = new HaltingStream(file, minBytes);
  64. // Note that we cheat and hold on to a pointer to stream, though it is owned by
  65. // partialCodec.
  66. auto partialCodec = SkCodec::MakeFromStream(std::unique_ptr<SkStream>(stream));
  67. if (!partialCodec) {
  68. ERRORF(r, "Failed to create codec for %s with %zu bytes", name, minBytes);
  69. return;
  70. }
  71. const SkImageInfo info = standardize_info(partialCodec.get());
  72. SkASSERT(info == truth.info());
  73. SkBitmap incremental;
  74. incremental.allocPixels(info);
  75. while (true) {
  76. const SkCodec::Result startResult = partialCodec->startIncrementalDecode(info,
  77. incremental.getPixels(), incremental.rowBytes());
  78. if (startResult == SkCodec::kSuccess) {
  79. break;
  80. }
  81. if (stream->isAllDataReceived()) {
  82. ERRORF(r, "Failed to start incremental decode\n");
  83. return;
  84. }
  85. stream->addNewData(increment);
  86. }
  87. while (true) {
  88. // This imitates how Chromium calls getFrameCount before resuming a decode.
  89. partialCodec->getFrameCount();
  90. const SkCodec::Result result = partialCodec->incrementalDecode();
  91. if (result == SkCodec::kSuccess) {
  92. break;
  93. }
  94. REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput);
  95. if (stream->isAllDataReceived()) {
  96. ERRORF(r, "Failed to completely decode %s", name);
  97. return;
  98. }
  99. stream->addNewData(increment);
  100. }
  101. // compare to original
  102. compare_bitmaps(r, truth, incremental);
  103. }
  104. static void test_partial(skiatest::Reporter* r, const char* name, size_t minBytes = 0) {
  105. sk_sp<SkData> file = GetResourceAsData(name);
  106. if (!file) {
  107. SkDebugf("missing resource %s\n", name);
  108. return;
  109. }
  110. // This size is arbitrary, but deliberately different from the buffer size used by SkPngCodec.
  111. constexpr size_t kIncrement = 1000;
  112. test_partial(r, name, file, SkTMax(file->size() / 2, minBytes), kIncrement);
  113. }
  114. DEF_TEST(Codec_partial, r) {
  115. #if 0
  116. // FIXME (scroggo): SkPngCodec needs to use SkStreamBuffer in order to
  117. // support incremental decoding.
  118. test_partial(r, "images/plane.png");
  119. test_partial(r, "images/plane_interlaced.png");
  120. test_partial(r, "images/yellow_rose.png");
  121. test_partial(r, "images/index8.png");
  122. test_partial(r, "images/color_wheel.png");
  123. test_partial(r, "images/mandrill_256.png");
  124. test_partial(r, "images/mandrill_32.png");
  125. test_partial(r, "images/arrow.png");
  126. test_partial(r, "images/randPixels.png");
  127. test_partial(r, "images/baby_tux.png");
  128. #endif
  129. test_partial(r, "images/box.gif");
  130. test_partial(r, "images/randPixels.gif", 215);
  131. test_partial(r, "images/color_wheel.gif");
  132. }
  133. DEF_TEST(Codec_partialWuffs, r) {
  134. const char* path = "images/alphabetAnim.gif";
  135. auto file = GetResourceAsData(path);
  136. if (!file) {
  137. ERRORF(r, "missing %s", path);
  138. } else {
  139. // This is the end of the first frame. SkCodec will treat this as a
  140. // single frame gif.
  141. file = SkData::MakeSubset(file.get(), 0, 153);
  142. // Start with 100 to get a partial decode, then add the rest of the
  143. // first frame to decode a full image.
  144. test_partial(r, path, file, 100, 53);
  145. }
  146. }
  147. // Verify that when decoding an animated gif byte by byte we report the correct
  148. // fRequiredFrame as soon as getFrameInfo reports the frame.
  149. DEF_TEST(Codec_requiredFrame, r) {
  150. auto path = "images/colorTables.gif";
  151. sk_sp<SkData> file = GetResourceAsData(path);
  152. if (!file) {
  153. return;
  154. }
  155. std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(file));
  156. if (!codec) {
  157. ERRORF(r, "Failed to create codec from %s", path);
  158. return;
  159. }
  160. auto frameInfo = codec->getFrameInfo();
  161. if (frameInfo.size() <= 1) {
  162. ERRORF(r, "Test is uninteresting with 0 or 1 frames");
  163. return;
  164. }
  165. HaltingStream* stream(nullptr);
  166. std::unique_ptr<SkCodec> partialCodec(nullptr);
  167. for (size_t i = 0; !partialCodec; i++) {
  168. if (file->size() == i) {
  169. ERRORF(r, "Should have created a partial codec for %s", path);
  170. return;
  171. }
  172. stream = new HaltingStream(file, i);
  173. partialCodec = SkCodec::MakeFromStream(std::unique_ptr<SkStream>(stream));
  174. }
  175. std::vector<SkCodec::FrameInfo> partialInfo;
  176. size_t frameToCompare = 0;
  177. while (true) {
  178. partialInfo = partialCodec->getFrameInfo();
  179. for (; frameToCompare < partialInfo.size(); frameToCompare++) {
  180. REPORTER_ASSERT(r, partialInfo[frameToCompare].fRequiredFrame
  181. == frameInfo[frameToCompare].fRequiredFrame);
  182. }
  183. if (frameToCompare == frameInfo.size()) {
  184. break;
  185. }
  186. if (stream->getLength() == file->size()) {
  187. ERRORF(r, "Should have found all frames for %s", path);
  188. return;
  189. }
  190. stream->addNewData(1);
  191. }
  192. }
  193. DEF_TEST(Codec_partialAnim, r) {
  194. auto path = "images/test640x479.gif";
  195. sk_sp<SkData> file = GetResourceAsData(path);
  196. if (!file) {
  197. return;
  198. }
  199. // This stream will be owned by fullCodec, but we hang on to the pointer
  200. // to determine frame offsets.
  201. std::unique_ptr<SkCodec> fullCodec(SkCodec::MakeFromStream(skstd::make_unique<SkMemoryStream>(file)));
  202. const auto info = standardize_info(fullCodec.get());
  203. // frameByteCounts stores the number of bytes to decode a particular frame.
  204. // - [0] is the number of bytes for the header
  205. // - frames[i] requires frameByteCounts[i+1] bytes to decode
  206. const std::vector<size_t> frameByteCounts = { 455, 69350, 1344, 1346, 1327 };
  207. std::vector<SkBitmap> frames;
  208. for (size_t i = 0; true; i++) {
  209. SkBitmap frame;
  210. frame.allocPixels(info);
  211. SkCodec::Options opts;
  212. opts.fFrameIndex = i;
  213. const SkCodec::Result result = fullCodec->getPixels(info, frame.getPixels(),
  214. frame.rowBytes(), &opts);
  215. if (result == SkCodec::kIncompleteInput || result == SkCodec::kInvalidInput) {
  216. // We need to distinguish between a partial frame and no more frames.
  217. // getFrameInfo lets us do this, since it tells the number of frames
  218. // not considering whether they are complete.
  219. // FIXME: Should we use a different Result?
  220. if (fullCodec->getFrameInfo().size() > i) {
  221. // This is a partial frame.
  222. frames.push_back(frame);
  223. }
  224. break;
  225. }
  226. if (result != SkCodec::kSuccess) {
  227. ERRORF(r, "Failed to decode frame %i from %s", i, path);
  228. return;
  229. }
  230. frames.push_back(frame);
  231. }
  232. // Now decode frames partially, then completely, and compare to the original.
  233. HaltingStream* haltingStream = new HaltingStream(file, frameByteCounts[0]);
  234. std::unique_ptr<SkCodec> partialCodec(SkCodec::MakeFromStream(
  235. std::unique_ptr<SkStream>(haltingStream)));
  236. if (!partialCodec) {
  237. ERRORF(r, "Failed to create a partial codec from %s with %i bytes out of %i",
  238. path, frameByteCounts[0], file->size());
  239. return;
  240. }
  241. SkASSERT(frameByteCounts.size() > frames.size());
  242. for (size_t i = 0; i < frames.size(); i++) {
  243. const size_t fullFrameBytes = frameByteCounts[i + 1];
  244. const size_t firstHalf = fullFrameBytes / 2;
  245. const size_t secondHalf = fullFrameBytes - firstHalf;
  246. haltingStream->addNewData(firstHalf);
  247. auto frameInfo = partialCodec->getFrameInfo();
  248. REPORTER_ASSERT(r, frameInfo.size() == i + 1);
  249. REPORTER_ASSERT(r, !frameInfo[i].fFullyReceived);
  250. SkBitmap frame;
  251. frame.allocPixels(info);
  252. SkCodec::Options opts;
  253. opts.fFrameIndex = i;
  254. SkCodec::Result result = partialCodec->startIncrementalDecode(info,
  255. frame.getPixels(), frame.rowBytes(), &opts);
  256. if (result != SkCodec::kSuccess) {
  257. ERRORF(r, "Failed to start incremental decode for %s on frame %i",
  258. path, i);
  259. return;
  260. }
  261. result = partialCodec->incrementalDecode();
  262. REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result);
  263. haltingStream->addNewData(secondHalf);
  264. result = partialCodec->incrementalDecode();
  265. REPORTER_ASSERT(r, SkCodec::kSuccess == result);
  266. frameInfo = partialCodec->getFrameInfo();
  267. REPORTER_ASSERT(r, frameInfo.size() == i + 1);
  268. REPORTER_ASSERT(r, frameInfo[i].fFullyReceived);
  269. if (!compare_bitmaps(r, frames[i], frame)) {
  270. ERRORF(r, "\tfailure was on frame %i", i);
  271. SkString name = SkStringPrintf("expected_%i", i);
  272. write_bm(name.c_str(), frames[i]);
  273. name = SkStringPrintf("actual_%i", i);
  274. write_bm(name.c_str(), frame);
  275. }
  276. }
  277. }
  278. // Test that calling getPixels when an incremental decode has been
  279. // started (but not finished) makes the next call to incrementalDecode
  280. // require a call to startIncrementalDecode.
  281. static void test_interleaved(skiatest::Reporter* r, const char* name) {
  282. sk_sp<SkData> file = GetResourceAsData(name);
  283. if (!file) {
  284. return;
  285. }
  286. const size_t halfSize = file->size() / 2;
  287. std::unique_ptr<SkCodec> partialCodec(SkCodec::MakeFromStream(
  288. skstd::make_unique<HaltingStream>(std::move(file), halfSize)));
  289. if (!partialCodec) {
  290. ERRORF(r, "Failed to create codec for %s", name);
  291. return;
  292. }
  293. const SkImageInfo info = standardize_info(partialCodec.get());
  294. SkBitmap incremental;
  295. incremental.allocPixels(info);
  296. const SkCodec::Result startResult = partialCodec->startIncrementalDecode(info,
  297. incremental.getPixels(), incremental.rowBytes());
  298. if (startResult != SkCodec::kSuccess) {
  299. ERRORF(r, "Failed to start incremental decode\n");
  300. return;
  301. }
  302. SkCodec::Result result = partialCodec->incrementalDecode();
  303. REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput);
  304. SkBitmap full;
  305. full.allocPixels(info);
  306. result = partialCodec->getPixels(info, full.getPixels(), full.rowBytes());
  307. REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput);
  308. // Now incremental decode will fail
  309. result = partialCodec->incrementalDecode();
  310. REPORTER_ASSERT(r, result == SkCodec::kInvalidParameters);
  311. }
  312. DEF_TEST(Codec_rewind, r) {
  313. test_interleaved(r, "images/plane.png");
  314. test_interleaved(r, "images/plane_interlaced.png");
  315. test_interleaved(r, "images/box.gif");
  316. }
  317. // Modified version of the giflib logo, from
  318. // http://giflib.sourceforge.net/whatsinagif/bits_and_bytes.html
  319. // The global color map has been replaced with a local color map.
  320. static unsigned char gNoGlobalColorMap[] = {
  321. // Header
  322. 0x47, 0x49, 0x46, 0x38, 0x39, 0x61,
  323. // Logical screen descriptor
  324. 0x0A, 0x00, 0x0A, 0x00, 0x11, 0x00, 0x00,
  325. // Image descriptor
  326. 0x2C, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x81,
  327. // Local color table
  328. 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
  329. // Image data
  330. 0x02, 0x16, 0x8C, 0x2D, 0x99, 0x87, 0x2A, 0x1C, 0xDC, 0x33, 0xA0, 0x02, 0x75,
  331. 0xEC, 0x95, 0xFA, 0xA8, 0xDE, 0x60, 0x8C, 0x04, 0x91, 0x4C, 0x01, 0x00,
  332. // Trailer
  333. 0x3B,
  334. };
  335. // Test that a gif file truncated before its local color map behaves as expected.
  336. DEF_TEST(Codec_GifPreMap, r) {
  337. sk_sp<SkData> data = SkData::MakeWithoutCopy(gNoGlobalColorMap, sizeof(gNoGlobalColorMap));
  338. std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(data));
  339. if (!codec) {
  340. ERRORF(r, "failed to create codec");
  341. return;
  342. }
  343. SkBitmap truth;
  344. auto info = standardize_info(codec.get());
  345. truth.allocPixels(info);
  346. auto result = codec->getPixels(info, truth.getPixels(), truth.rowBytes());
  347. REPORTER_ASSERT(r, result == SkCodec::kSuccess);
  348. // Truncate to 23 bytes, just before the color map. This should fail to decode.
  349. //
  350. // See also Codec_GifTruncated2 in GifTest.cpp for this magic 23.
  351. codec = SkCodec::MakeFromData(SkData::MakeWithoutCopy(gNoGlobalColorMap, 23));
  352. REPORTER_ASSERT(r, codec);
  353. if (codec) {
  354. SkBitmap bm;
  355. bm.allocPixels(info);
  356. result = codec->getPixels(info, bm.getPixels(), bm.rowBytes());
  357. // See the comments in Codec_GifTruncated2.
  358. #ifdef SK_HAS_WUFFS_LIBRARY
  359. REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput);
  360. #else
  361. REPORTER_ASSERT(r, result == SkCodec::kInvalidInput);
  362. #endif
  363. }
  364. // Again, truncate to 23 bytes, this time for an incremental decode. We
  365. // cannot start an incremental decode until we have more data. If we did,
  366. // we would be using the wrong color table.
  367. HaltingStream* stream = new HaltingStream(data, 23);
  368. codec = SkCodec::MakeFromStream(std::unique_ptr<SkStream>(stream));
  369. REPORTER_ASSERT(r, codec);
  370. if (codec) {
  371. SkBitmap bm;
  372. bm.allocPixels(info);
  373. result = codec->startIncrementalDecode(info, bm.getPixels(), bm.rowBytes());
  374. // See the comments in Codec_GifTruncated2.
  375. #ifdef SK_HAS_WUFFS_LIBRARY
  376. REPORTER_ASSERT(r, result == SkCodec::kSuccess);
  377. // Note that this is incrementalDecode, not startIncrementalDecode.
  378. result = codec->incrementalDecode();
  379. REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput);
  380. stream->addNewData(data->size());
  381. #else
  382. REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput);
  383. // Note that this is startIncrementalDecode, not incrementalDecode.
  384. stream->addNewData(data->size());
  385. result = codec->startIncrementalDecode(info, bm.getPixels(), bm.rowBytes());
  386. REPORTER_ASSERT(r, result == SkCodec::kSuccess);
  387. #endif
  388. result = codec->incrementalDecode();
  389. REPORTER_ASSERT(r, result == SkCodec::kSuccess);
  390. compare_bitmaps(r, truth, bm);
  391. }
  392. }
  393. DEF_TEST(Codec_emptyIDAT, r) {
  394. const char* name = "images/baby_tux.png";
  395. sk_sp<SkData> file = GetResourceAsData(name);
  396. if (!file) {
  397. return;
  398. }
  399. // Truncate to the beginning of the IDAT, immediately after the IDAT tag.
  400. file = SkData::MakeSubset(file.get(), 0, 80);
  401. std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(std::move(file)));
  402. if (!codec) {
  403. ERRORF(r, "Failed to create a codec for %s", name);
  404. return;
  405. }
  406. SkBitmap bm;
  407. const auto info = standardize_info(codec.get());
  408. bm.allocPixels(info);
  409. const auto result = codec->getPixels(info, bm.getPixels(), bm.rowBytes());
  410. REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result);
  411. }
  412. DEF_TEST(Codec_incomplete, r) {
  413. for (const char* name : { "images/baby_tux.png",
  414. "images/baby_tux.webp",
  415. "images/CMYK.jpg",
  416. "images/color_wheel.gif",
  417. "images/google_chrome.ico",
  418. "images/rle.bmp",
  419. "images/mandrill.wbmp",
  420. }) {
  421. sk_sp<SkData> file = GetResourceAsData(name);
  422. if (!file) {
  423. continue;
  424. }
  425. for (size_t len = 14; len <= file->size(); len += 5) {
  426. SkCodec::Result result;
  427. std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(
  428. skstd::make_unique<SkMemoryStream>(file->data(), len), &result));
  429. if (codec) {
  430. if (result != SkCodec::kSuccess) {
  431. ERRORF(r, "Created an SkCodec for %s with %lu bytes, but "
  432. "reported an error %i", name, len, result);
  433. }
  434. break;
  435. }
  436. if (SkCodec::kIncompleteInput != result) {
  437. ERRORF(r, "Reported error %i for %s with %lu bytes",
  438. result, name, len);
  439. break;
  440. }
  441. }
  442. }
  443. }