EncodeTest.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. /*
  2. * Copyright 2017 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 "tests/Test.h"
  8. #include "tools/Resources.h"
  9. #include "include/core/SkBitmap.h"
  10. #include "include/core/SkColorPriv.h"
  11. #include "include/core/SkEncodedImageFormat.h"
  12. #include "include/core/SkImage.h"
  13. #include "include/core/SkStream.h"
  14. #include "include/core/SkSurface.h"
  15. #include "include/encode/SkJpegEncoder.h"
  16. #include "include/encode/SkPngEncoder.h"
  17. #include "include/encode/SkWebpEncoder.h"
  18. #include "png.h"
  19. #include <algorithm>
  20. #include <string>
  21. #include <vector>
  22. // FIXME: Update the Google3 build's dependencies so it can run this test.
  23. #ifndef SK_BUILD_FOR_GOOGLE3
  24. #include "webp/decode.h"
  25. #endif
  26. static bool encode(SkEncodedImageFormat format, SkWStream* dst, const SkPixmap& src) {
  27. switch (format) {
  28. case SkEncodedImageFormat::kJPEG:
  29. return SkJpegEncoder::Encode(dst, src, SkJpegEncoder::Options());
  30. case SkEncodedImageFormat::kPNG:
  31. return SkPngEncoder::Encode(dst, src, SkPngEncoder::Options());
  32. default:
  33. return false;
  34. }
  35. }
  36. static std::unique_ptr<SkEncoder> make(SkEncodedImageFormat format, SkWStream* dst,
  37. const SkPixmap& src) {
  38. switch (format) {
  39. case SkEncodedImageFormat::kJPEG:
  40. return SkJpegEncoder::Make(dst, src, SkJpegEncoder::Options());
  41. case SkEncodedImageFormat::kPNG:
  42. return SkPngEncoder::Make(dst, src, SkPngEncoder::Options());
  43. default:
  44. return nullptr;
  45. }
  46. }
  47. static void test_encode(skiatest::Reporter* r, SkEncodedImageFormat format) {
  48. SkBitmap bitmap;
  49. bool success = GetResourceAsBitmap("images/mandrill_128.png", &bitmap);
  50. if (!success) {
  51. return;
  52. }
  53. SkPixmap src;
  54. success = bitmap.peekPixels(&src);
  55. REPORTER_ASSERT(r, success);
  56. if (!success) {
  57. return;
  58. }
  59. SkDynamicMemoryWStream dst0, dst1, dst2, dst3;
  60. success = encode(format, &dst0, src);
  61. REPORTER_ASSERT(r, success);
  62. auto encoder1 = make(format, &dst1, src);
  63. for (int i = 0; i < src.height(); i++) {
  64. success = encoder1->encodeRows(1);
  65. REPORTER_ASSERT(r, success);
  66. }
  67. auto encoder2 = make(format, &dst2, src);
  68. for (int i = 0; i < src.height(); i+=3) {
  69. success = encoder2->encodeRows(3);
  70. REPORTER_ASSERT(r, success);
  71. }
  72. auto encoder3 = make(format, &dst3, src);
  73. success = encoder3->encodeRows(200);
  74. REPORTER_ASSERT(r, success);
  75. sk_sp<SkData> data0 = dst0.detachAsData();
  76. sk_sp<SkData> data1 = dst1.detachAsData();
  77. sk_sp<SkData> data2 = dst2.detachAsData();
  78. sk_sp<SkData> data3 = dst3.detachAsData();
  79. REPORTER_ASSERT(r, data0->equals(data1.get()));
  80. REPORTER_ASSERT(r, data0->equals(data2.get()));
  81. REPORTER_ASSERT(r, data0->equals(data3.get()));
  82. }
  83. DEF_TEST(Encode, r) {
  84. test_encode(r, SkEncodedImageFormat::kJPEG);
  85. test_encode(r, SkEncodedImageFormat::kPNG);
  86. }
  87. static inline bool almost_equals(SkPMColor a, SkPMColor b, int tolerance) {
  88. if (SkTAbs((int)SkGetPackedR32(a) - (int)SkGetPackedR32(b)) > tolerance) {
  89. return false;
  90. }
  91. if (SkTAbs((int)SkGetPackedG32(a) - (int)SkGetPackedG32(b)) > tolerance) {
  92. return false;
  93. }
  94. if (SkTAbs((int)SkGetPackedB32(a) - (int)SkGetPackedB32(b)) > tolerance) {
  95. return false;
  96. }
  97. if (SkTAbs((int)SkGetPackedA32(a) - (int)SkGetPackedA32(b)) > tolerance) {
  98. return false;
  99. }
  100. return true;
  101. }
  102. static inline bool almost_equals(const SkBitmap& a, const SkBitmap& b, int tolerance) {
  103. if (a.info() != b.info()) {
  104. return false;
  105. }
  106. SkASSERT(kN32_SkColorType == a.colorType());
  107. for (int y = 0; y < a.height(); y++) {
  108. for (int x = 0; x < a.width(); x++) {
  109. if (!almost_equals(*a.getAddr32(x, y), *b.getAddr32(x, y), tolerance)) {
  110. return false;
  111. }
  112. }
  113. }
  114. return true;
  115. }
  116. DEF_TEST(Encode_JPG, r) {
  117. auto image = GetResourceAsImage("images/mandrill_128.png");
  118. if (!image) {
  119. return;
  120. }
  121. for (auto ct : { kRGBA_8888_SkColorType,
  122. kBGRA_8888_SkColorType,
  123. kRGB_565_SkColorType,
  124. kARGB_4444_SkColorType,
  125. kGray_8_SkColorType,
  126. kRGBA_F16_SkColorType }) {
  127. for (auto at : { kPremul_SkAlphaType, kUnpremul_SkAlphaType, kOpaque_SkAlphaType }) {
  128. auto info = SkImageInfo::Make(image->width(), image->height(), ct, at);
  129. auto surface = SkSurface::MakeRaster(info);
  130. auto canvas = surface->getCanvas();
  131. canvas->drawImage(image, 0, 0);
  132. SkBitmap bm;
  133. bm.allocPixels(info);
  134. if (!surface->makeImageSnapshot()->readPixels(bm.pixmap(), 0, 0)) {
  135. ERRORF(r, "failed to readPixels! ct: %i\tat: %i\n", ct, at);
  136. continue;
  137. }
  138. for (auto alphaOption : { SkJpegEncoder::AlphaOption::kIgnore,
  139. SkJpegEncoder::AlphaOption::kBlendOnBlack }) {
  140. SkJpegEncoder::Options opts;
  141. opts.fAlphaOption = alphaOption;
  142. SkNullWStream dummy;
  143. if (!SkJpegEncoder::Encode(&dummy, bm.pixmap(), opts)) {
  144. REPORTER_ASSERT(r, ct == kARGB_4444_SkColorType
  145. && alphaOption == SkJpegEncoder::AlphaOption::kBlendOnBlack);
  146. }
  147. }
  148. }
  149. }
  150. }
  151. DEF_TEST(Encode_JpegDownsample, r) {
  152. SkBitmap bitmap;
  153. bool success = GetResourceAsBitmap("images/mandrill_128.png", &bitmap);
  154. if (!success) {
  155. return;
  156. }
  157. SkPixmap src;
  158. success = bitmap.peekPixels(&src);
  159. REPORTER_ASSERT(r, success);
  160. if (!success) {
  161. return;
  162. }
  163. SkDynamicMemoryWStream dst0, dst1, dst2;
  164. SkJpegEncoder::Options options;
  165. success = SkJpegEncoder::Encode(&dst0, src, options);
  166. REPORTER_ASSERT(r, success);
  167. options.fDownsample = SkJpegEncoder::Downsample::k422;
  168. success = SkJpegEncoder::Encode(&dst1, src, options);
  169. REPORTER_ASSERT(r, success);
  170. options.fDownsample = SkJpegEncoder::Downsample::k444;
  171. success = SkJpegEncoder::Encode(&dst2, src, options);
  172. REPORTER_ASSERT(r, success);
  173. sk_sp<SkData> data0 = dst0.detachAsData();
  174. sk_sp<SkData> data1 = dst1.detachAsData();
  175. sk_sp<SkData> data2 = dst2.detachAsData();
  176. REPORTER_ASSERT(r, data0->size() < data1->size());
  177. REPORTER_ASSERT(r, data1->size() < data2->size());
  178. SkBitmap bm0, bm1, bm2;
  179. SkImage::MakeFromEncoded(data0)->asLegacyBitmap(&bm0);
  180. SkImage::MakeFromEncoded(data1)->asLegacyBitmap(&bm1);
  181. SkImage::MakeFromEncoded(data2)->asLegacyBitmap(&bm2);
  182. REPORTER_ASSERT(r, almost_equals(bm0, bm1, 60));
  183. REPORTER_ASSERT(r, almost_equals(bm1, bm2, 60));
  184. }
  185. static inline void pushComment(
  186. std::vector<std::string>& comments, const char* keyword, const char* text) {
  187. comments.push_back(keyword);
  188. comments.push_back(text);
  189. }
  190. static void testPngComments(const SkPixmap& src, SkPngEncoder::Options& options,
  191. skiatest::Reporter* r) {
  192. std::vector<std::string> commentStrings;
  193. pushComment(commentStrings, "key", "text");
  194. pushComment(commentStrings, "test", "something");
  195. pushComment(commentStrings, "have some", "spaces in both");
  196. std::string longKey(PNG_KEYWORD_MAX_LENGTH, 'x');
  197. #ifdef SK_DEBUG
  198. commentStrings.push_back(longKey);
  199. #else
  200. // We call SkDEBUGFAILF it the key is too long so we'll only test this in release mode.
  201. commentStrings.push_back(longKey + "x");
  202. #endif
  203. commentStrings.push_back("");
  204. std::vector<const char*> commentPointers;
  205. std::vector<size_t> commentSizes;
  206. for(auto& str : commentStrings) {
  207. commentPointers.push_back(str.c_str());
  208. commentSizes.push_back(str.length() + 1);
  209. }
  210. options.fComments = SkDataTable::MakeCopyArrays((void const *const *)commentPointers.data(),
  211. commentSizes.data(), commentStrings.size());
  212. SkDynamicMemoryWStream dst;
  213. bool success = SkPngEncoder::Encode(&dst, src, options);
  214. REPORTER_ASSERT(r, success);
  215. std::vector<char> output(dst.bytesWritten());
  216. dst.copyTo(output.data());
  217. // Each chunk is of the form length (4 bytes), chunk type (tEXt), data,
  218. // checksum (4 bytes). Make sure we find all of them in the encoded
  219. // results.
  220. const char kExpected1[] =
  221. "\x00\x00\x00\x08tEXtkey\x00text\x9e\xe7\x66\x51";
  222. const char kExpected2[] =
  223. "\x00\x00\x00\x0etEXttest\x00something\x29\xba\xef\xac";
  224. const char kExpected3[] =
  225. "\x00\x00\x00\x18tEXthave some\x00spaces in both\x8d\x69\x34\x2d";
  226. std::string longKeyRecord = "tEXt" + longKey; // A snippet of our long key comment
  227. std::string tooLongRecord = "tExt" + longKey + "x"; // A snippet whose key is too long
  228. auto search1 = std::search(output.begin(), output.end(),
  229. kExpected1, kExpected1 + sizeof(kExpected1));
  230. auto search2 = std::search(output.begin(), output.end(),
  231. kExpected2, kExpected2 + sizeof(kExpected2));
  232. auto search3 = std::search(output.begin(), output.end(),
  233. kExpected3, kExpected3 + sizeof(kExpected3));
  234. auto search4 = std::search(output.begin(), output.end(),
  235. longKeyRecord.begin(), longKeyRecord.end());
  236. auto search5 = std::search(output.begin(), output.end(),
  237. tooLongRecord.begin(), tooLongRecord.end());
  238. REPORTER_ASSERT(r, search1 != output.end());
  239. REPORTER_ASSERT(r, search2 != output.end());
  240. REPORTER_ASSERT(r, search3 != output.end());
  241. REPORTER_ASSERT(r, search4 != output.end());
  242. REPORTER_ASSERT(r, search5 == output.end());
  243. // Comments test ends
  244. }
  245. DEF_TEST(Encode_PngOptions, r) {
  246. SkBitmap bitmap;
  247. bool success = GetResourceAsBitmap("images/mandrill_128.png", &bitmap);
  248. if (!success) {
  249. return;
  250. }
  251. SkPixmap src;
  252. success = bitmap.peekPixels(&src);
  253. REPORTER_ASSERT(r, success);
  254. if (!success) {
  255. return;
  256. }
  257. SkDynamicMemoryWStream dst0, dst1, dst2;
  258. SkPngEncoder::Options options;
  259. success = SkPngEncoder::Encode(&dst0, src, options);
  260. REPORTER_ASSERT(r, success);
  261. options.fFilterFlags = SkPngEncoder::FilterFlag::kUp;
  262. success = SkPngEncoder::Encode(&dst1, src, options);
  263. REPORTER_ASSERT(r, success);
  264. options.fZLibLevel = 3;
  265. success = SkPngEncoder::Encode(&dst2, src, options);
  266. REPORTER_ASSERT(r, success);
  267. testPngComments(src, options, r);
  268. sk_sp<SkData> data0 = dst0.detachAsData();
  269. sk_sp<SkData> data1 = dst1.detachAsData();
  270. sk_sp<SkData> data2 = dst2.detachAsData();
  271. REPORTER_ASSERT(r, data0->size() < data1->size());
  272. REPORTER_ASSERT(r, data1->size() < data2->size());
  273. SkBitmap bm0, bm1, bm2;
  274. SkImage::MakeFromEncoded(data0)->asLegacyBitmap(&bm0);
  275. SkImage::MakeFromEncoded(data1)->asLegacyBitmap(&bm1);
  276. SkImage::MakeFromEncoded(data2)->asLegacyBitmap(&bm2);
  277. REPORTER_ASSERT(r, almost_equals(bm0, bm1, 0));
  278. REPORTER_ASSERT(r, almost_equals(bm0, bm2, 0));
  279. }
  280. #ifndef SK_BUILD_FOR_GOOGLE3
  281. DEF_TEST(Encode_WebpQuality, r) {
  282. SkBitmap bm;
  283. bm.allocN32Pixels(100, 100);
  284. bm.eraseColor(SK_ColorBLUE);
  285. auto dataLossy = SkEncodeBitmap(bm, SkEncodedImageFormat::kWEBP, 99);
  286. auto dataLossLess = SkEncodeBitmap(bm, SkEncodedImageFormat::kWEBP, 100);
  287. enum Format {
  288. kMixed = 0,
  289. kLossy = 1,
  290. kLossless = 2,
  291. };
  292. auto test = [&r](const sk_sp<SkData>& data, Format expected) {
  293. auto printFormat = [](int f) {
  294. switch (f) {
  295. case kMixed: return "mixed";
  296. case kLossy: return "lossy";
  297. case kLossless: return "lossless";
  298. default: return "unknown";
  299. }
  300. };
  301. if (!data) {
  302. ERRORF(r, "Failed to encode. Expected %s", printFormat(expected));
  303. return;
  304. }
  305. WebPBitstreamFeatures features;
  306. auto status = WebPGetFeatures(data->bytes(), data->size(), &features);
  307. if (status != VP8_STATUS_OK) {
  308. ERRORF(r, "Encode had an error %i. Expected %s", status, printFormat(expected));
  309. return;
  310. }
  311. if (expected != features.format) {
  312. ERRORF(r, "Expected %s encode, but got format %s", printFormat(expected),
  313. printFormat(features.format));
  314. }
  315. };
  316. test(dataLossy, kLossy);
  317. test(dataLossLess, kLossless);
  318. }
  319. #endif
  320. DEF_TEST(Encode_WebpOptions, r) {
  321. SkBitmap bitmap;
  322. bool success = GetResourceAsBitmap("images/google_chrome.ico", &bitmap);
  323. if (!success) {
  324. return;
  325. }
  326. SkPixmap src;
  327. success = bitmap.peekPixels(&src);
  328. REPORTER_ASSERT(r, success);
  329. if (!success) {
  330. return;
  331. }
  332. SkDynamicMemoryWStream dst0, dst1, dst2, dst3;
  333. SkWebpEncoder::Options options;
  334. options.fCompression = SkWebpEncoder::Compression::kLossless;
  335. options.fQuality = 0.0f;
  336. success = SkWebpEncoder::Encode(&dst0, src, options);
  337. REPORTER_ASSERT(r, success);
  338. options.fQuality = 100.0f;
  339. success = SkWebpEncoder::Encode(&dst1, src, options);
  340. REPORTER_ASSERT(r, success);
  341. options.fCompression = SkWebpEncoder::Compression::kLossy;
  342. options.fQuality = 100.0f;
  343. success = SkWebpEncoder::Encode(&dst2, src, options);
  344. REPORTER_ASSERT(r, success);
  345. options.fCompression = SkWebpEncoder::Compression::kLossy;
  346. options.fQuality = 50.0f;
  347. success = SkWebpEncoder::Encode(&dst3, src, options);
  348. REPORTER_ASSERT(r, success);
  349. sk_sp<SkData> data0 = dst0.detachAsData();
  350. sk_sp<SkData> data1 = dst1.detachAsData();
  351. sk_sp<SkData> data2 = dst2.detachAsData();
  352. sk_sp<SkData> data3 = dst3.detachAsData();
  353. REPORTER_ASSERT(r, data0->size() > data1->size());
  354. REPORTER_ASSERT(r, data1->size() > data2->size());
  355. REPORTER_ASSERT(r, data2->size() > data3->size());
  356. SkBitmap bm0, bm1, bm2, bm3;
  357. SkImage::MakeFromEncoded(data0)->asLegacyBitmap(&bm0);
  358. SkImage::MakeFromEncoded(data1)->asLegacyBitmap(&bm1);
  359. SkImage::MakeFromEncoded(data2)->asLegacyBitmap(&bm2);
  360. SkImage::MakeFromEncoded(data3)->asLegacyBitmap(&bm3);
  361. REPORTER_ASSERT(r, almost_equals(bm0, bm1, 0));
  362. REPORTER_ASSERT(r, almost_equals(bm0, bm2, 90));
  363. REPORTER_ASSERT(r, almost_equals(bm2, bm3, 50));
  364. }