From 271cffc77bd2fcb3458559e509634442517ca1e9 Mon Sep 17 00:00:00 2001 From: "bsalomon@google.com" Date: Fri, 20 May 2011 14:13:56 +0000 Subject: [PATCH] Add dual source blending support for proper blending with coverage. Review URL: http://codereview.appspot.com/4535088/ git-svn-id: http://skia.googlecode.com/svn/trunk@1390 2bbb7eff-a529-9590-31e7-b0007b416f81 --- gpu/include/GrDrawTarget.h | 2 +- gpu/include/GrGLDefines.h | 6 + gpu/include/GrGLInterface.h | 7 + gpu/include/GrGpu.h | 25 +++ gpu/include/GrTypes.h | 2 +- gpu/src/GrDrawTarget.cpp | 44 ++++- gpu/src/GrGLInterface.cpp | 9 + gpu/src/GrGLProgram.cpp | 161 ++++++++++++++---- gpu/src/GrGLProgram.h | 31 +++- gpu/src/GrGpuGL.cpp | 77 +++++---- gpu/src/GrGpuGL.h | 9 +- gpu/src/GrGpuGLFixed.cpp | 7 +- gpu/src/GrGpuGLShaders.cpp | 74 +++++++-- gpu/src/mac/GrGLDefaultInterface_mac.cpp | 2 + gpu/src/mesa/GrGLDefaultInterface_mesa.cpp | 1 + gpu/src/unix/GrGLDefaultInterface_unix.cpp | 1 + gpu/src/win/GrGLDefaultInterface_win.cpp | 1 + gyp/skia.gyp | 1 + samplecode/SampleXfermodesBlur.cpp | 182 +++++++++++++++++++++ src/utils/win/SkOSWindow_Win.cpp | 2 + 20 files changed, 556 insertions(+), 88 deletions(-) create mode 100644 samplecode/SampleXfermodesBlur.cpp diff --git a/gpu/include/GrDrawTarget.h b/gpu/include/GrDrawTarget.h index e89a2734f0..cd70d3e7c5 100644 --- a/gpu/include/GrDrawTarget.h +++ b/gpu/include/GrDrawTarget.h @@ -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 diff --git a/gpu/include/GrGLDefines.h b/gpu/include/GrGLDefines.h index 2e22803582..6c2483bacf 100644 --- a/gpu/include/GrGLDefines.h +++ b/gpu/include/GrGLDefines.h @@ -69,6 +69,12 @@ /* 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 diff --git a/gpu/include/GrGLInterface.h b/gpu/include/GrGLInterface.h index 591ab8c2e5..150e8e469a 100644 --- a/gpu/include/GrGLInterface.h +++ b/gpu/include/GrGLInterface.h @@ -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. diff --git a/gpu/include/GrGpu.h b/gpu/include/GrGpu.h index 19b83512e6..574a430be9 100644 --- a/gpu/include/GrGpu.h +++ b/gpu/include/GrGpu.h @@ -59,6 +59,21 @@ struct GrGpuStats { 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) @@ -189,6 +204,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 @@ -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. diff --git a/gpu/include/GrTypes.h b/gpu/include/GrTypes.h index 08b10f0033..d09ac75aa7 100644 --- a/gpu/include/GrTypes.h +++ b/gpu/include/GrTypes.h @@ -232,7 +232,7 @@ enum GrBlendCoeff { kConstA_BlendCoeff, // 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]); diff --git a/gpu/src/GrGLInterface.cpp b/gpu/src/GrGLInterface.cpp index 0825a3f925..5ecf8eb591 100644 --- a/gpu/src/GrGLInterface.cpp +++ b/gpu/src/GrGLInterface.cpp @@ -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; } diff --git a/gpu/src/GrGLProgram.cpp b/gpu/src/GrGLProgram.cpp index 9a9e3c2f91..516bc9892e 100644 --- a/gpu/src/GrGLProgram.cpp +++ b/gpu/src/GrGLProgram.cpp @@ -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(&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()); } } diff --git a/gpu/src/GrGLProgram.h b/gpu/src/GrGLProgram.h index d4a640626f..2553cb7005 100644 --- a/gpu/src/GrGLProgram.h +++ b/gpu/src/GrGLProgram.h @@ -19,17 +19,19 @@ #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. diff --git a/gpu/src/GrGpuGL.cpp b/gpu/src/GrGpuGL.cpp index 2e3563ec70..ff2d406e3d 100644 --- a/gpu/src/GrGpuGL.cpp +++ b/gpu/src/GrGpuGL.cpp @@ -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)) { diff --git a/gpu/src/GrGpuGL.h b/gpu/src/GrGpuGL.h index 0d3e4b4fed..696b72f290 100644 --- a/gpu/src/GrGpuGL.h +++ b/gpu/src/GrGpuGL.h @@ -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); diff --git a/gpu/src/GrGpuGLFixed.cpp b/gpu/src/GrGpuGLFixed.cpp index e197a82533..65229dc0a1 100644 --- a/gpu/src/GrGpuGLFixed.cpp +++ b/gpu/src/GrGpuGLFixed.cpp @@ -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(); } diff --git a/gpu/src/GrGpuGLShaders.cpp b/gpu/src/GrGpuGLShaders.cpp index bbf9719cf9..bf68deff77 100644 --- a/gpu/src/GrGpuGLShaders.cpp +++ b/gpu/src/GrGpuGLShaders.cpp @@ -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; + } + } + } } diff --git a/gpu/src/mac/GrGLDefaultInterface_mac.cpp b/gpu/src/mac/GrGLDefaultInterface_mac.cpp index b9396faace..fb5b182609 100644 --- a/gpu/src/mac/GrGLDefaultInterface_mac.cpp +++ b/gpu/src/mac/GrGLDefaultInterface_mac.cpp @@ -155,6 +155,8 @@ void GrGLSetDefaultGLInterface() { gDefaultInterface.fBlitFramebuffer = glBlitFramebufferEXT; #endif #endif + gDefaultInterface.fBindFragDataLocationIndexed = NULL; + gDefaultInterface.fBindingsExported = kDesktop_GrGLBinding; gDefaultInterfaceInit = true; diff --git a/gpu/src/mesa/GrGLDefaultInterface_mesa.cpp b/gpu/src/mesa/GrGLDefaultInterface_mesa.cpp index b494d76baa..0350c30dbf 100644 --- a/gpu/src/mesa/GrGLDefaultInterface_mesa.cpp +++ b/gpu/src/mesa/GrGLDefaultInterface_mesa.cpp @@ -174,6 +174,7 @@ void GrGLSetDefaultGLInterface() { // we must have FBOs return; } + GR_GL_GET_PROC(BindFragDataLocationIndexed); gDefaultInterface.fBindingsExported = kDesktop_GrGLBinding; gDefaultInterfaceInit = true; diff --git a/gpu/src/unix/GrGLDefaultInterface_unix.cpp b/gpu/src/unix/GrGLDefaultInterface_unix.cpp index ba670657c8..3e9b9754d6 100644 --- a/gpu/src/unix/GrGLDefaultInterface_unix.cpp +++ b/gpu/src/unix/GrGLDefaultInterface_unix.cpp @@ -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.) diff --git a/gpu/src/win/GrGLDefaultInterface_win.cpp b/gpu/src/win/GrGLDefaultInterface_win.cpp index 53fb26a17a..428abb1c2e 100644 --- a/gpu/src/win/GrGLDefaultInterface_win.cpp +++ b/gpu/src/win/GrGLDefaultInterface_win.cpp @@ -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.) diff --git a/gyp/skia.gyp b/gyp/skia.gyp index 4b0b6ae890..7bafbf7666 100644 --- a/gyp/skia.gyp +++ b/gyp/skia.gyp @@ -1504,6 +1504,7 @@ '../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 index 0000000000..166e4e5c7c --- /dev/null +++ b/samplecode/SampleXfermodesBlur.cpp @@ -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); diff --git a/src/utils/win/SkOSWindow_Win.cpp b/src/utils/win/SkOSWindow_Win.cpp index 48a6183d12..b63386c710 100644 --- a/src/utils/win/SkOSWindow_Win.cpp +++ b/src/utils/win/SkOSWindow_Win.cpp @@ -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, -- 2.34.1