aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Mike Reed <reed@google.com>2018-01-26 11:42:38 -0500
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2018-01-26 17:03:58 +0000
commit1bd556a1776c6c0b2a7b940516bc53f329516dc7 (patch)
tree30a25a1cc739c2167a6b31f3fb8903470833921d
parentf9a5e62981c9c1ddd838b5e466c991fae82233a4 (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.cpp99
-rw-r--r--include/core/SkCoverageMode.h30
-rw-r--r--include/core/SkMaskFilter.h8
-rw-r--r--src/core/SkCoverageModePriv.h49
-rw-r--r--src/core/SkMaskFilter.cpp70
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) {