Add extra blending modes.

This change adds the following blending modes for shaders and color filters:
Add
Multiply
Screen
Overlay
Darken
Lighten

Change-Id: Iff22f5ce6041b43c71b1857d73013f5010ab3413
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index 36413d7..cb1c333 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -331,13 +331,8 @@
 static SkiaShader* ComposeShader_postCreate2(JNIEnv* env, jobject o, SkShader* shader,
         SkiaShader* shaderA, SkiaShader* shaderB, SkPorterDuff::Mode porterDuffMode) {
 #ifdef USE_OPENGL_RENDERER
-    SkAutoUnref au(SkPorterDuff::CreateXfermode(porterDuffMode));
-    SkXfermode* mode = (SkXfermode*) au.get();
-    SkXfermode::Mode skiaMode;
-    if (!SkXfermode::IsMode(mode, &skiaMode)) {
-        skiaMode = SkXfermode::kSrcOver_Mode;
-    }
-    return new SkiaComposeShader(shaderA, shaderB, skiaMode, shader);
+    SkXfermode::Mode mode = SkPorterDuff::ToXfermodeMode(porterDuffMode);
+    return new SkiaComposeShader(shaderA, shaderB, mode, shader);
 #else
     return NULL;
 #endif
@@ -348,6 +343,7 @@
 #ifdef USE_OPENGL_RENDERER
     SkXfermode::Mode skiaMode;
     if (!SkXfermode::IsMode(mode, &skiaMode)) {
+        // TODO: Support other modes
         skiaMode = SkXfermode::kSrcOver_Mode;
     }
     return new SkiaComposeShader(shaderA, shaderB, skiaMode, shader);
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 2449b6d..39fe85a 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -137,7 +137,7 @@
 // PorterDuff snippets
 ///////////////////////////////////////////////////////////////////////////////
 
-const char* gPorterDuff[12] = {
+const char* gBlendOps[18] = {
         // Clear
         "return vec4(0.0, 0.0, 0.0, 0.0);\n",
         // Src
@@ -161,8 +161,26 @@
         // DstAtop
         "return vec4(dst.rgb * src.a + (1.0 - dst.a) * src.rgb, src.a);\n",
         // Xor
-        "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb, 1.0, "
+        "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb, "
                 "src.a + dst.a - 2.0 * src.a * dst.a);\n",
+        // Add
+        "return min(src + dst, 1.0);\n",
+        // Multiply
+        "return src * dst;\n",
+        // Screen
+        "return src + dst - src * dst;\n",
+        // Overlay
+        "return clamp(vec4(mix("
+                "2.0 * src.rgb * dst.rgb + src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a), "
+                "src.a * dst.a - 2.0 * (dst.a - dst.rgb) * (src.a - src.rgb) + src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a), "
+                "step(dst.a, 2.0 * dst.rgb)), "
+                "src.a + dst.a - src.a * dst.a), 0.0, 1.0);\n",
+        // Darken
+        "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb + "
+                "min(src.rgb * dst.a, dst.rgb * src.a), src.a + dst.a - src.a * dst.a);\n",
+        // Lighten
+        "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb + "
+                "max(src.rgb * dst.a, dst.rgb * src.a), src.a + dst.a - src.a * dst.a);\n",
 };
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -292,10 +310,10 @@
 
     // Generate required functions
     if (description.hasGradient && description.hasBitmap) {
-        generatePorterDuffBlend(shader, "blendShaders", description.shadersMode);
+        generateBlend(shader, "blendShaders", description.shadersMode);
     }
     if (description.colorOp == ProgramDescription::kColorBlend) {
-        generatePorterDuffBlend(shader, "blendColors", description.colorMode);
+        generateBlend(shader, "blendColors", description.colorMode);
     }
     if (description.isBitmapNpot) {
         generateTextureWrap(shader, description.bitmapWrapS, description.bitmapWrapT);
@@ -354,13 +372,12 @@
     return shader;
 }
 
-void ProgramCache::generatePorterDuffBlend(String8& shader, const char* name,
-        SkXfermode::Mode mode) {
+void ProgramCache::generateBlend(String8& shader, const char* name, SkXfermode::Mode mode) {
     shader.append("\nvec4 ");
     shader.append(name);
     shader.append("(vec4 src, vec4 dst) {\n");
     shader.append("    ");
-    shader.append(gPorterDuff[mode]);
+    shader.append(gBlendOps[mode]);
     shader.append("}\n");
 }
 
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
index a1a4a0e..54850ee 100644
--- a/libs/hwui/ProgramCache.h
+++ b/libs/hwui/ProgramCache.h
@@ -35,7 +35,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 // Debug
-#define DEBUG_PROGRAM_CACHE 0
+#define DEBUG_PROGRAM_CACHE 1
 
 // Debug
 #if DEBUG_PROGRAM_CACHE
@@ -57,9 +57,9 @@
 #define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600
 #define PROGRAM_KEY_BITMAP_WRAPT_MASK 0x1800
 
-// Support only the 12 Porter-Duff modes for now
-#define PROGRAM_MAX_XFERMODE 0xC
-#define PROGRAM_XFERMODE_SHADER_SHIFT 24
+// Encode the xfermodes on 6 bits
+#define PROGRAM_MAX_XFERMODE 0x1f
+#define PROGRAM_XFERMODE_SHADER_SHIFT 26
 #define PROGRAM_XFERMODE_COLOR_OP_SHIFT 20
 
 #define PROGRAM_BITMAP_WRAPS_SHIFT 9
@@ -177,7 +177,7 @@
     Program* generateProgram(const ProgramDescription& description, programid key);
     String8 generateVertexShader(const ProgramDescription& description);
     String8 generateFragmentShader(const ProgramDescription& description);
-    void generatePorterDuffBlend(String8& shader, const char* name, SkXfermode::Mode mode);
+    void generateBlend(String8& shader, const char* name, SkXfermode::Mode mode);
     void generateTextureWrap(String8& shader, GLenum wrapS, GLenum wrapT);
 
     void printLongString(const String8& shader) const;
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 90c82ee..181b4c8 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -180,5 +180,14 @@
             </intent-filter>
         </activity>
 
+        <activity
+                android:name="AdvancedBlendActivity"
+                android:label="_AdvancedBlend">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
     </application>
 </manifest>
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/AdvancedBlendActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/AdvancedBlendActivity.java
new file mode 100644
index 0000000..6c80a6d
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/AdvancedBlendActivity.java
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ */
+
+package com.google.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ComposeShader;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.Shader;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class AdvancedBlendActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(new ShadersView(this));
+    }
+
+    static class ShadersView extends View {
+        private BitmapShader mScaledShader;
+        private int mTexWidth;
+        private int mTexHeight;
+        private Paint mPaint;
+        private float mDrawWidth;
+        private float mDrawHeight;
+        private LinearGradient mHorGradient;
+        private ComposeShader mComposeShader;
+        private ComposeShader mCompose2Shader;
+        private ComposeShader mCompose3Shader;
+        private ComposeShader mCompose4Shader;
+        private ComposeShader mCompose5Shader;
+        private ComposeShader mCompose6Shader;
+        private BitmapShader mScaled2Shader;
+
+        ShadersView(Context c) {
+            super(c);
+
+            Bitmap texture = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+            mTexWidth = texture.getWidth();
+            mTexHeight = texture.getHeight();
+            mDrawWidth = mTexWidth * 2.2f;
+            mDrawHeight = mTexHeight * 1.2f;
+
+            mScaledShader = new BitmapShader(texture, Shader.TileMode.MIRROR,
+                    Shader.TileMode.MIRROR);
+            Matrix m2 = new Matrix();
+            m2.setScale(0.5f, 0.5f);
+            mScaledShader.setLocalMatrix(m2);
+            
+            mScaled2Shader = new BitmapShader(texture, Shader.TileMode.MIRROR,
+                    Shader.TileMode.MIRROR);
+            Matrix m3 = new Matrix();
+            m3.setScale(0.1f, 0.1f);
+            mScaled2Shader.setLocalMatrix(m3);
+
+            mHorGradient = new LinearGradient(0.0f, 0.0f, mDrawWidth, 0.0f,
+                    Color.BLACK, Color.WHITE, Shader.TileMode.CLAMP);
+            
+            mComposeShader = new ComposeShader(mScaledShader, mHorGradient,
+                    PorterDuff.Mode.DARKEN);
+            mCompose2Shader = new ComposeShader(mScaledShader, mHorGradient,
+                    PorterDuff.Mode.LIGHTEN);
+            mCompose3Shader = new ComposeShader(mScaledShader, mHorGradient,
+                    PorterDuff.Mode.MULTIPLY);
+            mCompose4Shader = new ComposeShader(mScaledShader, mHorGradient,
+                    PorterDuff.Mode.SCREEN);
+            mCompose5Shader = new ComposeShader(mScaledShader, mHorGradient,
+                    PorterDuff.Mode.ADD);
+            mCompose6Shader = new ComposeShader(mHorGradient, mScaledShader,
+                    PorterDuff.Mode.OVERLAY);
+
+            mPaint = new Paint();
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            canvas.drawRGB(255, 255, 255);
+
+            canvas.save();
+            canvas.translate(40.0f, 40.0f);
+
+            mPaint.setShader(mComposeShader);
+            canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+            
+            canvas.translate(0.0f, 40.0f + mDrawHeight);
+            mPaint.setShader(mCompose2Shader);
+            canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+            canvas.translate(0.0f, 40.0f + mDrawHeight);
+            mPaint.setShader(mCompose3Shader);
+            canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+            canvas.restore();
+            
+            canvas.save();
+            canvas.translate(40.0f + mDrawWidth + 40.0f, 40.0f);
+            
+            mPaint.setShader(mCompose4Shader);
+            canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+            canvas.translate(0.0f, 40.0f + mDrawHeight);
+            mPaint.setShader(mCompose5Shader);
+            canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+            canvas.translate(0.0f, 40.0f + mDrawHeight);
+            mPaint.setShader(mCompose6Shader);
+            canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+            
+            canvas.restore();
+        }
+    }
+}