Add NV12 texture conversion support.

If textures 1 and 2 passed into MakeFromYUVTexturesCopy are the same,
then treat the input as NV12 and sample from the g component of texture
2.

BUG=skia:5209
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2016593002

Review-Url: https://codereview.chromium.org/2016593002
diff --git a/gm/yuvtorgbeffect.cpp b/gm/yuvtorgbeffect.cpp
index 7fc8ff4..a153c99 100644
--- a/gm/yuvtorgbeffect.cpp
+++ b/gm/yuvtorgbeffect.cpp
@@ -115,12 +115,9 @@
             for (int i = 0; i < 6; ++i) {
                 GrPipelineBuilder pipelineBuilder;
                 pipelineBuilder.setXPFactory(GrPorterDuffXPFactory::Make(SkXfermode::kSrc_Mode));
-                sk_sp<GrFragmentProcessor> fp(
-                            GrYUVEffect::MakeYUVToRGB(texture[indices[i][0]],
-                                                      texture[indices[i][1]],
-                                                      texture[indices[i][2]],
-                                                      sizes,
-                                                      static_cast<SkYUVColorSpace>(space)));
+                sk_sp<GrFragmentProcessor> fp(GrYUVEffect::MakeYUVToRGB(
+                    texture[indices[i][0]], texture[indices[i][1]], texture[indices[i][2]], sizes,
+                    static_cast<SkYUVColorSpace>(space), false));
                 if (fp) {
                     SkMatrix viewMatrix;
                     viewMatrix.setTranslate(x, y);
@@ -142,6 +139,115 @@
 };
 
 DEF_GM(return new YUVtoRGBEffect;)
+
+//////////////////////////////////////////////////////////////////////////////
+
+class YUVNV12toRGBEffect : public GM {
+public:
+    YUVNV12toRGBEffect() {
+        this->setBGColor(0xFFFFFFFF);
+    }
+
+protected:
+    SkString onShortName() override {
+        return SkString("yuv_nv12_to_rgb_effect");
+    }
+
+    SkISize onISize() override {
+        return SkISize::Make(48, 120);
+    }
+
+    void onOnceBeforeDraw() override {
+        SkImageInfo yinfo = SkImageInfo::MakeA8(YSIZE, YSIZE);
+        fBmp[0].allocPixels(yinfo);
+        SkImageInfo uvinfo = SkImageInfo::MakeN32Premul(USIZE, USIZE);
+        fBmp[1].allocPixels(uvinfo);
+        int color[] = {0, 85, 170};
+        const int limit[] = {255, 0, 255};
+        const int invl[] = {0, 255, 0};
+        const int inc[] = {1, -1, 1};
+
+        {
+            unsigned char* pixels = (unsigned char*)fBmp[0].getPixels();
+            const size_t nbBytes = fBmp[0].rowBytes() * fBmp[0].height();
+            for (size_t j = 0; j < nbBytes; ++j) {
+                pixels[j] = (unsigned char)color[0];
+                color[0] = (color[0] == limit[0]) ? invl[0] : color[0] + inc[0];
+            }
+        }
+
+        {
+            for (int y = 0; y < fBmp[1].height(); ++y) {
+                uint32_t* pixels = fBmp[1].getAddr32(0, y);
+                for (int j = 0; j < fBmp[1].width(); ++j) {
+                    pixels[j] = SkColorSetARGB(0, color[1], color[2], 0);
+                    color[1] = (color[1] == limit[1]) ? invl[1] : color[1] + inc[1];
+                    color[2] = (color[2] == limit[2]) ? invl[2] : color[2] + inc[2];
+                }
+            }
+        }
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        GrDrawContext* drawContext = canvas->internal_private_accessTopLayerDrawContext();
+        if (!drawContext) {
+            skiagm::GM::DrawGpuOnlyMessage(canvas);
+            return;
+        }
+
+        GrContext* context = canvas->getGrContext();
+        if (!context) {
+            return;
+        }
+
+        SkAutoTUnref<GrTexture> texture[3];
+        texture[0].reset(GrRefCachedBitmapTexture(context, fBmp[0], GrTextureParams::ClampBilerp(),
+                                                  SkSourceGammaTreatment::kRespect));
+        texture[1].reset(GrRefCachedBitmapTexture(context, fBmp[1], GrTextureParams::ClampBilerp(),
+                                                  SkSourceGammaTreatment::kRespect));
+        texture[2].reset(GrRefCachedBitmapTexture(context, fBmp[1], GrTextureParams::ClampBilerp(),
+                                                  SkSourceGammaTreatment::kRespect));
+
+        if (!texture[0] || !texture[1] || !texture[2]) {
+            return;
+        }
+
+        static const SkScalar kDrawPad = 10.f;
+        static const SkScalar kTestPad = 10.f;
+        static const SkScalar kColorSpaceOffset = 36.f;
+        SkISize sizes[3] = {{YSIZE, YSIZE}, {USIZE, USIZE}, {VSIZE, VSIZE}};
+
+        for (int space = kJPEG_SkYUVColorSpace; space <= kLastEnum_SkYUVColorSpace; ++space) {
+            SkRect renderRect =
+                SkRect::MakeWH(SkIntToScalar(fBmp[0].width()), SkIntToScalar(fBmp[0].height()));
+            renderRect.outset(kDrawPad, kDrawPad);
+
+            SkScalar y = kDrawPad + kTestPad + space * kColorSpaceOffset;
+            SkScalar x = kDrawPad + kTestPad;
+
+            GrPipelineBuilder pipelineBuilder;
+            pipelineBuilder.setXPFactory(GrPorterDuffXPFactory::Make(SkXfermode::kSrc_Mode));
+            sk_sp<GrFragmentProcessor> fp(
+                GrYUVEffect::MakeYUVToRGB(texture[0], texture[1], texture[2], sizes,
+                                          static_cast<SkYUVColorSpace>(space), true));
+            if (fp) {
+                SkMatrix viewMatrix;
+                viewMatrix.setTranslate(x, y);
+                pipelineBuilder.addColorFragmentProcessor(fp);
+                SkAutoTUnref<GrDrawBatch> batch(GrRectBatchFactory::CreateNonAAFill(
+                    GrColor_WHITE, viewMatrix, renderRect, nullptr, nullptr));
+                drawContext->drawContextPriv().testingOnly_drawBatch(pipelineBuilder, batch);
+            }
+        }
+    }
+
+private:
+    SkBitmap fBmp[2];
+
+    typedef GM INHERITED;
+};
+
+DEF_GM(return new YUVNV12toRGBEffect;)
 }
 
 #endif
diff --git a/include/core/SkImage.h b/include/core/SkImage.h
index 50a9f57..52a2bf7 100644
--- a/include/core/SkImage.h
+++ b/include/core/SkImage.h
@@ -138,6 +138,15 @@
                                                   const SkISize yuvSizes[3],
                                                   GrSurfaceOrigin);
 
+    /**
+     *  Create a new image by copying the pixels from the specified y and uv textures. The data
+     *  from the textures is immediately ingested into the image and the textures can be modified or
+     *  deleted after the function returns. The image will have the dimensions of the y texture.
+     */
+    static sk_sp<SkImage> MakeFromNV12TexturesCopy(GrContext*, SkYUVColorSpace,
+                                                   const GrBackendObject nv12TextureHandles[2],
+                                                   const SkISize nv12Sizes[2], GrSurfaceOrigin);
+
     static sk_sp<SkImage> MakeFromPicture(sk_sp<SkPicture>, const SkISize& dimensions,
                                           const SkMatrix*, const SkPaint*);
 
diff --git a/src/gpu/GrYUVProvider.cpp b/src/gpu/GrYUVProvider.cpp
index b30ff1f..c35f57a 100644
--- a/src/gpu/GrYUVProvider.cpp
+++ b/src/gpu/GrYUVProvider.cpp
@@ -121,11 +121,9 @@
     }
 
     GrPaint paint;
-    sk_sp<GrFragmentProcessor> yuvToRgbProcessor(GrYUVEffect::MakeYUVToRGB(yuvTextures[0],
-                                                                           yuvTextures[1],
-                                                                           yuvTextures[2],
-                                                                           yuvInfo.fSizeInfo.fSizes,
-                                                                           yuvInfo.fColorSpace));
+    sk_sp<GrFragmentProcessor> yuvToRgbProcessor(
+        GrYUVEffect::MakeYUVToRGB(yuvTextures[0], yuvTextures[1], yuvTextures[2],
+                                  yuvInfo.fSizeInfo.fSizes, yuvInfo.fColorSpace, false));
     paint.addColorFragmentProcessor(std::move(yuvToRgbProcessor));
 
     // If we're decoding an sRGB image, the result of our linear math on the YUV planes is already
diff --git a/src/gpu/effects/GrYUVEffect.cpp b/src/gpu/effects/GrYUVEffect.cpp
index 94dca84..7f95930 100644
--- a/src/gpu/effects/GrYUVEffect.cpp
+++ b/src/gpu/effects/GrYUVEffect.cpp
@@ -64,7 +64,7 @@
 public:
     static sk_sp<GrFragmentProcessor> Make(GrTexture* yTexture, GrTexture* uTexture,
                                            GrTexture* vTexture, const SkISize sizes[3],
-                                           SkYUVColorSpace colorSpace) {
+                                           SkYUVColorSpace colorSpace, bool nv12) {
         SkScalar w[3], h[3];
         w[0] = SkIntToScalar(sizes[0].fWidth)  / SkIntToScalar(yTexture->width());
         h[0] = SkIntToScalar(sizes[0].fHeight) / SkIntToScalar(yTexture->height());
@@ -85,21 +85,23 @@
              (sizes[2].fHeight != sizes[0].fHeight)) ?
             GrTextureParams::kBilerp_FilterMode :
             GrTextureParams::kNone_FilterMode;
-        return sk_sp<GrFragmentProcessor>(
-            new YUVtoRGBEffect(yTexture, uTexture, vTexture, yuvMatrix, uvFilterMode, colorSpace));
+        return sk_sp<GrFragmentProcessor>(new YUVtoRGBEffect(
+            yTexture, uTexture, vTexture, yuvMatrix, uvFilterMode, colorSpace, nv12));
     }
 
     const char* name() const override { return "YUV to RGB"; }
 
     SkYUVColorSpace getColorSpace() const { return fColorSpace; }
 
+    bool isNV12() const {
+        return fNV12;
+    }
+
     class GLSLProcessor : public GrGLSLFragmentProcessor {
     public:
-        // this class always generates the same code.
-        static void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder*) {}
-
         void emitCode(EmitArgs& args) override {
             GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+            const YUVtoRGBEffect& effect = args.fFp.cast<YUVtoRGBEffect>();
 
             const char* colorSpaceMatrix = nullptr;
             fMatrixUni = args.fUniformHandler->addUniform(kFragment_GrShaderFlag,
@@ -111,10 +113,15 @@
             fragBuilder->codeAppend(".r,");
             fragBuilder->appendTextureLookup(args.fTexSamplers[1], args.fCoords[1].c_str(),
                                              args.fCoords[1].getType());
-            fragBuilder->codeAppend(".r,");
-            fragBuilder->appendTextureLookup(args.fTexSamplers[2], args.fCoords[2].c_str(),
-                                             args.fCoords[2].getType());
-            fragBuilder->codeAppendf(".r, 1.0) * %s;", colorSpaceMatrix);
+            if (effect.fNV12) {
+                fragBuilder->codeAppendf(".rg,");
+            } else {
+                fragBuilder->codeAppend(".r,");
+                fragBuilder->appendTextureLookup(args.fTexSamplers[2], args.fCoords[2].c_str(),
+                                                 args.fCoords[2].getType());
+                fragBuilder->codeAppendf(".g,");
+            }
+            fragBuilder->codeAppendf("1.0) * %s;", colorSpaceMatrix);
         }
 
     protected:
@@ -143,21 +150,24 @@
 private:
     YUVtoRGBEffect(GrTexture* yTexture, GrTexture* uTexture, GrTexture* vTexture,
                    const SkMatrix yuvMatrix[3], GrTextureParams::FilterMode uvFilterMode,
-                   SkYUVColorSpace colorSpace)
-    : fYTransform(kLocal_GrCoordSet, yuvMatrix[0], yTexture, GrTextureParams::kNone_FilterMode)
-    , fYAccess(yTexture)
-    , fUTransform(kLocal_GrCoordSet, yuvMatrix[1], uTexture, uvFilterMode)
-    , fUAccess(uTexture, uvFilterMode)
-    , fVTransform(kLocal_GrCoordSet, yuvMatrix[2], vTexture, uvFilterMode)
-    , fVAccess(vTexture, uvFilterMode)
-    , fColorSpace(colorSpace) {
+                   SkYUVColorSpace colorSpace, bool nv12)
+        : fYTransform(kLocal_GrCoordSet, yuvMatrix[0], yTexture, GrTextureParams::kNone_FilterMode)
+        , fYAccess(yTexture)
+        , fUTransform(kLocal_GrCoordSet, yuvMatrix[1], uTexture, uvFilterMode)
+        , fUAccess(uTexture, uvFilterMode)
+        , fVAccess(vTexture, uvFilterMode)
+        , fColorSpace(colorSpace)
+        , fNV12(nv12) {
         this->initClassID<YUVtoRGBEffect>();
         this->addCoordTransform(&fYTransform);
         this->addTextureAccess(&fYAccess);
         this->addCoordTransform(&fUTransform);
         this->addTextureAccess(&fUAccess);
-        this->addCoordTransform(&fVTransform);
-        this->addTextureAccess(&fVAccess);
+        if (!fNV12) {
+            fVTransform = GrCoordTransform(kLocal_GrCoordSet, yuvMatrix[2], vTexture, uvFilterMode);
+            this->addCoordTransform(&fVTransform);
+            this->addTextureAccess(&fVAccess);
+        }
     }
 
     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
@@ -165,12 +175,12 @@
     }
 
     void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
-        GLSLProcessor::GenKey(*this, caps, b);
+        b->add32(fNV12);
     }
 
     bool onIsEqual(const GrFragmentProcessor& sBase) const override {
         const YUVtoRGBEffect& s = sBase.cast<YUVtoRGBEffect>();
-        return fColorSpace == s.getColorSpace();
+        return (fColorSpace == s.getColorSpace()) && (fNV12 == s.isNV12());
     }
 
     void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
@@ -186,6 +196,7 @@
     GrCoordTransform fVTransform;
     GrTextureAccess fVAccess;
     SkYUVColorSpace fColorSpace;
+    bool fNV12;
 
     typedef GrFragmentProcessor INHERITED;
 };
@@ -350,11 +361,11 @@
 
 //////////////////////////////////////////////////////////////////////////////
 
-sk_sp<GrFragmentProcessor>
-GrYUVEffect::MakeYUVToRGB(GrTexture* yTexture, GrTexture* uTexture, GrTexture* vTexture,
-                            const SkISize sizes[3], SkYUVColorSpace colorSpace) {
+sk_sp<GrFragmentProcessor> GrYUVEffect::MakeYUVToRGB(GrTexture* yTexture, GrTexture* uTexture,
+                                                     GrTexture* vTexture, const SkISize sizes[3],
+                                                     SkYUVColorSpace colorSpace, bool nv12) {
     SkASSERT(yTexture && uTexture && vTexture && sizes);
-    return YUVtoRGBEffect::Make(yTexture, uTexture, vTexture, sizes, colorSpace);
+    return YUVtoRGBEffect::Make(yTexture, uTexture, vTexture, sizes, colorSpace, nv12);
 }
 
 sk_sp<GrFragmentProcessor>
diff --git a/src/gpu/effects/GrYUVEffect.h b/src/gpu/effects/GrYUVEffect.h
index 6d3b158..902a181 100644
--- a/src/gpu/effects/GrYUVEffect.h
+++ b/src/gpu/effects/GrYUVEffect.h
@@ -20,7 +20,7 @@
      */
     sk_sp<GrFragmentProcessor> MakeYUVToRGB(GrTexture* yTexture, GrTexture* uTexture,
                                             GrTexture* vTexture, const SkISize sizes[3],
-                                            SkYUVColorSpace colorSpace);
+                                            SkYUVColorSpace colorSpace, bool nv12);
 
     /**
      * Creates a processor that performs color conversion from the passed in processor's RGB
diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp
index cc92d4e..9ad33ea 100644
--- a/src/image/SkImage_Gpu.cpp
+++ b/src/image/SkImage_Gpu.cpp
@@ -205,18 +205,23 @@
                                    SkBudgeted::kYes);
 }
 
-sk_sp<SkImage> SkImage::MakeFromYUVTexturesCopy(GrContext* ctx , SkYUVColorSpace colorSpace,
-                                                const GrBackendObject yuvTextureHandles[3],
-                                                const SkISize yuvSizes[3],
-                                                GrSurfaceOrigin origin) {
+static sk_sp<SkImage> make_from_yuv_textures_copy(GrContext* ctx, SkYUVColorSpace colorSpace,
+                                                  bool nv12,
+                                                  const GrBackendObject yuvTextureHandles[],
+                                                  const SkISize yuvSizes[],
+                                                  GrSurfaceOrigin origin) {
     const SkBudgeted budgeted = SkBudgeted::kYes;
 
-    if (yuvSizes[0].fWidth <= 0 || yuvSizes[0].fHeight <= 0 ||
-        yuvSizes[1].fWidth <= 0 || yuvSizes[1].fHeight <= 0 ||
-        yuvSizes[2].fWidth <= 0 || yuvSizes[2].fHeight <= 0) {
+    if (yuvSizes[0].fWidth <= 0 || yuvSizes[0].fHeight <= 0 || yuvSizes[1].fWidth <= 0 ||
+        yuvSizes[1].fHeight <= 0) {
         return nullptr;
     }
-    static const GrPixelConfig kConfig = kAlpha_8_GrPixelConfig;
+    if (!nv12 && (yuvSizes[2].fWidth <= 0 || yuvSizes[2].fHeight <= 0)) {
+        return nullptr;
+    }
+
+    const GrPixelConfig kConfig = nv12 ? kRGBA_8888_GrPixelConfig : kAlpha_8_GrPixelConfig;
+
     GrBackendTextureDesc yDesc;
     yDesc.fConfig = kConfig;
     yDesc.fOrigin = origin;
@@ -233,20 +238,25 @@
     uDesc.fWidth = yuvSizes[1].fWidth;
     uDesc.fHeight = yuvSizes[1].fHeight;
 
-    GrBackendTextureDesc vDesc;
-    vDesc.fConfig = kConfig;
-    vDesc.fOrigin = origin;
-    vDesc.fSampleCnt = 0;
-    vDesc.fTextureHandle = yuvTextureHandles[2];
-    vDesc.fWidth = yuvSizes[2].fWidth;
-    vDesc.fHeight = yuvSizes[2].fHeight;
+    sk_sp<GrTexture> yTex(
+        ctx->textureProvider()->wrapBackendTexture(yDesc, kBorrow_GrWrapOwnership));
+    sk_sp<GrTexture> uTex(
+        ctx->textureProvider()->wrapBackendTexture(uDesc, kBorrow_GrWrapOwnership));
+    sk_sp<GrTexture> vTex;
+    if (nv12) {
+        vTex = uTex;
+    } else {
+        GrBackendTextureDesc vDesc;
+        vDesc.fConfig = kConfig;
+        vDesc.fOrigin = origin;
+        vDesc.fSampleCnt = 0;
+        vDesc.fTextureHandle = yuvTextureHandles[2];
+        vDesc.fWidth = yuvSizes[2].fWidth;
+        vDesc.fHeight = yuvSizes[2].fHeight;
 
-    SkAutoTUnref<GrTexture> yTex(ctx->textureProvider()->wrapBackendTexture(
-        yDesc, kBorrow_GrWrapOwnership));
-    SkAutoTUnref<GrTexture> uTex(ctx->textureProvider()->wrapBackendTexture(
-        uDesc, kBorrow_GrWrapOwnership));
-    SkAutoTUnref<GrTexture> vTex(ctx->textureProvider()->wrapBackendTexture(
-        vDesc, kBorrow_GrWrapOwnership));
+        vTex = sk_sp<GrTexture>(
+            ctx->textureProvider()->wrapBackendTexture(vDesc, kBorrow_GrWrapOwnership));
+    }
     if (!yTex || !uTex || !vTex) {
         return nullptr;
     }
@@ -266,8 +276,8 @@
 
     GrPaint paint;
     paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
-    paint.addColorFragmentProcessor(GrYUVEffect::MakeYUVToRGB(yTex, uTex, vTex, yuvSizes,
-                                                              colorSpace));
+    paint.addColorFragmentProcessor(
+        GrYUVEffect::MakeYUVToRGB(yTex.get(), uTex.get(), vTex.get(), yuvSizes, colorSpace, nv12));
 
     const SkRect rect = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
 
@@ -278,6 +288,19 @@
                                    drawContext->asTexture().get(), budgeted);
 }
 
+sk_sp<SkImage> SkImage::MakeFromYUVTexturesCopy(GrContext* ctx, SkYUVColorSpace colorSpace,
+                                                const GrBackendObject yuvTextureHandles[3],
+                                                const SkISize yuvSizes[3], GrSurfaceOrigin origin) {
+    return make_from_yuv_textures_copy(ctx, colorSpace, false, yuvTextureHandles, yuvSizes, origin);
+}
+
+sk_sp<SkImage> SkImage::MakeFromNV12TexturesCopy(GrContext* ctx, SkYUVColorSpace colorSpace,
+                                                 const GrBackendObject yuvTextureHandles[2],
+                                                 const SkISize yuvSizes[2],
+                                                 GrSurfaceOrigin origin) {
+    return make_from_yuv_textures_copy(ctx, colorSpace, true, yuvTextureHandles, yuvSizes, origin);
+}
+
 static sk_sp<SkImage> create_image_from_maker(GrTextureMaker* maker, SkAlphaType at, uint32_t id) {
     SkAutoTUnref<GrTexture> texture(maker->refTextureForParams(GrTextureParams::ClampNoFilter(),
                                                                SkSourceGammaTreatment::kRespect));