| /* |
| * Copyright 2013 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "SkArithmeticMode.h" |
| #include "SkColorPriv.h" |
| #include "SkFlattenableBuffers.h" |
| #include "SkString.h" |
| #include "SkUnPreMultiply.h" |
| #if SK_SUPPORT_GPU |
| #include "GrContext.h" |
| #include "gl/GrGLEffect.h" |
| #include "gl/GrGLEffectMatrix.h" |
| #include "GrTBackendEffectFactory.h" |
| #include "SkImageFilterUtils.h" |
| #endif |
| |
| static const bool gUseUnpremul = false; |
| |
| class SkArithmeticMode_scalar : public SkXfermode { |
| public: |
| SkArithmeticMode_scalar(SkScalar k1, SkScalar k2, SkScalar k3, SkScalar k4) { |
| fK[0] = k1; |
| fK[1] = k2; |
| fK[2] = k3; |
| fK[3] = k4; |
| } |
| |
| virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count, |
| const SkAlpha aa[]) const SK_OVERRIDE; |
| |
| SK_DEVELOPER_TO_STRING() |
| SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkArithmeticMode_scalar) |
| |
| #if SK_SUPPORT_GPU |
| virtual bool asNewEffectOrCoeff(GrContext*, GrEffectRef** effect, Coeff*, Coeff*, GrTexture* background) const SK_OVERRIDE; |
| #endif |
| |
| private: |
| SkArithmeticMode_scalar(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) { |
| fK[0] = buffer.readScalar(); |
| fK[1] = buffer.readScalar(); |
| fK[2] = buffer.readScalar(); |
| fK[3] = buffer.readScalar(); |
| } |
| |
| virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE { |
| INHERITED::flatten(buffer); |
| buffer.writeScalar(fK[0]); |
| buffer.writeScalar(fK[1]); |
| buffer.writeScalar(fK[2]); |
| buffer.writeScalar(fK[3]); |
| } |
| SkScalar fK[4]; |
| |
| typedef SkXfermode INHERITED; |
| }; |
| |
| static int pinToByte(int value) { |
| if (value < 0) { |
| value = 0; |
| } else if (value > 255) { |
| value = 255; |
| } |
| return value; |
| } |
| |
| static int arith(SkScalar k1, SkScalar k2, SkScalar k3, SkScalar k4, |
| int src, int dst) { |
| SkScalar result = SkScalarMul(k1, src * dst) + |
| SkScalarMul(k2, src) + |
| SkScalarMul(k3, dst) + |
| k4; |
| int res = SkScalarRoundToInt(result); |
| return pinToByte(res); |
| } |
| |
| static int blend(int src, int dst, int scale) { |
| return dst + ((src - dst) * scale >> 8); |
| } |
| |
| static bool needsUnpremul(int alpha) { |
| return 0 != alpha && 0xFF != alpha; |
| } |
| |
| void SkArithmeticMode_scalar::xfer32(SkPMColor dst[], const SkPMColor src[], |
| int count, const SkAlpha aaCoverage[]) const { |
| SkScalar k1 = fK[0] / 255; |
| SkScalar k2 = fK[1]; |
| SkScalar k3 = fK[2]; |
| SkScalar k4 = fK[3] * 255; |
| |
| for (int i = 0; i < count; ++i) { |
| if ((NULL == aaCoverage) || aaCoverage[i]) { |
| SkPMColor sc = src[i]; |
| SkPMColor dc = dst[i]; |
| |
| int a, r, g, b; |
| |
| if (gUseUnpremul) { |
| int sa = SkGetPackedA32(sc); |
| int da = SkGetPackedA32(dc); |
| |
| int srcNeedsUnpremul = needsUnpremul(sa); |
| int dstNeedsUnpremul = needsUnpremul(da); |
| |
| if (!srcNeedsUnpremul && !dstNeedsUnpremul) { |
| a = arith(k1, k2, k3, k4, sa, da); |
| r = arith(k1, k2, k3, k4, SkGetPackedR32(sc), SkGetPackedR32(dc)); |
| g = arith(k1, k2, k3, k4, SkGetPackedG32(sc), SkGetPackedG32(dc)); |
| b = arith(k1, k2, k3, k4, SkGetPackedB32(sc), SkGetPackedB32(dc)); |
| } else { |
| int sr = SkGetPackedR32(sc); |
| int sg = SkGetPackedG32(sc); |
| int sb = SkGetPackedB32(sc); |
| if (srcNeedsUnpremul) { |
| SkUnPreMultiply::Scale scale = SkUnPreMultiply::GetScale(sa); |
| sr = SkUnPreMultiply::ApplyScale(scale, sr); |
| sg = SkUnPreMultiply::ApplyScale(scale, sg); |
| sb = SkUnPreMultiply::ApplyScale(scale, sb); |
| } |
| |
| int dr = SkGetPackedR32(dc); |
| int dg = SkGetPackedG32(dc); |
| int db = SkGetPackedB32(dc); |
| if (dstNeedsUnpremul) { |
| SkUnPreMultiply::Scale scale = SkUnPreMultiply::GetScale(da); |
| dr = SkUnPreMultiply::ApplyScale(scale, dr); |
| dg = SkUnPreMultiply::ApplyScale(scale, dg); |
| db = SkUnPreMultiply::ApplyScale(scale, db); |
| } |
| |
| a = arith(k1, k2, k3, k4, sa, da); |
| r = arith(k1, k2, k3, k4, sr, dr); |
| g = arith(k1, k2, k3, k4, sg, dg); |
| b = arith(k1, k2, k3, k4, sb, db); |
| } |
| } else { |
| a = arith(k1, k2, k3, k4, SkGetPackedA32(sc), SkGetPackedA32(dc)); |
| r = arith(k1, k2, k3, k4, SkGetPackedR32(sc), SkGetPackedR32(dc)); |
| r = SkMin32(r, a); |
| g = arith(k1, k2, k3, k4, SkGetPackedG32(sc), SkGetPackedG32(dc)); |
| g = SkMin32(g, a); |
| b = arith(k1, k2, k3, k4, SkGetPackedB32(sc), SkGetPackedB32(dc)); |
| b = SkMin32(b, a); |
| } |
| |
| // apply antialias coverage if necessary |
| if (aaCoverage && 0xFF != aaCoverage[i]) { |
| int scale = aaCoverage[i] + (aaCoverage[i] >> 7); |
| a = blend(a, SkGetPackedA32(sc), scale); |
| r = blend(r, SkGetPackedR32(sc), scale); |
| g = blend(g, SkGetPackedG32(sc), scale); |
| b = blend(b, SkGetPackedB32(sc), scale); |
| } |
| |
| // turn the result back into premul |
| if (gUseUnpremul && (0xFF != a)) { |
| int scale = a + (a >> 7); |
| r = SkAlphaMul(r, scale); |
| g = SkAlphaMul(g, scale); |
| b = SkAlphaMul(b, scale); |
| } |
| dst[i] = SkPackARGB32(a, r, g, b); |
| } |
| } |
| } |
| |
| #ifdef SK_DEVELOPER |
| void SkArithmeticMode_scalar::toString(SkString* str) const { |
| str->append("SkArithmeticMode_scalar: "); |
| for (int i = 0; i < 4; ++i) { |
| str->appendScalar(fK[i]); |
| if (i < 3) { |
| str->append(" "); |
| } |
| } |
| } |
| #endif |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| static bool fitsInBits(SkScalar x, int bits) { |
| #ifdef SK_SCALAR_IS_FIXED |
| x = SkAbs32(x); |
| x += 1 << 7; |
| x >>= 8; |
| return x < (1 << (bits - 1)); |
| #else |
| return SkScalarAbs(x) < (1 << (bits - 1)); |
| #endif |
| } |
| |
| #if 0 // UNUSED |
| static int32_t toDot8(SkScalar x) { |
| #ifdef SK_SCALAR_IS_FIXED |
| x += 1 << 7; |
| x >>= 8; |
| return x; |
| #else |
| return (int32_t)(x * 256); |
| #endif |
| } |
| #endif |
| |
| SkXfermode* SkArithmeticMode::Create(SkScalar k1, SkScalar k2, |
| SkScalar k3, SkScalar k4) { |
| if (fitsInBits(k1, 8) && fitsInBits(k2, 16) && |
| fitsInBits(k2, 16) && fitsInBits(k2, 24)) { |
| |
| #if 0 // UNUSED |
| int32_t i1 = toDot8(k1); |
| int32_t i2 = toDot8(k2); |
| int32_t i3 = toDot8(k3); |
| int32_t i4 = toDot8(k4); |
| if (i1) { |
| return SkNEW_ARGS(SkArithmeticMode_quad, (i1, i2, i3, i4)); |
| } |
| if (0 == i2) { |
| return SkNEW_ARGS(SkArithmeticMode_dst, (i3, i4)); |
| } |
| if (0 == i3) { |
| return SkNEW_ARGS(SkArithmeticMode_src, (i2, i4)); |
| } |
| return SkNEW_ARGS(SkArithmeticMode_linear, (i2, i3, i4)); |
| #endif |
| } |
| return SkNEW_ARGS(SkArithmeticMode_scalar, (k1, k2, k3, k4)); |
| } |
| |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| #if SK_SUPPORT_GPU |
| |
| class GrGLArithmeticEffect : public GrGLEffect { |
| public: |
| GrGLArithmeticEffect(const GrBackendEffectFactory&, const GrDrawEffect&); |
| virtual ~GrGLArithmeticEffect(); |
| |
| virtual void emitCode(GrGLShaderBuilder*, |
| const GrDrawEffect&, |
| EffectKey, |
| const char* outputColor, |
| const char* inputColor, |
| const TextureSamplerArray&) SK_OVERRIDE; |
| |
| static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&); |
| |
| virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE; |
| |
| private: |
| static const GrEffect::CoordsType kCoordsType = GrEffect::kLocal_CoordsType; |
| GrGLEffectMatrix fBackgroundEffectMatrix; |
| GrGLUniformManager::UniformHandle fKUni; |
| |
| typedef GrGLEffect INHERITED; |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| class GrArithmeticEffect : public GrEffect { |
| public: |
| static GrEffectRef* Create(float k1, float k2, float k3, float k4, GrTexture* background) { |
| AutoEffectUnref effect(SkNEW_ARGS(GrArithmeticEffect, (k1, k2, k3, k4, background))); |
| return CreateEffectRef(effect); |
| } |
| |
| virtual ~GrArithmeticEffect(); |
| |
| virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; |
| |
| typedef GrGLArithmeticEffect GLEffect; |
| static const char* Name() { return "Arithmetic"; } |
| GrTexture* backgroundTexture() const { return fBackgroundAccess.getTexture(); } |
| |
| virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE; |
| |
| float k1() const { return fK1; } |
| float k2() const { return fK2; } |
| float k3() const { return fK3; } |
| float k4() const { return fK4; } |
| |
| private: |
| virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE; |
| |
| GrArithmeticEffect(float k1, float k2, float k3, float k4, GrTexture* background); |
| float fK1, fK2, fK3, fK4; |
| GrTextureAccess fBackgroundAccess; |
| |
| GR_DECLARE_EFFECT_TEST; |
| typedef GrEffect INHERITED; |
| |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| GrArithmeticEffect::GrArithmeticEffect(float k1, float k2, float k3, float k4, |
| GrTexture* background) |
| : fK1(k1), fK2(k2), fK3(k3), fK4(k4) { |
| if (background) { |
| fBackgroundAccess.reset(background); |
| this->addTextureAccess(&fBackgroundAccess); |
| } else { |
| this->setWillReadDstColor(); |
| } |
| } |
| |
| GrArithmeticEffect::~GrArithmeticEffect() { |
| } |
| |
| bool GrArithmeticEffect::onIsEqual(const GrEffect& sBase) const { |
| const GrArithmeticEffect& s = CastEffect<GrArithmeticEffect>(sBase); |
| return fK1 == s.fK1 && |
| fK2 == s.fK2 && |
| fK3 == s.fK3 && |
| fK4 == s.fK4 && |
| backgroundTexture() == s.backgroundTexture(); |
| } |
| |
| const GrBackendEffectFactory& GrArithmeticEffect::getFactory() const { |
| return GrTBackendEffectFactory<GrArithmeticEffect>::getInstance(); |
| } |
| |
| void GrArithmeticEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const { |
| // TODO: optimize this |
| *validFlags = 0; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| GrGLArithmeticEffect::GrGLArithmeticEffect(const GrBackendEffectFactory& factory, |
| const GrDrawEffect& drawEffect) |
| : INHERITED(factory) |
| , fBackgroundEffectMatrix(kCoordsType) { |
| } |
| |
| GrGLArithmeticEffect::~GrGLArithmeticEffect() { |
| } |
| |
| void GrGLArithmeticEffect::emitCode(GrGLShaderBuilder* builder, |
| const GrDrawEffect& drawEffect, |
| EffectKey key, |
| const char* outputColor, |
| const char* inputColor, |
| const TextureSamplerArray& samplers) { |
| |
| GrTexture* backgroundTex = drawEffect.castEffect<GrArithmeticEffect>().backgroundTexture(); |
| const char* dstColor; |
| if (backgroundTex) { |
| const char* bgCoords; |
| GrSLType bgCoordsType = fBackgroundEffectMatrix.emitCode(builder, key, &bgCoords, NULL, "BG"); |
| builder->fsCodeAppend("\t\tvec4 bgColor = "); |
| builder->appendTextureLookup(GrGLShaderBuilder::kFragment_ShaderType, |
| samplers[0], |
| bgCoords, |
| bgCoordsType); |
| builder->fsCodeAppendf(";\n"); |
| dstColor = "bgColor"; |
| } else { |
| dstColor = builder->dstColor(); |
| } |
| |
| GrAssert(NULL != dstColor); |
| fKUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType, |
| kVec4f_GrSLType, "k"); |
| const char* kUni = builder->getUniformCStr(fKUni); |
| |
| // We don't try to optimize for this case at all |
| if (NULL == inputColor) { |
| builder->fsCodeAppendf("\t\tconst vec4 src = %s;\n", GrGLSLOnesVecf(4)); |
| } else { |
| builder->fsCodeAppendf("\t\tvec4 src = %s;\n", inputColor); |
| if (gUseUnpremul) { |
| builder->fsCodeAppendf("\t\tsrc.rgb = clamp(src.rgb / src.a, 0.0, 1.0);\n"); |
| } |
| } |
| |
| builder->fsCodeAppendf("\t\tvec4 dst = %s;\n", dstColor); |
| if (gUseUnpremul) { |
| builder->fsCodeAppendf("\t\tdst.rgb = clamp(dst.rgb / dst.a, 0.0, 1.0);\n"); |
| } |
| |
| builder->fsCodeAppendf("\t\t%s = %s.x * src * dst + %s.y * src + %s.z * dst + %s.w;\n", outputColor, kUni, kUni, kUni, kUni); |
| builder->fsCodeAppendf("\t\t%s = clamp(%s, 0.0, 1.0);\n", outputColor, outputColor); |
| if (gUseUnpremul) { |
| builder->fsCodeAppendf("\t\t%s.rgb *= %s.a;\n", outputColor, outputColor); |
| } else { |
| builder->fsCodeAppendf("\t\t%s.rgb = min(%s.rgb, %s.a);\n", outputColor, outputColor, outputColor); |
| } |
| } |
| |
| void GrGLArithmeticEffect::setData(const GrGLUniformManager& uman, const GrDrawEffect& drawEffect) { |
| const GrArithmeticEffect& arith = drawEffect.castEffect<GrArithmeticEffect>(); |
| uman.set4f(fKUni, arith.k1(), arith.k2(), arith.k3(), arith.k4()); |
| GrTexture* bgTex = arith.backgroundTexture(); |
| if (bgTex) { |
| fBackgroundEffectMatrix.setData(uman, |
| GrEffect::MakeDivByTextureWHMatrix(bgTex), |
| drawEffect, |
| bgTex); |
| } |
| } |
| |
| GrGLEffect::EffectKey GrGLArithmeticEffect::GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { |
| const GrArithmeticEffect& effect = drawEffect.castEffect<GrArithmeticEffect>(); |
| GrTexture* bgTex = effect.backgroundTexture(); |
| EffectKey bgKey = 0; |
| if (bgTex) { |
| bgKey = GrGLEffectMatrix::GenKey(GrEffect::MakeDivByTextureWHMatrix(bgTex), |
| drawEffect, |
| GrGLArithmeticEffect::kCoordsType, |
| bgTex); |
| } |
| return bgKey; |
| } |
| |
| GrEffectRef* GrArithmeticEffect::TestCreate(SkMWCRandom* rand, |
| GrContext*, |
| const GrDrawTargetCaps&, |
| GrTexture*[]) { |
| float k1 = rand->nextF(); |
| float k2 = rand->nextF(); |
| float k3 = rand->nextF(); |
| float k4 = rand->nextF(); |
| |
| static AutoEffectUnref gEffect(SkNEW_ARGS(GrArithmeticEffect, (k1, k2, k3, k4, NULL))); |
| return CreateEffectRef(gEffect); |
| } |
| |
| GR_DEFINE_EFFECT_TEST(GrArithmeticEffect); |
| |
| bool SkArithmeticMode_scalar::asNewEffectOrCoeff(GrContext*, |
| GrEffectRef** effect, |
| Coeff*, |
| Coeff*, |
| GrTexture* background) const { |
| if (effect) { |
| *effect = GrArithmeticEffect::Create(SkScalarToFloat(fK[0]), |
| SkScalarToFloat(fK[1]), |
| SkScalarToFloat(fK[2]), |
| SkScalarToFloat(fK[3]), |
| background); |
| } |
| return true; |
| } |
| |
| #endif |
| |
| SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkArithmeticMode) |
| SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkArithmeticMode_scalar) |
| SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END |