程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> GdiPlus[57]: 圖像(九) IGPBitmap 特有的屬性與方法

GdiPlus[57]: 圖像(九) IGPBitmap 特有的屬性與方法

編輯:Delphi

這包括:

//屬性  
IGPBitmap.Pixels;     { 獲取或設置指定像素的顏色 }  
 
//方法  
IGPBitmap.SetResolution(); { 設置分辨率 }  
IGPBitmap.GetHBitmap();  { 創建 GDI 格式的 Bitmap 並返回句柄 }  
IGPBitmap.GetHIcon;    { 創建 Icon 文件並返回句柄 }  
IGPBitmap.LockBits();   { 鎖定對象中內存中的像素數據 }  
IGPBitmap.UnlockBits();  { 解除 LockBits 的鎖定 }  

  IGPBitmap.Pixels 屬性測試:

GdiPlus[57]: 圖像(九) IGPBitmap 特有的屬性與方法

uses GdiPlus; 
 
procedure TForm1.FormPaint(Sender: TObject); 
var 
 Bitmap: IGPBitmap; 
 Graphics: IGPGraphics; 
 i,j: Integer; 
 Color: TGPColor; 
begin 
 Bitmap := TGPBitmap.Create('C:\GdiPlusImg\Grapes.jpg' ); 
 
 Graphics := TGPGraphics.Create(Handle); 
 Graphics.DrawImage(Bitmap, 10 , 10, Bitmap.Width, Bitmap.Height); 
 
 for i := 0 to Bitmap.Width - 1 do for j := 0 to Bitmap.Height - 1 do 
 begin 
  Color := Bitmap.Pixels[i,j]; 
  Bitmap.Pixels[i,j] := TGPColor.Create(Color.R, 0, 0); 
 end; 
 
 Graphics.TranslateTransform(Bitmap.Width + 10, 0); 
 Graphics.DrawImage(Bitmap, 10, 10, Bitmap.Width, Bitmap.Height); 
end; 

 IGPBitmap.SetResolution 方法測試:

GdiPlus[57]: 圖像(九) IGPBitmap 特有的屬性與方法

uses GdiPlus; 
 
procedure TForm1.FormPaint(Sender: TObject); 
var 
 Graphics: IGPGraphics; 
 Bitmap: IGPBitmap; 
begin 
 Bitmap := TGPBitmap.Create('C:\GdiPlusImg\Apple.gif'); 
 Graphics := TGPGraphics.Create(Handle); 
 Graphics.DrawImage(Bitmap, 10, 10); 
 Graphics.TranslateTransform(Bitmap.Width + 10, 0); 
 
 Bitmap.SetResolution(Bitmap.HorizontalResolution/2, Bitmap.VerticalResolution/2); 
 Graphics.DrawImage(Bitmap, 10, 10); 
end; 

  IGPBitmap.GetHBitmap 方法測試:

GdiPlus[57]: 圖像(九) IGPBitmap 特有的屬性與方法

uses GdiPlus; 
 
procedure TForm1.FormPaint(Sender: TObject); 
var 
 Bitmap: IGPBitmap; 
 GDIBitmap: TBitmap; 
begin 
 Bitmap := TGPBitmap.Create('C:\GdiPlusImg\Bird.bmp'); 
 
 GDIBitmap := TBitmap.Create; 
 //GetHBitmap 的參數是背景色, 用於填充透明部分; 無透明部分時它會被忽略 
 GDIBitmap.Handle := Bitmap.GetHBitmap($FF00000); 
 GDIBitmap.TransparentColor := clWhite; 
 GDIBitmap.Transparent := True; 
 Canvas.Draw(10, 10, GDIBitmap); 
 GDIBitmap.Free; 
end; 

 IGPBitmap.GetHIcon 方法測試:

GdiPlus[57]: 圖像(九) IGPBitmap 特有的屬性與方法

uses GdiPlus; 
 
var Bitmap: IGPBitmap; 
 
procedure TForm1.FormPaint(Sender: TObject); 
var 
 Image: IGPImage; 
 Rect: TGPRect; 
 Graphics: IGPGraphics; 
 Attr: IGPImageAttributes; 
begin 
 Image := TGPImage.Create('C:\GdiPlusImg\Apple.gif'); 
 Bitmap := TGPBitmap.Create(16, 16); 
 Rect.Initialize(0, 0, Bitmap.Width, Bitmap.Height); 
 Graphics := TGPGraphics.Create(Bitmap); 
 Attr := TGPImageAttributes.Create; 
 Attr.SetColorKey($FFFFFFFF, $FFFFFFFF); 
 Graphics.DrawImage(Image, Rect, 0, 0, Image.Width, Image.Height, UnitPixel, Attr); 
 
 Application.Icon.Handle := Bitmap.GetHIcon; 
end; 

  接下來都是和 IGPBitmap.LockBits、IGPBitmap.UnlockBits 相關的了.

  在 WinAPI 中常常見到類似的函數, 因為內存中 Windows 的自動管理下, 數據中內存中的地址是動態的, 所以操作內存前一般要先鎖定, 鎖定後再給你指針; 這裡的 LockBits 也是這樣, 當然操作完成後要解鎖.

  這裡的 LockBits 返回的不僅僅有內存地址, 而是包含內存地址的一個結構:

TGPBitmapData = record 
 Width: Cardinal;       { 像素寬度; 或者說是一個掃描行中的像素數 } 
 Height: Cardinal;      { 像素高度; 或者說是掃描行數 } 
 Stride: Integer;       { 每一行的掃描寬度; 它應該是 4 的倍數 } 
 PixelFormat: TGPPixelFormat; { 像素格式信息 } 
 Scan0: Pointer;       { 第一個像素數據的地址 } 
 Reserved: Cardinal;     { 保留 } 
end; 


 結構中只有 Stride 比較費解, 它是每行所占用的字節數, 譬如:

  一行有 11 個像素(Width = 11), 對一個 32 位(每個像素 4 字節)的圖像, Stride = 11 * 4 = 44.

  但還有個字節對齊的問題, 譬如:

  一行有 11 個像素(Width = 11), 對一個 24 位(每個像素 3 字節)的圖像, Stride = 11 * 3 + 3 = 36.

  為什麼不是 Stride = 33? 因為它是按 4 字節對齊的.

  根據上面道理, 我們可以手動計算 Stride 的值:

  1、Stride = 每像素占用的字節數(也就是像素位數/8) * Width;

  2、如果 Stride 不是 4 的倍數, 那麼 Stride = Stride + (4 - Stride mod 4);

  下面的例子測試和驗證了上面的思路:

uses GdiPlus; 
 
procedure TForm1.FormPaint(Sender: TObject); 
var 
 Bitmap: IGPBitmap; 
 Rect: TGPRect; 
 Graphics: IGPGraphics; 
 Data: TGPBitmapData; 
 n: Integer; 
begin 
 ChDir('C:\GdiPlusImg\'); 
 Bitmap := TGPBitmap.Create('Grapes.jpg'); 
 Rect.Initialize(0, 0, Bitmap.Width, Bitmap.Height); 
 Data := Bitmap.LockBits(Rect, [ImageLockModeRead], Bitmap.PixelFormat); 
 n := GetPixelFormatSize(Data.PixelFormat) div 8 * Data.Width; 
 n := n + (4 - n mod 4); 
 ShowMessageFmt('%d, %d, %d', [Data.Width, Data.Stride, n]); { 187, 564, 564 } 
 Bitmap.UnlockBits(Data); 
 
 // 
 Bitmap := TGPBitmap.Create('Bird.bmp'); 
 Rect.Initialize(0, 0, Bitmap.Width, Bitmap.Height); 
 Data := Bitmap.LockBits(Rect, [ImageLockModeRead], Bitmap.PixelFormat); 
 n := GetPixelFormatSize(Data.PixelFormat) div 8 * Data.Width; 
 if n mod 4 <> 0 then n := n + (4 - n mod 4); 
 ShowMessageFmt('%d, %d, %d', [Data.Width, Data.Stride, n]); { 110, 112, 112 } 
 Bitmap.UnlockBits(Data); 
 
 // 
 Bitmap := TGPBitmap.Create('Apple.gif'); 
 Rect.Initialize(0, 0, Bitmap.Width, Bitmap.Height); 
 Data := Bitmap.LockBits(Rect, [ImageLockModeRead], Bitmap.PixelFormat); 
 n := GetPixelFormatSize(Data.PixelFormat) div 8 * Data.Width; 
 if n mod 4 <> 0 then n := n + (4 - n mod 4); 
 ShowMessageFmt('%d, %d, %d', [Data.Width, Data.Stride, n]); { 120, 120, 120 } 
 Bitmap.UnlockBits(Data); 
 
 // 
 Bitmap := TGPBitmap.Create('ImageFileSmall.jpg'); 
 Rect.Initialize(0, 0, Bitmap.Width, Bitmap.Height); 
 Data := Bitmap.LockBits(Rect, [ImageLockModeRead], Bitmap.PixelFormat); 
 n := GetPixelFormatSize(Data.PixelFormat) div 8 * Data.Width; 
 if n mod 4 <> 0 then n := n + (4 - n mod 4); 
 ShowMessageFmt('%d, %d, %d', [Data.Width, Data.Stride, n]); { 320, 960, 960 } 
 Bitmap.UnlockBits(Data); 
end; 


面對不同像素格式的圖像, 這個字節對齊的問題的確很麻煩;

  但對 32 位的圖像就可以不考慮這個問題了, 因為它每行的數據剛好是 4 的倍數; 下面的練習就都討了這個巧.

  另外還要知道: 32 位圖像每個像素的 4 個字節在內存中的排列次序是: BBGGRRAA.

  通過修改內存數據使圖像半透明的例子:

GdiPlus[57]: 圖像(九) IGPBitmap 特有的屬性與方法

uses GdiPlus; 
 
type 
 PPixelFour = ^TPixelFour; 
 TPixelFour = record 
  b1,b2,b3,b4: Byte; 
 end; 
 
procedure TForm1.FormPaint(Sender: TObject); 
var 
 Bitmap, Bitmap32: IGPBitmap; 
 Graphics: IGPGraphics; 
 Rect: TGPRect; 
 Data: TGPBitmapData; 
 P: PPixelFour; 
 Brush: IGPHatchBrush; 
 i: Integer; 
begin 
 Bitmap := TGPBitmap.Create('C:\GdiPlusImg\Grapes.jpg'); 
 Rect.Initialize(0, 0, Bitmap.Width, Bitmap.Height); 
 
 // 獲取一個 32 位像素格式的圖像; 
 // 好像 IGPBitmap.Clone 方法就可以方便做這個轉換, 但不靈; 
 // 還有一個 IGPBitmap.ConvertFormat 方法是 GDI+1.1 的; 
 // 只能手動了 
 Bitmap32 := TGPBitmap.Create(Bitmap.Width, Bitmap.Height, PixelFormat32bppARGB); 
 Graphics := TGPGraphics.Create(Bitmap32); 
 Graphics.DrawImage(Bitmap, Rect); 
 
 Data := Bitmap32.LockBits(Rect, [ImageLockModeWrite], Bitmap32.PixelFormat); 
 P := Data.Scan0; 
 {$PointerMath On} 
 for i := 0 to Data.Width * Data.Height - 1 do P[i].b4 := $80; 
 {$PointerMath Off} 
 Bitmap32.UnlockBits(Data); 
 
 Graphics := TGPGraphics.Create(Handle); 
 Brush := TGPHatchBrush.Create(HatchStyleCross, $FFD0D0D0, $FFFFFFFF); 
 Graphics.FillRectangle(Brush, TGPRect.Create(ClIEntRect)); 
 Graphics.DrawImage(Bitmap32, 10, 10); 
end; 


 通過修改內存數據只保留圖像綠色通道的例子:

GdiPlus[57]: 圖像(九) IGPBitmap 特有的屬性與方法

uses GdiPlus; 
 
type 
 PPixelFour = ^TPixelFour; 
 TPixelFour = record 
  b1,b2,b3,b4: Byte; 
 end; 
 
procedure TForm1.FormPaint(Sender: TObject); 
var 
 Bitmap, Bitmap32: IGPBitmap; 
 Graphics: IGPGraphics; 
 Rect: TGPRect; 
 Data: TGPBitmapData; 
 P: PPixelFour; 
 i: Integer; 
begin 
 Bitmap := TGPBitmap.Create('C:\GdiPlusImg\Grapes.jpg'); 
 Rect.Initialize(0, 0, Bitmap.Width, Bitmap.Height); 
 
 // 獲取一個 32 位像素格式的圖像; 
 Bitmap32 := TGPBitmap.Create(Bitmap.Width, Bitmap.Height, PixelFormat32bppARGB); 
 Graphics := TGPGraphics.Create(Bitmap32); 
 Graphics.DrawImage(Bitmap, Rect); 
 
 Data := Bitmap32.LockBits(Rect, [ImageLockModeWrite], Bitmap32.PixelFormat); 
 P := Data.Scan0; 
 {$PointerMath On} 
 for i := 0 to Data.Width * Data.Height - 1 do 
 begin 
  P[i].b1 := 0; 
  P[i].b3 := 0; 
 end; 
 {$PointerMath Off} 
 Bitmap32.UnlockBits(Data); 
 
 Graphics := TGPGraphics.Create(Handle); 
 Graphics.DrawImage(Bitmap32, 10, 10); 
end; 

通過修改內存數據使圖像轉灰度的例子:

GdiPlus[57]: 圖像(九) IGPBitmap 特有的屬性與方法

uses GdiPlus; 
 
type 
 PPixelFour = ^TPixelFour; 
 TPixelFour = record 
  b1,b2,b3,b4: Byte; 
 end; 
 
procedure TForm1.FormPaint(Sender: TObject); 
var 
 Bitmap, Bitmap32: IGPBitmap; 
 Graphics: IGPGraphics; 
 Rect: TGPRect; 
 Data: TGPBitmapData; 
 P: PPixelFour; 
 i,g: Integer; 
begin 
 Bitmap := TGPBitmap.Create('C:\GdiPlusImg\Grapes.jpg'); 
 Rect.Initialize(0, 0, Bitmap.Width, Bitmap.Height); 
 
 // 獲取一個 32 位像素格式的圖像; 
 Bitmap32 := TGPBitmap.Create(Bitmap.Width, Bitmap.Height, PixelFormat32bppARGB); 
 Graphics := TGPGraphics.Create(Bitmap32); 
 Graphics.DrawImage(Bitmap, Rect); 
 
 Data := Bitmap32.LockBits(Rect, [ImageLockModeWrite], Bitmap32.PixelFormat); 
 P := Data.Scan0; 
 {$PointerMath On} 
 for i := 0 to Data.Width * Data.Height - 1 do 
 begin 
  g := (P[i].b1 + P[i].b2 + P[i].b3) div 3; 
  P[i].b1 := g; 
  P[i].b2 := g; 
  P[i].b3 := g; 
 end; 
 {$PointerMath Off} 
 Bitmap32.UnlockBits(Data); 
 
 Graphics := TGPGraphics.Create(Handle); 
 Graphics.DrawImage(Bitmap32, 10, 10); 
end; 

  另外:

  1、其實僅就顏色變換, 以上手段都不如使用 ColorMatrix 來得方便;

  2、以上內存操作, 換用 IGPBitmap.Pixels 也都能更方便地做到, 但它太慢了.

  3、在處理字節偏移時或許 IGPImageBytes 接口是個解決方案, 不過沒有測試.





  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved