Fix mask/image filter bounds calculation for inverse-filled paths
Bug: skia:12587, skia:10959 Change-Id: I43dea5f2d2f54c7db47e778ac9c7bac53ae4998d Reviewed-on: https://skia-review.googlesource.com/c/skia/+/509176 Reviewed-by: Brian Osman <brianosman@google.com> Commit-Queue: Michael Ludwig <michaelludwig@google.com>
This commit is contained in:
parent
d6245fc4aa
commit
0b124c444e
@ -7,6 +7,7 @@
|
||||
|
||||
#include "gm/gm.h"
|
||||
#include "include/core/SkCanvas.h"
|
||||
#include "include/core/SkMaskFilter.h"
|
||||
#include "include/core/SkPaint.h"
|
||||
#include "include/core/SkPath.h"
|
||||
#include "include/core/SkPathEffect.h"
|
||||
@ -15,6 +16,7 @@
|
||||
#include "include/core/SkScalar.h"
|
||||
#include "include/core/SkTypes.h"
|
||||
#include "include/effects/SkDashPathEffect.h"
|
||||
#include "include/effects/SkImageFilters.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
@ -135,3 +137,64 @@ DEF_SIMPLE_GM(inverse_paths, canvas, 800, 1200) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEF_SIMPLE_GM(inverse_fill_filters, canvas, 384, 128) {
|
||||
auto draw = [canvas](const SkPaint& paint) {
|
||||
SkPath path = SkPath::Circle(65.f, 65.f, 30.f);
|
||||
path.setFillType(SkPathFillType::kInverseWinding);
|
||||
|
||||
canvas->save();
|
||||
canvas->clipRect({0, 0, 128, 128});
|
||||
canvas->drawPath(path, paint);
|
||||
canvas->restore();
|
||||
|
||||
SkPaint stroke;
|
||||
stroke.setStyle(SkPaint::kStroke_Style);
|
||||
stroke.setColor(SK_ColorWHITE);
|
||||
canvas->drawRect({0, 0, 128, 128}, stroke);
|
||||
};
|
||||
|
||||
SkPaint paint;
|
||||
paint.setAntiAlias(true);
|
||||
|
||||
draw(paint);
|
||||
|
||||
canvas->translate(128, 0);
|
||||
paint.setImageFilter(SkImageFilters::Blur(5.f, 5.f, nullptr));
|
||||
draw(paint);
|
||||
|
||||
canvas->translate(128, 0);
|
||||
paint.setImageFilter(nullptr);
|
||||
paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 5));
|
||||
draw(paint);
|
||||
}
|
||||
|
||||
DEF_SIMPLE_GM(inverse_windingmode_filters, canvas, 256, 100) {
|
||||
SkPath path;
|
||||
path.addRect({10, 10, 30, 30}, SkPathDirection::kCW);
|
||||
path.addRect({20, 20, 40, 40}, SkPathDirection::kCW);
|
||||
path.addRect({10, 60, 30, 80}, SkPathDirection::kCW);
|
||||
path.addRect({20, 70, 40, 90}, SkPathDirection::kCCW);
|
||||
SkPaint strokePaint;
|
||||
strokePaint.setStyle(SkPaint::kStroke_Style);
|
||||
SkRect clipRect = {0, 0, 51, 99};
|
||||
canvas->drawPath(path, strokePaint);
|
||||
SkPaint fillPaint;
|
||||
fillPaint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 1.0f));
|
||||
for (auto fillType : { SkPathFillType::kWinding,
|
||||
SkPathFillType::kEvenOdd,
|
||||
SkPathFillType::kInverseWinding,
|
||||
SkPathFillType::kInverseEvenOdd } ) {
|
||||
canvas->translate(51, 0);
|
||||
canvas->save();
|
||||
canvas->clipRect(clipRect);
|
||||
path.setFillType(fillType);
|
||||
canvas->drawPath(path, fillPaint);
|
||||
canvas->restore();
|
||||
SkPaint clipPaint;
|
||||
clipPaint.setColor(SK_ColorRED);
|
||||
clipPaint.setStyle(SkPaint::kStroke_Style);
|
||||
clipPaint.setStrokeWidth(1.f);
|
||||
canvas->drawRect(clipRect, clipPaint);
|
||||
}
|
||||
}
|
||||
|
@ -2129,7 +2129,7 @@ void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto layer = this->aboutToDraw(this, paint, &pathBounds);
|
||||
auto layer = this->aboutToDraw(this, paint, path.isInverseFillType() ? nullptr : &pathBounds);
|
||||
if (layer) {
|
||||
this->topDevice()->drawPath(path, layer->paint());
|
||||
}
|
||||
|
@ -1082,7 +1082,7 @@ void SkDraw::validate() const {
|
||||
#include "src/core/SkBlitter.h"
|
||||
#include "src/core/SkDraw.h"
|
||||
|
||||
bool SkDraw::ComputeMaskBounds(const SkRect& devPathBounds, const SkIRect* clipBounds,
|
||||
bool SkDraw::ComputeMaskBounds(const SkRect& devPathBounds, const SkIRect& clipBounds,
|
||||
const SkMaskFilter* filter, const SkMatrix* filterMatrix,
|
||||
SkIRect* bounds) {
|
||||
// init our bounds from the path
|
||||
@ -1101,21 +1101,18 @@ bool SkDraw::ComputeMaskBounds(const SkRect& devPathBounds, const SkIRect* clipB
|
||||
}
|
||||
}
|
||||
|
||||
// (possibly) trim the bounds to reflect the clip
|
||||
// (plus whatever slop the filter needs)
|
||||
if (clipBounds) {
|
||||
// trim the bounds to reflect the clip (plus whatever slop the filter needs)
|
||||
// Ugh. Guard against gigantic margins from wacky filters. Without this
|
||||
// check we can request arbitrary amounts of slop beyond our visible
|
||||
// clip, and bring down the renderer (at least on finite RAM machines
|
||||
// like handsets, etc.). Need to balance this invented value between
|
||||
// quality of large filters like blurs, and the corresponding memory
|
||||
// requests.
|
||||
static const int MAX_MARGIN = 128;
|
||||
if (!bounds->intersect(clipBounds->makeOutset(std::min(margin.fX, MAX_MARGIN),
|
||||
std::min(margin.fY, MAX_MARGIN)))) {
|
||||
static constexpr int kMaxMargin = 128;
|
||||
if (!bounds->intersect(clipBounds.makeOutset(std::min(margin.fX, kMaxMargin),
|
||||
std::min(margin.fY, kMaxMargin)))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1152,7 +1149,7 @@ static void draw_into_mask(const SkMask& mask, const SkPath& devPath,
|
||||
draw.drawPath(devPath, paint);
|
||||
}
|
||||
|
||||
bool SkDraw::DrawToMask(const SkPath& devPath, const SkIRect* clipBounds,
|
||||
bool SkDraw::DrawToMask(const SkPath& devPath, const SkIRect& clipBounds,
|
||||
const SkMaskFilter* filter, const SkMatrix* filterMatrix,
|
||||
SkMask* mask, SkMask::CreateMode mode,
|
||||
SkStrokeRec::InitStyle style) {
|
||||
@ -1161,7 +1158,13 @@ bool SkDraw::DrawToMask(const SkPath& devPath, const SkIRect* clipBounds,
|
||||
}
|
||||
|
||||
if (SkMask::kJustRenderImage_CreateMode != mode) {
|
||||
if (!ComputeMaskBounds(devPath.getBounds(), clipBounds, filter,
|
||||
// By using infinite bounds for inverse fills, ComputeMaskBounds is able to clip it to
|
||||
// 'clipBounds' outset by whatever extra margin the mask filter requires.
|
||||
static const SkRect kInverseBounds = { SK_ScalarNegativeInfinity, SK_ScalarNegativeInfinity,
|
||||
SK_ScalarInfinity, SK_ScalarInfinity};
|
||||
SkRect pathBounds = devPath.isInverseFillType() ? kInverseBounds
|
||||
: devPath.getBounds();
|
||||
if (!ComputeMaskBounds(pathBounds, clipBounds, filter,
|
||||
filterMatrix, &mask->fBounds))
|
||||
return false;
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ public:
|
||||
|
||||
void paintMasks(SkDrawableGlyphBuffer* accepted, const SkPaint& paint) const override;
|
||||
|
||||
static bool ComputeMaskBounds(const SkRect& devPathBounds, const SkIRect* clipBounds,
|
||||
static bool ComputeMaskBounds(const SkRect& devPathBounds, const SkIRect& clipBounds,
|
||||
const SkMaskFilter* filter, const SkMatrix* filterMatrix,
|
||||
SkIRect* bounds);
|
||||
|
||||
@ -93,7 +93,7 @@ public:
|
||||
that must be done afterwards (by calling filterMask). The maskfilter is provided
|
||||
solely to assist in computing the mask's bounds (if the mode requests that).
|
||||
*/
|
||||
static bool DrawToMask(const SkPath& devPath, const SkIRect* clipBounds,
|
||||
static bool DrawToMask(const SkPath& devPath, const SkIRect& clipBounds,
|
||||
const SkMaskFilter*, const SkMatrix* filterMatrix,
|
||||
SkMask* mask, SkMask::CreateMode mode,
|
||||
SkStrokeRec::InitStyle style);
|
||||
|
@ -265,7 +265,7 @@ bool SkMaskFilterBase::filterPath(const SkPath& devPath, const SkMatrix& matrix,
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), this, &matrix, &srcM,
|
||||
if (!SkDraw::DrawToMask(devPath, clip.getBounds(), this, &matrix, &srcM,
|
||||
SkMask::kComputeBoundsAndRenderImage_CreateMode,
|
||||
style)) {
|
||||
return false;
|
||||
|
@ -133,7 +133,7 @@ static GrSurfaceProxyView sw_create_filtered_mask(GrRecordingContext* rContext,
|
||||
devPath.transform(viewMatrix);
|
||||
|
||||
SkMask srcM, dstM;
|
||||
if (!SkDraw::DrawToMask(devPath, &clipBounds, filter, &viewMatrix, &srcM,
|
||||
if (!SkDraw::DrawToMask(devPath, clipBounds, filter, &viewMatrix, &srcM,
|
||||
SkMask::kComputeBoundsAndRenderImage_CreateMode, fillOrHairline)) {
|
||||
return {};
|
||||
}
|
||||
@ -237,12 +237,17 @@ static std::unique_ptr<skgpu::v1::SurfaceDrawContext> create_mask_GPU(
|
||||
|
||||
static bool get_unclipped_shape_dev_bounds(const GrStyledShape& shape, const SkMatrix& matrix,
|
||||
SkIRect* devBounds) {
|
||||
SkRect shapeDevBounds;
|
||||
if (shape.inverseFilled()) {
|
||||
shapeDevBounds = {SK_ScalarNegativeInfinity, SK_ScalarNegativeInfinity,
|
||||
SK_ScalarInfinity, SK_ScalarInfinity};
|
||||
} else {
|
||||
SkRect shapeBounds = shape.styledBounds();
|
||||
if (shapeBounds.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
SkRect shapeDevBounds;
|
||||
matrix.mapRect(&shapeDevBounds, shapeBounds);
|
||||
}
|
||||
// Even though these are "unclipped" bounds we still clip to the int32_t range.
|
||||
// This is the largest int32_t that is representable exactly as a float. The next 63 larger ints
|
||||
// would round down to this value when cast to a float, but who really cares.
|
||||
|
@ -504,7 +504,7 @@ void SkPDFDevice::internalDrawPathWithFilter(const SkClipStack& clipStack,
|
||||
|
||||
SkIRect bounds = clipStack.bounds(this->bounds()).roundOut();
|
||||
SkMask sourceMask;
|
||||
if (!SkDraw::DrawToMask(path, &bounds, paint->getMaskFilter(), &SkMatrix::I(),
|
||||
if (!SkDraw::DrawToMask(path, bounds, paint->getMaskFilter(), &SkMatrix::I(),
|
||||
&sourceMask, SkMask::kComputeBoundsAndRenderImage_CreateMode,
|
||||
initStyle)) {
|
||||
return;
|
||||
|
@ -1590,7 +1590,7 @@ void SkXPSDevice::drawPath(const SkPath& platonicPath,
|
||||
SkMask rasteredMask;
|
||||
if (SkDraw::DrawToMask(
|
||||
*pixelPath,
|
||||
&clipIRect,
|
||||
clipIRect,
|
||||
filter, //just to compute how much to draw.
|
||||
&matrix,
|
||||
&rasteredMask,
|
||||
|
Loading…
Reference in New Issue
Block a user