Delphi的圖形處理 之一 -- 圖像處理在可視化編程中的作用及其應(yīng)用價(jià)值 |
作者:何詠 發(fā)布日期:(2005-4-12 21:07:49)
聲明:本文著作權(quán)屬于何詠,如要轉(zhuǎn)載請(qǐng)聲明作者及出處。 |
第一章 圖像處理在可視化編程中的作用及其應(yīng)用價(jià)值
圖像處理,是可視化編程的基礎(chǔ)內(nèi)容。在Windows操作系統(tǒng)中,一切要輸出到屏幕上的東西都是通過(guò)圖形處理這部分的內(nèi)容來(lái)實(shí)現(xiàn)的。比如一個(gè)程序使用了標(biāo)簽控件,它看起來(lái)似乎并沒(méi)有用到什么圖形處理,但實(shí)際上標(biāo)簽控件就是通過(guò)使用GDI庫(kù)中的圖形處理函數(shù)來(lái)實(shí)現(xiàn)的??梢?jiàn)圖形處理在編程中的重要性。
圖像處理在實(shí)際的應(yīng)用中也極具價(jià)值。平面制作、動(dòng)畫(huà)制作等都離不開(kāi)它。這一部分的內(nèi)容十分繁多。我本次研究的內(nèi)容,只是其中最基礎(chǔ)的、最重要一部分。
探究Delphi的圖形處理 之二 -- 基本圖像處理函數(shù) |
作者:何詠 發(fā)布日期:(2005-4-12 21:06:29)
聲明:本文著作權(quán)屬于何詠,如要轉(zhuǎn)載請(qǐng)聲明作者及出處。 |
第二章 圖像處理函數(shù)
2.1 為什么選擇Delphi
所有的可視化編程語(yǔ)言都能夠進(jìn)行圖像處理。但由于這些語(yǔ)言的定位不同,它們?cè)谶M(jìn)行圖形處理的效率和便捷程度上也各不相同。實(shí)際上,Visual C 的圖像處理效率是最高的,這是由于GDI類(lèi)庫(kù)本身就是用C++寫(xiě)的。但是使用VC來(lái)編程并不是一件方便的事,因?yàn)檫@個(gè)語(yǔ)言本身就較為繁雜難懂,所以我沒(méi)有選擇它。Visual Basic(VB)也是一個(gè)常用的語(yǔ)言,但它在圖形處理方面能力較差。首先是它的坐標(biāo)系統(tǒng)是以twip為單位的浮點(diǎn)坐標(biāo)系統(tǒng),在調(diào)用GDI類(lèi)庫(kù)時(shí),必須對(duì)坐標(biāo)系統(tǒng)進(jìn)行轉(zhuǎn)換,浪費(fèi)了大量的資源,編程起來(lái)較為麻煩。在多方面因素的影響下,我覺(jué)得Delphi是一個(gè)理想的語(yǔ)言。Delphi已經(jīng)把絕大多數(shù)GDI繪圖函數(shù)都封裝成可直接調(diào)用的類(lèi),使用它進(jìn)行圖形處理操作十分方便,而且Delphi 是Pascal演變而來(lái)的,Pascal具有嚴(yán)謹(jǐn)易讀的特點(diǎn),因此很容易上手。
2.2 Delphi中用于圖形處理的類(lèi)
Delphi為我們提供了許多圖形圖像方面的類(lèi),合理地使用這些類(lèi),我們可以方便地開(kāi)發(fā)出各種圖形處理程序。這些類(lèi)有TPicture、TBitmap、TGraphic、TIcon、TJPEGImage和TCanvas。其中,TCanvas類(lèi)用于繪圖,TPicture、TBitmap、TIcon和TJPEGImage是專(zhuān)門(mén)用來(lái)處理圖片的類(lèi),TGraphic是一個(gè)抽象類(lèi),一般不直接使用。TPicture類(lèi)可以載入所有支持的圖片,而TBitmap、TIcon、TJPEGImage分別用于處理各種類(lèi)型的圖片。在實(shí)際應(yīng)用中,我們一般用這些具體類(lèi)型的類(lèi)載入圖片,再將圖片轉(zhuǎn)為Bitmap格式來(lái)處理。TPicture、TIcon、TJPEGImage類(lèi)一般只用于輸入和輸出。例如,下面的代碼可以載入一幅任意支持格式的圖片(Delphi所支持的格式為bmp、jpg、dib、wmf和emf)。
Var Pic:TPicture;
Begin
Pic := TPicture.Create;
Pic.LoadFromFile(FileName);
End; |
用TPicture類(lèi)來(lái)載入圖片時(shí),該類(lèi)會(huì)根據(jù)文件名的擴(kuò)展名來(lái)決定用何等方式來(lái)打開(kāi)圖片。這就出現(xiàn)了一個(gè)問(wèn)題,如果這個(gè)圖片的擴(kuò)展名被用戶(hù)非法修改,程序就會(huì)把這個(gè)圖片視為無(wú)效圖片。在真正編程中,我們要用TPicture、TBitmap、TJPEGImage、TIcon依次嘗試去打開(kāi)圖片。
另外,Delphi本身是不支持GIF文件格式的。我們可以借用一個(gè)第三方的類(lèi)——GIFImage來(lái)讓Delphi支持它。這個(gè)類(lèi)在附帶的光盤(pán)中可以找到。最終我們用下面的代碼來(lái)完成載入圖片的操作。
Procedure ReadPicture(FileName: String; Bitmap: Graphics.TBitmap);
var pic:TPicture;Bit:Graphics.TBitmap;jpgPic:TJPEGImage;FGifPic:TGIFImage;
icoPic:TIcon;
begin
FGifPic := TGifImage.Create;
Pic:=Tpicture.Create;
bit:=Graphics.TBitmap.Create;
jpgPic:=TJPEGImage.Create;
icoPic:=TIcon.Create;
try
pic.LoadFromFile(FileName);
if uppercase(ExtractFileExt(Filename)) = ‘.ICO‘ then begin
Bitmap.Height:=pic.Height;
Bitmap.Width:=Pic.Width;
Bitmap.Canvas.Draw(0,0,Pic.Graphic);
end
else
bitmap.Assign(pic.Graphic);
except
try
bitmap.LoadFromFile(FileName);
except
try
jpgPic.LoadFromFile(FileName);
bitmap.Assign(jpgPic);
except
try
icoPic.LoadFromFile(Filename);
bitmap.Free;
Bitmap:=TBitmap.Create;
bitmap.Height:=icoPic.Height;
bitmap.Width:=icoPic.Width;
bitmap.Canvas.Draw(0,0,icoPic);
except
try
FgifPic.LoadFromFile(FileName);
Bitmap.Assign(FGifPic.Bitmap);
except
bitmap.Height:=bitmap.Canvas.TextHeight(‘8‘);
bitmap.Width:=bitmap.Canvas.TextWidth(‘無(wú)效圖片‘);
BitMap.Canvas.TextOut(0,0,‘無(wú)效圖片‘);
end;{try}
end;
end;{try}
end;{try}
end;{try}
Pic.Free ;
bit.Free;
jpgPic.Free;
icoPic.Free;
FGifPic.Free;
end; |
保存圖片的方法跟打開(kāi)圖片的方法類(lèi)似,我們可以使用不同類(lèi)型的圖片類(lèi)的SaveToFile方法保存文件。下面的代碼可以根據(jù)文件名中擴(kuò)展名的不同使用不同的類(lèi)來(lái)保存Bitmap。
Procedure SaveBitmap(FileName: String; PicB: TBitmap);
var pic:TPicture; FileExt:String;picJPG:TJPEGImage;picGIF:TGIFImage;
begin
pic:=TPicture.Create;
picJPG:=TJPEGImage.Create;
picGIF:=TGIFImage.Create;
try
pic.Assign(PicB);
FileExt:= ExtractFileEXT(FileName);
if (Uppercase(FileExt)=‘.JPG‘)or(Uppercase(FileExt)=‘.JPEG‘)
or(Uppercase(FileExt)=‘.JPE‘) then
begin
picJPG.Assign(PicB);
picJPG.SaveToFile(FileName);
end
else If (UpperCase(FileExt)=‘.BMP‘)OR(UpperCase(FileExt)=‘.DIB‘)THEN
Begin
PicB.SaveToFile(FileName);
end
else if (UpperCase(FileExt)=‘.GIF‘) then begin
picGIF.Assign(PicB);
PicGIF.SaveToFile(FileName);
end
else
Pic.SaveToFile(FileName);
{End If}
ShowPicture(PictureIndex,False,TPicture(PicB));
Finally
pic.Free;
picJPG.Free;
PicGIF.Free;
end;
end; |
探究Delphi的圖形處理 之三 -- GDI及Canvas類(lèi)簡(jiǎn)介 |
作者:何詠 發(fā)布日期:(2005-4-12 21:05:11)
聲明:本文著作權(quán)屬于何詠,如要轉(zhuǎn)載請(qǐng)聲明作者及出處。 |
2.3 GDI及Canvas類(lèi)簡(jiǎn)介
GDI(Graphics Device Interface,圖形設(shè)備接口)是Windows為我們提供的一個(gè)專(zhuān)門(mén)用于圖形繪制和屏幕輸出的類(lèi)庫(kù)。這個(gè)類(lèi)庫(kù)提供了許多繪圖函數(shù),使用這些函數(shù),我們幾乎可以開(kāi)發(fā)出所有的平面繪制、平面處理的程序。它同時(shí)也是Windows系統(tǒng)的核心,Windows系統(tǒng)中所有的繪圖任務(wù)都由這個(gè)庫(kù)來(lái)完成。在任何語(yǔ)言中,我們都可以調(diào)用這個(gè)庫(kù)來(lái)完成繪圖任務(wù)。
在Delphi中,我們已經(jīng)有了一個(gè)已經(jīng)封裝了絕大多數(shù)GDI函數(shù)的類(lèi)。使用這個(gè)類(lèi)我們可以方便的完成各種圖像處理任務(wù)。這就是我們要研究的Canvas類(lèi)。
探究Delphi的圖形處理 之四 -- Canvas類(lèi)中的基本繪圖方法 |
作者:何詠 發(fā)布日期:(2005-4-12 21:04:21)
聲明:本文著作權(quán)屬于何詠,如要轉(zhuǎn)載請(qǐng)聲明作者及出處。 |
Canvas類(lèi)中的基本繪圖方法
Canvas就是“畫(huà)布”的意思,使用Canvas類(lèi)中的繪圖方法,我們可以在這塊畫(huà)布上繪制各種圖形。我們也可以通過(guò)設(shè)置每一個(gè)象素的顏色值來(lái)完成對(duì)圖像的處理。下面列出了Canvas類(lèi)中的一些常用繪圖方法。
CopyRect(Dest:TRect;Canvas:TCanvas;Source:TRect);
此方法用于把Canvas所指定的畫(huà)布的一部分(由Source指定)復(fù)制到當(dāng)前畫(huà)布中。Dest參數(shù)指定了復(fù)制后的圖像在當(dāng)前畫(huà)布中的位置。例如下面的語(yǔ)句:
ThisCanvas.CopyRect(Rect(5,5,20,20),SourceCanvas,Rect(10,10,25,25));
可以把SourceCanvas中的(10,10,25,25)這一區(qū)域復(fù)制到ThisCanvas中的(5,5,20,20)區(qū)域中。
值得注意的是,TRect是一個(gè)Record類(lèi)型的變量,用Rect的構(gòu)造函數(shù)可以創(chuàng)建一個(gè)Rect變量。Rect類(lèi)型所指定的區(qū)域是一個(gè)長(zhǎng)方形,由(x1,y1,x2,y2)兩個(gè)點(diǎn)來(lái)確定。例如,Rect(10,10,25,25)所確定的就是下圖所示的區(qū)域:
Draw(x,y:Integer;Graphic:TGraphic);
此方法可以在當(dāng)前畫(huà)布中,以(x,y)為繪圖原點(diǎn)繪制由Graphic所指定的圖形或圖片。
Ellips(x1,y1,x2,y2:Integer);
此方法可以在當(dāng)前畫(huà)布中,以(x1,y1),(x2,y2)兩點(diǎn)所指定的矩形范圍內(nèi)繪制一個(gè)橢圓。并用畫(huà)筆中所指定的顏色作為線條顏色,筆刷的顏色作為填充顏色。
MoveTo(x,y:Integer);
把畫(huà)筆的位置移動(dòng)到點(diǎn)(x,y)。
LineTo(x,y:Integer);
從畫(huà)筆當(dāng)前的位置繪制一條直線到點(diǎn)(x,y),并把畫(huà)筆的位置移動(dòng)到(x,y);
Polygon(Points:array of TPoint);
以Points中的點(diǎn)為頂點(diǎn)繪制一個(gè)多邊形。并用畫(huà)筆中所指定的顏色作為線條顏色,筆刷的顏色作為填充顏色。
StretchDraw(Const Rect:TRect;Graphic : TGraphic);
此方法可以在由Rect所指定的區(qū)域中繪制圖片,圖片會(huì)根據(jù)Rect的大小自動(dòng)縮放。
Rectangle(x1,y1,x2,y2);
繪制由(x1,y1),(x2,y2)所確定的矩形。并用畫(huà)筆中所指定的顏色作為線條顏色,筆刷的顏色作為填充顏色。
TextOut(x,y:Integer;Text:String);
以(x,y)為原點(diǎn)繪制參數(shù)Text所指定的文字。
TextHeight(Text:String);
返回在當(dāng)前字體設(shè)置下,Text所指定的字符串的高度。
TextWidth(Text:String);
返回在當(dāng)前字體設(shè)置下,Text所指定的字符串的寬高度。
除了這些基本繪圖方法外,Canvas類(lèi)中還有一些重要的屬性,它們是:
Pen(畫(huà)筆)
這個(gè)屬性包含很多項(xiàng)目,其中Color指定了畫(huà)筆的顏色,Weight指定了畫(huà)筆的寬度,PenMode指定了畫(huà)筆繪圖的方式。
Brush(筆刷)
這個(gè)屬性主要決定了圖形的填充方式。Color指定了填充顏色,BrushStyle決定了填充方式。
Font(字體)
它決定了在Canvas中,使用TestOut命令畫(huà)出的文字的字體和字號(hào)。
Pixels(象素?cái)?shù)組)
這個(gè)數(shù)組包含了Canvas中每一個(gè)象素的顏色值。
在一般的編程中,我們?cè)谛枰M(jìn)行象素級(jí)的圖像調(diào)整時(shí),一般不使用Pixels屬性。在Bitmap(位圖)類(lèi)中提供了一個(gè)ScanLine屬性,使用它我們可以快速地進(jìn)行象素讀取和設(shè)置。這在后面的章節(jié)中有詳細(xì)的說(shuō)明。
Canvas類(lèi)中所提供的繪圖方法遠(yuǎn)遠(yuǎn)不止上面提到的這些,本文檔所羅列的只是我認(rèn)為最常用的方法,更多的信息可以參考Delphi的幫助系統(tǒng)。
探究Delphi的圖形處理 之五 -- 使用Canvas類(lèi)繪圖 |
作者:何詠 發(fā)布日期:(2005-4-12 21:03:21)
聲明:本文著作權(quán)屬于何詠,如要轉(zhuǎn)載請(qǐng)聲明作者及出處。 |
使用Canvas類(lèi)繪圖
我們知道,在Canvas類(lèi)中可以完成各種繪圖操作。仔細(xì)觀察,會(huì)發(fā)現(xiàn)在Delphi提供的許多組件中,都有Canvas類(lèi)。這是因?yàn)檫@些組件都繼承自TGraphicControl基類(lèi),這個(gè)基類(lèi)就提供了Canvas類(lèi)。但我們并不滿(mǎn)足于直接使用它們的Canvas來(lái)繪圖,這是沒(méi)有效率的。因?yàn)?/SPAN>TGraphicControl是一個(gè)可視化控件,當(dāng)我們?cè)谶@些控件上繪圖時(shí),繪制的圖形會(huì)即時(shí)地翻印到前臺(tái)(即用戶(hù)的屏幕)上,而很多時(shí)候,我們希望在繪圖結(jié)束后才將圖像翻到前臺(tái),這樣可以大大提高工作效率。這里就使用到了一個(gè)緩沖的思想。即在內(nèi)存中開(kāi)一塊空間,在這塊空間上繪圖,繪圖完后,再將這塊空間中的圖像翻印到前臺(tái)。
這里,我們可以使用Delphi為我們提供的TBitmap(位圖)類(lèi)。這個(gè)類(lèi)也提供了Canvas類(lèi),我們同樣可以在這個(gè)Canvas類(lèi)上繪圖。繪制完后,我們用 控件名.Canvas.Draw(0,0,Bitmap)把這個(gè)位圖翻到前臺(tái)。
下面的例子可以在PaintBox上繪制一個(gè)漸變顏色的矩形。
程序2.1
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TfrmMain = class(TForm)
PaintBox1: TPaintBox;
btnDraw: TButton;
procedure btnDrawClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
procedure TfrmMain.btnDrawClick(Sender: TObject);
var Bit:TBitmap;
i:Integer;
begin
Bit := TBitmap.Create;
try
Bit.Height := 300;
Bit.Width := 387;
For i := 0 to 200 do begin
Bit.Canvas.MoveTo(50,i+50);
Bit.Canvas.Pen.Color := RGB(0,0,Round((1-(i)/200) * 255));
Bit.Canvas.LineTo(350,i+50);
end;
PaintBox1.Canvas.Draw(0,0,Bit);
finally
Bit.Free;
end;
end;
end. |
程序的運(yùn)行結(jié)果如下:
現(xiàn)在說(shuō)明一下上面的程序。首先我們用 Bit:=TBitmap.Create;語(yǔ)句在內(nèi)存中創(chuàng)建一個(gè)位圖對(duì)象。然后分別設(shè)置了位圖的高度和寬度。接下來(lái)使用了一個(gè)循環(huán)語(yǔ)句一行一行地畫(huà)出顏色不斷加深的線條。最后在PaintBox的Canvas中把這個(gè)位圖復(fù)制過(guò)去。
事實(shí)上,這個(gè)程序只使用了極少量的繪圖方法,并不需要?jiǎng)?chuàng)建位圖對(duì)象繪圖。本程序使用位圖只是為了說(shuō)明的方便。
探究Delphi的圖形處理 之六 -- 使用ScanLine屬性進(jìn)行高效的圖像處理 |
作者:何詠 發(fā)布日期:(2005-4-12 21:02:19)
聲明:本文著作權(quán)屬于何詠,如要轉(zhuǎn)載請(qǐng)聲明作者及出處。 |
使用ScanLine屬性進(jìn)行高效的圖像處理
在上一節(jié)的例子中,我們使用了Bitmap類(lèi)。Bitmap是一個(gè)處理位圖圖像的類(lèi)。這個(gè)類(lèi)允許你載入、創(chuàng)建和處理位圖圖像。Delphi的圖形處理,都是使用Bitmap類(lèi)來(lái)完成的。當(dāng)然,用Bitmap類(lèi)來(lái)處理圖片并不意味著Delphi只能處理位圖圖像,你可以用支持其他圖片格式的類(lèi)將這些圖片載入,然后把它們轉(zhuǎn)為Bitmap格式,再使用Bitmap類(lèi)進(jìn)行處理,最后在把Bitmap格式轉(zhuǎn)換為想要輸出的格式即可。這在后面的章節(jié)中會(huì)詳細(xì)地講解。
上一節(jié)中,我們提到了Canvas的Pixels屬性,該屬性可以讀取和更改位圖中每一像素的顏色值,這一功能在圖形處理非常有用。因?yàn)閳D像處理濾鏡就是通過(guò)讀取每一像素的顏色值決定當(dāng)前像素新的顏色值,通過(guò)改變這些顏色值來(lái)實(shí)現(xiàn)各種效果。但是,通過(guò)實(shí)驗(yàn)我發(fā)現(xiàn),對(duì)于一個(gè)較大的圖像,使用Pixels屬性是非常慢的。處理一幅800*600的圖像竟然需要幾秒中的時(shí)間。這是因?yàn)?/SPAN>Pixels屬性的Read和write過(guò)程調(diào)用了GetPixel和SetPixe這兩個(gè)GDI繪圖函數(shù)。每次執(zhí)行SetPixels和GetPixel,都進(jìn)行了大量的重復(fù)運(yùn)算,這樣只要圖像越大,處理時(shí)間會(huì)成倍增長(zhǎng)。使用Pixels屬性來(lái)處理圖片肯定不可行。然而我發(fā)現(xiàn)了一種新的東西來(lái)取代Pixels屬性,這就是Bitmap類(lèi)的ScanLine屬性。ScanLine屬性返回一個(gè)位圖中一行像素的顏色值。而且ScanLine屬性在讀取圖片的時(shí)候使用了DIB(位)處理方法,這種處理方法比通常的SetPixel和GetPixel快得多。下面的實(shí)驗(yàn)就展示了使用Pixel屬性和使用ScanLine屬性的速度差異。
這個(gè)實(shí)驗(yàn)是分別使用Pixel屬性和ScanLine屬性把一個(gè)大小為600*450的圖片轉(zhuǎn)為灰度。把圖片轉(zhuǎn)為灰度的算法在后面的章節(jié)中有具體的介紹,這里不再解釋。以下是使用Pixel屬性完成任務(wù)的程序。
程序2.2
unit Unit1;
{使用Pixels屬性將圖像轉(zhuǎn)為灰度}
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
PaintBox1: TPaintBox;
btnConvert: TButton;
lblTime: TLabel;
procedure FormCreate(Sender: TObject);
procedure btnConvertClick(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure PaintBox1Paint(Sender: TObject);
private
Procedure DecodeColor(Const Color : TColor; var R,G,B:Byte);
public
{ Public declarations }
end;
var
Form1: TForm1;
Bit: TBitmap;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
Bit:=TBitmap.Create;
Bit.LoadFromFile(‘Test.bmp‘);
end;
procedure TForm1.btnConvertClick(Sender: TObject);
var i , j :Integer;NewColor:Byte; R,G,B:Byte; C:TColor; T:LongInt;
begin
T:=GetTickCount;
For i := 0 to bit.Width-1 do
begin
for j := 0 to bit.Height-1 do
begin
C:= Bit.Canvas.Pixels[i,j];
DecodeColor(C,R,G,B);
NewColor := (R+G+B) Div 3;
Bit.Canvas.Pixels[i,j] := RGB(NewColor,NewColor,NewColor);
end;
end;
T := GetTickCount -t;
LblTime.Caption := ‘用時(shí):‘ + IntToStr(T) + ‘ms‘;
PaintBox1.Canvas.StretchDraw(Rect(0,0,320,240),Bit);
end;
procedure TForm1.DecodeColor(const Color: TColor; var R, G, B: Byte);
begin
R := Color mod 256;
G := (color Div 256) mod 256;
B := Color Div 65536;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
Bit.Free;
end;
procedure TForm1.PaintBox1Paint(Sender: TObject);
begin
PaintBox1.Canvas.StretchDraw(Rect(0,0,320,240),Bit);
end;
end. |
以下是使用ScanLine屬性的程序:
程序2.3
unit Unit1;
{使用ScanLine屬性完成任務(wù)}
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
PaintBox1: TPaintBox;
lblTime: TLabel;
btnConvert: TButton;
procedure FormCreate(Sender: TObject);
procedure PaintBox1Paint(Sender: TObject);
procedure btnConvertClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
Bit : TBitmap;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
Bit := TBitmap.Create;
Bit.LoadFromFile(‘Test.Bmp‘);
Bit.PixelFormat := pf24Bit;
Bit.HandleType := bmDIB;
end;
procedure TForm1.PaintBox1Paint(Sender: TObject);
begin
PaintBox1.Canvas.StretchDraw(Rect(0,0,320,240),Bit);
end;
procedure TForm1.btnConvertClick(Sender: TObject);
var CurLine : PByteArray;
NewColor : Byte;
i : Integer;
j : Integer;
m : Integer;
t : LongInt;
begin
t:=GetTickCount;
For i := 0 to Bit.Height-1 do
begin
CurLine := Bit.ScanLine[i];
for j := 0 to Bit.Width-1 do begin
m := j *3;
NewColor := (curLine[m]+CurLine[m+1]+CurLine[m+2])Div 3;
CurLine[m] := NewColor;
CurLine[m+1] := NewColor;
CurLine[m+2] := NewColor;
end;
end;
t:=GetTickCount-t;
PaintBox1.Canvas.StretchDraw(Rect(0,0,320,240),Bit);
lblTime.Caption := ‘用時(shí):‘+IntToStr(t) + ‘ms‘;
end;
end. |
下面就來(lái)比較一下它們的運(yùn)行結(jié)果:
|
|
使用Pixels屬性的運(yùn)行結(jié)果 |
使用ScanLine屬性的運(yùn)行結(jié)果 |
現(xiàn)在,我們來(lái)詳細(xì)分析ScanLine屬性的具體用法。
ScanLine屬性是一個(gè)只讀屬性,它返回一個(gè)數(shù)組指針,存放當(dāng)前Bitmap第i行的像素顏色值。數(shù)組指針的類(lèi)型可以是PByteArray(字節(jié)數(shù)組指針)或者^array of TRGBTriple(像素顏色數(shù)組指針)。我覺(jué)得使用PByteArray類(lèi)型是最直接、最方便的,在本文中,我們都將使用PByteArray類(lèi)型。PByteArray類(lèi)型指向一個(gè)Byte類(lèi)型的一維數(shù)組。這個(gè)數(shù)組的第j*3個(gè)值表示當(dāng)前行第j個(gè)像素顏色值的B分值(藍(lán)色分值),第j*3+1個(gè)值表示當(dāng)前行第j個(gè)像素顏色值的G分值(綠藍(lán)色分值), 第j*3+2個(gè)值表示當(dāng)前行第j個(gè)像素顏色值的R分值(紅藍(lán)色分值)。其中j∈[0,圖像寬度-1]。
ScanLine[i]表示當(dāng)前Bitmap第i行的像素值。因此RGB(ScanLine[i][j*3+2],ScanLine[i][j*3+1], ScanLine[i][j*3])可以表示圖像中點(diǎn)(j,i)的顏色值。不過(guò)在程序中,這樣寫(xiě)是沒(méi)有效率的,我們?yōu)榱双@取一個(gè)像素就用了3次ScanLine,浪費(fèi)了很多時(shí)間。下面的代碼可以用ScanLine讀取整個(gè)Bitmap的像素值:
程序2.4
Type TPixels = Array of Array of TRGBTriple;
Procedure ReadPixel(Pic: Tbitmap; var tPix: TPixels);
Var PixPtr:PbyteArray;i,j,m:Integer;
begin
SetLength(tPix,Pic.Width,Pic.Height);
Pic.PixelFormat := pf24bit;
Pic.HandleType:=bmDIB;
For i :=0 to pic.Height-1 do begin
PixPtr:=Pic.ScanLine[i];
for j:= 0 to pic.Width-1 do begin
m := j*3;
tPix[j,i].rgbtBlue:=PixPtr[m];
tPix[j,i].rgbtGreen := PixPtr[m+1];
tPix[j,i].rgbtRed := PixPtr[m+2];
end;
end;
end; |
在此說(shuō)明一下上面的代碼。首先定義TPixels為一個(gè)二維動(dòng)態(tài)數(shù)組,類(lèi)型為TRGBTriple。TRGBTriple是一個(gè)記錄類(lèi)型,它可以保存一個(gè)像素值的R、G、B分值。下面是TRGBTriple的原形聲明:
TRGBTriple = tagRGBTRIPLE;
tagRGBTRIPLE = packed record
rgbtBlue: Byte;
rgbtGreen: Byte;
rgbtRed: Byte;
end; |
因此,TPixels類(lèi)型就可以表示Bitmap中所有像素的值。值得強(qiáng)調(diào)的是程序的第二行:
Pic.PixelFormat := pf24bit; |
這行代碼的作用是把這個(gè)位圖轉(zhuǎn)為24位位圖格式。因?yàn)橹挥?/SPAN>24位的位圖格式才符合上面所說(shuō)的規(guī)則。如果沒(méi)有這行代碼,當(dāng)程序碰上非24位位圖的文件時(shí)就不能正常運(yùn)行。至于Pic.HandleType := bmDIB;這行代碼是為了強(qiáng)制把當(dāng)前Bitmap的操作方式轉(zhuǎn)化為DIB方式,這只是為了確保萬(wàn)無(wú)一失。
那么,既然ScanLine屬性是只讀的,我們?nèi)绾胃淖冞@些顏色值呢?我們知道,ScanLine屬性返回的是一個(gè)指針。既然是指針,我們就可以改變指針?biāo)赶虻臄?shù)據(jù),通過(guò)這種方式就可以改變Bitmap中的顏色值了。下面的程序段演示了如何把一個(gè)TPixels變量寫(xiě)到Bitmap中去。
程序2.5
Procedure WritePixel(Pic: TBitmap; tPix: TPixels);
var PixPtr:PByteArray;i,j,m:Integer;
begin
pic.PixelFormat := pf24bit;
pic.HandleType:=bmDIB;
Pic.Height := High(tPix[0])+1;
Pic.Width:= High(tPix)+1;
For i :=0 to pic.Height-1 do begin
PixPtr:=Pic.ScanLine[i];
for j:= 0 to pic.Width-1 do begin
m := j*3;
PixPtr[M] := tPix[j,i].rgbtBlue;
PixPtr[m+1] := tPix[j,i].rgbtGreen;
PixPtr[m+2] := tPix[j,i].rgbtRed;
end;
end;
end; |
這樣,我們?cè)趫D形處理時(shí),就可以先用ReadPixel過(guò)程把位圖讀到一個(gè)TPixels類(lèi)型的變量中去,然后處理這個(gè)TPixels變量,處理完后,用WritePixel過(guò)程把這個(gè)變量寫(xiě)到Bitmap中去,這就完成了修改過(guò)程。
|