Add SkImage::MakeFromYUVAPixmaps

Bug: skia:7903
Change-Id: I41ee31ad3657aee372e22ec3e7a0a317e31b2791
Reviewed-on: https://skia-review.googlesource.com/c/171007
Commit-Queue: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
diff --git a/docs/SkImage_Reference.bmh b/docs/SkImage_Reference.bmh
index 97538cd..7a0bc33 100644
--- a/docs/SkImage_Reference.bmh
+++ b/docs/SkImage_Reference.bmh
@@ -462,6 +462,27 @@
 
 #Method ##
 
+#Method static sk_sp<SkImage> MakeFromYUVAPixmaps(
+                                               GrContext* context,
+                                               SkYUVColorSpace yuvColorSpace,
+                                               const SkPixmap yuvaPixmaps[],
+                                               const SkYUVAIndex yuvaIndices[4],
+                                               SkISize imageSize,
+                                               GrSurfaceOrigin imageOrigin,
+                                               bool buildMips,
+                                               bool limitToMaxTextureSize = false,
+                                               sk_sp<SkColorSpace> imageColorSpace = nullptr);
+
+#In Constructor
+#Line # creates Image from YUV_ColorSpace data ##
+#Populate
+
+#NoExample
+##
+
+#SeeAlso MakeFromYUVATextures
+
+#Method ##
 
 # ------------------------------------------------------------------------------
 
diff --git a/gm/wacky_yuv_formats.cpp b/gm/wacky_yuv_formats.cpp
index 8ea25c0..00c2fe1 100644
--- a/gm/wacky_yuv_formats.cpp
+++ b/gm/wacky_yuv_formats.cpp
@@ -743,6 +743,7 @@
                         }
 
                         GrBackendTexture yuvaTextures[4];
+                        SkPixmap yuvaPixmaps[4];
 
                         for (int i = 0; i < 4; ++i) {
                             if (!used[i]) {
@@ -757,9 +758,12 @@
                                 false,
                                 GrMipMapped::kNo,
                                 resultBMs[i].rowBytes());
+                            yuvaPixmaps[i] = resultBMs[i].pixmap();
                         }
 
-                        if (counter & 0x1) {
+                        int counterMod = counter % 3;
+                        switch (counterMod) {
+                        case 0:
                             fImages[opaque][cs][format] = SkImage::MakeFromYUVATexturesCopy(
                                 context,
                                 (SkYUVColorSpace)cs,
@@ -767,7 +771,8 @@
                                 yuvaIndices,
                                 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
                                 kTopLeft_GrSurfaceOrigin);
-                        } else {
+                            break;
+                        case 1:
                             fImages[opaque][cs][format] = SkImage::MakeFromYUVATextures(
                                 context,
                                 (SkYUVColorSpace)cs,
@@ -775,7 +780,19 @@
                                 yuvaIndices,
                                 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
                                 kTopLeft_GrSurfaceOrigin);
+                            break;
+                        case 2:
+                        default:
+                            fImages[opaque][cs][format] = SkImage::MakeFromYUVAPixmaps(
+                                context,
+                                (SkYUVColorSpace)cs,
+                                yuvaPixmaps,
+                                yuvaIndices,
+                                { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
+                                kTopLeft_GrSurfaceOrigin, true);
+                            break;
                         }
+                        ++counter;
                     } else {
                         fImages[opaque][cs][format] = make_yuv_gen_image(
                                                                 fOriginalBMs[opaque].info(),
@@ -785,7 +802,6 @@
                     }
                 }
             }
-            ++counter;
         }
     }
 
diff --git a/include/core/SkImage.h b/include/core/SkImage.h
index 79067af..9b97884 100644
--- a/include/core/SkImage.h
+++ b/include/core/SkImage.h
@@ -357,31 +357,6 @@
                                                    GrSurfaceOrigin imageOrigin,
                                                    sk_sp<SkColorSpace> imageColorSpace = nullptr);
 
-    /** Creates an SkImage by storing the specified YUVA planes into an image, to be rendered
-        via multitexturing.
-
-        @param context         GPU context
-        @param yuvColorSpace   How the YUV values are converted to RGB. One of:
-                                            kJPEG_SkYUVColorSpace, kRec601_SkYUVColorSpace,
-                                            kRec709_SkYUVColorSpace
-        @param yuvaTextures    array of (up to four) YUVA textures on GPU which contain the,
-                               possibly interleaved, YUVA planes
-        @param yuvaIndices     array indicating which texture in yuvaTextures, and channel
-                               in that texture, maps to each component of YUVA.
-        @param imageSize       size of the resulting image
-        @param imageOrigin     origin of the resulting image. One of: kBottomLeft_GrSurfaceOrigin,
-                               kTopLeft_GrSurfaceOrigin
-        @param imageColorSpace range of colors of the resulting image; may be nullptr
-        @return                created SkImage, or nullptr
-    */
-    static sk_sp<SkImage> MakeFromYUVATextures(GrContext* context,
-                                               SkYUVColorSpace yuvColorSpace,
-                                               const GrBackendTexture yuvaTextures[],
-                                               const SkYUVAIndex yuvaIndices[4],
-                                               SkISize imageSize,
-                                               GrSurfaceOrigin imageOrigin,
-                                               sk_sp<SkColorSpace> imageColorSpace = nullptr);
-
     /** Creates an SkImage by flattening the specified YUVA planes into a single, interleaved RGBA
         image. 'backendTexture' is used to store the result of the flattening.
 
@@ -410,36 +385,71 @@
             const GrBackendTexture& backendTexture,
             sk_sp<SkColorSpace> imageColorSpace = nullptr);
 
-    /** Creates SkImage from copy of yuvTextures, an array of textures on GPU.
-        yuvTextures contain pixels for YUV planes of SkImage. Returned SkImage has the dimensions
-        yuvTextures[0]. yuvColorSpace describes how YUV colors convert to RGB colors.
+    /** Creates an SkImage by storing the specified YUVA planes into an image, to be rendered
+        via multitexturing.
 
         @param context         GPU context
-        @param yuvColorSpace   one of: kJPEG_SkYUVColorSpace, kRec601_SkYUVColorSpace,
-                               kRec709_SkYUVColorSpace
-        @param yuvTextures     array of YUV textures on GPU
-        @param imageOrigin     one of: kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin
-        @param imageColorSpace range of colors; may be nullptr
+        @param yuvColorSpace   How the YUV values are converted to RGB. One of:
+                                           kJPEG_SkYUVColorSpace, kRec601_SkYUVColorSpace,
+                                           kRec709_SkYUVColorSpace
+        @param yuvaTextures    array of (up to four) YUVA textures on GPU which contain the,
+                               possibly interleaved, YUVA planes
+        @param yuvaIndices     array indicating which texture in yuvaTextures, and channel
+                               in that texture, maps to each component of YUVA.
+        @param imageSize       size of the resulting image
+        @param imageOrigin     origin of the resulting image. One of: kBottomLeft_GrSurfaceOrigin,
+                               kTopLeft_GrSurfaceOrigin
+        @param imageColorSpace range of colors of the resulting image; may be nullptr
         @return                created SkImage, or nullptr
     */
+    static sk_sp<SkImage> MakeFromYUVATextures(GrContext* context,
+                                               SkYUVColorSpace yuvColorSpace,
+                                               const GrBackendTexture yuvaTextures[],
+                                               const SkYUVAIndex yuvaIndices[4],
+                                               SkISize imageSize,
+                                               GrSurfaceOrigin imageOrigin,
+                                               sk_sp<SkColorSpace> imageColorSpace = nullptr);
+
+    /** Creates SkImage from pixmap array representing YUVA data.
+        SkImage is uploaded to GPU back-end using context.
+
+        Each GrBackendTexture created from yuvaPixmaps array is uploaded to match SkSurface
+        using SkColorSpace of SkPixmap. SkColorSpace of SkImage is determined by imageColorSpace.
+
+        SkImage is returned referring to GPU back-end if context is not nullptr and
+        format of data is recognized and supported. Otherwise, nullptr is returned.
+        Recognized GPU formats vary by platform and GPU back-end.
+
+        @param context                GPU context
+        @param yuvColorSpace          How the YUV values are converted to RGB. One of:
+                                            kJPEG_SkYUVColorSpace, kRec601_SkYUVColorSpace,
+                                            kRec709_SkYUVColorSpace
+        @param yuvaPixmaps            array of (up to four) SkPixmap which contain the,
+                                      possibly interleaved, YUVA planes
+        @param yuvaIndices            array indicating which pixmap in yuvaPixmaps, and channel
+                                      in that pixmap, maps to each component of YUVA.
+        @param imageSize              size of the resulting image
+        @param imageOrigin            origin of the resulting image. One of:
+                                            kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin
+        @param buildMips              create internal YUVA textures as mip map if true
+        @param limitToMaxTextureSize  downscale image to GPU maximum texture size, if necessary
+        @param imageColorSpace        range of colors of the resulting image; may be nullptr
+        @return                       created SkImage, or nullptr
+    */
+    static sk_sp<SkImage> MakeFromYUVAPixmaps(
+            GrContext* context, SkYUVColorSpace yuvColorSpace, const SkPixmap yuvaPixmaps[],
+            const SkYUVAIndex yuvaIndices[4], SkISize imageSize, GrSurfaceOrigin imageOrigin,
+            bool buildMips, bool limitToMaxTextureSize = false,
+            sk_sp<SkColorSpace> imageColorSpace = nullptr);
+
+    /** To be deprecated.
+    */
     static sk_sp<SkImage> MakeFromYUVTexturesCopy(GrContext* context, SkYUVColorSpace yuvColorSpace,
                                                   const GrBackendTexture yuvTextures[3],
                                                   GrSurfaceOrigin imageOrigin,
                                                   sk_sp<SkColorSpace> imageColorSpace = nullptr);
 
-    /** Creates SkImage from copy of yuvTextures, an array of textures on GPU.
-        yuvTextures contain pixels for YUV planes of SkImage. Returned SkImage has the dimensions
-        yuvTextures[0] and stores pixels in backendTexture. yuvColorSpace describes how YUV colors
-        convert to RGB colors.
-
-        @param context         GPU context
-        @param yuvColorSpace   one of: kJPEG_SkYUVColorSpace, kRec601_SkYUVColorSpace,
-                               kRec709_SkYUVColorSpace
-        @param yuvTextures     array of YUV textures on GPU
-        @param imageOrigin     one of: kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin
-        @param backendTexture  the resource that stores the final pixels
-        @param imageColorSpace range of colors; may be nullptr
-        @return                created SkImage, or nullptr
+    /** To be deprecated.
     */
     static sk_sp<SkImage> MakeFromYUVTexturesCopyWithExternalBackend(
             GrContext* context, SkYUVColorSpace yuvColorSpace,
diff --git a/src/image/SkImage_Gpu.h b/src/image/SkImage_Gpu.h
index 7da0ca4..e184e17 100644
--- a/src/image/SkImage_Gpu.h
+++ b/src/image/SkImage_Gpu.h
@@ -94,6 +94,8 @@
                                              PromiseDoneProc promiseDoneProc,
                                              TextureContext textureContext);
 
+    /** To be deprecated. Use SkImage_GpuYUVA::MakePromiseYUVATexture instead.
+     */
     static sk_sp<SkImage> MakePromiseYUVATexture(GrContext* context,
                                                  SkYUVColorSpace yuvColorSpace,
                                                  const GrBackendFormat yuvaFormats[],
diff --git a/src/image/SkImage_GpuYUVA.cpp b/src/image/SkImage_GpuYUVA.cpp
index a1d0696..3113c1c 100644
--- a/src/image/SkImage_GpuYUVA.cpp
+++ b/src/image/SkImage_GpuYUVA.cpp
@@ -16,6 +16,7 @@
 #include "GrRenderTargetContext.h"
 #include "GrTexture.h"
 #include "GrTextureProducer.h"
+#include "SkAutoPixmapStorage.h"
 #include "SkGr.h"
 #include "SkImage_Gpu.h"
 #include "SkImage_GpuYUVA.h"
@@ -144,6 +145,68 @@
                                        numTextures, yuvaIndices, imageOrigin, imageColorSpace,
                                        SkBudgeted::kYes);
 }
+
+sk_sp<SkImage> SkImage::MakeFromYUVAPixmaps(
+        GrContext* context, SkYUVColorSpace yuvColorSpace, const SkPixmap yuvaPixmaps[],
+        const SkYUVAIndex yuvaIndices[4], SkISize imageSize, GrSurfaceOrigin imageOrigin,
+        bool buildMips, bool limitToMaxTextureSize, sk_sp<SkColorSpace> imageColorSpace) {
+    int numPixmaps;
+    if (!SkYUVAIndex::AreValidIndices(yuvaIndices, &numPixmaps)) {
+        return nullptr;
+    }
+
+    // Make proxies
+    GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
+    sk_sp<GrTextureProxy> tempTextureProxies[4];
+    for (int i = 0; i < numPixmaps; ++i) {
+        const SkPixmap* pixmap = &yuvaPixmaps[i];
+        SkAutoPixmapStorage resized;
+        int maxTextureSize = context->contextPriv().caps()->maxTextureSize();
+        int maxDim = SkTMax(yuvaPixmaps[i].width(), yuvaPixmaps[i].height());
+        if (limitToMaxTextureSize && maxDim > maxTextureSize) {
+            float scale = static_cast<float>(maxTextureSize) / maxDim;
+            int newWidth = SkTMin(static_cast<int>(yuvaPixmaps[i].width() * scale),
+                                  maxTextureSize);
+            int newHeight = SkTMin(static_cast<int>(yuvaPixmaps[i].height() * scale),
+                                   maxTextureSize);
+            SkImageInfo info = yuvaPixmaps[i].info().makeWH(newWidth, newHeight);
+            if (!resized.tryAlloc(info) ||
+                !yuvaPixmaps[i].scalePixels(resized, kLow_SkFilterQuality)) {
+                return nullptr;
+            }
+            pixmap = &resized;
+        }
+        // Turn the pixmap into a GrTextureProxy
+        if (buildMips) {
+            SkBitmap bmp;
+            bmp.installPixels(*pixmap);
+            tempTextureProxies[i] = proxyProvider->createMipMapProxyFromBitmap(bmp);
+        } else {
+            if (SkImageInfoIsValid(pixmap->info())) {
+                ATRACE_ANDROID_FRAMEWORK("Upload Texture [%ux%u]",
+                                         pixmap->width(), pixmap->height());
+                // We don't need a release proc on the data in pixmap since we know we are in a
+                // GrContext that has a resource provider. Thus the createTextureProxy call will
+                // immediately upload the data.
+                sk_sp<SkImage> image = SkImage::MakeFromRaster(*pixmap, nullptr, nullptr);
+                tempTextureProxies[i] =
+                        proxyProvider->createTextureProxy(std::move(image), kNone_GrSurfaceFlags, 1,
+                                                          SkBudgeted::kYes, SkBackingFit::kExact);
+            }
+        }
+
+        if (!tempTextureProxies[i]) {
+            return nullptr;
+        }
+    }
+
+    return sk_make_sp<SkImage_GpuYUVA>(sk_ref_sp(context), imageSize.width(), imageSize.height(),
+                                       kNeedNewImageUniqueID, yuvColorSpace, tempTextureProxies,
+                                       numPixmaps, yuvaIndices, imageOrigin, imageColorSpace,
+                                       SkBudgeted::kYes);
+}
+
+
 /////////////////////////////////////////////////////////////////////////////////////////////////
 sk_sp<SkImage> SkImage_GpuYUVA::MakePromiseYUVATexture(GrContext* context,
                                                        SkYUVColorSpace yuvColorSpace,