diff options
author | Mike Reed <reed@google.com> | 2018-01-26 11:42:38 -0500 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2018-01-26 17:03:58 +0000 |
commit | 1bd556a1776c6c0b2a7b940516bc53f329516dc7 (patch) | |
tree | 30a25a1cc739c2167a6b31f3fb8903470833921d | |
parent | f9a5e62981c9c1ddd838b5e466c991fae82233a4 (diff) |
use coverage modes instead of blend modes
Bug: skia:
Change-Id: Ib3aa0137644358173ea4087693f33dbc2118c6d2
Reviewed-on: https://skia-review.googlesource.com/99661
Commit-Queue: Mike Reed <reed@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
-rw-r--r-- | gm/shadermaskfilter.cpp | 99 | ||||
-rw-r--r-- | include/core/SkCoverageMode.h | 30 | ||||
-rw-r--r-- | include/core/SkMaskFilter.h | 8 | ||||
-rw-r--r-- | src/core/SkCoverageModePriv.h | 49 | ||||
-rw-r--r-- | src/core/SkMaskFilter.cpp | 70 |
5 files changed, 173 insertions, 83 deletions
diff --git a/gm/shadermaskfilter.cpp b/gm/shadermaskfilter.cpp index 3545e557e5..8011b45d41 100644 --- a/gm/shadermaskfilter.cpp +++ b/gm/shadermaskfilter.cpp @@ -66,36 +66,9 @@ DEF_SIMPLE_GM(shadermaskfilter_image, canvas, 512, 512) { /////////////////////////////////////////////////////////////////////////////////////////////////// -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); @@ -109,38 +82,64 @@ static sk_sp<SkMaskFilter> make_path_mf(const SkPath& path, unsigned alpha) { return SkShaderMaskFilter::Make(shader); } -DEF_SIMPLE_GM(combinemaskfilter, canvas, 340, 340) { +typedef void (*MakePathsProc)(const SkRect&, SkPath*, SkPath*); + +const char* gCoverageName[] = { + "union", "sect", "diff", "rev-diff", "xor" +}; + +DEF_SIMPLE_GM(combinemaskfilter, canvas, 565, 250) { const SkRect r = { 0, 0, 100, 100 }; + SkPaint paint; + paint.setColor(SK_ColorRED); + + SkPaint labelP; + labelP.setAntiAlias(true); + labelP.setTextSize(20); + labelP.setTextAlign(SkPaint::kCenter_Align); + 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); + auto proc0 = [](const SkRect& r, SkPath* pathA, SkPath* pathB) { + pathA->moveTo(r.fLeft, r.fBottom); + pathA->lineTo(r.fRight, r.fTop); + pathA->lineTo(r.fRight, r.fBottom); + pathB->moveTo(r.fLeft, r.fTop); + pathB->lineTo(r.fRight, r.fBottom); + pathB->lineTo(r.fLeft, r.fBottom); + }; + auto proc1 = [](const SkRect& r, SkPath* pathA, SkPath* pathB) { + pathA->addCircle(r.width()*0.25f, r.height()*0.25f, r.width()*0.5f); + pathB->addCircle(r.width()*0.75f, r.height()*0.75f, r.width()*0.5f); + }; + MakePathsProc procs[] = { proc0, proc1 }; + + sk_sp<SkMaskFilter> mfA[2], mfB[2]; + for (int i = 0; i < 2; ++i) { + SkPath a, b; + procs[i](r, &a, &b); + mfA[i] = make_path_mf(a, 1 * 0xFF / 3); + mfB[i] = make_path_mf(b, 2 * 0xFF / 3); + } - canvas->translate(10, 10); + canvas->translate(10, 10 + 20); 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(); + for (int i = 0; i < 5; ++i) { + canvas->drawText(gCoverageName[i], strlen(gCoverageName[i]), r.width()*0.5f, -10, labelP); + + SkCoverageMode mode = static_cast<SkCoverageMode>(i); + canvas->save(); + for (int j = 0; j < 2; ++j) { + paint.setMaskFilter(SkMaskFilter::MakeCombine(mfA[j], mfB[j], mode)); + canvas->drawRect(r2, paint2); + canvas->drawRect(r, paint); canvas->translate(0, r.height() + 10); - canvas->save(); } + canvas->restore(); + canvas->translate(r.width() + 10, 0); } canvas->restore(); } diff --git a/include/core/SkCoverageMode.h b/include/core/SkCoverageMode.h new file mode 100644 index 0000000000..96863be000 --- /dev/null +++ b/include/core/SkCoverageMode.h @@ -0,0 +1,30 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkCoverageMode_DEFINED +#define SkCoverageMode_DEFINED + +#include "SkTypes.h" + +/** + * Describes geometric operations (ala SkRegion::Op) that can be applied to coverage bytes. + * These can be thought of as variants of porter-duff (SkBlendMode) modes, but only applied + * to the alpha channel. + * + * See SkMaskFilter for ways to use these when combining two different masks. + */ +enum class SkCoverageMode { + kUnion, // A ∪ B A+B-A*B + kIntersect, // A ∪ B A*B + kDifference, // A - B A*(1-B) + kReverseDifference, // B - A B*(1-A) + kXor, // A ⊕ B A+B-2*A*B + + kLast = kXor +}; + +#endif diff --git a/include/core/SkMaskFilter.h b/include/core/SkMaskFilter.h index e8a4e38863..c68b2ac911 100644 --- a/include/core/SkMaskFilter.h +++ b/include/core/SkMaskFilter.h @@ -8,7 +8,7 @@ #ifndef SkMaskFilter_DEFINED #define SkMaskFilter_DEFINED -#include "SkBlendMode.h" +#include "SkCoverageMode.h" #include "SkFlattenable.h" class SkString; @@ -27,10 +27,10 @@ public: static sk_sp<SkMaskFilter> MakeCompose(sk_sp<SkMaskFilter> outer, sk_sp<SkMaskFilter> inner); /** - * Compose two maskfilters together using a blendmode. Returns nullptr on failure. + * Compose two maskfilters together using a coverage mode. Returns nullptr on failure. */ - static sk_sp<SkMaskFilter> MakeCombine(sk_sp<SkMaskFilter> dst, sk_sp<SkMaskFilter> src, - SkBlendMode); + static sk_sp<SkMaskFilter> MakeCombine(sk_sp<SkMaskFilter> filterA, sk_sp<SkMaskFilter> filterB, + SkCoverageMode mode); SK_TO_STRING_PUREVIRT() SK_DEFINE_FLATTENABLE_TYPE(SkMaskFilter) diff --git a/src/core/SkCoverageModePriv.h b/src/core/SkCoverageModePriv.h new file mode 100644 index 0000000000..d019e60bda --- /dev/null +++ b/src/core/SkCoverageModePriv.h @@ -0,0 +1,49 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkCoverageModePriv_DEFINED +#define SkCoverageModePriv_DEFINED + +#include "SkBlendMode.h" +#include "SkCoverageMode.h" + +const SkBlendMode gUncorrelatedCoverageToBlend[] = { + SkBlendMode::kSrcOver, // or DstOver + SkBlendMode::kSrcIn, // or kDstIn + SkBlendMode::kSrcOut, + SkBlendMode::kDstOut, + SkBlendMode::kXor, +}; + +#if 0 +// Experimental idea to extend to overlap types + +Master calculation = X(S,D) + Y(S,D) + Z(S,D) + +enum class SkCoverageOverlap { + // X Y Z + kUncorrelated, // S*D S*(1-D) D*(1-S) + kConjoint, // min(S,D) max(S-D,0) max(D-S,0) + kDisjoint, // max(S+D-1,0) min(S,1-D) min(D,1-S) + + kLast = kDisjoint +}; + +// The coverage modes each have a set of coefficients to be applied to the general equation (above) +// +// e.g. +// kXor+conjoint = max(S-D,0) + max(D-S,0) ==> abs(D-S) +// +kUnion, // 1,1,1 +kIntersect, // 1,0,0 +kDifference, // 0,1,0 +kReverseDifference, // 0,0,1 +kXor, // 0,1,1 + +#endif + +#endif diff --git a/src/core/SkMaskFilter.cpp b/src/core/SkMaskFilter.cpp index d895e35981..ae7d2e291a 100644 --- a/src/core/SkMaskFilter.cpp +++ b/src/core/SkMaskFilter.cpp @@ -10,6 +10,7 @@ #include "SkAutoMalloc.h" #include "SkBlitter.h" #include "SkCachedData.h" +#include "SkCoverageModePriv.h" #include "SkDraw.h" #include "SkPath.h" #include "SkRRect.h" @@ -384,6 +385,10 @@ template <typename T> static inline T join(const T& a, const T& b) { r.join(b); return r; } +template <typename T> static inline T sect(const T& a, const T& b) { + T r = a; + return r.intersect(b) ? r : T::MakeEmpty(); +} class SkComposeMF : public SkMaskFilterBase { public: @@ -461,8 +466,7 @@ void SkComposeMF::toString(SkString* str) const { class SkCombineMF : public SkMaskFilterBase { public: - - SkCombineMF(sk_sp<SkMaskFilter> dst, sk_sp<SkMaskFilter> src, SkBlendMode mode) + SkCombineMF(sk_sp<SkMaskFilter> dst, sk_sp<SkMaskFilter> src, SkCoverageMode mode) : fDst(std::move(dst)) , fSrc(std::move(src)) , fMode(mode) @@ -481,13 +485,14 @@ public: } 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; + SkCoverageMode fMode; void flatten(SkWriteBuffer&) const override; @@ -499,11 +504,8 @@ private: #include "SkSafeMath.h" class DrawIntoMask : public SkDraw { - SkMatrix fMatrixStorage; - SkRasterClip fRCStorage; - SkIPoint fOffset; - public: + // we ignore the offset of the mask->fBounds DrawIntoMask(SkMask* mask) { int w = mask->fBounds.width(); int h = mask->fBounds.height(); @@ -519,17 +521,31 @@ public: 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); + void drawAsBitmap(const SkMask& m, const SkPaint& p) { + SkBitmap b; + b.installMaskPixels(m); + this->drawSprite(b, m.fBounds.fLeft, m.fBounds.fTop, p); } + +private: + SkMatrix fMatrixStorage; + SkRasterClip fRCStorage; }; +static SkIRect join(const SkIRect& src, const SkIRect& dst, SkCoverageMode mode) { + switch (mode) { + case SkCoverageMode::kUnion: return join(src, dst); + case SkCoverageMode::kIntersect: return sect(src, dst); + case SkCoverageMode::kDifference: return src; + case SkCoverageMode::kReverseDifference: return dst; + case SkCoverageMode::kXor: return join(src, dst); + } + // not reached + return { 0, 0, 0, 0 }; +} + bool SkCombineMF::filterMask(SkMask* dst, const SkMask& src, const SkMatrix& ctm, SkIPoint* margin) const { SkIPoint srcP, dstP; @@ -542,7 +558,7 @@ bool SkCombineMF::filterMask(SkMask* dst, const SkMask& src, const SkMatrix& ctm return false; } - dst->fBounds = join(srcM.fBounds, dstM.fBounds); + dst->fBounds = join(srcM.fBounds, dstM.fBounds, fMode); dst->fFormat = SkMask::kA8_Format; if (src.fImage == nullptr) { dst->fImage = nullptr; @@ -553,9 +569,11 @@ bool SkCombineMF::filterMask(SkMask* dst, const SkMask& src, const SkMatrix& ctm SkPaint p; p.setBlendMode(SkBlendMode::kSrc); - md.drawMaskAsImage(dstM, p); - p.setBlendMode(fMode); - md.drawMaskAsImage(srcM, p); + dstM.fBounds.offset(-dst->fBounds.fLeft, -dst->fBounds.fTop); + md.drawAsBitmap(dstM, p); + p.setBlendMode(gUncorrelatedCoverageToBlend[static_cast<int>(fMode)]); + srcM.fBounds.offset(-dst->fBounds.fLeft, -dst->fBounds.fTop); + md.drawAsBitmap(srcM, p); sk_free(srcM.fImage); sk_free(dstM.fImage); @@ -572,7 +590,7 @@ 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); + SkCoverageMode mode = safe.checkLE(buffer.read32(), SkCoverageMode::kLast); if (!buffer.validate(dst && src && safe)) { return nullptr; } @@ -587,7 +605,8 @@ void SkCombineMF::toString(SkString* str) const { //////////////////////////////////////// -sk_sp<SkMaskFilter> SkMaskFilter::MakeCompose(sk_sp<SkMaskFilter> outer, sk_sp<SkMaskFilter> inner) { +sk_sp<SkMaskFilter> SkMaskFilter::MakeCompose(sk_sp<SkMaskFilter> outer, + sk_sp<SkMaskFilter> inner) { if (!outer) { return inner; } @@ -602,20 +621,13 @@ sk_sp<SkMaskFilter> SkMaskFilter::MakeCompose(sk_sp<SkMaskFilter> outer, sk_sp<S } 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) { + SkCoverageMode mode) { + if (!dst) { return src; } - if (!src || mode == SkBlendMode::kDst || mode == SkBlendMode::kSrcATop) { + if (!src) { 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) { |