Revert "Simplify SkRasterClip now that it's only intersect/diff"

This reverts commit 75bab9249d.

Reason for revert: Experiment to see if blocking Chrome roll

Original change's description:
> Simplify SkRasterClip now that it's only intersect/diff
>
> Bug: skia:10205
> Change-Id: Id29a63783bd38e5977e94bf8e8d7fbb4fe16cb51
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/442279
> Reviewed-by: Mike Reed <reed@google.com>
> Commit-Queue: Michael Ludwig <michaelludwig@google.com>

TBR=reed@google.com,michaelludwig@google.com,skcq-be@skia-corp.google.com.iam.gserviceaccount.com

Change-Id: I1ceeccf880a139dc9236b7df17e3599f8cae611f
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: skia:10205
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/443414
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
This commit is contained in:
Robert Phillips 2021-08-31 00:11:43 +00:00 committed by SkCQ
parent cadd5dbded
commit 9a0302cd9d
7 changed files with 300 additions and 150 deletions

View File

@ -42,16 +42,16 @@ public:
}
void clipRect(const SkRect& r, SkClipOp op) override {
fRC.op(r, fCTM, op, false);
fRC.op(r, fCTM, fBounds, (SkRegion::Op)op, false);
fCache.reset(nullptr);
}
void clipRRect(const SkRRect& rr, SkClipOp op) override {
fRC.op(rr, fCTM, op, false);
fRC.op(rr, fCTM, fBounds, (SkRegion::Op)op, false);
fCache.reset(nullptr);
}
void clipPath(const SkPath& p, SkClipOp op) override {
fRC.op(p, fCTM, op, false);
fRC.op(p, fCTM, fBounds, (SkRegion::Op)op, false);
fCache.reset(nullptr);
}

View File

@ -169,7 +169,7 @@ private:
SkIntToScalar(-fOrigin.y()));
fDevice->fRCStack.rc().translate(-fOrigin.x(), -fOrigin.y(), &fTileRC);
fTileRC.op(SkIRect::MakeWH(fDraw.fDst.width(), fDraw.fDst.height()),
SkClipOp::kIntersect);
SkRegion::kIntersect_Op);
}
};
@ -708,7 +708,7 @@ SkBaseDevice::ClipType SkBitmapDevice::onGetClipType() const {
const SkRasterClip& rc = fRCStack.rc();
if (rc.isEmpty()) {
return ClipType::kEmpty;
} else if (rc.isRect() && !SkToBool(rc.clipShader())) {
} else if (rc.isRect()) {
return ClipType::kRect;
} else {
return ClipType::kComplex;

View File

@ -1058,7 +1058,7 @@ static bool clipped_out(const SkMatrix& matrix, const SkRasterClip& clip,
}
static bool clipHandlesSprite(const SkRasterClip& clip, int x, int y, const SkPixmap& pmap) {
return clip.isBW() || clip.quickContains(SkIRect::MakeXYWH(x, y, pmap.width(), pmap.height()));
return clip.isBW() || clip.quickContains(x, y, x + pmap.width(), y + pmap.height());
}
void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix,

View File

@ -64,20 +64,6 @@ SkRasterClip::SkRasterClip() {
SkDEBUGCODE(this->validate();)
}
SkRasterClip::SkRasterClip(const SkPath& path, const SkIRect& bounds, bool doAA) {
SkRegion clip(bounds);
if (doAA) {
fIsBW = false;
fAA.setPath(path, &clip, true);
} else {
fIsBW = true;
fBW.setPath(path, clip);
}
fIsEmpty = this->computeIsEmpty(); // bounds might be empty, so compute
fIsRect = this->computeIsRect();
SkDEBUGCODE(this->validate();)
}
SkRasterClip::~SkRasterClip() {
SkDEBUGCODE(this->validate();)
}
@ -96,6 +82,14 @@ bool SkRasterClip::operator==(const SkRasterClip& other) const {
return isEqual;
}
bool SkRasterClip::isComplex() const {
return fIsBW ? fBW.isComplex() : !fAA.isEmpty();
}
const SkIRect& SkRasterClip::getBounds() const {
return fIsBW ? fBW.getBounds() : fAA.getBounds();
}
bool SkRasterClip::setEmpty() {
AUTO_RASTERCLIP_VALIDATE(*this);
@ -119,82 +113,51 @@ bool SkRasterClip::setRect(const SkIRect& rect) {
/////////////////////////////////////////////////////////////////////////////////////
bool SkRasterClip::op(const SkIRect& rect, SkClipOp op) {
AUTO_RASTERCLIP_VALIDATE(*this);
if (fIsBW) {
fBW.op(rect, (SkRegion::Op) op);
bool SkRasterClip::setConservativeRect(const SkRect& r, const SkIRect& clipR, bool isInverse) {
SkRegion::Op op;
if (isInverse) {
op = SkRegion::kDifference_Op;
} else {
fAA.op(rect, (SkRegion::Op) op);
op = SkRegion::kIntersect_Op;
}
fBW.setRect(clipR);
fBW.op(r.roundOut(), op);
return this->updateCacheAndReturnNonEmpty();
}
bool SkRasterClip::op(const SkRegion& rgn, SkClipOp op) {
/////////////////////////////////////////////////////////////////////////////////////
bool SkRasterClip::setPath(const SkPath& path, const SkRegion& clip, bool doAA) {
AUTO_RASTERCLIP_VALIDATE(*this);
if (fIsBW) {
(void)fBW.op(rgn, (SkRegion::Op) op);
if (this->isBW() && !doAA) {
(void)fBW.setPath(path, clip);
} else {
SkAAClip tmp;
tmp.setRegion(rgn);
(void)fAA.op(tmp, (SkRegion::Op) op);
}
return this->updateCacheAndReturnNonEmpty();
}
/**
* Our antialiasing currently has a granularity of 1/4 of a pixel along each
* axis. Thus we can treat an axis coordinate as an integer if it differs
* from its nearest int by < half of that value (1/8 in this case).
*/
static bool nearly_integral(SkScalar x) {
static const SkScalar domain = SK_Scalar1 / 4;
static const SkScalar halfDomain = domain / 2;
x += halfDomain;
return x - SkScalarFloorToScalar(x) < domain;
}
bool SkRasterClip::op(const SkRect& localRect, const SkMatrix& matrix, SkClipOp op, bool doAA) {
AUTO_RASTERCLIP_VALIDATE(*this);
const bool isScaleTrans = matrix.isScaleTranslate();
if (!isScaleTrans) {
SkPath path = SkPath::Rect(localRect);
path.setIsVolatile(true);
return this->op(path, matrix, op, doAA);
}
SkRect devRect = matrix.mapRect(localRect);
if (fIsBW && doAA) {
// check that the rect really needs aa, or is it close enought to
// integer boundaries that we can just treat it as a BW rect?
if (nearly_integral(devRect.fLeft) && nearly_integral(devRect.fTop) &&
nearly_integral(devRect.fRight) && nearly_integral(devRect.fBottom)) {
doAA = false;
}
}
if (fIsBW && !doAA) {
(void)fBW.op(devRect.round(), (SkRegion::Op) op);
} else {
if (fIsBW) {
// TODO: since we are going to over-write fAA completely (aren't we?)
// we should just clear our BW data (if any) and set fIsAA=true
if (this->isBW()) {
this->convertToAA();
}
(void)fAA.op(devRect, (SkRegion::Op) op, doAA);
(void)fAA.setPath(path, &clip, doAA);
}
return this->updateCacheAndReturnNonEmpty();
}
bool SkRasterClip::op(const SkRRect& rrect, const SkMatrix& matrix, SkClipOp op, bool doAA) {
return this->op(SkPath::RRect(rrect), matrix, op, doAA);
bool SkRasterClip::op(const SkRRect& rrect, const SkMatrix& matrix, const SkIRect& devBounds,
SkRegion::Op op, bool doAA) {
SkIRect bounds(devBounds);
return this->op(SkPath::RRect(rrect), matrix, bounds, op, doAA);
}
bool SkRasterClip::op(const SkPath& path, const SkMatrix& matrix, SkClipOp op, bool doAA) {
bool SkRasterClip::op(const SkPath& path, const SkMatrix& matrix, const SkIRect& devBounds,
SkRegion::Op op, bool doAA) {
AUTO_RASTERCLIP_VALIDATE(*this);
SkIRect bounds(devBounds);
// base is used to limit the size (and therefore memory allocation) of the
// region that results from scan converting devPath.
SkRegion base;
// Transform into device space
SkPath devPath;
if (matrix.isIdentity()) {
devPath = path;
@ -202,30 +165,66 @@ bool SkRasterClip::op(const SkPath& path, const SkMatrix& matrix, SkClipOp op, b
path.transform(matrix, &devPath);
devPath.setIsVolatile(true);
}
if (SkRegion::kIntersect_Op == op) {
// since we are intersect, we can do better (tighter) with currRgn's
// bounds, than just using the device. However, if currRgn is complex,
// our region blitter may hork, so we do that case in two steps.
if (this->isRect()) {
// FIXME: we should also be able to do this when this->isBW(),
// but relaxing the test above triggers GM asserts in
// SkRgnBuilder::blitH(). We need to investigate what's going on.
return this->setPath(devPath, this->bwRgn(), doAA);
} else {
base.setRect(this->getBounds());
SkRasterClip clip;
clip.setPath(devPath, base, doAA);
return this->op(clip, op);
}
} else {
base.setRect(bounds);
// Since op is either intersect or difference, the clip is always shrinking; that means we can
// always use our current bounds as the limiting factor for region/aaclip operations
SkRasterClip clip(devPath, this->getBounds(), doAA);
return this->op(clip, op);
if (SkRegion::kReplace_Op == op) {
return this->setPath(devPath, base, doAA);
} else {
SkRasterClip clip;
clip.setPath(devPath, base, doAA);
return this->op(clip, op);
}
}
}
bool SkRasterClip::op(sk_sp<SkShader> sh) {
bool SkRasterClip::setPath(const SkPath& path, const SkIRect& clip, bool doAA) {
SkRegion tmp;
tmp.setRect(clip);
return this->setPath(path, tmp, doAA);
}
bool SkRasterClip::op(const SkIRect& rect, SkRegion::Op op) {
AUTO_RASTERCLIP_VALIDATE(*this);
if (!fShader) {
fShader = sh;
} else {
fShader = SkShaders::Blend(SkBlendMode::kSrcIn, sh, fShader);
}
return !this->isEmpty();
fIsBW ? fBW.op(rect, op) : fAA.op(rect, op);
return this->updateCacheAndReturnNonEmpty();
}
bool SkRasterClip::op(const SkRasterClip& clip, SkClipOp op) {
bool SkRasterClip::op(const SkRegion& rgn, SkRegion::Op op) {
AUTO_RASTERCLIP_VALIDATE(*this);
if (fIsBW) {
(void)fBW.op(rgn, op);
} else {
SkAAClip tmp;
tmp.setRegion(rgn);
(void)fAA.op(tmp, op);
}
return this->updateCacheAndReturnNonEmpty();
}
bool SkRasterClip::op(const SkRasterClip& clip, SkRegion::Op op) {
AUTO_RASTERCLIP_VALIDATE(*this);
clip.validate();
if (this->isBW() && clip.isBW()) {
(void)fBW.op(clip.fBW, (SkRegion::Op) op);
(void)fBW.op(clip.fBW, op);
} else {
SkAAClip tmp;
const SkAAClip* other;
@ -239,7 +238,68 @@ bool SkRasterClip::op(const SkRasterClip& clip, SkClipOp op) {
} else {
other = &clip.aaRgn();
}
(void)fAA.op(*other, (SkRegion::Op) op);
(void)fAA.op(*other, op);
}
return this->updateCacheAndReturnNonEmpty();
}
bool SkRasterClip::op(sk_sp<SkShader> sh) {
AUTO_RASTERCLIP_VALIDATE(*this);
if (!fShader) {
fShader = sh;
} else {
fShader = SkShaders::Blend(SkBlendMode::kSrcIn, sh, fShader);
}
return !this->isEmpty();
}
/**
* Our antialiasing currently has a granularity of 1/4 of a pixel along each
* axis. Thus we can treat an axis coordinate as an integer if it differs
* from its nearest int by < half of that value (1.8 in this case).
*/
static bool nearly_integral(SkScalar x) {
static const SkScalar domain = SK_Scalar1 / 4;
static const SkScalar halfDomain = domain / 2;
x += halfDomain;
return x - SkScalarFloorToScalar(x) < domain;
}
bool SkRasterClip::op(const SkRect& localRect, const SkMatrix& matrix, const SkIRect& devBounds,
SkRegion::Op op, bool doAA) {
AUTO_RASTERCLIP_VALIDATE(*this);
SkRect devRect;
const bool isScaleTrans = matrix.isScaleTranslate();
if (!isScaleTrans) {
SkPath path;
path.addRect(localRect);
path.setIsVolatile(true);
return this->op(path, matrix, devBounds, op, doAA);
}
matrix.mapRect(&devRect, localRect);
if (fIsBW && doAA) {
// check that the rect really needs aa, or is it close enought to
// integer boundaries that we can just treat it as a BW rect?
if (nearly_integral(devRect.fLeft) && nearly_integral(devRect.fTop) &&
nearly_integral(devRect.fRight) && nearly_integral(devRect.fBottom)) {
doAA = false;
}
}
if (fIsBW && !doAA) {
SkIRect ir;
devRect.round(&ir);
(void)fBW.op(ir, op);
} else {
if (fIsBW) {
this->convertToAA();
}
(void)fAA.op(devRect, op, doAA);
}
return this->updateCacheAndReturnNonEmpty();
}
@ -271,6 +331,21 @@ void SkRasterClip::translate(int dx, int dy, SkRasterClip* dst) const {
dst->updateCacheAndReturnNonEmpty();
}
bool SkRasterClip::quickContains(const SkIRect& ir) const {
return fIsBW ? fBW.quickContains(ir) : fAA.quickContains(ir);
}
///////////////////////////////////////////////////////////////////////////////
const SkRegion& SkRasterClip::forceGetBW() {
AUTO_RASTERCLIP_VALIDATE(*this);
if (!fIsBW) {
fBW.setRect(fAA.getBounds());
}
return fBW;
}
void SkRasterClip::convertToAA() {
AUTO_RASTERCLIP_VALIDATE(*this);

View File

@ -8,7 +8,6 @@
#ifndef SkRasterClip_DEFINED
#define SkRasterClip_DEFINED
#include "include/core/SkClipOp.h"
#include "include/core/SkRegion.h"
#include "include/core/SkShader.h"
#include "include/private/SkMacros.h"
@ -19,19 +18,23 @@ class SkRRect;
/**
* Wraps a SkRegion and SkAAClip, so we have a single object that can represent either our
* BW or antialiased clips.
*
* This class is optimized for the raster backend of canvas, but can be expense to keep up2date,
* so it supports a runtime option (force-conservative-rects) to turn it into a super-fast
* rect-only tracker. The gpu backend uses this since it does not need the result (it uses
* SkClipStack instead).
*/
class SkRasterClip {
public:
SkRasterClip();
explicit SkRasterClip(const SkIRect&);
explicit SkRasterClip(const SkRegion&);
explicit SkRasterClip(const SkRasterClip&);
SkRasterClip(const SkPath& path, const SkIRect& bounds, bool doAA);
SkRasterClip(const SkIRect&);
SkRasterClip(const SkRegion&);
SkRasterClip(const SkRasterClip&);
SkRasterClip& operator=(const SkRasterClip&);
~SkRasterClip();
SkRasterClip& operator=(const SkRasterClip&);
// Only compares the current state. Does not compare isForceConservativeRects(), so that field
// could be different but this could still return true.
bool operator==(const SkRasterClip&) const;
bool operator!=(const SkRasterClip& other) const {
return !(*this == other);
@ -52,27 +55,27 @@ public:
return fIsRect;
}
bool isComplex() const {
return fIsBW ? fBW.isComplex() : !fAA.isEmpty();
}
const SkIRect& getBounds() const {
return fIsBW ? fBW.getBounds() : fAA.getBounds();
}
bool isComplex() const;
const SkIRect& getBounds() const;
bool setEmpty();
bool setRect(const SkIRect&);
bool op(const SkIRect&, SkClipOp);
bool op(const SkRegion&, SkClipOp);
bool op(const SkRect&, const SkMatrix& matrix, SkClipOp, bool doAA);
bool op(const SkRRect&, const SkMatrix& matrix, SkClipOp, bool doAA);
bool op(const SkPath&, const SkMatrix& matrix, SkClipOp, bool doAA);
bool op(const SkIRect&, SkRegion::Op);
bool op(const SkRegion&, SkRegion::Op);
bool op(const SkRect&, const SkMatrix& matrix, const SkIRect&, SkRegion::Op, bool doAA);
bool op(const SkRRect&, const SkMatrix& matrix, const SkIRect&, SkRegion::Op, bool doAA);
bool op(const SkPath&, const SkMatrix& matrix, const SkIRect&, SkRegion::Op, bool doAA);
bool op(sk_sp<SkShader>);
void translate(int dx, int dy, SkRasterClip* dst) const;
void translate(int dx, int dy) {
this->translate(dx, dy, this);
}
bool quickContains(const SkIRect& rect) const {
return fIsBW ? fBW.quickContains(rect) : fAA.quickContains(rect);
bool quickContains(const SkIRect& rect) const;
bool quickContains(int left, int top, int right, int bottom) const {
return quickContains(SkIRect::MakeLTRB(left, top, right, bottom));
}
/**
@ -84,6 +87,9 @@ public:
return !SkIRect::Intersects(this->getBounds(), rect);
}
// hack for SkCanvas::getTotalClip
const SkRegion& forceGetBW();
#ifdef SK_DEBUG
void validate() const;
#else
@ -126,7 +132,10 @@ private:
void convertToAA();
bool op(const SkRasterClip&, SkClipOp);
bool setPath(const SkPath& path, const SkRegion& clip, bool doAA);
bool setPath(const SkPath& path, const SkIRect& clip, bool doAA);
bool op(const SkRasterClip&, SkRegion::Op);
bool setConservativeRect(const SkRect& r, const SkIRect& clipR, bool isInverse);
};
class SkAutoRasterClipValidate : SkNoncopyable {

View File

@ -9,16 +9,68 @@
#define SkRasterClipStack_DEFINED
#include "include/core/SkClipOp.h"
#include "include/private/SkDeque.h"
#include "src/core/SkRasterClip.h"
#include "src/core/SkScan.h"
#include "src/core/SkTBlockList.h"
#include <new>
template <typename T> class SkTStack {
public:
SkTStack(void* storage, size_t size) : fDeque(sizeof(T), storage, size), fTop(nullptr) {}
~SkTStack() {
while (!fDeque.empty()) {
((T*)fDeque.back())->~T();
fDeque.pop_back();
}
}
bool empty() const { return fDeque.empty(); }
int count() const { return fDeque.count(); }
const T& top() const {
SkASSERT(fTop);
return *fTop;
}
T& top() {
SkASSERT(fTop);
return *fTop;
}
T* push_raw() { return (T*)fDeque.push_back(); }
T& push() {
fTop = this->push_raw();
new (fTop) T();
return *fTop;
}
T& push(const T& src) {
fTop = this->push_raw();
new (fTop) T(src);
return *fTop;
}
void pop() {
fTop->~T();
fDeque.pop_back();
fTop = fDeque.empty() ? nullptr : (T*)fDeque.back();
}
private:
SkDeque fDeque;
T* fTop;
};
class SkRasterClipStack : SkNoncopyable {
int fCounter = 0;
public:
SkRasterClipStack(int width, int height)
: fRootBounds(SkIRect::MakeWH(width, height))
, fDisableAA(SkScan::DowngradeClipAA(fRootBounds)) {
fStack.emplace_back(SkRasterClip(fRootBounds));
: fStack(fStorage, sizeof(fStorage))
, fRootBounds(SkIRect::MakeWH(width, height))
, fDisableAA(SkScan::DowngradeClipAA(fRootBounds)) {
Rec& rec = fStack.push();
rec.fRC.setRect(fRootBounds);
rec.fDeferredCount = 0;
SkASSERT(fStack.count() == 1);
}
@ -26,42 +78,43 @@ public:
fRootBounds.setXYWH(0, 0, w, h);
SkASSERT(fStack.count() == 1);
Rec& rec = fStack.back();
Rec& rec = fStack.top();
SkASSERT(rec.fDeferredCount == 0);
rec.fRC.setRect(fRootBounds);
}
const SkRasterClip& rc() const { return fStack.back().fRC; }
const SkRasterClip& rc() const { return fStack.top().fRC; }
void save() {
SkDEBUGCODE(fCounter += 1);
SkASSERT(fStack.back().fDeferredCount >= 0);
fStack.back().fDeferredCount += 1;
fCounter += 1;
SkASSERT(fStack.top().fDeferredCount >= 0);
fStack.top().fDeferredCount += 1;
}
void restore() {
SkDEBUGCODE(fCounter -= 1);
SkASSERT(fCounter >= 0);
if (--fStack.back().fDeferredCount < 0) {
SkASSERT(fStack.back().fDeferredCount == -1);
fCounter -= 1; SkASSERT(fCounter >= 0);
if (--fStack.top().fDeferredCount < 0) {
SkASSERT(fStack.top().fDeferredCount == -1);
SkASSERT(fStack.count() > 1);
fStack.pop_back();
fStack.pop();
}
}
void clipRect(const SkMatrix& ctm, const SkRect& rect, SkClipOp op, bool aa) {
this->writable_rc().op(rect, ctm, op, this->finalAA(aa));
this->writable_rc().op(rect, ctm, fRootBounds, (SkRegion::Op)op, this->finalAA(aa));
this->trimIfExpanding(op);
this->validate();
}
void clipRRect(const SkMatrix& ctm, const SkRRect& rrect, SkClipOp op, bool aa) {
this->writable_rc().op(rrect, ctm, op, this->finalAA(aa));
this->writable_rc().op(rrect, ctm, fRootBounds, (SkRegion::Op)op, this->finalAA(aa));
this->trimIfExpanding(op);
this->validate();
}
void clipPath(const SkMatrix& ctm, const SkPath& path, SkClipOp op, bool aa) {
this->writable_rc().op(path, ctm, op, this->finalAA(aa));
this->writable_rc().op(path, ctm, fRootBounds, (SkRegion::Op)op, this->finalAA(aa));
this->trimIfExpanding(op);
this->validate();
}
@ -71,7 +124,8 @@ public:
}
void clipRegion(const SkRegion& rgn, SkClipOp op) {
this->writable_rc().op(rgn, op);
this->writable_rc().op(rgn, (SkRegion::Op)op);
this->trimIfExpanding(op);
this->validate();
}
@ -97,27 +151,38 @@ public:
private:
struct Rec {
SkRasterClip fRC;
int fDeferredCount; // 0 for a "normal" entry
Rec(const SkRasterClip& rc) : fRC(rc), fDeferredCount(0) {}
SkRasterClip fRC;
int fDeferredCount; // 0 for a "normal" entry
};
SkTBlockList<Rec, 16> fStack;
SkIRect fRootBounds;
bool fDisableAA;
SkDEBUGCODE(int fCounter = 0);
enum {
ELEM_COUNT = 16,
PTR_COUNT = ELEM_COUNT * sizeof(Rec) / sizeof(void*)
};
void* fStorage[PTR_COUNT];
SkTStack<Rec> fStack;
SkIRect fRootBounds;
bool fDisableAA;
SkRasterClip& writable_rc() {
SkASSERT(fStack.back().fDeferredCount >= 0);
if (fStack.back().fDeferredCount > 0) {
fStack.back().fDeferredCount -= 1;
fStack.emplace_back(fStack.back().fRC);
SkASSERT(fStack.top().fDeferredCount >= 0);
if (fStack.top().fDeferredCount > 0) {
fStack.top().fDeferredCount -= 1;
fStack.push(fStack.top());
fStack.top().fDeferredCount = 0;
}
return fStack.back().fRC;
return fStack.top().fRC;
}
bool finalAA(bool aa) const { return aa && !fDisableAA; }
void trimIfExpanding(SkClipOp op) {
if ((int)op > (int)SkClipOp::kIntersect) {
Rec& rec = fStack.top();
SkASSERT(rec.fDeferredCount == 0);
rec.fRC.op(fRootBounds, SkRegion::kIntersect_Op);
}
}
};
#endif

View File

@ -356,6 +356,7 @@ static void test_really_a_rect(skiatest::Reporter* reporter) {
static void did_dx_affect(skiatest::Reporter* reporter, const SkScalar dx[],
size_t count, bool changed) {
const SkIRect baseBounds = SkIRect::MakeXYWH(0, 0, 10, 10);
SkIRect ir = { 0, 0, 10, 10 };
for (size_t i = 0; i < count; ++i) {
@ -366,11 +367,11 @@ static void did_dx_affect(skiatest::Reporter* reporter, const SkScalar dx[],
SkRasterClip rc1(ir);
SkRasterClip rc2(ir);
rc0.op(r, SkMatrix::I(), SkClipOp::kIntersect, false);
rc0.op(r, SkMatrix::I(), baseBounds, SkRegion::kIntersect_Op, false);
r.offset(dx[i], 0);
rc1.op(r, SkMatrix::I(), SkClipOp::kIntersect, true);
rc1.op(r, SkMatrix::I(), baseBounds, SkRegion::kIntersect_Op, true);
r.offset(-2*dx[i], 0);
rc2.op(r, SkMatrix::I(), SkClipOp::kIntersect, true);
rc2.op(r, SkMatrix::I(), baseBounds, SkRegion::kIntersect_Op, true);
REPORTER_ASSERT(reporter, changed != (rc0 == rc1));
REPORTER_ASSERT(reporter, changed != (rc0 == rc2));
@ -416,7 +417,7 @@ static void test_crbug_422693(skiatest::Reporter* reporter) {
SkRasterClip rc(SkIRect::MakeLTRB(-25000, -25000, 25000, 25000));
SkPath path;
path.addCircle(50, 50, 50);
rc.op(path, SkMatrix::I(), SkClipOp::kIntersect, true);
rc.op(path, SkMatrix::I(), rc.getBounds(), SkRegion::kIntersect_Op, true);
}
static void test_huge(skiatest::Reporter* reporter) {