SkRegion
========
*Regions - set operations with rectangles*
<!-- Updated Mar 4, 2011 -->
Regions are a highly compressed way to represent (integer) areas. Skia
uses them to represent (internally) the current clip on the
canvas. Regions take their inspiration from the data type with the
same name on the original Macintosh (thank you Bill).
Regions are opaque structures, but they can be queried via
iterators. Best of all, they can be combined with other regions and
with rectangles (which can be thought of as "simple" regions. If you
remember Set operations from math class (intersection, union,
difference, etc.), then you're all ready to use regions.
<!--?prettify lang=cc?-->
bool SkRegion::isEmpty();
bool SkRegion::isRect();
bool SkRegion::isComplex();
Regions can be classified into one of three types: empty, rectangular,
or complex.
Empty regions are just that, empty. All empty regions are equal (using
operator==). Compare this to rectangles (SkRect or SkIRect). Any
rectangle with fLeft >= fRight or fTop >= fBottom is consider empty,
but clearly there are different empty rectangles that are not equal.
<!--?prettify lang=cc?-->
SkRect a = { 0, 0, 0, 0 };
SkRect b = { 1, 1, 1, 1 };
Both a and b are empty, but they are definitely not equal to each
other. However, with regions, all empty regions are equal. If you
query its bounds, you will always get { 0, 0, 0, 0 }. Even if you
translate it, it will still be all zeros.
<!--?prettify lang=cc?-->
<!--?prettify lang=cc?-->
SkRegion a, b; // regions default to empty
assert(a == b);
a.offset(10, 20);
assert(a == b);
assert(a.getBounds() == { 0, 0, 0, 0 }); // not legal C++, but you get the point
assert(b.getBounds() == { 0, 0, 0, 0 });
To initialize a region to something more interesting, use one of the
set() methods
<!--?prettify lang=cc?-->
SkRegion a, b;
a.setRect(10, 10, 50, 50);
b.setRect(rect); // see SkIRect
c.setPath(path); // see SkPath
This is the first step that SkCanvas performs when one of its
clip...() methods are called. The clip data is first transformed into
device coordinates (see SkMatrix), and then a region is build from the
data (either a rect or a path). The final step is to combine this new
region with the existing clip using the specified operator.
<!--?prettify lang=cc?-->
enum Op {
kUnion_Op,
kIntersect_Op,
kDifference_Op,
kXor_Op,
kReverseDifference_Op,
kReplace_Op
};
By default, intersect op is used when a clip call is made, but the
other operators are equally valid.
<!--?prettify lang=cc?-->
// returns true if the resulting clip is non-empty (i.e. drawing can
// still occur)
bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
SkRegion rgn;
// peek at the CTM (current transformation matrix on the canvas)
const SkMatrix& m = this->getTotalMatrix();
if (m.rectStaysRect()) { // check if a transformed rect can be
// represented as another rect
SkRect deviceRect;
m.mapRect(&deviceRect, rect);
SkIRect intRect;
deviceRect.round(&intRect);
rgn.setRect(intRect);
} else { // matrix rotates or skew (or is perspective)
SkPath path;
path.addRect(rect);
path.transform(m);
rgn.setPath(path);
}
// now combine the new region with the current one, using the specified *op*
return fCurrentClip.op(rgn, op);
}