SkAndroidCodec.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. /*
  2. * Copyright 2015 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/SkPixmap.h"
  10. #include "src/codec/SkAndroidCodecAdapter.h"
  11. #include "src/codec/SkCodecPriv.h"
  12. #include "src/codec/SkSampledCodec.h"
  13. #include "src/core/SkMakeUnique.h"
  14. #include "src/core/SkPixmapPriv.h"
  15. static bool is_valid_sample_size(int sampleSize) {
  16. // FIXME: As Leon has mentioned elsewhere, surely there is also a maximum sampleSize?
  17. return sampleSize > 0;
  18. }
  19. /**
  20. * Loads the gamut as a set of three points (triangle).
  21. */
  22. static void load_gamut(SkPoint rgb[], const skcms_Matrix3x3& xyz) {
  23. // rx = rX / (rX + rY + rZ)
  24. // ry = rY / (rX + rY + rZ)
  25. // gx, gy, bx, and gy are calulcated similarly.
  26. for (int rgbIdx = 0; rgbIdx < 3; rgbIdx++) {
  27. float sum = xyz.vals[rgbIdx][0] + xyz.vals[rgbIdx][1] + xyz.vals[rgbIdx][2];
  28. rgb[rgbIdx].fX = xyz.vals[rgbIdx][0] / sum;
  29. rgb[rgbIdx].fY = xyz.vals[rgbIdx][1] / sum;
  30. }
  31. }
  32. /**
  33. * Calculates the area of the triangular gamut.
  34. */
  35. static float calculate_area(SkPoint abc[]) {
  36. SkPoint a = abc[0];
  37. SkPoint b = abc[1];
  38. SkPoint c = abc[2];
  39. return 0.5f * SkTAbs(a.fX*b.fY + b.fX*c.fY - a.fX*c.fY - c.fX*b.fY - b.fX*a.fY);
  40. }
  41. static constexpr float kSRGB_D50_GamutArea = 0.084f;
  42. static bool is_wide_gamut(const skcms_ICCProfile& profile) {
  43. // Determine if the source image has a gamut that is wider than sRGB. If so, we
  44. // will use P3 as the output color space to avoid clipping the gamut.
  45. if (profile.has_toXYZD50) {
  46. SkPoint rgb[3];
  47. load_gamut(rgb, profile.toXYZD50);
  48. return calculate_area(rgb) > kSRGB_D50_GamutArea;
  49. }
  50. return false;
  51. }
  52. static inline SkImageInfo adjust_info(SkCodec* codec,
  53. SkAndroidCodec::ExifOrientationBehavior orientationBehavior) {
  54. auto info = codec->getInfo();
  55. if (orientationBehavior == SkAndroidCodec::ExifOrientationBehavior::kIgnore
  56. || !SkPixmapPriv::ShouldSwapWidthHeight(codec->getOrigin())) {
  57. return info;
  58. }
  59. return SkPixmapPriv::SwapWidthHeight(info);
  60. }
  61. SkAndroidCodec::SkAndroidCodec(SkCodec* codec, ExifOrientationBehavior orientationBehavior)
  62. : fInfo(adjust_info(codec, orientationBehavior))
  63. , fOrientationBehavior(orientationBehavior)
  64. , fCodec(codec)
  65. {}
  66. SkAndroidCodec::~SkAndroidCodec() {}
  67. std::unique_ptr<SkAndroidCodec> SkAndroidCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
  68. SkPngChunkReader* chunkReader) {
  69. auto codec = SkCodec::MakeFromStream(std::move(stream), nullptr, chunkReader);
  70. return MakeFromCodec(std::move(codec));
  71. }
  72. std::unique_ptr<SkAndroidCodec> SkAndroidCodec::MakeFromCodec(std::unique_ptr<SkCodec> codec,
  73. ExifOrientationBehavior orientationBehavior) {
  74. if (nullptr == codec) {
  75. return nullptr;
  76. }
  77. switch ((SkEncodedImageFormat)codec->getEncodedFormat()) {
  78. case SkEncodedImageFormat::kPNG:
  79. case SkEncodedImageFormat::kICO:
  80. case SkEncodedImageFormat::kJPEG:
  81. #ifndef SK_HAS_WUFFS_LIBRARY
  82. case SkEncodedImageFormat::kGIF:
  83. #endif
  84. case SkEncodedImageFormat::kBMP:
  85. case SkEncodedImageFormat::kWBMP:
  86. case SkEncodedImageFormat::kHEIF:
  87. return skstd::make_unique<SkSampledCodec>(codec.release(), orientationBehavior);
  88. #ifdef SK_HAS_WUFFS_LIBRARY
  89. case SkEncodedImageFormat::kGIF:
  90. #endif
  91. #ifdef SK_HAS_WEBP_LIBRARY
  92. case SkEncodedImageFormat::kWEBP:
  93. #endif
  94. #ifdef SK_CODEC_DECODES_RAW
  95. case SkEncodedImageFormat::kDNG:
  96. #endif
  97. #if defined(SK_HAS_WEBP_LIBRARY) || defined(SK_CODEC_DECODES_RAW) || defined(SK_HAS_WUFFS_LIBRARY)
  98. return skstd::make_unique<SkAndroidCodecAdapter>(codec.release(), orientationBehavior);
  99. #endif
  100. default:
  101. return nullptr;
  102. }
  103. }
  104. std::unique_ptr<SkAndroidCodec> SkAndroidCodec::MakeFromData(sk_sp<SkData> data,
  105. SkPngChunkReader* chunkReader) {
  106. if (!data) {
  107. return nullptr;
  108. }
  109. return MakeFromStream(SkMemoryStream::Make(std::move(data)), chunkReader);
  110. }
  111. SkColorType SkAndroidCodec::computeOutputColorType(SkColorType requestedColorType) {
  112. bool highPrecision = fCodec->getEncodedInfo().bitsPerComponent() > 8;
  113. switch (requestedColorType) {
  114. case kARGB_4444_SkColorType:
  115. return kN32_SkColorType;
  116. case kN32_SkColorType:
  117. break;
  118. case kAlpha_8_SkColorType:
  119. // Fall through to kGray_8. Before kGray_8_SkColorType existed,
  120. // we allowed clients to request kAlpha_8 when they wanted a
  121. // grayscale decode.
  122. case kGray_8_SkColorType:
  123. if (kGray_8_SkColorType == this->getInfo().colorType()) {
  124. return kGray_8_SkColorType;
  125. }
  126. break;
  127. case kRGB_565_SkColorType:
  128. if (kOpaque_SkAlphaType == this->getInfo().alphaType()) {
  129. return kRGB_565_SkColorType;
  130. }
  131. break;
  132. case kRGBA_F16_SkColorType:
  133. return kRGBA_F16_SkColorType;
  134. default:
  135. break;
  136. }
  137. // F16 is the Android default for high precision images.
  138. return highPrecision ? kRGBA_F16_SkColorType : kN32_SkColorType;
  139. }
  140. SkAlphaType SkAndroidCodec::computeOutputAlphaType(bool requestedUnpremul) {
  141. if (kOpaque_SkAlphaType == this->getInfo().alphaType()) {
  142. return kOpaque_SkAlphaType;
  143. }
  144. return requestedUnpremul ? kUnpremul_SkAlphaType : kPremul_SkAlphaType;
  145. }
  146. sk_sp<SkColorSpace> SkAndroidCodec::computeOutputColorSpace(SkColorType outputColorType,
  147. sk_sp<SkColorSpace> prefColorSpace) {
  148. switch (outputColorType) {
  149. case kRGBA_F16_SkColorType:
  150. case kRGB_565_SkColorType:
  151. case kRGBA_8888_SkColorType:
  152. case kBGRA_8888_SkColorType: {
  153. // If |prefColorSpace| is supplied, choose it.
  154. if (prefColorSpace) {
  155. return prefColorSpace;
  156. }
  157. const skcms_ICCProfile* encodedProfile = fCodec->getEncodedInfo().profile();
  158. if (encodedProfile) {
  159. if (auto encodedSpace = SkColorSpace::Make(*encodedProfile)) {
  160. // Leave the pixels in the encoded color space. Color space conversion
  161. // will be handled after decode time.
  162. return encodedSpace;
  163. }
  164. if (is_wide_gamut(*encodedProfile)) {
  165. return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3);
  166. }
  167. }
  168. return SkColorSpace::MakeSRGB();
  169. }
  170. default:
  171. // Color correction not supported for kGray.
  172. return nullptr;
  173. }
  174. }
  175. static bool supports_any_down_scale(const SkCodec* codec) {
  176. return codec->getEncodedFormat() == SkEncodedImageFormat::kWEBP;
  177. }
  178. // There are a variety of ways two SkISizes could be compared. This method
  179. // returns true if either dimensions of a is < that of b.
  180. // computeSampleSize also uses the opposite, which means that both
  181. // dimensions of a >= b.
  182. static inline bool smaller_than(const SkISize& a, const SkISize& b) {
  183. return a.width() < b.width() || a.height() < b.height();
  184. }
  185. // Both dimensions of a > that of b.
  186. static inline bool strictly_bigger_than(const SkISize& a, const SkISize& b) {
  187. return a.width() > b.width() && a.height() > b.height();
  188. }
  189. int SkAndroidCodec::computeSampleSize(SkISize* desiredSize) const {
  190. SkASSERT(desiredSize);
  191. if (!desiredSize || *desiredSize == fInfo.dimensions()) {
  192. return 1;
  193. }
  194. if (smaller_than(fInfo.dimensions(), *desiredSize)) {
  195. *desiredSize = fInfo.dimensions();
  196. return 1;
  197. }
  198. // Handle bad input:
  199. if (desiredSize->width() < 1 || desiredSize->height() < 1) {
  200. *desiredSize = SkISize::Make(std::max(1, desiredSize->width()),
  201. std::max(1, desiredSize->height()));
  202. }
  203. if (supports_any_down_scale(fCodec.get())) {
  204. return 1;
  205. }
  206. int sampleX = fInfo.width() / desiredSize->width();
  207. int sampleY = fInfo.height() / desiredSize->height();
  208. int sampleSize = std::min(sampleX, sampleY);
  209. auto computedSize = this->getSampledDimensions(sampleSize);
  210. if (computedSize == *desiredSize) {
  211. return sampleSize;
  212. }
  213. if (computedSize == fInfo.dimensions() || sampleSize == 1) {
  214. // Cannot downscale
  215. *desiredSize = computedSize;
  216. return 1;
  217. }
  218. if (strictly_bigger_than(computedSize, *desiredSize)) {
  219. // See if there is a tighter fit.
  220. while (true) {
  221. auto smaller = this->getSampledDimensions(sampleSize + 1);
  222. if (smaller == *desiredSize) {
  223. return sampleSize + 1;
  224. }
  225. if (smaller == computedSize || smaller_than(smaller, *desiredSize)) {
  226. // Cannot get any smaller without being smaller than desired.
  227. *desiredSize = computedSize;
  228. return sampleSize;
  229. }
  230. sampleSize++;
  231. computedSize = smaller;
  232. }
  233. SkASSERT(false);
  234. }
  235. if (!smaller_than(computedSize, *desiredSize)) {
  236. // This means one of the computed dimensions is equal to desired, and
  237. // the other is bigger. This is as close as we can get.
  238. *desiredSize = computedSize;
  239. return sampleSize;
  240. }
  241. // computedSize is too small. Make it larger.
  242. while (sampleSize > 2) {
  243. auto bigger = this->getSampledDimensions(sampleSize - 1);
  244. if (bigger == *desiredSize || !smaller_than(bigger, *desiredSize)) {
  245. *desiredSize = bigger;
  246. return sampleSize - 1;
  247. }
  248. sampleSize--;
  249. }
  250. *desiredSize = fInfo.dimensions();
  251. return 1;
  252. }
  253. SkISize SkAndroidCodec::getSampledDimensions(int sampleSize) const {
  254. if (!is_valid_sample_size(sampleSize)) {
  255. return {0, 0};
  256. }
  257. // Fast path for when we are not scaling.
  258. if (1 == sampleSize) {
  259. return fInfo.dimensions();
  260. }
  261. auto dims = this->onGetSampledDimensions(sampleSize);
  262. if (fOrientationBehavior == SkAndroidCodec::ExifOrientationBehavior::kIgnore
  263. || !SkPixmapPriv::ShouldSwapWidthHeight(fCodec->getOrigin())) {
  264. return dims;
  265. }
  266. return { dims.height(), dims.width() };
  267. }
  268. bool SkAndroidCodec::getSupportedSubset(SkIRect* desiredSubset) const {
  269. if (!desiredSubset || !is_valid_subset(*desiredSubset, fInfo.dimensions())) {
  270. return false;
  271. }
  272. return this->onGetSupportedSubset(desiredSubset);
  273. }
  274. SkISize SkAndroidCodec::getSampledSubsetDimensions(int sampleSize, const SkIRect& subset) const {
  275. if (!is_valid_sample_size(sampleSize)) {
  276. return {0, 0};
  277. }
  278. // We require that the input subset is a subset that is supported by SkAndroidCodec.
  279. // We test this by calling getSupportedSubset() and verifying that no modifications
  280. // are made to the subset.
  281. SkIRect copySubset = subset;
  282. if (!this->getSupportedSubset(&copySubset) || copySubset != subset) {
  283. return {0, 0};
  284. }
  285. // If the subset is the entire image, for consistency, use getSampledDimensions().
  286. if (fInfo.dimensions() == subset.size()) {
  287. return this->getSampledDimensions(sampleSize);
  288. }
  289. // This should perhaps call a virtual function, but currently both of our subclasses
  290. // want the same implementation.
  291. return {get_scaled_dimension(subset.width(), sampleSize),
  292. get_scaled_dimension(subset.height(), sampleSize)};
  293. }
  294. static bool acceptable_result(SkCodec::Result result) {
  295. switch (result) {
  296. // These results mean a partial or complete image. They should be considered
  297. // a success by SkPixmapPriv.
  298. case SkCodec::kSuccess:
  299. case SkCodec::kIncompleteInput:
  300. case SkCodec::kErrorInInput:
  301. return true;
  302. default:
  303. return false;
  304. }
  305. }
  306. SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo& requestInfo,
  307. void* requestPixels, size_t requestRowBytes, const AndroidOptions* options) {
  308. if (!requestPixels) {
  309. return SkCodec::kInvalidParameters;
  310. }
  311. if (requestRowBytes < requestInfo.minRowBytes()) {
  312. return SkCodec::kInvalidParameters;
  313. }
  314. SkImageInfo adjustedInfo = fInfo;
  315. if (ExifOrientationBehavior::kRespect == fOrientationBehavior
  316. && SkPixmapPriv::ShouldSwapWidthHeight(fCodec->getOrigin())) {
  317. adjustedInfo = SkPixmapPriv::SwapWidthHeight(adjustedInfo);
  318. }
  319. AndroidOptions defaultOptions;
  320. if (!options) {
  321. options = &defaultOptions;
  322. } else if (options->fSubset) {
  323. if (!is_valid_subset(*options->fSubset, adjustedInfo.dimensions())) {
  324. return SkCodec::kInvalidParameters;
  325. }
  326. if (SkIRect::MakeSize(adjustedInfo.dimensions()) == *options->fSubset) {
  327. // The caller wants the whole thing, rather than a subset. Modify
  328. // the AndroidOptions passed to onGetAndroidPixels to not specify
  329. // a subset.
  330. defaultOptions = *options;
  331. defaultOptions.fSubset = nullptr;
  332. options = &defaultOptions;
  333. }
  334. }
  335. if (ExifOrientationBehavior::kIgnore == fOrientationBehavior) {
  336. return this->onGetAndroidPixels(requestInfo, requestPixels, requestRowBytes, *options);
  337. }
  338. SkCodec::Result result;
  339. auto decode = [this, options, &result](const SkPixmap& pm) {
  340. result = this->onGetAndroidPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), *options);
  341. return acceptable_result(result);
  342. };
  343. SkPixmap dst(requestInfo, requestPixels, requestRowBytes);
  344. if (SkPixmapPriv::Orient(dst, fCodec->getOrigin(), decode)) {
  345. return result;
  346. }
  347. // Orient returned false. If onGetAndroidPixels succeeded, then Orient failed internally.
  348. if (acceptable_result(result)) {
  349. return SkCodec::kInternalError;
  350. }
  351. return result;
  352. }
  353. SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo& info, void* pixels,
  354. size_t rowBytes) {
  355. return this->getAndroidPixels(info, pixels, rowBytes, nullptr);
  356. }