Add support for ColorFilters.

Color filters are fully supported and can be used with shaders.

Change-Id: Id90ccf1c81cb462f2431f366f3f8f710d7971e04
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index fe1b524..8f28612 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -11,6 +11,7 @@
 	PatchCache.cpp \
 	Program.cpp \
 	ProgramCache.cpp \
+	SkiaColorFilter.cpp \
 	SkiaShader.cpp \
 	TextureCache.cpp
 
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index 99b34dd..7778290 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "OpenGLRenderer"
-
 #ifndef ANDROID_UI_EXTENSIONS_H
 #define ANDROID_UI_EXTENSIONS_H
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 187e9d8..d694039 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -132,6 +132,7 @@
 
     mCurrentProgram = NULL;
     mShader = NULL;
+    mColorFilter = NULL;
 
     memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
 
@@ -460,7 +461,6 @@
     const float u2 = srcRight / width;
     const float v2 = srcBottom / height;
 
-    // TODO: Do this in the shader
     resetDrawTextureTexCoords(u1, v1, u2, v2);
 
     drawTextureRect(dstLeft, dstTop, dstRight, dstBottom, texture, paint);
@@ -552,6 +552,8 @@
     mModelView.loadIdentity();
 
     GLuint textureUnit = 0;
+    // Needs to be set prior to calling FontRenderer::getTexture()
+    glActiveTexture(gTextureUnits[textureUnit]);
 
     ProgramDescription description;
     description.hasTexture = true;
@@ -559,10 +561,14 @@
     if (mShader) {
         mShader->describe(description, mExtensions);
     }
+    if (mColorFilter) {
+        mColorFilter->describe(description, mExtensions);
+    }
 
     useProgram(mProgramCache.get(description));
     mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
 
+    // Text is always blended, no need to check the shader
     chooseBlending(true, mode);
     bindTexture(mFontRenderer.getTexture(), GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, textureUnit);
     glUniform1i(mCurrentProgram->getUniform("sampler"), textureUnit);
@@ -578,6 +584,9 @@
     if (mShader) {
         mShader->setupProgram(mCurrentProgram, mModelView, *mSnapshot, &textureUnit);
     }
+    if (mColorFilter) {
+        mColorFilter->setupProgram(mCurrentProgram);
+    }
 
     // TODO: Implement scale properly
     const Rect& clip = mSnapshot->getLocalClip();
@@ -604,6 +613,18 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+// Color filters
+///////////////////////////////////////////////////////////////////////////////
+
+void OpenGLRenderer::resetColorFilter() {
+    mColorFilter = NULL;
+}
+
+void OpenGLRenderer::setupColorFilter(SkiaColorFilter* filter) {
+    mColorFilter = filter;
+}
+
+///////////////////////////////////////////////////////////////////////////////
 // Drawing implementation
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -631,6 +652,9 @@
     if (mShader) {
         mShader->describe(description, mExtensions);
     }
+    if (mColorFilter) {
+        mColorFilter->describe(description, mExtensions);
+    }
 
     // Build and use the appropriate shader
     useProgram(mProgramCache.get(description));
@@ -654,6 +678,9 @@
     if (mShader) {
         mShader->setupProgram(mCurrentProgram, mModelView, *mSnapshot, &textureUnit);
     }
+    if (mColorFilter) {
+        mColorFilter->setupProgram(mCurrentProgram);
+    }
 
     // Draw the mesh
     glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
@@ -680,6 +707,9 @@
         GLvoid* vertices, GLvoid* texCoords, GLvoid* indices, GLsizei elementsCount) {
     ProgramDescription description;
     description.hasTexture = true;
+    if (mColorFilter) {
+        mColorFilter->describe(description, mExtensions);
+    }
 
     mModelView.loadTranslate(left, top, 0.0f);
     mModelView.scale(right - left, bottom - top, 1.0f);
@@ -703,6 +733,11 @@
             gMeshStride, vertices);
     glVertexAttribPointer(texCoordsSlot, 2, GL_FLOAT, GL_FALSE, gMeshStride, texCoords);
 
+    // Color filter
+    if (mColorFilter) {
+        mColorFilter->setupProgram(mCurrentProgram);
+    }
+
     if (!indices) {
         glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
     } else {
@@ -712,8 +747,6 @@
 }
 
 void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode, bool isPremultiplied) {
-    // In theory we should not blend if the mode is Src, but it's rare enough
-    // that it's not worth it
     blend = blend || mode != SkXfermode::kSrcOver_Mode;
     if (blend) {
         if (!mBlend) {
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index dc0f50f..d2a291f 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -43,6 +43,7 @@
 #include "FontRenderer.h"
 #include "ProgramCache.h"
 #include "SkiaShader.h"
+#include "SkiaColorFilter.h"
 
 namespace android {
 namespace uirenderer {
@@ -95,6 +96,9 @@
     void resetShader();
     void setupShader(SkiaShader* shader);
 
+    void resetColorFilter();
+    void setupColorFilter(SkiaColorFilter* filter);
+
     void drawText(const char* text, int bytesCount, int count, float x, float y, SkPaint* paint);
 
 private:
@@ -281,6 +285,9 @@
     Program* mCurrentProgram;
     SkiaShader* mShader;
 
+    // Color filters
+    SkiaColorFilter* mColorFilter;
+
     // Used to draw textured quads
     TextureVertex mMeshVertices[4];
 
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 23923f6..3205258 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -27,8 +27,6 @@
 // Vertex shaders snippets
 ///////////////////////////////////////////////////////////////////////////////
 
-// TODO: Implement BitmapShader, implement repeat/mirror for npot
-
 const char* gVS_Header_Attributes =
         "attribute vec4 position;\n";
 const char* gVS_Header_Attributes_TexCoords =
@@ -85,10 +83,10 @@
         "uniform mat4 colorMatrix;\n"
         "uniform vec4 colorMatrixVector;\n",
         // Lighting
-        "uniform float lightingMul;\n"
-        "uniform float lightingAdd;\n",
+        "uniform vec4 lightingMul;\n"
+        "uniform vec4 lightingAdd;\n",
         // PorterDuff
-        "uniform vec4 colorBLend;\n"
+        "uniform vec4 colorBlend;\n"
 };
 const char* gFS_Main =
         "\nvoid main(void) {\n"
@@ -121,11 +119,14 @@
         // None
         "",
         // Matrix
+        // TODO: Fix premultiplied alpha computations for color matrix
         "    fragColor *= colorMatrix;\n"
-        "    fragColor += colorMatrixVector;\n",
+        "    fragColor += colorMatrixVector;\n"
+        "    fragColor.rgb *= fragColor.a;\n",
         // Lighting
-        "    fragColor *= lightingMul;\n"
-        "    fragColor += lightingAdd;\n",
+        "    float lightingAlpha = fragColor.a;\n"
+        "    fragColor = min(fragColor * lightingMul + (lightingAdd * lightingAlpha), lightingAlpha);\n"
+        "    fragColor.a = lightingAlpha;\n",
         // PorterDuff
         "    fragColor = blendColors(colorBlend, fragColor);\n"
 };
@@ -345,7 +346,11 @@
     // End the shader
     shader.append(gFS_Footer);
 
-    PROGRAM_LOGD("*** Generated fragment shader:\n\n%s", shader.string());
+    if (DEBUG_PROGRAM_CACHE) {
+        PROGRAM_LOGD("*** Generated fragment shader:\n\n");
+        printLongString(shader);
+    }
+
     return shader;
 }
 
@@ -391,5 +396,19 @@
     shader.append("}\n");
 }
 
+void ProgramCache::printLongString(const String8& shader) const {
+    ssize_t index = 0;
+    ssize_t lastIndex = 0;
+    const char* str = shader.string();
+    while ((index = shader.find("\n", index)) > -1) {
+        String8 line(str, index - lastIndex);
+        if (line.length() == 0) line.append("\n");
+        PROGRAM_LOGD("%s", line.string());
+        index++;
+        str += (index - lastIndex);
+        lastIndex = index;
+    }
+}
+
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
index d60f6ce..a1a4a0e 100644
--- a/libs/hwui/ProgramCache.h
+++ b/libs/hwui/ProgramCache.h
@@ -35,7 +35,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 // Debug
-#define DEBUG_PROGRAM_CACHE 1
+#define DEBUG_PROGRAM_CACHE 0
 
 // Debug
 #if DEBUG_PROGRAM_CACHE
@@ -180,6 +180,8 @@
     void generatePorterDuffBlend(String8& shader, const char* name, SkXfermode::Mode mode);
     void generateTextureWrap(String8& shader, GLenum wrapS, GLenum wrapT);
 
+    void printLongString(const String8& shader) const;
+
     KeyedVector<programid, Program*> mCache;
 
 }; // class ProgramCache
diff --git a/libs/hwui/SkiaColorFilter.cpp b/libs/hwui/SkiaColorFilter.cpp
new file mode 100644
index 0000000..fe57ae7
--- /dev/null
+++ b/libs/hwui/SkiaColorFilter.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkiaColorFilter.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Base color filter
+///////////////////////////////////////////////////////////////////////////////
+
+SkiaColorFilter::SkiaColorFilter(Type type, bool blend): mType(type), mBlend(blend) {
+}
+
+SkiaColorFilter::~SkiaColorFilter() {
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Color matrix filter
+///////////////////////////////////////////////////////////////////////////////
+
+SkiaColorMatrixFilter::SkiaColorMatrixFilter(float* matrix, float* vector):
+        SkiaColorFilter(kColorMatrix, true), mMatrix(matrix), mVector(vector) {
+}
+
+SkiaColorMatrixFilter::~SkiaColorMatrixFilter() {
+    delete[] mMatrix;
+    delete[] mVector;
+}
+
+void SkiaColorMatrixFilter::describe(ProgramDescription& description,
+        const Extensions& extensions) {
+    description.colorOp = ProgramDescription::kColorMatrix;
+}
+
+void SkiaColorMatrixFilter::setupProgram(Program* program) {
+    glUniformMatrix4fv(program->getUniform("colorMatrix"), 1, GL_FALSE, &mMatrix[0]);
+    glUniform4fv(program->getUniform("colorMatrixVector"), 1, mVector);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Lighting color filter
+///////////////////////////////////////////////////////////////////////////////
+
+SkiaLightingFilter::SkiaLightingFilter(int multiply, int add):
+        SkiaColorFilter(kLighting, true) {
+    mMulR = ((multiply >> 16) & 0xFF) / 255.0f;
+    mMulG = ((multiply >>  8) & 0xFF) / 255.0f;
+    mMulB = ((multiply      ) & 0xFF) / 255.0f;
+
+    mAddR = ((add >> 16) & 0xFF) / 255.0f;
+    mAddG = ((add >>  8) & 0xFF) / 255.0f;
+    mAddB = ((add      ) & 0xFF) / 255.0f;
+}
+
+void SkiaLightingFilter::describe(ProgramDescription& description, const Extensions& extensions) {
+    description.colorOp = ProgramDescription::kColorLighting;
+}
+
+void SkiaLightingFilter::setupProgram(Program* program) {
+    glUniform4f(program->getUniform("lightingMul"), mMulR, mMulG, mMulB, 1.0f);
+    glUniform4f(program->getUniform("lightingAdd"), mAddR, mAddG, mAddB, 0.0f);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Blend color filter
+///////////////////////////////////////////////////////////////////////////////
+
+SkiaBlendFilter::SkiaBlendFilter(int color, SkXfermode::Mode mode):
+        SkiaColorFilter(kBlend, true), mMode(mode) {
+    const int alpha = (color >> 24) & 0xFF;
+    mA = alpha / 255.0f;
+    mR = mA * ((color >> 16) & 0xFF) / 255.0f;
+    mG = mA * ((color >>  8) & 0xFF) / 255.0f;
+    mB = mA * ((color      ) & 0xFF) / 255.0f;
+}
+
+void SkiaBlendFilter::describe(ProgramDescription& description, const Extensions& extensions) {
+    description.colorOp = ProgramDescription::kColorBlend;
+    description.colorMode = mMode;
+}
+
+void SkiaBlendFilter::setupProgram(Program* program) {
+    glUniform4f(program->getUniform("colorBlend"), mR, mG, mB, mA);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/SkiaColorFilter.h b/libs/hwui/SkiaColorFilter.h
new file mode 100644
index 0000000..865b6f0
--- /dev/null
+++ b/libs/hwui/SkiaColorFilter.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UI_SKIA_COLOR_FILTER_H
+#define ANDROID_UI_SKIA_COLOR_FILTER_H
+
+#include <GLES2/gl2.h>
+
+#include "ProgramCache.h"
+#include "Extensions.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Base color filter
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Represents a Skia color filter. A color filter modifies a ProgramDescription
+ * and sets uniforms on the resulting shaders.
+ */
+struct SkiaColorFilter {
+    /**
+     * Type of Skia color filter in use.
+     */
+    enum Type {
+        kNone,
+        kColorMatrix,
+        kLighting,
+        kBlend,
+    };
+
+    SkiaColorFilter(Type type, bool blend);
+    virtual ~SkiaColorFilter();
+
+    virtual void describe(ProgramDescription& description, const Extensions& extensions) = 0;
+    virtual void setupProgram(Program* program) = 0;
+
+    inline bool blend() const {
+        return mBlend;
+    }
+
+    Type type() const {
+        return mType;
+    }
+
+protected:
+    Type mType;
+    bool mBlend;
+}; // struct SkiaColorFilter
+
+///////////////////////////////////////////////////////////////////////////////
+// Implementations
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * A color filter that multiplies the source color with a matrix and adds a vector.
+ */
+struct SkiaColorMatrixFilter: public SkiaColorFilter {
+    SkiaColorMatrixFilter(float* matrix, float* vector);
+    ~SkiaColorMatrixFilter();
+
+    void describe(ProgramDescription& description, const Extensions& extensions);
+    void setupProgram(Program* program);
+
+private:
+    float* mMatrix;
+    float* mVector;
+}; // struct SkiaColorMatrixFilter
+
+/**
+ * A color filters that multiplies the source color with a fixed value and adds
+ * another fixed value. Ignores the alpha channel of both arguments.
+ */
+struct SkiaLightingFilter: public SkiaColorFilter {
+    SkiaLightingFilter(int multiply, int add);
+
+    void describe(ProgramDescription& description, const Extensions& extensions);
+    void setupProgram(Program* program);
+
+private:
+    GLfloat mMulR, mMulG, mMulB;
+    GLfloat mAddR, mAddG, mAddB;
+}; // struct SkiaLightingFilter
+
+/**
+ * A color filters that blends the source color with a specified destination color
+ * and PorterDuff blending mode.
+ */
+struct SkiaBlendFilter: public SkiaColorFilter {
+    SkiaBlendFilter(int color, SkXfermode::Mode mode);
+
+    void describe(ProgramDescription& description, const Extensions& extensions);
+    void setupProgram(Program* program);
+
+private:
+    SkXfermode::Mode mMode;
+    GLfloat mR, mG, mB, mA;
+}; // struct SkiaBlendFilter
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_SKIA_COLOR_FILTER_H
diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h
index b5e6aeb..c19eac3 100644
--- a/libs/hwui/SkiaShader.h
+++ b/libs/hwui/SkiaShader.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef SKIA_SHADER_H
-#define SKIA_SHADER_H
+#ifndef ANDROID_UI_SKIA_SHADER_H
+#define ANDROID_UI_SKIA_SHADER_H
 
 #include <SkShader.h>
 #include <SkXfermode.h>
@@ -160,4 +160,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // SKIA_SHADER_H
+#endif // ANDROID_UI_SKIA_SHADER_H