Test YUV images in DDL

The main two CLs calved off (and landed independently) from this CL are:

https://skia-review.googlesource.com/c/skia/+/156761 (Add SkImage_Gpu::MakePromiseYUVATexture)
  -- adds internal place holder for YUVA promise images (only returns Y channel)

https://skia-review.googlesource.com/c/skia/+/156140 (Add SkImage_Base API to access planar data)
  -- adds ability to grab planes for testing purposes (not externally visible)

Bug: skia:7903
Bug: skia:8424
Change-Id: Id0f2f84851dacc66c2c266a30cafa0b628b12eb4
Reviewed-on: https://skia-review.googlesource.com/151983
Reviewed-by: Greg Daniel <egdaniel@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
diff --git a/tools/DDLPromiseImageHelper.cpp b/tools/DDLPromiseImageHelper.cpp
index 0d4e0ff..51225b3 100644
--- a/tools/DDLPromiseImageHelper.cpp
+++ b/tools/DDLPromiseImageHelper.cpp
@@ -10,7 +10,11 @@
 #include "GrContext.h"
 #include "GrContextPriv.h"
 #include "GrGpu.h"
+#include "SkCachedData.h"
 #include "SkDeferredDisplayListRecorder.h"
+#include "SkImage_Base.h"
+#include "SkImagePriv.h"
+#include "SkYUVSizeInfo.h"
 
 DDLPromiseImageHelper::PromiseImageCallbackContext::~PromiseImageCallbackContext() {
     GrGpu* gpu = fContext->contextPriv().getGpu();
@@ -26,6 +30,8 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
+DDLPromiseImageHelper::~DDLPromiseImageHelper() {}
+
 sk_sp<SkData> DDLPromiseImageHelper::deflateSKP(const SkPicture* inputPicture) {
     SkSerialProcs procs;
 
@@ -50,23 +56,45 @@
     SkASSERT(gpu);
 
     for (int i = 0; i < fImageInfo.count(); ++i) {
-        sk_sp<PromiseImageCallbackContext> callbackContext(
-                                                new PromiseImageCallbackContext(context));
-
         const PromiseImageInfo& info = fImageInfo[i];
 
         // DDL TODO: how can we tell if we need mipmapping!
-        callbackContext->setBackendTexture(gpu->createTestingOnlyBackendTexture(
-                                                            info.fBitmap.getPixels(),
-                                                            info.fBitmap.width(),
-                                                            info.fBitmap.height(),
-                                                            info.fBitmap.colorType(),
-                                                            false, GrMipMapped::kNo));
-        // The GMs sometimes request too large an image
-        //SkAssertResult(callbackContext->backendTexture().isValid());
+        if (info.isYUV()) {
+            for (int j = 0; j < 3; ++j) {
+                const SkPixmap& yuvPixmap = info.yuvPixmap(j);
 
-        // The fImageInfo array gets the creation ref
-        fImageInfo[i].fCallbackContext = std::move(callbackContext);
+                sk_sp<PromiseImageCallbackContext> callbackContext(
+                                                        new PromiseImageCallbackContext(context));
+                callbackContext->setBackendTexture(gpu->createTestingOnlyBackendTexture(
+                                                            yuvPixmap.addr(),
+                                                            yuvPixmap.width(),
+                                                            yuvPixmap.height(),
+                                                            yuvPixmap.colorType(),
+                                                            false, GrMipMapped::kNo,
+                                                            yuvPixmap.rowBytes()));
+                SkAssertResult(callbackContext->backendTexture().isValid());
+
+                fImageInfo[i].setCallbackContext(j, std::move(callbackContext));
+            }
+        } else {
+            sk_sp<PromiseImageCallbackContext> callbackContext(
+                                                    new PromiseImageCallbackContext(context));
+
+            const SkBitmap& bm = info.normalBitmap();
+
+            callbackContext->setBackendTexture(gpu->createTestingOnlyBackendTexture(
+                                                                bm.getPixels(),
+                                                                bm.width(),
+                                                                bm.height(),
+                                                                bm.colorType(),
+                                                                false, GrMipMapped::kNo,
+                                                                bm.rowBytes()));
+            // The GMs sometimes request too large an image
+            //SkAssertResult(callbackContext->backendTexture().isValid());
+
+            fImageInfo[i].setCallbackContext(0, std::move(callbackContext));
+        }
+
     }
 }
 
@@ -99,34 +127,71 @@
 
     const DDLPromiseImageHelper::PromiseImageInfo& curImage = helper->getInfo(*indexPtr);
 
-    if (!curImage.fCallbackContext->backendTexture().isValid()) {
+    if (!curImage.backendTexture(0).isValid()) {
+        SkASSERT(!curImage.isYUV());
         // We weren't able to make a backend texture for this SkImage. In this case we create
         // a separate bitmap-backed image for each thread.
-        SkASSERT(curImage.fBitmap.isImmutable());
-        return SkImage::MakeFromBitmap(curImage.fBitmap);
+        SkASSERT(curImage.normalBitmap().isImmutable());
+        return SkImage::MakeFromBitmap(curImage.normalBitmap());
     }
-    SkASSERT(curImage.fIndex == *indexPtr);
+    SkASSERT(curImage.index() == *indexPtr);
 
-    const GrCaps* caps = curImage.fCallbackContext->caps();
-    const GrBackendTexture& backendTex = curImage.fCallbackContext->backendTexture();
-    GrBackendFormat backendFormat = caps->createFormatFromBackendTexture(backendTex);
+    const GrCaps* caps = curImage.caps();
 
-    // Each DDL recorder gets its own ref on the promise callback context for the
-    // promise images it creates.
-    // DDL TODO: sort out mipmapping
-    sk_sp<SkImage> image = recorder->makePromiseTexture(
-                                            backendFormat,
-                                            curImage.fBitmap.width(),
-                                            curImage.fBitmap.height(),
-                                            GrMipMapped::kNo,
-                                            GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
-                                            curImage.fBitmap.colorType(),
-                                            curImage.fBitmap.alphaType(),
-                                            curImage.fBitmap.refColorSpace(),
-                                            DDLPromiseImageHelper::PromiseImageFulfillProc,
-                                            DDLPromiseImageHelper::PromiseImageReleaseProc,
-                                            DDLPromiseImageHelper::PromiseImageDoneProc,
-                                            (void*) SkSafeRef(curImage.fCallbackContext.get()));
+    sk_sp<SkImage> image;
+    if (curImage.isYUV()) {
+        GrBackendFormat backendFormats[4];
+        void* contexts[4] = { nullptr, nullptr, nullptr, nullptr };
+
+        for (int i = 0; i < 3; ++i) {
+            const GrBackendTexture& backendTex = curImage.backendTexture(i);
+            backendFormats[i] = caps->createFormatFromBackendTexture(backendTex);
+
+            contexts[i] = curImage.refCallbackContext(i).release();
+        }
+
+        SkYUVAIndex yuvaIndices[4] = {
+                SkYUVAIndex{0, SkImageSourceChannel::kA_SkImageSourceChannel},
+                SkYUVAIndex{1, SkImageSourceChannel::kA_SkImageSourceChannel},
+                SkYUVAIndex{2, SkImageSourceChannel::kA_SkImageSourceChannel},
+                SkYUVAIndex{-1, SkImageSourceChannel::kA_SkImageSourceChannel}
+        };
+
+        int tempWidth = curImage.backendTexture(0).width();
+        int tempHeight = curImage.backendTexture(0).height();
+
+        image = recorder->makeYUVAPromiseTexture(curImage.yuvColorSpace(),
+                                                 backendFormats,
+                                                 yuvaIndices,
+                                                 tempWidth,  //curImage.overallWidth(),
+                                                 tempHeight, //curImage.overallHeight(),
+                                                 GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
+                                                 curImage.refOverallColorSpace(),
+                                                 DDLPromiseImageHelper::PromiseImageFulfillProc,
+                                                 DDLPromiseImageHelper::PromiseImageReleaseProc,
+                                                 DDLPromiseImageHelper::PromiseImageDoneProc,
+                                                 contexts);
+
+    } else {
+        const GrBackendTexture& backendTex = curImage.backendTexture(0);
+        GrBackendFormat backendFormat = caps->createFormatFromBackendTexture(backendTex);
+
+        // Each DDL recorder gets its own ref on the promise callback context for the
+        // promise images it creates.
+        // DDL TODO: sort out mipmapping
+        image = recorder->makePromiseTexture(backendFormat,
+                                             curImage.overallWidth(),
+                                             curImage.overallHeight(),
+                                             GrMipMapped::kNo,
+                                             GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
+                                             curImage.overallColorType(),
+                                             curImage.overallAlphaType(),
+                                             curImage.refOverallColorSpace(),
+                                             DDLPromiseImageHelper::PromiseImageFulfillProc,
+                                             DDLPromiseImageHelper::PromiseImageReleaseProc,
+                                             DDLPromiseImageHelper::PromiseImageDoneProc,
+                                             (void*) curImage.refCallbackContext(0).release());
+    }
     perRecorderContext->fPromiseImages->push_back(image);
     SkASSERT(image);
     return image;
@@ -134,9 +199,9 @@
 
 int DDLPromiseImageHelper::findImage(SkImage* image) const {
     for (int i = 0; i < fImageInfo.count(); ++i) {
-        if (fImageInfo[i].fOriginalUniqueID == image->uniqueID()) { // trying to dedup here
-            SkASSERT(fImageInfo[i].fIndex == i);
-            SkASSERT(this->isValidID(i) && this->isValidID(fImageInfo[i].fIndex));
+        if (fImageInfo[i].originalUniqueID() == image->uniqueID()) { // trying to dedup here
+            SkASSERT(fImageInfo[i].index() == i);
+            SkASSERT(this->isValidID(i) && this->isValidID(fImageInfo[i].index()));
             return i;
         }
     }
@@ -144,26 +209,42 @@
 }
 
 int DDLPromiseImageHelper::addImage(SkImage* image) {
-    sk_sp<SkImage> rasterImage = image->makeRasterImage(); // force decoding of lazy images
+    SkImage_Base* ib = as_IB(image);
 
-    SkImageInfo ii = SkImageInfo::Make(rasterImage->width(), rasterImage->height(),
-                                        rasterImage->colorType(), rasterImage->alphaType(),
-                                        rasterImage->refColorSpace());
+    SkImageInfo overallII = SkImageInfo::Make(image->width(), image->height(),
+                                              image->colorType(), image->alphaType(),
+                                              image->refColorSpace());
 
-    SkBitmap bm;
-    bm.allocPixels(ii);
+    PromiseImageInfo& newImageInfo = fImageInfo.emplace_back(fImageInfo.count(),
+                                                             image->uniqueID(),
+                                                             overallII);
 
-    if (!rasterImage->readPixels(bm.pixmap(), 0, 0)) {
-        return -1;
+    SkYUVSizeInfo yuvSizeInfo;
+    SkYUVColorSpace yuvColorSpace;
+    const void* planes[3];
+    sk_sp<SkCachedData> yuvData = ib->getPlanes(&yuvSizeInfo, &yuvColorSpace, planes);
+    if (yuvData) {
+        newImageInfo.setYUVData(std::move(yuvData), yuvColorSpace);
+
+        for (int i = 0; i < 3; ++i) {
+            SkImageInfo planeII = SkImageInfo::MakeA8(yuvSizeInfo.fSizes[i].fWidth,
+                                                      yuvSizeInfo.fSizes[i].fHeight);
+            newImageInfo.addYUVPlane(i, planeII, planes[i], yuvSizeInfo.fWidthBytes[i]);
+        }
+    } else {
+        sk_sp<SkImage> rasterImage = image->makeRasterImage(); // force decoding of lazy images
+
+        SkBitmap tmp;
+        tmp.allocPixels(overallII);
+
+        if (!rasterImage->readPixels(tmp.pixmap(), 0, 0)) {
+            return -1;
+        }
+
+        tmp.setImmutable();
+        newImageInfo.setNormalBitmap(tmp);
     }
-
-    bm.setImmutable();
-
-    PromiseImageInfo& newImageInfo = fImageInfo.push_back();
-    newImageInfo.fIndex = fImageInfo.count()-1;
-    newImageInfo.fOriginalUniqueID = image->uniqueID();
-    newImageInfo.fBitmap = bm;
-    /* fCallbackContext is filled in by uploadAllToGPU */
+    // In either case newImageInfo's PromiseImageCallbackContext is filled in by uploadAllToGPU
 
     return fImageInfo.count()-1;
 }