Handle EXIF orientation in hwui/ImageDecoder

Bug: 160984428
Test: AImageDecoderTest#testRespectOrientation
       ImageDecoderTest#testRespectOrientation
      Ieda439910ae52e609f0710d424503616d99ae5c7
      I23caef26b4c82173c758dd0ce7fb6f04e4154588
      I345a13d20776a007052d32e74fa42865b42f726d

It is possible to create an animated image with an exif orientation.
Using kRespect, there is no clean way to handle the orientation plus
compositing frames. Switch ImageDecoder to use kIgnore (the default).

Depends on a change in Skia (https://review.skia.org/344762) to make
SkAnimatedImage handle the orientation even with kIgnore.

Change-Id: Ib93b0ced09fa3cca4a6681745406355c48158fae
diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp
index 43cc4f2..6889134 100644
--- a/libs/hwui/hwui/ImageDecoder.cpp
+++ b/libs/hwui/hwui/ImageDecoder.cpp
@@ -40,13 +40,14 @@
 ImageDecoder::ImageDecoder(std::unique_ptr<SkAndroidCodec> codec, sk_sp<SkPngChunkReader> peeker)
     : mCodec(std::move(codec))
     , mPeeker(std::move(peeker))
-    , mTargetSize(mCodec->getInfo().dimensions())
-    , mDecodeSize(mTargetSize)
+    , mDecodeSize(mCodec->codec()->dimensions())
     , mOutColorType(mCodec->computeOutputColorType(kN32_SkColorType))
     , mUnpremultipliedRequired(false)
     , mOutColorSpace(getDefaultColorSpace())
     , mSampleSize(1)
 {
+    mTargetSize = swapWidthHeight() ? SkISize { mDecodeSize.height(), mDecodeSize.width() }
+                                    : mDecodeSize;
 }
 
 SkAlphaType ImageDecoder::getOutAlphaType() const {
@@ -77,7 +78,8 @@
         }
     }
 
-    SkISize targetSize = { width, height }, decodeSize = targetSize;
+    SkISize targetSize = { width, height };
+    SkISize decodeSize = swapWidthHeight() ? SkISize { height, width } : targetSize;
     int sampleSize = mCodec->computeSampleSize(&decodeSize);
 
     if (decodeSize != targetSize && mUnpremultipliedRequired && !opaque()) {
@@ -157,6 +159,22 @@
     return SkImageInfo::Make(size, mOutColorType, getOutAlphaType(), getOutputColorSpace());
 }
 
+bool ImageDecoder::swapWidthHeight() const {
+    return SkEncodedOriginSwapsWidthHeight(mCodec->codec()->getOrigin());
+}
+
+int ImageDecoder::width() const {
+    return swapWidthHeight()
+            ? mCodec->codec()->dimensions().height()
+            : mCodec->codec()->dimensions().width();
+}
+
+int ImageDecoder::height() const {
+    return swapWidthHeight()
+            ? mCodec->codec()->dimensions().width()
+            : mCodec->codec()->dimensions().height();
+}
+
 bool ImageDecoder::opaque() const {
     return mCodec->getInfo().alphaType() == kOpaque_SkAlphaType;
 }
@@ -174,7 +192,9 @@
     // FIXME: Use scanline decoding on only a couple lines to save memory. b/70709380.
     SkBitmap tmp;
     const bool scale = mDecodeSize != mTargetSize;
-    if (scale || mCropRect) {
+    const auto origin = mCodec->codec()->getOrigin();
+    const bool handleOrigin = origin != kDefault_SkEncodedOrigin;
+    if (scale || handleOrigin || mCropRect) {
         if (!tmp.setInfo(decodeInfo)) {
             return SkCodec::kInternalError;
         }
@@ -189,7 +209,7 @@
     options.fSampleSize = mSampleSize;
     auto result = mCodec->getAndroidPixels(decodeInfo, decodePixels, decodeRowBytes, &options);
 
-    if (scale || mCropRect) {
+    if (scale || handleOrigin || mCropRect) {
         SkBitmap scaledBm;
         if (!scaledBm.installPixels(getOutputInfo(), pixels, rowBytes)) {
             return SkCodec::kInternalError;
@@ -200,15 +220,27 @@
         paint.setFilterQuality(kLow_SkFilterQuality);  // bilinear filtering
 
         SkCanvas canvas(scaledBm, SkCanvas::ColorBehavior::kLegacy);
+        SkMatrix outputMatrix;
         if (mCropRect) {
-            canvas.translate(-mCropRect->fLeft, -mCropRect->fTop);
-        }
-        if (scale) {
-            float scaleX = (float) mTargetSize.width()  / mDecodeSize.width();
-            float scaleY = (float) mTargetSize.height() / mDecodeSize.height();
-            canvas.scale(scaleX, scaleY);
+            outputMatrix.setTranslate(-mCropRect->fLeft, -mCropRect->fTop);
         }
 
+        int targetWidth  = mTargetSize.width();
+        int targetHeight = mTargetSize.height();
+        if (handleOrigin) {
+            outputMatrix.preConcat(SkEncodedOriginToMatrix(origin, targetWidth, targetHeight));
+            if (SkEncodedOriginSwapsWidthHeight(origin)) {
+                std::swap(targetWidth, targetHeight);
+            }
+        }
+
+        if (scale) {
+            float scaleX = (float) targetWidth  / mDecodeSize.width();
+            float scaleY = (float) targetHeight / mDecodeSize.height();
+            outputMatrix.preScale(scaleX, scaleY);
+        }
+
+        canvas.setMatrix(outputMatrix);
         canvas.drawBitmap(tmp, 0.0f, 0.0f, &paint);
     }
 
diff --git a/libs/hwui/hwui/ImageDecoder.h b/libs/hwui/hwui/ImageDecoder.h
index a1b5157..a08e924 100644
--- a/libs/hwui/hwui/ImageDecoder.h
+++ b/libs/hwui/hwui/ImageDecoder.h
@@ -49,7 +49,11 @@
     // The size is the final size after scaling and cropping.
     SkImageInfo getOutputInfo() const;
 
+    int width() const;
+    int height() const;
+
     bool opaque() const;
+
     bool gray() const;
 
     SkCodec::Result decode(void* pixels, size_t rowBytes);
@@ -68,6 +72,7 @@
 
     SkAlphaType getOutAlphaType() const;
     sk_sp<SkColorSpace> getOutputColorSpace() const;
+    bool swapWidthHeight() const;
 };
 
 } // namespace android
diff --git a/libs/hwui/jni/ImageDecoder.cpp b/libs/hwui/jni/ImageDecoder.cpp
index da91d46..96e912f 100644
--- a/libs/hwui/jni/ImageDecoder.cpp
+++ b/libs/hwui/jni/ImageDecoder.cpp
@@ -135,19 +135,15 @@
         return throw_exception(env, kSourceException, "", jexception, source);
     }
 
-    auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec),
-            SkAndroidCodec::ExifOrientationBehavior::kRespect);
+    auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
     if (!androidCodec.get()) {
         return throw_exception(env, kSourceMalformedData, "", nullptr, source);
     }
 
-    const auto& info = androidCodec->getInfo();
-    const int width = info.width();
-    const int height = info.height();
     const bool isNinePatch = peeker->mPatch != nullptr;
     ImageDecoder* decoder = new ImageDecoder(std::move(androidCodec), std::move(peeker));
     return env->NewObject(gImageDecoder_class, gImageDecoder_constructorMethodID,
-                          reinterpret_cast<jlong>(decoder), width, height,
+                          reinterpret_cast<jlong>(decoder), decoder->width(), decoder->height(),
                           animated, isNinePatch);
 }
 
diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp
index ac4c16a..4aeebe4 100644
--- a/native/graphics/jni/imagedecoder.cpp
+++ b/native/graphics/jni/imagedecoder.cpp
@@ -65,17 +65,18 @@
     SkCodec::Result result;
     auto codec = SkCodec::MakeFromStream(std::move(stream), &result, nullptr,
                                          SkCodec::SelectionPolicy::kPreferAnimation);
-    auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec),
-            SkAndroidCodec::ExifOrientationBehavior::kRespect);
+    // These may be swapped due to the SkEncodedOrigin, but we're just checking
+    // them to make sure they fit in int32_t.
+    auto dimensions = codec->dimensions();
+    auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
     if (!androidCodec) {
         return ResultToErrorCode(result);
     }
 
     // AImageDecoderHeaderInfo_getWidth/Height return an int32_t. Ensure that
     // the conversion is safe.
-    const auto& info = androidCodec->getInfo();
-    if (info.width() > std::numeric_limits<int32_t>::max()
-        || info.height() > std::numeric_limits<int32_t>::max()) {
+    if (dimensions.width() > std::numeric_limits<int32_t>::max() ||
+        dimensions.height() > std::numeric_limits<int32_t>::max()) {
         return ANDROID_IMAGE_DECODER_INVALID_INPUT;
     }
 
@@ -200,14 +201,14 @@
     if (!info) {
         return 0;
     }
-    return toDecoder(info)->mCodec->getInfo().width();
+    return toDecoder(info)->width();
 }
 
 int32_t AImageDecoderHeaderInfo_getHeight(const AImageDecoderHeaderInfo* info) {
     if (!info) {
         return 0;
     }
-    return toDecoder(info)->mCodec->getInfo().height();
+    return toDecoder(info)->height();
 }
 
 const char* AImageDecoderHeaderInfo_getMimeType(const AImageDecoderHeaderInfo* info) {