在GDI+中,对于区域的部分基本上使用了GDI的区域构造函数与合并方法,所以我们先看看GDI中的区域是如何构造与操作的。
一、基本函数
创建矩形区域:
HRGN CreateRectRgnIndirect(CONST RECT *lprc); HRGN CreateRectRgn( int nLeftRect, // left点坐标(X) int nTopRect, //top点坐标值(Y) int nRightRect, //right点坐标值(X) int nBottomRect //bottom点坐标值(Y) );创建椭圆区域:
HRGN CreateEllipticRgnIndirect( CONST RECT *lprc // bounding rectangle ); HRGN CreateEllipticRgn( int nLeftRect, // x-coord of upper-left corner of rectangle int nTopRect, // y-coord of upper-left corner of rectangle int nRightRect, // x-coord of lower-right corner of rectangle int nBottomRect // y-coord of lower-right corner of rectangle );创建多边形区域:
HRGN CreatePolygonRgn( CONST POINT *lppt, // array of points int cPoints, // number of points in array int fnPolyFillMode // polygon-filling mode );合并区域:
int CombineRgn( HRGN hrgnDest, // handle to destination region HRGN hrgnSrc1, // handle to source region HRGN hrgnSrc2, // handle to source region int fnCombineMode // region combining mode );对于CombinMode有四个取值:
区域的句柄可用的4个绘图函数:
FillRgn(hdc, hRgn, hBrush);// 类似FillRect FrameRgn(hdc, hRgn, hBrush, xFrame, yFrame);// 类似FrameRect InverRgn(hdc, hRgn); //类似InvertRect PaintRgn(hdc, hRgn);// 用设备描述表中的当前画刷填充所指的区域。所有这些函数都假设区域是逻辑坐标定义的
DeleteObject(hRgn); //Windows的2个作用于区域而不是矩形的函数 InvalidateRgn(hwnd, hRgn, bErase); //类似于InvalidateRect ValidateRgn(hwnd, hRgn); //类似于ValidateRect上面两个函数也会以WM_PAINT消息作出反应
SelectObject(hdc, hRgn); SelectClipRgn(hdc, hRgn);上面两个函数都是将一个区域选进设备描述表来创建自己的剪裁区域,这个剪裁区域使用设备坐标。
ExcludeClipRect 用于将一个矩形从剪裁区域里排除掉 IntersectClipRect 用于创建一个新的剪裁区域,他是前一个剪裁区域与一个矩形的交集 OffsetClipRgn 用于将剪裁区域移动到客户区的另一部分注意:
看在GDI中使用区域函数的一个示例:(区域抠图)
原理:先用图片大小的矩形构造一个目的区域,所以这个区域的大小就是整个图片的大小,然后从图片的左上角(0,0)开始,按照从上到下、从左到右的顺序形成长宽都为1的矩形(就是一个像素大小),如果矩形中的背景是白色,将之从目的区域中去除,所以最终剩下的区域就是主画面的区域了。这就是“抠”出主界面区域的关键思路所在。
先看下原来的图片:
抠图代码:
Bitmap photo(L"wlh.bmp"); //得到相框尺寸 INT iWidth=photo.GetWidth(); INT iHeight=photo.GetHeight(); graphics.DrawImage(&photo,0,0,iWidth,iHeight); //将绘图平面右移,设置新的绘图原点 graphics.TranslateTransform(iWidth+10,0); Color color,colorTemp; HRGN endRgn=CreateRectRgn(0,0,iWidth,iHeight); //依次获取相框的每一个相素 for(int iRow=0;iRow<iHeight;iRow++){ for(int iColumn=0;iColumn<iWidth;iColumn++){ photo.GetPixel(iColumn,iRow,&color); //如果像素为白色,从原有区域中去除当前区域点 if(color.GetR()==255&&color.GetG()==255&&color.GetB()==255){ HRGN tempRgn=CreateRectRgn(iColumn,iRow,iColumn+1,iRow+1); CombineRgn(endRgn,tempRgn,endRgn,RGN_XOR); }//if--end } } //创建GDI+区域变量 Region fillrgn(endRgn); graphics.FillRegion(&SolidBrush(Color::Green),&fillrgn);
一、构造函数(Region):
Region() Region(path) //从路径构建 Region(hRgn) //从GDI中的HRGN句柄构建 Region(Rect& rect) //从矩形构建 Region(RectF& rect) Region(regionData, size) //使用区域数组信息创建这里有两个非常重要的构建函数,要说明一下:
Region(hRgn) //从GDI中的HRGN句柄构建 Region(Rect& rect) //从矩形构建对于Region(hRgn):我们很多时候,可以用GDI中区域函数构建区域;然后使用这个构建函数,构建Regin变量,然后使用GDI+中的函数fillRegion啥啥的;
二、区域操作(构建区域)
对于GDI中的操作,我们知道CombineRgn的最后一个参数,可以实现对区域的RGN_AND、RGN_DIFF、RGN_OR、RGN_XOR
在GDI+中,Regin类中,也有对区域的操作函数,同样实现了GDI中的区域操作功能,它们分别是:
Region::Intersect(region)//求区域A和区域B的共有部分(交集) Region::Union(region)//求同时包含区域A和区域B的区域(并集) Region::Xor(region)//求不包含区域A和区域B相交部分的区域(异并集,又称为对称差) Region::Complement(region)//求区域B中不含区域A的区域(A的补集) Region::Exclude(region)//求区域A中不含区域B的区域(B的补集) //异并集(集合的对称差):设A、B为任意两个集合,A和B的对称差为集合S,其元素或属于A,或属于B,但不能即属于A又属于B,这样的集合S称为集合A与B的对称差
简单的看一个示例吧,(异并集的)
Region rgn1(RectF(25,10,50,100)); Region rgn2(RectF(0,50,100,30)); Pen pen1(Color::Red,2); Pen pen2(Color::Blue,2); SolidBrush brush(Color::Green); rgn1.Xor(&rgn2); graphics.TranslateTransform(20,0);//转变绘制原点,这里这句并不是必须的,我只是为了让大家记住这个函数 graphics.FillRegion(&brush,&rgn1); graphics.DrawRectangle(&pen1,RectF(25,10,50,100)); graphics.DrawRectangle(&pen2,RectF(0,50,100,30));
对于特定的区域,我们都可以使用多个矩形来表示其大致形状。事实上,如果矩形足够小,一定数量的矩形就能够精确表示区域的形状,也就是说,一定数量的矩形所合成的形状,也可以代表区域的形状。Region类的GetRegionScans函数,实现了获取组成区域的矩形集的功能,其调用格式如下:
GetRegionScans(Matrix* matrix, Rect* rects, INT* count) GetRegionScans(Matrix* matrix, RectF* rects, INT* count)参数:
示例:
SolidBrush solidBrush(Color::Red); Pen pen(Color::Green); GraphicsPath path; Matrix matrix; Rect *rects=NULL; INT count=0; path.AddEllipse(10,0,80,120); Region patchRegion(&path); graphics.FillRegion(&solidBrush,&patchRegion); graphics.GetTransform(&matrix); count=patchRegion.GetRegionScansCount(&matrix); //为矩形集分配空间 rects=(Rect*)malloc(count*sizeof(Rect)); patchRegion.GetRegionScans(&matrix,rects,&count); graphics.TranslateTransform(140,0); for(INT j=0;j<count;j++){ graphics.DrawRectangle(&pen,rects[j]); } free(rects);
击中测试(HitTest),简单地说就是判断一个点是否位于指定的区域内。Region类的成员函数IsVisible提供了这样一个功能。而且还提供了扩展:不仅可以测试一个点是否位于指定的区域中,还可以测试两矩形区域是存否在交集。IsVisible的调用格式为:
IsVisible(Point& point, Graphics* g) IsVisible(PointF& point, Graphics* g) IsVisible(Rect& rect, Graphics* g) IsVisible(RectF& rect, Graphics* g) IsVisible(INT x, INT y, Graphics* g) IsVisible(REAL x, REAL y, Graphics* g) IsVisible(INT x, INT y, INT width, INT height, Graphics* g) IsVisible(REAL x, REAL y, REAL width, REAL height, Graphics* g)参数说明:
示例:
考虑这样一个应用,当鼠标移动到指定区域时,输出字符串“yes”,否则擦掉这个字符串(擦除操作是用填充背景颜色完成的);
在View类中的OnMouseMove(UINT nFlags, CPoint point)添加如下代码:
Graphics graphics(this->GetDC()->m_hDC); FontFamily ff(L"Arial"); Font font(&ff,15,FontStyleRegular,UnitPixel); Region rgn(RectF(10,10,100,200)); bool binrgn=rgn.IsVisible(point.x,point.y,&graphics); graphics.DrawRectangle(&Pen(Color::Green,2),RectF(10,10,100,200)); if(binrgn){ graphics.DrawString(L"yes",-1,&font,PointF(200,10),&SolidBrush(Color::Red)); }else{ graphics.FillRectangle(&SolidBrush(Color::White),RectF(200,10,100,100)); }