Implement the color matrix filter in Ganesh. Also, fix and enable the color
authorsenorblanco@chromium.org <senorblanco@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 3 Jan 2012 20:51:57 +0000 (20:51 +0000)
committersenorblanco@chromium.org <senorblanco@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 3 Jan 2012 20:51:57 +0000 (20:51 +0000)
matrix test slide.  This was basically implemented in the same places where
the blending-based color filter was being done.  The shader simply does a mat4
matrix multiply and a vec4 add.

Review URL:  http://codereview.appspot.com/5489107/

git-svn-id: http://skia.googlecode.com/svn/trunk@2948 2bbb7eff-a529-9590-31e7-b0007b416f81

12 files changed:
gm/colormatrix.cpp
gyp/gmslides.gypi
include/gpu/GrPaint.h
src/effects/SkColorMatrixFilter.cpp
src/gpu/GrContext.cpp
src/gpu/GrDrawState.h
src/gpu/GrGLProgram.cpp
src/gpu/GrGLProgram.h
src/gpu/GrGLShaderVar.h
src/gpu/GrGpuGLShaders.cpp
src/gpu/GrGpuGLShaders.h
src/gpu/SkGpuDevice.cpp

index 2c6e44c1f2d3a51596ca5e22b6a4d4bc82382c25..7d57b3278b74b1bea607bc6ea99ba1096adbea76 100644 (file)
@@ -34,6 +34,7 @@ protected:
         bm.setConfig(SkBitmap::kARGB_8888_Config, width, height);
         bm.allocPixels();
         SkCanvas canvas(bm);
+        canvas.clear(0x0);
         for (int y = 0; y < height; ++y) {
             for (int x = 0; x < width; ++x) {
                 SkPaint paint;
index 953699de7e09281aeec5343a48d7734376972c06..c4a88d3e7e72b766fea1cdc070753e1057cb30b3 100644 (file)
@@ -8,9 +8,7 @@
     '../gm/bitmapfilters.cpp',
     '../gm/bitmapscroll.cpp',
     '../gm/blurs.cpp',
-    #Disabling this test until premult issues can be worked out.
-    #See http://code.google.com/p/skia/issues/detail?id=423
-    #'../gm/colormatrix.cpp',
+    '../gm/colormatrix.cpp',
     '../gm/complexclip.cpp',
     '../gm/complexclip2.cpp',
     '../gm/cubicpaths.cpp',
index ace11cc07440f06f3001ac2cbe9b1132203eaa9d..f1d74b286089250c3fef542b167e05265206c37b 100644 (file)
@@ -33,11 +33,13 @@ public:
     GrBlendCoeff                fDstBlendCoeff;
     bool                        fAntiAlias;
     bool                        fDither;
+    bool                        fColorMatrixEnabled;
 
     GrColor                     fColor;
 
     GrColor                     fColorFilterColor;
     SkXfermode::Mode            fColorFilterXfermode;
+    float                       fColorMatrix[20];
 
     void setTexture(int i, GrTexture* texture) {
         GrAssert((unsigned)i < kMaxTextures);
@@ -127,6 +129,8 @@ public:
 
         fColorFilterColor = paint.fColorFilterColor;
         fColorFilterXfermode = paint.fColorFilterXfermode;
+        memcpy(fColorMatrix, paint.fColorMatrix, sizeof(fColorMatrix));
+        fColorMatrixEnabled = paint.fColorMatrixEnabled;
 
         for (int i = 0; i < kMaxTextures; ++i) {
             GrSafeUnref(fTextures[i]);
@@ -165,6 +169,8 @@ public:
     void resetColorFilter() {
         fColorFilterXfermode = SkXfermode::kDst_Mode;
         fColorFilterColor = GrColorPackRGBA(0xff, 0xff, 0xff, 0xff);
+        memset(fColorMatrix, 0, sizeof(fColorMatrix));
+        fColorMatrixEnabled = false;
     }
 
     bool hasTexture() const {
index 5ed86981a2a3c99f36b980f115bcce5b3274e5a2..95f4b0bac4d3a325f8e78ad5adbeab60c7fbc5ab 100644 (file)
@@ -335,16 +335,17 @@ SkColorMatrixFilter::SkColorMatrixFilter(SkFlattenableReadBuffer& buffer)
 
 bool SkColorMatrixFilter::asColorMatrix(SkScalar matrix[20]) {
     int32_t* SK_RESTRICT array = fState.fArray;
+    int unshift = 16 - fState.fShift;
     for (int i = 0; i < 20; i++) {
-        matrix[i] = SkFixedToScalar(array[i]);
+        matrix[i] = SkFixedToScalar(array[i] << unshift);
     }
     if (NULL != fProc) {
         // Undo the offset applied to the constant column in setup().
-        SkScalar offset = SkFixedToScalar(1 << (fState.fShift - 1));
-        matrix[4] -= offset;
-        matrix[9] -= offset;
-        matrix[14] -= offset;
-        matrix[19] -= offset;
+        SkFixed offset = 1 << (fState.fShift - 1);
+        matrix[4] = SkFixedToScalar((array[4] - offset) << unshift);
+        matrix[9] = SkFixedToScalar((array[9] - offset) << unshift);
+        matrix[14] = SkFixedToScalar((array[14] - offset) << unshift);
+        matrix[19] = SkFixedToScalar((array[19] - offset) << unshift);
     }
     return true;
 }
index be3b7bebefc3a1d12819d8c5f325073d25b06ac2..a4066299eddef04acea6a291ced02d91549374f9 100644 (file)
@@ -1938,8 +1938,14 @@ void GrContext::setPaint(const GrPaint& paint, GrDrawTarget* target) {
     } else {
         drawState->disableState(GrDrawState::kHWAntialias_StateBit);
     }
+    if (paint.fColorMatrixEnabled) {
+        drawState->enableState(GrDrawState::kColorMatrix_StateBit);
+    } else {
+        drawState->disableState(GrDrawState::kColorMatrix_StateBit);
+    }
     drawState->setBlendFunc(paint.fSrcBlendCoeff, paint.fDstBlendCoeff);
     drawState->setColorFilter(paint.fColorFilterColor, paint.fColorFilterXfermode);
+    drawState->setColorMatrix(paint.fColorMatrix);
 
     if (paint.getActiveMaskStageMask() && !target->canApplyCoverage()) {
         GrPrintf("Partial pixel coverage will be incorrectly blended.\n");
index a1191152f58444296cc6424df4808b6be8a08084..dfe0d4b04fb1f3fa29e9bae7c80b84107a2f9005 100644 (file)
@@ -454,6 +454,22 @@ struct GrDrawState {
 
     /// @}
 
+    ///////////////////////////////////////////////////////////////////////////
+    /// @name Color Matrix
+    ////
+
+    /**
+     * Sets the color matrix to use for the next draw.
+     * @param matrix  the 5x4 matrix to apply to the incoming color
+     */
+    void setColorMatrix(const float matrix[20]) {
+        memcpy(fColorMatrix, matrix, sizeof(fColorMatrix));
+    }
+
+    const float* getColorMatrix() const { return fColorMatrix; }
+
+    /// @}
+
     ///////////////////////////////////////////////////////////////////////////
     // @name Edge AA
     // There are two ways to perform antialiasing using edge equations. One
@@ -569,6 +585,11 @@ struct GrDrawState {
          * source polygon is non-convex.
          */
         kEdgeAAConcave_StateBit = 0x10,
+        /**
+         * Draws will apply the color matrix, otherwise the color matrix is
+         * ignored.
+         */
+        kColorMatrix_StateBit   = 0x20,
 
         // Users of the class may add additional bits to the vector
         kDummyStateBit,
@@ -703,6 +724,7 @@ private:
     GrRenderTarget*         fRenderTarget;
     GrColor                 fColor;
     GrColor                 fColorFilterColor;
+    float                   fColorMatrix[20];
     GrStencilSettings       fStencilSettings;
     GrMatrix                fViewMatrix;
     // @{ Data for GrTesselatedPathRenderer
index b7e902d83ff4fc4386b65367ae28c3ec895e7076..ce87b857af9f7132ac1c91b1bdf7d31da4d63431 100644 (file)
@@ -94,6 +94,8 @@ typedef GrGLProgram::ProgramDesc::StageDesc StageDesc;
 #define COL_UNI_NAME "uColor"
 #define EDGES_UNI_NAME "uEdges"
 #define COL_FILTER_UNI_NAME "uColorFilter"
+#define COL_MATRIX_UNI_NAME "uColorMatrix"
+#define COL_MATRIX_VEC_UNI_NAME "uColorMatrixVec"
 
 namespace {
 inline void tex_attr_name(int coordIdx, GrStringBuilder* s) {
@@ -365,6 +367,14 @@ static void addColorFilter(GrStringBuilder* fsCode, const char * outputVar,
 
     add_helper(outputVar, colorStr.c_str(), constStr.c_str(), fsCode);
 }
+/**
+ * Adds code to the fragment shader code which modifies the color by
+ * the specified color matrix.
+ */
+static void addColorMatrix(GrStringBuilder* fsCode, const char * outputVar,
+                           const char* inColor) {
+    fsCode->appendf("%s = %s * %s + %s;\n", outputVar, COL_MATRIX_UNI_NAME, inColor, COL_MATRIX_VEC_UNI_NAME);
+}
 
 namespace {
 
@@ -798,10 +808,10 @@ bool GrGLProgram::genProgram(const GrGLInterface* gl,
                                          COL_FILTER_UNI_NAME);
         programData->fUniLocations.fColorFilterUni = kUseUniform;
     }
-
     bool wroteFragColorZero = false;
     if (SkXfermode::kZero_Coeff == uniformCoeff &&
-        SkXfermode::kZero_Coeff == colorCoeff) {
+        SkXfermode::kZero_Coeff == colorCoeff &&
+        !fProgramDesc.fColorMatrixEnabled) {
         segments.fFSCode.appendf("\t%s = %s;\n",
                                  fsColorOutput,
                                  all_zeros_vec(4));
@@ -822,6 +832,19 @@ bool GrGLProgram::genProgram(const GrGLInterface* gl,
                        colorCoeff, color);
         inColor = "filteredColor";
     }
+    if (fProgramDesc.fColorMatrixEnabled) {
+        segments.fFSUnis.push_back().set(GrGLShaderVar::kMat44f_Type,
+                                         GrGLShaderVar::kUniform_TypeModifier,
+                                         COL_MATRIX_UNI_NAME);
+        segments.fFSUnis.push_back().set(GrGLShaderVar::kVec4f_Type,
+                                         GrGLShaderVar::kUniform_TypeModifier,
+                                         COL_MATRIX_VEC_UNI_NAME);
+        programData->fUniLocations.fColorMatrixUni = kUseUniform;
+        programData->fUniLocations.fColorMatrixVecUni = kUseUniform;
+        segments.fFSCode.appendf("\tvec4 matrixedColor;\n");
+        addColorMatrix(&segments.fFSCode, "matrixedColor", inColor.c_str());
+        inColor = "matrixedColor";
+    }
 
     ///////////////////////////////////////////////////////////////////////////
     // compute the partial coverage (coverage stages and edge aa)
@@ -1243,6 +1266,16 @@ void GrGLProgram::getUniformLocationsAndInitCache(const GrGLInterface* gl,
         GrAssert(kUnusedUniform != programData->fUniLocations.fColorFilterUni);
     }
 
+    if (kUseUniform == programData->fUniLocations.fColorMatrixUni) {
+        GR_GL_CALL_RET(gl, programData->fUniLocations.fColorMatrixUni,
+                       GetUniformLocation(progID, COL_MATRIX_UNI_NAME));
+    }
+
+    if (kUseUniform == programData->fUniLocations.fColorMatrixVecUni) {
+        GR_GL_CALL_RET(gl, programData->fUniLocations.fColorMatrixVecUni,
+                       GetUniformLocation(progID, COL_MATRIX_VEC_UNI_NAME));
+    }
+
     if (kUseUniform == programData->fUniLocations.fEdgesUni) {
         GR_GL_CALL_RET(gl, programData->fUniLocations.fEdgesUni,
                        GetUniformLocation(progID, EDGES_UNI_NAME));
index 450da05ca1e1d33d7b8555b754c476b3f155e5ac..7d19f7ad547adfabad60fe8949327382ef3a1500 100644 (file)
@@ -216,9 +216,11 @@ public:
         int8_t fFirstCoverageStage;
         SkBool8 fEmitsPointSize;
         SkBool8 fEdgeAAConcave;
+        SkBool8 fColorMatrixEnabled;
 
         int8_t fEdgeAANumEdges;
         uint8_t fColorFilterXfermode;  // casts to enum SkXfermode::Mode
+        int8_t fPadding[3];
 
     } fProgramDesc;
     GR_STATIC_ASSERT(!(sizeof(ProgramDesc) % 4));
@@ -260,12 +262,16 @@ public:
         GrGLint fColorUni;
         GrGLint fEdgesUni;
         GrGLint fColorFilterUni;
+        GrGLint fColorMatrixUni;
+        GrGLint fColorMatrixVecUni;
         StageUniLocations fStages[GrDrawState::kNumStages];
         void reset() {
             fViewMatrixUni = kUnusedUniform;
             fColorUni = kUnusedUniform;
             fEdgesUni = kUnusedUniform;
             fColorFilterUni = kUnusedUniform;
+            fColorMatrixUni = kUnusedUniform;
+            fColorMatrixVecUni = kUnusedUniform;
             for (int s = 0; s < GrDrawState::kNumStages; ++s) {
                 fStages[s].reset();
             }
index 5f1ba03efb7b8ca4c120d03f0ea03a276da22a5f..1d5d7cad5dfa83f20817e7f28e0b093e224a94a8 100644 (file)
@@ -27,6 +27,7 @@ public:
         kVec3f_Type,
         kVec4f_Type,
         kMat33f_Type,
+        kMat44f_Type,
         kSampler2D_Type,
     };
 
@@ -242,6 +243,8 @@ public:
                 return "vec4";
             case kMat33f_Type:
                 return "mat3";
+            case kMat44f_Type:
+                return "mat4";
             case kSampler2D_Type:
                 return "sampler2D";
             default:
index 98ac1bfe2e43944c00847d6864de7a612d59c631..e9aea2abfa7c3a37377418f29da2f95e9095fa65 100644 (file)
@@ -234,6 +234,8 @@ bool GrGpuGLShaders::programUnitTest() {
             pdesc.fEdgeAANumEdges = 0;
         }
 
+        pdesc.fColorMatrixEnabled = random_bool(&random);
+
         if (this->getCaps().fDualSourceBlendingSupport) {
             pdesc.fDualSrcOutput = random_int(&random, ProgramDesc::kDualSrcOutputCnt);
         } else {
@@ -612,6 +614,28 @@ void GrGpuGLShaders::flushEdgeAAData() {
     }
 }
 
+void GrGpuGLShaders::flushColorMatrix() {
+    const ProgramDesc& desc = fCurrentProgram.getDesc();
+    int matrixUni = fProgramData->fUniLocations.fColorMatrixUni;
+    int vecUni = fProgramData->fUniLocations.fColorMatrixVecUni;
+    if (GrGLProgram::kUnusedUniform != matrixUni
+     && GrGLProgram::kUnusedUniform != vecUni) {
+        const float* m = this->getDrawState().getColorMatrix();
+        GrGLfloat mt[]  = {
+            m[0], m[5], m[10], m[15],
+            m[1], m[6], m[11], m[16],
+            m[2], m[7], m[12], m[17],
+            m[3], m[8], m[13], m[18],
+        };
+        static float scale = 1.0f / 255.0f;
+        GrGLfloat vec[] = {
+            m[4] * scale, m[9] * scale, m[14] * scale, m[19] * scale,
+        };
+        GL_CALL(UniformMatrix4fv(matrixUni, 1, false, mt));
+        GL_CALL(Uniform4fv(vecUni, 1, vec));
+    }
+}
+
 static const float ONE_OVER_255 = 1.f / 255.f;
 
 #define GR_COLOR_TO_VEC4(color) {\
@@ -731,6 +755,7 @@ bool GrGpuGLShaders::flushGraphicsState(GrPrimitiveType type) {
         }
     }
     this->flushEdgeAAData();
+    this->flushColorMatrix();
     resetDirtyFlags();
     return true;
 }
@@ -910,6 +935,8 @@ void GrGpuGLShaders::buildProgram(GrPrimitiveType type,
                                 SkXfermode::kDst_Mode :
                                 drawState.getColorFilterMode();
 
+    desc.fColorMatrixEnabled = drawState.isStateFlagEnabled(GrDrawState::kColorMatrix_StateBit);
+
     // no reason to do edge aa or look at per-vertex coverage if coverage is
     // ignored
     if (skipCoverage) {
index 36712963e8183e64e66c1b6fda467c91a039cc59..4b972b5f47e514a7a3cc4e0ab45f1a4f9cf55038 100644 (file)
@@ -75,6 +75,9 @@ private:
     // flushes the edges for edge AA
     void flushEdgeAAData();
 
+    // flushes the color matrix
+    void flushColorMatrix();
+
     static void DeleteProgram(const GrGLInterface* gl,
                               CachedData* programData);
 
index b80d3a6063fc2251276865cd7ef9da83cf892e20..6e9b3661f2b7f1f43000162772929d19c64afc37 100644 (file)
@@ -441,16 +441,23 @@ bool SkGpuDevice::skPaint2GrPaintNoShader(const SkPaint& skPaint,
     SkColorFilter* colorFilter = skPaint.getColorFilter();
     SkColor color;
     SkXfermode::Mode filterMode;
+    SkScalar matrix[20];
     if (colorFilter != NULL && colorFilter->asColorMode(&color, &filterMode)) {
+        grPaint->fColorMatrixEnabled = false;
         if (!constantColor) {
             grPaint->fColorFilterColor = SkGr::SkColor2GrColor(color);
             grPaint->fColorFilterXfermode = filterMode;
-            return true;
+        } else {
+            SkColor filtered = colorFilter->filterColor(skPaint.getColor());
+            grPaint->fColor = SkGr::SkColor2GrColor(filtered);
         }
-        SkColor filtered = colorFilter->filterColor(skPaint.getColor());
-        grPaint->fColor = SkGr::SkColor2GrColor(filtered);
+    } else if (colorFilter != NULL && colorFilter->asColorMatrix(matrix)) {
+        grPaint->fColorMatrixEnabled = true;
+        memcpy(grPaint->fColorMatrix, matrix, sizeof(matrix));
+        grPaint->fColorFilterXfermode = SkXfermode::kDst_Mode;
+    } else {
+        grPaint->resetColorFilter();
     }
-    grPaint->resetColorFilter();
     return true;
 }