C++程序  |  353行  |  12.46 KB

/*
 * Copyright (C) 2005 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#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    0

/** \class SkRegion

    The SkRegion class encapsulates the geometric region used to specify
    clipping areas for drawing.
*/
class SkRegion {
public:
    typedef int32_t RunType;
    enum {
        kRunTypeSentinel = 0x7FFFFFFF
    };
    
    SkRegion();
    SkRegion(const SkRegion&);
    explicit SkRegion(const SkIRect&);
    ~SkRegion();

    SkRegion& operator=(const SkRegion&);
    
    friend int operator==(const SkRegion& a, const SkRegion& b);
    friend int operator!=(const SkRegion& a, const SkRegion& b) {
        return !(a == b);
    }
    
    /** Replace this region with the specified region, and return true if the
        resulting region is non-empty.
    */
    bool set(const SkRegion& src) {
        SkASSERT(&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 true if the region is non-empty, and if so, sets the specified
        path to the boundary(s) of the region.
    */
    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);

    /** 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
    };
    
    /** 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) { 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);

    /** Returns the sequence of rectangles, sorted in Y and X, that make up
        this region.
    */
    class Iterator {
    public:
        Iterator() : fRgn(NULL), 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() { return fDone; }
        void next();
        const SkIRect& rect() const { return fRect; }

    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 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.
    */
    uint32_t flatten(void* buffer) const;
    /** Initialized the region from the buffer, returning the number
        of bytes actually read.
    */
    uint32_t unflatten(const void* buffer);
    
    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 {
        kRectRegionRuns = 6 // need to store a region of a rect [T B L R S S]        
    };

    friend class android::Region;    // needed for marshalling efficiently
    void allocateRuns(int count); // allocate space for count runs

    struct RunHead;

    SkIRect     fBounds;
    RunHead*    fRunHead;

    void            freeRuns();
    const RunType*  getRuns(RunType tmpStorage[], int* count) const;
    bool            setRuns(RunType runs[], int count);

    int count_runtype_values(int* itop, int* ibot) const;
    
    static void BuildRectRuns(const SkIRect& bounds,
                              RunType runs[kRectRegionRuns]);
    // returns true if runs are just a rect
    static bool ComputeRunBounds(const RunType runs[], int count,
                                 SkIRect* bounds);

    friend struct RunHead;
    friend class Iterator;
    friend class Spanerator;
    friend class SkRgnBuilder;
    friend class SkFlatRegion;
};


#endif