前言
最近做一個微信公眾號服務,有一些簡單的圖片處理功能。主要就是用戶在頁面操作,前端做一些立刻顯示的效果,然后提交保存時后端真正修改原圖。
從程序設計的角度看,gdi包括兩部分:一部分是gdi對象,另一部分是gdi函數。gdi對象定義了gdi函數使用的工具和環境變量,而gdi函數使用gdi對象繪制各種圖形,在c#中,進行圖形程序編寫時用到的是gdi+(graphics device interface plus圖形設備接口)版本,gdi+是gdi的進一步擴展,它使我們編程更加方便。
c#中的gdi+就相當于java中的swing控件,是編寫圖形界面必不可缺的一個接口。gdi+繪圖最大的方便得益于c#的可視化編程,所有的控件只需要自己drag,然后place,最后cilck添加監聽方法。真的是too young too simple。
我們的后端是 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 對象。
比如:
1
2
3
4
|
private void form1_paint( object sender, painteventargs e) { graphics g = e.graphics; // 創建畫板,這里的畫板是由form提供的. } |
不過本文討論的是其他場景,比如 asp.net mvc,或單純的控制臺程序。這些時候是沒有控件的,所以要用其他方法。
我一般用以下方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
// // 摘要: // 從指定的 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 的。
如何創建畫板
如果是要對原圖進行處理,比如旋轉圖片,添加文字等,可以直接通過原圖片獲得畫板對象。
1
2
|
image img = image.fromfile(imgpath); graphics graphics = graphics.fromimage(img); |
如果是要畫一個新的圖,可以通過要保存的圖片寬、高生成畫板。
1
2
|
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+中發生一般性錯誤)
1
2
3
4
5
6
7
8
|
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 代碼段里。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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 代碼段時自動釋放該資源。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/// <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
1
2
3
4
5
6
7
8
|
string directory = @"d:\test\" ; string filename = "output.png" ; // 檢查文件夾是否存在,不存在則先創建 if (!directory.exists(directory)) { directory.createdirectory(directory); } bmp.save(directory + filename); |
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。
原文鏈接:http://www.cnblogs.com/dandelion-drq/p/use-gdiplus-to-draw-image-in-csharp.html