diff options
Diffstat (limited to 'chromium/third_party/skia/src/effects/SkAlphaThresholdFilter.cpp')
-rw-r--r-- | chromium/third_party/skia/src/effects/SkAlphaThresholdFilter.cpp | 366 |
1 files changed, 366 insertions, 0 deletions
diff --git a/chromium/third_party/skia/src/effects/SkAlphaThresholdFilter.cpp b/chromium/third_party/skia/src/effects/SkAlphaThresholdFilter.cpp new file mode 100644 index 00000000000..6fcd2b4351e --- /dev/null +++ b/chromium/third_party/skia/src/effects/SkAlphaThresholdFilter.cpp @@ -0,0 +1,366 @@ +/* + * 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 "SkAlphaThresholdFilter.h" +#include "SkBitmap.h" +#include "SkReadBuffer.h" +#include "SkWriteBuffer.h" +#include "SkRegion.h" + +class SK_API SkAlphaThresholdFilterImpl : public SkImageFilter { +public: + SkAlphaThresholdFilterImpl(const SkRegion& region, SkScalar innerThreshold, SkScalar outerThreshold); + + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkAlphaThresholdFilterImpl) + +protected: + explicit SkAlphaThresholdFilterImpl(SkReadBuffer& buffer); + virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE; + + virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&, + SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE; +#if SK_SUPPORT_GPU + virtual bool asNewEffect(GrEffectRef** effect, GrTexture* texture, + const SkMatrix& matrix, const SkIRect& bounds) const SK_OVERRIDE; +#endif + +private: + SkRegion fRegion; + SkScalar fInnerThreshold; + SkScalar fOuterThreshold; + typedef SkImageFilter INHERITED; +}; + +SkImageFilter* SkAlphaThresholdFilter::Create(const SkRegion& region, + SkScalar innerThreshold, + SkScalar outerThreshold) { + return SkNEW_ARGS(SkAlphaThresholdFilterImpl, (region, innerThreshold, outerThreshold)); +} + +#if SK_SUPPORT_GPU +#include "GrContext.h" +#include "GrCoordTransform.h" +#include "GrEffect.h" +#include "gl/GrGLEffect.h" +#include "GrTBackendEffectFactory.h" +#include "GrTextureAccess.h" + +#include "SkGr.h" + +class GrGLAlphaThresholdEffect; + +class AlphaThresholdEffect : public GrEffect { + +public: + static GrEffectRef* Create(GrTexture* texture, + GrTexture* maskTexture, + float innerThreshold, + float outerThreshold) { + AutoEffectUnref effect(SkNEW_ARGS(AlphaThresholdEffect, (texture, + maskTexture, + innerThreshold, + outerThreshold))); + return CreateEffectRef(effect); + } + + virtual ~AlphaThresholdEffect() {}; + + static const char* Name() { return "Alpha Threshold"; } + + virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; + virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE; + + float innerThreshold() const { return fInnerThreshold; } + float outerThreshold() const { return fOuterThreshold; } + + typedef GrGLAlphaThresholdEffect GLEffect; + +private: + AlphaThresholdEffect(GrTexture* texture, + GrTexture* maskTexture, + float innerThreshold, + float outerThreshold) + : fInnerThreshold(innerThreshold) + , fOuterThreshold(outerThreshold) + , fImageCoordTransform(kLocal_GrCoordSet, MakeDivByTextureWHMatrix(texture), texture) + , fImageTextureAccess(texture) + , fMaskCoordTransform(kLocal_GrCoordSet, MakeDivByTextureWHMatrix(maskTexture), maskTexture) + , fMaskTextureAccess(maskTexture) { + this->addCoordTransform(&fImageCoordTransform); + this->addTextureAccess(&fImageTextureAccess); + this->addCoordTransform(&fMaskCoordTransform); + this->addTextureAccess(&fMaskTextureAccess); + } + + virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE; + + GR_DECLARE_EFFECT_TEST; + + float fInnerThreshold; + float fOuterThreshold; + GrCoordTransform fImageCoordTransform; + GrTextureAccess fImageTextureAccess; + GrCoordTransform fMaskCoordTransform; + GrTextureAccess fMaskTextureAccess; + + typedef GrEffect INHERITED; +}; + +class GrGLAlphaThresholdEffect : public GrGLEffect { +public: + GrGLAlphaThresholdEffect(const GrBackendEffectFactory&, const GrDrawEffect&); + + virtual void emitCode(GrGLShaderBuilder*, + const GrDrawEffect&, + EffectKey, + const char* outputColor, + const char* inputColor, + const TransformedCoordsArray&, + const TextureSamplerArray&) SK_OVERRIDE; + + virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE; + +private: + + GrGLUniformManager::UniformHandle fInnerThresholdVar; + GrGLUniformManager::UniformHandle fOuterThresholdVar; + + typedef GrGLEffect INHERITED; +}; + +GrGLAlphaThresholdEffect::GrGLAlphaThresholdEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&) + : INHERITED(factory) { +} + +void GrGLAlphaThresholdEffect::emitCode(GrGLShaderBuilder* builder, + const GrDrawEffect&, + EffectKey key, + const char* outputColor, + const char* inputColor, + const TransformedCoordsArray& coords, + const TextureSamplerArray& samplers) { + SkString coords2D = builder->ensureFSCoords2D(coords, 0); + SkString maskCoords2D = builder->ensureFSCoords2D(coords, 1); + fInnerThresholdVar = builder->addUniform( + GrGLShaderBuilder::kFragment_Visibility, + kFloat_GrSLType, "inner_threshold"); + fOuterThresholdVar = builder->addUniform( + GrGLShaderBuilder::kFragment_Visibility, + kFloat_GrSLType, "outer_threshold"); + + builder->fsCodeAppendf("\t\tvec2 coord = %s;\n", coords2D.c_str()); + builder->fsCodeAppendf("\t\tvec2 mask_coord = %s;\n", maskCoords2D.c_str()); + builder->fsCodeAppend("\t\tvec4 input_color = "); + builder->fsAppendTextureLookup(samplers[0], "coord"); + builder->fsCodeAppend(";\n"); + builder->fsCodeAppend("\t\tvec4 mask_color = "); + builder->fsAppendTextureLookup(samplers[1], "mask_coord"); + builder->fsCodeAppend(";\n"); + + builder->fsCodeAppendf("\t\tfloat inner_thresh = %s;\n", + builder->getUniformCStr(fInnerThresholdVar)); + builder->fsCodeAppendf("\t\tfloat outer_thresh = %s;\n", + builder->getUniformCStr(fOuterThresholdVar)); + builder->fsCodeAppend("\t\tfloat mask = mask_color.a;\n"); + + builder->fsCodeAppend("vec4 color = input_color;\n"); + builder->fsCodeAppend("\t\tif (mask < 0.5) {\n" + "\t\t\tif (color.a > outer_thresh) {\n" + "\t\t\t\tfloat scale = outer_thresh / color.a;\n" + "\t\t\t\tcolor.rgb *= scale;\n" + "\t\t\t\tcolor.a = outer_thresh;\n" + "\t\t\t}\n" + "\t\t} else if (color.a < inner_thresh) {\n" + "\t\t\tfloat scale = inner_thresh / max(0.001, color.a);\n" + "\t\t\tcolor.rgb *= scale;\n" + "\t\t\tcolor.a = inner_thresh;\n" + "\t\t}\n"); + + builder->fsCodeAppendf("%s = %s;\n", outputColor, + (GrGLSLExpr4(inputColor) * GrGLSLExpr4("color")).c_str()); +} + +void GrGLAlphaThresholdEffect::setData(const GrGLUniformManager& uman, + const GrDrawEffect& drawEffect) { + const AlphaThresholdEffect& alpha_threshold = + drawEffect.castEffect<AlphaThresholdEffect>(); + uman.set1f(fInnerThresholdVar, alpha_threshold.innerThreshold()); + uman.set1f(fOuterThresholdVar, alpha_threshold.outerThreshold()); +} + +///////////////////////////////////////////////////////////////////// + +GR_DEFINE_EFFECT_TEST(AlphaThresholdEffect); + +GrEffectRef* AlphaThresholdEffect::TestCreate(SkRandom* random, + GrContext* context, + const GrDrawTargetCaps&, + GrTexture** textures) { + GrTexture* bmpTex = textures[GrEffectUnitTest::kSkiaPMTextureIdx]; + GrTexture* maskTex = textures[GrEffectUnitTest::kAlphaTextureIdx]; + float inner_thresh = random->nextUScalar1(); + float outer_thresh = random->nextUScalar1(); + return AlphaThresholdEffect::Create(bmpTex, maskTex, inner_thresh, outer_thresh); +} + +/////////////////////////////////////////////////////////////////////////////// + +const GrBackendEffectFactory& AlphaThresholdEffect::getFactory() const { + return GrTBackendEffectFactory<AlphaThresholdEffect>::getInstance(); +} + +bool AlphaThresholdEffect::onIsEqual(const GrEffect& sBase) const { + const AlphaThresholdEffect& s = CastEffect<AlphaThresholdEffect>(sBase); + return (this->texture(0) == s.texture(0) && + this->fInnerThreshold == s.fInnerThreshold && + this->fOuterThreshold == s.fOuterThreshold); +} + +void AlphaThresholdEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const { + if ((*validFlags & kA_GrColorComponentFlag) && 0xFF == GrColorUnpackA(*color) && + GrPixelConfigIsOpaque(this->texture(0)->config())) { + *validFlags = kA_GrColorComponentFlag; + } else { + *validFlags = 0; + } +} + +#endif + +SkAlphaThresholdFilterImpl::SkAlphaThresholdFilterImpl(SkReadBuffer& buffer) + : INHERITED(1, buffer) { + fInnerThreshold = buffer.readScalar(); + fOuterThreshold = buffer.readScalar(); + buffer.readRegion(&fRegion); +} + +SkAlphaThresholdFilterImpl::SkAlphaThresholdFilterImpl(const SkRegion& region, + SkScalar innerThreshold, + SkScalar outerThreshold) + : INHERITED(0) + , fRegion(region) + , fInnerThreshold(innerThreshold) + , fOuterThreshold(outerThreshold) { +} + +#if SK_SUPPORT_GPU +bool SkAlphaThresholdFilterImpl::asNewEffect(GrEffectRef** effect, GrTexture* texture, + const SkMatrix& in_matrix, const SkIRect&) const { + if (effect) { + GrContext* context = texture->getContext(); + GrTextureDesc maskDesc; + if (context->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) { + maskDesc.fConfig = kAlpha_8_GrPixelConfig; + } else { + maskDesc.fConfig = kRGBA_8888_GrPixelConfig; + } + maskDesc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit; + // Add one pixel of border to ensure that clamp mode will be all zeros + // the outside. + maskDesc.fWidth = texture->width(); + maskDesc.fHeight = texture->height(); + GrAutoScratchTexture ast(context, maskDesc, GrContext::kApprox_ScratchTexMatch); + GrTexture* maskTexture = ast.texture(); + if (NULL == maskTexture) { + return false; + } + + { + GrContext::AutoRenderTarget art(context, ast.texture()->asRenderTarget()); + GrPaint grPaint; + grPaint.setBlendFunc(kOne_GrBlendCoeff, kZero_GrBlendCoeff); + SkRegion::Iterator iter(fRegion); + context->clear(NULL, 0x0, true); + + SkMatrix old_matrix = context->getMatrix(); + context->setMatrix(in_matrix); + + while (!iter.done()) { + SkRect rect = SkRect::Make(iter.rect()); + context->drawRect(grPaint, rect); + iter.next(); + } + context->setMatrix(old_matrix); + } + + *effect = AlphaThresholdEffect::Create(texture, + maskTexture, + fInnerThreshold, + fOuterThreshold); + } + return true; +} +#endif + +void SkAlphaThresholdFilterImpl::flatten(SkWriteBuffer& buffer) const { + this->INHERITED::flatten(buffer); + buffer.writeScalar(fInnerThreshold); + buffer.writeScalar(fOuterThreshold); + buffer.writeRegion(fRegion); +} + +bool SkAlphaThresholdFilterImpl::onFilterImage(Proxy*, const SkBitmap& src, + const Context& ctx, SkBitmap* dst, + SkIPoint* offset) const { + SkASSERT(src.colorType() == kN32_SkColorType); + + if (src.colorType() != kN32_SkColorType) { + return false; + } + + SkMatrix localInverse; + if (!ctx.ctm().invert(&localInverse)) { + return false; + } + + SkAutoLockPixels alp(src); + SkASSERT(src.getPixels()); + if (!src.getPixels() || src.width() <= 0 || src.height() <= 0) { + return false; + } + + if (!dst->allocPixels(src.info())) { + return false; + } + + U8CPU innerThreshold = (U8CPU)(fInnerThreshold * 0xFF); + U8CPU outerThreshold = (U8CPU)(fOuterThreshold * 0xFF); + SkColor* sptr = src.getAddr32(0, 0); + SkColor* dptr = dst->getAddr32(0, 0); + int width = src.width(), height = src.height(); + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + const SkColor& source = sptr[y * width + x]; + SkColor output_color(source); + SkPoint position; + localInverse.mapXY((SkScalar)x, (SkScalar)y, &position); + if (fRegion.contains((int32_t)position.x(), (int32_t)position.y())) { + if (SkColorGetA(source) < innerThreshold) { + U8CPU alpha = SkColorGetA(source); + if (alpha == 0) + alpha = 1; + float scale = (float)innerThreshold / alpha; + output_color = SkColorSetARGB(innerThreshold, + (U8CPU)(SkColorGetR(source) * scale), + (U8CPU)(SkColorGetG(source) * scale), + (U8CPU)(SkColorGetB(source) * scale)); + } + } else { + if (SkColorGetA(source) > outerThreshold) { + float scale = (float)outerThreshold / SkColorGetA(source); + output_color = SkColorSetARGB(outerThreshold, + (U8CPU)(SkColorGetR(source) * scale), + (U8CPU)(SkColorGetG(source) * scale), + (U8CPU)(SkColorGetB(source) * scale)); + } + } + dptr[y * dst->width() + x] = output_color; + } + } + + return true; +} |