Add SkImage_Base API to access planar data

In order to test YUV promise images we need to be able to access the planes.

Split out of: https://skia-review.googlesource.com/c/skia/+/151983 (Test YUV images in DDL)

Bug: skia:7903
Change-Id: If232a5fdeb90833da6db727b1997a3c744bcd9dd
Reviewed-on: https://skia-review.googlesource.com/156140
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
diff --git a/src/gpu/GrYUVProvider.cpp b/src/gpu/GrYUVProvider.cpp
index 9ffd8a7..a5b71dd 100644
--- a/src/gpu/GrYUVProvider.cpp
+++ b/src/gpu/GrYUVProvider.cpp
@@ -20,43 +20,53 @@
 #include "SkYUVPlanesCache.h"
 #include "effects/GrYUVtoRGBEffect.h"
 
-sk_sp<SkCachedData> init_provider(GrYUVProvider* provider, SkYUVPlanesCache::Info* yuvInfo,
-                                  void* planes[3]) {
+sk_sp<SkCachedData> GrYUVProvider::getPlanes(SkYUVSizeInfo* size,
+                                             SkYUVColorSpace* colorSpace,
+                                             const void* constPlanes[3]) {
     sk_sp<SkCachedData> data;
-    data.reset(SkYUVPlanesCache::FindAndRef(provider->onGetID(), yuvInfo));
+    SkYUVPlanesCache::Info yuvInfo;
+    data.reset(SkYUVPlanesCache::FindAndRef(this->onGetID(), &yuvInfo));
+
+    void* planes[3];
 
     if (data.get()) {
         planes[0] = (void*)data->data();
-        planes[1] = (uint8_t*)planes[0] + (yuvInfo->fSizeInfo.fWidthBytes[SkYUVSizeInfo::kY] *
-                                           yuvInfo->fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight);
-        planes[2] = (uint8_t*)planes[1] + (yuvInfo->fSizeInfo.fWidthBytes[SkYUVSizeInfo::kU] *
-                                           yuvInfo->fSizeInfo.fSizes[SkYUVSizeInfo::kU].fHeight);
+        planes[1] = (uint8_t*)planes[0] + (yuvInfo.fSizeInfo.fWidthBytes[SkYUVSizeInfo::kY] *
+                                           yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight);
+        planes[2] = (uint8_t*)planes[1] + (yuvInfo.fSizeInfo.fWidthBytes[SkYUVSizeInfo::kU] *
+                                           yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kU].fHeight);
     } else {
         // Fetch yuv plane sizes for memory allocation.
-        if (!provider->onQueryYUV8(&yuvInfo->fSizeInfo, &yuvInfo->fColorSpace)) {
+        if (!this->onQueryYUV8(&yuvInfo.fSizeInfo, &yuvInfo.fColorSpace)) {
             return nullptr;
         }
 
         // Allocate the memory for YUV
         size_t totalSize(0);
         for (int i = 0; i < 3; i++) {
-            totalSize += yuvInfo->fSizeInfo.fWidthBytes[i] * yuvInfo->fSizeInfo.fSizes[i].fHeight;
+            totalSize += yuvInfo.fSizeInfo.fWidthBytes[i] * yuvInfo.fSizeInfo.fSizes[i].fHeight;
         }
         data.reset(SkResourceCache::NewCachedData(totalSize));
         planes[0] = data->writable_data();
-        planes[1] = (uint8_t*)planes[0] + (yuvInfo->fSizeInfo.fWidthBytes[SkYUVSizeInfo::kY] *
-                                           yuvInfo->fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight);
-        planes[2] = (uint8_t*)planes[1] + (yuvInfo->fSizeInfo.fWidthBytes[SkYUVSizeInfo::kU] *
-                                           yuvInfo->fSizeInfo.fSizes[SkYUVSizeInfo::kU].fHeight);
+        planes[1] = (uint8_t*)planes[0] + (yuvInfo.fSizeInfo.fWidthBytes[SkYUVSizeInfo::kY] *
+                                           yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight);
+        planes[2] = (uint8_t*)planes[1] + (yuvInfo.fSizeInfo.fWidthBytes[SkYUVSizeInfo::kU] *
+                                           yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kU].fHeight);
 
         // Get the YUV planes.
-        if (!provider->onGetYUV8Planes(yuvInfo->fSizeInfo, planes)) {
+        if (!this->onGetYUV8Planes(yuvInfo.fSizeInfo, planes)) {
             return nullptr;
         }
 
         // Decoding is done, cache the resulting YUV planes
-        SkYUVPlanesCache::Add(provider->onGetID(), data.get(), yuvInfo);
+        SkYUVPlanesCache::Add(this->onGetID(), data.get(), &yuvInfo);
     }
+
+    *size = yuvInfo.fSizeInfo;
+    *colorSpace = yuvInfo.fColorSpace;
+    constPlanes[0] = planes[0];
+    constPlanes[1] = planes[1];
+    constPlanes[2] = planes[2];
     return data;
 }
 
@@ -69,28 +79,29 @@
 sk_sp<GrTextureProxy> GrYUVProvider::refAsTextureProxy(GrContext* ctx, const GrSurfaceDesc& desc,
                                                        SkColorSpace* srcColorSpace,
                                                        SkColorSpace* dstColorSpace) {
-    SkYUVPlanesCache::Info yuvInfo;
-    void* planes[3];
+    SkYUVSizeInfo yuvSizeInfo;
+    SkYUVColorSpace yuvColorSpace;
+    const void* planes[3];
 
-    sk_sp<SkCachedData>  dataStorage = init_provider(this, &yuvInfo, planes);
+    sk_sp<SkCachedData> dataStorage = this->getPlanes(&yuvSizeInfo, &yuvColorSpace, planes);
     if (!dataStorage) {
         return nullptr;
     }
 
     sk_sp<GrTextureProxy> yuvTextureProxies[3];
     for (int i = 0; i < 3; i++) {
-        int componentWidth  = yuvInfo.fSizeInfo.fSizes[i].fWidth;
-        int componentHeight = yuvInfo.fSizeInfo.fSizes[i].fHeight;
+        int componentWidth  = yuvSizeInfo.fSizes[i].fWidth;
+        int componentHeight = yuvSizeInfo.fSizes[i].fHeight;
         // If the sizes of the components are not all the same we choose to create exact-match
-        // textures for the smaller onces rather than add a texture domain to the draw.
-        // TODO: revisit this decision to imporve texture reuse?
+        // textures for the smaller ones rather than add a texture domain to the draw.
+        // TODO: revisit this decision to improve texture reuse?
         SkBackingFit fit =
-                (componentWidth  != yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fWidth) ||
-                (componentHeight != yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight)
+                (componentWidth  != yuvSizeInfo.fSizes[SkYUVSizeInfo::kY].fWidth) ||
+                (componentHeight != yuvSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight)
                     ? SkBackingFit::kExact : SkBackingFit::kApprox;
 
         SkImageInfo imageInfo = SkImageInfo::MakeA8(componentWidth, componentHeight);
-        SkPixmap pixmap(imageInfo, planes[i], yuvInfo.fSizeInfo.fWidthBytes[i]);
+        SkPixmap pixmap(imageInfo, planes[i], yuvSizeInfo.fWidthBytes[i]);
         SkCachedData* dataStoragePtr = dataStorage.get();
         // We grab a ref to cached yuv data. When the SkImage we create below goes away it will call
         // the YUVGen_DataReleaseProc which will release this ref.
@@ -106,8 +117,8 @@
         yuvTextureProxies[i] = proxyProvider->createTextureProxy(yuvImage, kNone_GrSurfaceFlags,
                                                                  1, SkBudgeted::kYes, fit);
 
-        SkASSERT(yuvTextureProxies[i]->width() == yuvInfo.fSizeInfo.fSizes[i].fWidth);
-        SkASSERT(yuvTextureProxies[i]->height() == yuvInfo.fSizeInfo.fSizes[i].fHeight);
+        SkASSERT(yuvTextureProxies[i]->width() == yuvSizeInfo.fSizes[i].fWidth);
+        SkASSERT(yuvTextureProxies[i]->height() == yuvSizeInfo.fSizes[i].fHeight);
     }
 
     // TODO: investigate preallocating mip maps here
@@ -124,7 +135,7 @@
             GrYUVtoRGBEffect::Make(std::move(yuvTextureProxies[0]),
                                    std::move(yuvTextureProxies[1]),
                                    std::move(yuvTextureProxies[2]),
-                                   yuvInfo.fColorSpace, false);
+                                   yuvColorSpace, false);
     paint.addColorFragmentProcessor(std::move(yuvToRgbProcessor));
 
     // If the caller expects the pixels in a different color space than the one from the image,
@@ -136,8 +147,8 @@
     }
 
     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
-    const SkRect r = SkRect::MakeIWH(yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fWidth,
-                                     yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight);
+    const SkRect r = SkRect::MakeIWH(yuvSizeInfo.fSizes[SkYUVSizeInfo::kY].fWidth,
+                                     yuvSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight);
 
     renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), r);
 
diff --git a/src/gpu/GrYUVProvider.h b/src/gpu/GrYUVProvider.h
index 42a823a..de6ce33 100644
--- a/src/gpu/GrYUVProvider.h
+++ b/src/gpu/GrYUVProvider.h
@@ -16,6 +16,7 @@
 struct GrSurfaceDesc;
 class GrTexture;
 class GrTextureProxy;
+class SkCachedData;
 
 /**
  *  There are at least 2 different ways to extract/retrieve YUV planar data...
@@ -42,7 +43,10 @@
                                             SkColorSpace* srcColorSpace,
                                             SkColorSpace* dstColorSpace);
 
-    virtual uint32_t onGetID() = 0;
+    sk_sp<SkCachedData> getPlanes(SkYUVSizeInfo*, SkYUVColorSpace*, const void* planes[3]);
+
+private:
+    virtual uint32_t onGetID() const = 0;
 
     // These are not meant to be called by a client, only by the implementation
 
@@ -68,7 +72,6 @@
      */
     virtual bool onGetYUV8Planes(const SkYUVSizeInfo& sizeInfo, void* planes[3]) = 0;
 
-private:
     // This is used as release callback for the YUV data that we capture in an SkImage when
     // uploading to a gpu. When the upload is complete and we release the SkImage this callback will
     // release the underlying data.
diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp
index 4c2c6db..00b7117 100644
--- a/src/image/SkImage.cpp
+++ b/src/image/SkImage.cpp
@@ -227,6 +227,10 @@
     return as_IB(this)->onAsLegacyBitmap(bitmap);
 }
 
+sk_sp<SkCachedData> SkImage_Base::getPlanes(SkYUVSizeInfo*, SkYUVColorSpace*,const void*[3]) {
+    return nullptr;
+}
+
 bool SkImage_Base::onAsLegacyBitmap(SkBitmap* bitmap) const {
     // As the base-class, all we can do is make a copy (regardless of mode).
     // Subclasses that want to be more optimal should override.
diff --git a/src/image/SkImage_Base.h b/src/image/SkImage_Base.h
index d9c738a..3adf51e 100644
--- a/src/image/SkImage_Base.h
+++ b/src/image/SkImage_Base.h
@@ -22,6 +22,8 @@
 #include <new>
 
 class GrSamplerState;
+class SkCachedData;
+struct SkYUVSizeInfo;
 
 enum {
     kNeedNewImageUniqueID = 0
@@ -72,6 +74,7 @@
 
     virtual sk_sp<SkImage> onMakeSubset(const SkIRect&) const = 0;
 
+    virtual sk_sp<SkCachedData> getPlanes(SkYUVSizeInfo*, SkYUVColorSpace*, const void* planes[3]);
     virtual sk_sp<SkData> onRefEncoded() const { return nullptr; }
 
     virtual bool onAsLegacyBitmap(SkBitmap*) const;
diff --git a/src/image/SkImage_Lazy.cpp b/src/image/SkImage_Lazy.cpp
index 15722fe..6aa0ca0 100644
--- a/src/image/SkImage_Lazy.cpp
+++ b/src/image/SkImage_Lazy.cpp
@@ -351,18 +351,21 @@
 }
 
 class Generator_GrYUVProvider : public GrYUVProvider {
-    SkImageGenerator* fGen;
-
 public:
     Generator_GrYUVProvider(SkImageGenerator* gen) : fGen(gen) {}
 
-    uint32_t onGetID() override { return fGen->uniqueID(); }
+private:
+    uint32_t onGetID() const override { return fGen->uniqueID(); }
     bool onQueryYUV8(SkYUVSizeInfo* sizeInfo, SkYUVColorSpace* colorSpace) const override {
         return fGen->queryYUV8(sizeInfo, colorSpace);
     }
     bool onGetYUV8Planes(const SkYUVSizeInfo& sizeInfo, void* planes[3]) override {
         return fGen->getYUV8Planes(sizeInfo, planes);
     }
+
+    SkImageGenerator* fGen;
+
+    typedef GrYUVProvider INHERITED;
 };
 
 static void set_key_on_proxy(GrProxyProvider* proxyProvider,
@@ -383,6 +386,21 @@
     }
 }
 
+sk_sp<SkCachedData> SkImage_Lazy::getPlanes(SkYUVSizeInfo* yuvSizeInfo,
+                                            SkYUVColorSpace* yuvColorSpace,
+                                            const void* planes[3]) {
+    ScopedGenerator generator(fSharedGenerator);
+    Generator_GrYUVProvider provider(generator);
+
+    sk_sp<SkCachedData> data = provider.getPlanes(yuvSizeInfo, yuvColorSpace, planes);
+    if (!data) {
+        return nullptr;
+    }
+
+    return data;
+}
+
+
 /*
  *  We have 4 ways to try to return a texture (in sorted order)
  *
diff --git a/src/image/SkImage_Lazy.h b/src/image/SkImage_Lazy.h
index b13cd16..433817e 100644
--- a/src/image/SkImage_Lazy.h
+++ b/src/image/SkImage_Lazy.h
@@ -56,6 +56,7 @@
                                             sk_sp<SkColorSpace>*,
                                             SkScalar scaleAdjust[2]) const override;
 #endif
+    sk_sp<SkCachedData> getPlanes(SkYUVSizeInfo*, SkYUVColorSpace*, const void* planes[3]) override;
     sk_sp<SkData> onRefEncoded() const override;
     sk_sp<SkImage> onMakeSubset(const SkIRect&) const override;
     bool getROPixels(SkBitmap*, SkColorSpace* dstColorSpace, CachingHint) const override;