13.2 区域
区域(region)是由若干几何形状所构成的一种封闭图形,主要用于复杂图形的绘制、图形输出的剪裁和鼠标击中的测试等。最简单也是最常用的区域是矩形,其次是椭圆和多边形以及它们的组合。这些也正是GDI所支持的区域类型。
GDI+中的区域是一种显示表面的范围(an area of the display surface),可以是任意形状(的图形的组合),边界一般为路径。除了上面所讲的矩形、椭圆、多边形之外,其边界还可以含直线、折线、弧、贝塞尔曲线和基样条曲线等开图形,其内容还可以包含饼、闭贝塞尔曲线和基样条曲线等闭图形。
在GDI+中,区域所对应的类是Region,它是一个独立的类(没有基类,也没有派生类)。但是它有若干相关的类,如各种图形类和图形路径类等。
13.2.1 构造函数
Region类有6个构造函数:
Region(VOID); // 创建一个空区域
Region(const Rect &rect); // 创建一个整数型矩形区域 Region(const RectF &rect); // 创建一个浮点数型矩形区域 Region(const GraphicsPath *path); // 由图形路径来创建区域
Region(const BYTE *regionData, INT size); // 由(另一)区域的数据构造区域 Region(HRGN hRgn); // 由GDI的区域句柄构造区域
其中,创建矩形区域最简单,由路径创建区域最常用。
13.2.2 其他方法
区域类的其他方法有(其中的图形对象g也用于坐标计算):
Region *Clone(VOID); // 克隆(复制) // 判断本区域是否与另一区域region相等:
BOOL Equals(const Region *region, const Graphics *g) const; BOOL IsEmpty(const Graphics *g) const; // 判断区域是否为空 Status MakeEmpty(VOID); // 清空区域
11
BOOL IsInfinite(const Graphics *g) const; // 判断区域是否无限 Status MakeInfinite(VOID); // 使区域成为无限的
Status GetBounds(Rect *rect, const Graphics *g) const; //获取外接(包)矩形 Status GetBounds(RectF *rect, const Graphics *g) const; //获取外接(包)浮点矩形 UINT GetDataSize(VOID) const; // 获取区域数据的字节大小 // 获取区域的二进制数据,可以用于创建其他区域:
Status GetData(BYTE *buffer, UINT bufferSize, UINT *sizeFilled = NULL) const; UINT GetRegionScansCount(const Matrix *matrix) const; // 获取区域的矩形逼近表示 Status GetRegionScans(const Matrix *matrix, Rect *rects, INT *count) const; Status GetRegionScans(const Matrix *matrix, RectF *rects, INT *count) const; Status Translate(INT dx, INT dy); // 平移区域 Status Translate(REAL dx, REAL dy);
Status Transform(const Matrix *matrix); // 变换区域
Status GetLastStatus(VOID); // 获取最后一次区域操作的状态,成功返回Ok HRGN GetHRGN(const Graphics *g) const; // 获取区域所对应的GDI区域的句柄 区域类还有集合运算和区域测试等方法,将在下面分别介绍。
13.2.3 区域的集合运算
? 并(union)
Status Union(const Region *region); // 与另一区域的并
Status Union(const Rect[F] &rect); // 与另一整数[浮点数]型矩形的并 Status Union(const GraphicsPath *path); // 与另一路径的并 ? 交(intersect):Status Intersect(重载和参数同并的); ? 余(exclude):Status Exclude(重载和参数同并的); ? 补(complement):Status Complement(重载和参数同并的); ? 异或(xor):Status Xor(重载和参数同并的); 例如(参见图13-7):
SolidBrush textBrush(Color::Black); Font font(L\宋体\
12
StringFormat stringFormat;
stringFormat.SetAlignment(StringAlignmentCenter); Point points[] = {Point(100, 0), Point(122, 69),
Point(195, 69), Point(137, 111), Point(159, 181), Point(100, 138), Point(41, 181), Point(63, 111), Point(5, 69), Point(78, 69)};
GraphicsPath path;
path.AddPolygon(points, 10);
Region *pRgn, rgnRect(Rect(0, 50, 200, 80)); SolidBrush *pBrush, gbrush(Color::Green),
tbrush(Color::Turquoise);
CString strs[] = {L\原集\并集\交集\余集\补集\
L\异或集\
PointF pts[] = {PointF(0.0f, 0.0f), PointF(210.0f, 0.0f),
PointF(210.0f, 0.0f), PointF(-420.0f, 220.0f), PointF(210.0f, 0.0f), PointF(210.0f, 0.0f)};
Graphics graph(pDC->m_hDC); for (int i = 0; i < 6; i++) {
pRgn = new Region(&path); switch (i) { }
if (i == 0) pBrush = &gbrush; else pBrush = &tbrush; graph.TranslateTransform(pts[i].X, pts[i].Y); graph.FillRegion(pBrush, pRgn); if (i == 0) graph.FillRegion(
13
case 1: pRgn->Union(&rgnRect); break; case 2: pRgn->Intersect(&rgnRect); break; case 3: pRgn->Exclude(&rgnRect); break; case 4: pRgn->Complement(&rgnRect); break; case 5: pRgn->Xor(&rgnRect); break;
}
&SolidBrush (Color(128, 255, 0, 0)), &rgnRect);
graph.DrawString(strs[i], -1, &font, PointF(100, 190),
&stringFormat, &textBrush);
delete pRgn;
图13-7 区域的集合运算
又例如(抠图,参见图13-8。注意:图像不能太大,背景必须为单色):
Bitmap bmp(L\米老鼠和唐老鸭.bmp\Graphics graph(pDC->m_hDC); graph.DrawImage(&bmp, 0, 0);
int w = bmp.GetWidth(), h = bmp.GetHeight(); Region rgn(Rect(0, 0, w, h)); Color col, col0;
bmp.GetPixel(0, 0, &col0); // 获取背景色 ARGB argb0 = col0.GetValue(); for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
bmp.GetPixel(i, j, &col); if (col.GetValue() == argb0) {
Region pixelRgn(Rect(i, j, 1, 1)); // 单像素矩形 rgn.Exclude(&pixelRgn); // 挖去
14
}
}
}
graph.TranslateTransform(REAL(w), 0.0f);
graph.FillRegion(&SolidBrush(Color::Green), &rgn); // 容易溢出
图13-8 抠图
为了防止调用FillRegion方法造成堆栈溢出,必须减少区域的复杂度。解决办法之一是,不再每次去一个点,而是一次去一个(垂)直线段,则复杂度会减少一到两个数量级。从而可以避免溢出,代码大家可以自己去写。
13.2.4 击中测试
区域类的下列方法用于复杂图形的击中测试(其中的图形对象g用于坐标计算):
// 点是否在区域内
BOOL IsVisible(INT x, INT y, const Graphics *g) const; // 整数点坐标 BOOL IsVisible(const Point &point, const Graphics *g) const; // 整数点 BOOL IsVisible(REAL x, REAL y, const Graphics *g) const; // 浮点数点坐标 BOOL IsVisible(const PointF &point, const Graphics *g) const; // 浮点数点 // 矩形是否在区域内
// 整数左上角坐标与高宽
BOOL IsVisible(INT x, INT y, INT width, INT height, const Graphics *g) const; BOOL IsVisible(const Rect &rect, const Graphics *g) const; // 整数矩阵
// 浮点数左上角坐标与高宽
BOOL IsVisible(REAL x, REAL y, REAL width, REAL height, const Graphics *g) const; BOOL IsVisible(const RectF &rect, const Graphics *g) const; // 浮点矩阵
15