# C# Bitmap
可以用來 建立、修改、顯示、存取影像像素、儲存影像 等。
通常會搭配 Graphics、PictureBox、Image 等一起使用。
[TOC]
## 創建新的 Bitmap
可以用 new Bitmap(width, height) 來建立新的圖片。
```csharp=
Bitmap bmp = new Bitmap(200, 100); // 建立 200x100 大小的影像
```
## 繪圖
用 Graphics 來繪製線條、文字、圖案等:
```csharp=
using System.Drawing;
Bitmap bmp = new Bitmap(200, 100);
using (Graphics g = Graphics.FromImage(bmp))
{
g.Clear(Color.White); // 設定背景為白色
g.DrawLine(Pens.Black, 10, 10, 190, 90); // 畫一條黑色線
g.DrawString("Hello Bitmap!", new Font("Arial", 12), Brushes.Red, new PointF(10, 40)); // 畫紅色文字
}
bmp.Save("output.png"); // 存成 PNG
```
📌 重點
* Graphics g = Graphics.FromImage(bmp) → 取得 Bitmap 的繪圖區域。
* g.Clear(Color.White) → 設定背景色。
* g.DrawLine()、g.DrawString() → 繪製線條與文字。
* bmp.Save("output.png") → 存成圖片。
## 檔案載入 Bitmap
```csharp=
Bitmap bmp = new Bitmap("image.jpg");
```
搭配 PictureBox 顯示:
```csharp=
pictureBox1.Image = bmp;
```
## 存取 Bitmap 的像素
用 GetPixel() 取得某個像素顏色:
```csharp=
Color pixelColor = bmp.GetPixel(50, 50);
Console.WriteLine(pixelColor); // 輸出該像素的顏色
```
用 SetPixel() 設定像素顏色:
```csharp=
bmp.SetPixel(50, 50, Color.Red); // 設定 (50,50) 的像素為紅色
bmp.Save("modified.png"); // 儲存修改後的圖片
```
📌 ⚠️ GetPixel() 和 SetPixel() 速度較慢,若大量處理影像,建議用 LockBits() 提高效率!
## 儲存 Bitmap 為不同格式
```csharp=
bmp.Save("image.png", System.Drawing.Imaging.ImageFormat.Png);
bmp.Save("image.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
bmp.Save("image.bmp", System.Drawing.Imaging.ImageFormat.Bmp);
```
## 讀取PictureBox儲存
```csharp=
Bitmap bmp = new Bitmap(pictureBox1.Image);
bmp.Save(save.FileName);
```
## 釋放 Bitmap (避免記憶體占用)
Bitmap 會佔用記憶體,當用完後應該釋放:
```csharp=
bmp.Dispose();
```
## 使用 LockBits() 加速像素存取
如果需要修改大量像素,應該用 LockBits() 而不是 GetPixel() 和 SetPixel():
```csharp=
Bitmap bmp = new Bitmap("image.jpg");
// 鎖定 Bitmap
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(rect,
System.Drawing.Imaging.ImageLockMode.ReadWrite,
bmp.PixelFormat);
// 取得位元組指標
IntPtr ptr = bmpData.Scan0;
// 計算影像大小
int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
byte[] rgbValues = new byte[bytes];
// 複製像素到陣列
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
// 在這裡可以對 `rgbValues` 陣列做像素修改
// (例如:變成灰階、調整亮度等)
// 寫回 Bitmap
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
// 解鎖 Bitmap
bmp.UnlockBits(bmpData);
bmp.Save("fast_modified.jpg");
bmp.Dispose();
```
📌 為什麼 LockBits() 比 SetPixel() 快?
* SetPixel() 會一個一個像素修改,速度慢。
* LockBits() 一次取出整張圖片的像素到陣列,然後修改陣列,再一次寫回去,速度快很多!
## Invalidate()內容變更
當 PictureBox 的內容變更時,例如:
* 圖片內容改變
* 移動畫面
* 需要即時更新
但畫面不會自動刷新時,可以呼叫 Invalidate() 來強制重新繪製控件。
### Invalidate() 進階用法
只刷新 PictureBox 指定區域
```csharp=
Rectangle refreshArea = new Rectangle(10, 10, 50, 50);
pictureBox1.Invalidate(refreshArea);
```
這樣只會刷新 (10,10) 開始,寬 50、高 50 的範圍,提高效能。
## Invalidate() vs Refresh()
Refresh():立即重新繪製控件
📌 Invalidate()
* 只是標記這個控件需要重畫,系統會在適當的時機處理。
* 適合多次變更時一起刷新,效率較高。
📌 Refresh()
* 會 立即執行 Invalidate() 並強制更新 畫面。
* 如果需要立即更新 UI,可以用 Refresh()。
🔸 Invalidate() 與 Refresh() 比較
```csharp=
pictureBox1.Invalidate(); // 讓系統稍後刷新 (較省效能)
pictureBox1.Refresh(); // 立即刷新 (但影響效能)
💡要使用PictureBox1_Pain
```
## Rectangle 比例
1. 把圖片縮放到 Rectangle 裡,如果 Rectangle 不是原圖比例,圖片就會變形(壓扁或拉長)。
```csharp=
new Rectangle(10, 10, pictureBox1.Width - 20, pictureBox1.Height - 50)
e.Graphics.Clear(pictureBox1.BackColor);
e.Graphics.DrawImage(loadedImage, new Rectangle(x, y, pictureBox1.Width - 2 * x, pictureBox1.Height - 2 * y));
```
2. 原始圖片比例 ≠ Rectangle 設定的比例:
假設原圖是 16:9,但 Rectangle 設定成 4:3,圖片就會被壓縮或拉長。
## 拖曳圖片
```csharp=
float offsetX = 100, offsetY = 50;
int iw = 200, ih = 150;
e.Graphics.DrawImage(image, -offsetX, -offsetY, iw, ih);
```
效果:
* 圖片會從 (100, 50) 位置開始顯示
* 圖片大小變成 200x150
* 如果 offsetX、offsetY 改變,就能實現拖曳效果
方法二:
```csharp=
double offsetY = ((100 - trackBar2.Value) / 100.00) * (ih - ph);
double offsetX = (trackBar1.Value / 100.00) * (iw - pw);
e.Graphics.Clear(pictureBox1.BackColor);
e.Graphics.DrawImage(image, -(float)offsetX, -(float)offsetY, iw, ih);
```
## DrawImage說明
```csharp=
e.Graphics.DrawImage(影像, X座標, Y座標, 寬度, 高度);
```