Add support for the Rec601 YUV color space to GrYUVtoRGBEffect.

R=bsalomon@google.com, senorblanco@chromium.org, sugoi@chromium.org, reed@google.com

Author: rileya@chromium.org

Review URL: https://codereview.chromium.org/516463005
diff --git a/expectations/gm/ignored-tests.txt b/expectations/gm/ignored-tests.txt
index b877161..377575e 100644
--- a/expectations/gm/ignored-tests.txt
+++ b/expectations/gm/ignored-tests.txt
@@ -38,3 +38,6 @@
 
 # reed - conservative_rasterclip CL
 multipicturedraw_pathclip_tiled
+
+# rileya - https://codereview.chromium.org/516463005/ will rebaseline after bots cycle
+yuv_to_rgb_effect
diff --git a/gm/yuvtorgbeffect.cpp b/gm/yuvtorgbeffect.cpp
index 12676f2..acb6f22 100644
--- a/gm/yuvtorgbeffect.cpp
+++ b/gm/yuvtorgbeffect.cpp
@@ -35,7 +35,7 @@
     }
 
     virtual SkISize onISize() SK_OVERRIDE {
-        return SkISize::Make(334, 64);
+        return SkISize::Make(334, 128);
     }
 
     virtual uint32_t onGetFlags() const SK_OVERRIDE {
@@ -93,31 +93,36 @@
 
         static const SkScalar kDrawPad = 10.f;
         static const SkScalar kTestPad = 10.f;
+        static const SkScalar kColorSpaceOffset = 64.f;
 
-        SkRect renderRect = SkRect::MakeWH(SkIntToScalar(fBmp[0].width()),
-                                           SkIntToScalar(fBmp[0].height()));
-        renderRect.outset(kDrawPad, kDrawPad);
+        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;
-        SkScalar x = kDrawPad + kTestPad;
+          SkScalar y = kDrawPad + kTestPad + space * kColorSpaceOffset;
+          SkScalar x = kDrawPad + kTestPad;
 
-        const int indices[6][3] = {{0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0}};
+          const int indices[6][3] = {{0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0}};
 
-        for (int i = 0; i < 6; ++i) {
-            SkAutoTUnref<GrEffect> effect(
-                        GrYUVtoRGBEffect::Create(texture[indices[i][0]],
-                                                 texture[indices[i][1]],
-                                                 texture[indices[i][2]]));
-            if (effect) {
-                SkMatrix viewMatrix;
-                viewMatrix.setTranslate(x, y);
-                drawState->reset(viewMatrix);
-                drawState->setRenderTarget(rt);
-                drawState->setColor(0xffffffff);
-                drawState->addColorEffect(effect, 1);
-                tt.target()->drawSimpleRect(renderRect);
-            }
-            x += renderRect.width() + kTestPad;
+          for (int i = 0; i < 6; ++i) {
+              SkAutoTUnref<GrEffect> effect(
+                          GrYUVtoRGBEffect::Create(texture[indices[i][0]],
+                                                   texture[indices[i][1]],
+                                                   texture[indices[i][2]],
+                                                   static_cast<SkYUVColorSpace>(space)));
+              if (effect) {
+                  SkMatrix viewMatrix;
+                  viewMatrix.setTranslate(x, y);
+                  drawState->reset(viewMatrix);
+                  drawState->setRenderTarget(rt);
+                  drawState->setColor(0xffffffff);
+                  drawState->addColorEffect(effect, 1);
+                  tt.target()->drawSimpleRect(renderRect);
+              }
+              x += renderRect.width() + kTestPad;
+          }
         }
 
         GrUnlockAndUnrefCachedBitmapTexture(texture[0]);
diff --git a/include/core/SkImageGenerator.h b/include/core/SkImageGenerator.h
index 4eb5cf8..fc6b1b4 100644
--- a/include/core/SkImageGenerator.h
+++ b/include/core/SkImageGenerator.h
@@ -8,8 +8,8 @@
 #ifndef SkImageGenerator_DEFINED
 #define SkImageGenerator_DEFINED
 
-#include "SkImageInfo.h"
 #include "SkColor.h"
+#include "SkImageInfo.h"
 
 class SkBitmap;
 class SkData;
@@ -121,7 +121,8 @@
      *  associated YUV data into those planes of memory supplied by the caller. It should validate
      *  that the sizes match what it expected. If the sizes do not match, it should return false.
      */
-    bool getYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3]);
+    bool getYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3],
+                       SkYUVColorSpace* colorSpace);
 
 protected:
     virtual SkData* onRefEncodedData();
@@ -130,6 +131,8 @@
                              void* pixels, size_t rowBytes,
                              SkPMColor ctable[], int* ctableCount);
     virtual bool onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3]);
+    virtual bool onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3],
+                                 SkYUVColorSpace* colorSpace);
 };
 
 #endif  // SkImageGenerator_DEFINED
diff --git a/include/core/SkImageInfo.h b/include/core/SkImageInfo.h
index 6204cb3..7fedfa1 100644
--- a/include/core/SkImageInfo.h
+++ b/include/core/SkImageInfo.h
@@ -133,6 +133,21 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 /**
+ *  Describes the color space a YUV pixel.
+ */
+enum SkYUVColorSpace {
+    /** Standard JPEG color space. */
+    kJPEG_SkYUVColorSpace,
+    /** SDTV standard Rec. 601 color space. Uses "studio swing" [16, 235] color
+       range. See http://en.wikipedia.org/wiki/Rec._601 for details. */
+    kRec601_SkYUVColorSpace,
+
+    kLastEnum_SkYUVColorSpace = kRec601_SkYUVColorSpace
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
  *  Describe an image's dimensions and pixel type.
  */
 struct SkImageInfo {
diff --git a/include/core/SkPixelRef.h b/include/core/SkPixelRef.h
index afab7fa..02d696e 100644
--- a/include/core/SkPixelRef.h
+++ b/include/core/SkPixelRef.h
@@ -227,9 +227,13 @@
      *  If all planes and rowBytes are not NULL, then it should copy the associated Y,U,V data
      *  into those planes of memory supplied by the caller. It should validate that the sizes
      *  match what it expected. If the sizes do not match, it should return false.
+     *
+     *  If colorSpace is not NULL, the YUV color space of the data should be stored in the address
+     *  it points at.
      */
-    bool getYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3]) {
-        return this->onGetYUV8Planes(sizes, planes, rowBytes);
+    bool getYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3],
+                       SkYUVColorSpace* colorSpace) {
+        return this->onGetYUV8Planes(sizes, planes, rowBytes, colorSpace);
     }
 
     bool readPixels(SkBitmap* dst, const SkIRect* subset = NULL);
@@ -319,7 +323,8 @@
     virtual SkData* onRefEncodedData();
 
     // default impl returns false.
-    virtual bool onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3]);
+    virtual bool onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3],
+                                 SkYUVColorSpace* colorSpace);
 
     /**
      *  Returns the size (in bytes) of the internally allocated memory.
diff --git a/src/core/SkImageGenerator.cpp b/src/core/SkImageGenerator.cpp
index 7008e7b..c062978 100644
--- a/src/core/SkImageGenerator.cpp
+++ b/src/core/SkImageGenerator.cpp
@@ -57,7 +57,8 @@
 }
 #endif
 
-bool SkImageGenerator::getYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3]) {
+bool SkImageGenerator::getYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3],
+                                     SkYUVColorSpace* colorSpace) {
 #ifdef SK_DEBUG
     // In all cases, we need the sizes array
     SkASSERT(sizes);
@@ -89,13 +90,25 @@
               (rowBytes[2] >= (size_t)sizes[2].fWidth)));
 #endif
 
-    return this->onGetYUV8Planes(sizes, planes, rowBytes);
+    return this->onGetYUV8Planes(sizes, planes, rowBytes, colorSpace);
 }
 
 bool SkImageGenerator::onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3]) {
     return false;
 }
 
+bool SkImageGenerator::onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3],
+                                       SkYUVColorSpace* colorSpace) {
+    // In order to maintain compatibility with clients that implemented the original
+    // onGetYUV8Planes interface, we assume that the color space is JPEG.
+    // TODO(rileya): remove this and the old onGetYUV8Planes once clients switch over to
+    // the new interface.
+    if (colorSpace) {
+        *colorSpace = kJPEG_SkYUVColorSpace;
+    }
+    return this->onGetYUV8Planes(sizes, planes, rowBytes);
+}
+
 /////////////////////////////////////////////////////////////////////////////////////////////
 
 SkData* SkImageGenerator::onRefEncodedData() {
diff --git a/src/core/SkPixelRef.cpp b/src/core/SkPixelRef.cpp
index 1e6db7e..0aa00e0 100644
--- a/src/core/SkPixelRef.cpp
+++ b/src/core/SkPixelRef.cpp
@@ -253,7 +253,8 @@
     return NULL;
 }
 
-bool SkPixelRef::onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3]) {
+bool SkPixelRef::onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3],
+                                 SkYUVColorSpace* colorSpace) {
     return false;
 }
 
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index 73773b0..56f3a16 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -222,7 +222,7 @@
                                    const SkBitmap& bm, const GrTextureDesc& desc) {
     SkPixelRef* pixelRef = bm.pixelRef();
     SkISize yuvSizes[3];
-    if ((NULL == pixelRef) || !pixelRef->getYUV8Planes(yuvSizes, NULL, NULL)) {
+    if ((NULL == pixelRef) || !pixelRef->getYUV8Planes(yuvSizes, NULL, NULL, NULL)) {
         return NULL;
     }
 
@@ -239,8 +239,10 @@
     planes[1] = (uint8_t*)planes[0] + sizes[0];
     planes[2] = (uint8_t*)planes[1] + sizes[1];
 
+    SkYUVColorSpace colorSpace;
+
     // Get the YUV planes
-    if (!pixelRef->getYUV8Planes(yuvSizes, planes, rowBytes)) {
+    if (!pixelRef->getYUV8Planes(yuvSizes, planes, rowBytes, &colorSpace)) {
         return NULL;
     }
 
@@ -269,7 +271,8 @@
     GrRenderTarget* renderTarget = result ? result->asRenderTarget() : NULL;
     if (renderTarget) {
         SkAutoTUnref<GrEffect> yuvToRgbEffect(GrYUVtoRGBEffect::Create(
-            yuvTextures[0].texture(), yuvTextures[1].texture(), yuvTextures[2].texture()));
+            yuvTextures[0].texture(), yuvTextures[1].texture(), yuvTextures[2].texture(),
+            colorSpace));
         GrPaint paint;
         paint.addColorEffect(yuvToRgbEffect);
         SkRect r = SkRect::MakeWH(SkIntToScalar(yuvSizes[0].fWidth),
diff --git a/src/gpu/effects/GrYUVtoRGBEffect.cpp b/src/gpu/effects/GrYUVtoRGBEffect.cpp
index 1a46c96..0023b1b 100644
--- a/src/gpu/effects/GrYUVtoRGBEffect.cpp
+++ b/src/gpu/effects/GrYUVtoRGBEffect.cpp
@@ -17,8 +17,9 @@
 
 class YUVtoRGBEffect : public GrEffect {
 public:
-    static GrEffect* Create(GrTexture* yTexture, GrTexture* uTexture, GrTexture* vTexture) {
-        return SkNEW_ARGS(YUVtoRGBEffect, (yTexture, uTexture, vTexture));
+    static GrEffect* Create(GrTexture* yTexture, GrTexture* uTexture, GrTexture* vTexture,
+                            SkYUVColorSpace colorSpace) {
+        return SkNEW_ARGS(YUVtoRGBEffect, (yTexture, uTexture, vTexture, colorSpace));
     }
 
     static const char* Name() { return "YUV to RGB"; }
@@ -34,8 +35,15 @@
         *validFlags = kA_GrColorComponentFlag;
     }
 
+    SkYUVColorSpace getColorSpace() const {
+        return fColorSpace;
+    }
+
     class GLEffect : public GrGLEffect {
     public:
+        static const GrGLfloat kJPEGConversionMatrix[16];
+        static const GrGLfloat kRec601ConversionMatrix[16];
+
         // this class always generates the same code.
         static void GenKey(const GrDrawEffect&, const GrGLCaps&, GrEffectKeyBuilder*) {}
 
@@ -45,19 +53,18 @@
         }
 
         virtual void emitCode(GrGLProgramBuilder* builder,
-                              const GrDrawEffect&,
+                              const GrDrawEffect& drawEffect,
                               const GrEffectKey&,
                               const char* outputColor,
                               const char* inputColor,
                               const TransformedCoordsArray& coords,
                               const TextureSamplerArray& samplers) SK_OVERRIDE {
             GrGLFragmentShaderBuilder* fsBuilder = builder->getFragmentShaderBuilder();
-            const char* yuvMatrix   = "yuvMatrix";
-            fsBuilder->codeAppendf("\tconst mat4 %s = mat4(1.0,  0.0,    1.402, -0.701,\n\t\t\t"
-                                                          "1.0, -0.344, -0.714,  0.529,\n\t\t\t"
-                                                          "1.0,  1.772,  0.0,   -0.886,\n\t\t\t"
-                                                          "0.0,  0.0,    0.0,    1.0);\n",
-                                   yuvMatrix);
+
+            const char* yuvMatrix   = NULL;
+            fMatrixUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
+                                             kMat44f_GrSLType, "YUVMatrix",
+                                             &yuvMatrix);
             fsBuilder->codeAppendf("\t%s = vec4(\n\t\t", outputColor);
             fsBuilder->appendTextureLookup(samplers[0], coords[0].c_str(), coords[0].type());
             fsBuilder->codeAppend(".r,\n\t\t");
@@ -67,16 +74,34 @@
             fsBuilder->codeAppendf(".r,\n\t\t1.0) * %s;\n", yuvMatrix);
         }
 
+        virtual void setData(const GrGLProgramDataManager& pdman,
+                             const GrDrawEffect& drawEffect) SK_OVERRIDE {
+            const YUVtoRGBEffect& yuvEffect = drawEffect.castEffect<YUVtoRGBEffect>();
+            switch (yuvEffect.getColorSpace()) {
+                case kJPEG_SkYUVColorSpace:
+                    pdman.setMatrix4f(fMatrixUni, kJPEGConversionMatrix);
+                    break;
+                case kRec601_SkYUVColorSpace:
+                    pdman.setMatrix4f(fMatrixUni, kRec601ConversionMatrix);
+                    break;
+            }
+        }
+
+    private:
+        GrGLProgramDataManager::UniformHandle fMatrixUni;
+
         typedef GrGLEffect INHERITED;
     };
 
 private:
-    YUVtoRGBEffect(GrTexture* yTexture, GrTexture* uTexture, GrTexture* vTexture)
-    : fCoordTransform(kLocal_GrCoordSet, GrCoordTransform::MakeDivByTextureWHMatrix(yTexture),
-                      yTexture)
+    YUVtoRGBEffect(GrTexture* yTexture, GrTexture* uTexture, GrTexture* vTexture,
+                   SkYUVColorSpace colorSpace)
+     : fCoordTransform(kLocal_GrCoordSet, GrCoordTransform::MakeDivByTextureWHMatrix(yTexture),
+                       yTexture)
     , fYAccess(yTexture)
     , fUAccess(uTexture)
-    , fVAccess(vTexture) {
+    , fVAccess(vTexture)
+    , fColorSpace(colorSpace) {
         this->addCoordTransform(&fCoordTransform);
         this->addTextureAccess(&fYAccess);
         this->addTextureAccess(&fUAccess);
@@ -88,21 +113,34 @@
         const YUVtoRGBEffect& s = CastEffect<YUVtoRGBEffect>(sBase);
         return fYAccess.getTexture() == s.fYAccess.getTexture() &&
                fUAccess.getTexture() == s.fUAccess.getTexture() &&
-               fVAccess.getTexture() == s.fVAccess.getTexture();
+               fVAccess.getTexture() == s.fVAccess.getTexture() &&
+               fColorSpace == s.getColorSpace();
     }
 
     GrCoordTransform fCoordTransform;
     GrTextureAccess fYAccess;
     GrTextureAccess fUAccess;
     GrTextureAccess fVAccess;
+    SkYUVColorSpace fColorSpace;
 
     typedef GrEffect INHERITED;
 };
 
+const GrGLfloat YUVtoRGBEffect::GLEffect::kJPEGConversionMatrix[16] = {
+    1.0f,  0.0f,      1.402f,  -0.701f,
+    1.0f, -0.34414f, -0.71414f, 0.529f,
+    1.0f,  1.772f,    0.0f,    -0.886f,
+    0.0f,  0.0f,      0.0f,     1.0};
+const GrGLfloat YUVtoRGBEffect::GLEffect::kRec601ConversionMatrix[16] = {
+    1.164f,  0.0f,    1.596f, -1.08175f,
+    1.164f, -0.391f, -0.813f,  0.529f,
+    1.164f,  2.018f,  0.0f,   -1.08175f,
+    0.0f,    0.0f,    0.0f,    1.0};
 }
 
 //////////////////////////////////////////////////////////////////////////////
 
-GrEffect* GrYUVtoRGBEffect::Create(GrTexture* yTexture, GrTexture* uTexture, GrTexture* vTexture) {
-    return YUVtoRGBEffect::Create(yTexture, uTexture, vTexture);
+GrEffect* GrYUVtoRGBEffect::Create(GrTexture* yTexture, GrTexture* uTexture, GrTexture* vTexture,
+                                   SkYUVColorSpace colorSpace) {
+    return YUVtoRGBEffect::Create(yTexture, uTexture, vTexture, colorSpace);
 }
diff --git a/src/gpu/effects/GrYUVtoRGBEffect.h b/src/gpu/effects/GrYUVtoRGBEffect.h
index 150acd5..52ccd23 100644
--- a/src/gpu/effects/GrYUVtoRGBEffect.h
+++ b/src/gpu/effects/GrYUVtoRGBEffect.h
@@ -8,6 +8,8 @@
 #ifndef GrYUVtoRGBEffect_DEFINED
 #define GrYUVtoRGBEffect_DEFINED
 
+#include "SkImageInfo.h"
+
 class GrEffect;
 class GrTexture;
 
@@ -15,7 +17,8 @@
     /**
      * Creates an effect that performs color conversion from YUV to RGB
      */
-    GrEffect* Create(GrTexture* yTexture, GrTexture* uTexture, GrTexture* vTexture);
+    GrEffect* Create(GrTexture* yTexture, GrTexture* uTexture, GrTexture* vTexture,
+                     SkYUVColorSpace colorSpace);
 };
 
 #endif
diff --git a/src/lazy/SkDiscardablePixelRef.h b/src/lazy/SkDiscardablePixelRef.h
index d31a040..142c8a4 100644
--- a/src/lazy/SkDiscardablePixelRef.h
+++ b/src/lazy/SkDiscardablePixelRef.h
@@ -50,8 +50,9 @@
 
     virtual bool onGetYUV8Planes(SkISize sizes[3],
                                  void* planes[3],
-                                 size_t rowBytes[3]) SK_OVERRIDE {
-        return fGenerator->getYUV8Planes(sizes, planes, rowBytes);
+                                 size_t rowBytes[3],
+                                 SkYUVColorSpace* colorSpace) SK_OVERRIDE {
+        return fGenerator->getYUV8Planes(sizes, planes, rowBytes, colorSpace);
     }
 
     friend bool SkInstallDiscardablePixelRef(SkImageGenerator*, SkBitmap*,
diff --git a/tests/ImageGeneratorTest.cpp b/tests/ImageGeneratorTest.cpp
index aaf149b..1f960ea 100644
--- a/tests/ImageGeneratorTest.cpp
+++ b/tests/ImageGeneratorTest.cpp
@@ -16,16 +16,19 @@
     sizes[2] = SkISize::Make( 50,  50);
     void*   planes[3] = { NULL };
     size_t  rowBytes[3] = { 0 };
+    SkYUVColorSpace colorSpace;
 
     // Check that the YUV decoding API does not cause any crashes
-    ig.getYUV8Planes(sizes, NULL, NULL);
-    ig.getYUV8Planes(sizes, planes, NULL);
-    ig.getYUV8Planes(sizes, NULL, rowBytes);
-    ig.getYUV8Planes(sizes, planes, rowBytes);
+    ig.getYUV8Planes(sizes, NULL, NULL, &colorSpace);
+    ig.getYUV8Planes(sizes, NULL, NULL, NULL);
+    ig.getYUV8Planes(sizes, planes, NULL, NULL);
+    ig.getYUV8Planes(sizes, NULL, rowBytes, NULL);
+    ig.getYUV8Planes(sizes, planes, rowBytes, NULL);
+    ig.getYUV8Planes(sizes, planes, rowBytes, &colorSpace);
 
     int dummy;
     planes[0] = planes[1] = planes[2] = &dummy;
     rowBytes[0] = rowBytes[1] = rowBytes[2] = 250;
 
-    ig.getYUV8Planes(sizes, planes, rowBytes);
+    ig.getYUV8Planes(sizes, planes, rowBytes, &colorSpace);
 }