aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/core/SkMaskFilter.h64
-rw-r--r--src/core/SkMaskFilter.cpp69
-rw-r--r--src/device/xps/SkXPSDevice.cpp15
-rw-r--r--src/effects/SkBlurMaskFilter.cpp160
-rw-r--r--src/gpu/SkGpuDevice.cpp299
-rw-r--r--tests/BlurTest.cpp12
-rw-r--r--tools/PictureRenderer.cpp5
-rw-r--r--tools/PictureRenderer.h4
8 files changed, 366 insertions, 262 deletions
diff --git a/include/core/SkMaskFilter.h b/include/core/SkMaskFilter.h
index aa499ecd23..40a03a5205 100644
--- a/include/core/SkMaskFilter.h
+++ b/include/core/SkMaskFilter.h
@@ -14,6 +14,7 @@
#include "SkMask.h"
#include "SkPaint.h"
+class SkBitmap;
class SkBlitter;
class SkBounder;
class SkMatrix;
@@ -58,27 +59,56 @@ public:
virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
SkIPoint* margin) const;
- enum BlurType {
- kNone_BlurType, //!< this maskfilter is not a blur
- kNormal_BlurType, //!< fuzzy inside and outside
- kSolid_BlurType, //!< solid inside, fuzzy outside
- kOuter_BlurType, //!< nothing inside, fuzzy outside
- kInner_BlurType //!< fuzzy inside, nothing outside
- };
+#if SK_SUPPORT_GPU
+ /**
+ * Returns true if the filter can be expressed a single-pass
+ * GrEffect, used to process this filter on the GPU, or false if
+ * not.
+ *
+ * If effect is non-NULL, a new GrEffect instance is stored
+ * in it. The caller assumes ownership of the stage, and it is up to the
+ * caller to unref it.
+ */
+ virtual bool asNewEffect(GrEffectRef** effect, GrTexture*) const;
- struct BlurInfo {
- SkScalar fRadius;
- bool fIgnoreTransform;
- bool fHighQuality;
- };
+ /**
+ * Returns true if the filter can be processed on the GPU. This is most
+ * often used for multi-pass effects, where intermediate results must be
+ * rendered to textures. For single-pass effects, use asNewEffect().
+ *
+ * 'maskRect' returns the device space portion of the mask the the filter
+ * needs. The mask passed into 'filterMaskGPU' should have the same extent
+ * as 'maskRect' but be translated to the upper-left corner of the mask
+ * (i.e., (maskRect.fLeft, maskRect.fTop) appears at (0, 0) in the mask).
+ */
+ virtual bool canFilterMaskGPU(const SkRect& devBounds,
+ const SkIRect& clipBounds,
+ const SkMatrix& ctm,
+ SkRect* maskRect) const;
+
+ /**
+ * Perform this mask filter on the GPU. This is most often used for
+ * multi-pass effects, where intermediate results must be rendered to
+ * textures. For single-pass effects, use asNewEffect(). 'src' is the
+ * source image for processing, as a texture-backed bitmap. 'result' is
+ * the destination bitmap, which should contain a texture-backed pixelref
+ * on success. 'maskRect' should be the rect returned from canFilterMaskGPU.
+ */
+ bool filterMaskGPU(GrContext* context,
+ const SkBitmap& src,
+ const SkRect& maskRect,
+ SkBitmap* result) const;
/**
- * Optional method for maskfilters that can be described as a blur. If so,
- * they return the corresponding BlurType and set the fields in BlurInfo
- * (if not null). If they cannot be described as a blur, they return
- * kNone_BlurType and ignore the info parameter.
+ * This flavor of 'filterMaskGPU' provides a more direct means of accessing
+ * the filtering capabilities. Setting 'canOverwriteSrc' can allow some
+ * filters to skip the allocation of an additional texture.
*/
- virtual BlurType asABlur(BlurInfo*) const;
+ virtual bool filterMaskGPU(GrTexture* src,
+ const SkRect& maskRect,
+ GrTexture** result,
+ bool canOverwriteSrc) const;
+#endif
/**
* The fast bounds function is used to enable the paint to be culled early
diff --git a/src/core/SkMaskFilter.cpp b/src/core/SkMaskFilter.cpp
index 0346ce0e9c..fbee4dc09b 100644
--- a/src/core/SkMaskFilter.cpp
+++ b/src/core/SkMaskFilter.cpp
@@ -11,7 +11,11 @@
#include "SkBlitter.h"
#include "SkBounder.h"
#include "SkDraw.h"
+#include "SkGr.h"
+#include "SkGrPixelRef.h"
#include "SkRasterClip.h"
+#include "SkTypes.h"
+#include "GrTexture.h"
SK_DEFINE_INST_COUNT(SkMaskFilter)
@@ -266,8 +270,69 @@ SkMaskFilter::filterRectsToNine(const SkRect[], int count, const SkMatrix&,
return kUnimplemented_FilterReturn;
}
-SkMaskFilter::BlurType SkMaskFilter::asABlur(BlurInfo*) const {
- return kNone_BlurType;
+bool SkMaskFilter::asNewEffect(GrEffectRef** effect, GrTexture*) const {
+ return false;
+}
+
+bool SkMaskFilter::canFilterMaskGPU(const SkRect& devBounds,
+ const SkIRect& clipBounds,
+ const SkMatrix& ctm,
+ SkRect* maskRect) const {
+ return false;
+}
+
+bool SkMaskFilter::filterMaskGPU(GrContext* context,
+ const SkBitmap& srcBM,
+ const SkRect& maskRect,
+ SkBitmap* resultBM) const {
+ SkAutoTUnref<GrTexture> src;
+ bool canOverwriteSrc = false;
+ if (NULL == srcBM.getTexture()) {
+ GrTextureDesc desc;
+ // Needs to be a render target to be overwritten in filterMaskGPU
+ desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
+ desc.fConfig = SkBitmapConfig2GrPixelConfig(srcBM.config());
+ desc.fWidth = srcBM.width();
+ desc.fHeight = srcBM.height();
+
+ // TODO: right now this is exact to guard against out of bounds reads
+ // by the filter code. More thought needs to be devoted to the
+ // "filterMaskGPU" contract and then enforced (i.e., clamp the code
+ // in "filterMaskGPU" so it never samples beyond maskRect)
+ GrAutoScratchTexture ast(context, desc, GrContext::kExact_ScratchTexMatch);
+ if (NULL == ast.texture()) {
+ return false;
+ }
+
+ SkAutoLockPixels alp(srcBM);
+ ast.texture()->writePixels(0, 0, srcBM.width(), srcBM.height(),
+ desc.fConfig,
+ srcBM.getPixels(), srcBM.rowBytes());
+
+ src.reset(ast.detach());
+ canOverwriteSrc = true;
+ } else {
+ src.reset((GrTexture*) srcBM.getTexture());
+ src.get()->ref();
+ }
+ GrTexture* dst;
+
+ bool result = this->filterMaskGPU(src, maskRect, &dst, canOverwriteSrc);
+ if (!result) {
+ return false;
+ }
+
+ resultBM->setConfig(srcBM.config(), dst->width(), dst->height());
+ resultBM->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (dst)))->unref();
+ dst->unref();
+ return true;
+}
+
+bool SkMaskFilter::filterMaskGPU(GrTexture* src,
+ const SkRect& maskRect,
+ GrTexture** result,
+ bool canOverwriteSrc) const {
+ return false;
}
void SkMaskFilter::computeFastBounds(const SkRect& src, SkRect* dst) const {
diff --git a/src/device/xps/SkXPSDevice.cpp b/src/device/xps/SkXPSDevice.cpp
index d7a559865f..bfae6de2af 100644
--- a/src/device/xps/SkXPSDevice.cpp
+++ b/src/device/xps/SkXPSDevice.cpp
@@ -1542,21 +1542,6 @@ void SkXPSDevice::convertToPpm(const SkMaskFilter* filter,
SkMatrix* matrix,
SkVector* ppuScale,
const SkIRect& clip, SkIRect* clipIRect) {
- //TODO: currently ignoring the ppm if blur ignoring transform.
- if (filter) {
- SkMaskFilter::BlurInfo blurInfo;
- SkMaskFilter::BlurType blurType = filter->asABlur(&blurInfo);
-
- if (SkMaskFilter::kNone_BlurType != blurType
- && blurInfo.fIgnoreTransform) {
-
- ppuScale->fX = SK_Scalar1;
- ppuScale->fY = SK_Scalar1;
- *clipIRect = clip;
- return;
- }
- }
-
//This action is in unit space, but the ppm is specified in physical space.
ppuScale->fX = SkScalarDiv(this->fCurrentPixelsPerMeter.fX,
this->fCurrentUnitsPerMeter.fX);
diff --git a/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp
index eaf7704bce..8c14bc8845 100644
--- a/src/effects/SkBlurMaskFilter.cpp
+++ b/src/effects/SkBlurMaskFilter.cpp
@@ -10,10 +10,16 @@
#include "SkBlurMask.h"
#include "SkFlattenableBuffers.h"
#include "SkMaskFilter.h"
-#include "SkBounder.h"
-#include "SkRasterClip.h"
#include "SkRTConf.h"
#include "SkStringUtils.h"
+#include "SkStrokeRec.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "GrTexture.h"
+#include "effects/GrSimpleTextureEffect.h"
+#include "SkGrPixelRef.h"
+#endif
class SkBlurMaskFilterImpl : public SkMaskFilter {
public:
@@ -25,7 +31,17 @@ public:
virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
SkIPoint* margin) const SK_OVERRIDE;
- virtual BlurType asABlur(BlurInfo*) const SK_OVERRIDE;
+#if SK_SUPPORT_GPU
+ virtual bool canFilterMaskGPU(const SkRect& devBounds,
+ const SkIRect& clipBounds,
+ const SkMatrix& ctm,
+ SkRect* maskRect) const SK_OVERRIDE;
+ virtual bool filterMaskGPU(GrTexture* src,
+ const SkRect& maskRect,
+ GrTexture** result,
+ bool canOverwriteSrc) const;
+#endif
+
virtual void computeFastBounds(const SkRect&, SkRect*) const SK_OVERRIDE;
SkDEVCODE(virtual void toString(SkString* str) const SK_OVERRIDE;)
@@ -40,16 +56,41 @@ protected:
SkIPoint* margin, SkMask::CreateMode createMode) const;
private:
+ // To avoid unseemly allocation requests (esp. for finite platforms like
+ // handset) we limit the radius so something manageable. (as opposed to
+ // a request like 10,000)
+ static const SkScalar kMAX_RADIUS;
+ // This constant approximates the scaling done in the software path's
+ // "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)).
+ // IMHO, it actually should be 1: we blur "less" than we should do
+ // according to the CSS and canvas specs, simply because Safari does the same.
+ // Firefox used to do the same too, until 4.0 where they fixed it. So at some
+ // point we should probably get rid of these scaling constants and rebaseline
+ // all the blur tests.
+ static const SkScalar kBLUR_SIGMA_SCALE;
+
SkScalar fRadius;
SkBlurMaskFilter::BlurStyle fBlurStyle;
uint32_t fBlurFlags;
SkBlurMaskFilterImpl(SkFlattenableReadBuffer&);
virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+#if SK_SUPPORT_GPU
+ SkScalar computeXformedRadius(const SkMatrix& ctm) const {
+ bool ignoreTransform = SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag);
+
+ SkScalar xformedRadius = ignoreTransform ? fRadius
+ : ctm.mapRadius(fRadius);
+ return SkMinScalar(xformedRadius, kMAX_RADIUS);
+ }
+#endif
typedef SkMaskFilter INHERITED;
};
+const SkScalar SkBlurMaskFilterImpl::kMAX_RADIUS = SkIntToScalar(128);
+const SkScalar SkBlurMaskFilterImpl::kBLUR_SIGMA_SCALE = 0.6f;
+
SkMaskFilter* SkBlurMaskFilter::Create(SkScalar radius,
SkBlurMaskFilter::BlurStyle style,
uint32_t flags) {
@@ -97,11 +138,7 @@ bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
radius = matrix.mapRadius(fRadius);
}
- // To avoid unseemly allocation requests (esp. for finite platforms like
- // handset) we limit the radius so something manageable. (as opposed to
- // a request like 10,000)
- static const SkScalar MAX_RADIUS = SkIntToScalar(128);
- radius = SkMinScalar(radius, MAX_RADIUS);
+ radius = SkMinScalar(radius, kMAX_RADIUS);
SkBlurMask::Quality blurQuality =
(fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ?
SkBlurMask::kHigh_Quality : SkBlurMask::kLow_Quality;
@@ -120,11 +157,7 @@ bool SkBlurMaskFilterImpl::filterRectMask(SkMask* dst, const SkRect& r,
radius = matrix.mapRadius(fRadius);
}
- // To avoid unseemly allocation requests (esp. for finite platforms like
- // handset) we limit the radius so something manageable. (as opposed to
- // a request like 10,000)
- static const SkScalar MAX_RADIUS = SkIntToScalar(128);
- radius = SkMinScalar(radius, MAX_RADIUS);
+ radius = SkMinScalar(radius, kMAX_RADIUS);
return SkBlurMask::BlurRect(dst, r, radius, (SkBlurMask::Style)fBlurStyle,
margin, createMode);
@@ -320,22 +353,99 @@ void SkBlurMaskFilterImpl::flatten(SkFlattenableWriteBuffer& buffer) const {
buffer.writeUInt(fBlurFlags);
}
-static const SkMaskFilter::BlurType gBlurStyle2BlurType[] = {
- SkMaskFilter::kNormal_BlurType,
- SkMaskFilter::kSolid_BlurType,
- SkMaskFilter::kOuter_BlurType,
- SkMaskFilter::kInner_BlurType,
-};
+#if SK_SUPPORT_GPU
-SkMaskFilter::BlurType SkBlurMaskFilterImpl::asABlur(BlurInfo* info) const {
- if (info) {
- info->fRadius = fRadius;
- info->fIgnoreTransform = SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag);
- info->fHighQuality = SkToBool(fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag);
+bool SkBlurMaskFilterImpl::canFilterMaskGPU(const SkRect& srcBounds,
+ const SkIRect& clipBounds,
+ const SkMatrix& ctm,
+ SkRect* maskRect) const {
+ SkScalar xformedRadius = this->computeXformedRadius(ctm);
+ if (xformedRadius <= 0) {
+ return false;
}
- return gBlurStyle2BlurType[fBlurStyle];
+
+ static const SkScalar kMIN_GPU_BLUR_SIZE = SkIntToScalar(64);
+ static const SkScalar kMIN_GPU_BLUR_RADIUS = SkIntToScalar(32);
+
+ if (srcBounds.width() <= kMIN_GPU_BLUR_SIZE &&
+ srcBounds.height() <= kMIN_GPU_BLUR_SIZE &&
+ xformedRadius <= kMIN_GPU_BLUR_RADIUS) {
+ // We prefer to blur small rect with small radius via CPU.
+ return false;
+ }
+
+ if (NULL == maskRect) {
+ // don't need to compute maskRect
+ return true;
+ }
+
+ float sigma3 = 3 * SkScalarToFloat(xformedRadius) * kBLUR_SIGMA_SCALE;
+
+ SkRect clipRect = SkRect::MakeFromIRect(clipBounds);
+ SkRect srcRect(srcBounds);
+
+ // Outset srcRect and clipRect by 3 * sigma, to compute affected blur area.
+ srcRect.outset(SkFloatToScalar(sigma3), SkFloatToScalar(sigma3));
+ clipRect.outset(SkFloatToScalar(sigma3), SkFloatToScalar(sigma3));
+ srcRect.intersect(clipRect);
+ *maskRect = srcRect;
+ return true;
+}
+
+bool SkBlurMaskFilterImpl::filterMaskGPU(GrTexture* src,
+ const SkRect& maskRect,
+ GrTexture** result,
+ bool canOverwriteSrc) const {
+ SkRect clipRect = SkRect::MakeWH(maskRect.width(), maskRect.height());
+
+ GrContext* context = src->getContext();
+
+ GrContext::AutoWideOpenIdentityDraw awo(context, NULL);
+
+ SkScalar xformedRadius = this->computeXformedRadius(context->getMatrix());
+ SkASSERT(xformedRadius > 0);
+
+ float sigma = SkScalarToFloat(xformedRadius) * kBLUR_SIGMA_SCALE;
+
+ // If we're doing a normal blur, we can clobber the pathTexture in the
+ // gaussianBlur. Otherwise, we need to save it for later compositing.
+ bool isNormalBlur = (SkBlurMaskFilter::kNormal_BlurStyle == fBlurStyle);
+ *result = context->gaussianBlur(src, isNormalBlur && canOverwriteSrc,
+ clipRect, sigma, sigma);
+ if (NULL == result) {
+ return false;
+ }
+
+ if (!isNormalBlur) {
+ context->setIdentityMatrix();
+ GrPaint paint;
+ SkMatrix matrix;
+ matrix.setIDiv(src->width(), src->height());
+ // Blend pathTexture over blurTexture.
+ GrContext::AutoRenderTarget art(context, (*result)->asRenderTarget());
+ paint.colorStage(0)->setEffect(
+ GrSimpleTextureEffect::Create(src, matrix))->unref();
+ if (SkBlurMaskFilter::kInner_BlurStyle == fBlurStyle) {
+ // inner: dst = dst * src
+ paint.setBlendFunc(kDC_GrBlendCoeff, kZero_GrBlendCoeff);
+ } else if (SkBlurMaskFilter::kSolid_BlurStyle == fBlurStyle) {
+ // solid: dst = src + dst - src * dst
+ // = (1 - dst) * src + 1 * dst
+ paint.setBlendFunc(kIDC_GrBlendCoeff, kOne_GrBlendCoeff);
+ } else if (SkBlurMaskFilter::kOuter_BlurStyle == fBlurStyle) {
+ // outer: dst = dst * (1 - src)
+ // = 0 * src + (1 - src) * dst
+ paint.setBlendFunc(kZero_GrBlendCoeff, kISC_GrBlendCoeff);
+ }
+ context->drawRect(paint, clipRect);
+ }
+
+ return true;
}
+#endif // SK_SUPPORT_GPU
+
+
#ifdef SK_DEVELOPER
void SkBlurMaskFilterImpl::toString(SkString* str) const {
str->append("SkBlurMaskFilterImpl: (");
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index bfc3695f3e..01b80213a9 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -47,18 +47,6 @@ enum {
kXfermodeEffectIdx = 2,
};
-#define MAX_BLUR_SIGMA 4.0f
-// FIXME: This value comes from from SkBlurMaskFilter.cpp.
-// Should probably be put in a common header someplace.
-#define MAX_BLUR_RADIUS SkIntToScalar(128)
-// This constant approximates the scaling done in the software path's
-// "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)).
-// IMHO, it actually should be 1: we blur "less" than we should do
-// according to the CSS and canvas specs, simply because Safari does the same.
-// Firefox used to do the same too, until 4.0 where they fixed it. So at some
-// point we should probably get rid of these scaling constants and rebaseline
-// all the blur tests.
-#define BLUR_SIGMA_SCALE 0.6f
// This constant represents the screen alignment criterion in texels for
// requiring texture domain clamping to prevent color bleeding when drawing
// a sub region of a larger source image.
@@ -776,138 +764,11 @@ void SkGpuDevice::drawOval(const SkDraw& draw, const SkRect& oval,
// helpers for applying mask filters
namespace {
-// We prefer to blur small rect with small radius via CPU.
-#define MIN_GPU_BLUR_SIZE SkIntToScalar(64)
-#define MIN_GPU_BLUR_RADIUS SkIntToScalar(32)
-inline bool shouldDrawBlurWithCPU(const SkRect& rect, SkScalar radius) {
- if (rect.width() <= MIN_GPU_BLUR_SIZE &&
- rect.height() <= MIN_GPU_BLUR_SIZE &&
- radius <= MIN_GPU_BLUR_RADIUS) {
- return true;
- }
- return false;
-}
-
-bool drawWithGPUMaskFilter(GrContext* context, const SkPath& devPath, const SkStrokeRec& stroke,
- SkMaskFilter* filter, const SkRegion& clip,
- SkBounder* bounder, GrPaint* grp) {
- SkMaskFilter::BlurInfo info;
- SkMaskFilter::BlurType blurType = filter->asABlur(&info);
- if (SkMaskFilter::kNone_BlurType == blurType) {
- return false;
- }
- SkScalar radius = info.fIgnoreTransform ? info.fRadius
- : context->getMatrix().mapRadius(info.fRadius);
- radius = SkMinScalar(radius, MAX_BLUR_RADIUS);
- if (radius <= 0) {
- return false;
- }
-
- SkRect srcRect = devPath.getBounds();
- if (shouldDrawBlurWithCPU(srcRect, radius)) {
- return false;
- }
-
- float sigma = SkScalarToFloat(radius) * BLUR_SIGMA_SCALE;
- float sigma3 = sigma * 3.0f;
-
- SkRect clipRect;
- clipRect.set(clip.getBounds());
-
- // Outset srcRect and clipRect by 3 * sigma, to compute affected blur area.
- srcRect.inset(SkFloatToScalar(-sigma3), SkFloatToScalar(-sigma3));
- clipRect.inset(SkFloatToScalar(-sigma3), SkFloatToScalar(-sigma3));
- srcRect.intersect(clipRect);
- SkRect finalRect = srcRect;
- SkIRect finalIRect;
- finalRect.roundOut(&finalIRect);
- if (clip.quickReject(finalIRect)) {
- return true;
- }
- if (bounder && !bounder->doIRect(finalIRect)) {
- return true;
- }
- GrPoint offset = GrPoint::Make(-srcRect.fLeft, -srcRect.fTop);
- srcRect.offset(offset);
- GrTextureDesc desc;
- desc.fFlags = kRenderTarget_GrTextureFlagBit;
- desc.fWidth = SkScalarCeilToInt(srcRect.width());
- desc.fHeight = SkScalarCeilToInt(srcRect.height());
- // We actually only need A8, but it often isn't supported as a
- // render target so default to RGBA_8888
- desc.fConfig = kRGBA_8888_GrPixelConfig;
-
- if (context->isConfigRenderable(kAlpha_8_GrPixelConfig)) {
- desc.fConfig = kAlpha_8_GrPixelConfig;
- }
-
- GrAutoScratchTexture pathEntry(context, desc);
- GrTexture* pathTexture = pathEntry.texture();
- if (NULL == pathTexture) {
- return false;
- }
-
- SkAutoTUnref<GrTexture> blurTexture;
-
- {
- GrContext::AutoRenderTarget art(context, pathTexture->asRenderTarget());
- GrContext::AutoClip ac(context, srcRect);
-
- context->clear(NULL, 0);
-
- GrPaint tempPaint;
- if (grp->isAntiAlias()) {
- tempPaint.setAntiAlias(true);
- // AA uses the "coverage" stages on GrDrawTarget. Coverage with a dst
- // blend coeff of zero requires dual source blending support in order
- // to properly blend partially covered pixels. This means the AA
- // code path may not be taken. So we use a dst blend coeff of ISA. We
- // could special case AA draws to a dst surface with known alpha=0 to
- // use a zero dst coeff when dual source blending isn't available.f
- tempPaint.setBlendFunc(kOne_GrBlendCoeff, kISC_GrBlendCoeff);
- }
-
- GrContext::AutoMatrix am;
-
- // Draw hard shadow to pathTexture with path top-left at origin using tempPaint.
- SkMatrix translate;
- translate.setTranslate(offset.fX, offset.fY);
- am.set(context, translate);
- context->drawPath(tempPaint, devPath, stroke);
-
- // If we're doing a normal blur, we can clobber the pathTexture in the
- // gaussianBlur. Otherwise, we need to save it for later compositing.
- bool isNormalBlur = blurType == SkMaskFilter::kNormal_BlurType;
- blurTexture.reset(context->gaussianBlur(pathTexture, isNormalBlur,
- srcRect, sigma, sigma));
- if (NULL == blurTexture) {
- return false;
- }
-
- if (!isNormalBlur) {
- context->setIdentityMatrix();
- GrPaint paint;
- SkMatrix matrix;
- matrix.setIDiv(pathTexture->width(), pathTexture->height());
- // Blend pathTexture over blurTexture.
- context->setRenderTarget(blurTexture->asRenderTarget());
- paint.colorStage(0)->setEffect(
- GrSimpleTextureEffect::Create(pathTexture, matrix))->unref();
- if (SkMaskFilter::kInner_BlurType == blurType) {
- // inner: dst = dst * src
- paint.setBlendFunc(kDC_GrBlendCoeff, kZero_GrBlendCoeff);
- } else if (SkMaskFilter::kSolid_BlurType == blurType) {
- // solid: dst = src + dst - src * dst
- // = (1 - dst) * src + 1 * dst
- paint.setBlendFunc(kIDC_GrBlendCoeff, kOne_GrBlendCoeff);
- } else if (SkMaskFilter::kOuter_BlurType == blurType) {
- // outer: dst = dst * (1 - src)
- // = 0 * src + (1 - src) * dst
- paint.setBlendFunc(kZero_GrBlendCoeff, kISC_GrBlendCoeff);
- }
- context->drawRect(paint, srcRect);
- }
- }
+// Draw a mask using the supplied paint. Since the coverage/geometry
+// is already burnt into the mask this boils down to a rect draw.
+// Return true if the mask was successfully drawn.
+bool draw_mask(GrContext* context, const SkRect& maskRect,
+ GrPaint* grp, GrTexture* mask) {
GrContext::AutoMatrix am;
if (!am.setIdentity(context, grp)) {
@@ -919,19 +780,18 @@ bool drawWithGPUMaskFilter(GrContext* context, const SkPath& devPath, const SkSt
GrAssert(!grp->isCoverageStageEnabled(MASK_IDX));
SkMatrix matrix;
- matrix.setTranslate(-finalRect.fLeft, -finalRect.fTop);
- matrix.postIDiv(blurTexture->width(), blurTexture->height());
+ matrix.setTranslate(-maskRect.fLeft, -maskRect.fTop);
+ matrix.postIDiv(mask->width(), mask->height());
grp->coverageStage(MASK_IDX)->reset();
- grp->coverageStage(MASK_IDX)->setEffect(
- GrSimpleTextureEffect::Create(blurTexture, matrix))->unref();
- context->drawRect(*grp, finalRect);
+ grp->coverageStage(MASK_IDX)->setEffect(GrSimpleTextureEffect::Create(mask, matrix))->unref();
+ context->drawRect(*grp, maskRect);
return true;
}
-bool drawWithMaskFilter(GrContext* context, const SkPath& devPath,
- SkMaskFilter* filter, const SkRegion& clip, SkBounder* bounder,
- GrPaint* grp, SkPaint::Style style) {
+bool draw_with_mask_filter(GrContext* context, const SkPath& devPath,
+ SkMaskFilter* filter, const SkRegion& clip, SkBounder* bounder,
+ GrPaint* grp, SkPaint::Style style) {
SkMask srcM, dstM;
if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), filter, &context->getMatrix(), &srcM,
@@ -955,9 +815,6 @@ bool drawWithMaskFilter(GrContext* context, const SkPath& devPath,
// we now have a device-aligned 8bit mask in dstM, ready to be drawn using
// the current clip (and identity matrix) and GrPaint settings
- GrContext::AutoMatrix am;
- am.setIdentity(context, grp);
-
GrTextureDesc desc;
desc.fWidth = dstM.fBounds.width();
desc.fHeight = dstM.fBounds.height();
@@ -972,28 +829,75 @@ bool drawWithMaskFilter(GrContext* context, const SkPath& devPath,
texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
dstM.fImage, dstM.fRowBytes);
- static const int MASK_IDX = GrPaint::kMaxCoverageStages - 1;
- // we assume the last mask index is available for use
- GrAssert(!grp->isCoverageStageEnabled(MASK_IDX));
+ SkRect maskRect = SkRect::MakeFromIRect(dstM.fBounds);
- SkMatrix m;
- m.setTranslate(-dstM.fBounds.fLeft*SK_Scalar1, -dstM.fBounds.fTop*SK_Scalar1);
- m.postIDiv(texture->width(), texture->height());
+ return draw_mask(context, maskRect, grp, texture);
+}
+
+// Create a mask of 'devPath' and place the result in 'mask'. Return true on
+// success; false otherwise.
+bool create_mask_GPU(GrContext* context,
+ const SkRect& maskRect,
+ const SkPath& devPath,
+ const SkStrokeRec& stroke,
+ bool doAA,
+ GrAutoScratchTexture* mask) {
+ GrTextureDesc desc;
+ desc.fFlags = kRenderTarget_GrTextureFlagBit;
+ desc.fWidth = SkScalarCeilToInt(maskRect.width());
+ desc.fHeight = SkScalarCeilToInt(maskRect.height());
+ // We actually only need A8, but it often isn't supported as a
+ // render target so default to RGBA_8888
+ desc.fConfig = kRGBA_8888_GrPixelConfig;
+ if (context->isConfigRenderable(kAlpha_8_GrPixelConfig)) {
+ desc.fConfig = kAlpha_8_GrPixelConfig;
+ }
- grp->coverageStage(MASK_IDX)->setEffect(GrSimpleTextureEffect::Create(texture, m))->unref();
- GrRect d;
- d.setLTRB(SkIntToScalar(dstM.fBounds.fLeft),
- SkIntToScalar(dstM.fBounds.fTop),
- SkIntToScalar(dstM.fBounds.fRight),
- SkIntToScalar(dstM.fBounds.fBottom));
+ mask->set(context, desc);
+ if (NULL == mask->texture()) {
+ return false;
+ }
- context->drawRect(*grp, d);
+ GrTexture* maskTexture = mask->texture();
+ SkRect clipRect = SkRect::MakeWH(maskRect.width(), maskRect.height());
+
+ GrContext::AutoRenderTarget art(context, maskTexture->asRenderTarget());
+ GrContext::AutoClip ac(context, clipRect);
+
+ context->clear(NULL, 0x0);
+
+ GrPaint tempPaint;
+ if (doAA) {
+ tempPaint.setAntiAlias(true);
+ // AA uses the "coverage" stages on GrDrawTarget. Coverage with a dst
+ // blend coeff of zero requires dual source blending support in order
+ // to properly blend partially covered pixels. This means the AA
+ // code path may not be taken. So we use a dst blend coeff of ISA. We
+ // could special case AA draws to a dst surface with known alpha=0 to
+ // use a zero dst coeff when dual source blending isn't available.
+ tempPaint.setBlendFunc(kOne_GrBlendCoeff, kISC_GrBlendCoeff);
+ }
+
+ GrContext::AutoMatrix am;
+
+ // Draw the mask into maskTexture with the path's top-left at the origin using tempPaint.
+ SkMatrix translate;
+ translate.setTranslate(-maskRect.fLeft, -maskRect.fTop);
+ am.set(context, translate);
+ context->drawPath(tempPaint, devPath, stroke);
return true;
}
+SkBitmap wrap_texture(GrTexture* texture) {
+ SkBitmap result;
+ bool dummy;
+ SkBitmap::Config config = grConfig2skConfig(texture->config(), &dummy);
+ result.setConfig(config, texture->width(), texture->height());
+ result.setPixelRef(SkNEW_ARGS(SkGrPixelRef, (texture)))->unref();
+ return result;
}
-///////////////////////////////////////////////////////////////////////////////
+};
void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath,
const SkPaint& paint, const SkMatrix* prePathMatrix,
@@ -1051,6 +955,7 @@ void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath,
if (!stroke.isHairlineStyle()) {
if (stroke.applyToPath(&tmpPath, *pathPtr)) {
pathPtr = &tmpPath;
+ pathIsMutable = true;
stroke.setFillStyle();
}
}
@@ -1060,13 +965,46 @@ void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath,
// transform the path into device space
pathPtr->transform(fContext->getMatrix(), devPathPtr);
- if (!drawWithGPUMaskFilter(fContext, *devPathPtr, stroke, paint.getMaskFilter(),
- *draw.fClip, draw.fBounder, &grPaint)) {
- SkPaint::Style style = stroke.isHairlineStyle() ? SkPaint::kStroke_Style :
- SkPaint::kFill_Style;
- drawWithMaskFilter(fContext, *devPathPtr, paint.getMaskFilter(),
- *draw.fClip, draw.fBounder, &grPaint, style);
+
+ SkRect maskRect;
+ if (paint.getMaskFilter()->canFilterMaskGPU(devPathPtr->getBounds(),
+ draw.fClip->getBounds(),
+ fContext->getMatrix(),
+ &maskRect)) {
+ SkIRect finalIRect;
+ maskRect.roundOut(&finalIRect);
+ if (draw.fClip->quickReject(finalIRect)) {
+ // clipped out
+ return;
+ }
+ if (NULL != draw.fBounder && !draw.fBounder->doIRect(finalIRect)) {
+ // nothing to draw
+ return;
+ }
+
+ GrAutoScratchTexture mask;
+
+ if (create_mask_GPU(fContext, maskRect, *devPathPtr, stroke,
+ grPaint.isAntiAlias(), &mask)) {
+ GrTexture* filtered;
+
+ if (paint.getMaskFilter()->filterMaskGPU(mask.texture(), maskRect, &filtered, true)) {
+ SkAutoTUnref<GrTexture> atu(filtered);
+
+ if (draw_mask(fContext, maskRect, &grPaint, filtered)) {
+ // This path is completely drawn
+ return;
+ }
+ }
+ }
}
+
+ // draw the mask on the CPU - this is a fallthrough path in case the
+ // GPU path fails
+ SkPaint::Style style = stroke.isHairlineStyle() ? SkPaint::kStroke_Style :
+ SkPaint::kFill_Style;
+ draw_with_mask_filter(fContext, *devPathPtr, paint.getMaskFilter(),
+ *draw.fClip, draw.fBounder, &grPaint, style);
return;
}
@@ -1437,15 +1375,6 @@ void SkGpuDevice::internalDrawBitmap(const SkBitmap& bitmap,
fContext->drawRectToRect(*grPaint, dstRect, paintRect, &m);
}
-static SkBitmap wrap_texture(GrTexture* texture) {
- SkBitmap result;
- bool dummy;
- SkBitmap::Config config = grConfig2skConfig(texture->config(), &dummy);
- result.setConfig(config, texture->width(), texture->height());
- result.setPixelRef(SkNEW_ARGS(SkGrPixelRef, (texture)))->unref();
- return result;
-}
-
static bool filter_texture(SkDevice* device, GrContext* context,
GrTexture* texture, SkImageFilter* filter,
int w, int h, SkBitmap* result) {
@@ -1608,8 +1537,6 @@ bool SkGpuDevice::filterImage(SkImageFilter* filter, const SkBitmap& src,
return false;
}
- GrPaint paint;
-
GrTexture* texture;
// We assume here that the filter will not attempt to tile the src. Otherwise, this cache lookup
// must be pushed upstack.
diff --git a/tests/BlurTest.cpp b/tests/BlurTest.cpp
index 57dc940a6d..6fb36b6217 100644
--- a/tests/BlurTest.cpp
+++ b/tests/BlurTest.cpp
@@ -105,18 +105,6 @@ static void test_blur(skiatest::Reporter* reporter) {
SkMaskFilter* filter;
filter = SkBlurMaskFilter::Create(radius, blurStyle, flags);
- SkMaskFilter::BlurInfo info;
- sk_bzero(&info, sizeof(info));
- SkMaskFilter::BlurType type = filter->asABlur(&info);
-
- REPORTER_ASSERT(reporter, type ==
- static_cast<SkMaskFilter::BlurType>(style + 1));
- REPORTER_ASSERT(reporter, info.fRadius == radius);
- REPORTER_ASSERT(reporter, info.fIgnoreTransform ==
- SkToBool(flags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag));
- REPORTER_ASSERT(reporter, info.fHighQuality ==
- SkToBool(flags & SkBlurMaskFilter::kHighQuality_BlurFlag));
-
paint.setMaskFilter(filter);
filter->unref();
diff --git a/tools/PictureRenderer.cpp b/tools/PictureRenderer.cpp
index 12a10707a7..2730a23f52 100644
--- a/tools/PictureRenderer.cpp
+++ b/tools/PictureRenderer.cpp
@@ -64,10 +64,9 @@ public:
virtual bool filter(SkPaint* paint, Type t) {
paint->setFlags(paint->getFlags() & ~fFlags[t] & SkPaint::kAllFlags);
- if (PictureRenderer::kBlur_DrawFilterFlag & fFlags[t]) {
+ if (PictureRenderer::kMaskFilter_DrawFilterFlag & fFlags[t]) {
SkMaskFilter* maskFilter = paint->getMaskFilter();
- SkMaskFilter::BlurInfo blurInfo;
- if (maskFilter && maskFilter->asABlur(&blurInfo)) {
+ if (NULL != maskFilter) {
paint->setMaskFilter(NULL);
}
}
diff --git a/tools/PictureRenderer.h b/tools/PictureRenderer.h
index cb06679a78..738b812279 100644
--- a/tools/PictureRenderer.h
+++ b/tools/PictureRenderer.h
@@ -59,13 +59,13 @@ public:
// this uses SkPaint::Flags as a base and adds additional flags
enum DrawFilterFlags {
kNone_DrawFilterFlag = 0,
- kBlur_DrawFilterFlag = 0x8000, // toggles between blur and no blur
+ kMaskFilter_DrawFilterFlag = 0x8000, // toggles on/off mask filters (e.g., blurs)
kHinting_DrawFilterFlag = 0x10000, // toggles between no hinting and normal hinting
kSlightHinting_DrawFilterFlag = 0x20000, // toggles between slight and normal hinting
kAAClip_DrawFilterFlag = 0x40000, // toggles between soft and hard clip
};
- SK_COMPILE_ASSERT(!(kBlur_DrawFilterFlag & SkPaint::kAllFlags), blur_flag_must_be_greater);
+ SK_COMPILE_ASSERT(!(kMaskFilter_DrawFilterFlag & SkPaint::kAllFlags), maskfilter_flag_must_be_greater);
SK_COMPILE_ASSERT(!(kHinting_DrawFilterFlag & SkPaint::kAllFlags),
hinting_flag_must_be_greater);
SK_COMPILE_ASSERT(!(kSlightHinting_DrawFilterFlag & SkPaint::kAllFlags),