From: bsalomon@google.com Date: Fri, 19 Apr 2013 15:03:21 +0000 (+0000) Subject: Add all remaining separable xfer modes to GPU backend. X-Git-Tag: submit/tizen/20180928.044319~12677 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=8da9bc751e1898d50dd84d7e5ca666b00e4ff624;p=platform%2Fupstream%2FlibSkiaSharp.git Add all remaining separable xfer modes to GPU backend. Review URL: https://codereview.chromium.org/14189024 git-svn-id: http://skia.googlecode.com/svn/trunk@8768 2bbb7eff-a529-9590-31e7-b0007b416f81 --- diff --git a/include/core/SkXfermode.h b/include/core/SkXfermode.h index c5da0625e5..f960e14af7 100644 --- a/include/core/SkXfermode.h +++ b/include/core/SkXfermode.h @@ -87,6 +87,9 @@ public: [a, c] - Resulting (alpha, color) values For these equations, the colors are in premultiplied state. If no xfermode is specified, kSrcOver is assumed. + The modes are ordered by those that can be expressed as a pair of Coeffs, followed by those + that aren't Coeffs but have separable r,g,b computations, and finally + those that are not separable. */ enum Mode { kClear_Mode, //!< [0, 0] @@ -107,9 +110,9 @@ public: // Following blend modes are defined in the CSS Compositing standard: // https://dvcs.w3.org/hg/FXTF/rawfile/tip/compositing/index.html#blending kScreen_Mode, - // all above modes can be expressed as pair of src/dst Coeffs - kCoeffModesCnt, - kOverlay_Mode = kCoeffModesCnt, + kLastCoeffMode = kScreen_Mode, + + kOverlay_Mode, kDarken_Mode, kLighten_Mode, kColorDodge_Mode, @@ -119,12 +122,12 @@ public: kDifference_Mode, kExclusion_Mode, kMultiply_Mode, + kLastSeparableMode = kMultiply_Mode, kHue_Mode, kSaturation_Mode, kColor_Mode, kLuminosity_Mode, - kLastMode = kLuminosity_Mode }; diff --git a/src/core/SkXfermode.cpp b/src/core/SkXfermode.cpp index a90fa666db..2312ff082e 100644 --- a/src/core/SkXfermode.cpp +++ b/src/core/SkXfermode.cpp @@ -950,14 +950,21 @@ void SkProcXfermode::toString(SkString* str) const { #include "gl/GrGLEffect.h" /** - * GrEffect that implements the kDarken_Mode Xfermode. It requires access to the dst pixel color - * in the shader. TODO: Make this work for all non-Coeff SkXfermode::Mode values. + * GrEffect that implements the all the separable xfer modes that cannot be expressed as Coeffs. */ -class DarkenEffect : public GrEffect { +class XferEffect : public GrEffect { public: - static GrEffectRef* Create() { - static AutoEffectUnref gEffect(SkNEW(DarkenEffect)); - return CreateEffectRef(gEffect); + static bool IsSupportedMode(SkXfermode::Mode mode) { + return mode > SkXfermode::kLastCoeffMode && mode <= SkXfermode::kLastSeparableMode; + } + + static GrEffectRef* Create(SkXfermode::Mode mode) { + if (!IsSupportedMode(mode)) { + return NULL; + } else { + AutoEffectUnref effect(SkNEW_ARGS(XferEffect, (mode))); + return CreateEffectRef(effect); + } } virtual void getConstantColorComponents(GrColor* color, @@ -966,10 +973,12 @@ public: } virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { - return GrTBackendEffectFactory::getInstance(); + return GrTBackendEffectFactory::getInstance(); } - static const char* Name() { return "XfermodeDarken"; } + static const char* Name() { return "XferEffect"; } + + SkXfermode::Mode mode() const { return fMode; } class GLEffect : public GrGLEffect { public: @@ -982,47 +991,214 @@ public: const char* outputColor, const char* inputColor, const TextureSamplerArray& samplers) SK_OVERRIDE { - const char* dstColorName = builder->dstColor(); - GrAssert(NULL != dstColorName); + const char* dstColor = builder->dstColor(); + GrAssert(NULL != dstColor); + + // We don't try to optimize for this case at all if (NULL == inputColor) { - // the input color is solid white - builder->fsCodeAppendf("\t\t%s.a = 1.0;\n", outputColor); - builder->fsCodeAppendf("\t\t%s.rgb = vec3(1.0, 1.0, 1.0) - %s.aaa + %s.rgb;\n", - outputColor, dstColorName, dstColorName); - } else { - builder->fsCodeAppendf("\t\t%s.a = 1.0 - (1.0 - %s.a) * (1.0 - %s.a);\n", - outputColor, dstColorName, inputColor); - builder->fsCodeAppendf("\t\t%s.rgb = min((1.0 - %s.a) * %s.rgb + %s.rgb," - " (1.0 - %s.a) * %s.rgb + %s.rgb);\n", - outputColor, - inputColor, dstColorName, inputColor, - dstColorName, inputColor, dstColorName); + builder->fsCodeAppendf("\tconst vec4 ones = %s;\n", GrGLSLOnesVecf(4)); + inputColor = "ones"; + } + + // These all perform src-over on the alpha channel. + builder->fsCodeAppendf("\t\t%s.a = %s.a + (1.0 - %s.a) * %s.a;\n", + outputColor, inputColor, inputColor, dstColor); + + switch (drawEffect.castEffect().mode()) { + case SkXfermode::kOverlay_Mode: + // Overlay is Hard-Light with the src and dst reversed + HardLight(builder, outputColor, dstColor, inputColor); + break; + case SkXfermode::kDarken_Mode: + builder->fsCodeAppendf("\t\t%s.rgb = min((1.0 - %s.a) * %s.rgb + %s.rgb, " + "(1.0 - %s.a) * %s.rgb + %s.rgb);\n", + outputColor, + inputColor, dstColor, inputColor, + dstColor, inputColor, dstColor); + break; + case SkXfermode::kLighten_Mode: + builder->fsCodeAppendf("\t\t%s.rgb = max((1.0 - %s.a) * %s.rgb + %s.rgb, " + "(1.0 - %s.a) * %s.rgb + %s.rgb);\n", + outputColor, + inputColor, dstColor, inputColor, + dstColor, inputColor, dstColor); + break; + case SkXfermode::kColorDodge_Mode: + ColorDodgeComponent(builder, outputColor, inputColor, dstColor, 'r'); + ColorDodgeComponent(builder, outputColor, inputColor, dstColor, 'g'); + ColorDodgeComponent(builder, outputColor, inputColor, dstColor, 'b'); + break; + case SkXfermode::kColorBurn_Mode: + ColorBurnComponent(builder, outputColor, inputColor, dstColor, 'r'); + ColorBurnComponent(builder, outputColor, inputColor, dstColor, 'g'); + ColorBurnComponent(builder, outputColor, inputColor, dstColor, 'b'); + break; + case SkXfermode::kHardLight_Mode: + HardLight(builder, outputColor, inputColor, dstColor); + break; + case SkXfermode::kSoftLight_Mode: + builder->fsCodeAppendf("\t\tif (0.0 == %s.a) {\n", dstColor); + builder->fsCodeAppendf("\t\t\t%s.rgba = %s;\n", outputColor, inputColor); + builder->fsCodeAppendf("\t\t} else {\n"); + SoftLightComponentPosDstAlpha(builder, outputColor, inputColor, dstColor, 'r'); + SoftLightComponentPosDstAlpha(builder, outputColor, inputColor, dstColor, 'g'); + SoftLightComponentPosDstAlpha(builder, outputColor, inputColor, dstColor, 'b'); + builder->fsCodeAppendf("\t\t}\n"); + break; + case SkXfermode::kDifference_Mode: + builder->fsCodeAppendf("\t\t%s.rgb = %s.rgb + %s.rgb -" + "2.0 * min(%s.rgb * %s.a, %s.rgb * %s.a);\n", + outputColor, inputColor, dstColor, inputColor, dstColor, + dstColor, inputColor); + break; + case SkXfermode::kExclusion_Mode: + builder->fsCodeAppendf("\t\t%s.rgb = %s.rgb + %s.rgb - " + "2.0 * %s.rgb * %s.rgb;\n", + outputColor, dstColor, inputColor, dstColor, inputColor); + break; + case SkXfermode::kMultiply_Mode: + builder->fsCodeAppendf("\t\t%s.rgb = (1.0 - %s.a) * %s.rgb + " + "(1.0 - %s.a) * %s.rgb + " + "%s.rgb * %s.rgb;\n", + outputColor, inputColor, dstColor, dstColor, inputColor, + inputColor, dstColor); + break; + case SkXfermode::kHue_Mode: + case SkXfermode::kSaturation_Mode: + case SkXfermode::kColor_Mode: + case SkXfermode::kLuminosity_Mode: + GrCrash("Unimplemented XferEffect mode."); + break; + default: + GrCrash("Unknown XferEffect mode."); + break; } } - static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&) { return 0; } + static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { + return drawEffect.castEffect().mode(); + } virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {} private: + static void HardLight(GrGLShaderBuilder* builder, + const char* final, + const char* src, + const char* dst) { + builder->fsCodeAppendf("\t\t%s.a = 1.0 - (1.0 - %s.a) * (1.0 - %s.a);\n", + final, dst, src); + builder->fsCodeAppendf("\t\t%s.rgb = mix(2.0 * %s.rgb * %s.rgb, ", + final, src, dst); + builder->fsCodeAppendf("%s.aaa * %s.aaa - 2.0 * (%s.aaa - %s.rgb) * (%s.aaa - %s.rgb),", + src, dst, dst, dst, src, src); + builder->fsCodeAppendf("vec3(greaterThan(2.0 * %s.rgb, %s.aaa)));\n", + src, src); + builder->fsCodeAppendf("\t\t%s.rgb += %s.rgb * (1.0 - %s.a) + %s.rgb * (1.0 - %s.a);\n", + final, src, dst, dst, src); + } + + // Does one component of color-dodge + static void ColorDodgeComponent(GrGLShaderBuilder* builder, + const char* final, + const char* src, + const char* dst, + const char component) { + builder->fsCodeAppendf("\t\tif (0.0 == %s.%c) {\n", dst, component); + builder->fsCodeAppendf("\t\t\t%s.%c = %s.%c * (1 - %s.a);\n", + final, component, src, component, dst); + builder->fsCodeAppend("\t\t} else {\n"); + builder->fsCodeAppendf("\t\t\tfloat d = %s.a - %s.%c;\n", src, src, component); + builder->fsCodeAppend("\t\t\tif (0 == d) {\n"); + builder->fsCodeAppendf("\t\t\t\t%s.%c = %s.a * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);\n", + final, component, src, dst, src, component, dst, dst, component, + src); + builder->fsCodeAppend("\t\t\t} else {\n"); + builder->fsCodeAppendf("\t\t\t\td = min(%s.a, %s.%c * %s.a / d);\n", + dst, dst, component, src); + builder->fsCodeAppendf("\t\t\t\t%s.%c = d * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);\n", + final, component, src, src, component, dst, dst, component, src); + builder->fsCodeAppend("\t\t\t}\n"); + builder->fsCodeAppend("\t\t}\n"); + } + + // Does one component of color-burn + static void ColorBurnComponent(GrGLShaderBuilder* builder, + const char* final, + const char* src, + const char* dst, + const char component) { + builder->fsCodeAppendf("\t\tif (%s.a == %s.%c) {\n", dst, dst, component); + builder->fsCodeAppendf("\t\t\t%s.%c = %s.a * %s.a + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);\n", + final, component, src, dst, src, component, dst, dst, component, + src); + builder->fsCodeAppendf("\t\t} else if (0.0 == %s.%c) {\n", src, component); + builder->fsCodeAppendf("\t\t\t%s.%c = %s.%c * (1.0 - %s.a);\n", + final, component, dst, component, src); + builder->fsCodeAppend("\t\t} else {\n"); + builder->fsCodeAppendf("\t\t\tfloat d = max(0.0, %s.a - (%s.a - %s.%c) * %s.a / %s.%c);\n", + dst, dst, dst, component, src, src, component); + builder->fsCodeAppendf("\t\t\t%s.%c = %s.a * d + %s.%c * (1.0 - %s.a) + %s.%c * (1.0 - %s.a);\n", + final, component, src, src, component, dst, dst, component, src); + builder->fsCodeAppend("\t\t}\n"); + } + + // Does one component of soft-light. Caller should have already checked that dst alpha > 0. + static void SoftLightComponentPosDstAlpha(GrGLShaderBuilder* builder, + const char* final, + const char* src, + const char* dst, + const char component) { + // if (2S < Sa) + builder->fsCodeAppendf("\t\t\tif (2.0 * %s.%c <= %s.a) {\n", src, component, src); + // (D^2 (Sa-2 S))/Da+(1-Da) S+D (-Sa+2 S+1) + builder->fsCodeAppendf("\t\t\t\t%s.%c = (%s.%c*%s.%c*(%s.a - 2*%s.%c)) / %s.a + (1 - %s.a) * %s.%c + %s.%c*(-%s.a + 2*%s.%c + 1);\n", + final, component, dst, component, dst, component, src, src, + component, dst, dst, src, component, dst, component, src, src, + component); + // else if (4D < Da) + builder->fsCodeAppendf("\t\t\t} else if (4.0 * %s.%c <= %s.a) {\n", + dst, component, dst); + builder->fsCodeAppendf("\t\t\t\tfloat DSqd = %s.%c * %s.%c;\n", + dst, component, dst, component); + builder->fsCodeAppendf("\t\t\t\tfloat DCub = DSqd * %s.%c;\n", dst, component); + builder->fsCodeAppendf("\t\t\t\tfloat DaSqd = %s.a * %s.a;\n", dst, dst); + builder->fsCodeAppendf("\t\t\t\tfloat DaCub = DaSqd * %s.a;\n", dst); + // (Da^3 (-S)+Da^2 (S-D (3 Sa-6 S-1))+12 Da D^2 (Sa-2 S)-16 D^3 (Sa-2 S))/Da^2 + builder->fsCodeAppendf("\t\t\t\t%s.%c = (-DaCub*%s.%c + DaSqd*(%s.%c - %s.%c * (3*%s.a - 6*%s.%c - 1)) + 12*%s.a*DSqd*(%s.a - 2*%s.%c) - 16*DCub * (%s.a - 2*%s.%c)) / DaSqd;\n", + final, component, src, component, src, component, dst, component, + src, src, component, dst, src, src, component, src, src, + component); + builder->fsCodeAppendf("\t\t\t} else {\n"); + // -sqrt(Da * D) (Sa-2 S)-Da S+D (Sa-2 S+1)+S + builder->fsCodeAppendf("\t\t\t\t%s.%c = -sqrt(%s.a*%s.%c)*(%s.a - 2*%s.%c) - %s.a*%s.%c + %s.%c*(%s.a - 2*%s.%c + 1) + %s.%c;\n", + final, component, dst, dst, component, src, src, component, dst, + src, component, dst, component, src, src, component, src, + component); + builder->fsCodeAppendf("\t\t\t}\n"); + } + typedef GrGLEffect INHERITED; }; GR_DECLARE_EFFECT_TEST; private: - DarkenEffect() { this->setWillReadDst(); } + XferEffect(SkXfermode::Mode mode) : fMode(mode) { this->setWillReadDst(); } virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE { return true; } + + SkXfermode::Mode fMode; typedef GrEffect INHERITED; }; -GR_DEFINE_EFFECT_TEST(DarkenEffect); -GrEffectRef* DarkenEffect::TestCreate(SkMWCRandom*, - GrContext*, - const GrDrawTargetCaps&, - GrTexture*[]) { - static AutoEffectUnref gEffect(SkNEW(DarkenEffect)); +GR_DEFINE_EFFECT_TEST(XferEffect); +GrEffectRef* XferEffect::TestCreate(SkMWCRandom* rand, + GrContext*, + const GrDrawTargetCaps&, + GrTexture*[]) { + int mode = rand->nextRangeU(SkXfermode::kLastCoeffMode + 1, SkXfermode::kLastSeparableMode); + static AutoEffectUnref gEffect(SkNEW_ARGS(XferEffect, (static_cast(mode)))); return CreateEffectRef(gEffect); } @@ -1070,9 +1246,10 @@ public: if (this->asCoeff(src, dst)) { return true; } - if (kDarken_Mode == fMode) { + if (XferEffect::IsSupportedMode(fMode)) { if (NULL != effect) { - *effect = DarkenEffect::Create(); + *effect = XferEffect::Create(fMode); + SkASSERT(NULL != *effect); } return true; } diff --git a/src/gpu/gl/GrGLSL_impl.h b/src/gpu/gl/GrGLSL_impl.h index 0dc0d8f402..3940926a0c 100644 --- a/src/gpu/gl/GrGLSL_impl.h +++ b/src/gpu/gl/GrGLSL_impl.h @@ -189,5 +189,4 @@ GrSLConstantVec GrGLSLSubtractf(SkString* outAppend, } } - #endif diff --git a/tests/GLProgramsTest.cpp b/tests/GLProgramsTest.cpp index 4d0772c7e2..ae6fe7b3c4 100644 --- a/tests/GLProgramsTest.cpp +++ b/tests/GLProgramsTest.cpp @@ -44,7 +44,7 @@ void GrGLProgramDesc::setRandom(SkMWCRandom* random, kAttribute_ColorInput == fCoverageInput); fCoverageAttributeIndex = (fCoverageInput == kAttribute_ColorInput) ? currAttribIndex++ : -1; - fColorFilterXfermode = random->nextULessThan(SkXfermode::kCoeffModesCnt); + fColorFilterXfermode = random->nextULessThan(SkXfermode::kLastCoeffMode + 1); fFirstCoverageStage = random->nextULessThan(GrDrawState::kNumStages);