# 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座標, 寬度, 高度); ```