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