-
c语言入门之不规则窗体的应用增加软件的吸引力
传统的WINDOWS应用软件界面给人的感觉总是千篇一律的方方正正的窗体,看的时间长
了难免会有些厌烦,总是希望能见到些不同一般的软件界面。如今,相当数量的商业软件在
提供优秀而强大的功能的同时,软件的界面也是做得越来越漂亮,比如《超级解霸2000》中
的界面插件,使用过的人一定对其华丽的外观充满好感。作为一个编程爱好者,如果自己写
出的软件也拥有类似的界面,也许会吸引更多目光的注视。那么,我们现在就开始动手制作
自己的漂亮界面吧。
技术内幕
要想在自己的程序中加入不规则窗体的应用,你首先要熟悉几个WINDOWS API函数的使
用,它们是:椭圆形(或圆形)区域创建函数CreateEllipticRgn 、多边形区域创建函数
CreatePolygonRgn、 矩形区域创建函数CreateRectRgn、 带圆角的矩形区域创建函数
CreateRoundRectRgn。你可以用这些函数创建不同类型的窗体区域,也可以用WINDOWS API
函数CombineRgn将几个简单区域组合成一个复杂区域。
下一步要做的就是将已经创建好的区域显示在屏幕上,同样也是使用WINDOWS API 函数
来实现,这次用到的是SetWindowRgn函数。
WINDOWS API 函数在Borland C++ Builder 头文件中均已定义,在应用程序中使用这些
API函数就象使用C++的普通库函数一样。
准备工作
为你的程序准备一幅背景图片,推荐方法是: 在PhotoShop中打开图片后使用磁性套索
工具选取你所需要的图象轮廓--复制--新建文件(背景使用白色)--粘贴--另存文
件(PSD文件)--用ACDSee等看图软件将保存的PSD文件转换为BMP文件face.bmp备用。如
下图:
程序中引用图片
打开Borland C++ Builder,在窗体上放置一个Image控件Image1,其Picture暂为空;
在窗体上放置一个Popup菜单,编辑菜单项增加“Close”项(添加程序代码使得激活弹出菜
单时即可关闭应用程序)。程序中做如下处理:
void __fastcall TForm1::FormCreate(TObject *Sender)
{
< 。
< 。
< 。
Image1->Picture->LoadFromFile(".\\face.bmp");
Width=Image1->Width;
Height=Image1->Height;
Repaint();
< 。
< 。
< 。
}
此时,窗体的大小已能跟随所用图片的大小而改变,但仍旧是传统的WINDOWS界面,要
想显示成具有图片轮廓的窗体外形,就需要使用前文介绍的WINDOWS API函数将不需要显示
的部分抠去。
抠像方法一
这是一种非常简单的方法,采用对图片逐行扫描的方式,将图片像素点为白色的部分抠
去,使用的方法是:在像素点附近产生一个包含几个像素点的矩形,与原图片采用异或方式
抠去,程序如下:
HRGN tepRgn;
for(y=0;y<Image1->Height;y++)
for(x=0;x<Image1->Width;x++)
if(Image1->Canvas->Pixels[x][y]==clWhite)
{
< tepRgn=CreateRectRgn(x,y,x+1,y+1);
CombineRgn(WndRgn,WndRgn,tepRgn,RGN_XOR);
DeleteObject(tepRgn);
}
这种方法的优点是处理比较简单,缺点是处理速度太慢,尤其是在处理大幅图片时,往
往要4~5秒的时间才能将窗体显示出来。因此产生了通过另外的途径快速勾勒图片轮廓的想
法。
抠像方法二
这次我们采用另一个WINDOWS API函数CreatePolygonRgn(多边形区域),使用这个函
数时需为它准备图片轮廓的坐标点数组及坐标点个数,也是通过对图片逐行扫描的方式,找
到白色像素点与非白色像素点的分界点,将该点的坐标存入数组中,然后用
CreatePolygonRgn函数一次就可以把图片外围的不用部分抠去,从而省去大量的处理时间。
程序如下:
register int x,y;
int l,r;
POINT *a;
bool lb,rb;
HRGN WndRgn,TempRgn,;
if((a=(POINT *)malloc(800*2*(sizeof(POINT))))==NULL)
{
ShowMessage("申请内存失败!");
exit(0);
}
l=0;r=Image1->Height*2-1;
WndRgn=CreateRectRgn(0,0,Image1->Width,Image1->Height);
for(y=0;y<Image1->Height;y++)
{
lb=true;
for(x=0;x<Image1->Width;x++)
if(Image1->Canvas->Pixels[x][y]!=clWhite)
{
a[l].x=x;
a[l].y=y;
lb=false;
break;
}
if(lb) a[l]=a[l-1];
l++;
rb=true;
for(x=Image1->Width-1;x>=0;x--)
if(Image1->Canvas->Pixels[x][y]!=clWhite)
{
a[r].x=x;
a[r].y=y;
rb=false;
break;
}
if(rb) a[r]=a[r+1];
r--;
}
TempRgn=CreatePolygonRgn(a,Image1->Height*2,ALTERNATE);
CombineRgn(WndRgn,WndRgn,TempRgn,RGN_AND);
DeleteObject(TempRgn);
< free(a);
程序中对每一像素行都从左右两个方向分别扫描,找到两边的分界点存入数组。
不过这个方法也存在一些缺陷,那就是图片的内凹部分轮廓并未表现出来。从下图中可
以看出:
最终解决方案
考虑到既不增加算法的复杂度,又可大幅度缩短不规则窗体的创建速度,因此采用综合
以上两种方案,达到我们应用的目的,程序中首先应用方法二对图片双向扫描,产生轮廓坐
标点数组,然后在图片轮廓内应用方法一将内凹部分抠去,最后才用多边形区域创建函数抠
去图片外围部分。程序如下:
void __fastcall TForm1::FormCreate(TObject *Sender)
{
register int x,y;
int l,r;
POINT *a;
bool lb,rb;
HRGN WndRgn,TempRgn,tepRgn;
Width=800;Height=600;
if((a=(POINT *)malloc(800*4*(sizeof(POINT))))==NULL)
{
ShowMessage("申请内存失败!");
exit(0);
}
Image1->Picture->LoadFromFile(".\\face.bmp");
Width=Image1->Width;
Height=Image1->Height;
Repaint();
l=0;r=Image1->Height*2-1;
WndRgn=CreateRectRgn(0,0,Image1->Width,Image1->Height);
< file://应用方法二产生轮廓坐标点数组
for(y=0;y<Image1->Height;y++)
{
lb=true;
for(x=0;x<Image1->Width;x++)
if(Image1->Canvas->Pixels[x][y]!=clWhite)
{
a[l].x=x+1;
a[l].y=y;
lb=false;
break;
}
if(lb) a[l]=a[l-1];
l++;
rb=true;
for(x=Image1->Width-1;x>=0;x--)
if(Image1->Canvas->Pixels[x][y]!=clWhite)
{
a[r].x=x;
a[r].y=y;
rb=false;
break;
}
if(rb) a[r]=a[r+1];
r--;
}
file://应用方法一抠去图片内凹部分
r=Image1->Height*2-1;
for(y=0;y<Image1->Height;y++){
for(x=a[y].x;x<a[r].x;x++)
if(Image1->Canvas->Pixels[x][y]==clWhite)
{
< tepRgn=CreateRectRgn(x,y,x+1,y+1);
CombineRgn(WndRgn,WndRgn,tepRgn,RGN_XOR);
DeleteObject(tepRgn);
}
r--;
}
file://将图片外围部分抠去
TempRgn=CreatePolygonRgn(a,Image1->Height*2,ALTERNATE);
CombineRgn(WndRgn,WndRgn,TempRgn,RGN_AND);
DeleteObject(TempRgn);
free(a);
file://显示不规则窗体
SetWindowRgn(Handle,WndRgn,true);
SetWindowPos(Handle,HWND_TOP,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE);
}
r--;
}
file://将图片外围部分抠去
TempRgn=CreatePolygonRgn(a,Image1->Height*2,ALTERNATE);
CombineRgn(WndRgn,WndRgn,TempRgn,RGN_AND);
DeleteObject(TempRgn);
free(a);
file://显示不规则窗体
SetWindowRgn(Handle,WndRgn,true);
SetWindowPos(Handle,HWND_TOP,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE);
}
至此,一个漂亮的程序界面就出现在你的屏幕上了。
了难免会有些厌烦,总是希望能见到些不同一般的软件界面。如今,相当数量的商业软件在
提供优秀而强大的功能的同时,软件的界面也是做得越来越漂亮,比如《超级解霸2000》中
的界面插件,使用过的人一定对其华丽的外观充满好感。作为一个编程爱好者,如果自己写
出的软件也拥有类似的界面,也许会吸引更多目光的注视。那么,我们现在就开始动手制作
自己的漂亮界面吧。
技术内幕
要想在自己的程序中加入不规则窗体的应用,你首先要熟悉几个WINDOWS API函数的使
用,它们是:椭圆形(或圆形)区域创建函数CreateEllipticRgn 、多边形区域创建函数
CreatePolygonRgn、 矩形区域创建函数CreateRectRgn、 带圆角的矩形区域创建函数
CreateRoundRectRgn。你可以用这些函数创建不同类型的窗体区域,也可以用WINDOWS API
函数CombineRgn将几个简单区域组合成一个复杂区域。
下一步要做的就是将已经创建好的区域显示在屏幕上,同样也是使用WINDOWS API 函数
来实现,这次用到的是SetWindowRgn函数。
WINDOWS API 函数在Borland C++ Builder 头文件中均已定义,在应用程序中使用这些
API函数就象使用C++的普通库函数一样。
准备工作
为你的程序准备一幅背景图片,推荐方法是: 在PhotoShop中打开图片后使用磁性套索
工具选取你所需要的图象轮廓--复制--新建文件(背景使用白色)--粘贴--另存文
件(PSD文件)--用ACDSee等看图软件将保存的PSD文件转换为BMP文件face.bmp备用。如
下图:
程序中引用图片
打开Borland C++ Builder,在窗体上放置一个Image控件Image1,其Picture暂为空;
在窗体上放置一个Popup菜单,编辑菜单项增加“Close”项(添加程序代码使得激活弹出菜
单时即可关闭应用程序)。程序中做如下处理:
void __fastcall TForm1::FormCreate(TObject *Sender)
{
< 。
< 。
< 。
Image1->Picture->LoadFromFile(".\\face.bmp");
Width=Image1->Width;
Height=Image1->Height;
Repaint();
< 。
< 。
< 。
}
此时,窗体的大小已能跟随所用图片的大小而改变,但仍旧是传统的WINDOWS界面,要
想显示成具有图片轮廓的窗体外形,就需要使用前文介绍的WINDOWS API函数将不需要显示
的部分抠去。
抠像方法一
这是一种非常简单的方法,采用对图片逐行扫描的方式,将图片像素点为白色的部分抠
去,使用的方法是:在像素点附近产生一个包含几个像素点的矩形,与原图片采用异或方式
抠去,程序如下:
HRGN tepRgn;
for(y=0;y<Image1->Height;y++)
for(x=0;x<Image1->Width;x++)
if(Image1->Canvas->Pixels[x][y]==clWhite)
{
< tepRgn=CreateRectRgn(x,y,x+1,y+1);
CombineRgn(WndRgn,WndRgn,tepRgn,RGN_XOR);
DeleteObject(tepRgn);
}
这种方法的优点是处理比较简单,缺点是处理速度太慢,尤其是在处理大幅图片时,往
往要4~5秒的时间才能将窗体显示出来。因此产生了通过另外的途径快速勾勒图片轮廓的想
法。
抠像方法二
这次我们采用另一个WINDOWS API函数CreatePolygonRgn(多边形区域),使用这个函
数时需为它准备图片轮廓的坐标点数组及坐标点个数,也是通过对图片逐行扫描的方式,找
到白色像素点与非白色像素点的分界点,将该点的坐标存入数组中,然后用
CreatePolygonRgn函数一次就可以把图片外围的不用部分抠去,从而省去大量的处理时间。
程序如下:
register int x,y;
int l,r;
POINT *a;
bool lb,rb;
HRGN WndRgn,TempRgn,;
if((a=(POINT *)malloc(800*2*(sizeof(POINT))))==NULL)
{
ShowMessage("申请内存失败!");
exit(0);
}
l=0;r=Image1->Height*2-1;
WndRgn=CreateRectRgn(0,0,Image1->Width,Image1->Height);
for(y=0;y<Image1->Height;y++)
{
lb=true;
for(x=0;x<Image1->Width;x++)
if(Image1->Canvas->Pixels[x][y]!=clWhite)
{
a[l].x=x;
a[l].y=y;
lb=false;
break;
}
if(lb) a[l]=a[l-1];
l++;
rb=true;
for(x=Image1->Width-1;x>=0;x--)
if(Image1->Canvas->Pixels[x][y]!=clWhite)
{
a[r].x=x;
a[r].y=y;
rb=false;
break;
}
if(rb) a[r]=a[r+1];
r--;
}
TempRgn=CreatePolygonRgn(a,Image1->Height*2,ALTERNATE);
CombineRgn(WndRgn,WndRgn,TempRgn,RGN_AND);
DeleteObject(TempRgn);
< free(a);
程序中对每一像素行都从左右两个方向分别扫描,找到两边的分界点存入数组。
不过这个方法也存在一些缺陷,那就是图片的内凹部分轮廓并未表现出来。从下图中可
以看出:
最终解决方案
考虑到既不增加算法的复杂度,又可大幅度缩短不规则窗体的创建速度,因此采用综合
以上两种方案,达到我们应用的目的,程序中首先应用方法二对图片双向扫描,产生轮廓坐
标点数组,然后在图片轮廓内应用方法一将内凹部分抠去,最后才用多边形区域创建函数抠
去图片外围部分。程序如下:
void __fastcall TForm1::FormCreate(TObject *Sender)
{
register int x,y;
int l,r;
POINT *a;
bool lb,rb;
HRGN WndRgn,TempRgn,tepRgn;
Width=800;Height=600;
if((a=(POINT *)malloc(800*4*(sizeof(POINT))))==NULL)
{
ShowMessage("申请内存失败!");
exit(0);
}
Image1->Picture->LoadFromFile(".\\face.bmp");
Width=Image1->Width;
Height=Image1->Height;
Repaint();
l=0;r=Image1->Height*2-1;
WndRgn=CreateRectRgn(0,0,Image1->Width,Image1->Height);
< file://应用方法二产生轮廓坐标点数组
for(y=0;y<Image1->Height;y++)
{
lb=true;
for(x=0;x<Image1->Width;x++)
if(Image1->Canvas->Pixels[x][y]!=clWhite)
{
a[l].x=x+1;
a[l].y=y;
lb=false;
break;
}
if(lb) a[l]=a[l-1];
l++;
rb=true;
for(x=Image1->Width-1;x>=0;x--)
if(Image1->Canvas->Pixels[x][y]!=clWhite)
{
a[r].x=x;
a[r].y=y;
rb=false;
break;
}
if(rb) a[r]=a[r+1];
r--;
}
file://应用方法一抠去图片内凹部分
r=Image1->Height*2-1;
for(y=0;y<Image1->Height;y++){
for(x=a[y].x;x<a[r].x;x++)
if(Image1->Canvas->Pixels[x][y]==clWhite)
{
< tepRgn=CreateRectRgn(x,y,x+1,y+1);
CombineRgn(WndRgn,WndRgn,tepRgn,RGN_XOR);
DeleteObject(tepRgn);
}
r--;
}
file://将图片外围部分抠去
TempRgn=CreatePolygonRgn(a,Image1->Height*2,ALTERNATE);
CombineRgn(WndRgn,WndRgn,TempRgn,RGN_AND);
DeleteObject(TempRgn);
free(a);
file://显示不规则窗体
SetWindowRgn(Handle,WndRgn,true);
SetWindowPos(Handle,HWND_TOP,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE);
}
r--;
}
file://将图片外围部分抠去
TempRgn=CreatePolygonRgn(a,Image1->Height*2,ALTERNATE);
CombineRgn(WndRgn,WndRgn,TempRgn,RGN_AND);
DeleteObject(TempRgn);
free(a);
file://显示不规则窗体
SetWindowRgn(Handle,WndRgn,true);
SetWindowPos(Handle,HWND_TOP,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE);
}
至此,一个漂亮的程序界面就出现在你的屏幕上了。
最新更新
Objective-C语法之代码块(block)的使用
VB.NET eBook
Add-in and Automation Development In VB.NET 2003 (F
Add-in and Automation Development In VB.NET 2003 (8
Add-in and Automation Development in VB.NET 2003 (6
Add-in and Automation Development In VB.NET 2003 (5
AddIn Automation Development In VB.NET 2003 (4)
AddIn And Automation Development In VB.NET 2003 (2)
Addin and Automation Development In VB.NET 2003 (3)
AddIn And Automation Development In VB.NET 2003 (1)
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
SQL Server -- 解决存储过程传入参数作为s
武装你的WEBAPI-OData入门
武装你的WEBAPI-OData便捷查询
武装你的WEBAPI-OData分页查询
武装你的WEBAPI-OData资源更新Delta
5. 武装你的WEBAPI-OData使用Endpoint 05-09
武装你的WEBAPI-OData之API版本管理
武装你的WEBAPI-OData常见问题
武装你的WEBAPI-OData聚合查询
OData WebAPI实践-OData与EDM
OData WebAPI实践-Non-EDM模式