Add SkEncodedOrigin to SkYUVASizeInfo, fix JPEG orientation in GPU decode path

Bug: skia:7523
Change-Id: I7d48e5f5930b413fa7f27aa391bf92c5af1342e9
Reviewed-on: https://skia-review.googlesource.com/c/173429
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: Leon Scroggins <scroggo@google.com>
diff --git a/bench/ReadPixBench.cpp b/bench/ReadPixBench.cpp
index b9a2335..1c1ce04 100644
--- a/bench/ReadPixBench.cpp
+++ b/bench/ReadPixBench.cpp
@@ -89,13 +89,11 @@
     }
 
     void onDraw(int loops, SkCanvas*) override {
-        const SkPixmapPriv::OrientFlags flags = SkPixmapPriv::kSwapXY;
-
         SkPixmap src, dst;
         fSrc.peekPixels(&src);
         fDst.peekPixels(&dst);
         for (int i = 0; i < loops; ++i) {
-            SkPixmapPriv::Orient(dst, src, flags);
+            SkPixmapPriv::Orient(dst, src, kTopRight_SkEncodedOrigin);
         }
     }
 
diff --git a/include/codec/SkEncodedOrigin.h b/include/codec/SkEncodedOrigin.h
index 622ade1..89ed9bb 100644
--- a/include/codec/SkEncodedOrigin.h
+++ b/include/codec/SkEncodedOrigin.h
@@ -7,6 +7,9 @@
 
 #ifndef SkEncodedOrigin_DEFINED
 #define SkEncodedOrigin_DEFINED
+
+#include "SkMatrix.h"
+
 // These values match the orientation www.exif.org/Exif2-2.PDF.
 enum SkEncodedOrigin {
     kTopLeft_SkEncodedOrigin     = 1, // Default
@@ -20,4 +23,26 @@
     kDefault_SkEncodedOrigin     = kTopLeft_SkEncodedOrigin,
     kLast_SkEncodedOrigin        = kLeftBottom_SkEncodedOrigin,
 };
+
+/**
+ * Given an encoded origin and the width and height of the source data, returns a matrix
+ * that transforms the source rectangle [0, 0, w, h] to a correctly oriented destination
+ * rectangle, with the upper left corner still at [0, 0].
+ */
+static inline SkMatrix SkEncodedOriginToMatrix(SkEncodedOrigin origin, int w, int h) {
+    switch (origin) {
+        case     kTopLeft_SkEncodedOrigin: return SkMatrix::I();
+        case    kTopRight_SkEncodedOrigin: return SkMatrix::MakeAll(-1,  0, w,  0,  1, 0, 0, 0, 1);
+        case kBottomRight_SkEncodedOrigin: return SkMatrix::MakeAll(-1,  0, w,  0, -1, h, 0, 0, 1);
+        case  kBottomLeft_SkEncodedOrigin: return SkMatrix::MakeAll( 1,  0, 0,  0, -1, h, 0, 0, 1);
+        case     kLeftTop_SkEncodedOrigin: return SkMatrix::MakeAll( 0,  1, 0,  1,  0, 0, 0, 0, 1);
+        case    kRightTop_SkEncodedOrigin: return SkMatrix::MakeAll( 0, -1, h,  1,  0, 0, 0, 0, 1);
+        case kRightBottom_SkEncodedOrigin: return SkMatrix::MakeAll( 0, -1, h, -1,  0, w, 0, 0, 1);
+        case  kLeftBottom_SkEncodedOrigin: return SkMatrix::MakeAll( 0,  1, 0, -1,  0, w, 0, 0, 1);
+    }
+    SK_ABORT("Unexpected origin");
+    return SkMatrix::I();
+}
+
+
 #endif // SkEncodedOrigin_DEFINED
diff --git a/include/core/SkYUVASizeInfo.h b/include/core/SkYUVASizeInfo.h
index 4f2e2f5..8782ff8 100644
--- a/include/core/SkYUVASizeInfo.h
+++ b/include/core/SkYUVASizeInfo.h
@@ -8,6 +8,7 @@
 #ifndef SkYUVASizeInfo_DEFINED
 #define SkYUVASizeInfo_DEFINED
 
+#include "SkEncodedOrigin.h"
 #include "SkImageInfo.h"
 #include "SkSize.h"
 
@@ -30,6 +31,12 @@
      */
     size_t      fWidthBytes[kMaxCount];
 
+    /**
+     * YUVA data often comes from formats like JPEG that support EXIF orientation.
+     * Code that operates on the raw YUV data often needs to know that orientation.
+     */
+    SkEncodedOrigin fOrigin = kDefault_SkEncodedOrigin;
+
     bool operator==(const SkYUVASizeInfo& that) const {
         for (int i = 0; i < kMaxCount; ++i) {
             SkASSERT((!fSizes[i].isEmpty() && fWidthBytes[i]) ||
diff --git a/src/codec/SkJpegCodec.cpp b/src/codec/SkJpegCodec.cpp
index 77ec70f..ca3ac3b 100644
--- a/src/codec/SkJpegCodec.cpp
+++ b/src/codec/SkJpegCodec.cpp
@@ -854,6 +854,8 @@
     // JPEG never has an alpha channel
     sizeInfo->fSizes[3].fHeight = sizeInfo->fSizes[3].fWidth = sizeInfo->fWidthBytes[3] = 0;
 
+    sizeInfo->fOrigin = this->getOrigin();
+
     if (colorSpace) {
         *colorSpace = kJPEG_SkYUVColorSpace;
     }
diff --git a/src/core/SkPixmap.cpp b/src/core/SkPixmap.cpp
index 8f550f8..316614e 100644
--- a/src/core/SkPixmap.cpp
+++ b/src/core/SkPixmap.cpp
@@ -434,7 +434,7 @@
 
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
-static bool draw_orientation(const SkPixmap& dst, const SkPixmap& src, unsigned flags) {
+static bool draw_orientation(const SkPixmap& dst, const SkPixmap& src, SkEncodedOrigin origin) {
     auto surf = SkSurface::MakeRasterDirect(dst.info(), dst.writable_addr(), dst.rowBytes());
     if (!surf) {
         return false;
@@ -443,26 +443,8 @@
     SkBitmap bm;
     bm.installPixels(src);
 
-    SkMatrix m;
-    m.setIdentity();
+    SkMatrix m = SkEncodedOriginToMatrix(origin, src.width(), src.height());
 
-    SkScalar W = SkIntToScalar(src.width());
-    SkScalar H = SkIntToScalar(src.height());
-    if (flags & SkPixmapPriv::kSwapXY) {
-        SkMatrix s;
-        s.setAll(0, 1, 0, 1, 0, 0, 0, 0, 1);
-        m.postConcat(s);
-        using std::swap;
-        swap(W, H);
-    }
-    if (flags & SkPixmapPriv::kMirrorX) {
-        m.postScale(-1, 1);
-        m.postTranslate(W, 0);
-    }
-    if (flags & SkPixmapPriv::kMirrorY) {
-        m.postScale(1, -1);
-        m.postTranslate(0, H);
-    }
     SkPaint p;
     p.setBlendMode(SkBlendMode::kSrc);
     surf->getCanvas()->concat(m);
@@ -470,8 +452,7 @@
     return true;
 }
 
-bool SkPixmapPriv::Orient(const SkPixmap& dst, const SkPixmap& src, OrientFlags flags) {
-    SkASSERT((flags & ~(kMirrorX | kMirrorY | kSwapXY)) == 0);
+bool SkPixmapPriv::Orient(const SkPixmap& dst, const SkPixmap& src, SkEncodedOrigin origin) {
     if (src.colorType() != dst.colorType()) {
         return false;
     }
@@ -479,7 +460,7 @@
 
     int w = src.width();
     int h = src.height();
-    if (flags & kSwapXY) {
+    if (ShouldSwapWidthHeight(origin)) {
         using std::swap;
         swap(w, h);
     }
@@ -492,34 +473,14 @@
 
     // check for aliasing to self
     if (src.addr() == dst.addr()) {
-        return flags == 0;
+        return kTopLeft_SkEncodedOrigin == origin;
     }
-    return draw_orientation(dst, src, flags);
+    return draw_orientation(dst, src, origin);
 }
 
-#define kMirrorX    SkPixmapPriv::kMirrorX
-#define kMirrorY    SkPixmapPriv::kMirrorY
-#define kSwapXY     SkPixmapPriv::kSwapXY
-
-static constexpr uint8_t gOrientationFlags[] = {
-    0,                              // kTopLeft_SkEncodedOrigin
-    kMirrorX,                       // kTopRight_SkEncodedOrigin
-    kMirrorX | kMirrorY,            // kBottomRight_SkEncodedOrigin
-               kMirrorY,            // kBottomLeft_SkEncodedOrigin
-                          kSwapXY,  // kLeftTop_SkEncodedOrigin
-    kMirrorX            | kSwapXY,  // kRightTop_SkEncodedOrigin
-    kMirrorX | kMirrorY | kSwapXY,  // kRightBottom_SkEncodedOrigin
-               kMirrorY | kSwapXY,  // kLeftBottom_SkEncodedOrigin
-};
-
-SkPixmapPriv::OrientFlags SkPixmapPriv::OriginToOrient(SkEncodedOrigin o) {
-    unsigned io = static_cast<int>(o) - 1;
-    SkASSERT(io < SK_ARRAY_COUNT(gOrientationFlags));
-    return static_cast<SkPixmapPriv::OrientFlags>(gOrientationFlags[io]);
-}
-
-bool SkPixmapPriv::ShouldSwapWidthHeight(SkEncodedOrigin o) {
-    return SkToBool(OriginToOrient(o) & kSwapXY);
+bool SkPixmapPriv::ShouldSwapWidthHeight(SkEncodedOrigin origin) {
+    // The last four SkEncodedOrigin values involve 90 degree rotations
+    return origin >= kLeftTop_SkEncodedOrigin;
 }
 
 SkImageInfo SkPixmapPriv::SwapWidthHeight(const SkImageInfo& info) {
diff --git a/src/core/SkPixmapPriv.h b/src/core/SkPixmapPriv.h
index cced3e2..4f3b3b5 100644
--- a/src/core/SkPixmapPriv.h
+++ b/src/core/SkPixmapPriv.h
@@ -14,20 +14,11 @@
 
 class SkPixmapPriv {
 public:
-    // These flag are applied in this order (swap is applied last)
-    enum OrientFlags {
-        kMirrorX = 1 << 0,
-        kMirrorY = 1 << 1,
-        kSwapXY  = 1 << 2,
-    };
-
-    static OrientFlags OriginToOrient(SkEncodedOrigin);
-
     /**
      *  Copy the pixels in this pixmap into dst, applying the orientation transformations specified
      *  by the flags. If the inputs are invalid, this returns false and no copy is made.
      */
-    static bool Orient(const SkPixmap& dst, const SkPixmap& src, OrientFlags);
+    static bool Orient(const SkPixmap& dst, const SkPixmap& src, SkEncodedOrigin);
 
     static bool ShouldSwapWidthHeight(SkEncodedOrigin o);
     static SkImageInfo SwapWidthHeight(const SkImageInfo& info);
@@ -59,7 +50,7 @@
             return false;
         }
         if (tmp != &dst) {
-            return Orient(dst, *tmp, OriginToOrient(origin));
+            return Orient(dst, *tmp, origin);
         }
         return true;
     }
@@ -71,4 +62,3 @@
 };
 
 #endif
-
diff --git a/src/gpu/GrYUVProvider.cpp b/src/gpu/GrYUVProvider.cpp
index 16027df..6b3eb88 100644
--- a/src/gpu/GrYUVProvider.cpp
+++ b/src/gpu/GrYUVProvider.cpp
@@ -182,7 +182,8 @@
     const SkRect r = SkRect::MakeIWH(yuvSizeInfo.fSizes[0].fWidth,
                                      yuvSizeInfo.fSizes[0].fHeight);
 
-    renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), r);
+    SkMatrix m = SkEncodedOriginToMatrix(yuvSizeInfo.fOrigin, r.width(), r.height());
+    renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, m, r);
 
     return renderTargetContext->asTextureProxyRef();
 }