Committed: http://code.google.com/p/skia/source/detail?r=11122

R=reed@google.com

Author: bsalomon@google.com

Review URL: https://chromiumcodereview.appspot.com/23707019

git-svn-id: http://skia.googlecode.com/svn/trunk@11131 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
commit-bot@chromium.org 2013-09-06 15:31:06 +00:00
Родитель 04c500fd75
Коммит 91a798f121
10 изменённых файлов: 498 добавлений и 6 удалений

144
gm/bitmapalphathreshold.cpp Normal file
Просмотреть файл

@ -0,0 +1,144 @@
/*
* 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 "gm.h"
#include "SkGradientShader.h"
#include "SkBitmapAlphaThresholdShader.h"
#include "SkTArray.h"
#include "SkParsePath.h"
class BitmapAlphaThresholdGM : public skiagm::GM {
public:
BitmapAlphaThresholdGM() {
this->setBGColor(0xFF000000);
}
private:
virtual uint32_t onGetFlags() const SK_OVERRIDE {
// narrow this flags when the shader has a CPU implementation and
// when it serializes.
return
kSkipPDF_Flag |
kSkipPicture_Flag |
kSkipPipe_Flag |
kSkipPipeCrossProcess_Flag |
kSkipTiled_Flag |
kSkip565_Flag |
kSkipScaledReplay_Flag |
kSkipPDFRasterization_Flag |
kGPUOnly_Flag;
}
virtual void onOnceBeforeDraw() SK_OVERRIDE {
fBM.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
if (!fBM.allocPixels()) {
return;
}
SkCanvas canvas(fBM);
SkPoint pts[] = { {0, 0}, {SkIntToScalar(fBM.width()), SkIntToScalar(fBM.height())} };
SkColor colors[] = {0x00000000, 0xffffffff};
SkShader* grad = SkGradientShader::CreateLinear(pts, colors, NULL, 2,
SkShader::kClamp_TileMode);
SkPaint gradPaint;
gradPaint.setShader(grad)->unref();
gradPaint.setXfermodeMode(SkXfermode::kSrc_Mode);
canvas.drawPaint(gradPaint);
// Construct the region used as a mask.
SkRegion bmpBoundsClip;
bmpBoundsClip.setRect(0, 0, fBM.width(), fBM.height());
SkPath circlePath;
SkScalar radius = SkScalarSqrt(SkIntToScalar(fBM.width() * fBM.height())) / 2;
circlePath.addCircle(SkIntToScalar(fBM.width() / 2),
SkIntToScalar(fBM.height() / 2),
radius);
fMask.setPath(circlePath, bmpBoundsClip);
SkPath batPath;
SkParsePath::FromSVGString(
"M305.214,374.779c2.463,0,3.45,0.493,3.45,0.493l1.478-6.241c0,0,1.15,4.763,1.643,9.034"
"c0.493,4.271,8.048,1.479,14.454,0.164c6.405-1.314,7.72-11.662,7.72-11.662h59.294c0,0-35.807,10.841-26.772,34.656"
"c0,0-52.889-8.048-61.101,24.967h-0.001c-8.212-33.015-61.101-24.967-61.101-24.967c9.034-23.815-26.772-34.656-26.772-34.656"
"h59.294c0,0,1.314,10.348,7.719,11.662c6.406,1.314,13.962,4.106,14.454-0.164c0.493-4.271,1.643-9.034,1.643-9.034l1.479,6.241"
"c0,0,0.985-0.493,3.449-0.493H305.214L305.214,374.779z",
&batPath);
SkMatrix matrix;
matrix.setTranslate(-208, -280);
matrix.postScale(radius / 100, radius / 100);
batPath.transform(matrix, &batPath);
SkRegion batRegion;
batRegion.setPath(batPath, bmpBoundsClip);
fMask.op(batRegion, SkRegion::kDifference_Op);
}
virtual SkString onShortName() SK_OVERRIDE {
return SkString("bat");
}
virtual SkISize onISize() SK_OVERRIDE {
return SkISize::Make(518, 735);
}
virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
SkTArray<SkMatrix> lms;
lms.push_back().reset();
lms.push_back().setScale(SK_Scalar1 / 2, SK_Scalar1);
lms.push_back().setScale(SK_Scalar1, 2 * SK_Scalar1);
lms.push_back().setRotate(-SK_Scalar1 * 30);
lms.push_back().setSkew(0, SK_Scalar1 / 5);
static const SkScalar kMargin = 5 * SK_Scalar1;
canvas->translate(kMargin, kMargin);
canvas->save();
static const U8CPU kThresholds[] = { 0x0, 0x08, 0x40, 0x80, 0xC0, 0xF0, 0xFF };
for (size_t i = 0; i < SK_ARRAY_COUNT(kThresholds); ++i) {
for (int j = 0; j < lms.count(); ++j) {
SkRect rect;
rect.fLeft = 0;
rect.fTop = 0;
rect.fRight = SkIntToScalar(fBM.width());
rect.fBottom = SkIntToScalar(fBM.height());
SkShader* thresh;
// This SkShader currently only has a GPU implementation.
if (canvas->getDevice()->accessRenderTarget()) {
thresh = SkBitmapAlphaThresholdShader::Create(fBM, fMask, kThresholds[i]);
} else {
thresh = SkShader::CreateBitmapShader(fBM, SkShader::kClamp_TileMode,
SkShader::kClamp_TileMode);
}
thresh->setLocalMatrix(lms[j]);
SkPaint paint;
paint.setShader(thresh)->unref();
canvas->drawRect(rect, paint);
canvas->translate(SkIntToScalar(fBM.width() + kMargin), 0);
}
canvas->restore();
canvas->translate(0, SkIntToScalar(fBM.height() + kMargin));
canvas->save();
}
}
SkBitmap fBM;
SkRegion fMask;
typedef skiagm::GM INHERITED;
};
//////////////////////////////////////////////////////////////////////////////
DEF_GM( return new BitmapAlphaThresholdGM(); )

Просмотреть файл

@ -12,6 +12,7 @@
'<(skia_src_path)/effects/SkArithmeticMode.cpp',
'<(skia_src_path)/effects/SkAvoidXfermode.cpp',
'<(skia_src_path)/effects/SkBicubicImageFilter.cpp',
'<(skia_src_path)/effects/SkBitmapAlphaThresholdShader.cpp',
'<(skia_src_path)/effects/SkBitmapSource.cpp',
'<(skia_src_path)/effects/SkBlurDrawLooper.cpp',
'<(skia_src_path)/effects/SkBlurMask.cpp',
@ -80,6 +81,7 @@
'<(skia_include_path)/effects/SkXfermodeImageFilter.h',
'<(skia_include_path)/effects/SkArithmeticMode.h',
'<(skia_include_path)/effects/SkAvoidXfermode.h',
'<(skia_include_path)/effects/SkBitmapAlphaThresholdShader.h',
'<(skia_include_path)/effects/SkBitmapSource.h',
'<(skia_include_path)/effects/SkBlurDrawLooper.h',
'<(skia_include_path)/effects/SkBlurImageFilter.h',

Просмотреть файл

@ -10,6 +10,7 @@
'../gm/bicubicfilter.cpp',
'../gm/bigmatrix.cpp',
'../gm/bigtext.cpp',
'../gm/bitmapalphathreshold.cpp',
'../gm/bitmapcopy.cpp',
'../gm/bitmapmatrix.cpp',
'../gm/bitmapfilters.cpp',

Просмотреть файл

@ -318,9 +318,11 @@ public:
virtual GradientType asAGradient(GradientInfo* info) const;
/**
* If the shader subclass has a GrEffect implementation, this installs an effect on the stage.
* The GrContext may be used by the effect to create textures. The GPU device does not call
* setContext. Instead we pass the paint here in case the shader needs paint info.
* If the shader subclass has a GrEffect implementation, this resturns the effect to install.
* The incoming color to the effect has r=g=b=a all extracted from the SkPaint's alpha.
* The output color should be the computed SkShader premul color modulated by the incoming
* color. The GrContext may be used by the effect to create textures. The GPU device does not
* call setContext. Instead we pass the SkPaint here in case the shader needs paint info.
*/
virtual GrEffectRef* asNewEffect(GrContext* context, const SkPaint& paint) const;

Просмотреть файл

@ -0,0 +1,24 @@
/*
* 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 "SkShader.h"
#include "SkBitmap.h"
#include "SkRegion.h"
#include "SkString.h"
class SK_API SkBitmapAlphaThresholdShader : public SkShader {
public:
/**
* Creates a shader that samples a bitmap and a region. If the sample is inside the region
* the alpha of the bitmap color is boosted up to a threshold value. If it is
* outside the region then the bitmap alpha is decreased to the threshold value.
* The 0,0 point of the region corresponds to the upper left corner of the bitmap
* Currently, this only has a GPU implementation, doesn't respect the paint's bitmap
* filter setting, and always uses clamp mode.
*/
static SkShader* Create(const SkBitmap& bitmap, const SkRegion& region, U8CPU threshold);
};

Просмотреть файл

@ -204,8 +204,9 @@ public:
protected:
/**
* Subclasses call this from their constructor to register GrTextureAccesses. The effect
* subclass manages the lifetime of the accesses (this function only stores a pointer). This
* must only be called from the constructor because GrEffects are immutable.
* subclass manages the lifetime of the accesses (this function only stores a pointer). The
* GrTextureAccess is typically a member field of the GrEffet subclass. This must only be
* called from the constructor because GrEffects are immutable.
*/
void addTextureAccess(const GrTextureAccess* textureAccess);

Просмотреть файл

@ -18,7 +18,7 @@ class GrTexture;
* Represents the filtering and tile modes used to access a texture. It is mostly used with
* GrTextureAccess (defined below). Also, some of the texture cache methods require knowledge about
* filtering and tiling to perform a cache lookup. If it wasn't for this latter usage this would
* be folded into GrTextureAccess.
* be folded into GrTextureAccess. The default is clamp tile modes and no filtering.
*/
class GrTextureParams {
public:

Просмотреть файл

@ -0,0 +1,311 @@
/*
* 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 "SkBitmapAlphaThresholdShader.h"
class BATShader : public SkShader {
public:
SK_DECLARE_INST_COUNT(SkThresholdShader);
BATShader(const SkBitmap& bitmap, SkRegion region, U8CPU);
BATShader(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
// We should probably do something here.
}
virtual void shadeSpan(int x, int y, SkPMColor[], int count) SK_OVERRIDE {};
#if SK_SUPPORT_GPU
virtual GrEffectRef* asNewEffect(GrContext* context, const SkPaint& paint) const SK_OVERRIDE;
#endif
SK_DEVELOPER_TO_STRING();
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(BATShader)
private:
SkBitmap fBitmap;
SkRegion fRegion;
U8CPU fThreshold;
typedef SkShader INHERITED;
};
SkShader* SkBitmapAlphaThresholdShader::Create(const SkBitmap& bitmap,
const SkRegion& region,
U8CPU threshold) {
SkASSERT(threshold < 256);
return SkNEW_ARGS(BATShader, (bitmap, region, threshold));
}
BATShader::BATShader(const SkBitmap& bitmap, SkRegion region, U8CPU threshold)
: fBitmap(bitmap)
, fRegion(region)
, fThreshold(threshold) {
};
#ifdef SK_DEVELOPER
void BATShader::toString(SkString* str) const {
str->append("BATShader: (");
fBitmap.toString(str);
this->INHERITED::toString(str);
str->append(")");
}
#endif
#if SK_SUPPORT_GPU
#include "GrContext.h"
#include "GrEffect.h"
#include "gl/GrGLEffect.h"
#include "gl/GrGLEffectMatrix.h"
#include "GrTBackendEffectFactory.h"
#include "GrTextureAccess.h"
#include "SkGr.h"
/**
* Could create specializations for some simple cases:
* - The region is empty.
* - The region fully contains the bitmap.
* - The regions is 1 rect (or maybe a small number of rects).
*/
class ThresholdEffect : public GrEffect {
public:
virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
return GrTBackendEffectFactory<ThresholdEffect>::getInstance();
}
static GrEffectRef* Create(GrTexture* bmpTexture, const SkMatrix& bmpMatrix,
GrTexture* maskTexture, const SkMatrix& maskMatrix,
U8CPU threshold) {
SkScalar thresh = SkIntToScalar(threshold) / 255;
AutoEffectUnref effect(SkNEW_ARGS(ThresholdEffect, (bmpTexture, bmpMatrix,
maskTexture, maskMatrix,
thresh)));
return CreateEffectRef(effect);
}
virtual void getConstantColorComponents(GrColor* color,
uint32_t* validFlags) const SK_OVERRIDE {
if ((kA_GrColorComponentFlag & *validFlags) && 0 == GrColorUnpackA(*color)) {
return;
}
*validFlags = 0;
return;
}
static const char* Name() { return "Bitmap Alpha Threshold"; }
class GLEffect : public GrGLEffect {
public:
GLEffect(const GrBackendEffectFactory& factory,
const GrDrawEffect& e)
: GrGLEffect(factory)
, fBmpMatrix(GrEffect::kLocal_CoordsType)
, fMaskMatrix(GrEffect::kLocal_CoordsType)
, fPrevThreshold(-SK_Scalar1) {
}
virtual void emitCode(GrGLShaderBuilder* builder,
const GrDrawEffect& drawEffect,
EffectKey key,
const char* outputColor,
const char* inputColor,
const TextureSamplerArray& samplers) SK_OVERRIDE {
SkString bmpCoord;
SkString maskCoord;
GrSLType bmpCoordType = fBmpMatrix.emitCode(builder, key, &bmpCoord, NULL, "Bmp");
EffectKey maskMatrixKey = key >> GrGLEffectMatrix::kKeyBits;
GrSLType maskCoordType = fMaskMatrix.emitCode(builder,
maskMatrixKey,
&maskCoord,
NULL,
"Mask");
// put bitmap color in "color"
builder->fsCodeAppend("\t\tvec4 color = ");
builder->fsAppendTextureLookup(samplers[0], bmpCoord.c_str(), bmpCoordType);
builder->fsCodeAppend(";\n");
// put alpha from mask texture in "mask"
builder->fsCodeAppend("\t\tfloat mask = ");
builder->fsAppendTextureLookup(samplers[1], maskCoord.c_str(), maskCoordType);
builder->fsCodeAppend(".a;\n");
const char* threshold;
fThresholdUniHandle = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
kFloat_GrSLType,
"threshold",
&threshold);
builder->fsCodeAppendf("\t\tfloat thresh = %s;\n", threshold);
builder->fsCodeAppend("\t\tif (mask < 0.5) {\n"
"\t\t\tif (color.a > thresh) {\n"
"\t\t\t\tfloat scale = thresh / color.a;\n"
"\t\t\t\tcolor.rgb *= scale;\n"
"\t\t\t\tcolor.a = thresh;\n"
"\t\t\t}\n"
"\t\t} else if (color.a < thresh) {\n"
"\t\t\tfloat scale = thresh / color.a;\n"
"\t\t\tcolor.rgb *= scale;\n"
"\t\t\tcolor.a = thresh;\n"
"\t\t}\n");
builder->fsCodeAppend("color = ");
SkString outStr;
outStr.appendf("\t\t%s = ", outputColor);
GrGLSLModulatef<4>(&outStr, inputColor, "color");
outStr.append(";\n");
builder->fsCodeAppend(outStr.c_str());
}
virtual void setData(const GrGLUniformManager& uman, const GrDrawEffect& e) SK_OVERRIDE {
const ThresholdEffect& effect = e.castEffect<ThresholdEffect>();
fBmpMatrix.setData(uman, effect.fBmpMatrix, e, effect.fBmpAccess.getTexture());
fMaskMatrix.setData(uman, effect.fMaskMatrix, e, effect.fMaskAccess.getTexture());
if (fPrevThreshold != effect.fThreshold) {
uman.set1f(fThresholdUniHandle, effect.fThreshold);
}
}
static inline EffectKey GenKey(const GrDrawEffect& e, const GrGLCaps&) {
const ThresholdEffect& effect = e.castEffect<ThresholdEffect>();
EffectKey bmpMKey = GrGLEffectMatrix::GenKey(effect.fBmpMatrix,
e,
GrEffect::kLocal_CoordsType,
effect.fBmpAccess.getTexture());
EffectKey maskMKey = GrGLEffectMatrix::GenKey(effect.fMaskMatrix,
e,
GrEffect::kLocal_CoordsType,
effect.fMaskAccess.getTexture());
return bmpMKey | (maskMKey << GrGLEffectMatrix::kKeyBits);
}
private:
GrGLEffectMatrix fBmpMatrix;
GrGLEffectMatrix fMaskMatrix;
GrGLUniformManager::UniformHandle fThresholdUniHandle;
SkScalar fPrevThreshold;
};
GR_DECLARE_EFFECT_TEST;
private:
ThresholdEffect(GrTexture* bmpTexture, const SkMatrix& bmpMatrix,
GrTexture* maskTexture, const SkMatrix& maskMatrix,
SkScalar threshold)
: fBmpAccess(bmpTexture, GrTextureParams())
, fMaskAccess(maskTexture, GrTextureParams())
, fBmpMatrix(bmpMatrix)
, fMaskMatrix(maskMatrix)
, fThreshold(threshold) {
this->addTextureAccess(&fBmpAccess);
this->addTextureAccess(&fMaskAccess);
}
virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
const ThresholdEffect& e = CastEffect<ThresholdEffect>(other);
return e.fBmpAccess.getTexture() == fBmpAccess.getTexture() &&
e.fMaskAccess.getTexture() == fMaskAccess.getTexture() &&
e.fBmpMatrix == fBmpMatrix &&
e.fMaskMatrix == fMaskMatrix &&
e.fThreshold == fThreshold;
}
GrTextureAccess fBmpAccess;
GrTextureAccess fMaskAccess;
SkMatrix fBmpMatrix;
SkMatrix fMaskMatrix;
SkScalar fThreshold;
};
GR_DEFINE_EFFECT_TEST(ThresholdEffect);
GrEffectRef* ThresholdEffect::TestCreate(SkMWCRandom* rand,
GrContext*,
const GrDrawTargetCaps&,
GrTexture* textures[]) {
GrTexture* bmpTex = textures[GrEffectUnitTest::kSkiaPMTextureIdx];
GrTexture* maskTex = textures[GrEffectUnitTest::kAlphaTextureIdx];
U8CPU thresh = rand->nextU() % 0xff;
return ThresholdEffect::Create(bmpTex, SkMatrix::I(), maskTex, SkMatrix::I(), thresh);
}
GrEffectRef* BATShader::asNewEffect(GrContext* context, const SkPaint& paint) const {
SkMatrix localInverse;
if (!this->getLocalMatrix().invert(&localInverse)) {
return NULL;
}
GrTextureDesc maskDesc;
if (context->isConfigRenderable(kAlpha_8_GrPixelConfig)) {
maskDesc.fConfig = kAlpha_8_GrPixelConfig;
} else {
maskDesc.fConfig = kRGBA_8888_GrPixelConfig;
}
maskDesc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
const SkIRect& bounds = fRegion.getBounds();
// Add one pixel of border to ensure that clamp mode will be all zeros
// the outside.
maskDesc.fWidth = bounds.width() + 2;
maskDesc.fHeight = bounds.height() + 2;
GrAutoScratchTexture ast(context, maskDesc, GrContext::kApprox_ScratchTexMatch);
GrTexture* maskTexture = ast.texture();
if (NULL == maskTexture) {
return NULL;
}
GrPaint grPaint;
grPaint.setBlendFunc(kOne_GrBlendCoeff, kZero_GrBlendCoeff);
SkRegion::Iterator iter(fRegion);
context->setRenderTarget(maskTexture->asRenderTarget());
context->clear(NULL, 0x0);
// offset to ensure border is zero on top/left
SkMatrix matrix;
matrix.setTranslate(SK_Scalar1, SK_Scalar1);
context->setMatrix(matrix);
while (!iter.done()) {
SkRect rect = SkRect::MakeFromIRect(iter.rect());
context->drawRect(grPaint, rect);
iter.next();
}
GrTexture* bmpTexture = GrLockAndRefCachedBitmapTexture(context, fBitmap, NULL);
if (NULL == bmpTexture) {
return NULL;
}
SkMatrix bmpMatrix = localInverse;
bmpMatrix.postIDiv(bmpTexture->width(), bmpTexture->height());
SkMatrix maskMatrix = localInverse;
// compensate for the border
maskMatrix.postTranslate(SK_Scalar1, SK_Scalar1);
maskMatrix.postIDiv(maskTexture->width(), maskTexture->height());
GrEffectRef* effect = ThresholdEffect::Create(bmpTexture, bmpMatrix,
maskTexture, maskMatrix,
fThreshold);
GrUnlockAndUnrefCachedBitmapTexture(bmpTexture);
return effect;
}
#endif

Просмотреть файл

@ -535,6 +535,11 @@ inline bool skPaint2GrPaintShader(SkGpuDevice* dev,
return skPaint2GrPaintNoShader(dev, skPaint, false, constantColor, grPaint);
}
// SkShader::asNewEffect() may do offscreen rendering. Setup default drawing state
// Also require shader to set the render target .
GrContext::AutoWideOpenIdentityDraw awo(dev->context(), NULL);
GrContext::AutoRenderTarget(dev->context(), NULL);
// setup the shader as the first color effect on the paint
SkAutoTUnref<GrEffectRef> effect(shader->asNewEffect(dev->context(), skPaint));
if (NULL != effect.get()) {

Просмотреть файл

@ -228,6 +228,7 @@ DEFINE_GPUTESTCLASS("GLPrograms", GLProgramsTestClass, GLProgramsTest)
#include "SkLightingImageFilter.h"
#include "SkMagnifierImageFilter.h"
#include "SkColorMatrixFilter.h"
#include "SkBitmapAlphaThresholdShader.h"
void forceLinking();
@ -240,6 +241,7 @@ void forceLinking() {
SkMatrix::I());
SkScalar matrix[20];
SkColorMatrixFilter cmf(matrix);
SkBitmapAlphaThresholdShader::Create(SkBitmap(), SkRegion(), 0x80);
}
#endif