最近做一個微信公眾號服務,有一些簡單的圖片處理功能。主要就是用戶在頁面操作,前端做一些立刻顯示的效果,然後提交保存時後端真正修改原圖。 我們的後端是 ,也就是 語言了, 本身處理圖片還是比較方便的,使用 就好,只需要添加 引用,不需要任何第三方庫。於是最近也用到一些比較常用的 圖片處理方法,就整理一 ...
最近做一個微信公眾號服務,有一些簡單的圖片處理功能。主要就是用戶在頁面操作,前端做一些立刻顯示的效果,然後提交保存時後端真正修改原圖。
我們的後端是 ASP.NET
,也就是 C#
語言了,C#
本身處理圖片還是比較方便的,使用 GDI+
就好,只需要添加 System.Drawing
引用,不需要任何第三方庫。於是最近也用到一些比較常用的 GDI+
圖片處理方法,就整理一下做個記錄了。
這個題目大概會寫幾篇文章,第一篇先簡單介紹一下 GDI+
的常用對象,以及一些使用時候的註意事項,後面會挑一些項目中做過的比較有用的處理過程來介紹一下。
廢話不多說,開始進入正題。
需要用到的類
使用 GDI+
畫圖會用到的幾個常用的類有:Graphics
、Bitmap
、Image
。
其中 Graphics
是畫板。這個類包含了許多畫圖的方法,包括畫圖片(DrawImage
),畫線(DrawLine
),畫圓(DrawEllipse、FillEllipse
),寫字(DrawString
)等等。簡單說使用這個類可以完成我們需要的大部分工作。
生成一個 Graphics
對象需要用到 Image
或者 Bitmap
。
PS: Winform
下可以直接從窗體或控制項的事件中引用 Graphics
對象。
比如:
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics; // 創建畫板,這裡的畫板是由Form提供的.
}
不過本文討論的是其他場景,比如 ASP.NET MVC
,或單純的控制台程式。這些時候是沒有控制項的,所以要用其他方法。
我一般用以下方法:
//
// 摘要:
// 從指定的 System.Drawing.Image 創建新的 System.Drawing.Graphics。
//
// 參數:
// image:
// 從中創建新 System.Drawing.Graphics 的 System.Drawing.Image。
//
// 返回結果:
// 此方法為指定的 System.Drawing.Image 返回一個新的 System.Drawing.Graphics。
//
// 異常:
// T:System.ArgumentNullException:
// image 為 null。
//
// T:System.Exception:
// image 具有索引像素格式,或者格式未定義。
public static Graphics FromImage(Image image);
其中的參數可以傳入 Image
或 Bitmap
,因為 Bitmap
是繼承自 Image
的。
如何創建畫板
如果是要對原圖進行處理,比如旋轉圖片,添加文字等,可以直接通過原圖片獲得畫板對象。
Image img = Image.FromFile(imgPath); Graphics graphics = Graphics.FromImage(img);
如果是要畫一個新的圖,可以通過要保存的圖片寬、高生成畫板。
Bitmap bmp = new Bitmap(width, height); Graphics graph = Graphics.FromImage(bmp);
PS:
Graphics
本身是沒有提供構造函數來直接生成的。所以我們可以先創建一個需要保存圖片大小的Bitmap
點陣圖對象,然後再獲得畫板對象。
如何保存畫好的圖片
通過調用 img.Save(savePath)
或者 bmp.Save(savePath)
即可保存對象。
PS: Bitmap
的 Save
方法是直接繼承自 Image
的。
GDI+
的坐標系
GDI+
的坐標系是個二維坐標系,不過又有點不一樣,它的原點是在左上角的。如下圖:
使用 GDI+
的一些註意事項
這裡我忍不住要先吐槽一下,GDI+
的報錯信息不太友好啊。經常只是返回一個“GDI+ 中發生一般性錯誤。”,不能快速地根據這個錯誤提示定位問題。比如說沒有釋放圖片資源時想再次訪問資源會報這個錯誤,想要保存圖片的文件夾不存在時也是提示這個錯誤。看不出來區別……
1. 保存到相同路徑的文件時要先釋放圖片資源,否則會報錯(GDI+中發生一般性錯誤)
Image img = Image.FromFile(imgPath);
Bitmap bmp = new Bitmap(img);
Graphics graphics = Graphics.FromImage(bmp);
... // 對圖片進行一些處理
img.Dispose(); // 釋放原圖資源
bmp.Save(imgPath); // 保存到原圖
graphics.Dispose(); // 圖片處理過程完成,剩餘資源全部釋放
bmp.Dispose();
2. 使用完的資源記得要釋放。可以用 try..catch..finally
或者 using
的方式,這樣即使遇到代碼運行報錯也能及時釋放資源,更加保險。
try..catch...finally
:把釋放資源的代碼寫到finally
代碼段里。Image img = Image.FromFile(imgPath); Bitmap bmp = new Bitmap(img); Graphics graphics = Graphics.FromImage(bmp); try { // ... } catch (System.Exception ex) { throw ex; } finally { graphics.Dispose(); bmp.Dispose(); img.Dispose(); }
using
:使用using
語句創建的資源會在離開using
代碼段時自動釋放該資源。/// <summary> /// 縮放圖像 /// </summary> /// <param name="originalImagePath">原圖路徑</param> /// <param name="destWidth">目標圖寬度</param> /// <param name="destHeight">目標圖高度</param> /// <returns></returns> public Bitmap GetThumbnail(string originalImagePath, int destWidth, int destHeight) { using (Image imgSource = Image.FromFile(originalImagePath)) { return GetThumbnail(imgSource, destWidth, destHeight); } }
3. 要保存圖片的文件夾一定要是已經存在的,否則會報錯(GDI+中發生一般性錯誤)
eg:假設圖片要保存到 D:\test\output.png
string directory = @"D:\test\";
string fileName = "output.png";
// 檢查文件夾是否存在,不存在則先創建
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
bmp.Save(directory + fileName);