YUV to RGB converter

This is a first piece of the GPU YUV decoder, which is the actual effect that performs the conversion. For now, it simply applies the conversion matrix, since it is all I need. I may add modes if different matrices need to be applied or if I add color profile support here.

I'll try to keep these cls short and easy to review, but there should be a few of them coming once this one is in.

BUG=skia:
R=senorblanco@chromium.org, senorblanco@google.com, reed@google.com, bsalomon@google.com

Author: sugoi@chromium.org

Review URL: https://codereview.chromium.org/378503006
diff --git a/gm/yuvtorgbeffect.cpp b/gm/yuvtorgbeffect.cpp
new file mode 100644
index 0000000..b1f495e
--- /dev/null
+++ b/gm/yuvtorgbeffect.cpp
@@ -0,0 +1,137 @@
+
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// This test only works with the GPU backend.
+
+#include "gm.h"
+
+#if SK_SUPPORT_GPU
+
+#include "GrContext.h"
+#include "GrTest.h"
+#include "effects/GrYUVtoRGBEffect.h"
+#include "SkBitmap.h"
+#include "SkGr.h"
+#include "SkGradientShader.h"
+
+namespace skiagm {
+/**
+ * This GM directly exercises GrYUVtoRGBEffect.
+ */
+class YUVtoRGBEffect : public GM {
+public:
+    YUVtoRGBEffect() {
+        this->setBGColor(0xFFFFFFFF);
+    }
+
+protected:
+    virtual SkString onShortName() SK_OVERRIDE {
+        return SkString("yuv_to_rgb_effect");
+    }
+
+    virtual SkISize onISize() SK_OVERRIDE {
+        return SkISize::Make(334, 64);
+    }
+
+    virtual uint32_t onGetFlags() const SK_OVERRIDE {
+        // This is a GPU-specific GM.
+        return kGPUOnly_Flag;
+    }
+
+    virtual void onOnceBeforeDraw() SK_OVERRIDE {
+        SkImageInfo info = SkImageInfo::MakeA8(24, 24);
+        fBmp[0].allocPixels(info);
+        fBmp[1].allocPixels(info);
+        fBmp[2].allocPixels(info);
+        unsigned char* pixels[3];
+        for (int i = 0; i < 3; ++i) {
+            pixels[i] = (unsigned char*)fBmp[i].getPixels();
+        }
+        int color[] = {0, 85, 170};
+        const int limit[] = {255, 0, 255};
+        const int invl[]  = {0, 255, 0};
+        const int inc[]   = {1, -1, 1};
+        for (int j = 0; j < 576; ++j) {
+            for (int i = 0; i < 3; ++i) {
+                pixels[i][j] = (unsigned char)color[i];
+                color[i] = (color[i] == limit[i]) ? invl[i] : color[i] + inc[i];
+            }
+        }
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
+        if (NULL == rt) {
+            return;
+        }
+        GrContext* context = rt->getContext();
+        if (NULL == context) {
+            return;
+        }
+
+        GrTestTarget tt;
+        context->getTestTarget(&tt);
+        if (NULL == tt.target()) {
+            SkDEBUGFAIL("Couldn't get Gr test target.");
+            return;
+        }
+
+        GrDrawState* drawState = tt.target()->drawState();
+
+        GrTexture* texture[3];
+        texture[0] = GrLockAndRefCachedBitmapTexture(context, fBmp[0], NULL);
+        texture[1] = GrLockAndRefCachedBitmapTexture(context, fBmp[1], NULL);
+        texture[2] = GrLockAndRefCachedBitmapTexture(context, fBmp[2], NULL);
+        if ((NULL == texture[0]) || (NULL == texture[1]) || (NULL == texture[2])) {
+            return;
+        }
+
+        static const SkScalar kDrawPad = 10.f;
+        static const SkScalar kTestPad = 10.f;
+
+        SkRect renderRect = SkRect::MakeWH(SkIntToScalar(fBmp[0].width()),
+                                           SkIntToScalar(fBmp[0].height()));
+        renderRect.outset(kDrawPad, kDrawPad);
+
+        SkScalar y = kDrawPad + kTestPad;
+        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}};
+
+        for (int i = 0; i < 6; ++i) {
+            SkAutoTUnref<GrEffectRef> 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;
+        }
+
+        GrUnlockAndUnrefCachedBitmapTexture(texture[0]);
+        GrUnlockAndUnrefCachedBitmapTexture(texture[1]);
+        GrUnlockAndUnrefCachedBitmapTexture(texture[2]);
+    }
+
+private:
+    SkBitmap fBmp[3];
+
+    typedef GM INHERITED;
+};
+
+DEF_GM( return SkNEW(YUVtoRGBEffect); )
+}
+
+#endif
diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi
index 6bf46a3..1ee27fd 100644
--- a/gyp/gmslides.gypi
+++ b/gyp/gmslides.gypi
@@ -182,6 +182,7 @@
     '../gm/xfermodes.cpp',
     '../gm/xfermodes2.cpp',
     '../gm/xfermodes3.cpp',
+    '../gm/yuvtorgbeffect.cpp',
 
     # Files needed by particular GMs
     '../src/utils/debugger/SkDrawCommand.h',
diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi
index 9b01a46..551f48d 100644
--- a/gyp/gpu.gypi
+++ b/gyp/gpu.gypi
@@ -173,6 +173,8 @@
       '<(skia_src_path)/gpu/effects/GrTextureDomain.h',
       '<(skia_src_path)/gpu/effects/GrTextureStripAtlas.cpp',
       '<(skia_src_path)/gpu/effects/GrTextureStripAtlas.h',
+      '<(skia_src_path)/gpu/effects/GrYUVtoRGBEffect.cpp',
+      '<(skia_src_path)/gpu/effects/GrYUVtoRGBEffect.h',
 
       '<(skia_src_path)/gpu/gl/GrGLAssembleInterface.cpp',
       '<(skia_src_path)/gpu/gl/GrGLAssembleInterface.h',
diff --git a/src/gpu/effects/GrYUVtoRGBEffect.cpp b/src/gpu/effects/GrYUVtoRGBEffect.cpp
new file mode 100644
index 0000000..1e3810f
--- /dev/null
+++ b/src/gpu/effects/GrYUVtoRGBEffect.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrYUVtoRGBEffect.h"
+
+#include "GrCoordTransform.h"
+#include "GrEffect.h"
+#include "gl/GrGLEffect.h"
+#include "GrTBackendEffectFactory.h"
+
+namespace {
+
+class YUVtoRGBEffect : public GrEffect {
+public:
+    static GrEffectRef* Create(GrTexture* yTexture, GrTexture* uTexture, GrTexture* vTexture) {
+        AutoEffectUnref effect(SkNEW_ARGS(YUVtoRGBEffect, (yTexture, uTexture, vTexture)));
+        return CreateEffectRef(effect);
+    }
+
+    static const char* Name() { return "YUV to RGB"; }
+
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
+        return GrTBackendEffectFactory<YUVtoRGBEffect>::getInstance();
+    }
+
+    virtual void getConstantColorComponents(GrColor* color,
+                                            uint32_t* validFlags) const SK_OVERRIDE {
+        // YUV is opaque
+        *color = 0xFF;
+        *validFlags = kA_GrColorComponentFlag;
+    }
+
+    class GLEffect : public GrGLEffect {
+    public:
+        // this class always generates the same code.
+        static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&) { return 0; }
+
+        GLEffect(const GrBackendEffectFactory& factory,
+                 const GrDrawEffect&)
+        : INHERITED(factory) {
+        }
+
+        virtual void emitCode(GrGLShaderBuilder* builder,
+                              const GrDrawEffect&,
+                              EffectKey,
+                              const char* outputColor,
+                              const char* inputColor,
+                              const TransformedCoordsArray& coords,
+                              const TextureSamplerArray& samplers) SK_OVERRIDE {
+            const char* yuvMatrix   = "yuvMatrix";
+            builder->fsCodeAppendf("\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);
+            builder->fsCodeAppendf("\t%s = vec4(\n\t\t", outputColor);
+            builder->fsAppendTextureLookup(samplers[0], coords[0].c_str(), coords[0].type());
+            builder->fsCodeAppend(".r,\n\t\t");
+            builder->fsAppendTextureLookup(samplers[1], coords[0].c_str(), coords[0].type());
+            builder->fsCodeAppend(".r,\n\t\t");
+            builder->fsAppendTextureLookup(samplers[2], coords[0].c_str(), coords[0].type());
+            builder->fsCodeAppendf(".r,\n\t\t1.0) * %s;\n", yuvMatrix);
+        }
+
+        typedef GrGLEffect INHERITED;
+    };
+
+private:
+    YUVtoRGBEffect(GrTexture* yTexture, GrTexture* uTexture, GrTexture* vTexture)
+    : fCoordTransform(kLocal_GrCoordSet, MakeDivByTextureWHMatrix(yTexture), yTexture)
+    , fYAccess(yTexture)
+    , fUAccess(uTexture)
+    , fVAccess(vTexture) {
+        this->addCoordTransform(&fCoordTransform);
+        this->addTextureAccess(&fYAccess);
+        this->addTextureAccess(&fUAccess);
+        this->addTextureAccess(&fVAccess);
+        this->setWillNotUseInputColor();
+    }
+
+    virtual bool onIsEqual(const GrEffect& sBase) const {
+        const YUVtoRGBEffect& s = CastEffect<YUVtoRGBEffect>(sBase);
+        return fYAccess.getTexture() == s.fYAccess.getTexture() &&
+               fUAccess.getTexture() == s.fUAccess.getTexture() &&
+               fVAccess.getTexture() == s.fVAccess.getTexture();
+    }
+
+    GrCoordTransform fCoordTransform;
+    GrTextureAccess fYAccess;
+    GrTextureAccess fUAccess;
+    GrTextureAccess fVAccess;
+
+    typedef GrEffect INHERITED;
+};
+
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+GrEffectRef* GrYUVtoRGBEffect::Create(GrTexture* yTexture,
+                                      GrTexture* uTexture,
+                                      GrTexture* vTexture) {
+    return YUVtoRGBEffect::Create(yTexture, uTexture, vTexture);
+}
diff --git a/src/gpu/effects/GrYUVtoRGBEffect.h b/src/gpu/effects/GrYUVtoRGBEffect.h
new file mode 100644
index 0000000..cc86af7
--- /dev/null
+++ b/src/gpu/effects/GrYUVtoRGBEffect.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrYUVtoRGBEffect_DEFINED
+#define GrYUVtoRGBEffect_DEFINED
+
+class GrEffectRef;
+class GrTexture;
+
+namespace GrYUVtoRGBEffect {
+    /**
+     * Creates an effect that performs color conversion from YUV to RGB
+     */
+    GrEffectRef* Create(GrTexture* yTexture, GrTexture* uTexture, GrTexture* vTexture);
+};
+
+#endif