compose and combine maskfilters

Bug: skia:
Change-Id: Id470124021d00ee25cf4ae392a4ee345ced84110
Reviewed-on: https://skia-review.googlesource.com/97760
Reviewed-by: Mike Klein <mtklein@chromium.org>
Reviewed-by: Florin Malita <fmalita@chromium.org>
Commit-Queue: Mike Reed <reed@google.com>
This commit is contained in:
Mike Reed 2018-01-24 16:34:02 -05:00 committed by Skia Commit-Bot
parent 868a68061e
commit 20dc672821
4 changed files with 365 additions and 2 deletions

View File

@ -7,16 +7,21 @@
#include "gm.h"
#include "sk_tool_utils.h"
#include "SkBlurMaskFilter.h"
#include "SkCanvas.h"
#include "SkImage.h"
#include "SkShaderMaskFilter.h"
static void draw_masked_image(SkCanvas* canvas, const SkImage* image, SkScalar x, SkScalar y,
const SkImage* mask) {
const SkImage* mask, sk_sp<SkMaskFilter> outer = nullptr) {
SkMatrix matrix = SkMatrix::MakeScale(SkIntToScalar(image->width()) / mask->width(),
SkIntToScalar(image->height() / mask->height()));
SkPaint paint;
paint.setMaskFilter(SkShaderMaskFilter::Make(mask->makeShader(&matrix)));
auto mf = SkShaderMaskFilter::Make(mask->makeShader(&matrix));
if (outer) {
mf = SkMaskFilter::MakeCompose(outer, mf);
}
paint.setMaskFilter(mf);
canvas->drawImage(image, x, y, &paint);
}
@ -49,9 +54,93 @@ DEF_SIMPLE_GM(shadermaskfilter_image, canvas, 512, 512) {
auto image = GetResourceAsImage("images/mandrill_128.png");
auto mask = GetResourceAsImage("images/color_wheel.png");
auto blurmf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle, 5);
canvas->drawImage(image, 10, 10, nullptr);
canvas->drawImage(mask, 10 + image->width() + 10.f, 10, nullptr);
draw_masked_image(canvas, image.get(), 10, 10 + image->height() + 10.f, mask.get());
draw_masked_image(canvas, image.get(), 10 + image->width() + 10.f, 10 + image->height() + 10.f,
mask.get(), blurmf);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
namespace {
enum class SkAlphaBlendMode {
kClear, // 0
kSrc, // src
kDst, // dst
kOver, // src + dst - src*dst
kIn, // src * dst
kSrcOut, // src * (1 - dst)
kDstOut, // dst * (1 - src)
kXor, // src + dst - 2*src*dst
kPlus, // src + dst
kLast = kPlus,
};
const SkBlendMode gAlphaToBlendMode[] = {
SkBlendMode::kClear, // SkAlphaBlendMode::kClear
SkBlendMode::kSrc, // SkAlphaBlendMode::kSrc
SkBlendMode::kDst, // SkAlphaBlendMode::kDst
SkBlendMode::kSrcOver, // SkAlphaBlendMode::kOver
SkBlendMode::kSrcIn, // SkAlphaBlendMode::kIn
SkBlendMode::kSrcOut, // SkAlphaBlendMode::kSrcOut
SkBlendMode::kDstOut, // SkAlphaBlendMode::kDstOut
SkBlendMode::kXor, // SkAlphaBlendMode::kXor
SkBlendMode::kPlus, // SkAlphaBlendMode::kPlus
};
}
#include "SkPictureRecorder.h"
#include "SkPath.h"
static sk_sp<SkMaskFilter> make_path_mf(const SkPath& path, unsigned alpha) {
SkPaint paint;
paint.setAntiAlias(true);
paint.setAlpha(alpha);
SkPictureRecorder recorder;
recorder.beginRecording(1000, 1000)->drawPath(path, paint);
auto shader = SkShader::MakePictureShader(recorder.finishRecordingAsPicture(),
SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
nullptr, nullptr);
return SkShaderMaskFilter::Make(shader);
}
DEF_SIMPLE_GM(combinemaskfilter, canvas, 340, 340) {
const SkRect r = { 0, 0, 100, 100 };
const SkRect r2 = r.makeOutset(1.5f, 1.5f);
SkPaint paint2;
paint2.setStyle(SkPaint::kStroke_Style);
SkPath pathA;
pathA.moveTo(r.fLeft, r.fBottom);
pathA.lineTo(r.fRight, r.fTop);
pathA.lineTo(r.fRight, r.fBottom);
auto mfA = make_path_mf(pathA, 1 * 0xFF / 3);
SkPath pathB;
pathB.moveTo(r.fLeft, r.fTop);
pathB.lineTo(r.fRight, r.fBottom);
pathB.lineTo(r.fLeft, r.fBottom);
auto mfB = make_path_mf(pathB, 2 * 0xFF / 3);
canvas->translate(10, 10);
canvas->save();
for (int i = 0; i < 9; ++i) {
SkPaint paint;
paint.setMaskFilter(SkMaskFilter::MakeCombine(mfA, mfB, gAlphaToBlendMode[i]));
canvas->drawRect(r2, paint2);
canvas->drawRect(r, paint);
canvas->translate(r.width() + 10, 0);
if ((i % 3) == 2) {
canvas->restore();
canvas->translate(0, r.height() + 10);
canvas->save();
}
}
canvas->restore();
}

View File

@ -8,6 +8,7 @@
#ifndef SkMaskFilter_DEFINED
#define SkMaskFilter_DEFINED
#include "SkBlendMode.h"
#include "SkFlattenable.h"
class SkString;
@ -19,8 +20,24 @@ class SkString;
*/
class SK_API SkMaskFilter : public SkFlattenable {
public:
/**
* Construct a maskfilter whose effect is to first apply the inner filter and then apply
* the outer filter to the result of the inner's. Returns nullptr on failure.
*/
static sk_sp<SkMaskFilter> MakeCompose(sk_sp<SkMaskFilter> outer, sk_sp<SkMaskFilter> inner);
/**
* Compose two maskfilters together using a blendmode. Returns nullptr on failure.
*/
static sk_sp<SkMaskFilter> MakeCombine(sk_sp<SkMaskFilter> dst, sk_sp<SkMaskFilter> src,
SkBlendMode);
SK_TO_STRING_PUREVIRT()
SK_DEFINE_FLATTENABLE_TYPE(SkMaskFilter)
private:
static void InitializeFlattenables();
friend class SkFlattenable;
};
#endif

View File

@ -14,6 +14,9 @@
#include "SkPath.h"
#include "SkRRect.h"
#include "SkRasterClip.h"
#include "SkReadBuffer.h"
#include "SkSafeRange.h"
#include "SkWriteBuffer.h"
#if SK_SUPPORT_GPU
#include "GrTextureProxy.h"
@ -373,3 +376,256 @@ void SkMaskFilterBase::computeFastBounds(const SkRect& src, SkRect* dst) const {
dst->set(srcM.fBounds);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
template <typename T> static inline T join(const T& a, const T& b) {
T r = a;
r.join(b);
return r;
}
class SkComposeMF : public SkMaskFilterBase {
public:
SkComposeMF(sk_sp<SkMaskFilter> outer, sk_sp<SkMaskFilter> inner)
: fOuter(std::move(outer))
, fInner(std::move(inner))
{
SkASSERT(as_MFB(fOuter)->getFormat() == SkMask::kA8_Format);
SkASSERT(as_MFB(fInner)->getFormat() == SkMask::kA8_Format);
}
bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&, SkIPoint*) const override;
void computeFastBounds(const SkRect& src, SkRect* dst) const override {
SkRect tmp;
as_MFB(fInner)->computeFastBounds(src, &tmp);
as_MFB(fOuter)->computeFastBounds(tmp, dst);
}
SkMask::Format getFormat() const override { return SkMask::kA8_Format; }
SK_TO_STRING_OVERRIDE()
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkComposeMF)
private:
sk_sp<SkMaskFilter> fOuter;
sk_sp<SkMaskFilter> fInner;
void flatten(SkWriteBuffer&) const override;
friend class SkMaskFilter;
typedef SkMaskFilterBase INHERITED;
};
bool SkComposeMF::filterMask(SkMask* dst, const SkMask& src, const SkMatrix& ctm,
SkIPoint* margin) const {
SkIPoint innerMargin;
SkMask innerMask;
if (!as_MFB(fInner)->filterMask(&innerMask, src, ctm, &innerMargin)) {
return false;
}
if (!as_MFB(fOuter)->filterMask(dst, innerMask, ctm, margin)) {
return false;
}
if (margin) {
margin->fX += innerMargin.fX;
margin->fY += innerMargin.fY;
}
sk_free(innerMask.fImage);
return true;
}
void SkComposeMF::flatten(SkWriteBuffer & buffer) const {
buffer.writeFlattenable(fOuter.get());
buffer.writeFlattenable(fInner.get());
}
sk_sp<SkFlattenable> SkComposeMF::CreateProc(SkReadBuffer& buffer) {
auto outer = buffer.readMaskFilter();
auto inner = buffer.readMaskFilter();
if (!buffer.validate(outer && inner)) {
return nullptr;
}
return SkMaskFilter::MakeCompose(std::move(outer), std::move(inner));
}
#ifndef SK_IGNORE_TO_STRING
void SkComposeMF::toString(SkString* str) const {
str->set("SkComposeMF:");
}
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////
class SkCombineMF : public SkMaskFilterBase {
public:
SkCombineMF(sk_sp<SkMaskFilter> dst, sk_sp<SkMaskFilter> src, SkBlendMode mode)
: fDst(std::move(dst))
, fSrc(std::move(src))
, fMode(mode)
{
SkASSERT(as_MFB(fSrc)->getFormat() == SkMask::kA8_Format);
SkASSERT(as_MFB(fDst)->getFormat() == SkMask::kA8_Format);
}
bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&, SkIPoint*) const override;
void computeFastBounds(const SkRect& src, SkRect* dst) const override {
SkRect srcR, dstR;
as_MFB(fSrc)->computeFastBounds(src, &srcR);
as_MFB(fDst)->computeFastBounds(src, &dstR);
*dst = join(srcR, dstR);
}
SkMask::Format getFormat() const override { return SkMask::kA8_Format; }
SK_TO_STRING_OVERRIDE()
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkCombineMF)
private:
sk_sp<SkMaskFilter> fDst;
sk_sp<SkMaskFilter> fSrc;
const SkBlendMode fMode;
void flatten(SkWriteBuffer&) const override;
friend class SkMaskFilter;
typedef SkMaskFilterBase INHERITED;
};
#include "SkSafeMath.h"
class DrawIntoMask : public SkDraw {
SkMatrix fMatrixStorage;
SkRasterClip fRCStorage;
SkIPoint fOffset;
public:
DrawIntoMask(SkMask* mask) {
int w = mask->fBounds.width();
int h = mask->fBounds.height();
size_t size = SkSafeMath::Mul(w, h);
mask->fFormat = SkMask::kA8_Format;
mask->fImage = SkMask::AllocImage(size, SkMask::kZeroInit_Alloc);
mask->fRowBytes = w;
SkAssertResult(fDst.reset(*mask));
fMatrixStorage.reset();
fMatrix = &fMatrixStorage;
fRCStorage.setRect({ 0, 0, w, h });
fRC = &fRCStorage;
fOffset.set(mask->fBounds.left(), mask->fBounds.top());
}
void drawMaskAsImage(const SkMask& m, const SkPaint& p) {
SkBitmap bm;
bm.installMaskPixels(m);
this->drawSprite(bm, m.fBounds.left() - fOffset.x(), m.fBounds.top() - fOffset.y(), p);
}
};
bool SkCombineMF::filterMask(SkMask* dst, const SkMask& src, const SkMatrix& ctm,
SkIPoint* margin) const {
SkIPoint srcP, dstP;
SkMask srcM, dstM;
if (!as_MFB(fSrc)->filterMask(&srcM, src, ctm, &srcP)) {
return false;
}
if (!as_MFB(fDst)->filterMask(&dstM, src, ctm, &dstP)) {
return false;
}
dst->fBounds = join(srcM.fBounds, dstM.fBounds);
dst->fFormat = SkMask::kA8_Format;
if (src.fImage == nullptr) {
dst->fImage = nullptr;
return true;
}
DrawIntoMask md(dst);
SkPaint p;
p.setBlendMode(SkBlendMode::kSrc);
md.drawMaskAsImage(dstM, p);
p.setBlendMode(fMode);
md.drawMaskAsImage(srcM, p);
sk_free(srcM.fImage);
sk_free(dstM.fImage);
return true;
}
void SkCombineMF::flatten(SkWriteBuffer & buffer) const {
buffer.writeFlattenable(fDst.get());
buffer.writeFlattenable(fSrc.get());
buffer.write32(static_cast<uint32_t>(fMode));
}
sk_sp<SkFlattenable> SkCombineMF::CreateProc(SkReadBuffer& buffer) {
SkSafeRange safe;
auto dst = buffer.readMaskFilter();
auto src = buffer.readMaskFilter();
SkBlendMode mode = safe.checkLE(buffer.read32(), SkBlendMode::kLastMode);
if (!buffer.validate(dst && src && safe)) {
return nullptr;
}
return SkMaskFilter::MakeCombine(std::move(dst), std::move(src), mode);
}
#ifndef SK_IGNORE_TO_STRING
void SkCombineMF::toString(SkString* str) const {
str->set("SkCombineMF:");
}
#endif
////////////////////////////////////////
sk_sp<SkMaskFilter> SkMaskFilter::MakeCompose(sk_sp<SkMaskFilter> outer, sk_sp<SkMaskFilter> inner) {
if (!outer) {
return inner;
}
if (!inner) {
return outer;
}
if (as_MFB(inner)->getFormat() != SkMask::kA8_Format ||
as_MFB(outer)->getFormat() != SkMask::kA8_Format) {
return nullptr;
}
return sk_sp<SkMaskFilter>(new SkComposeMF(std::move(outer), std::move(inner)));
}
sk_sp<SkMaskFilter> SkMaskFilter::MakeCombine(sk_sp<SkMaskFilter> dst, sk_sp<SkMaskFilter> src,
SkBlendMode mode) {
if (mode == SkBlendMode::kClear) {
// return sk_sp<SkMaskFilter>(new ClearMF);
}
if (!dst || mode == SkBlendMode::kSrc || mode == SkBlendMode::kDstATop) {
return src;
}
if (!src || mode == SkBlendMode::kDst || mode == SkBlendMode::kSrcATop) {
return dst;
}
// This step isn't really needed, but it documents that we don't need any modes after kModulate
if (mode > SkBlendMode::kModulate) {
mode = SkBlendMode::kSrcOver;
}
if (as_MFB(dst)->getFormat() != SkMask::kA8_Format ||
as_MFB(src)->getFormat() != SkMask::kA8_Format) {
return nullptr;
}
return sk_sp<SkMaskFilter>(new SkCombineMF(std::move(dst), std::move(src), mode));
}
void SkMaskFilter::InitializeFlattenables() {
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkComposeMF)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkCombineMF)
}

View File

@ -61,6 +61,7 @@
*/
void SkFlattenable::PrivateInitializer::InitEffects() {
// MaskFilter
SkMaskFilter::InitializeFlattenables();
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkEmbossMaskFilter)
SkBlurMaskFilter::InitializeFlattenables();
SkRRectsGaussianEdgeMaskFilter::InitializeFlattenables();