学习编程真是一件艰苦的事情。不过,对于我们编程的人来说,肯定感觉它也是最有趣的。当我们苦苦思索都无法实现一个功能时,我们并不会感觉苦恼,强烈的求知欲足以使我们忘掉一切。而当我们终于实现了,那种柳暗花明、豁然开朗、拨云见日出的玄妙感觉真是只可意会不可言传。我相信,大多数的共享软件、免费软件不是因为作者不想赚钱,而是因为程序员更愿意把自己的旷世绝招惊天一式炫耀给大家看。
有很多学习编程的人问我,怎样提高自己的水平。哦,我的回答可能和别人有点不同呵。我认为不要抱着书本,等全部都弄明白了再来编程。懂得一点就可以动手了。那我们编什么呢?很简单,我们试着实现别的优秀的软件里实现的功能,看看我们能不能实现它。这样,大家的水平一定提高得很快哦。
比如说,POTOSHOP里的喷枪是怎样实现的呢?
我们可以这样思考:喷枪的形状我们可以通过改变鼠标的形状得到,这样我们就前进一步了。我们把剩下的问题简化,可以先不考虑喷枪的强弱,而将问题简化成如何在鼠标的周围的一定范围内画点。具体的方法不需要我再罗嗦了吧?然后我们再慢慢添加线形、色彩、强弱等其它功能,喷枪我们就算完成了。
今天我要实现的是POTOSHOP里流动的选取边框功能。
在POTOSHOP里,当我们选取部分图形时,它的选取边框是流动式的。在其他的画图看图软件里可不是这样的哦,比如说ACDSEE。如果在我们的程序中使用这项功能,感觉一定很酷。不过,它是怎样实现的呢?
一开始我也认为很简单。矩形的流动选取边框很容易实现吗。不过,在POTOSHOP里,套索等其他不规则的选取边框都是流动型的。
我是这样解决问题的。
首先,在不知怎么入手时,要选择合适的关键词来查找资料。你会选择什么词?这就要凭借个人的经验了。我注意到“划线”(Line)和“路径”(Path)两个词。你也首先会选Line这个词吧?为什么我会选Path呢?哎,这就是感觉了,我也说不清楚,不过,随着你编程越来越多(实际呢,我曾经编过空心字的程序,所以有过这方面的经验了)。废话少说,先试试帮助文件吧,还不必动用MSDN。依次打开DELPHI的帮助Help——>Windows SDK,输入关键字Line或者Path,真的不错,出来一堆函数。不管有没有用,都先看看吧,就当作是一次学习。不过,我的运气真好:两个关键字都命中了目标!如果没有命中怎么办?多选几个啦。不过,不要选对了都不知道噢。
在这里,我们关键要阅读一下LineDDA函数的说明。
我们下一步就是来编程试验一下了。我选择建立一个流动的字体边框,就像是霓虹灯的那种效果。
程序如下,程序我加了详细的注释。没想到程序会这么短吧。
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Timer1: TTimer;
procedure Button1Click(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
pathpoints :array [0..1000] of TPoint;
PathTypes:array [0..1000] of byte;
PPts :PPoint;
PTypes :PByteArray;
Number :Integer;
Counter :Byte;
implementation
{$R *.dfm}
// MovingDots的格式可是预定好的。你可阅读LineDDA的帮助。
//这里我用它来定义流动线的色彩。
//注意了:MovingDots是一个CALLBACK函数,需加上stdcall关键字。
procedure MovingDots(X,Y: Integer; TheCanvas: TCanvas); stdcall;
begin
if counter=15 then counter:=0;
if counter<5 then
TheCanvas.Pixels[X,Y] :=clWhite
else if counter<12 then
TheCanvas.Pixels[X,Y] :=clred
else
TheCanvas.Pixels[X,Y] :=clBlue;
inc(counter);
end;
//Timer1的Interval我设置为100。
//如果你在编程时感觉到字体闪烁,你可以调整Interval、流动线色彩长度、字体
//比如在本程序中,去掉字体[fsItalic]属性,你就会感到闪烁。
//甚至字数也闪烁的程度影响。你只能综合这些因素慢慢调试。
procedure TForm1.Timer1Timer(Sender: TObject);
var
J ,k:Integer;
begin
//你用过Pred函数吗?这儿你也可以用Number-1代替。
//你知道它们的区别吗?哈哈。
for J := 0 to Pred(Number) do
begin
//当前点与下一个关键点使用MovingDots的方法相连 LineDDA(PathPoints[J].X,PathPoints[J].Y,PathPoints[J+1].X,PathPoints[J+1].Y,
@MovingDots,LongInt(Form1.Canvas))
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Canvas.Font.Name := '幼圆'; //这里的字体一定要是TrueType Font
Canvas.Font.size := 72;
Canvas.Font.style := [fsItalic,fsBold];//最好设置为粗体字,这样轮廓大一些
beginpath(canvas.handle); //开始捕获 Canvas上绘制的轮廓
//使用SetBkMode去除文字外框,你可以不使用它,比较一下效果
SetBkMode( Canvas.Handle, TRANSPARENT );
canvas.TextOut(120,20,'电脑');
endpath(canvas.handle); //结束捕获
//使用FlattenPath使轮廓平滑,你可以不使用它,比较一下效果
if FlattenPath(Canvas.Handle)then
begin
//得到轮廓的控制点
PPts := nil;
PTypes := nil;
Number := GetPath(Canvas.Handle,PPts,PTypes,0);
Number := GetPath(Canvas.Handle,PathPoints,PathTypes,Number);
end;
end;
end.
效果如图:
上边怎么有那么多杂线?
为了去掉这些杂线,我熬了整个通宵。怪就怪我没有好好的看帮助啊。你也跟踪一下PathTypes这个数组的内容吧,然后再读一遍关于它的帮助。
PT_MOVETO:相当于线段的起点啦。
PT_CLOSEFIGURE:相当于封闭曲线的结束点,与相对的PT_MOVETO连接构成一个封闭的曲线。
现在你知道毛病出在那儿了吧?我们举的例子“电脑” 两个字是由多个封闭的曲线组成的。在我们的程序中,一条曲线的PT_CLOSEFIGURE并没有与相对的PT_MOVETO连接构成一个封闭的曲线,而是与下一条曲线的起点连接了。那条从(0,0)点斜拉的线段是由于PathPoints最终点后的数据都是0。
知道了这些,程序就好改动了。把下面的几个句子输到LineDDA函数前:
if PathTypes[j]=3 then //如果是曲线的终点。为什么是3呢?
//注意,我没有用PT_MOVETO来判断。
begin
for k:=J downto 0 do
begin
if PathTypes[k]=6 then //找到对应的曲线起点。为什么是6呢?
//注意,我没有用PT_CLOSEFIGURE来判断。
begin
//把终点和起点连接起来构成一条封闭的曲线。 LineDDA(PathPoints[J].X,PathPoints[J].Y,PathPoints[k].X,PathPoints[k].Y,
@MovingDots,LongInt(Form1.Canvas));
break;
end;
end;
continue;
end;
这次可以了,效果不错吧?如图:
不知道你从本次编程中学到点什么没有。你有什么心得也可以和我交流啊。我的QQ号为44065089,呢称吗,就是寒夜看雪。
本程序在DELPHI7.0+WIN ME下通过。