a766ca9af1
Will work next to try to make isEmpty() private Bug: skia:7470 Bug:799715 Change-Id: I7b43028ecd86dca68e0c67225712516d2f2f88a2 Reviewed-on: https://skia-review.googlesource.com/92620 Commit-Queue: Mike Reed <reed@google.com> Reviewed-by: Brian Salomon <bsalomon@google.com>
465 lines
15 KiB
C++
465 lines
15 KiB
C++
|
|
/*
|
|
* Copyright 2005 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.
|
|
*/
|
|
|
|
|
|
#ifndef SkRegion_DEFINED
|
|
#define SkRegion_DEFINED
|
|
|
|
#include "SkRect.h"
|
|
|
|
class SkPath;
|
|
class SkRgnBuilder;
|
|
|
|
namespace android {
|
|
class Region;
|
|
}
|
|
|
|
#define SkRegion_gEmptyRunHeadPtr ((SkRegion::RunHead*)-1)
|
|
#define SkRegion_gRectRunHeadPtr nullptr
|
|
|
|
/** \class SkRegion
|
|
|
|
The SkRegion class encapsulates the geometric region used to specify
|
|
clipping areas for drawing.
|
|
*/
|
|
class SK_API SkRegion {
|
|
public:
|
|
typedef int32_t RunType;
|
|
enum {
|
|
kRunTypeSentinel = 0x7FFFFFFF
|
|
};
|
|
|
|
SkRegion();
|
|
SkRegion(const SkRegion&);
|
|
explicit SkRegion(const SkIRect&);
|
|
~SkRegion();
|
|
|
|
SkRegion& operator=(const SkRegion&);
|
|
|
|
/**
|
|
* Return true if the two regions are equal. i.e. The enclose exactly
|
|
* the same area.
|
|
*/
|
|
bool operator==(const SkRegion& other) const;
|
|
|
|
/**
|
|
* Return true if the two regions are not equal.
|
|
*/
|
|
bool operator!=(const SkRegion& other) const {
|
|
return !(*this == other);
|
|
}
|
|
|
|
/**
|
|
* Replace this region with the specified region, and return true if the
|
|
* resulting region is non-empty.
|
|
*/
|
|
bool set(const SkRegion& src) {
|
|
*this = src;
|
|
return !this->isEmpty();
|
|
}
|
|
|
|
/**
|
|
* Swap the contents of this and the specified region. This operation
|
|
* is gauarenteed to never fail.
|
|
*/
|
|
void swap(SkRegion&);
|
|
|
|
/** Return true if this region is empty */
|
|
bool isEmpty() const { return fRunHead == SkRegion_gEmptyRunHeadPtr; }
|
|
|
|
/** Return true if this region is a single, non-empty rectangle */
|
|
bool isRect() const { return fRunHead == SkRegion_gRectRunHeadPtr; }
|
|
|
|
/** Return true if this region consists of more than 1 rectangular area */
|
|
bool isComplex() const { return !this->isEmpty() && !this->isRect(); }
|
|
|
|
/**
|
|
* Return the bounds of this region. If the region is empty, returns an
|
|
* empty rectangle.
|
|
*/
|
|
const SkIRect& getBounds() const { return fBounds; }
|
|
|
|
/**
|
|
* Returns a value that grows approximately linearly with the number of
|
|
* intervals comprised in the region. Empty region will return 0, Rect
|
|
* will return 1, Complex will return a value > 1.
|
|
*
|
|
* Use this to compare two regions, where the larger count likely
|
|
* indicates a more complex region.
|
|
*/
|
|
int computeRegionComplexity() const;
|
|
|
|
/**
|
|
* Returns true if the region is non-empty, and if so, appends the
|
|
* boundary(s) of the region to the specified path.
|
|
* If the region is empty, returns false, and path is left unmodified.
|
|
*/
|
|
bool getBoundaryPath(SkPath* path) const;
|
|
|
|
/**
|
|
* Set the region to be empty, and return false, since the resulting
|
|
* region is empty
|
|
*/
|
|
bool setEmpty();
|
|
|
|
/**
|
|
* If rect is non-empty, set this region to that rectangle and return true,
|
|
* otherwise set this region to empty and return false.
|
|
*/
|
|
bool setRect(const SkIRect&);
|
|
|
|
/**
|
|
* If left < right and top < bottom, set this region to that rectangle and
|
|
* return true, otherwise set this region to empty and return false.
|
|
*/
|
|
bool setRect(int32_t left, int32_t top, int32_t right, int32_t bottom) {
|
|
return this->setRect({ left, top, right, bottom });
|
|
}
|
|
|
|
/**
|
|
* Set this region to the union of an array of rects. This is generally
|
|
* faster than calling region.op(rect, kUnion_Op) in a loop. If count is
|
|
* 0, then this region is set to the empty region.
|
|
* @return true if the resulting region is non-empty
|
|
*/
|
|
bool setRects(const SkIRect rects[], int count);
|
|
|
|
/**
|
|
* Set this region to the specified region, and return true if it is
|
|
* non-empty.
|
|
*/
|
|
bool setRegion(const SkRegion&);
|
|
|
|
/**
|
|
* Set this region to the area described by the path, clipped.
|
|
* Return true if the resulting region is non-empty.
|
|
* This produces a region that is identical to the pixels that would be
|
|
* drawn by the path (with no antialiasing) with the specified clip.
|
|
*/
|
|
bool setPath(const SkPath&, const SkRegion& clip);
|
|
|
|
/**
|
|
* Returns true if the specified rectangle has a non-empty intersection
|
|
* with this region.
|
|
*/
|
|
bool intersects(const SkIRect&) const;
|
|
|
|
/**
|
|
* Returns true if the specified region has a non-empty intersection
|
|
* with this region.
|
|
*/
|
|
bool intersects(const SkRegion&) const;
|
|
|
|
/**
|
|
* Return true if the specified x,y coordinate is inside the region.
|
|
*/
|
|
bool contains(int32_t x, int32_t y) const;
|
|
|
|
/**
|
|
* Return true if the specified rectangle is completely inside the region.
|
|
* This works for simple (rectangular) and complex regions, and always
|
|
* returns the correct result. Note: if either this region or the rectangle
|
|
* is empty, contains() returns false.
|
|
*/
|
|
bool contains(const SkIRect&) const;
|
|
|
|
/**
|
|
* Return true if the specified region is completely inside the region.
|
|
* This works for simple (rectangular) and complex regions, and always
|
|
* returns the correct result. Note: if either region is empty, contains()
|
|
* returns false.
|
|
*/
|
|
bool contains(const SkRegion&) const;
|
|
|
|
/**
|
|
* Return true if this region is a single rectangle (not complex) and the
|
|
* specified rectangle is contained by this region. Returning false is not
|
|
* a guarantee that the rectangle is not contained by this region, but
|
|
* return true is a guarantee that the rectangle is contained by this region.
|
|
*/
|
|
bool quickContains(const SkIRect& r) const {
|
|
return this->quickContains(r.fLeft, r.fTop, r.fRight, r.fBottom);
|
|
}
|
|
|
|
/**
|
|
* Return true if this region is a single rectangle (not complex) and the
|
|
* specified rectangle is contained by this region. Returning false is not
|
|
* a guarantee that the rectangle is not contained by this region, but
|
|
* return true is a guarantee that the rectangle is contained by this
|
|
* region.
|
|
*/
|
|
bool quickContains(int32_t left, int32_t top, int32_t right,
|
|
int32_t bottom) const {
|
|
SkASSERT(this->isEmpty() == fBounds.isEmpty()); // valid region
|
|
|
|
return left < right && top < bottom &&
|
|
fRunHead == SkRegion_gRectRunHeadPtr && // this->isRect()
|
|
/* fBounds.contains(left, top, right, bottom); */
|
|
fBounds.fLeft <= left && fBounds.fTop <= top &&
|
|
fBounds.fRight >= right && fBounds.fBottom >= bottom;
|
|
}
|
|
|
|
/**
|
|
* Return true if this region is empty, or if the specified rectangle does
|
|
* not intersect the region. Returning false is not a guarantee that they
|
|
* intersect, but returning true is a guarantee that they do not.
|
|
*/
|
|
bool quickReject(const SkIRect& rect) const {
|
|
return this->isEmpty() || rect.isEmpty() ||
|
|
!SkIRect::Intersects(fBounds, rect);
|
|
}
|
|
|
|
/**
|
|
* Return true if this region, or rgn, is empty, or if their bounds do not
|
|
* intersect. Returning false is not a guarantee that they intersect, but
|
|
* returning true is a guarantee that they do not.
|
|
*/
|
|
bool quickReject(const SkRegion& rgn) const {
|
|
return this->isEmpty() || rgn.isEmpty() ||
|
|
!SkIRect::Intersects(fBounds, rgn.fBounds);
|
|
}
|
|
|
|
/** Translate the region by the specified (dx, dy) amount. */
|
|
void translate(int dx, int dy) { this->translate(dx, dy, this); }
|
|
|
|
/**
|
|
* Translate the region by the specified (dx, dy) amount, writing the
|
|
* resulting region into dst. Note: it is legal to pass this region as the
|
|
* dst parameter, effectively translating the region in place. If dst is
|
|
* null, nothing happens.
|
|
*/
|
|
void translate(int dx, int dy, SkRegion* dst) const;
|
|
|
|
/**
|
|
* The logical operations that can be performed when combining two regions.
|
|
*/
|
|
enum Op {
|
|
kDifference_Op, //!< subtract the op region from the first region
|
|
kIntersect_Op, //!< intersect the two regions
|
|
kUnion_Op, //!< union (inclusive-or) the two regions
|
|
kXOR_Op, //!< exclusive-or the two regions
|
|
/** subtract the first region from the op region */
|
|
kReverseDifference_Op,
|
|
kReplace_Op, //!< replace the dst region with the op region
|
|
|
|
kLastOp = kReplace_Op
|
|
};
|
|
|
|
static const int kOpCnt = kLastOp + 1;
|
|
|
|
/**
|
|
* Set this region to the result of applying the Op to this region and the
|
|
* specified rectangle: this = (this op rect).
|
|
* Return true if the resulting region is non-empty.
|
|
*/
|
|
bool op(const SkIRect& rect, Op op) {
|
|
if (this->isRect() && kIntersect_Op == op) {
|
|
if (!fBounds.intersect(rect)) {
|
|
return this->setEmpty();
|
|
}
|
|
return true;
|
|
}
|
|
return this->op(*this, rect, op);
|
|
}
|
|
|
|
/**
|
|
* Set this region to the result of applying the Op to this region and the
|
|
* specified rectangle: this = (this op rect).
|
|
* Return true if the resulting region is non-empty.
|
|
*/
|
|
bool op(int left, int top, int right, int bottom, Op op) {
|
|
SkIRect rect;
|
|
rect.set(left, top, right, bottom);
|
|
return this->op(*this, rect, op);
|
|
}
|
|
|
|
/**
|
|
* Set this region to the result of applying the Op to this region and the
|
|
* specified region: this = (this op rgn).
|
|
* Return true if the resulting region is non-empty.
|
|
*/
|
|
bool op(const SkRegion& rgn, Op op) { return this->op(*this, rgn, op); }
|
|
|
|
/**
|
|
* Set this region to the result of applying the Op to the specified
|
|
* rectangle and region: this = (rect op rgn).
|
|
* Return true if the resulting region is non-empty.
|
|
*/
|
|
bool op(const SkIRect& rect, const SkRegion& rgn, Op);
|
|
|
|
/**
|
|
* Set this region to the result of applying the Op to the specified
|
|
* region and rectangle: this = (rgn op rect).
|
|
* Return true if the resulting region is non-empty.
|
|
*/
|
|
bool op(const SkRegion& rgn, const SkIRect& rect, Op);
|
|
|
|
/**
|
|
* Set this region to the result of applying the Op to the specified
|
|
* regions: this = (rgna op rgnb).
|
|
* Return true if the resulting region is non-empty.
|
|
*/
|
|
bool op(const SkRegion& rgna, const SkRegion& rgnb, Op op);
|
|
|
|
#ifdef SK_BUILD_FOR_ANDROID
|
|
/** Returns a new char* containing the list of rectangles in this region
|
|
*/
|
|
char* toString();
|
|
#endif
|
|
|
|
/**
|
|
* Returns the sequence of rectangles, sorted in Y and X, that make up
|
|
* this region.
|
|
*/
|
|
class SK_API Iterator {
|
|
public:
|
|
Iterator() : fRgn(nullptr), fDone(true) {}
|
|
Iterator(const SkRegion&);
|
|
// if we have a region, reset to it and return true, else return false
|
|
bool rewind();
|
|
// reset the iterator, using the new region
|
|
void reset(const SkRegion&);
|
|
bool done() const { return fDone; }
|
|
void next();
|
|
const SkIRect& rect() const { return fRect; }
|
|
// may return null
|
|
const SkRegion* rgn() const { return fRgn; }
|
|
|
|
private:
|
|
const SkRegion* fRgn;
|
|
const RunType* fRuns;
|
|
SkIRect fRect;
|
|
bool fDone;
|
|
};
|
|
|
|
/**
|
|
* Returns the sequence of rectangles, sorted in Y and X, that make up
|
|
* this region intersected with the specified clip rectangle.
|
|
*/
|
|
class SK_API Cliperator {
|
|
public:
|
|
Cliperator(const SkRegion&, const SkIRect& clip);
|
|
bool done() { return fDone; }
|
|
void next();
|
|
const SkIRect& rect() const { return fRect; }
|
|
|
|
private:
|
|
Iterator fIter;
|
|
SkIRect fClip;
|
|
SkIRect fRect;
|
|
bool fDone;
|
|
};
|
|
|
|
/**
|
|
* Returns the sequence of runs that make up this region for the specified
|
|
* Y scanline, clipped to the specified left and right X values.
|
|
*/
|
|
class Spanerator {
|
|
public:
|
|
Spanerator(const SkRegion&, int y, int left, int right);
|
|
bool next(int* left, int* right);
|
|
|
|
private:
|
|
const SkRegion::RunType* fRuns;
|
|
int fLeft, fRight;
|
|
bool fDone;
|
|
};
|
|
|
|
/**
|
|
* Write the region to the buffer, and return the number of bytes written.
|
|
* If buffer is NULL, it still returns the number of bytes.
|
|
*/
|
|
size_t writeToMemory(void* buffer) const;
|
|
/**
|
|
* Initializes the region from the buffer
|
|
*
|
|
* @param buffer Memory to read from
|
|
* @param length Amount of memory available in the buffer
|
|
* @return number of bytes read (must be a multiple of 4) or
|
|
* 0 if there was not enough memory available
|
|
*/
|
|
size_t readFromMemory(const void* buffer, size_t length);
|
|
|
|
/**
|
|
* Returns a reference to a global empty region. Just a convenience for
|
|
* callers that need a const empty region.
|
|
*/
|
|
static const SkRegion& GetEmptyRegion();
|
|
|
|
SkDEBUGCODE(void dump() const;)
|
|
SkDEBUGCODE(void validate() const;)
|
|
SkDEBUGCODE(static void UnitTest();)
|
|
|
|
// expose this to allow for regression test on complex regions
|
|
SkDEBUGCODE(bool debugSetRuns(const RunType runs[], int count);)
|
|
|
|
private:
|
|
enum {
|
|
kOpCount = kReplace_Op + 1
|
|
};
|
|
|
|
enum {
|
|
// T
|
|
// [B N L R S]
|
|
// S
|
|
kRectRegionRuns = 7
|
|
};
|
|
|
|
friend class android::Region; // needed for marshalling efficiently
|
|
|
|
struct RunHead;
|
|
|
|
// allocate space for count runs
|
|
void allocateRuns(int count);
|
|
void allocateRuns(int count, int ySpanCount, int intervalCount);
|
|
void allocateRuns(const RunHead& src);
|
|
|
|
SkIRect fBounds;
|
|
RunHead* fRunHead;
|
|
|
|
void freeRuns();
|
|
|
|
/**
|
|
* Return the runs from this region, consing up fake runs if the region
|
|
* is empty or a rect. In those 2 cases, we use tmpStorage to hold the
|
|
* run data.
|
|
*/
|
|
const RunType* getRuns(RunType tmpStorage[], int* intervals) const;
|
|
|
|
// This is called with runs[] that do not yet have their interval-count
|
|
// field set on each scanline. That is computed as part of this call
|
|
// (inside ComputeRunBounds).
|
|
bool setRuns(RunType runs[], int count);
|
|
|
|
int count_runtype_values(int* itop, int* ibot) const;
|
|
|
|
bool isValid() const;
|
|
|
|
static void BuildRectRuns(const SkIRect& bounds,
|
|
RunType runs[kRectRegionRuns]);
|
|
|
|
// If the runs define a simple rect, return true and set bounds to that
|
|
// rect. If not, return false and ignore bounds.
|
|
static bool RunsAreARect(const SkRegion::RunType runs[], int count,
|
|
SkIRect* bounds);
|
|
|
|
/**
|
|
* If the last arg is null, just return if the result is non-empty,
|
|
* else store the result in the last arg.
|
|
*/
|
|
static bool Oper(const SkRegion&, const SkRegion&, SkRegion::Op, SkRegion*);
|
|
|
|
friend struct RunHead;
|
|
friend class Iterator;
|
|
friend class Spanerator;
|
|
friend class SkRgnBuilder;
|
|
friend class SkFlatRegion;
|
|
};
|
|
|
|
#endif
|