Perform the same analytic blur calculation on the GPU that we do on the CPU. Results in significant performance gains when using Ganesh to render drop shadows in Chrome.
BUG= R=bsalomon@google.com, reed@google.com Author: humper@google.com Review URL: https://codereview.chromium.org/119343003 git-svn-id: http://skia.googlecode.com/svn/trunk@13210 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
parent
01260b2dbc
commit
d1829151a5
@ -16,7 +16,6 @@
|
||||
'<(skia_src_path)/effects/SkBitmapSource.cpp',
|
||||
'<(skia_src_path)/effects/SkBlurDrawLooper.cpp',
|
||||
'<(skia_src_path)/effects/SkBlurMask.cpp',
|
||||
'<(skia_src_path)/effects/SkBlurMask.h',
|
||||
'<(skia_src_path)/effects/SkBlurImageFilter.cpp',
|
||||
'<(skia_src_path)/effects/SkBlurMaskFilter.cpp',
|
||||
'<(skia_src_path)/effects/SkColorFilters.cpp',
|
||||
@ -88,6 +87,7 @@
|
||||
'<(skia_include_path)/effects/SkBitmapSource.h',
|
||||
'<(skia_include_path)/effects/SkBlurDrawLooper.h',
|
||||
'<(skia_include_path)/effects/SkBlurImageFilter.h',
|
||||
'<(skia_include_path)/effects/SkBlurMask.h',
|
||||
'<(skia_include_path)/effects/SkBlurMaskFilter.h',
|
||||
'<(skia_include_path)/effects/SkColorMatrix.h',
|
||||
'<(skia_include_path)/effects/SkColorMatrixFilter.h',
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "SkPaint.h"
|
||||
|
||||
class GrContext;
|
||||
class GrPaint;
|
||||
class SkBitmap;
|
||||
class SkBlitter;
|
||||
class SkBounder;
|
||||
@ -22,6 +23,7 @@ class SkMatrix;
|
||||
class SkPath;
|
||||
class SkRasterClip;
|
||||
class SkRRect;
|
||||
class SkStrokeRec;
|
||||
|
||||
/** \class SkMaskFilter
|
||||
|
||||
@ -93,6 +95,15 @@ public:
|
||||
const SkMatrix& ctm,
|
||||
SkRect* maskRect) const;
|
||||
|
||||
/**
|
||||
* Try to directly render the mask filter into the target. Returns
|
||||
* true if drawing was successful.
|
||||
*/
|
||||
virtual bool directFilterMaskGPU(GrContext* context,
|
||||
GrPaint* grp,
|
||||
const SkStrokeRec& strokeRec,
|
||||
const SkPath& path) const;
|
||||
|
||||
/**
|
||||
* This function is used to implement filters that require an explicit src mask. It should only
|
||||
* be called if canFilterMaskGPU returned true and the maskRect param should be the output from
|
||||
|
@ -309,6 +309,13 @@ bool SkMaskFilter::canFilterMaskGPU(const SkRect& devBounds,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SkMaskFilter::directFilterMaskGPU(GrContext* context,
|
||||
GrPaint* grp,
|
||||
const SkStrokeRec& strokeRec,
|
||||
const SkPath& path) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool SkMaskFilter::filterMaskGPU(GrTexture* src,
|
||||
const SkMatrix& ctm,
|
||||
|
@ -671,7 +671,7 @@ static float gaussianIntegral(float x) {
|
||||
return 0.4375f + (-x3 / 6.0f - 3.0f * x2 * 0.25f - 1.125f * x);
|
||||
}
|
||||
|
||||
/* compute_profile allocates and fills in an array of floating
|
||||
/* ComputeBlurProfile allocates and fills in an array of floating
|
||||
point values between 0 and 255 for the profile signature of
|
||||
a blurred half-plane with the given blur radius. Since we're
|
||||
going to be doing screened multiplications (i.e., 1 - (1-x)(1-y))
|
||||
@ -682,11 +682,11 @@ static float gaussianIntegral(float x) {
|
||||
memory returned in profile_out.
|
||||
*/
|
||||
|
||||
static void compute_profile(SkScalar sigma, unsigned int **profile_out) {
|
||||
void SkBlurMask::ComputeBlurProfile(SkScalar sigma, uint8_t **profile_out) {
|
||||
int size = SkScalarCeilToInt(6*sigma);
|
||||
|
||||
int center = size >> 1;
|
||||
unsigned int *profile = SkNEW_ARRAY(unsigned int, size);
|
||||
uint8_t *profile = SkNEW_ARRAY(uint8_t, size);
|
||||
|
||||
float invr = 1.f/(2*sigma);
|
||||
|
||||
@ -707,7 +707,7 @@ static void compute_profile(SkScalar sigma, unsigned int **profile_out) {
|
||||
// Implementation adapted from Michael Herf's approach:
|
||||
// http://stereopsis.com/shadowrect/
|
||||
|
||||
static inline unsigned int profile_lookup( unsigned int *profile, int loc, int blurred_width, int sharp_width ) {
|
||||
uint8_t SkBlurMask::ProfileLookup(const uint8_t *profile, int loc, int blurred_width, int sharp_width) {
|
||||
int dx = SkAbs32(((loc << 1) + 1) - blurred_width) - sharp_width; // how far are we from the original edge?
|
||||
int ox = dx >> 1;
|
||||
if (ox < 0) {
|
||||
@ -717,6 +717,30 @@ static inline unsigned int profile_lookup( unsigned int *profile, int loc, int b
|
||||
return profile[ox];
|
||||
}
|
||||
|
||||
void SkBlurMask::ComputeBlurredScanline(uint8_t *pixels, const uint8_t *profile,
|
||||
unsigned int width, SkScalar sigma) {
|
||||
|
||||
unsigned int profile_size = SkScalarCeilToInt(6*sigma);
|
||||
SkAutoTMalloc<uint8_t> horizontalScanline(width);
|
||||
|
||||
unsigned int sw = width - profile_size;
|
||||
// nearest odd number less than the profile size represents the center
|
||||
// of the (2x scaled) profile
|
||||
int center = ( profile_size & ~1 ) - 1;
|
||||
|
||||
int w = sw - center;
|
||||
|
||||
for (unsigned int x = 0 ; x < width ; ++x) {
|
||||
if (profile_size <= sw) {
|
||||
pixels[x] = ProfileLookup(profile, x, width, w);
|
||||
} else {
|
||||
float span = float(sw)/(2*sigma);
|
||||
float giX = 1.5f - (x+.5f)/(2*sigma);
|
||||
pixels[x] = (uint8_t) (255 * (gaussianIntegral(giX) - gaussianIntegral(giX + span)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SkBlurMask::BlurRect(SkMask *dst, const SkRect &src,
|
||||
SkScalar radius, Style style,
|
||||
SkIPoint *margin, SkMask::CreateMode createMode) {
|
||||
@ -757,10 +781,10 @@ bool SkBlurMask::BlurRect(SkScalar sigma, SkMask *dst,
|
||||
}
|
||||
return true;
|
||||
}
|
||||
unsigned int *profile = NULL;
|
||||
uint8_t *profile = NULL;
|
||||
|
||||
compute_profile(sigma, &profile);
|
||||
SkAutoTDeleteArray<unsigned int> ada(profile);
|
||||
ComputeBlurProfile(sigma, &profile);
|
||||
SkAutoTDeleteArray<uint8_t> ada(profile);
|
||||
|
||||
size_t dstSize = dst->computeImageSize();
|
||||
if (0 == dstSize) {
|
||||
@ -774,39 +798,17 @@ bool SkBlurMask::BlurRect(SkScalar sigma, SkMask *dst,
|
||||
int dstHeight = dst->fBounds.height();
|
||||
int dstWidth = dst->fBounds.width();
|
||||
|
||||
// nearest odd number less than the profile size represents the center
|
||||
// of the (2x scaled) profile
|
||||
int center = ( profile_size & ~1 ) - 1;
|
||||
|
||||
int w = sw - center;
|
||||
int h = sh - center;
|
||||
|
||||
uint8_t *outptr = dp;
|
||||
|
||||
SkAutoTMalloc<uint8_t> horizontalScanline(dstWidth);
|
||||
SkAutoTMalloc<uint8_t> verticalScanline(dstHeight);
|
||||
|
||||
for (int x = 0 ; x < dstWidth ; ++x) {
|
||||
if (profile_size <= sw) {
|
||||
horizontalScanline[x] = profile_lookup(profile, x, dstWidth, w);
|
||||
} else {
|
||||
float span = float(sw)/(2*sigma);
|
||||
float giX = 1.5f - (x+.5f)/(2*sigma);
|
||||
horizontalScanline[x] = (uint8_t) (255 * (gaussianIntegral(giX) - gaussianIntegral(giX + span)));
|
||||
}
|
||||
}
|
||||
ComputeBlurredScanline(horizontalScanline, profile, dstWidth, sigma);
|
||||
ComputeBlurredScanline(verticalScanline, profile, dstHeight, sigma);
|
||||
|
||||
for (int y = 0 ; y < dstHeight ; ++y) {
|
||||
unsigned int profile_y;
|
||||
if (profile_size <= sh) {
|
||||
profile_y = profile_lookup(profile, y, dstHeight, h);
|
||||
} else {
|
||||
float span = float(sh)/(2*sigma);
|
||||
float giY = 1.5f - (y+.5f)/(2*sigma);
|
||||
profile_y = (uint8_t) (255 * (gaussianIntegral(giY) - gaussianIntegral(giY + span)));
|
||||
}
|
||||
|
||||
for (int x = 0 ; x < dstWidth ; x++) {
|
||||
unsigned int maskval = SkMulDiv255Round(horizontalScanline[x], profile_y);
|
||||
unsigned int maskval = SkMulDiv255Round(horizontalScanline[x], verticalScanline[y]);
|
||||
*(outptr++) = maskval;
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +62,41 @@ public:
|
||||
SkIPoint* margin = NULL);
|
||||
|
||||
static SkScalar ConvertRadiusToSigma(SkScalar radius);
|
||||
|
||||
/* Helper functions for analytic rectangle blurs */
|
||||
|
||||
/** Look up the intensity of the (one dimnensional) blurred half-plane.
|
||||
@param profile The precomputed 1D blur profile; memory allocated by and managed by
|
||||
ComputeBlurProfile below.
|
||||
@param loc the location to look up; The lookup will clamp invalid inputs, but
|
||||
meaningful data are available between 0 and blurred_width
|
||||
@param blurred_width The width of the final, blurred rectangle
|
||||
@param sharp_width The width of the original, unblurred rectangle.
|
||||
*/
|
||||
static uint8_t ProfileLookup(const uint8_t* profile, int loc, int blurred_width, int sharp_width);
|
||||
|
||||
/** Allocate memory for and populate the profile of a 1D blurred halfplane. The caller
|
||||
must free the memory. The amount of memory allocated will be exactly 6*sigma bytes.
|
||||
@param sigma The standard deviation of the gaussian blur kernel
|
||||
@param profile_out The location to store the allocated profile curve
|
||||
*/
|
||||
|
||||
static void ComputeBlurProfile(SkScalar sigma, uint8_t** profile_out);
|
||||
|
||||
/** Compute an entire scanline of a blurred step function. This is a 1D helper that
|
||||
will produce both the horizontal and vertical profiles of the blurry rectangle.
|
||||
@param pixels Location to store the resulting pixel data; allocated and managed by caller
|
||||
@param profile Precomputed blur profile computed by ComputeBlurProfile above.
|
||||
@param width Size of the pixels array.
|
||||
@param sigma Standard deviation of the gaussian blur kernel used to compute the profile;
|
||||
this implicitly gives the size of the pixels array.
|
||||
*/
|
||||
|
||||
static void ComputeBlurredScanline(uint8_t* pixels, const uint8_t* profile,
|
||||
unsigned int width, SkScalar sigma);
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -19,7 +19,10 @@
|
||||
#if SK_SUPPORT_GPU
|
||||
#include "GrContext.h"
|
||||
#include "GrTexture.h"
|
||||
#include "GrEffect.h"
|
||||
#include "gl/GrGLEffect.h"
|
||||
#include "effects/GrSimpleTextureEffect.h"
|
||||
#include "GrTBackendEffectFactory.h"
|
||||
#include "SkGrPixelRef.h"
|
||||
#endif
|
||||
|
||||
@ -37,6 +40,11 @@ public:
|
||||
const SkIRect& clipBounds,
|
||||
const SkMatrix& ctm,
|
||||
SkRect* maskRect) const SK_OVERRIDE;
|
||||
virtual bool directFilterMaskGPU(GrContext* context,
|
||||
GrPaint* grp,
|
||||
const SkStrokeRec& strokeRec,
|
||||
const SkPath& path) const SK_OVERRIDE;
|
||||
|
||||
virtual bool filterMaskGPU(GrTexture* src,
|
||||
const SkMatrix& ctm,
|
||||
const SkRect& maskRect,
|
||||
@ -500,6 +508,274 @@ void SkBlurMaskFilterImpl::flatten(SkFlattenableWriteBuffer& buffer) const {
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
|
||||
class GrGLRectBlurEffect;
|
||||
|
||||
class GrRectBlurEffect : public GrEffect {
|
||||
public:
|
||||
virtual ~GrRectBlurEffect();
|
||||
|
||||
static const char* Name() { return "RectBlur"; }
|
||||
|
||||
typedef GrGLRectBlurEffect GLEffect;
|
||||
|
||||
virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
|
||||
virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
|
||||
|
||||
/**
|
||||
* Create a simple filter effect with custom bicubic coefficients.
|
||||
*/
|
||||
static GrEffectRef* Create(GrContext *context, const SkRect& rect,
|
||||
float sigma) {
|
||||
GrTexture *horizontalScanline, *verticalScanline;
|
||||
bool createdScanlines = CreateScanlineTextures(context, sigma,
|
||||
SkScalarCeilToInt(rect.width()),
|
||||
SkScalarCeilToInt(rect.height()),
|
||||
&horizontalScanline, &verticalScanline);
|
||||
if (!createdScanlines) {
|
||||
return NULL;
|
||||
}
|
||||
AutoEffectUnref effect(SkNEW_ARGS(GrRectBlurEffect, (rect, sigma,
|
||||
horizontalScanline, verticalScanline)));
|
||||
return CreateEffectRef(effect);
|
||||
}
|
||||
|
||||
unsigned int getWidth() const { return fWidth; }
|
||||
unsigned int getHeight() const { return fHeight; }
|
||||
float getSigma() const { return fSigma; }
|
||||
|
||||
private:
|
||||
GrRectBlurEffect(const SkRect& rect, float sigma,
|
||||
GrTexture *horizontal_scanline, GrTexture *vertical_scanline);
|
||||
virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
|
||||
|
||||
static bool CreateScanlineTextures(GrContext *context, float sigma,
|
||||
unsigned int width, unsigned int height,
|
||||
GrTexture **horizontalScanline,
|
||||
GrTexture **verticalScanline);
|
||||
|
||||
unsigned int fWidth, fHeight;
|
||||
float fSigma;
|
||||
GrTextureAccess fHorizontalScanlineAccess;
|
||||
GrTextureAccess fVerticalScanlineAccess;
|
||||
GrCoordTransform fTransform;
|
||||
|
||||
GR_DECLARE_EFFECT_TEST;
|
||||
|
||||
typedef GrEffect INHERITED;
|
||||
};
|
||||
|
||||
class GrGLRectBlurEffect : public GrGLEffect {
|
||||
public:
|
||||
GrGLRectBlurEffect(const GrBackendEffectFactory& factory,
|
||||
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:
|
||||
typedef GrGLUniformManager::UniformHandle UniformHandle;
|
||||
|
||||
UniformHandle fWidthUni;
|
||||
UniformHandle fHeightUni;
|
||||
|
||||
typedef GrGLEffect INHERITED;
|
||||
};
|
||||
|
||||
GrGLRectBlurEffect::GrGLRectBlurEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
|
||||
: INHERITED(factory) {
|
||||
}
|
||||
|
||||
void GrGLRectBlurEffect::emitCode(GrGLShaderBuilder* builder,
|
||||
const GrDrawEffect&,
|
||||
EffectKey key,
|
||||
const char* outputColor,
|
||||
const char* inputColor,
|
||||
const TransformedCoordsArray& coords,
|
||||
const TextureSamplerArray& samplers) {
|
||||
|
||||
SkString texture_coords = builder->ensureFSCoords2D(coords, 0);
|
||||
|
||||
if (inputColor) {
|
||||
builder->fsCodeAppendf("\tvec4 src=%s;\n", inputColor);
|
||||
} else {
|
||||
builder->fsCodeAppendf("\tvec4 src=vec4(1)\n;");
|
||||
}
|
||||
|
||||
builder->fsCodeAppendf("\tvec4 horiz = ");
|
||||
builder->fsAppendTextureLookup( samplers[0], texture_coords.c_str() );
|
||||
builder->fsCodeAppendf(";\n");
|
||||
builder->fsCodeAppendf("\tvec4 vert = ");
|
||||
builder->fsAppendTextureLookup( samplers[1], texture_coords.c_str() );
|
||||
builder->fsCodeAppendf(";\n");
|
||||
|
||||
builder->fsCodeAppendf("\tfloat final = (horiz*vert).r;\n");
|
||||
builder->fsCodeAppendf("\t%s = final*src;\n", outputColor);
|
||||
}
|
||||
|
||||
void GrGLRectBlurEffect::setData(const GrGLUniformManager& uman,
|
||||
const GrDrawEffect& drawEffect) {
|
||||
}
|
||||
|
||||
bool GrRectBlurEffect::CreateScanlineTextures(GrContext *context, float sigma,
|
||||
unsigned int width, unsigned int height,
|
||||
GrTexture **horizontalScanline,
|
||||
GrTexture **verticalScanline) {
|
||||
GrTextureParams params;
|
||||
GrTextureDesc texDesc;
|
||||
|
||||
unsigned int profile_size = SkScalarFloorToInt(6*sigma);
|
||||
|
||||
texDesc.fWidth = width;
|
||||
texDesc.fHeight = 1;
|
||||
texDesc.fConfig = kAlpha_8_GrPixelConfig;
|
||||
|
||||
static const GrCacheID::Domain gBlurProfileDomain = GrCacheID::GenerateDomain();
|
||||
GrCacheID::Key key;
|
||||
memset(&key, 0, sizeof(key));
|
||||
key.fData32[0] = profile_size;
|
||||
key.fData32[1] = width;
|
||||
key.fData32[2] = 1;
|
||||
GrCacheID horizontalCacheID(gBlurProfileDomain, key);
|
||||
|
||||
uint8_t *profile = NULL;
|
||||
SkAutoTDeleteArray<uint8_t> ada(profile);
|
||||
|
||||
*horizontalScanline = context->findAndRefTexture(texDesc, horizontalCacheID, ¶ms);
|
||||
|
||||
if (NULL == *horizontalScanline) {
|
||||
|
||||
SkBlurMask::ComputeBlurProfile(sigma, &profile);
|
||||
|
||||
SkAutoTMalloc<uint8_t> horizontalPixels(width);
|
||||
SkBlurMask::ComputeBlurredScanline(horizontalPixels, profile, width, sigma);
|
||||
|
||||
*horizontalScanline = context->createTexture(¶ms, texDesc, horizontalCacheID,
|
||||
horizontalPixels, 0);
|
||||
|
||||
if (NULL == *horizontalScanline) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
texDesc.fWidth = 1;
|
||||
texDesc.fHeight = height;
|
||||
key.fData32[1] = 1;
|
||||
key.fData32[2] = height;
|
||||
GrCacheID verticalCacheID(gBlurProfileDomain, key);
|
||||
|
||||
*verticalScanline = context->findAndRefTexture(texDesc, verticalCacheID, ¶ms);
|
||||
if (NULL == *verticalScanline) {
|
||||
if (NULL == profile) {
|
||||
SkBlurMask::ComputeBlurProfile(sigma, &profile);
|
||||
}
|
||||
|
||||
SkAutoTMalloc<uint8_t> verticalPixels(height);
|
||||
SkBlurMask::ComputeBlurredScanline(verticalPixels, profile, height, sigma);
|
||||
|
||||
*verticalScanline = context->createTexture(¶ms, texDesc, verticalCacheID,
|
||||
verticalPixels, 0);
|
||||
|
||||
if (NULL == *verticalScanline) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
GrRectBlurEffect::GrRectBlurEffect(const SkRect& rect, float sigma,
|
||||
GrTexture *horizontal_scanline, GrTexture *vertical_scanline)
|
||||
: INHERITED(),
|
||||
fWidth(horizontal_scanline->width()),
|
||||
fHeight(vertical_scanline->width()),
|
||||
fSigma(sigma),
|
||||
fHorizontalScanlineAccess(horizontal_scanline),
|
||||
fVerticalScanlineAccess(vertical_scanline) {
|
||||
SkMatrix mat;
|
||||
mat.setRectToRect(rect, SkRect::MakeWH(1,1), SkMatrix::kFill_ScaleToFit);
|
||||
fTransform = GrCoordTransform(kLocal_GrCoordSet, mat);
|
||||
this->addTextureAccess(&fHorizontalScanlineAccess);
|
||||
this->addTextureAccess(&fVerticalScanlineAccess);
|
||||
this->addCoordTransform(&fTransform);
|
||||
}
|
||||
|
||||
GrRectBlurEffect::~GrRectBlurEffect() {
|
||||
}
|
||||
|
||||
const GrBackendEffectFactory& GrRectBlurEffect::getFactory() const {
|
||||
return GrTBackendEffectFactory<GrRectBlurEffect>::getInstance();
|
||||
}
|
||||
|
||||
bool GrRectBlurEffect::onIsEqual(const GrEffect& sBase) const {
|
||||
const GrRectBlurEffect& s = CastEffect<GrRectBlurEffect>(sBase);
|
||||
return this->getWidth() == s.getWidth() &&
|
||||
this->getHeight() == s.getHeight() &&
|
||||
this->getSigma() == s.getSigma();
|
||||
}
|
||||
|
||||
void GrRectBlurEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
|
||||
*validFlags = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
GR_DEFINE_EFFECT_TEST(GrRectBlurEffect);
|
||||
|
||||
GrEffectRef* GrRectBlurEffect::TestCreate(SkRandom* random,
|
||||
GrContext* context,
|
||||
const GrDrawTargetCaps&,
|
||||
GrTexture**) {
|
||||
float sigma = random->nextRangeF(3,8);
|
||||
float width = random->nextRangeF(200,300);
|
||||
float height = random->nextRangeF(200,300);
|
||||
return GrRectBlurEffect::Create(context, SkRect::MakeWH(width, height), sigma);
|
||||
}
|
||||
|
||||
|
||||
bool SkBlurMaskFilterImpl::directFilterMaskGPU(GrContext* context,
|
||||
GrPaint* grp,
|
||||
const SkStrokeRec& strokeRec,
|
||||
const SkPath& path) const {
|
||||
if (fBlurStyle != SkBlurMaskFilter::kNormal_BlurStyle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SkRect rect;
|
||||
if (!path.isRect(&rect)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!strokeRec.isFillStyle()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SkMatrix ctm = context->getMatrix();
|
||||
SkScalar xformedSigma = this->computeXformedSigma(ctm);
|
||||
rect.outset(3*xformedSigma, 3*xformedSigma);
|
||||
|
||||
SkAutoTUnref<GrEffectRef> effect(GrRectBlurEffect::Create(
|
||||
context, rect, xformedSigma));
|
||||
if (!effect) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GrContext::AutoMatrix am;
|
||||
if (!am.setIdentity(context, grp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
grp->addCoverageEffect(effect);
|
||||
|
||||
context->drawRect(*grp, rect);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkBlurMaskFilterImpl::canFilterMaskGPU(const SkRect& srcBounds,
|
||||
const SkIRect& clipBounds,
|
||||
const SkMatrix& ctm,
|
||||
|
@ -954,6 +954,13 @@ void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath,
|
||||
return;
|
||||
}
|
||||
|
||||
if (paint.getMaskFilter()->directFilterMaskGPU(fContext, &grPaint,
|
||||
SkStrokeRec(paint), *devPathPtr)) {
|
||||
// the mask filter was able to draw itself directly, so there's nothing
|
||||
// left to do.
|
||||
return;
|
||||
}
|
||||
|
||||
GrAutoScratchTexture mask;
|
||||
|
||||
if (create_mask_GPU(fContext, maskRect, *devPathPtr, stroke,
|
||||
|
Loading…
Reference in New Issue
Block a user