blob: d56f7ae4b9f5fe7127eb05cc1975531be42e3171 [file] [log] [blame]
/*
* 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/SkImageFilter.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/base/SkTLazy.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>
class SkRRect;
struct SkDeserialProcs;
SkMaskFilterBase::NinePatch::~NinePatch() {
if (fCache) {
SkASSERT((const void*)fMask.fImage == fCache->data());
fCache->unref();
} else {
// fMask is about to be destroyed and "owns" its fImage.
SkMaskBuilder::FreeImage(const_cast<uint8_t*>(fMask.fImage));
}
}
bool SkMaskFilterBase::asABlur(BlurRec*) const {
return false;
}
sk_sp<SkImageFilter> SkMaskFilterBase::asImageFilter(const SkMatrix& ctm) const {
return nullptr;
}
static SkMask extractMaskSubset(const SkMask& src, SkIRect bounds, int32_t newX, int32_t newY) {
SkASSERT(src.fBounds.contains(bounds));
const int dx = bounds.left() - src.fBounds.left();
const int dy = bounds.top() - src.fBounds.top();
bounds.offsetTo(newX, newY);
return SkMask(src.fImage + dy * src.fRowBytes + dx,
bounds,
src.fRowBytes,
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());
}
}
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();
SkIRect bounds;
// top-left
bounds = mask.fBounds;
bounds.fRight = cx;
bounds.fBottom = cy;
if (bounds.width() > 0 && bounds.height() > 0) {
SkMask m = extractMaskSubset(mask, bounds, outerR.left(), outerR.top());
blitClippedMask(blitter, m, m.fBounds, clipR);
}
// top-right
bounds = mask.fBounds;
bounds.fLeft = cx + 1;
bounds.fBottom = cy;
if (bounds.width() > 0 && bounds.height() > 0) {
SkMask m = extractMaskSubset(mask, bounds, outerR.right() - bounds.width(), outerR.top());
blitClippedMask(blitter, m, m.fBounds, clipR);
}
// bottom-left
bounds = mask.fBounds;
bounds.fRight = cx;
bounds.fTop = cy + 1;
if (bounds.width() > 0 && bounds.height() > 0) {
SkMask m = extractMaskSubset(mask, bounds, outerR.left(), outerR.bottom() - bounds.height());
blitClippedMask(blitter, m, m.fBounds, clipR);
}
// bottom-right
bounds = mask.fBounds;
bounds.fLeft = cx + 1;
bounds.fTop = cy + 1;
if (bounds.width() > 0 && bounds.height() > 0) {
SkMask m = extractMaskSubset(mask, bounds, outerR.right() - bounds.width(),
outerR.bottom() - bounds.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(mask.getAddr8(mask.fBounds.left() + r.left() - outerR.left(),
mask.fBounds.top() + cy),
r,
0, // so we repeat the scanline for our height
SkMask::kA8_Format);
blitter->blitMask(leftMask, r);
}
// right
r.setLTRB(innerR.right(), innerR.top(), outerR.right(), innerR.bottom());
if (r.intersect(clipR)) {
SkMask rightMask(mask.getAddr8(mask.fBounds.right() - outerR.right() + r.left(),
mask.fBounds.top() + cy),
r,
0, // so we repeat the scanline for our height
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.
SkTLazy<NinePatch> patch;
if (kTrue_FilterReturn != this->filterRRectToNine(devRRect, matrix,
clip.getBounds(),
&patch)) {
SkASSERT(!patch.isValid());
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) {
SkTLazy<NinePatch> patch;
switch (this->filterRectsToNine(rects, rectCount, matrix, clip.getBounds(), &patch)) {
case kFalse_FilterReturn:
SkASSERT(!patch.isValid());
return false;
case kTrue_FilterReturn:
draw_nine(patch->fMask, patch->fOuterRect, patch->fCenter, 1 == rectCount, clip,
blitter);
return true;
case kUnimplemented_FilterReturn:
SkASSERT(!patch.isValid());
// fall out
break;
}
}
SkMaskBuilder 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,
SkMaskBuilder::kComputeBoundsAndRenderImage_CreateMode,
style)) {
return false;
}
SkAutoMaskFreeImage autoSrc(srcM.image());
if (!this->filterMask(&dstM, srcM, matrix, nullptr)) {
return false;
}
SkAutoMaskFreeImage autoDst(dstM.image());
// 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, SkTLazy<NinePatch>*) const {
return kUnimplemented_FilterReturn;
}
SkMaskFilterBase::FilterReturn
SkMaskFilterBase::filterRectsToNine(const SkRect[], int count, const SkMatrix&,
const SkIRect& clipBounds, SkTLazy<NinePatch>*) const {
return kUnimplemented_FilterReturn;
}
void SkMaskFilterBase::computeFastBounds(const SkRect& src, SkRect* dst) const {
SkMask srcM(nullptr, src.roundOut(), 0, SkMask::kA8_Format);
SkMaskBuilder dstM;
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();
}
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()));
}