From daaafa6e81860e3dc52660ba019c336f0a43f1e7 Mon Sep 17 00:00:00 2001 From: "reed@google.com" Date: Tue, 29 Apr 2014 15:20:16 +0000 Subject: [PATCH] add asAShadowBlur for android to drawlooper BUG=skia: R=djsollen@google.com, scroggo@google.com Review URL: https://codereview.chromium.org/253633003 git-svn-id: http://skia.googlecode.com/svn/trunk@14431 2bbb7eff-a529-9590-31e7-b0007b416f81 --- include/core/SkDrawLooper.h | 21 +++ include/core/SkMaskFilter.h | 13 ++ include/effects/SkBlurDrawLooper.h | 5 +- include/effects/SkEmbossMaskFilter.h | 4 +- include/effects/SkLayerDrawLooper.h | 2 + src/core/SkDrawLooper.cpp | 4 + src/core/SkMaskFilter.cpp | 4 + src/effects/SkBlurDrawLooper.cpp | 91 ++++++++----- src/effects/SkBlurMask.cpp | 24 ++-- src/effects/SkBlurMask.h | 3 + src/effects/SkBlurMaskFilter.cpp | 26 +++- src/effects/SkEmbossMaskFilter.cpp | 6 +- src/effects/SkLayerDrawLooper.cpp | 44 +++++++ tests/BlurTest.cpp | 185 ++++++++++++++++++++++++++- 14 files changed, 375 insertions(+), 57 deletions(-) diff --git a/include/core/SkDrawLooper.h b/include/core/SkDrawLooper.h index cc42952d07..b92baccc08 100644 --- a/include/core/SkDrawLooper.h +++ b/include/core/SkDrawLooper.h @@ -10,7 +10,10 @@ #ifndef SkDrawLooper_DEFINED #define SkDrawLooper_DEFINED +#include "SkBlurTypes.h" #include "SkFlattenable.h" +#include "SkPoint.h" +#include "SkColor.h" class SkCanvas; class SkPaint; @@ -88,6 +91,24 @@ public: virtual void computeFastBounds(const SkPaint& paint, const SkRect& src, SkRect* dst) const; + struct BlurShadowRec { + SkScalar fSigma; + SkVector fOffset; + SkColor fColor; + SkBlurStyle fStyle; + SkBlurQuality fQuality; + }; + /** + * If this looper can be interpreted as having two layers, such that + * 1. The first layer (bottom most) just has a blur and translate + * 2. The second layer has no modifications to either paint or canvas + * 3. No other layers. + * then return true, and if not null, fill out the BlurShadowRec). + * + * If any of the above are not met, return false and ignore the BlurShadowRec parameter. + */ + virtual bool asABlurShadow(BlurShadowRec*) const; + SK_TO_STRING_PUREVIRT() SK_DEFINE_FLATTENABLE_TYPE(SkDrawLooper) diff --git a/include/core/SkMaskFilter.h b/include/core/SkMaskFilter.h index 3113c93442..8051e7d831 100644 --- a/include/core/SkMaskFilter.h +++ b/include/core/SkMaskFilter.h @@ -10,6 +10,7 @@ #ifndef SkMaskFilter_DEFINED #define SkMaskFilter_DEFINED +#include "SkBlurTypes.h" #include "SkFlattenable.h" #include "SkMask.h" #include "SkPaint.h" @@ -138,6 +139,18 @@ public: */ virtual void computeFastBounds(const SkRect& src, SkRect* dest) const; + struct BlurRec { + SkScalar fSigma; + SkBlurStyle fStyle; + SkBlurQuality fQuality; + }; + /** + * If this filter can be represented by a BlurRec, return true and (if not null) fill in the + * provided BlurRec parameter. If this effect cannot be represented as a BlurRec, return false + * and ignore the BlurRec parameter. + */ + virtual bool asABlur(BlurRec*) const; + SK_TO_STRING_PUREVIRT() SK_DEFINE_FLATTENABLE_TYPE(SkMaskFilter) diff --git a/include/effects/SkBlurDrawLooper.h b/include/effects/SkBlurDrawLooper.h index fe945c3801..75ed80e679 100644 --- a/include/effects/SkBlurDrawLooper.h +++ b/include/effects/SkBlurDrawLooper.h @@ -61,10 +61,12 @@ protected: SkBlurDrawLooper(SkReadBuffer&); virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE; + virtual bool asABlurShadow(BlurShadowRec*) const SK_OVERRIDE; + private: SkMaskFilter* fBlur; SkColorFilter* fColorFilter; - SkScalar fDx, fDy; + SkScalar fDx, fDy, fSigma; SkColor fBlurColor; uint32_t fBlurFlags; @@ -86,6 +88,7 @@ private: }; void init(SkScalar sigma, SkScalar dx, SkScalar dy, SkColor color, uint32_t flags); + void initEffects(); typedef SkDrawLooper INHERITED; }; diff --git a/include/effects/SkEmbossMaskFilter.h b/include/effects/SkEmbossMaskFilter.h index eb8f8117a4..65dbbe7a59 100644 --- a/include/effects/SkEmbossMaskFilter.h +++ b/include/effects/SkEmbossMaskFilter.h @@ -23,9 +23,7 @@ public: uint8_t fSpecular; // exponent, 4.4 right now }; - static SkEmbossMaskFilter* Create(SkScalar blurSigma, const Light& light) { - return SkNEW_ARGS(SkEmbossMaskFilter, (blurSigma, light)); - } + static SkEmbossMaskFilter* Create(SkScalar blurSigma, const Light& light); // overrides from SkMaskFilter // This method is not exported to java. diff --git a/include/effects/SkLayerDrawLooper.h b/include/effects/SkLayerDrawLooper.h index fe660a8aae..ac56e288dc 100644 --- a/include/effects/SkLayerDrawLooper.h +++ b/include/effects/SkLayerDrawLooper.h @@ -77,6 +77,8 @@ public: virtual size_t contextSize() const SK_OVERRIDE { return sizeof(LayerDrawLooperContext); } + virtual bool asABlurShadow(BlurShadowRec* rec) const SK_OVERRIDE; + SK_TO_STRING_OVERRIDE() /// Implements Flattenable. diff --git a/src/core/SkDrawLooper.cpp b/src/core/SkDrawLooper.cpp index c620cd08da..d18d12710a 100644 --- a/src/core/SkDrawLooper.cpp +++ b/src/core/SkDrawLooper.cpp @@ -59,3 +59,7 @@ void SkDrawLooper::computeFastBounds(const SkPaint& paint, const SkRect& src, } } } + +bool SkDrawLooper::asABlurShadow(BlurShadowRec*) const { + return false; +} diff --git a/src/core/SkMaskFilter.cpp b/src/core/SkMaskFilter.cpp index 9b023d0d65..8b9792c2c4 100644 --- a/src/core/SkMaskFilter.cpp +++ b/src/core/SkMaskFilter.cpp @@ -26,6 +26,10 @@ bool SkMaskFilter::filterMask(SkMask*, const SkMask&, const SkMatrix&, return false; } +bool SkMaskFilter::asABlur(BlurRec*) const { + return false; +} + static void extractMaskSubset(const SkMask& src, SkMask* dst) { SkASSERT(src.fBounds.contains(dst->fBounds)); diff --git a/src/effects/SkBlurDrawLooper.cpp b/src/effects/SkBlurDrawLooper.cpp index 03e635be12..5af02db8ee 100644 --- a/src/effects/SkBlurDrawLooper.cpp +++ b/src/effects/SkBlurDrawLooper.cpp @@ -1,10 +1,10 @@ - /* * Copyright 2011 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ + #include "SkBlurDrawLooper.h" #include "SkBlurMask.h" // just for SkBlurMask::ConvertRadiusToSigma #include "SkBlurMaskFilter.h" @@ -29,32 +29,27 @@ SkBlurDrawLooper::SkBlurDrawLooper(SkColor color, SkScalar sigma, this->init(sigma, dx, dy, color, flags); } -void SkBlurDrawLooper::init(SkScalar sigma, SkScalar dx, SkScalar dy, - SkColor color, uint32_t flags) { - fDx = dx; - fDy = dy; - fBlurColor = color; - fBlurFlags = flags; - - SkASSERT(flags <= kAll_BlurFlag); - if (sigma > 0) { - uint32_t blurFlags = flags & kIgnoreTransform_BlurFlag ? - SkBlurMaskFilter::kIgnoreTransform_BlurFlag : - SkBlurMaskFilter::kNone_BlurFlag; - - blurFlags |= flags & kHighQuality_BlurFlag ? - SkBlurMaskFilter::kHighQuality_BlurFlag : - SkBlurMaskFilter::kNone_BlurFlag; - - fBlur = SkBlurMaskFilter::Create(kNormal_SkBlurStyle, sigma, blurFlags); +// only call from constructor +void SkBlurDrawLooper::initEffects() { + SkASSERT(fBlurFlags <= kAll_BlurFlag); + if (fSigma > 0) { + uint32_t flags = fBlurFlags & kIgnoreTransform_BlurFlag ? + SkBlurMaskFilter::kIgnoreTransform_BlurFlag : + SkBlurMaskFilter::kNone_BlurFlag; + + flags |= fBlurFlags & kHighQuality_BlurFlag ? + SkBlurMaskFilter::kHighQuality_BlurFlag : + SkBlurMaskFilter::kNone_BlurFlag; + + fBlur = SkBlurMaskFilter::Create(kNormal_SkBlurStyle, fSigma, flags); } else { fBlur = NULL; } - - if (flags & kOverrideColor_BlurFlag) { + + if (fBlurFlags & kOverrideColor_BlurFlag) { // Set alpha to 1 for the override since transparency will already // be baked into the blurred mask. - SkColor opaqueColor = SkColorSetA(color, 255); + SkColor opaqueColor = SkColorSetA(fBlurColor, 255); //The SrcIn xfer mode will multiply 'color' by the incoming alpha fColorFilter = SkColorFilter::CreateModeFilter(opaqueColor, SkXfermode::kSrcIn_Mode); @@ -63,15 +58,35 @@ void SkBlurDrawLooper::init(SkScalar sigma, SkScalar dx, SkScalar dy, } } -SkBlurDrawLooper::SkBlurDrawLooper(SkReadBuffer& buffer) -: INHERITED(buffer) { +void SkBlurDrawLooper::init(SkScalar sigma, SkScalar dx, SkScalar dy, + SkColor color, uint32_t flags) { + fSigma = sigma; + fDx = dx; + fDy = dy; + fBlurColor = color; + fBlurFlags = flags; + this->initEffects(); +} + +SkBlurDrawLooper::SkBlurDrawLooper(SkReadBuffer& buffer) : INHERITED(buffer) { + + fSigma = buffer.readScalar(); fDx = buffer.readScalar(); fDy = buffer.readScalar(); fBlurColor = buffer.readColor(); - fBlur = buffer.readMaskFilter(); - fColorFilter = buffer.readColorFilter(); fBlurFlags = buffer.readUInt() & kAll_BlurFlag; + + this->initEffects(); +} + +void SkBlurDrawLooper::flatten(SkWriteBuffer& buffer) const { + this->INHERITED::flatten(buffer); + buffer.writeScalar(fSigma); + buffer.writeScalar(fDx); + buffer.writeScalar(fDy); + buffer.writeColor(fBlurColor); + buffer.write32(fBlurFlags); } SkBlurDrawLooper::~SkBlurDrawLooper() { @@ -79,16 +94,24 @@ SkBlurDrawLooper::~SkBlurDrawLooper() { SkSafeUnref(fColorFilter); } -void SkBlurDrawLooper::flatten(SkWriteBuffer& buffer) const { - this->INHERITED::flatten(buffer); - buffer.writeScalar(fDx); - buffer.writeScalar(fDy); - buffer.writeColor(fBlurColor); - buffer.writeFlattenable(fBlur); - buffer.writeFlattenable(fColorFilter); - buffer.writeUInt(fBlurFlags); +bool SkBlurDrawLooper::asABlurShadow(BlurShadowRec* rec) const { + if (fSigma <= 0 || (fBlurFlags & fBlurFlags & kIgnoreTransform_BlurFlag)) { + return false; + } + + if (rec) { + rec->fSigma = fSigma; + rec->fColor = fBlurColor; + rec->fOffset.set(fDx, fDy); + rec->fStyle = kNormal_SkBlurStyle; + rec->fQuality = (fBlurFlags & kHighQuality_BlurFlag) ? + kHigh_SkBlurQuality : kLow_SkBlurQuality; + } + return true; } +//////////////////////////////////////////////////////////////////////////////////////// + SkDrawLooper::Context* SkBlurDrawLooper::createContext(SkCanvas*, void* storage) const { return SkNEW_PLACEMENT_ARGS(storage, BlurDrawLooperContext, (this)); } diff --git a/src/effects/SkBlurMask.cpp b/src/effects/SkBlurMask.cpp index c04a6f89f8..bf50845ab6 100644 --- a/src/effects/SkBlurMask.cpp +++ b/src/effects/SkBlurMask.cpp @@ -13,17 +13,21 @@ #include "SkEndian.h" -SkScalar SkBlurMask::ConvertRadiusToSigma(SkScalar 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 = 0.57735f; +// 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 = 0.57735f; - return radius ? kBLUR_SIGMA_SCALE * radius + 0.5f : 0.0f; +SkScalar SkBlurMask::ConvertRadiusToSigma(SkScalar radius) { + return radius > 0 ? kBLUR_SIGMA_SCALE * radius + 0.5f : 0.0f; +} + +SkScalar SkBlurMask::ConvertSigmaToRadius(SkScalar sigma) { + return sigma > 0.5f ? (sigma - 0.5f) / kBLUR_SIGMA_SCALE : 0.0f; } #define UNROLL_SEPARABLE_LOOPS diff --git a/src/effects/SkBlurMask.h b/src/effects/SkBlurMask.h index 39adb96f9a..71f60d91fa 100644 --- a/src/effects/SkBlurMask.h +++ b/src/effects/SkBlurMask.h @@ -39,7 +39,10 @@ public: static bool BlurGroundTruth(SkScalar sigma, SkMask* dst, const SkMask& src, SkBlurStyle, SkIPoint* margin = NULL); + // If radius > 0, return the corresponding sigma, else return 0 static SkScalar ConvertRadiusToSigma(SkScalar radius); + // If sigma > 0.5, return the corresponding radius, else return 0 + static SkScalar ConvertSigmaToRadius(SkScalar sigma); /* Helper functions for analytic rectangle blurs */ diff --git a/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp index 2bed2fe8d6..5dffd6fd1f 100644 --- a/src/effects/SkBlurMaskFilter.cpp +++ b/src/effects/SkBlurMaskFilter.cpp @@ -59,6 +59,7 @@ public: #endif virtual void computeFastBounds(const SkRect&, SkRect*) const SK_OVERRIDE; + virtual bool asABlur(BlurRec*) const SK_OVERRIDE; SK_TO_STRING_OVERRIDE() SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurMaskFilterImpl) @@ -87,6 +88,11 @@ private: SkBlurStyle fBlurStyle; uint32_t fBlurFlags; + SkBlurQuality getQuality() const { + return (fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ? + kHigh_SkBlurQuality : kLow_SkBlurQuality; + } + SkBlurMaskFilterImpl(SkReadBuffer&); virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE; @@ -145,16 +151,24 @@ SkMask::Format SkBlurMaskFilterImpl::getFormat() const { return SkMask::kA8_Format; } +bool SkBlurMaskFilterImpl::asABlur(BlurRec* rec) const { + if (fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag) { + return false; + } + + if (rec) { + rec->fSigma = fSigma; + rec->fStyle = fBlurStyle; + rec->fQuality = this->getQuality(); + } + return true; +} + bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src, const SkMatrix& matrix, SkIPoint* margin) const{ SkScalar sigma = this->computeXformedSigma(matrix); - - SkBlurQuality blurQuality = - (fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ? - kHigh_SkBlurQuality : kLow_SkBlurQuality; - - return SkBlurMask::BoxBlur(dst, src, sigma, fBlurStyle, blurQuality, margin); + return SkBlurMask::BoxBlur(dst, src, sigma, fBlurStyle, this->getQuality(), margin); } bool SkBlurMaskFilterImpl::filterRectMask(SkMask* dst, const SkRect& r, diff --git a/src/effects/SkEmbossMaskFilter.cpp b/src/effects/SkEmbossMaskFilter.cpp index 9bf5025e70..cdd55fcc5b 100644 --- a/src/effects/SkEmbossMaskFilter.cpp +++ b/src/effects/SkEmbossMaskFilter.cpp @@ -1,4 +1,3 @@ - /* * Copyright 2006 The Android Open Source Project * @@ -6,7 +5,6 @@ * found in the LICENSE file. */ - #include "SkEmbossMaskFilter.h" #include "SkBlurMaskFilter.h" #include "SkBlurMask.h" @@ -15,6 +13,10 @@ #include "SkWriteBuffer.h" #include "SkString.h" +SkEmbossMaskFilter* SkEmbossMaskFilter::Create(SkScalar blurSigma, const Light& light) { + return SkNEW_ARGS(SkEmbossMaskFilter, (blurSigma, light)); +} + static inline int pin2byte(int n) { if (n < 0) { n = 0; diff --git a/src/effects/SkLayerDrawLooper.cpp b/src/effects/SkLayerDrawLooper.cpp index fa590d2d3f..aed2c9bbd4 100644 --- a/src/effects/SkLayerDrawLooper.cpp +++ b/src/effects/SkLayerDrawLooper.cpp @@ -153,6 +153,50 @@ bool SkLayerDrawLooper::LayerDrawLooperContext::next(SkCanvas* canvas, return true; } +bool SkLayerDrawLooper::asABlurShadow(BlurShadowRec* bsRec) const { + if (fCount != 2) { + return false; + } + const Rec* rec = fRecs; + + // bottom layer needs to be just blur(maskfilter) + if ((rec->fInfo.fPaintBits & ~kMaskFilter_Bit)) { + return false; + } + if (SkXfermode::kSrc_Mode != rec->fInfo.fColorMode) { + return false; + } + const SkMaskFilter* mf = rec->fPaint.getMaskFilter(); + if (NULL == mf) { + return false; + } + SkMaskFilter::BlurRec maskBlur; + if (!mf->asABlur(&maskBlur)) { + return false; + } + + rec = rec->fNext; + // top layer needs to be "plain" + if (rec->fInfo.fPaintBits) { + return false; + } + if (SkXfermode::kDst_Mode != rec->fInfo.fColorMode) { + return false; + } + if (!rec->fInfo.fOffset.equals(0, 0)) { + return false; + } + + if (bsRec) { + bsRec->fSigma = maskBlur.fSigma; + bsRec->fOffset = fRecs->fInfo.fOffset; + bsRec->fColor = fRecs->fPaint.getColor(); + bsRec->fStyle = maskBlur.fStyle; + bsRec->fQuality = maskBlur.fQuality; + } + return true; +} + /////////////////////////////////////////////////////////////////////////////// void SkLayerDrawLooper::flatten(SkWriteBuffer& buffer) const { diff --git a/tests/BlurTest.cpp b/tests/BlurTest.cpp index 028b509b51..c09a4ee1c6 100644 --- a/tests/BlurTest.cpp +++ b/tests/BlurTest.cpp @@ -1,12 +1,15 @@ - /* * Copyright 2011 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ + #include "SkBlurMask.h" #include "SkBlurMaskFilter.h" +#include "SkBlurDrawLooper.h" +#include "SkLayerDrawLooper.h" +#include "SkEmbossMaskFilter.h" #include "SkCanvas.h" #include "SkMath.h" #include "SkPaint.h" @@ -380,7 +383,187 @@ static void test_sigma_range(skiatest::Reporter* reporter, GrContextFactory* fac } } +/////////////////////////////////////////////////////////////////////////////////////////// + +static SkBlurQuality blurMaskFilterFlags_as_quality(uint32_t blurMaskFilterFlags) { + return (blurMaskFilterFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ? + kHigh_SkBlurQuality : kLow_SkBlurQuality; +} + +static uint32_t blurMaskFilterFlags_to_blurDrawLooperFlags(uint32_t bmf) { + const struct { + uint32_t fBlurMaskFilterFlag; + uint32_t fBlurDrawLooperFlag; + } pairs[] = { + { SkBlurMaskFilter::kIgnoreTransform_BlurFlag, SkBlurDrawLooper::kIgnoreTransform_BlurFlag }, + { SkBlurMaskFilter::kHighQuality_BlurFlag, SkBlurDrawLooper::kHighQuality_BlurFlag }, + }; + + uint32_t bdl = 0; + for (size_t i = 0; i < SK_ARRAY_COUNT(pairs); ++i) { + if (bmf & pairs[i].fBlurMaskFilterFlag) { + bdl |= pairs[i].fBlurDrawLooperFlag; + } + } + return bdl; +} + +static void test_blurDrawLooper(skiatest::Reporter* reporter, SkScalar sigma, + SkBlurStyle style, uint32_t blurMaskFilterFlags) { + if (kNormal_SkBlurStyle != style) { + return; // blurdrawlooper only supports normal + } + + const SkColor color = 0xFF335577; + const SkScalar dx = 10; + const SkScalar dy = -5; + const SkBlurQuality quality = blurMaskFilterFlags_as_quality(blurMaskFilterFlags); + uint32_t flags = blurMaskFilterFlags_to_blurDrawLooperFlags(blurMaskFilterFlags); + + SkAutoTUnref lp(SkBlurDrawLooper::Create(color, sigma, dx, dy, flags)); + + const bool expectSuccess = sigma > 0 && + 0 == (flags & SkBlurDrawLooper::kIgnoreTransform_BlurFlag); + + if (NULL == lp.get()) { + REPORTER_ASSERT(reporter, sigma <= 0); + } else { + SkDrawLooper::BlurShadowRec rec; + bool success = lp->asABlurShadow(&rec); + REPORTER_ASSERT(reporter, success == expectSuccess); + if (success) { + REPORTER_ASSERT(reporter, rec.fSigma == sigma); + REPORTER_ASSERT(reporter, rec.fOffset.x() == dx); + REPORTER_ASSERT(reporter, rec.fOffset.y() == dy); + REPORTER_ASSERT(reporter, rec.fColor == color); + REPORTER_ASSERT(reporter, rec.fStyle == style); + REPORTER_ASSERT(reporter, rec.fQuality == quality); + } + } +} + +static void test_delete_looper(skiatest::Reporter* reporter, SkDrawLooper* lp, SkScalar sigma, + SkBlurStyle style, SkBlurQuality quality, bool expectSuccess) { + SkDrawLooper::BlurShadowRec rec; + bool success = lp->asABlurShadow(&rec); + REPORTER_ASSERT(reporter, success == expectSuccess); + if (success != expectSuccess) { + lp->asABlurShadow(&rec); + } + if (success) { + REPORTER_ASSERT(reporter, rec.fSigma == sigma); + REPORTER_ASSERT(reporter, rec.fStyle == style); + REPORTER_ASSERT(reporter, rec.fQuality == quality); + } + lp->unref(); +} + +static void make_noop_layer(SkLayerDrawLooper::Builder* builder) { + SkLayerDrawLooper::LayerInfo info; + + info.fPaintBits = 0; + info.fColorMode = SkXfermode::kDst_Mode; + builder->addLayer(info); +} + +static void make_blur_layer(SkLayerDrawLooper::Builder* builder, SkMaskFilter* mf) { + SkLayerDrawLooper::LayerInfo info; + + info.fPaintBits = SkLayerDrawLooper::kMaskFilter_Bit; + info.fColorMode = SkXfermode::kSrc_Mode; + SkPaint* paint = builder->addLayer(info); + paint->setMaskFilter(mf); +} + +static void test_layerDrawLooper(skiatest::Reporter* reporter, SkMaskFilter* mf, SkScalar sigma, + SkBlurStyle style, SkBlurQuality quality, bool expectSuccess) { + + SkLayerDrawLooper::LayerInfo info; + SkLayerDrawLooper::Builder builder; + + // 1 layer is too few + make_noop_layer(&builder); + test_delete_looper(reporter, builder.detachLooper(), sigma, style, quality, false); + + // 2 layers is good, but need blur + make_noop_layer(&builder); + make_noop_layer(&builder); + test_delete_looper(reporter, builder.detachLooper(), sigma, style, quality, false); + + // 2 layers is just right + make_noop_layer(&builder); + make_blur_layer(&builder, mf); + test_delete_looper(reporter, builder.detachLooper(), sigma, style, quality, expectSuccess); + + // 3 layers is too many + make_noop_layer(&builder); + make_blur_layer(&builder, mf); + make_noop_layer(&builder); + test_delete_looper(reporter, builder.detachLooper(), sigma, style, quality, false); +} + +static void test_asABlur(skiatest::Reporter* reporter) { + const SkBlurStyle styles[] = { + kNormal_SkBlurStyle, kSolid_SkBlurStyle, kOuter_SkBlurStyle, kInner_SkBlurStyle + }; + const SkScalar sigmas[] = { + // values <= 0 should not success for a blur + -1, 0, 0.5f, 2 + }; + + // Test asABlur for SkBlurMaskFilter + // + for (size_t i = 0; i < SK_ARRAY_COUNT(styles); ++i) { + const SkBlurStyle style = (SkBlurStyle)styles[i]; + for (size_t j = 0; j < SK_ARRAY_COUNT(sigmas); ++j) { + const SkScalar sigma = sigmas[j]; + for (int flags = 0; flags <= SkBlurMaskFilter::kAll_BlurFlag; ++flags) { + const SkBlurQuality quality = blurMaskFilterFlags_as_quality(flags); + + SkAutoTUnref mf(SkBlurMaskFilter::Create(style, sigma, flags)); + if (NULL == mf.get()) { + REPORTER_ASSERT(reporter, sigma <= 0); + } else { + REPORTER_ASSERT(reporter, sigma > 0); + SkMaskFilter::BlurRec rec; + bool success = mf->asABlur(&rec); + if (flags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag) { + REPORTER_ASSERT(reporter, !success); + } else { + REPORTER_ASSERT(reporter, success); + REPORTER_ASSERT(reporter, rec.fSigma == sigma); + REPORTER_ASSERT(reporter, rec.fStyle == style); + REPORTER_ASSERT(reporter, rec.fQuality == quality); + } + test_layerDrawLooper(reporter, mf, sigma, style, quality, success); + } + test_blurDrawLooper(reporter, sigma, style, flags); + } + } + } + + // Test asABlur for SkEmbossMaskFilter -- should never succeed + // + { + SkEmbossMaskFilter::Light light = { + { 1, 1, 1 }, 0, 127, 127 + }; + for (size_t j = 0; j < SK_ARRAY_COUNT(sigmas); ++j) { + const SkScalar sigma = sigmas[j]; + SkAutoTUnref mf(SkEmbossMaskFilter::Create(sigma, light)); + if (mf.get()) { + SkMaskFilter::BlurRec rec; + bool success = mf->asABlur(&rec); + REPORTER_ASSERT(reporter, !success); + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////// + DEF_GPUTEST(Blur, reporter, factory) { test_blur_drawing(reporter); test_sigma_range(reporter, factory); + test_asABlur(reporter); }