| 
| 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)中,一切要輸出到屏幕上的東西都是通過圖形處理這部分的內(nèi)容來實(shí)現(xiàn)的。比如一個(gè)程序使用了標(biāo)簽控件,它看起來似乎并沒有用到什么圖形處理,但實(shí)際上標(biāo)簽控件就是通過使用GDI庫中的圖形處理函數(shù)來實(shí)現(xiàn)的??梢妶D形處理在編程中的重要性。 圖像處理在實(shí)際的應(yīng)用中也極具價(jià)值。平面制作、動(dòng)畫制作等都離不開它。這一部分的內(nèi)容十分繁多。我本次研究的內(nèi)容,只是其中最基礎(chǔ)的、最重要一部分。  
| 探究Delphi的圖形處理 之二 -- 基本圖像處理函數(shù) |  
| 作者:何詠     發(fā)布日期:(2005-4-12 21:06:29) 聲明:本文著作權(quán)屬于何詠,如要轉(zhuǎn)載請(qǐng)聲明作者及出處。  |    第二章           圖像處理函數(shù) 
2.1 為什么選擇Delphi        所有的可視化編程語言都能夠進(jìn)行圖像處理。但由于這些語言的定位不同,它們?cè)谶M(jìn)行圖形處理的效率和便捷程度上也各不相同。實(shí)際上,Visual C 的圖像處理效率是最高的,這是由于GDI類庫本身就是用C++寫的。但是使用VC來編程并不是一件方便的事,因?yàn)檫@個(gè)語言本身就較為繁雜難懂,所以我沒有選擇它。Visual Basic(VB)也是一個(gè)常用的語言,但它在圖形處理方面能力較差。首先是它的坐標(biāo)系統(tǒng)是以twip為單位的浮點(diǎn)坐標(biāo)系統(tǒng),在調(diào)用GDI類庫時(shí),必須對(duì)坐標(biāo)系統(tǒng)進(jìn)行轉(zhuǎn)換,浪費(fèi)了大量的資源,編程起來較為麻煩。在多方面因素的影響下,我覺得Delphi是一個(gè)理想的語言。Delphi已經(jīng)把絕大多數(shù)GDI繪圖函數(shù)都封裝成可直接調(diào)用的類,使用它進(jìn)行圖形處理操作十分方便,而且Delphi 是Pascal演變而來的,Pascal具有嚴(yán)謹(jǐn)易讀的特點(diǎn),因此很容易上手。 2.2  Delphi中用于圖形處理的類 Delphi為我們提供了許多圖形圖像方面的類,合理地使用這些類,我們可以方便地開發(fā)出各種圖形處理程序。這些類有TPicture、TBitmap、TGraphic、TIcon、TJPEGImage和TCanvas。其中,TCanvas類用于繪圖,TPicture、TBitmap、TIcon和TJPEGImage是專門用來處理圖片的類,TGraphic是一個(gè)抽象類,一般不直接使用。TPicture類可以載入所有支持的圖片,而TBitmap、TIcon、TJPEGImage分別用于處理各種類型的圖片。在實(shí)際應(yīng)用中,我們一般用這些具體類型的類載入圖片,再將圖片轉(zhuǎn)為Bitmap格式來處理。TPicture、TIcon、TJPEGImage類一般只用于輸入和輸出。例如,下面的代碼可以載入一幅任意支持格式的圖片(Delphi所支持的格式為bmp、jpg、dib、wmf和emf)。 
| Var Pic:TPicture; Begin   Pic := TPicture.Create;   Pic.LoadFromFile(FileName); End; |  用TPicture類來載入圖片時(shí),該類會(huì)根據(jù)文件名的擴(kuò)展名來決定用何等方式來打開圖片。這就出現(xiàn)了一個(gè)問題,如果這個(gè)圖片的擴(kuò)展名被用戶非法修改,程序就會(huì)把這個(gè)圖片視為無效圖片。在真正編程中,我們要用TPicture、TBitmap、TJPEGImage、TIcon依次嘗試去打開圖片。 另外,Delphi本身是不支持GIF文件格式的。我們可以借用一個(gè)第三方的類——GIFImage來讓Delphi支持它。這個(gè)類在附帶的光盤中可以找到。最終我們用下面的代碼來完成載入圖片的操作。 
| 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(‘無效圖片‘);                          BitMap.Canvas.TextOut(0,0,‘無效圖片‘);                       end;{try}                   end;                 end;{try}              end;{try}     end;{try}    Pic.Free ;    bit.Free;    jpgPic.Free;    icoPic.Free;    FGifPic.Free; end; |  保存圖片的方法跟打開圖片的方法類似,我們可以使用不同類型的圖片類的SaveToFile方法保存文件。下面的代碼可以根據(jù)文件名中擴(kuò)展名的不同使用不同的類來保存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類簡(jiǎn)介 |  
| 作者:何詠     發(fā)布日期:(2005-4-12 21:05:11) 聲明:本文著作權(quán)屬于何詠,如要轉(zhuǎn)載請(qǐng)聲明作者及出處。  |    
2.3 GDI及Canvas類簡(jiǎn)介 GDI(Graphics Device Interface,圖形設(shè)備接口)是Windows為我們提供的一個(gè)專門用于圖形繪制和屏幕輸出的類庫。這個(gè)類庫提供了許多繪圖函數(shù),使用這些函數(shù),我們幾乎可以開發(fā)出所有的平面繪制、平面處理的程序。它同時(shí)也是Windows系統(tǒng)的核心,Windows系統(tǒng)中所有的繪圖任務(wù)都由這個(gè)庫來完成。在任何語言中,我們都可以調(diào)用這個(gè)庫來完成繪圖任務(wù)。 在Delphi中,我們已經(jīng)有了一個(gè)已經(jīng)封裝了絕大多數(shù)GDI函數(shù)的類。使用這個(gè)類我們可以方便的完成各種圖像處理任務(wù)。這就是我們要研究的Canvas類。  
| 探究Delphi的圖形處理 之四 -- Canvas類中的基本繪圖方法 |  
| 作者:何詠     發(fā)布日期:(2005-4-12 21:04:21) 聲明:本文著作權(quán)屬于何詠,如要轉(zhuǎn)載請(qǐng)聲明作者及出處。  |    
Canvas類中的基本繪圖方法        Canvas就是“畫布”的意思,使用Canvas類中的繪圖方法,我們可以在這塊畫布上繪制各種圖形。我們也可以通過設(shè)置每一個(gè)象素的顏色值來完成對(duì)圖像的處理。下面列出了Canvas類中的一些常用繪圖方法。        CopyRect(Dest:TRect;Canvas:TCanvas;Source:TRect);        此方法用于把Canvas所指定的畫布的一部分(由Source指定)復(fù)制到當(dāng)前畫布中。Dest參數(shù)指定了復(fù)制后的圖像在當(dāng)前畫布中的位置。例如下面的語句:        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類型的變量,用Rect的構(gòu)造函數(shù)可以創(chuàng)建一個(gè)Rect變量。Rect類型所指定的區(qū)域是一個(gè)長(zhǎng)方形,由(x1,y1,x2,y2)兩個(gè)點(diǎn)來確定。例如,Rect(10,10,25,25)所確定的就是下圖所示的區(qū)域: 
        Draw(x,y:Integer;Graphic:TGraphic);        此方法可以在當(dāng)前畫布中,以(x,y)為繪圖原點(diǎn)繪制由Graphic所指定的圖形或圖片。        Ellips(x1,y1,x2,y2:Integer);        此方法可以在當(dāng)前畫布中,以(x1,y1),(x2,y2)兩點(diǎn)所指定的矩形范圍內(nèi)繪制一個(gè)橢圓。并用畫筆中所指定的顏色作為線條顏色,筆刷的顏色作為填充顏色。        MoveTo(x,y:Integer);        把畫筆的位置移動(dòng)到點(diǎn)(x,y)。        LineTo(x,y:Integer);        從畫筆當(dāng)前的位置繪制一條直線到點(diǎn)(x,y),并把畫筆的位置移動(dòng)到(x,y);        Polygon(Points:array of TPoint);        以Points中的點(diǎn)為頂點(diǎn)繪制一個(gè)多邊形。并用畫筆中所指定的顏色作為線條顏色,筆刷的顏色作為填充顏色。        StretchDraw(Const Rect:TRect;Graphic : TGraphic);        此方法可以在由Rect所指定的區(qū)域中繪制圖片,圖片會(huì)根據(jù)Rect的大小自動(dòng)縮放。        Rectangle(x1,y1,x2,y2);        繪制由(x1,y1),(x2,y2)所確定的矩形。并用畫筆中所指定的顏色作為線條顏色,筆刷的顏色作為填充顏色。        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類中還有一些重要的屬性,它們是:        Pen(畫筆)        這個(gè)屬性包含很多項(xiàng)目,其中Color指定了畫筆的顏色,Weight指定了畫筆的寬度,PenMode指定了畫筆繪圖的方式。        Brush(筆刷) 這個(gè)屬性主要決定了圖形的填充方式。Color指定了填充顏色,BrushStyle決定了填充方式。 Font(字體) 它決定了在Canvas中,使用TestOut命令畫出的文字的字體和字號(hào)。 Pixels(象素?cái)?shù)組) 這個(gè)數(shù)組包含了Canvas中每一個(gè)象素的顏色值。 在一般的編程中,我們?cè)谛枰M(jìn)行象素級(jí)的圖像調(diào)整時(shí),一般不使用Pixels屬性。在Bitmap(位圖)類中提供了一個(gè)ScanLine屬性,使用它我們可以快速地進(jìn)行象素讀取和設(shè)置。這在后面的章節(jié)中有詳細(xì)的說明。 Canvas類中所提供的繪圖方法遠(yuǎn)遠(yuǎn)不止上面提到的這些,本文檔所羅列的只是我認(rèn)為最常用的方法,更多的信息可以參考Delphi的幫助系統(tǒng)。  
| 探究Delphi的圖形處理 之五 -- 使用Canvas類繪圖 |  
| 作者:何詠     發(fā)布日期:(2005-4-12 21:03:21) 聲明:本文著作權(quán)屬于何詠,如要轉(zhuǎn)載請(qǐng)聲明作者及出處。  |    使用Canvas類繪圖 我們知道,在Canvas類中可以完成各種繪圖操作。仔細(xì)觀察,會(huì)發(fā)現(xiàn)在Delphi提供的許多組件中,都有Canvas類。這是因?yàn)檫@些組件都繼承自TGraphicControl基類,這個(gè)基類就提供了Canvas類。但我們并不滿足于直接使用它們的Canvas來繪圖,這是沒有效率的。因?yàn)?/SPAN>TGraphicControl是一個(gè)可視化控件,當(dāng)我們?cè)谶@些控件上繪圖時(shí),繪制的圖形會(huì)即時(shí)地翻印到前臺(tái)(即用戶的屏幕)上,而很多時(shí)候,我們希望在繪圖結(jié)束后才將圖像翻到前臺(tái),這樣可以大大提高工作效率。這里就使用到了一個(gè)緩沖的思想。即在內(nèi)存中開一塊空間,在這塊空間上繪圖,繪圖完后,再將這塊空間中的圖像翻印到前臺(tái)。 這里,我們可以使用Delphi為我們提供的TBitmap(位圖)類。這個(gè)類也提供了Canvas類,我們同樣可以在這個(gè)Canvas類上繪圖。繪制完后,我們用 控件名.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)在說明一下上面的程序。首先我們用 Bit:=TBitmap.Create;語句在內(nèi)存中創(chuàng)建一個(gè)位圖對(duì)象。然后分別設(shè)置了位圖的高度和寬度。接下來使用了一個(gè)循環(huán)語句一行一行地畫出顏色不斷加深的線條。最后在PaintBox的Canvas中把這個(gè)位圖復(fù)制過去。 事實(shí)上,這個(gè)程序只使用了極少量的繪圖方法,并不需要?jiǎng)?chuàng)建位圖對(duì)象繪圖。本程序使用位圖只是為了說明的方便。 
| 探究Delphi的圖形處理 之六 -- 使用ScanLine屬性進(jìn)行高效的圖像處理 |  
| 作者:何詠     發(fā)布日期:(2005-4-12 21:02:19) 聲明:本文著作權(quán)屬于何詠,如要轉(zhuǎn)載請(qǐng)聲明作者及出處。  |    使用ScanLine屬性進(jìn)行高效的圖像處理 在上一節(jié)的例子中,我們使用了Bitmap類。Bitmap是一個(gè)處理位圖圖像的類。這個(gè)類允許你載入、創(chuàng)建和處理位圖圖像。Delphi的圖形處理,都是使用Bitmap類來完成的。當(dāng)然,用Bitmap類來處理圖片并不意味著Delphi只能處理位圖圖像,你可以用支持其他圖片格式的類將這些圖片載入,然后把它們轉(zhuǎn)為Bitmap格式,再使用Bitmap類進(jìn)行處理,最后在把Bitmap格式轉(zhuǎn)換為想要輸出的格式即可。這在后面的章節(jié)中會(huì)詳細(xì)地講解。 上一節(jié)中,我們提到了Canvas的Pixels屬性,該屬性可以讀取和更改位圖中每一像素的顏色值,這一功能在圖形處理非常有用。因?yàn)閳D像處理濾鏡就是通過讀取每一像素的顏色值決定當(dāng)前像素新的顏色值,通過改變這些顏色值來實(shí)現(xiàn)各種效果。但是,通過實(shí)驗(yàn)我發(fā)現(xiàn),對(duì)于一個(gè)較大的圖像,使用Pixels屬性是非常慢的。處理一幅800*600的圖像竟然需要幾秒中的時(shí)間。這是因?yàn)?/SPAN>Pixels屬性的Read和write過程調(diào)用了GetPixel和SetPixe這兩個(gè)GDI繪圖函數(shù)。每次執(zhí)行SetPixels和GetPixel,都進(jìn)行了大量的重復(fù)運(yùn)算,這樣只要圖像越大,處理時(shí)間會(huì)成倍增長(zhǎng)。使用Pixels屬性來處理圖片肯定不可行。然而我發(fā)現(xiàn)了一種新的東西來取代Pixels屬性,這就是Bitmap類的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. |  下面就來比較一下它們的運(yùn)行結(jié)果: 
 
| 
 | 
 |  
| 使用Pixels屬性的運(yùn)行結(jié)果 | 使用ScanLine屬性的運(yùn)行結(jié)果 |  現(xiàn)在,我們來詳細(xì)分析ScanLine屬性的具體用法。 ScanLine屬性是一個(gè)只讀屬性,它返回一個(gè)數(shù)組指針,存放當(dāng)前Bitmap第i行的像素顏色值。數(shù)組指針的類型可以是PByteArray(字節(jié)數(shù)組指針)或者^array of TRGBTriple(像素顏色數(shù)組指針)。我覺得使用PByteArray類型是最直接、最方便的,在本文中,我們都將使用PByteArray類型。PByteArray類型指向一個(gè)Byte類型的一維數(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)的顏色值。不過在程序中,這樣寫是沒有效率的,我們?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; |         在此說明一下上面的代碼。首先定義TPixels為一個(gè)二維動(dòng)態(tài)數(shù)組,類型為TRGBTriple。TRGBTriple是一個(gè)記錄類型,它可以保存一個(gè)像素值的R、G、B分值。下面是TRGBTriple的原形聲明: 
 
| TRGBTriple = tagRGBTRIPLE; tagRGBTRIPLE = packed record     rgbtBlue: Byte;     rgbtGreen: Byte;     rgbtRed: Byte;   end;   |  因此,TPixels類型就可以表示Bitmap中所有像素的值。值得強(qiáng)調(diào)的是程序的第二行: 
 
| Pic.PixelFormat := pf24bit; |  這行代碼的作用是把這個(gè)位圖轉(zhuǎn)為24位位圖格式。因?yàn)橹挥?/SPAN>24位的位圖格式才符合上面所說的規(guī)則。如果沒有這行代碼,當(dāng)程序碰上非24位位圖的文件時(shí)就不能正常運(yùn)行。至于Pic.HandleType := bmDIB;這行代碼是為了強(qiáng)制把當(dāng)前Bitmap的操作方式轉(zhuǎn)化為DIB方式,這只是為了確保萬無一失。        那么,既然ScanLine屬性是只讀的,我們?nèi)绾胃淖冞@些顏色值呢?我們知道,ScanLine屬性返回的是一個(gè)指針。既然是指針,我們就可以改變指針?biāo)赶虻臄?shù)據(jù),通過這種方式就可以改變Bitmap中的顏色值了。下面的程序段演示了如何把一個(gè)TPixels變量寫到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過程把位圖讀到一個(gè)TPixels類型的變量中去,然后處理這個(gè)TPixels變量,處理完后,用WritePixel過程把這個(gè)變量寫到Bitmap中去,這就完成了修改過程。 |