summaryrefslogtreecommitdiffstats
path: root/gfx/skia/skia/src/core/SkMaskFilter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/skia/skia/src/core/SkMaskFilter.cpp')
-rw-r--r--gfx/skia/skia/src/core/SkMaskFilter.cpp414
1 files changed, 414 insertions, 0 deletions
diff --git a/gfx/skia/skia/src/core/SkMaskFilter.cpp b/gfx/skia/skia/src/core/SkMaskFilter.cpp
new file mode 100644
index 0000000000..9cae59f5b6
--- /dev/null
+++ b/gfx/skia/skia/src/core/SkMaskFilter.cpp
@@ -0,0 +1,414 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "include/core/SkMaskFilter.h"
+
+#include "include/core/SkFlattenable.h"
+#include "include/core/SkMatrix.h"
+#include "include/core/SkPath.h"
+#include "include/core/SkPoint.h"
+#include "include/core/SkRect.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkRegion.h"
+#include "include/core/SkStrokeRec.h"
+#include "include/core/SkTypes.h"
+#include "include/private/base/SkTemplates.h"
+#include "src/base/SkAutoMalloc.h"
+#include "src/core/SkBlitter.h"
+#include "src/core/SkCachedData.h"
+#include "src/core/SkDraw.h"
+#include "src/core/SkMask.h"
+#include "src/core/SkMaskFilterBase.h"
+#include "src/core/SkPathPriv.h"
+#include "src/core/SkRasterClip.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <memory>
+
+#if defined(SK_GANESH)
+#include "include/private/base/SkTo.h"
+#include "src/gpu/ganesh/GrFragmentProcessor.h"
+#include "src/gpu/ganesh/GrSurfaceProxyView.h"
+
+class GrClip;
+class GrRecordingContext;
+class GrStyledShape;
+enum class GrColorType;
+struct GrFPArgs;
+namespace skgpu {
+namespace ganesh {
+class SurfaceDrawContext;
+}
+} // namespace skgpu
+#endif
+#if defined(SK_GANESH) || defined(SK_GRAPHITE)
+#include "src/text/gpu/SDFMaskFilter.h"
+#endif
+
+class SkRRect;
+enum SkAlphaType : int;
+struct SkDeserialProcs;
+
+SkMaskFilterBase::NinePatch::~NinePatch() {
+ if (fCache) {
+ SkASSERT((const void*)fMask.fImage == fCache->data());
+ fCache->unref();
+ } else {
+ SkMask::FreeImage(fMask.fImage);
+ }
+}
+
+bool SkMaskFilterBase::asABlur(BlurRec*) const {
+ return false;
+}
+
+static void extractMaskSubset(const SkMask& src, SkMask* dst) {
+ SkASSERT(src.fBounds.contains(dst->fBounds));
+
+ const int dx = dst->fBounds.left() - src.fBounds.left();
+ const int dy = dst->fBounds.top() - src.fBounds.top();
+ dst->fImage = src.fImage + dy * src.fRowBytes + dx;
+ dst->fRowBytes = src.fRowBytes;
+ dst->fFormat = src.fFormat;
+}
+
+static void blitClippedMask(SkBlitter* blitter, const SkMask& mask,
+ const SkIRect& bounds, const SkIRect& clipR) {
+ SkIRect r;
+ if (r.intersect(bounds, clipR)) {
+ blitter->blitMask(mask, r);
+ }
+}
+
+static void blitClippedRect(SkBlitter* blitter, const SkIRect& rect, const SkIRect& clipR) {
+ SkIRect r;
+ if (r.intersect(rect, clipR)) {
+ blitter->blitRect(r.left(), r.top(), r.width(), r.height());
+ }
+}
+
+#if 0
+static void dump(const SkMask& mask) {
+ for (int y = mask.fBounds.top(); y < mask.fBounds.bottom(); ++y) {
+ for (int x = mask.fBounds.left(); x < mask.fBounds.right(); ++x) {
+ SkDebugf("%02X", *mask.getAddr8(x, y));
+ }
+ SkDebugf("\n");
+ }
+ SkDebugf("\n");
+}
+#endif
+
+static void draw_nine_clipped(const SkMask& mask, const SkIRect& outerR,
+ const SkIPoint& center, bool fillCenter,
+ const SkIRect& clipR, SkBlitter* blitter) {
+ int cx = center.x();
+ int cy = center.y();
+ SkMask m;
+
+ // top-left
+ m.fBounds = mask.fBounds;
+ m.fBounds.fRight = cx;
+ m.fBounds.fBottom = cy;
+ if (m.fBounds.width() > 0 && m.fBounds.height() > 0) {
+ extractMaskSubset(mask, &m);
+ m.fBounds.offsetTo(outerR.left(), outerR.top());
+ blitClippedMask(blitter, m, m.fBounds, clipR);
+ }
+
+ // top-right
+ m.fBounds = mask.fBounds;
+ m.fBounds.fLeft = cx + 1;
+ m.fBounds.fBottom = cy;
+ if (m.fBounds.width() > 0 && m.fBounds.height() > 0) {
+ extractMaskSubset(mask, &m);
+ m.fBounds.offsetTo(outerR.right() - m.fBounds.width(), outerR.top());
+ blitClippedMask(blitter, m, m.fBounds, clipR);
+ }
+
+ // bottom-left
+ m.fBounds = mask.fBounds;
+ m.fBounds.fRight = cx;
+ m.fBounds.fTop = cy + 1;
+ if (m.fBounds.width() > 0 && m.fBounds.height() > 0) {
+ extractMaskSubset(mask, &m);
+ m.fBounds.offsetTo(outerR.left(), outerR.bottom() - m.fBounds.height());
+ blitClippedMask(blitter, m, m.fBounds, clipR);
+ }
+
+ // bottom-right
+ m.fBounds = mask.fBounds;
+ m.fBounds.fLeft = cx + 1;
+ m.fBounds.fTop = cy + 1;
+ if (m.fBounds.width() > 0 && m.fBounds.height() > 0) {
+ extractMaskSubset(mask, &m);
+ m.fBounds.offsetTo(outerR.right() - m.fBounds.width(),
+ outerR.bottom() - m.fBounds.height());
+ blitClippedMask(blitter, m, m.fBounds, clipR);
+ }
+
+ SkIRect innerR;
+ innerR.setLTRB(outerR.left() + cx - mask.fBounds.left(),
+ outerR.top() + cy - mask.fBounds.top(),
+ outerR.right() + (cx + 1 - mask.fBounds.right()),
+ outerR.bottom() + (cy + 1 - mask.fBounds.bottom()));
+ if (fillCenter) {
+ blitClippedRect(blitter, innerR, clipR);
+ }
+
+ const int innerW = innerR.width();
+ size_t storageSize = (innerW + 1) * (sizeof(int16_t) + sizeof(uint8_t));
+ SkAutoSMalloc<4*1024> storage(storageSize);
+ int16_t* runs = (int16_t*)storage.get();
+ uint8_t* alpha = (uint8_t*)(runs + innerW + 1);
+
+ SkIRect r;
+ // top
+ r.setLTRB(innerR.left(), outerR.top(), innerR.right(), innerR.top());
+ if (r.intersect(clipR)) {
+ int startY = std::max(0, r.top() - outerR.top());
+ int stopY = startY + r.height();
+ int width = r.width();
+ for (int y = startY; y < stopY; ++y) {
+ runs[0] = width;
+ runs[width] = 0;
+ alpha[0] = *mask.getAddr8(cx, mask.fBounds.top() + y);
+ blitter->blitAntiH(r.left(), outerR.top() + y, alpha, runs);
+ }
+ }
+ // bottom
+ r.setLTRB(innerR.left(), innerR.bottom(), innerR.right(), outerR.bottom());
+ if (r.intersect(clipR)) {
+ int startY = outerR.bottom() - r.bottom();
+ int stopY = startY + r.height();
+ int width = r.width();
+ for (int y = startY; y < stopY; ++y) {
+ runs[0] = width;
+ runs[width] = 0;
+ alpha[0] = *mask.getAddr8(cx, mask.fBounds.bottom() - y - 1);
+ blitter->blitAntiH(r.left(), outerR.bottom() - y - 1, alpha, runs);
+ }
+ }
+ // left
+ r.setLTRB(outerR.left(), innerR.top(), innerR.left(), innerR.bottom());
+ if (r.intersect(clipR)) {
+ SkMask leftMask;
+ leftMask.fImage = mask.getAddr8(mask.fBounds.left() + r.left() - outerR.left(),
+ mask.fBounds.top() + cy);
+ leftMask.fBounds = r;
+ leftMask.fRowBytes = 0; // so we repeat the scanline for our height
+ leftMask.fFormat = SkMask::kA8_Format;
+ blitter->blitMask(leftMask, r);
+ }
+ // right
+ r.setLTRB(innerR.right(), innerR.top(), outerR.right(), innerR.bottom());
+ if (r.intersect(clipR)) {
+ SkMask rightMask;
+ rightMask.fImage = mask.getAddr8(mask.fBounds.right() - outerR.right() + r.left(),
+ mask.fBounds.top() + cy);
+ rightMask.fBounds = r;
+ rightMask.fRowBytes = 0; // so we repeat the scanline for our height
+ rightMask.fFormat = SkMask::kA8_Format;
+ blitter->blitMask(rightMask, r);
+ }
+}
+
+static void draw_nine(const SkMask& mask, const SkIRect& outerR, const SkIPoint& center,
+ bool fillCenter, const SkRasterClip& clip, SkBlitter* blitter) {
+ // if we get here, we need to (possibly) resolve the clip and blitter
+ SkAAClipBlitterWrapper wrapper(clip, blitter);
+ blitter = wrapper.getBlitter();
+
+ SkRegion::Cliperator clipper(wrapper.getRgn(), outerR);
+
+ if (!clipper.done()) {
+ const SkIRect& cr = clipper.rect();
+ do {
+ draw_nine_clipped(mask, outerR, center, fillCenter, cr, blitter);
+ clipper.next();
+ } while (!clipper.done());
+ }
+}
+
+static int countNestedRects(const SkPath& path, SkRect rects[2]) {
+ if (SkPathPriv::IsNestedFillRects(path, rects)) {
+ return 2;
+ }
+ return path.isRect(&rects[0]);
+}
+
+bool SkMaskFilterBase::filterRRect(const SkRRect& devRRect, const SkMatrix& matrix,
+ const SkRasterClip& clip, SkBlitter* blitter) const {
+ // Attempt to speed up drawing by creating a nine patch. If a nine patch
+ // cannot be used, return false to allow our caller to recover and perform
+ // the drawing another way.
+ NinePatch patch;
+ patch.fMask.fImage = nullptr;
+ if (kTrue_FilterReturn != this->filterRRectToNine(devRRect, matrix,
+ clip.getBounds(),
+ &patch)) {
+ SkASSERT(nullptr == patch.fMask.fImage);
+ return false;
+ }
+ draw_nine(patch.fMask, patch.fOuterRect, patch.fCenter, true, clip, blitter);
+ return true;
+}
+
+bool SkMaskFilterBase::filterPath(const SkPath& devPath, const SkMatrix& matrix,
+ const SkRasterClip& clip, SkBlitter* blitter,
+ SkStrokeRec::InitStyle style) const {
+ SkRect rects[2];
+ int rectCount = 0;
+ if (SkStrokeRec::kFill_InitStyle == style) {
+ rectCount = countNestedRects(devPath, rects);
+ }
+ if (rectCount > 0) {
+ NinePatch patch;
+
+ switch (this->filterRectsToNine(rects, rectCount, matrix, clip.getBounds(), &patch)) {
+ case kFalse_FilterReturn:
+ SkASSERT(nullptr == patch.fMask.fImage);
+ return false;
+
+ case kTrue_FilterReturn:
+ draw_nine(patch.fMask, patch.fOuterRect, patch.fCenter, 1 == rectCount, clip,
+ blitter);
+ return true;
+
+ case kUnimplemented_FilterReturn:
+ SkASSERT(nullptr == patch.fMask.fImage);
+ // fall out
+ break;
+ }
+ }
+
+ SkMask srcM, dstM;
+
+#if defined(SK_BUILD_FOR_FUZZER)
+ if (devPath.countVerbs() > 1000 || devPath.countPoints() > 1000) {
+ return false;
+ }
+#endif
+ if (!SkDraw::DrawToMask(devPath, clip.getBounds(), this, &matrix, &srcM,
+ SkMask::kComputeBoundsAndRenderImage_CreateMode,
+ style)) {
+ return false;
+ }
+ SkAutoMaskFreeImage autoSrc(srcM.fImage);
+
+ if (!this->filterMask(&dstM, srcM, matrix, nullptr)) {
+ return false;
+ }
+ SkAutoMaskFreeImage autoDst(dstM.fImage);
+
+ // if we get here, we need to (possibly) resolve the clip and blitter
+ SkAAClipBlitterWrapper wrapper(clip, blitter);
+ blitter = wrapper.getBlitter();
+
+ SkRegion::Cliperator clipper(wrapper.getRgn(), dstM.fBounds);
+
+ if (!clipper.done()) {
+ const SkIRect& cr = clipper.rect();
+ do {
+ blitter->blitMask(dstM, cr);
+ clipper.next();
+ } while (!clipper.done());
+ }
+
+ return true;
+}
+
+SkMaskFilterBase::FilterReturn
+SkMaskFilterBase::filterRRectToNine(const SkRRect&, const SkMatrix&,
+ const SkIRect& clipBounds, NinePatch*) const {
+ return kUnimplemented_FilterReturn;
+}
+
+SkMaskFilterBase::FilterReturn
+SkMaskFilterBase::filterRectsToNine(const SkRect[], int count, const SkMatrix&,
+ const SkIRect& clipBounds, NinePatch*) const {
+ return kUnimplemented_FilterReturn;
+}
+
+#if defined(SK_GANESH)
+std::unique_ptr<GrFragmentProcessor>
+SkMaskFilterBase::asFragmentProcessor(const GrFPArgs& args, const SkMatrix& ctm) const {
+ auto fp = this->onAsFragmentProcessor(args, MatrixRec(ctm));
+ SkASSERT(SkToBool(fp) == this->hasFragmentProcessor());
+ return fp;
+}
+bool SkMaskFilterBase::hasFragmentProcessor() const {
+ return this->onHasFragmentProcessor();
+}
+
+std::unique_ptr<GrFragmentProcessor>
+SkMaskFilterBase::onAsFragmentProcessor(const GrFPArgs&, const MatrixRec&) const {
+ return nullptr;
+}
+bool SkMaskFilterBase::onHasFragmentProcessor() const { return false; }
+
+bool SkMaskFilterBase::canFilterMaskGPU(const GrStyledShape& shape,
+ const SkIRect& devSpaceShapeBounds,
+ const SkIRect& clipBounds,
+ const SkMatrix& ctm,
+ SkIRect* maskRect) const {
+ return false;
+}
+
+bool SkMaskFilterBase::directFilterMaskGPU(GrRecordingContext*,
+ skgpu::ganesh::SurfaceDrawContext*,
+ GrPaint&&,
+ const GrClip*,
+ const SkMatrix& viewMatrix,
+ const GrStyledShape&) const {
+ return false;
+}
+
+GrSurfaceProxyView SkMaskFilterBase::filterMaskGPU(GrRecordingContext*,
+ GrSurfaceProxyView view,
+ GrColorType srcColorType,
+ SkAlphaType srcAlphaType,
+ const SkMatrix& ctm,
+ const SkIRect& maskRect) const {
+ return {};
+}
+#endif
+
+void SkMaskFilterBase::computeFastBounds(const SkRect& src, SkRect* dst) const {
+ SkMask srcM, dstM;
+
+ srcM.fBounds = src.roundOut();
+ srcM.fRowBytes = 0;
+ srcM.fFormat = SkMask::kA8_Format;
+
+ SkIPoint margin; // ignored
+ if (this->filterMask(&dstM, srcM, SkMatrix::I(), &margin)) {
+ dst->set(dstM.fBounds);
+ } else {
+ dst->set(srcM.fBounds);
+ }
+}
+
+SkRect SkMaskFilter::approximateFilteredBounds(const SkRect& src) const {
+ SkRect dst;
+ as_MFB(this)->computeFastBounds(src, &dst);
+ return dst;
+}
+
+void SkMaskFilter::RegisterFlattenables() {
+ sk_register_blur_maskfilter_createproc();
+#if (defined(SK_GANESH) || defined(SK_GRAPHITE)) && !defined(SK_DISABLE_SDF_TEXT)
+ sktext::gpu::register_sdf_maskfilter_createproc();
+#endif
+}
+
+sk_sp<SkMaskFilter> SkMaskFilter::Deserialize(const void* data, size_t size,
+ const SkDeserialProcs* procs) {
+ return sk_sp<SkMaskFilter>(static_cast<SkMaskFilter*>(
+ SkFlattenable::Deserialize(
+ kSkMaskFilter_Type, data, size, procs).release()));
+}