Add dual source blending support for proper blending with coverage.
authorbsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 20 May 2011 14:13:56 +0000 (14:13 +0000)
committerbsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 20 May 2011 14:13:56 +0000 (14:13 +0000)
Review URL: http://codereview.appspot.com/4535088/

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

20 files changed:
gpu/include/GrDrawTarget.h
gpu/include/GrGLDefines.h
gpu/include/GrGLInterface.h
gpu/include/GrGpu.h
gpu/include/GrTypes.h
gpu/src/GrDrawTarget.cpp
gpu/src/GrGLInterface.cpp
gpu/src/GrGLProgram.cpp
gpu/src/GrGLProgram.h
gpu/src/GrGpuGL.cpp
gpu/src/GrGpuGL.h
gpu/src/GrGpuGLFixed.cpp
gpu/src/GrGpuGLShaders.cpp
gpu/src/mac/GrGLDefaultInterface_mac.cpp
gpu/src/mesa/GrGLDefaultInterface_mesa.cpp
gpu/src/unix/GrGLDefaultInterface_unix.cpp
gpu/src/win/GrGLDefaultInterface_win.cpp
gyp/skia.gyp
samplecode/SampleXfermodesBlur.cpp [new file with mode: 0644]
src/utils/win/SkOSWindow_Win.cpp

index e89a273..cd70d3e 100644 (file)
@@ -453,7 +453,7 @@ public:
      * @param srcCoef coeffecient applied to the src color.
      * @param dstCoef coeffecient applied to the dst color.
      */
-    void setBlendFunc(GrBlendCoeff srcCoef, GrBlendCoeff dstCoef);
+    void setBlendFunc(GrBlendCoeff srcCoeff, GrBlendCoeff dstCoeff);
 
     /**
      * Sets the blending function constant referenced by the following blending
index 2e22803..6c2483b 100644 (file)
 /*      GL_DST_ALPHA */
 /*      GL_ONE_MINUS_DST_ALPHA */
 
+/* ExtendedBlendFactors */
+#define GR_GL_SRC1_COLOR                     0x88F9
+#define GR_GL_ONE_MINUS_SRC1_COLOR           0x88FA
+/*      GL_SRC1_ALPHA */
+#define GR_GL_ONE_MINUS_SRC1_ALPHA           0x88FB
+
 /* BlendEquationSeparate */
 #define GR_GL_FUNC_ADD                       0x8006
 #define GR_GL_BLEND_EQUATION                 0x8009
index 591ab8c..150e8e4 100644 (file)
@@ -63,6 +63,7 @@ typedef unsigned int GrGLenum;
 typedef unsigned char GrGLboolean;
 typedef unsigned int GrGLbitfield;
 typedef signed char GrGLbyte;
+typedef char GrGLchar;
 typedef short GrGLshort;
 typedef int GrGLint;
 typedef int GrGLsizei;
@@ -199,6 +200,9 @@ extern "C" {
     // Buffer mapping (extension in ES).
     typedef GrGLvoid* (GR_GL_FUNCTION_TYPE *GrGLMapBufferProc)(GrGLenum target, GrGLenum access);
     typedef GrGLboolean (GR_GL_FUNCTION_TYPE *GrGLUnmapBufferProc)(GrGLenum target);
+
+    // Dual source blending
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLBindFragDataLocationIndexedProc)(GrGLuint program, GrGLuint colorNumber, GrGLuint index, const GrGLchar * name);
 }  // extern "C"
 
 /*
@@ -333,6 +337,9 @@ struct GrGLInterface {
     GrGLMapBufferProc fMapBuffer;
     GrGLUnmapBufferProc fUnmapBuffer;
 
+    // Dual Source Blending
+    GrGLBindFragDataLocationIndexedProc fBindFragDataLocationIndexed;
+
     // Code that initializes this struct using a static initializer should
     // make this the last entry in the static initializer. It can help to guard
     // against failing to initialize newly-added members of this struct.
index 19b8351..574a430 100644 (file)
@@ -60,6 +60,21 @@ class GrGpu : public GrDrawTarget {
 
 public:
     /**
+     * Additional blend coeffecients for dual source blending, not exposed
+     * through GrPaint/GrContext.
+     */
+    enum ExtendedBlendCoeffs {
+        // source 2 refers to second output color when
+        // using dual source blending.
+        kS2C_BlendCoeff = kPublicBlendCoeffCount,
+        kIS2C_BlendCoeff,
+        kS2A_BlendCoeff,
+        kIS2A_BlendCoeff,
+
+        kTotalBlendCoeffCount
+    };
+
+    /**
      *  Create an instance of GrGpu that matches the specified Engine backend.
      *  If the requested engine is not supported (at compile-time or run-time)
      *  this returns NULL.
@@ -190,6 +205,15 @@ public:
     bool supports4x4DownsampleFilter() const { return f4X4DownsampleFilterSupport; }
 
     /**
+     * Does this instance support dual-source blending? Required for proper
+     * blending with partial coverage with certain blend modes (dst coeff is
+     * not 1, ISA, or ISC)
+     */
+    bool supportsDualSourceBlending() const { 
+        return fDualSourceBlendingSupport; 
+    }
+
+    /**
      * Gets the minimum width of a render target. If a texture/rt is created
      * with a width less than this size the GrGpu object will clamp it to this
      * value.
@@ -371,6 +395,7 @@ protected:
     bool fAALineSupport;
     bool fFSAASupport;
     bool f4X4DownsampleFilterSupport; // supports GrSamplerState::k4x4Downsample_Filter
+    bool fDualSourceBlendingSupport;
 
     // set by subclass to true if index and vertex buffers can be locked, false
     // otherwise.
index 08b10f0..d09ac75 100644 (file)
@@ -232,7 +232,7 @@ enum GrBlendCoeff {
     kConstA_BlendCoeff,  //<! constant color alpha
     kIConstA_BlendCoeff, //<! one minus constant color alpha
 
-    kBlendCoeffCount
+    kPublicBlendCoeffCount
 };
 
 /**
index 250fbe1..2848999 100644 (file)
@@ -374,10 +374,34 @@ void GrDrawTarget::disableState(uint32_t bits) {
     fCurrDrawState.fFlagBits &= ~(bits);
 }
 
-void GrDrawTarget::setBlendFunc(GrBlendCoeff srcCoef,
-                                GrBlendCoeff dstCoef) {
-    fCurrDrawState.fSrcBlend = srcCoef;
-    fCurrDrawState.fDstBlend = dstCoef;
+void GrDrawTarget::setBlendFunc(GrBlendCoeff srcCoeff,
+                                GrBlendCoeff dstCoeff) {
+    fCurrDrawState.fSrcBlend = srcCoeff;
+    fCurrDrawState.fDstBlend = dstCoeff;
+#if GR_DEBUG
+    switch (dstCoeff) {
+    case kDC_BlendCoeff:
+    case kIDC_BlendCoeff:
+    case kDA_BlendCoeff:
+    case kIDA_BlendCoeff:
+        GrPrintf("Unexpected dst blend coeff. Won't work correctly with"
+                 "coverage stages.\n");
+        break;
+    default:
+        break;
+    }
+    switch (srcCoeff) {
+    case kSC_BlendCoeff:
+    case kISC_BlendCoeff:
+    case kSA_BlendCoeff:
+    case kISA_BlendCoeff:
+        GrPrintf("Unexpected src blend coeff. Won't work correctly with"
+                 "coverage stages.\n");
+        break;
+    default:
+        break;
+    }
+#endif
 }
 
 void GrDrawTarget::setColor(GrColor c) {
@@ -482,10 +506,16 @@ void GrDrawTarget::setIndexSourceToBuffer(const GrIndexBuffer* buffer) {
 ///////////////////////////////////////////////////////////////////////////////
 
 bool GrDrawTarget::canDisableBlend() const {
-    // If we're using edge antialiasing, we can't force blend off.
+    // If we compute a coverage value (using edge AA or a coverage stage) then
+    // we can't force blending off.
     if (fCurrDrawState.fEdgeAANumEdges > 0) {
         return false;
     }
+    for (int s = fCurrDrawState.fFirstCoverageStage; s < kNumStages; ++s) {
+        if (this->isStageEnabled(s)) {
+            return false;
+        }
+    }
 
     if ((kOne_BlendCoeff == fCurrDrawState.fSrcBlend) &&
         (kZero_BlendCoeff == fCurrDrawState.fDstBlend)) {
@@ -510,8 +540,8 @@ bool GrDrawTarget::canDisableBlend() const {
         return false;
     }
 
-    // ...and there isn't a texture with an alpha channel...
-    for (int s = 0; s < kNumStages; ++s) {
+    // ...and there isn't a texture stage with an alpha channel...
+    for (int s = 0; s < fCurrDrawState.fFirstCoverageStage; ++s) {
         if (this->isStageEnabled(s)) {
             GrAssert(NULL != fCurrDrawState.fTextures[s]);
 
index 0825a3f..5ecf8eb 100644 (file)
@@ -333,6 +333,15 @@ bool GrGLInterface::validate(GrEngine engine) const {
         }
     }
 
+    // Dual source blending
+    if (kDesktop_GrGLBinding == fBindingsExported  &&
+        (has_gl_extension_from_string("GL_ARB_blend_func_extended", ext) ||
+         (3 < major) || (3 == major && 3 <= minor))) {
+        if (NULL == fBindFragDataLocationIndexed) {
+            return false;
+        }
+    }
+
     return true;
 }
 
index 9a9e3c2..516bc98 100644 (file)
@@ -99,6 +99,9 @@ static inline const char* all_zeros_vec(int count) {
     return ZEROSVEC[count];
 }
 
+static inline const char* declared_color_output_name() { return "fsColorOut"; }
+static inline const char* dual_source_output_name() { return "dualSourceOut"; }
+
 static void tex_matrix_name(int stage, GrStringBuilder* s) {
 #if GR_GL_ATTRIBUTE_MATRICES
     *s = "aTexM";
@@ -144,12 +147,32 @@ GrGLProgram::GrGLProgram() {
 GrGLProgram::~GrGLProgram() {
 }
 
+void GrGLProgram::overrideBlend(GrBlendCoeff* srcCoeff, 
+                                GrBlendCoeff* dstCoeff) const {
+    switch (fProgramDesc.fDualSrcOutput) {
+        case ProgramDesc::kNone_DualSrcOutput:
+            break;
+        // the prog will write a coverage value to the secondary
+        // output and the dst is blended by one minus that value.
+        case ProgramDesc::kCoverage_DualSrcOutput:
+        case ProgramDesc::kCoverageISA_DualSrcOutput:
+        case ProgramDesc::kCoverageISC_DualSrcOutput:
+        *dstCoeff = (GrBlendCoeff)GrGpu::kIS2C_BlendCoeff;
+        break;
+        default:
+            GrCrash("Unexpected dual source blend output");
+            break;
+    }
+}
+
 void GrGLProgram::buildKey(GrBinHashKeyBuilder& key) const {
     // Add stage configuration to the key
     key.keyData(reinterpret_cast<const uint8_t*>(&fProgramDesc), sizeof(ProgramDesc));
 }
 
 // assigns modulation of two vars to an output var
+// vars can be vec4s or floats (or one of each)
+// result is always vec4
 // if either var is "" then assign to the other var
 // if both are "" then assign all ones
 static inline void modulate_helper(const char* outputVar,
@@ -167,15 +190,17 @@ static inline void modulate_helper(const char* outputVar,
     if (!has0 && !has1) {
         code->appendf("\t%s = %s;\n", outputVar, all_ones_vec(4));
     } else if (!has0) {
-        code->appendf("\t%s = %s;\n", outputVar, var1);
+        code->appendf("\t%s = vec4(%s);\n", outputVar, var1);
     } else if (!has1) {
-        code->appendf("\t%s = %s;\n", outputVar, var0);
+        code->appendf("\t%s = vec4(%s);\n", outputVar, var0);
     } else {
-        code->appendf("\t%s = %s * %s;\n", outputVar, var0, var1);
+        code->appendf("\t%s = vec4(%s * %s);\n", outputVar, var0, var1);
     }
 }
 
 // assigns addition of two vars to an output var
+// vars can be vec4s or floats (or one of each)
+// result is always vec4
 // if either var is "" then assign to the other var
 // if both are "" then assign all zeros
 static inline void add_helper(const char* outputVar,
@@ -193,11 +218,11 @@ static inline void add_helper(const char* outputVar,
     if (!has0 && !has1) {
         code->appendf("\t%s = %s;\n", outputVar, all_zeros_vec(4));
     } else if (!has0) {
-        code->appendf("\t%s = %s;\n", outputVar, var1);
+        code->appendf("\t%s = vec4(%s);\n", outputVar, var1);
     } else if (!has1) {
-        code->appendf("\t%s = %s;\n", outputVar, var0);
+        code->appendf("\t%s = vec4(%s);\n", outputVar, var0);
     } else {
-        code->appendf("\t%s = %s + %s;\n", outputVar, var0, var1);
+        code->appendf("\t%s = vec4(%s + %s);\n", outputVar, var0, var1);
     }
 }
 
@@ -325,6 +350,21 @@ bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const {
     needBlendInputs(uniformCoeff, colorCoeff,
                     &needColorFilterUniform, &needComputedColor);
 
+    // the dual source output has no canonical var name, have to
+    // declare an output, which is incompatible with gl_FragColor/gl_FragData.
+    const char* fsColorOutput;
+    bool dualSourceOutputWritten = false;
+    bool usingDeclaredOutputs = ProgramDesc::kNone_DualSrcOutput != 
+                                fProgramDesc.fDualSrcOutput;
+    if (usingDeclaredOutputs) {
+        GrAssert(0 == segments.fHeader.size());
+        segments.fHeader.printf("#version 150\n");
+        fsColorOutput = declared_color_output_name();
+        segments.fFSOutputs.appendf("out vec4 %s;\n", fsColorOutput);
+    } else {
+        fsColorOutput = "gl_FragColor";
+    }
+
 #if GR_GL_ATTRIBUTE_MATRICES
     segments.fVSAttrs += "attribute mat3 " VIEW_MATRIX_NAME ";\n";
     programData->fUniLocations.fViewMatrixUni = kSetAsAttribute;
@@ -433,7 +473,9 @@ bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const {
     bool wroteFragColorZero = false;
     if (SkXfermode::kZero_Coeff == uniformCoeff && 
         SkXfermode::kZero_Coeff == colorCoeff) {
-        segments.fFSCode.appendf("\tgl_FragColor = %s;\n", all_zeros_vec(4));
+        segments.fFSCode.appendf("\t%s = %s;\n", 
+                                 fsColorOutput,
+                                 all_zeros_vec(4));
         wroteFragColorZero = true;
     } else if (SkXfermode::kDst_Mode != fProgramDesc.fColorFilterXfermode) {
         segments.fFSCode.appendf("\tvec4 filteredColor;\n");
@@ -447,11 +489,11 @@ bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const {
     // compute the partial coverage (coverage stages and edge aa)
 
     GrStringBuilder inCoverage;
-    bool coverageIsScalar = false;
 
-    // we will want to compute coverage for some blend when there is no
-    // color (when dual source blending is enabled). But for now we have this if
-    if (!wroteFragColorZero) {
+    // we don't need to compute coverage at all if we know the final shader
+    // output will be zero and we don't have a dual src blend output.
+    if (!wroteFragColorZero ||
+        ProgramDesc::kNone_DualSrcOutput != fProgramDesc.fDualSrcOutput) {
         if (fProgramDesc.fEdgeAANumEdges > 0) {
             segments.fFSUnis.append("uniform vec3 " EDGES_UNI_NAME "[");
             segments.fFSUnis.appendS32(fProgramDesc.fEdgeAANumEdges);
@@ -483,7 +525,6 @@ bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const {
             }
             segments.fFSCode.append(";\n");
             inCoverage = "edgeAlpha";
-            coverageIsScalar = true;
         }
 
         GrStringBuilder outCoverage;
@@ -516,26 +557,48 @@ bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const {
                              &segments,
                              &programData->fUniLocations.fStages[s]);
                 inCoverage = outCoverage;
-                coverageIsScalar = false;
             }
         }
+        if (ProgramDesc::kNone_DualSrcOutput != fProgramDesc.fDualSrcOutput) {
+            segments.fFSOutputs.appendf("out vec4 %s;\n", 
+                                        dual_source_output_name());
+            bool outputIsZero = false;
+            GrStringBuilder coeff;
+            if (ProgramDesc::kCoverage_DualSrcOutput != 
+                fProgramDesc.fDualSrcOutput && !wroteFragColorZero) {
+                if (!inColor.size()) {
+                    outputIsZero = true;
+                } else {
+                    if (fProgramDesc.fDualSrcOutput == 
+                        ProgramDesc::kCoverageISA_DualSrcOutput) {
+                        coeff.printf("(1 - %s.a)", inColor.c_str());
+                    } else {
+                        coeff.printf("(vec4(1,1,1,1) - %s)", inColor.c_str());
+                    }
+                }
+            }
+            if (outputIsZero) {
+                segments.fFSCode.appendf("\t%s = %s;\n", 
+                                         dual_source_output_name(),
+                                         all_zeros_vec(4));
+            } else {
+                modulate_helper(dual_source_output_name(),
+                                coeff.c_str(),
+                                inCoverage.c_str(),
+                                &segments.fFSCode);
+            }
+            dualSourceOutputWritten = true;
+        }
     }
 
-    // TODO: ADD dual source blend output based on coverage here
-
     ///////////////////////////////////////////////////////////////////////////
     // combine color and coverage as frag color
 
     if (!wroteFragColorZero) {
-        if (coverageIsScalar && !inColor.size()) {
-            GrStringBuilder oldCoverage = inCoverage;
-            inCoverage.swap(oldCoverage);
-            inCoverage.printf("vec4(%s,%s,%s,%s)", oldCoverage.c_str(), 
-                              oldCoverage.c_str(), oldCoverage.c_str(), 
-                              oldCoverage.c_str());
-        }
-        modulate_helper("gl_FragColor", inColor.c_str(),
-                        inCoverage.c_str(), &segments.fFSCode);
+        modulate_helper(fsColorOutput,
+                         inColor.c_str(),
+                         inCoverage.c_str(),
+                         &segments.fFSCode);
     }
 
     segments.fVSCode.append("}\n");
@@ -548,7 +611,10 @@ bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const {
         return false;
     }
 
-    if (!this->bindAttribsAndLinkProgram(texCoordAttrs, programData)) {
+    if (!this->bindOutputsAttribsAndLinkProgram(texCoordAttrs,
+                                                usingDeclaredOutputs,
+                                                dualSourceOutputWritten,
+                                                programData)) {
         return false;
     }
 
@@ -560,10 +626,16 @@ bool GrGLProgram::genProgram(GrGLProgram::CachedData* programData) const {
 bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments,
                                  CachedData* programData) {
 
-    const char* strings[4];
-    int lengths[4];
+    static const int MAX_STRINGS = 6;
+    const char* strings[MAX_STRINGS];
+    int lengths[MAX_STRINGS];
     int stringCnt = 0;
 
+    if (segments.fHeader.size()) {
+        strings[stringCnt] = segments.fHeader.c_str();
+        lengths[stringCnt] = segments.fHeader.size();
+        ++stringCnt;
+    }
     if (segments.fVSUnis.size()) {
         strings[stringCnt] = segments.fVSUnis.c_str();
         lengths[stringCnt] = segments.fVSUnis.size();
@@ -586,12 +658,14 @@ bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments,
     ++stringCnt;
 
 #if PRINT_SHADERS
+    GrPrintf(segments.fHeader.c_str());
     GrPrintf(segments.fVSUnis.c_str());
     GrPrintf(segments.fVSAttrs.c_str());
     GrPrintf(segments.fVaryings.c_str());
     GrPrintf(segments.fVSCode.c_str());
     GrPrintf("\n");
 #endif
+    GrAssert(stringCnt <= MAX_STRINGS);
     programData->fVShaderID = CompileShader(GR_GL_VERTEX_SHADER,
                                         stringCnt,
                                         strings,
@@ -603,6 +677,11 @@ bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments,
 
     stringCnt = 0;
 
+    if (segments.fHeader.size()) {
+        strings[stringCnt] = segments.fHeader.c_str();
+        lengths[stringCnt] = segments.fHeader.size();
+        ++stringCnt;
+    }
     if (strlen(GrShaderPrecision()) > 1) {
         strings[stringCnt] = GrShaderPrecision();
         lengths[stringCnt] = strlen(GrShaderPrecision());
@@ -618,6 +697,11 @@ bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments,
         lengths[stringCnt] = segments.fVaryings.size();
         ++stringCnt;
     }
+    if (segments.fFSOutputs.size()) {
+        strings[stringCnt] = segments.fFSOutputs.c_str();
+        lengths[stringCnt] = segments.fFSOutputs.size();
+        ++stringCnt;
+    }
 
     GrAssert(segments.fFSCode.size());
     strings[stringCnt] = segments.fFSCode.c_str();
@@ -625,12 +709,15 @@ bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments,
     ++stringCnt;
 
 #if PRINT_SHADERS
+    GrPrintf(segments.fHeader.c_str());
     GrPrintf(GrShaderPrecision());
     GrPrintf(segments.fFSUnis.c_str());
     GrPrintf(segments.fVaryings.c_str());
+    GrPrintf(segments.fFSOutputs.c_str());
     GrPrintf(segments.fFSCode.c_str());
     GrPrintf("\n");
 #endif
+    GrAssert(stringCnt <= MAX_STRINGS);
     programData->fFShaderID = CompileShader(GR_GL_FRAGMENT_SHADER,
                                             stringCnt,
                                             strings,
@@ -639,6 +726,7 @@ bool GrGLProgram::CompileFSAndVS(const ShaderCodeSegments& segments,
     if (!programData->fFShaderID) {
         return false;
     }
+
     return true;
 }
 
@@ -678,8 +766,11 @@ GrGLuint GrGLProgram::CompileShader(GrGLenum type,
     return shader;
 }
 
-bool GrGLProgram::bindAttribsAndLinkProgram(GrStringBuilder texCoordAttrNames[],
-                                            CachedData* programData) const {
+bool GrGLProgram::bindOutputsAttribsAndLinkProgram(
+                                        GrStringBuilder texCoordAttrNames[],
+                                        bool bindColorOut,
+                                        bool bindDualSrcOut, 
+                                        CachedData* programData) const {
     programData->fProgramID = GR_GL(CreateProgram());
     if (!programData->fProgramID) {
         return false;
@@ -689,6 +780,15 @@ bool GrGLProgram::bindAttribsAndLinkProgram(GrStringBuilder texCoordAttrNames[],
     GR_GL(AttachShader(progID, programData->fVShaderID));
     GR_GL(AttachShader(progID, programData->fFShaderID));
 
+    if (bindColorOut) {
+        GR_GL(BindFragDataLocationIndexed(programData->fProgramID, 
+                                          0, 0, declared_color_output_name()));
+    }
+    if (bindDualSrcOut) {
+        GR_GL(BindFragDataLocationIndexed(programData->fProgramID,
+                                          0, 1, dual_source_output_name()));
+    }
+
     // Bind the attrib locations to same values for all shaders
     GR_GL(BindAttribLocation(progID, PositionAttributeIdx(), POS_ATTR_NAME));
     for (int t = 0; t < GrDrawTarget::kMaxTexCoords; ++t) {
@@ -699,7 +799,6 @@ bool GrGLProgram::bindAttribsAndLinkProgram(GrStringBuilder texCoordAttrNames[],
         }
     }
 
-
     if (kSetAsAttribute == programData->fUniLocations.fViewMatrixUni) {
         GR_GL(BindAttribLocation(progID,
                              ViewMatrixAttributeIdx(),
@@ -1072,7 +1171,7 @@ void GrGLProgram::genStageCode(int stageNum,
         segments->fFSCode.appendf("\t%s += %s(%s, %s + vec2(+%s.x,+%s.y))%s;\n", accumVar.c_str(), texFunc.c_str(), samplerName.c_str(), sampleCoords.c_str(), texelSizeName.c_str(), texelSizeName.c_str(), smear);
         segments->fFSCode.appendf("\t%s = .25 * %s%s;\n", fsOutColor, accumVar.c_str(), modulate.c_str());
     } else {
-        segments->fFSCode.appendf("\t%s = %s(%s, %s)%s %s;\n", fsOutColor, texFunc.c_str(), samplerName.c_str(), sampleCoords.c_str(), smear, modulate.c_str());
+        segments->fFSCode.appendf("\t%s = %s(%s, %s)%s%s;\n", fsOutColor, texFunc.c_str(), samplerName.c_str(), sampleCoords.c_str(), smear, modulate.c_str());
     }
 }
 
index d4a6406..2553cb7 100644 (file)
 
 #include "GrGLInterface.h"
 #include "GrStringBuilder.h"
-#include "GrDrawTarget.h"
+#include "GrGpu.h"
 
 #include "SkXfermode.h"
 
 class GrBinHashKeyBuilder;
 
 struct ShaderCodeSegments {
+    GrStringBuilder fHeader; // VS+FS, GLSL version, etc
     GrStringBuilder fVSUnis;
     GrStringBuilder fVSAttrs;
     GrStringBuilder fVaryings;
     GrStringBuilder fFSUnis;
+    GrStringBuilder fFSOutputs;
     GrStringBuilder fVSCode;
     GrStringBuilder fFSCode;
 };
@@ -65,6 +67,14 @@ public:
      */
     bool genProgram(CachedData* programData) const;
 
+     /**
+      * The shader may modify the blend coeffecients. Params are in/out
+      */
+     void overrideBlend(GrBlendCoeff* srcCoeff, GrBlendCoeff* dstCoeff) const;
+
+    /**
+     * Attribute indices
+     */
     static int PositionAttributeIdx() { return 0; }
     static int TexCoordAttributeIdx(int tcIdx) { return 1 + tcIdx; }
     static int ColorAttributeIdx() { return 1 + GrDrawTarget::kMaxTexCoords; }
@@ -94,6 +104,17 @@ private:
             kUniform_ColorType      = 2,
         } fColorType;
 
+        // Dual-src blending makes use of a secondary output color that can be 
+        // used as a per-pixel blend coeffecient. This controls whether a
+        // secondary source is output and what value it holds.
+        enum DualSrcOutput {
+            kNone_DualSrcOutput,
+            kCoverage_DualSrcOutput,
+            kCoverageISA_DualSrcOutput,
+            kCoverageISC_DualSrcOutput,
+            kDualSrcOutputCnt
+        } fDualSrcOutput;
+
         int  fFirstCoverageStage;
         bool fEmitsPointSize;
         int fEdgeAANumEdges;
@@ -181,7 +202,6 @@ public:
             memcpy(this, &other, sizeof(*this));
         }
 
-
     public:
 
         // IDs
@@ -238,8 +258,11 @@ private:
 
     // Creates a GL program ID, binds shader attributes to GL vertex attrs, and
     // links the program
-    bool bindAttribsAndLinkProgram(GrStringBuilder texCoordAttrNames[GrDrawTarget::kMaxTexCoords],
-                                   CachedData* programData) const;
+    bool bindOutputsAttribsAndLinkProgram(
+                GrStringBuilder texCoordAttrNames[GrDrawTarget::kMaxTexCoords],
+                bool bindColorOut,
+                bool bindDualSrcOut,
+                CachedData* programData) const;
 
     // Gets locations for all uniforms set to kUseUniform and initializes cache
     // to invalid values.
index 2e3563e..ff2d406 100644 (file)
@@ -42,9 +42,15 @@ static const GrGLenum gXfermodeCoeff2Blend[] = {
     GR_GL_ONE_MINUS_CONSTANT_COLOR,
     GR_GL_CONSTANT_ALPHA,
     GR_GL_ONE_MINUS_CONSTANT_ALPHA,
+
+    // extended blend coeffs
+    GR_GL_SRC1_COLOR,
+    GR_GL_ONE_MINUS_SRC1_COLOR,
+    GR_GL_SRC1_ALPHA,
+    GR_GL_ONE_MINUS_SRC1_ALPHA,
 };
 
-bool GrGpuGL::BlendCoefReferencesConstant(GrBlendCoeff coeff) {
+bool GrGpuGL::BlendCoeffReferencesConstant(GrBlendCoeff coeff) {
     static const bool gCoeffReferencesBlendConst[] = {
         false,
         false,
@@ -60,28 +66,40 @@ bool GrGpuGL::BlendCoefReferencesConstant(GrBlendCoeff coeff) {
         true,
         true,
         true,
+
+        // extended blend coeffs
+        false,
+        false,
+        false,
+        false,
     };
     return gCoeffReferencesBlendConst[coeff];
-    GR_STATIC_ASSERT(kBlendCoeffCount == GR_ARRAY_COUNT(gCoeffReferencesBlendConst));
+    GR_STATIC_ASSERT(kTotalBlendCoeffCount == GR_ARRAY_COUNT(gCoeffReferencesBlendConst));
+
+    GR_STATIC_ASSERT(0 == kZero_BlendCoeff);
+    GR_STATIC_ASSERT(1 == kOne_BlendCoeff);
+    GR_STATIC_ASSERT(2 == kSC_BlendCoeff);
+    GR_STATIC_ASSERT(3 == kISC_BlendCoeff);
+    GR_STATIC_ASSERT(4 == kDC_BlendCoeff);
+    GR_STATIC_ASSERT(5 == kIDC_BlendCoeff);
+    GR_STATIC_ASSERT(6 == kSA_BlendCoeff);
+    GR_STATIC_ASSERT(7 == kISA_BlendCoeff);
+    GR_STATIC_ASSERT(8 == kDA_BlendCoeff);
+    GR_STATIC_ASSERT(9 == kIDA_BlendCoeff);
+    GR_STATIC_ASSERT(10 == kConstC_BlendCoeff);
+    GR_STATIC_ASSERT(11 == kIConstC_BlendCoeff);
+    GR_STATIC_ASSERT(12 == kConstA_BlendCoeff);
+    GR_STATIC_ASSERT(13 == kIConstA_BlendCoeff);
+
+    GR_STATIC_ASSERT(14 == kS2C_BlendCoeff);
+    GR_STATIC_ASSERT(15 == kIS2C_BlendCoeff);
+    GR_STATIC_ASSERT(16 == kS2A_BlendCoeff);
+    GR_STATIC_ASSERT(17 == kIS2A_BlendCoeff);
+
+    // assertion for gXfermodeCoeff2Blend have to be in GrGpu scope
+    GR_STATIC_ASSERT(kTotalBlendCoeffCount == GR_ARRAY_COUNT(gXfermodeCoeff2Blend));
 }
 
-GR_STATIC_ASSERT(0 == kZero_BlendCoeff);
-GR_STATIC_ASSERT(1 == kOne_BlendCoeff);
-GR_STATIC_ASSERT(2 == kSC_BlendCoeff);
-GR_STATIC_ASSERT(3 == kISC_BlendCoeff);
-GR_STATIC_ASSERT(4 == kDC_BlendCoeff);
-GR_STATIC_ASSERT(5 == kIDC_BlendCoeff);
-GR_STATIC_ASSERT(6 == kSA_BlendCoeff);
-GR_STATIC_ASSERT(7 == kISA_BlendCoeff);
-GR_STATIC_ASSERT(8 == kDA_BlendCoeff);
-GR_STATIC_ASSERT(9 == kIDA_BlendCoeff);
-GR_STATIC_ASSERT(10 == kConstC_BlendCoeff);
-GR_STATIC_ASSERT(11 == kIConstC_BlendCoeff);
-GR_STATIC_ASSERT(12 == kConstA_BlendCoeff);
-GR_STATIC_ASSERT(13 == kIConstA_BlendCoeff);
-
-GR_STATIC_ASSERT(kBlendCoeffCount == GR_ARRAY_COUNT(gXfermodeCoeff2Blend));
-
 ///////////////////////////////////////////////////////////////////////////////
 
 void GrGpuGL::AdjustTextureMatrix(const GrGLTexture* texture,
@@ -1652,7 +1670,9 @@ void GrGpuGL::flushAAState(GrPrimitiveType type) {
     }
 }
 
-void GrGpuGL::flushBlend(GrPrimitiveType type) {
+void GrGpuGL::flushBlend(GrPrimitiveType type, 
+                         GrBlendCoeff srcCoeff, 
+                         GrBlendCoeff dstCoeff) {
     if (GrIsPrimTypeLines(type) && useSmoothLines()) {
         if (fHWBlendDisabled) {
             GR_GL(Enable(GR_GL_BLEND));
@@ -1676,15 +1696,15 @@ void GrGpuGL::flushBlend(GrPrimitiveType type) {
             fHWBlendDisabled = blendOff;
         }
         if (!blendOff) {
-            if (fHWDrawState.fSrcBlend != fCurrDrawState.fSrcBlend ||
-                  fHWDrawState.fDstBlend != fCurrDrawState.fDstBlend) {
-                GR_GL(BlendFunc(gXfermodeCoeff2Blend[fCurrDrawState.fSrcBlend],
-                                gXfermodeCoeff2Blend[fCurrDrawState.fDstBlend]));
-                fHWDrawState.fSrcBlend = fCurrDrawState.fSrcBlend;
-                fHWDrawState.fDstBlend = fCurrDrawState.fDstBlend;
+            if (fHWDrawState.fSrcBlend != srcCoeff ||
+                fHWDrawState.fDstBlend != dstCoeff) {
+                GR_GL(BlendFunc(gXfermodeCoeff2Blend[srcCoeff],
+                                gXfermodeCoeff2Blend[dstCoeff]));
+                fHWDrawState.fSrcBlend = srcCoeff;
+                fHWDrawState.fDstBlend = dstCoeff;
             }
-            if ((BlendCoefReferencesConstant(fCurrDrawState.fSrcBlend) ||
-                 BlendCoefReferencesConstant(fCurrDrawState.fDstBlend)) &&
+            if ((BlendCoeffReferencesConstant(srcCoeff) ||
+                 BlendCoeffReferencesConstant(dstCoeff)) &&
                 fHWDrawState.fBlendConstant != fCurrDrawState.fBlendConstant) {
 
                 float c[] = {
@@ -1787,7 +1807,6 @@ bool GrGpuGL::flushGLStateCommon(GrPrimitiveType type) {
     }
     this->flushRenderTarget(rect);
     this->flushAAState(type);
-    this->flushBlend(type);
     
     if ((fCurrDrawState.fFlagBits & kDither_StateBit) !=
         (fHWDrawState.fFlagBits & kDither_StateBit)) {
index 0d3e4b4..696b72f 100644 (file)
@@ -116,13 +116,17 @@ protected:
     // flushes state that is common to fixed and programmable GL
     // dither
     // line smoothing
-    // blend func
     // texture binding
     // sampler state (filtering, tiling)
     // FBO binding
     // line width
     bool flushGLStateCommon(GrPrimitiveType type);
 
+    // subclass should call this to flush the blend state
+    void flushBlend(GrPrimitiveType type,
+                    GrBlendCoeff srcCoeff,
+                    GrBlendCoeff dstCoeff);
+
     // adjusts texture matrix to account for orientation, size, and npotness
     static void AdjustTextureMatrix(const GrGLTexture* texture,
                                     GrSamplerState::SampleMode mode,
@@ -134,7 +138,7 @@ protected:
     static bool TextureMatrixIsIdentity(const GrGLTexture* texture,
                                         const GrSamplerState& sampler);
 
-    static bool BlendCoefReferencesConstant(GrBlendCoeff coeff);
+    static bool BlendCoeffReferencesConstant(GrBlendCoeff coeff);
 
 private:
 
@@ -156,7 +160,6 @@ private:
     void flushRenderTarget(const GrIRect* bound);
     void flushStencil();
     void flushAAState(GrPrimitiveType type);
-    void flushBlend(GrPrimitiveType type);
 
     void resolveRenderTarget(GrGLRenderTarget* texture);
 
index e197a82..65229dc 100644 (file)
@@ -57,6 +57,7 @@ static const GrGLenum gMatrixMode2Enum[] = {
 
 GrGpuGLFixed::GrGpuGLFixed() {
     f4X4DownsampleFilterSupport = false;
+    fDualSourceBlendingSupport = false;
 }
 
 GrGpuGLFixed::~GrGpuGLFixed() {
@@ -136,8 +137,8 @@ bool GrGpuGLFixed::flushGraphicsState(GrPrimitiveType type) {
     }
 
     if (GR_GL_SUPPORT_ES1) {
-        if (BlendCoefReferencesConstant(fCurrDrawState.fSrcBlend) ||
-            BlendCoefReferencesConstant(fCurrDrawState.fDstBlend)) {
+        if (BlendCoeffReferencesConstant(fCurrDrawState.fSrcBlend) ||
+            BlendCoeffReferencesConstant(fCurrDrawState.fDstBlend)) {
             unimpl("ES1 doesn't support blend constant");
             return false;
         }
@@ -147,6 +148,8 @@ bool GrGpuGLFixed::flushGraphicsState(GrPrimitiveType type) {
         return false;
     }
 
+    this->flushBlend(type, fCurrDrawState.fSrcBlend, fCurrDrawState.fDstBlend);
+
     if (fDirtyFlags.fRenderTargetChanged) {
         flushProjectionMatrix();
     }
index bbf9719..bf68def 100644 (file)
@@ -195,6 +195,15 @@ void GrGpuGLShaders::ProgramUnitTest() {
 
         pdesc.fEdgeAANumEdges = (random.nextF() * (getMaxEdges() + 1));
 
+        if (fDualSourceBlendingSupport) {
+            pdesc.fDualSrcOutput = 
+               (GrGLProgram::ProgramDesc::DualSrcOutput)
+               (int)(random.nextF() * GrGLProgram::ProgramDesc::kDualSrcOutputCnt);
+        } else {
+            pdesc.fDualSrcOutput = 
+                                GrGLProgram::ProgramDesc::kNone_DualSrcOutput;
+        }
+
         for (int s = 0; s < kNumStages; ++s) {
             // enable the stage?
             if (random.nextF() > .5f) {
@@ -234,7 +243,17 @@ void GrGpuGLShaders::ProgramUnitTest() {
 GrGpuGLShaders::GrGpuGLShaders() {
 
     resetContext();
+    int major, minor;
+    gl_version(&major, &minor);
+
     f4X4DownsampleFilterSupport = true;
+    if (GR_GL_SUPPORT_DESKTOP) {
+        fDualSourceBlendingSupport =
+            major > 3 ||(3 == major && 3 <= minor) ||
+            has_gl_extension("GL_ARB_blend_func_extended");
+    } else {
+        fDualSourceBlendingSupport = false;
+    }
 
     fProgramData = NULL;
     fProgramCache = new ProgramCache();
@@ -533,6 +552,11 @@ bool GrGpuGLShaders::flushGraphicsState(GrPrimitiveType type) {
         GR_GL(UseProgram(fProgramData->fProgramID));
         fHWProgramID = fProgramData->fProgramID;
     }
+    GrBlendCoeff srcCoeff = fCurrDrawState.fSrcBlend;
+    GrBlendCoeff dstCoeff = fCurrDrawState.fDstBlend;
+    
+    fCurrentProgram.overrideBlend(&srcCoeff, &dstCoeff);
+    this->flushBlend(type, srcCoeff, dstCoeff);
 
     this->flushColor();
 
@@ -677,16 +701,6 @@ void GrGpuGLShaders::buildProgram(GrPrimitiveType type) {
 
     desc.fColorFilterXfermode = fCurrDrawState.fColorFilterXfermode;
 
-    // coverage vs. color only applies when there is a color filter
-    // (currently)
-    if (SkXfermode::kDst_Mode != desc.fColorFilterXfermode) {
-        desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage;
-    } else {
-        // use canonical value when this won't affect generated
-        // code to prevent duplicate programs.
-        desc.fFirstCoverageStage = kNumStages;
-    }
-
 #if GR_AGGRESSIVE_SHADER_OPTS
     if (!requiresAttributeColors && (0xffffffff == fCurrDrawState.fColor)) {
         desc.fColorType = GrGLProgram::ProgramDesc::kNone_ColorType;
@@ -704,12 +718,15 @@ void GrGpuGLShaders::buildProgram(GrPrimitiveType type) {
 
     desc.fEdgeAANumEdges = fCurrDrawState.fEdgeAANumEdges;
 
+    int lastEnabledStage = -1;
+
     for (int s = 0; s < kNumStages; ++s) {
         GrGLProgram::ProgramDesc::StageDesc& stage = desc.fStages[s];
 
         stage.fEnabled = this->isStageEnabled(s);
 
         if (stage.fEnabled) {
+            lastEnabledStage = s;
             GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[s];
             GrAssert(NULL != texture);
             // we matrix to invert when orientation is TopDown, so make sure
@@ -775,6 +792,43 @@ void GrGpuGLShaders::buildProgram(GrPrimitiveType type) {
             stage.fModulation   = (GrGLProgram::ProgramDesc::StageDesc::Modulation)0;
         }
     }
+
+    desc.fDualSrcOutput = GrGLProgram::ProgramDesc::kNone_DualSrcOutput;
+    // use canonical value when coverage/color distinction won't affect
+    // generated code to prevent duplicate programs.
+    desc.fFirstCoverageStage = kNumStages;
+    if (fCurrDrawState.fFirstCoverageStage <= lastEnabledStage) {
+        // color filter is applied between color/coverage computation
+        if (SkXfermode::kDst_Mode != desc.fColorFilterXfermode) {
+            desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage;
+        }
+
+        // We could consider cases where the final color is solid (0xff alpha)
+        // and the dst coeff can correctly be set to a non-dualsrc gl value.
+        // (e.g. solid draw, and dst coeff is kZero. It's correct to make
+        // the dst coeff be kISA. Or solid draw with kSA can be tweaked to be
+        // kOne).
+        if (fDualSourceBlendingSupport) {
+            if (kZero_BlendCoeff == fCurrDrawState.fDstBlend) {
+                // write the coverage value to second color
+                desc.fDualSrcOutput = 
+                                GrGLProgram::ProgramDesc::kCoverage_DualSrcOutput;
+                desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage;
+            } else if (kSA_BlendCoeff == fCurrDrawState.fDstBlend) {
+                // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially 
+                // cover
+                desc.fDualSrcOutput = 
+                            GrGLProgram::ProgramDesc::kCoverageISA_DualSrcOutput;
+                desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage;
+            } else if (kSC_BlendCoeff == fCurrDrawState.fDstBlend) {
+                // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially
+                // cover
+                desc.fDualSrcOutput = 
+                        GrGLProgram::ProgramDesc::kCoverageISC_DualSrcOutput;
+                desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage;
+            }
+        }
+    }
 }
 
 
index b9396fa..fb5b182 100644 (file)
@@ -155,6 +155,8 @@ void GrGLSetDefaultGLInterface() {
         gDefaultInterface.fBlitFramebuffer = glBlitFramebufferEXT;
     #endif
 #endif
+        gDefaultInterface.fBindFragDataLocationIndexed = NULL;
+
         gDefaultInterface.fBindingsExported = kDesktop_GrGLBinding;
 
         gDefaultInterfaceInit = true;
index b494d76..0350c30 100644 (file)
@@ -174,6 +174,7 @@ void GrGLSetDefaultGLInterface() {
             // we must have FBOs
             return;
         }
+        GR_GL_GET_PROC(BindFragDataLocationIndexed);
         gDefaultInterface.fBindingsExported = kDesktop_GrGLBinding;
 
         gDefaultInterfaceInit = true;
index ba67065..3e9b975 100644 (file)
@@ -133,6 +133,7 @@ void GrGLSetDefaultGLInterface() {
         GR_GL_GET_PROC(VertexAttribPointer);
         gDefaultInterface.fVertexPointer = glVertexPointer;
         gDefaultInterface.fViewport = glViewport;
+        GR_GL_GET_PROC(BindFragDataLocationIndexed);
 
         // First look for GL3.0 FBO or GL_ARB_framebuffer_object (same since
         // GL_ARB_framebuffer_object doesn't use ARB suffix.)
index 53fb26a..428abb1 100644 (file)
@@ -139,6 +139,7 @@ void GrGLSetDefaultGLInterface() {
             GR_GL_GET_PROC(UseProgram);
             GR_GL_GET_PROC(VertexAttrib4fv);
             GR_GL_GET_PROC(VertexAttribPointer);
+            GR_GL_GET_PROC(BindFragDataLocationIndexed);
 
             // First look for GL3.0 FBO or GL_ARB_framebuffer_object (same since
             // GL_ARB_framebuffer_object doesn't use ARB suffix.)
index 4b0b6ae..7bafbf7 100644 (file)
         '../samplecode/SampleUnitMapper.cpp',
         '../samplecode/SampleVertices.cpp',
         '../samplecode/SampleXfermodes.cpp',
+        '../samplecode/SampleXfermodesBlur.cpp',
       ],
       'sources!': [
         '../samplecode/SampleSkLayer.cpp', #relies on SkMatrix44 which doesn't compile
diff --git a/samplecode/SampleXfermodesBlur.cpp b/samplecode/SampleXfermodesBlur.cpp
new file mode 100644 (file)
index 0000000..166e4e5
--- /dev/null
@@ -0,0 +1,182 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "Sk64.h"
+#include "SkCornerPathEffect.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkKernel33MaskFilter.h"
+#include "SkPath.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+
+#include "SkStream.h"
+#include "SkXMLParser.h"
+#include "SkColorPriv.h"
+#include "SkImageDecoder.h"
+#include "SkBlurMaskFilter.h"
+
+static void setNamedTypeface(SkPaint* paint, const char name[]) {
+    SkTypeface* face = SkTypeface::CreateFromName(name, SkTypeface::kNormal);
+    paint->setTypeface(face);
+    SkSafeUnref(face);
+}
+
+static uint16_t gBG[] = { 0xFFFF, 0xCCCF, 0xCCCF, 0xFFFF };
+
+class XfermodesBlurView : public SampleView {
+    SkBitmap    fBG;
+    SkBitmap    fSrcB, fDstB;
+
+    void draw_mode(SkCanvas* canvas, SkXfermode* mode, int alpha,
+                   SkScalar x, SkScalar y) {
+        SkPaint p;
+        SkMaskFilter* mf = SkBlurMaskFilter::Create(5, SkBlurMaskFilter::kNormal_BlurStyle, 0);
+        p.setMaskFilter(mf)->unref();
+
+        SkScalar ww = SkIntToScalar(W);
+        SkScalar hh = SkIntToScalar(H);
+
+        // draw a circle covering the upper
+        // left three quarters of the canvas
+        p.setColor(0xFFCC44FF);
+        SkRect r;
+        r.set(0, 0, ww*3/4, hh*3/4);
+        r.offset(x, y);
+        canvas->drawOval(r, p);
+
+        p.setXfermode(mode);
+
+        // draw a square overlapping the circle
+        // in the lower right of the canvas
+        p.setColor(0x00AA6633 | alpha << 24);
+        r.set(ww/3, hh/3, ww*19/20, hh*19/20);
+        r.offset(x, y);
+        canvas->drawRect(r, p);
+    }
+
+public:
+    const static int W = 64;
+    const static int H = 64;
+       XfermodesBlurView() {
+        const int W = 64;
+        const int H = 64;
+
+        fBG.setConfig(SkBitmap::kARGB_4444_Config, 2, 2, 4);
+        fBG.setPixels(gBG);
+        fBG.setIsOpaque(true);
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "XfermodesBlur");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
+
+        const struct {
+            SkXfermode::Mode  fMode;
+            const char*         fLabel;
+        } gModes[] = {
+            { SkXfermode::kClear_Mode,    "Clear"     },
+            { SkXfermode::kSrc_Mode,      "Src"       },
+            { SkXfermode::kDst_Mode,      "Dst"       },
+            { SkXfermode::kSrcOver_Mode,  "SrcOver"   },
+            { SkXfermode::kDstOver_Mode,  "DstOver"   },
+            { SkXfermode::kSrcIn_Mode,    "SrcIn"     },
+            { SkXfermode::kDstIn_Mode,    "DstIn"     },
+            { SkXfermode::kSrcOut_Mode,   "SrcOut"    },
+            { SkXfermode::kDstOut_Mode,   "DstOut"    },
+            { SkXfermode::kSrcATop_Mode,  "SrcATop"   },
+            { SkXfermode::kDstATop_Mode,  "DstATop"   },
+            { SkXfermode::kXor_Mode,      "Xor"       },
+
+            { SkXfermode::kPlus_Mode,         "Plus"          },
+            /*{ SkXfermode::kMultiply_Mode,     "Multiply"      },
+            { SkXfermode::kScreen_Mode,       "Screen"        },
+            { SkXfermode::kOverlay_Mode,      "Overlay"       },
+            { SkXfermode::kDarken_Mode,       "Darken"        },
+            { SkXfermode::kLighten_Mode,      "Lighten"       },
+            { SkXfermode::kColorDodge_Mode,   "ColorDodge"    },
+            { SkXfermode::kColorBurn_Mode,    "ColorBurn"     },
+            { SkXfermode::kHardLight_Mode,    "HardLight"     },
+            { SkXfermode::kSoftLight_Mode,    "SoftLight"     },
+            { SkXfermode::kDifference_Mode,   "Difference"    },
+            { SkXfermode::kExclusion_Mode,    "Exclusion"     },*/
+        };
+
+        const SkScalar w = SkIntToScalar(W);
+        const SkScalar h = SkIntToScalar(H);
+        SkShader* s = SkShader::CreateBitmapShader(fBG,
+                                                   SkShader::kRepeat_TileMode,
+                                                   SkShader::kRepeat_TileMode);
+        SkMatrix m;
+        m.setScale(SkIntToScalar(6), SkIntToScalar(6));
+        s->setLocalMatrix(m);
+
+        SkPaint labelP;
+        labelP.setAntiAlias(true);
+        labelP.setLCDRenderText(true);
+        labelP.setTextAlign(SkPaint::kCenter_Align);
+        setNamedTypeface(&labelP, "Menlo Regular");
+
+        const int W = 5;
+
+        SkScalar x0 = 0;
+        for (int twice = 0; twice < 2; twice++) {
+            SkScalar x = x0, y = 0;
+            for (size_t i = 0; i < SK_ARRAY_COUNT(gModes); i++) {
+                SkXfermode* mode = SkXfermode::Create(gModes[i].fMode);
+                SkAutoUnref aur(mode);
+                SkRect r;
+                r.set(x, y, x+w, y+h);
+
+                SkPaint p;
+                p.setStyle(SkPaint::kFill_Style);
+                p.setShader(s);
+                canvas->drawRect(r, p);
+
+                canvas->saveLayer(&r, NULL, SkCanvas::kARGB_ClipLayer_SaveFlag);
+                draw_mode(canvas, mode, twice ? 0x88 : 0xFF, r.fLeft, r.fTop);
+                canvas->restore();
+
+                r.inset(-SK_ScalarHalf, -SK_ScalarHalf);
+                p.setStyle(SkPaint::kStroke_Style);
+                p.setShader(NULL);
+                canvas->drawRect(r, p);
+
+                canvas->drawText(gModes[i].fLabel, strlen(gModes[i].fLabel),
+                                 x + w/2, y - labelP.getTextSize()/2, labelP);
+                x += w + SkIntToScalar(10);
+                if ((i % W) == W - 1) {
+                    x = x0;
+                    y += h + SkIntToScalar(30);
+                }
+            }
+            x0 += SkIntToScalar(400);
+        }
+        s->unref();
+    }
+
+private:
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new XfermodesBlurView; }
+static SkViewRegister reg(MyFactory);
index 48a6183..b63386c 100644 (file)
@@ -345,6 +345,7 @@ void kill_dummy(HWND dummy) {
 #define WGL_SUPPORT_OPENGL_ARB      0x2010
 #define WGL_DOUBLE_BUFFER_ARB       0x2011
 #define WGL_COLOR_BITS_ARB          0x2014
+#define WGL_ALPHA_BITS_ARB          0x201B
 #define WGL_STENCIL_BITS_ARB        0x2023
 #define WGL_FULL_ACCELERATION_ARB   0x2027
 
@@ -419,6 +420,7 @@ HGLRC create_gl(HWND hwnd) {
         WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
         WGL_SUPPORT_OPENGL_ARB, TRUE,
         WGL_COLOR_BITS_ARB, 24,
+        WGL_ALPHA_BITS_ARB, 8,
         WGL_STENCIL_BITS_ARB, 8,
 #if USE_MSAA
         WGL_SAMPLE_BUFFERS_ARB, TRUE,