AndroidCodecTest.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  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/codec/SkAndroidCodec.h"
  8. #include "include/codec/SkCodec.h"
  9. #include "include/core/SkBitmap.h"
  10. #include "include/core/SkColor.h"
  11. #include "include/core/SkColorSpace.h"
  12. #include "include/core/SkData.h"
  13. #include "include/core/SkEncodedImageFormat.h"
  14. #include "include/core/SkImageGenerator.h"
  15. #include "include/core/SkImageInfo.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/third_party/skcms/skcms.h"
  21. #include "src/codec/SkCodecImageGenerator.h"
  22. #include "src/core/SkPixmapPriv.h"
  23. #include "tests/Test.h"
  24. #include "tools/Resources.h"
  25. #include <string.h>
  26. #include <initializer_list>
  27. #include <memory>
  28. #include <utility>
  29. static SkISize times(const SkISize& size, float factor) {
  30. return { (int) (size.width() * factor), (int) (size.height() * factor) };
  31. }
  32. static SkISize plus(const SkISize& size, int term) {
  33. return { size.width() + term, size.height() + term };
  34. }
  35. static bool invalid(const SkISize& size) {
  36. return size.width() < 1 || size.height() < 1;
  37. }
  38. DEF_TEST(AndroidCodec_computeSampleSize, r) {
  39. if (GetResourcePath().isEmpty()) {
  40. return;
  41. }
  42. for (const char* file : { "images/color_wheel.webp",
  43. "images/ship.png",
  44. "images/dog.jpg",
  45. "images/color_wheel.gif",
  46. "images/rle.bmp",
  47. "images/google_chrome.ico",
  48. "images/mandrill.wbmp",
  49. #ifdef SK_CODEC_DECODES_RAW
  50. "images/sample_1mp.dng",
  51. #endif
  52. }) {
  53. auto data = GetResourceAsData(file);
  54. if (!data) {
  55. ERRORF(r, "Could not get %s", file);
  56. continue;
  57. }
  58. auto codec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(std::move(data)));
  59. if (!codec) {
  60. ERRORF(r, "Could not create codec for %s", file);
  61. continue;
  62. }
  63. const auto dims = codec->getInfo().dimensions();
  64. const SkISize downscales[] = {
  65. plus(dims, -1),
  66. times(dims, .15f),
  67. times(dims, .6f),
  68. { (int32_t) (dims.width() * .25f), (int32_t) (dims.height() * .75f ) },
  69. { 1, 1 },
  70. { 1, 2 },
  71. { 2, 1 },
  72. { 0, -1 },
  73. { dims.width(), dims.height() - 1 },
  74. };
  75. for (SkISize size : downscales) {
  76. const auto requested = size;
  77. const int computedSampleSize = codec->computeSampleSize(&size);
  78. REPORTER_ASSERT(r, size.width() >= 1 && size.height() >= 1);
  79. if (codec->getEncodedFormat() == SkEncodedImageFormat::kWEBP) {
  80. // WebP supports arbitrary down-scaling.
  81. REPORTER_ASSERT(r, size == requested || invalid(requested));
  82. } else if (computedSampleSize == 1) {
  83. REPORTER_ASSERT(r, size == dims);
  84. } else {
  85. REPORTER_ASSERT(r, computedSampleSize > 1);
  86. if (size.width() >= dims.width() || size.height() >= dims.height()) {
  87. ERRORF(r, "File %s's computed sample size (%i) is bigger than"
  88. " original? original: %i x %i\tsampled: %i x %i",
  89. file, computedSampleSize, dims.width(), dims.height(),
  90. size.width(), size.height());
  91. }
  92. REPORTER_ASSERT(r, size.width() >= requested.width() &&
  93. size.height() >= requested.height());
  94. REPORTER_ASSERT(r, size.width() < dims.width() &&
  95. size.height() < dims.height());
  96. }
  97. }
  98. const SkISize upscales[] = {
  99. dims, plus(dims, 5), times(dims, 2),
  100. };
  101. for (SkISize size : upscales) {
  102. const int computedSampleSize = codec->computeSampleSize(&size);
  103. REPORTER_ASSERT(r, computedSampleSize == 1);
  104. REPORTER_ASSERT(r, dims == size);
  105. }
  106. // This mimics how Android's ImageDecoder uses SkAndroidCodec. A client
  107. // can choose their dimensions based on calling getSampledDimensions,
  108. // but the ImageDecoder API takes an arbitrary size. It then uses
  109. // computeSampleSize to determine the best dimensions and sampleSize.
  110. // It should return the same dimensions. the sampleSize may be different
  111. // due to integer division.
  112. for (int sampleSize : { 1, 2, 3, 4, 8, 16, 32 }) {
  113. const SkISize sampledDims = codec->getSampledDimensions(sampleSize);
  114. SkISize size = sampledDims;
  115. const int computedSampleSize = codec->computeSampleSize(&size);
  116. if (sampledDims != size) {
  117. ERRORF(r, "File '%s'->getSampledDimensions(%i) yields computed"
  118. " sample size of %i\n\tsampledDimensions: %i x %i\t"
  119. "computed dimensions: %i x %i",
  120. file, sampleSize, computedSampleSize,
  121. sampledDims.width(), sampledDims.height(),
  122. size.width(), size.height());
  123. }
  124. }
  125. }
  126. }
  127. DEF_TEST(AndroidCodec_wide, r) {
  128. if (GetResourcePath().isEmpty()) {
  129. return;
  130. }
  131. const char* path = "images/wide-gamut.png";
  132. auto data = GetResourceAsData(path);
  133. if (!data) {
  134. ERRORF(r, "Missing file %s", path);
  135. return;
  136. }
  137. auto codec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(std::move(data)));
  138. if (!codec) {
  139. ERRORF(r, "Failed to create codec from %s", path);
  140. return;
  141. }
  142. auto info = codec->getInfo();
  143. auto cs = codec->computeOutputColorSpace(info.colorType(), nullptr);
  144. if (!cs) {
  145. ERRORF(r, "%s should have a color space", path);
  146. return;
  147. }
  148. auto expected = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3);
  149. REPORTER_ASSERT(r, SkColorSpace::Equals(cs.get(), expected.get()));
  150. }
  151. DEF_TEST(AndroidCodec_P3, r) {
  152. if (GetResourcePath().isEmpty()) {
  153. return;
  154. }
  155. const char* path = "images/purple-displayprofile.png";
  156. auto data = GetResourceAsData(path);
  157. if (!data) {
  158. ERRORF(r, "Missing file %s", path);
  159. return;
  160. }
  161. auto codec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(std::move(data)));
  162. if (!codec) {
  163. ERRORF(r, "Failed to create codec from %s", path);
  164. return;
  165. }
  166. auto info = codec->getInfo();
  167. auto cs = codec->computeOutputColorSpace(info.colorType(), nullptr);
  168. if (!cs) {
  169. ERRORF(r, "%s should have a color space", path);
  170. return;
  171. }
  172. REPORTER_ASSERT(r, !cs->isSRGB());
  173. REPORTER_ASSERT(r, cs->gammaCloseToSRGB());
  174. skcms_Matrix3x3 matrix;
  175. cs->toXYZD50(&matrix);
  176. static constexpr skcms_Matrix3x3 kExpected = {{
  177. { 0.426254272f, 0.369018555f, 0.168914795f },
  178. { 0.226013184f, 0.685974121f, 0.0880126953f },
  179. { 0.0116729736f, 0.0950927734f, 0.71812439f },
  180. }};
  181. REPORTER_ASSERT(r, 0 == memcmp(&matrix, &kExpected, sizeof(skcms_Matrix3x3)));
  182. }
  183. DEF_TEST(AndroidCodec_orientation, r) {
  184. if (GetResourcePath().isEmpty()) {
  185. return;
  186. }
  187. for (const char* ext : { "jpg", "webp" })
  188. for (char i = '1'; i <= '8'; ++i) {
  189. SkString path = SkStringPrintf("images/orientation/%c.%s", i, ext);
  190. auto data = GetResourceAsData(path.c_str());
  191. auto gen = SkCodecImageGenerator::MakeFromEncodedCodec(data);
  192. if (!gen) {
  193. ERRORF(r, "failed to decode %s", path.c_str());
  194. return;
  195. }
  196. // Dimensions after adjusting for the origin.
  197. const SkISize expectedDims = { 100, 80 };
  198. // SkCodecImageGenerator automatically adjusts for the origin.
  199. REPORTER_ASSERT(r, gen->getInfo().dimensions() == expectedDims);
  200. auto androidCodec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(data));
  201. if (!androidCodec) {
  202. ERRORF(r, "failed to decode %s", path.c_str());
  203. return;
  204. }
  205. // SkAndroidCodec does not adjust for the origin by default. Dimensions may be reversed.
  206. if (SkPixmapPriv::ShouldSwapWidthHeight(androidCodec->codec()->getOrigin())) {
  207. auto swappedDims = SkPixmapPriv::SwapWidthHeight(androidCodec->getInfo()).dimensions();
  208. REPORTER_ASSERT(r, expectedDims == swappedDims);
  209. } else {
  210. REPORTER_ASSERT(r, expectedDims == androidCodec->getInfo().dimensions());
  211. }
  212. // Passing kRespect adjusts for the origin.
  213. androidCodec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(std::move(data)),
  214. SkAndroidCodec::ExifOrientationBehavior::kRespect);
  215. auto info = androidCodec->getInfo();
  216. REPORTER_ASSERT(r, info.dimensions() == expectedDims);
  217. SkBitmap fromGenerator;
  218. fromGenerator.allocPixels(info);
  219. REPORTER_ASSERT(r, gen->getPixels(info, fromGenerator.getPixels(),
  220. fromGenerator.rowBytes()));
  221. SkBitmap fromAndroidCodec;
  222. fromAndroidCodec.allocPixels(info);
  223. auto result = androidCodec->getPixels(info, fromAndroidCodec.getPixels(),
  224. fromAndroidCodec.rowBytes());
  225. REPORTER_ASSERT(r, result == SkCodec::kSuccess);
  226. for (int i = 0; i < info.width(); ++i)
  227. for (int j = 0; j < info.height(); ++j) {
  228. SkColor c1 = *fromGenerator .getAddr32(i, j);
  229. SkColor c2 = *fromAndroidCodec.getAddr32(i, j);
  230. if (c1 != c2) {
  231. ERRORF(r, "Bitmaps for %s do not match starting at position %i, %i\n"
  232. "\tfromGenerator: %x\tfromAndroidCodec: %x", path.c_str(), i, j,
  233. c1, c2);
  234. return;
  235. }
  236. }
  237. }
  238. }
  239. DEF_TEST(AndroidCodec_sampledOrientation, r) {
  240. if (GetResourcePath().isEmpty()) {
  241. return;
  242. }
  243. // kRightTop_SkEncodedOrigin = 6, // Rotated 90 CW
  244. auto path = "images/orientation/6.jpg";
  245. auto data = GetResourceAsData(path);
  246. if (!data) {
  247. ERRORF(r, "Failed to get resource %s", path);
  248. return;
  249. }
  250. auto androidCodec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(std::move(data)),
  251. SkAndroidCodec::ExifOrientationBehavior::kRespect);
  252. constexpr int sampleSize = 7;
  253. auto sampledDims = androidCodec->getSampledDimensions(sampleSize);
  254. SkAndroidCodec::AndroidOptions options;
  255. options.fSampleSize = sampleSize;
  256. SkBitmap bm;
  257. auto info = androidCodec->getInfo().makeWH(sampledDims.width(), sampledDims.height());
  258. bm.allocPixels(info);
  259. auto result = androidCodec->getAndroidPixels(info, bm.getPixels(), bm.rowBytes(), &options);
  260. if (result != SkCodec::kSuccess) {
  261. ERRORF(r, "got result \"%s\"\n", SkCodec::ResultToString(result));
  262. }
  263. }