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:
parent
868a68061e
commit
20dc672821
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -61,6 +61,7 @@
|
||||
*/
|
||||
void SkFlattenable::PrivateInitializer::InitEffects() {
|
||||
// MaskFilter
|
||||
SkMaskFilter::InitializeFlattenables();
|
||||
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkEmbossMaskFilter)
|
||||
SkBlurMaskFilter::InitializeFlattenables();
|
||||
SkRRectsGaussianEdgeMaskFilter::InitializeFlattenables();
|
||||
|
Loading…
Reference in New Issue
Block a user