From da3e9ad894379713cbb66779136ca6877ccac7dd Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Fri, 26 Jan 2018 15:48:26 -0500 Subject: Make SkAndroidCodec (optionally) respect origin Bug: b/63909536 ImageDecoder will respect the origin, but BitmapFactory will maintain its current behavior of not respecting it. Add an option to respect it. In addition, add support for reading the EXIF data from a WEBP. This seems to be an uncommon use case, but is occasionally used when converting from a JPEG. Add 8 WEBPs, all converted (with cwebp) from their analogous JPEG files already checked in. Change-Id: I38afca58c86fa99ee9ab7d1dc83aaa4f23132c11 Reviewed-on: https://skia-review.googlesource.com/95300 Reviewed-by: Derek Sollenberger Commit-Queue: Leon Scroggins --- include/codec/SkAndroidCodec.h | 39 +++++++++++++---- resources/images/orientation/1.webp | Bin 0 -> 2628 bytes resources/images/orientation/2.webp | Bin 0 -> 2888 bytes resources/images/orientation/3.webp | Bin 0 -> 2810 bytes resources/images/orientation/4.webp | Bin 0 -> 2820 bytes resources/images/orientation/5.webp | Bin 0 -> 2890 bytes resources/images/orientation/6.webp | Bin 0 -> 2884 bytes resources/images/orientation/7.webp | Bin 0 -> 2766 bytes resources/images/orientation/8.webp | Bin 0 -> 2864 bytes src/codec/SkAndroidCodec.cpp | 84 ++++++++++++++++++++++++++++++------ src/codec/SkCodecPriv.h | 3 ++ src/codec/SkJpegCodec.cpp | 20 +++++---- src/codec/SkRawAdapterCodec.cpp | 4 +- src/codec/SkRawAdapterCodec.h | 3 +- src/codec/SkSampledCodec.cpp | 4 +- src/codec/SkSampledCodec.h | 3 +- src/codec/SkWebpAdapterCodec.cpp | 4 +- src/codec/SkWebpAdapterCodec.h | 2 +- src/codec/SkWebpCodec.cpp | 31 +++++++++---- src/codec/SkWebpCodec.h | 2 +- tests/AndroidCodecTest.cpp | 68 +++++++++++++++++++++++++++++ 21 files changed, 215 insertions(+), 52 deletions(-) create mode 100644 resources/images/orientation/1.webp create mode 100644 resources/images/orientation/2.webp create mode 100644 resources/images/orientation/3.webp create mode 100644 resources/images/orientation/4.webp create mode 100644 resources/images/orientation/5.webp create mode 100644 resources/images/orientation/6.webp create mode 100644 resources/images/orientation/7.webp create mode 100644 resources/images/orientation/8.webp diff --git a/include/codec/SkAndroidCodec.h b/include/codec/SkAndroidCodec.h index 0746f066c8..750e2351f2 100644 --- a/include/codec/SkAndroidCodec.h +++ b/include/codec/SkAndroidCodec.h @@ -19,10 +19,31 @@ */ class SK_API SkAndroidCodec : SkNoncopyable { public: + enum class ExifOrientationBehavior { + /** + * Ignore any exif orientation markers in the data. + * + * getInfo's width and height will match the header of the image, and + * no processing will be done to match the marker. + */ + kIgnore, + + /** + * Respect the exif orientation marker. + * + * getInfo's width and height will represent what they should be after + * applying the orientation. For example, if the marker specifies a + * rotation by 90 degrees, they will be swapped relative to the header. + * getAndroidPixels will apply the orientation as well. + */ + kRespect, + }; + /** * Pass ownership of an SkCodec to a newly-created SkAndroidCodec. */ - static std::unique_ptr MakeFromCodec(std::unique_ptr); + static std::unique_ptr MakeFromCodec(std::unique_ptr, + ExifOrientationBehavior = ExifOrientationBehavior::kIgnore); /** * If this stream represents an encoded image that we know how to decode, @@ -33,6 +54,8 @@ public: * * If NULL is returned, the stream is deleted immediately. Otherwise, the * SkCodec takes ownership of it, and will delete it when done with it. + * + * ExifOrientationBehavior is set to kIgnore. */ static std::unique_ptr MakeFromStream(std::unique_ptr, SkPngChunkReader* = nullptr); @@ -43,6 +66,8 @@ public: * * The SkPngChunkReader handles unknown chunks in PNGs. * See SkCodec.h for more details. + * + * ExifOrientationBehavior is set to kIgnore. */ static std::unique_ptr MakeFromData(sk_sp, SkPngChunkReader* = nullptr); @@ -245,8 +270,7 @@ public: SkCodec* codec() const { return fCodec.get(); } protected: - - SkAndroidCodec(SkCodec*); + SkAndroidCodec(SkCodec*, ExifOrientationBehavior = ExifOrientationBehavior::kIgnore); virtual SkISize onGetSampledDimensions(int sampleSize) const = 0; @@ -256,11 +280,8 @@ protected: size_t rowBytes, const AndroidOptions& options) = 0; private: - - // This will always be a reference to the info that is contained by the - // embedded SkCodec. - const SkImageInfo& fInfo; - - std::unique_ptr fCodec; + const SkImageInfo fInfo; + const ExifOrientationBehavior fOrientationBehavior; + std::unique_ptr fCodec; }; #endif // SkAndroidCodec_DEFINED diff --git a/resources/images/orientation/1.webp b/resources/images/orientation/1.webp new file mode 100644 index 0000000000..5b613d66ac Binary files /dev/null and b/resources/images/orientation/1.webp differ diff --git a/resources/images/orientation/2.webp b/resources/images/orientation/2.webp new file mode 100644 index 0000000000..77e9e201be Binary files /dev/null and b/resources/images/orientation/2.webp differ diff --git a/resources/images/orientation/3.webp b/resources/images/orientation/3.webp new file mode 100644 index 0000000000..aad50feb2a Binary files /dev/null and b/resources/images/orientation/3.webp differ diff --git a/resources/images/orientation/4.webp b/resources/images/orientation/4.webp new file mode 100644 index 0000000000..19e60cee35 Binary files /dev/null and b/resources/images/orientation/4.webp differ diff --git a/resources/images/orientation/5.webp b/resources/images/orientation/5.webp new file mode 100644 index 0000000000..cb48e2ed18 Binary files /dev/null and b/resources/images/orientation/5.webp differ diff --git a/resources/images/orientation/6.webp b/resources/images/orientation/6.webp new file mode 100644 index 0000000000..2298968aaa Binary files /dev/null and b/resources/images/orientation/6.webp differ diff --git a/resources/images/orientation/7.webp b/resources/images/orientation/7.webp new file mode 100644 index 0000000000..7a9f11f4c9 Binary files /dev/null and b/resources/images/orientation/7.webp differ diff --git a/resources/images/orientation/8.webp b/resources/images/orientation/8.webp new file mode 100644 index 0000000000..353eb41bac Binary files /dev/null and b/resources/images/orientation/8.webp differ diff --git a/src/codec/SkAndroidCodec.cpp b/src/codec/SkAndroidCodec.cpp index 486257f9a3..536e3441de 100644 --- a/src/codec/SkAndroidCodec.cpp +++ b/src/codec/SkAndroidCodec.cpp @@ -9,6 +9,8 @@ #include "SkCodec.h" #include "SkCodecPriv.h" #include "SkMakeUnique.h" +#include "SkPixmap.h" +#include "SkPixmapPriv.h" #include "SkRawAdapterCodec.h" #include "SkSampledCodec.h" #include "SkWebpAdapterCodec.h" @@ -57,19 +59,32 @@ static bool is_wide_gamut(const SkColorSpace* colorSpace) { return false; } -SkAndroidCodec::SkAndroidCodec(SkCodec* codec) - : fInfo(codec->getInfo()) +static inline SkImageInfo adjust_info(SkCodec* codec, + SkAndroidCodec::ExifOrientationBehavior orientationBehavior) { + auto info = codec->getInfo(); + if (orientationBehavior == SkAndroidCodec::ExifOrientationBehavior::kIgnore + || !SkPixmapPriv::ShouldSwapWidthHeight(codec->getOrigin())) { + return info; + } + return SkPixmapPriv::SwapWidthHeight(info); +} + +SkAndroidCodec::SkAndroidCodec(SkCodec* codec, ExifOrientationBehavior orientationBehavior) + : fInfo(adjust_info(codec, orientationBehavior)) + , fOrientationBehavior(orientationBehavior) , fCodec(codec) {} SkAndroidCodec::~SkAndroidCodec() {} -std::unique_ptr SkAndroidCodec::MakeFromStream(std::unique_ptr stream, SkPngChunkReader* chunkReader) { +std::unique_ptr SkAndroidCodec::MakeFromStream(std::unique_ptr stream, + SkPngChunkReader* chunkReader) { auto codec = SkCodec::MakeFromStream(std::move(stream), nullptr, chunkReader); return MakeFromCodec(std::move(codec)); } -std::unique_ptr SkAndroidCodec::MakeFromCodec(std::unique_ptr codec) { +std::unique_ptr SkAndroidCodec::MakeFromCodec(std::unique_ptr codec, + ExifOrientationBehavior orientationBehavior) { if (nullptr == codec) { return nullptr; } @@ -88,14 +103,16 @@ std::unique_ptr SkAndroidCodec::MakeFromCodec(std::unique_ptr(codec.release()); + return skstd::make_unique(codec.release(), orientationBehavior); #ifdef SK_HAS_WEBP_LIBRARY case SkEncodedImageFormat::kWEBP: - return skstd::make_unique((SkWebpCodec*) codec.release()); + return skstd::make_unique((SkWebpCodec*) codec.release(), + orientationBehavior); #endif #ifdef SK_CODEC_DECODES_RAW case SkEncodedImageFormat::kDNG: - return skstd::make_unique((SkRawCodec*)codec.release()); + return skstd::make_unique((SkRawCodec*)codec.release(), + orientationBehavior); #endif default: return nullptr; @@ -325,24 +342,43 @@ SkISize SkAndroidCodec::getSampledSubsetDimensions(int sampleSize, const SkIRect get_scaled_dimension(subset.height(), sampleSize)}; } -SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo& info, void* pixels, - size_t rowBytes, const AndroidOptions* options) { - if (!pixels) { +static bool acceptable_result(SkCodec::Result result) { + switch (result) { + // These results mean a partial or complete image. They should be considered + // a success by SkPixmapPriv. + case SkCodec::kSuccess: + case SkCodec::kIncompleteInput: + case SkCodec::kErrorInInput: + return true; + default: + return false; + } +} + +SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo& requestInfo, + void* requestPixels, size_t requestRowBytes, const AndroidOptions* options) { + if (!requestPixels) { return SkCodec::kInvalidParameters; } - if (rowBytes < info.minRowBytes()) { + if (requestRowBytes < requestInfo.minRowBytes()) { return SkCodec::kInvalidParameters; } + SkImageInfo adjustedInfo = fInfo; + if (ExifOrientationBehavior::kRespect == fOrientationBehavior + && SkPixmapPriv::ShouldSwapWidthHeight(fCodec->getOrigin())) { + adjustedInfo = SkPixmapPriv::SwapWidthHeight(adjustedInfo); + } + AndroidOptions defaultOptions; if (!options) { options = &defaultOptions; } else if (options->fSubset) { - if (!is_valid_subset(*options->fSubset, fInfo.dimensions())) { + if (!is_valid_subset(*options->fSubset, adjustedInfo.dimensions())) { return SkCodec::kInvalidParameters; } - if (SkIRect::MakeSize(fInfo.dimensions()) == *options->fSubset) { + if (SkIRect::MakeSize(adjustedInfo.dimensions()) == *options->fSubset) { // The caller wants the whole thing, rather than a subset. Modify // the AndroidOptions passed to onGetAndroidPixels to not specify // a subset. @@ -352,7 +388,27 @@ SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo& info, void* } } - return this->onGetAndroidPixels(info, pixels, rowBytes, *options); + if (ExifOrientationBehavior::kIgnore == fOrientationBehavior) { + return this->onGetAndroidPixels(requestInfo, requestPixels, requestRowBytes, *options); + } + + SkCodec::Result result; + auto decode = [this, options, &result](const SkPixmap& pm) { + result = this->onGetAndroidPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), *options); + return acceptable_result(result); + }; + + SkPixmap dst(requestInfo, requestPixels, requestRowBytes); + if (SkPixmapPriv::Orient(dst, fCodec->getOrigin(), decode)) { + return result; + } + + // Orient returned false. If onGetAndroidPixels succeeded, then Orient failed internally. + if (acceptable_result(result)) { + return SkCodec::kInternalError; + } + + return result; } SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo& info, void* pixels, diff --git a/src/codec/SkCodecPriv.h b/src/codec/SkCodecPriv.h index 84215b9fb3..1251d8d73c 100644 --- a/src/codec/SkCodecPriv.h +++ b/src/codec/SkCodecPriv.h @@ -13,6 +13,7 @@ #include "SkColorSpaceXformPriv.h" #include "SkColorTable.h" #include "SkEncodedInfo.h" +#include "SkEncodedOrigin.h" #include "SkImageInfo.h" #include "SkTypes.h" @@ -295,4 +296,6 @@ static inline SkAlphaType select_xform_alpha(SkAlphaType dstAlphaType, SkAlphaTy return (kOpaque_SkAlphaType == srcAlphaType) ? kOpaque_SkAlphaType : dstAlphaType; } +bool is_orientation_marker(const uint8_t* data, size_t data_length, SkEncodedOrigin* orientation); + #endif // SkCodecPriv_DEFINED diff --git a/src/codec/SkJpegCodec.cpp b/src/codec/SkJpegCodec.cpp index 80a32ad75a..d2c023b437 100644 --- a/src/codec/SkJpegCodec.cpp +++ b/src/codec/SkJpegCodec.cpp @@ -49,32 +49,36 @@ static bool is_orientation_marker(jpeg_marker_struct* marker, SkEncodedOrigin* o return false; } - const uint8_t* data = marker->data; constexpr uint8_t kExifSig[] { 'E', 'x', 'i', 'f', '\0' }; - if (memcmp(data, kExifSig, sizeof(kExifSig))) { + if (memcmp(marker->data, kExifSig, sizeof(kExifSig))) { return false; } + // Account for 'E', 'x', 'i', 'f', '\0', ''. + constexpr size_t kOffset = 6; + return is_orientation_marker(marker->data + kOffset, marker->data_length - kOffset, + orientation); +} + +bool is_orientation_marker(const uint8_t* data, size_t data_length, SkEncodedOrigin* orientation) { bool littleEndian; - if (!is_valid_endian_marker(data + 6, &littleEndian)) { + if (!is_valid_endian_marker(data, &littleEndian)) { return false; } // Get the offset from the start of the marker. - // Account for 'E', 'x', 'i', 'f', '\0', ''. // Though this only reads four bytes, use a larger int in case it overflows. - uint64_t offset = get_endian_int(data + 10, littleEndian); - offset += sizeof(kExifSig) + 1; + uint64_t offset = get_endian_int(data + 4, littleEndian); // Require that the marker is at least large enough to contain the number of entries. - if (marker->data_length < offset + 2) { + if (data_length < offset + 2) { return false; } uint32_t numEntries = get_endian_short(data + offset, littleEndian); // Tag (2 bytes), Datatype (2 bytes), Number of elements (4 bytes), Data (4 bytes) const uint32_t kEntrySize = 12; - const auto max = SkTo((marker->data_length - offset - 2) / kEntrySize); + const auto max = SkTo((data_length - offset - 2) / kEntrySize); numEntries = SkTMin(numEntries, max); // Advance the data to the start of the entries. diff --git a/src/codec/SkRawAdapterCodec.cpp b/src/codec/SkRawAdapterCodec.cpp index 49170911a4..d4b3149987 100644 --- a/src/codec/SkRawAdapterCodec.cpp +++ b/src/codec/SkRawAdapterCodec.cpp @@ -9,8 +9,8 @@ #include "SkCodecPriv.h" #include "SkRawAdapterCodec.h" -SkRawAdapterCodec::SkRawAdapterCodec(SkRawCodec* codec) - : INHERITED(codec) +SkRawAdapterCodec::SkRawAdapterCodec(SkRawCodec* codec, ExifOrientationBehavior behavior) + : INHERITED(codec, behavior) {} SkISize SkRawAdapterCodec::onGetSampledDimensions(int sampleSize) const { diff --git a/src/codec/SkRawAdapterCodec.h b/src/codec/SkRawAdapterCodec.h index 29e817c7d8..d4828ce2f7 100644 --- a/src/codec/SkRawAdapterCodec.h +++ b/src/codec/SkRawAdapterCodec.h @@ -21,8 +21,7 @@ */ class SkRawAdapterCodec : public SkAndroidCodec { public: - - explicit SkRawAdapterCodec(SkRawCodec*); + explicit SkRawAdapterCodec(SkRawCodec*, ExifOrientationBehavior); ~SkRawAdapterCodec() override {} diff --git a/src/codec/SkSampledCodec.cpp b/src/codec/SkSampledCodec.cpp index b8e2a56286..ac0539fc74 100644 --- a/src/codec/SkSampledCodec.cpp +++ b/src/codec/SkSampledCodec.cpp @@ -12,8 +12,8 @@ #include "SkSampler.h" #include "SkTemplates.h" -SkSampledCodec::SkSampledCodec(SkCodec* codec) - : INHERITED(codec) +SkSampledCodec::SkSampledCodec(SkCodec* codec, ExifOrientationBehavior behavior) + : INHERITED(codec, behavior) {} SkISize SkSampledCodec::accountForNativeScaling(int* sampleSizePtr, int* nativeSampleSize) const { diff --git a/src/codec/SkSampledCodec.h b/src/codec/SkSampledCodec.h index 4bcf5bcadf..faa955ca28 100644 --- a/src/codec/SkSampledCodec.h +++ b/src/codec/SkSampledCodec.h @@ -16,8 +16,7 @@ */ class SkSampledCodec : public SkAndroidCodec { public: - - explicit SkSampledCodec(SkCodec*); + explicit SkSampledCodec(SkCodec*, ExifOrientationBehavior); ~SkSampledCodec() override {} diff --git a/src/codec/SkWebpAdapterCodec.cpp b/src/codec/SkWebpAdapterCodec.cpp index 18e1a54911..81011d017b 100644 --- a/src/codec/SkWebpAdapterCodec.cpp +++ b/src/codec/SkWebpAdapterCodec.cpp @@ -9,8 +9,8 @@ #include "SkCodecPriv.h" #include "SkWebpAdapterCodec.h" -SkWebpAdapterCodec::SkWebpAdapterCodec(SkWebpCodec* codec) - : INHERITED(codec) +SkWebpAdapterCodec::SkWebpAdapterCodec(SkWebpCodec* codec, ExifOrientationBehavior behavior) + : INHERITED(codec, behavior) {} SkISize SkWebpAdapterCodec::onGetSampledDimensions(int sampleSize) const { diff --git a/src/codec/SkWebpAdapterCodec.h b/src/codec/SkWebpAdapterCodec.h index b2c6c7bd3b..fc8328cf56 100644 --- a/src/codec/SkWebpAdapterCodec.h +++ b/src/codec/SkWebpAdapterCodec.h @@ -17,7 +17,7 @@ class SkWebpAdapterCodec : public SkAndroidCodec { public: - explicit SkWebpAdapterCodec(SkWebpCodec*); + explicit SkWebpAdapterCodec(SkWebpCodec*, ExifOrientationBehavior); ~SkWebpAdapterCodec() override {} diff --git a/src/codec/SkWebpCodec.cpp b/src/codec/SkWebpCodec.cpp index aa3547dca5..d9b13e8f06 100644 --- a/src/codec/SkWebpCodec.cpp +++ b/src/codec/SkWebpCodec.cpp @@ -87,14 +87,25 @@ std::unique_ptr SkWebpCodec::MakeFromStream(std::unique_ptr s } } - WebPChunkIterator chunkIterator; - SkAutoTCallVProc autoCI(&chunkIterator); sk_sp colorSpace = nullptr; - if (WebPDemuxGetChunk(demux, "ICCP", 1, &chunkIterator)) { - colorSpace = SkColorSpace::MakeICC(chunkIterator.chunk.bytes, chunkIterator.chunk.size); + { + WebPChunkIterator chunkIterator; + SkAutoTCallVProc autoCI(&chunkIterator); + if (WebPDemuxGetChunk(demux, "ICCP", 1, &chunkIterator)) { + colorSpace = SkColorSpace::MakeICC(chunkIterator.chunk.bytes, chunkIterator.chunk.size); + } + if (!colorSpace || colorSpace->type() != SkColorSpace::kRGB_Type) { + colorSpace = SkColorSpace::MakeSRGB(); + } } - if (!colorSpace || colorSpace->type() != SkColorSpace::kRGB_Type) { - colorSpace = SkColorSpace::MakeSRGB(); + + SkEncodedOrigin origin = kDefault_SkEncodedOrigin; + { + WebPChunkIterator chunkIterator; + SkAutoTCallVProc autoCI(&chunkIterator); + if (WebPDemuxGetChunk(demux, "EXIF", 1, &chunkIterator)) { + is_orientation_marker(chunkIterator.chunk.bytes, chunkIterator.chunk.size, &origin); + } } // Get the first frame and its "features" to determine the color and alpha types. @@ -156,10 +167,12 @@ std::unique_ptr SkWebpCodec::MakeFromStream(std::unique_ptr s return nullptr; } + *result = kSuccess; SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, 8); return std::unique_ptr(new SkWebpCodec(width, height, info, std::move(colorSpace), - std::move(stream), demux.release(), std::move(data))); + std::move(stream), demux.release(), std::move(data), + origin)); } SkISize SkWebpCodec::onGetScaledDimensions(float desiredScale) const { @@ -646,9 +659,9 @@ SkCodec::Result SkWebpCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, SkWebpCodec::SkWebpCodec(int width, int height, const SkEncodedInfo& info, sk_sp colorSpace, std::unique_ptr stream, - WebPDemuxer* demux, sk_sp data) + WebPDemuxer* demux, sk_sp data, SkEncodedOrigin origin) : INHERITED(width, height, info, SkColorSpaceXform::kBGRA_8888_ColorFormat, std::move(stream), - std::move(colorSpace)) + std::move(colorSpace), origin) , fDemux(demux) , fData(std::move(data)) , fFailed(false) diff --git a/src/codec/SkWebpCodec.h b/src/codec/SkWebpCodec.h index 010771bcff..fdd5422252 100644 --- a/src/codec/SkWebpCodec.h +++ b/src/codec/SkWebpCodec.h @@ -48,7 +48,7 @@ protected: private: SkWebpCodec(int width, int height, const SkEncodedInfo&, sk_sp, - std::unique_ptr, WebPDemuxer*, sk_sp); + std::unique_ptr, WebPDemuxer*, sk_sp, SkEncodedOrigin); SkAutoTCallVProc fDemux; diff --git a/tests/AndroidCodecTest.cpp b/tests/AndroidCodecTest.cpp index b86a210279..d383713bb9 100644 --- a/tests/AndroidCodecTest.cpp +++ b/tests/AndroidCodecTest.cpp @@ -7,7 +7,9 @@ #include "SkAndroidCodec.h" #include "SkCodec.h" +#include "SkCodecImageGenerator.h" #include "SkEncodedImageFormat.h" +#include "SkPixmapPriv.h" #include "Resources.h" #include "Test.h" @@ -117,3 +119,69 @@ DEF_TEST(AndroidCodec_computeSampleSize, r) { } } } + +DEF_TEST(AndroidCodec_orientation, r) { + if (GetResourcePath().isEmpty()) { + return; + } + + for (const char* ext : { "jpg", "webp" }) + for (char i = '1'; i <= '8'; ++i) { + SkString path = SkStringPrintf("images/orientation/%c.%s", i, ext); + auto data = GetResourceAsData(path.c_str()); + auto gen = SkCodecImageGenerator::MakeFromEncodedCodec(data); + if (!gen) { + ERRORF(r, "failed to decode %s", path.c_str()); + return; + } + + // Dimensions after adjusting for the origin. + const SkISize expectedDims = { 100, 80 }; + + // SkCodecImageGenerator automatically adjusts for the origin. + REPORTER_ASSERT(r, gen->getInfo().dimensions() == expectedDims); + + auto androidCodec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(data)); + if (!androidCodec) { + ERRORF(r, "failed to decode %s", path.c_str()); + return; + } + + // SkAndroidCodec does not adjust for the origin by default. Dimensions may be reversed. + if (SkPixmapPriv::ShouldSwapWidthHeight(androidCodec->codec()->getOrigin())) { + auto swappedDims = SkPixmapPriv::SwapWidthHeight(androidCodec->getInfo()).dimensions(); + REPORTER_ASSERT(r, expectedDims == swappedDims); + } else { + REPORTER_ASSERT(r, expectedDims == androidCodec->getInfo().dimensions()); + } + + // Passing kRespect adjusts for the origin. + androidCodec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(std::move(data)), + SkAndroidCodec::ExifOrientationBehavior::kRespect); + auto info = androidCodec->getInfo(); + REPORTER_ASSERT(r, info.dimensions() == expectedDims); + + SkBitmap fromGenerator; + fromGenerator.allocPixels(info); + REPORTER_ASSERT(r, gen->getPixels(info, fromGenerator.getPixels(), + fromGenerator.rowBytes())); + + SkBitmap fromAndroidCodec; + fromAndroidCodec.allocPixels(info); + auto result = androidCodec->getPixels(info, fromAndroidCodec.getPixels(), + fromAndroidCodec.rowBytes()); + REPORTER_ASSERT(r, result == SkCodec::kSuccess); + + for (int i = 0; i < info.width(); ++i) + for (int j = 0; j < info.height(); ++j) { + SkColor c1 = *fromGenerator .getAddr32(i, j); + SkColor c2 = *fromAndroidCodec.getAddr32(i, j); + if (c1 != c2) { + ERRORF(r, "Bitmaps for %s do not match starting at position %i, %i\n" + "\tfromGenerator: %x\tfromAndroidCodec: %x", path.c_str(), i, j, + c1, c2); + return; + } + } + } +} -- cgit v1.2.3