关于根据位图做出不规则Form(很简单,帮我加个注释就行了)(200分)

  • 主题发起人 年轻和尚
  • 开始时间

年轻和尚

Unregistered / Unconfirmed
GUEST, unregistred user!
[red]帮我给这些代码加个中文注释,请通俗易懂些,谢了先[/red][:D]
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, StdCtrls, Buttons;

type
TForm1 = class(TForm)
Image1: TImage;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
private
function CreateRegion(wMask: TBitmap; wColor: TColor;
hControl: THandle): HRGN;
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.DFM}

function Tform1.CreateRegion(wMask:TBitmap;wColor:TColor;hControl:THandle): HRGN;
var
dc, dc_c: HDC;
rgn: HRGN;
x, y: integer;
coord: TPoint;
line: boolean;
color: TColor;
begin
dc := GetWindowDC(hControl);
dc_c := CreateCompatibleDC(dc);
SelectObject(dc_c, wMask.Handle);
BeginPath(dc);
for x:=0 to wMask.Width-1 do
begin
line := false;
for y:=0 to wMask.Height-1 do
begin
color := GetPixel(dc_c, x, y);
if not (color = wColor) then
begin
if not line then
begin
line := true;
coord.x := x;
coord.y := y;
end;
end;
if (color = wColor) or (y=wMask.Height-1) then
begin
if line then
begin
line := false;
MoveToEx(dc, coord.x, coord.y, nil);
LineTo(dc, coord.x, y);
LineTo(dc, coord.x + 1, y);
LineTo(dc, coord.x + 1, coord.y);
CloseFigure(dc);
end;
end;
end;
end;
EndPath(dc);
rgn := PathToRegion(dc);
ReleaseDC(hControl, dc);
Result := rgn;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
w1:TBitmap;
w2:TColor;
rgn: HRGN;
begin
w1:=TBitmap.Create;
w1.Assign(image1.Picture.Bitmap);
w2:=w1.Canvas.Pixels[0,0];
rgn := CreateRegion(w1,w2,Handle);
if rgn<>0 then
begin
SetWindowRgn(Handle, rgn, true);
end;
w1.Free;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
Close;
end;

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
ReleaseCapture;
SendMessage(Handle, WM_SYSCOMMAND, $F012, 0);
end;

end.

 
for x:=0 to wMask.Width-1 do
for y:=0 to wMask.Height-1 do

主要看这个两重循环,它判断位图中跟wColor相同的颜色加以过滤,并画出路径,将路径
转成一块用来显示的区域。

窗口显示的时候,生成这个区域,并将区域应用在窗口上(windows会将非区域部分自动透明),

ReleaseCapture;
SendMessage(Handle, WM_SYSCOMMAND, $F012, 0);
欺骗Windows以为鼠标按在标题栏上,这样窗口就能移动了。
 
关建帮我讲讲line和那个coord
就这二个的用途我不明白!
 
关建讲这段就行了,我的脑子快炸了
[red]SelectObject(dc_c, wMask.Handle);
BeginPath(dc);
for x:=0 to wMask.Width-1 do
begin
line := false;
for y:=0 to wMask.Height-1 do
begin
color := GetPixel(dc_c, x, y);
if not (color = wColor) then
begin
if not line then
begin
line := true;
coord.x := x;
coord.y := y;
end;
end;
if (color = wColor) or (y=wMask.Height-1) then
begin
if line then
begin
line := false;
MoveToEx(dc, coord.x, coord.y, nil);
LineTo(dc, coord.x, y);
LineTo(dc, coord.x + 1, y);
LineTo(dc, coord.x + 1, coord.y);
CloseFigure(dc);
end;
end;
end;
end;
EndPath(dc);
rgn := PathToRegion(dc);
ReleaseDC(hControl, dc); [/red]
 
贴一片文章,有些帮助
张俊锋
(中国船舶工业总公司第七一三研究所八室,450052)

1 路径的概念
在Windows 95/NT 这样的Win32操作系统中,除了已有的位图,画笔,画刷,字体,调色板和区域之外,还增加了一个新的GDI对象:路径。路径是可以被填充,画出轮廓或同时被画出轮廓并填充的一个或多个图形。路径的引入,大大地丰富了Windows的图形功能,使得应用程序可以方便地建立复杂区域,绘制和填充不规则图形。这里说的不规则图形是指由直线和贝塞尔曲线组成的图形(相对于矩形,多边形,椭圆等规则图形)。

2 路径的使用
与其它原有的GDI对象不同的是,MFC类库没有专门用一个C++类来封装路径对象(或许在以后的版本中会得到支持)。有关路径的定义和使用等各种操作都必须通过调用API函数(或CDC类中对应的成员函数)来实现。
路径的使用过程大致如下:
(1)调用BeginPath()函数开始路径定义;
(2)调用GDI绘图函数来定义路径;
在Win32中,可以用于定义路径的GDI绘图函数包括:
AngleArc Arc ArcTo Chord *CloseFigure
Ellipse *ExtTextOut *LineTo *MoveToEx Pie
*PolyBezier *PolyBezierTo PolyDraw *Polygon *Polyline
*PolyLineTo *PolyPolygon *PolyPolylin Rectangl RoundRect
*TextOut
其中,在Windows 95中只能使用上述带*的GDI函数。
(3)调用EndPath()函数结束路径定义;
完成路径定义后,所定义的路径即被同时选进设备描述表,设备描述表中原有的路径对象在调用BeginPath()函数开始路径定义时即被废弃。
(4)使用路径对象。
完成路径定义工作之后,应用程序便可以利用有关GDI函数来使用路径,这些函数包括绘制路径轮廓StrokePath(),填充路径FillPath(),绘制轮廓并填充StrokeAndFillPath(),把路径转换成区域PathToRegion(),把路径直线化FlattenPath(),提取路径数据GetPath(),加宽路径WidenPath()和设置裁剪路径SelectClipPath()等。这些函数的具体使用方法可参阅有关的SDK文档。

3 应用举例
路径的引入为我们在应用程序中定义复杂区域提供了极大的方便,而不再局限于直线和椭圆弧这两种线形,这一点是很容易理解的。
另外,注意到在定义路径时可以使用TextOut()和ExtTextOut()函数,我们便可以在文字特色显示方面巧妙地使用路径,克服以往文字特显对位图操作的倚赖,从而方便快捷地制作出堪与WPS和Word等文字处理软件相媲美的“艺术汉字”来。
本文下面所提供的这个示例程序执行后,在窗口中显示出按正弦曲线起伏排列的“龙腾虎跃”五个楷体大字。窗口背景为灰色,文字前景则为一幅256色位图,就好象是把彩图剪成文字粘贴在窗口上一样(见下图)。下面具体说明该示例程序的创建方法。

(1)启动VC++,创建一个单文档应用,项目名取为Path,其它选项保留原缺省设置。
(2)在CPathView类中增加一个成员变量:
// PathView.h : interface of the CPathView class
……
class CPathView : public CView
{
……
// Implementation
public:
CFont m_fontKaiTi;
……
并在CPathView类的构造函数中创建该Cfont对象,在CPathView类的析构函数中撤消该Cfont对象:
// PathView.cpp : implementation of the CPathView class
……
CPathView::CPathView()
{
// TODO: add construction code here
m_fontKaiTi.CreateFont(200 , 0 , 0 , 0 , FW_BLACK ,
FALSE , FALSE , FALSE ,
GB2312_CHARSET ,
OUT_DEFAULT_PRECIS ,
CLIP_DEFAULT_PRECIS ,
DEFAULT_QUALITY ,
FIXED_PITCH | FF_MODERN,
"楷体_GB2312");
}
CPathView::~CPathView()
{
m_fontKaiTi.DeleteObject();
}
(3)在CPathView::OnDraw()函数中添加如下代码:
void CPathView::OnDraw(CDC* pDC)
{
……
// TODO: add draw code for native data here
RECT rect;
GetClientRect(&amp;rect);

CFont* pOldFont=(CFont*)pDC->SelectObject(&amp;m_fontKaiTi);
pDC->SetBkMode(TRANSPARENT);

//定义路径
pDC->BeginPath();{
pDC->TextOut(0,10,"龙",2);
pDC->TextOut(200,10,"腾",2);
pDC->TextOut(400,10,"虎",2);
pDC->TextOut(600,10,"跃",2); }
pDC->EndPath();

pDC->SelectObject(pOldFont);

//检取路径数据
int nCount=pDC->GetPath(NULL,NULL,0);
CPoint* points=new CPoint[nCount];
BYTE* bytes=new BYTE[nCount];
pDC->GetPath(points,bytes,nCount);

//对路径定义点按正弦曲线进行变换
int i;
for(i=0;i<nCount;i++)
points.y=points.y+(int)(80*sin(points.x
/300.*3.1415926)+100);

//重建一个新的路径
CPoint ptStart;
pDC->BeginPath();{
for(i=0;i<nCount;i++){
switch(bytes){
//移动当前点位置
case PT_MOVETO:
pDC->MoveTo(points);
ptStart=points;
break;
//画直线
case PT_LINETO:
pDC->LineTo(points);
break;
//画贝塞尔曲线
case PT_BEZIERTO:
pDC->PolyBezierTo(points+i,3);
i=i+2;
break;
//画贝塞尔曲线并封闭图形
case PT_BEZIERTO|PT_CLOSEFIGURE:
points[i+2]=ptStart;
pDC->PolyBezierTo(points+i,3);
i=i+2;
break;
//画直线并封闭图形
case PT_LINETO|PT_CLOSEFIGURE:
pDC->LineTo(ptStart);
break;
}
}
pDC->CloseFigure();
}
pDC->EndPath();

//绘制窗口灰色背景
CBrush* pOldBrush=(CBrush*)(pDC->SelectStockObject(GRAY_BRUSH));
pDC->Rectangle(&amp;rect);
pDC->SelectObject(pOldBrush);

//设置裁剪路径
pDC->SetPolyFillMode(WINDING);
pDC->SelectClipPath(RGN_COPY);

//用位图填充裁剪区域
CBitmap bmp;
CBitmap* pBmpOld;
bmp.LoadBitmap(IDB_BMP);

CDC dcMem;
dcMem.CreateCompatibleDC(pDC);
pBmpOld=dcMem.SelectObject(&amp;bmp);

pDC->StretchBlt(0,0,rect.right,rect.bottom,
&amp;dcMem,0,0,600,100,SRCCOPY);

dcMem.SelectObject(pBmpOld);
dcMem.DeleteDC();
bmp.DeleteObject();
}
(5)编译,连接,运行该应用程序。

tyn的解释
通常的路径体如下:
BeginPath;
....
....
EndPath;
SetPolyFillMode();//设置填充模式
StrokeAndFillPath();//显示、封闭并且填充路径
//如果不想填充只是显示路径,就用StrokePath();
//FillPath()是封闭并且用指定画笔填充路径
//CloseFigure()仅仅是将开放的路径封闭

路径应用很关键的一点就是路径与区域的转化: PathToRegion();

在那个流动椭圆的例子中,首先是绘制椭圆路径,BeginPath.....EndPath;
然后利用FlattenPath()将椭圆曲线折线化,从而由GetPath()得到椭圆轨迹上的各个控制点.

利用路径函数,其实也是一种变相的种子填充算法的实现;
比如你绘制了3次Bezier曲线,你想填充它为实心的,怎么办?
用通常的种子填充FloodFill()显然是不可行的,因为在比较狭窄的地方是填充不了的。
而利用路径函数,根本就不存在填充时漏点的问题。
例程:
var
qpoint:array [0..6]of tpoint;
begin
beginpath(Canvas.Handle);
qpoint[0]:=point(80,80);
qpoint[1]:=point(120,120);
qpoint[2]:=point(150,90);
qpoint[3]:=point(270,270);
qpoint[4]:=point(150,90+10);
qpoint[5]:=point(120,120+10);
qpoint[6]:=point(80,100);
Canvas.Polybezier(qpoint);
endpath(Canvas.Handle);
SetPolyFillMode(Canvas.Handle,WINDING);
Canvas.Brush.Color := clRed;
StrokeAndFillPath(Canvas.Handle);
end;

也不知道有没有说明白,其实只要自己做几个例子,一切就OK。





 
dc := GetWindowDC(hControl); //得到相应的window设备上下文
dc_c := CreateCompatibleDC(dc); //在Windows 中,图像是一种GUI对象,在内存中建立兼容的设备上下文
SelectObject(dc_c, wMask.Handle); //在GUI对象选入设备上下文
BeginPath(dc); //开始画路径
for x:=0 to wMask.Width-1 do
begin
line := false;
for y:=0 to wMask.Height-1 do
begin //查找指定颜色像素
color := GetPixel(dc_c, x, y);
if not (color = wColor) then
begin
if not line then
begin
line := true;
coord.x := x;
coord.y := y;
end;
end;
if (color = wColor) or (y=wMask.Height-1) then
begin //没有指定色,画一个矩形区域
if line then
begin
line := false;
MoveToEx(dc, coord.x, coord.y, nil);
LineTo(dc, coord.x, y);
LineTo(dc, coord.x + 1, y);
LineTo(dc, coord.x + 1, coord.y);
CloseFigure(dc);
end;
end;
end;
end;
EndPath(dc);
rgn := PathToRegion(dc);
ReleaseDC(hControl, dc);
Result := rgn; //返回路径区域
end;

procedure TForm1.FormCreate(Sender: TObject);
var
w1:TBitmap;
w2:TColor;
rgn: HRGN;
begin
w1:=TBitmap.Create;
w1.Assign(image1.Picture.Bitmap);
w2:=w1.Canvas.Pixels[0,0];
rgn := CreateRegion(w1,w2,Handle); //建立区域
if rgn<>0 then
begin
SetWindowRgn(Handle, rgn, true); //设置窗口显示区域
end;
w1.Free;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
Close;
end;

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
ReleaseCapture; //在窗口中任意地方点击,成为在标题栏点击,这样可以移动窗口
SendMessage(Handle, WM_SYSCOMMAND, $F012, 0);
end;
 
同意huazai的说法
 
多人接受答案了。
 
顶部