圖形繪制基礎
在Windows的用戶界面中,當創(chuàng)建一個窗口,并在該窗口進行繪圖時,一般要聲明一個派生于System.Windows.Forms.Form的類。如果要編寫一個定制控件,就要聲明一個派生于System.Windows.Forms.UserControl的類。在這兩種情況下,都重寫了虛擬函數OnPaint()。只要窗口的任何一部分需要重新繪制,Windows都會調用這個函數。
在這個事件中,PaintEventArgs類是一個參數。在PaintEventArgs中有兩個重要的信息:Graphics對象和ClipRectangle對象。
Graphics類:
這個類封裝了一個GDI+繪圖界面。有3種基本類型的繪圖界面:
l Windows和屏幕上的控件
l 要發(fā)送給打印機的頁面
l 內存中的位圖和圖像
Graphics類提供了可以在這些繪圖界面上繪圖的功能。在其他功能中,我們可以使用它繪制圓弧、曲線、Bezier曲線、橢圓、圖像、線條、矩形和文本。
給窗口獲得Graphics對象有兩種不同的方式。首先是重寫OnPaint()事件,該事件是一個Form類繼承Control類的虛擬方法。下面利用從該事件的PaintEventArgs中獲取Graphics對象:
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
// do our drawing here
}
有時,需要直接在窗口中繪圖,而無需等待OnPaint()事件。例如要編寫代碼,選擇窗口中的某些圖像(類似于在Windows Explorer中選擇圖標),或者用鼠標拖動一些對象,就是這種情況。在窗體上調用CreateGraphics()方法就可以獲得一個Graphics對象,這是Form類繼承Control類的另一個方法:
protected void Form1_Click(object sender, System.EventArgs e)
{
Graphics g = this.CreateGraphics();
// do our drawing here
g.Dispose(); // this is important
}
只有在無用存儲單元收集器(GC)調用了析構函數時,我們才不必調用Dispose(),但不能確保GC何時運行。所以很可能在釋放這些資源前系統(tǒng)資源就被用盡了,所以需要手動釋放這些資源。還有一種方法就是使用using關鍵字,在對象超出作用域時using結構會自動調用Dispose()。
using(Graphics g = this.CreateGraphics())
{
g.DrawLine(Pens.Black,new Point(0,0),new Point(3,5));
}
坐標系統(tǒng):
GDI+的坐標系統(tǒng)建立在通過像素中心的假想數學直線上,這些直線從0開始,其左上角的交點是X=0,Y=0(簡短記號是(0,0)/Ponit(0,0)。
在繪制線條時,GDI+ 會把繪制出來的像素在指定的數學直線上對中。在繪制整數坐標的水平線時,可以認為每個像素的一半落在假想數學直線的上半部分,而另一半落在假想數學直線的下半部分。
這里有一點需要注意的地方,如果指定了寬度為5(例如畫一個從(1,0)到(6,4)的矩形),就會在水平方向上繪制6個像素。但如果考慮到數學直線通過像素中心,則該矩形只有5個像素寬,繪制的線條有一半像素落在假想數學直線的外面,一半像素則落在假想數學直線的里面。
不僅如此,如果使用圖形保真技術進行繪圖,其他像素就會上一半的顏色,創(chuàng)建出光滑的線條,部分避免了對角線的“臺階”外觀。
在繪圖時,常常用3種結構指定坐標:Point,Size和Rectangle。
Point:
GDI+使用Point表示一個點。這是一個二維平面上的點——一個像素的表示方式。許多GDI+函數例如DrawLine(),以Point作為其參數。聲明和構造Point的代碼如下所示:
Point p = new Point(1,1);
通過其公用屬性可以獲得和設置Point的X和Y坐標。
Size:
GDI+使用Size表示一個尺寸(像素)。Size結構包含寬度和高度。聲明和構造Size的代碼如下所示:
Size s = new Size(5,5);
通過其公用屬性可以獲得和設置Size的寬度和高速。
Rectangle:
有兩個構造函數。一個構造函數的參數是X坐標、Y坐標、寬度和高度。另一個構造函數的參數是Point和Size結構(Point定義矩形的左上角,Size定義其大。。聲明方式如下:
Rectangle r1 = new Rectangle(1,2,5,6);
Point p = new Point(1,2);
Size s = new Size(5,6);
Rectangle r2 = new Rectangle(p,s);
Rectangle的成員:
公共字段:
Empty:表示其屬性未被初始化的 Rectangle 結構。
公共屬性:
Bottom:獲取此 Rectangle 結構下邊緣的 y 坐標。
Height:獲取或設置此 Rectangle 結構的高度。
IsEmpty:測試此 Rectangle 的所有數值屬性是否都具有零值。
Left:獲取此 Rectangle 結構左邊緣的 x 坐標。
Location:獲取或設置此 Rectangle 結構左上角的坐標。
Right:獲取此 Rectangle 結構右邊緣的 x 坐標。
Size:獲取或設置此 Rectangle 的大小。
Top:獲取此 Rectangle 結構上邊緣的 y 坐標。
Width:獲取或設置此 Rectangle 結構的寬度。
X:獲取或設置此 Rectangle 結構左上角的 x 坐標。
Y:獲取或設置此 Rectangle 結構左上角的 y 坐標。
公共方法:
Ceiling:通過將 RectangleF 值舍入到比它大的相鄰整數值,將指定的 RectangleF 結構轉換為 Rectangle 結構。
Contains:已重載。確定指定的點是否包含在此 Rectangle 定義的矩形區(qū)域范圍內。
Equals:已重寫。測試 obj 是否為與此 Rectangle 結構具有相同位置和大小的 Rectangle 結構。
FromLTRB:創(chuàng)建一個具有指定邊緣位置的 Rectangle 結構。
GetHashCode:已重寫。返回此 Rectangle 結構的哈希代碼。有關如何使用哈希代碼的信息,請參見 Object.GetHashCode。
Inflate:已重載。創(chuàng)建并返回指定 Rectangle 結構的放大副本。該副本被放大指定的量。
Intersect:已重載。將此 Rectangle 結構替換為其自身與指定 Rectangle 結構的交集。
IntersectsWith:確定此矩形是否與 rect 相交。
Offset:已重載。將此矩形的位置調整指定的量。
Round:通過將 RectangleF 舍入到最近的整數值,將指定的 RectangleF 轉換為 Rectangle。
ToString:已重寫。將此 Rectangle 的屬性轉換為可讀字符串。
Truncate:通過截斷 RectangleF 值,將指定的 RectangleF 轉換為 Rectangle。
Union:獲取包含兩個 Rectangle 結構的交集的 Rectangle 結構。
公共運算符:
相等運算符:測試兩個 Rectangle 結構的位置和大小是否相同。
不等運算符:測試兩個 Rectangle 結構的位置或大小是否不同。
GraphicsPaths:
這個類表示一系列連接的線條和曲線。在構造一條路徑時,可以添加線條、Bezier曲線、圓弧、餅形圖、多邊形和矩形等。在構造一條復雜的路徑后,可以用一個操作繪制路徑:調用DrawPath()?梢哉{用FillPath()填充路徑。
使用一個點數組和PathTypes構造GraphicsPath,PathTypes是一個byte數組,其中的每個元素對應于點數組中的每一個元素,并給出了路徑如何通過這些點來構造的其他信息。例如,如果點是路徑的起始點,那么這個點的路徑類型就是PathPointType.Start。如果點是兩個線條的連接點,那么這個點的路徑類型就是PathPointType.Line。如果點用于構造一條從前一點到后一點之間的Bezier曲線,路經類型就是PathPointType.Bezier。
注意要使用GraphicsPaths需要引入如下命名空間:
using System.Drawing.Drawing2D;
示例,用四條線段創(chuàng)建一個圖形路徑:
GraphicsPath path;
path = new GraphicsPath(new Point[]{
new Point(10,10),
new Point(150,10),
new Point(200,150),
new Point(10,150),
new Point(200,160)
},new byte[]{
(byte)PathPointType.Start,
(byte)PathPointType.Line,
(byte)PathPointType.Line,
(byte)PathPointType.Line,
(byte)PathPointType.Line
}
);
using(Graphics g = this.CreateGraphics())
{
g.DrawPath(Pens.Black,path);
}
Regions:
這個類是一個復雜的圖形,由矩形和路徑組成。在構造了一個Region后,就可以使用FillRegion()方法繪制該區(qū)域。
下面的代碼創(chuàng)建了一個區(qū)域,給它添加一個Rectangle和一個GraphicsPath,再用藍色填充該區(qū)域:
Rectangle r1 = new Rectangle(10,10,50,50);
Rectangle r2 = new Rectangle(40,40,50,50);
Region r = new Region(r1);
r.Union(r2);
GraphicsPath path;
path = new GraphicsPath(new Point[]{
new Point(45,45),
new Point(145,55),
new Point(200,150),
new Point(75,150),
new Point(45,45)
},new byte[]{
(byte)PathPointType.Start,
(byte)PathPointType.Bezier,
(byte)PathPointType.Bezier,
(byte)PathPointType.Bezier,
(byte)PathPointType.Line
}
);
r.Union(path);
using(Graphics g = this.CreateGraphics())
{
g.FillRegion(Brushes.Blue,r);
}
顏色:
可以用兩種不同的方式來表示,一種是RGB(將紅、綠、藍色值傳送給Color結構的一個函數),另一種是把顏色分解為3種組件(色調、飽和度和亮度)。
GetBrightness:獲取此 Color 結構的“色調-飽和度-亮度”(HSB) 的亮度值。
GetHue:獲取此 Color 結構的“色調-飽和度-亮度”(HSB) 的色調值,以度為單位。
GetSaturation:獲取此 Color 結構的“色調-飽和度-亮度”(HSB) 的飽和度值。
GDI+中的顏色還有第4個組件:Alpha組件。使用這個組件可以設置顏色的不透明度,以便創(chuàng)建淡入淡出效果。
圖形繪制進階-線條、字體
使用Pen類繪制線條
Pen類在System.Drawing名稱空間中。
例如,如下代碼即可在Form窗體加載調用繪圖方法時繪制一些直線
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
using (Pen blackPen = new Pen(Color.Black,1))
{
for (int y = 0;y < ClientRectangle.Height;y += ClientRectangle.Height / 10)
{
g.DrawLine(blackPen, new Point(0,0), new Point(ClientRectangle.Width,y));
}
}
}
獲得Pen有更簡單的方式,Pens包含的屬性可以包含創(chuàng)建大約150種鋼筆,每個鋼筆都有前面介紹的約定義顏色。下面我們使用這種鋼筆做一個例子:
protected override void OnPaint(PaintEventArgs e)
{
for (int y = 0;y < ClientRectangle.Height;y += ClientRectangle.Height / 10)
{
e.Graphics.DrawLine(Pens.Black, new Point(0,0), new Point(ClientRectangle.Width,y));
}
}
這樣就可以不用創(chuàng)建一個Pen的對象,最后也不用擔心忘記釋放對象而調用Dispose()方法。
使用Brush類繪制圖形
Brush類是一個抽象的基類,要實例化一個Brush對象,應適用派生于Brush的類,例如SolidBrush、TextureBrush和LinearGradientBrush。Brush類在System.Drawing名稱空間中,但TextureBrush和LinearGradientBrush在System.Drawing.Drawing2D名稱空間中。
1.SolidBrush用一種單色填充圖形。
2.TextureBrush用一個位圖填充圖形。在構造這個畫筆時,還指定了一個邊框矩形和一個填充模式。邊框矩形指定畫筆適用位圖的哪一部分——可以不使用整個位圖。填充模式有許多選項,包括平鋪紋理的Tile、TileFlipX、TileFlipY和TileFlipXY,它們指定連續(xù)平鋪時翻轉對象。使用TextureBrush可以創(chuàng)建非常有趣和富有相像力的效果。
3.LinearGradientBrush封裝了一個畫筆,該畫筆可以繪制兩種顏色漸變的圖形,其中第一種顏色以指定的角度逐漸過渡到第二種顏色。角度則可以根據程度來指定。0º表示顏色從左向右過渡。90º表示顏色從上到下過渡。
還有一種畫筆PathGradientBrush,它可以創(chuàng)建精細的陰影效果,其中陰影從路徑的中心趨向路徑的邊界。這種畫筆可以讓人想起用彩筆繪制的陰影地圖,可以實現類似在不同的省或國家愛之間的邊界上涂上較暗的顏色。
使用的時候需要對窗體的構造函數進行相應的修改:
public Form3()
{
//
// Windows 窗體設計器支持所必需的
//
InitializeComponent();
// 控件被繪制為不透明的,不繪制背景
SetStyle(ControlStyles.Opaque,true);
//
// TODO: 在 InitializeComponent 調用后添加任何構造函數代碼
//
}
會出現如下效果,默認運行時是個透明白色的背景。但是將任何一個窗口覆蓋上后就繪制成了被覆蓋部分的位圖。
覆蓋前:
當將選中的窗口覆蓋他后就會出現如下效果:
就是被重新繪制了。
(由于圖片上傳出現問題,不知如何上傳,以前可以上傳的名字都不能用了,還有好多以前可用的圖片現在都不可用了,好像不接受中文命名)
這并不是我們需要的。我們引入System.Drawing.Drawing2D命名空間,為了此名稱空間下的使用LinearGradientBrush畫筆。
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
g.FillRectangle(Brushes.White, ClientRectangle);
g.FillRectangle(Brushes.Red, new Rectangle(10,60,50,50));
// 實現漸變色
Brush linearGradientBrush = new LinearGradientBrush(new Rectangle(10,60,50,50), Color.Red, Color.White, 45);
g.FillRectangle(linearGradientBrush, new Rectangle(10,60,50,50));
linearGradientBrush.Dispose();
g.FillEllipse(Brushes.Aquamarine, new Rectangle(60,20,50,30));
g.FillPie(Brushes.Chartreuse, new Rectangle(60,60,50,50),90,210);
g.FillPolygon(Brushes.BlueViolet, new Point[]{new Point(110,10),new Point(150,10),new Point(160,40),new Point(120,20),new Point(120,60)});
}
使用Font繪制文本
Font類封裝了字體的3個主要特征:字體系列、字體大小和字體樣式。Font類在System.Drawing明稱空間中。
在.NET Framework中,Size并不僅僅是點的大小,通過Unit屬性可以改變GraphicsUnit屬性,Unit定義了字體的測量單位。一個點等于1/72英寸,所以10點的字體有10/72英寸高。在GraphicsUnit枚舉中,可以把字體的大小指定為:
l 點的大小
l 顯示大。1/75英寸)
l 文檔(1/300英寸)
l 英寸
l 毫米
l 像素
一般情況下,屏幕上每英寸有72個像素。打印機上每英寸右300個像素、600個像素,甚至更多。使用Graphics對象的MeasureString()方法可以計算出給定字體的字符串寬度。
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
string str = "This is a string";
SizeF size = g.MeasureString(str,Font);
g.DrawRectangle(Pens.Black,0,0,size.Width,size.Height);
g.DrawString(str,Font,Brushes.Blue,new RectangleF(0,0,size.Width,size.Height));
}
這個例子將在獲取字符串的寬度和高度后繪制出一個黑色矩形,然后將字符串以藍色繪制在矩形中。
StringFormat類封裝了文本局部信息,包括對齊和行間距信息。
public Form3()
{
//
// Windows 窗體設計器支持所必需的
//
InitializeComponent();
// 控件被繪制為不透明的,不繪制背景
SetStyle(ControlStyles.Opaque,true);
Bounds = new Rectangle(0,0,500,300);
//
// TODO: 在 InitializeComponent 調用后添加任何構造函數代碼
//
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
int y = 0;
g.FillRectangle(Brushes.White,ClientRectangle);
// Draw left justifed text
Rectangle rect = new Rectangle(0,y,400,Font.Height);
g.DrawRectangle(Pens.Blue,rect);
g.DrawString("This text is left justified.",Font,Brushes.Black,rect);
y += Font.Height + 20;
// Draw right justifed text
Font aFont = new Font("Arial",16,FontStyle.Bold);
rect = new Rectangle(0,y,400,aFont.Height);
g.DrawRectangle(Pens.Blue,rect);
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Far;
g.DrawString("This text is right justified.",aFont,Brushes.Blue,rect,sf);
y += Font.Height + 20;
aFont.Dispose();
// Draw centered text
Font cFont = new Font("Courier New",12,FontStyle.Underline);
rect = new Rectangle(0,y,400,cFont.Height);
g.DrawRectangle(Pens.Blue,rect);
sf = new StringFormat();
sf.Alignment = StringAlignment.Center;
g.DrawString("This text is centered and underlined.",cFont,Brushes.Red,rect,sf);
y += Font.Height + 20;
cFont.Dispose();
// Draw multiline text
Font trFont = new Font("Times New Roman",12);
rect = new Rectangle(0,y,400,trFont.Height * 3);
g.DrawRectangle(Pens.Blue,rect);
String longString = "This text is much longer, and drawn ";
longString += "into a rectangle that is higher than ";
longString += "one line, so that it will wrap. It is ";
longString += "very easy to wrap text using GDI+.";
g.DrawString(longString,trFont,Brushes.Black,rect);
trFont.Dispose();
}
實現畫不同類型的字體。
圖形繪制進階-圖像(雙倍緩沖)
圖像在GDI+中有很多用途。當然,可以在窗口中繪制圖像,也可以用圖像創(chuàng)建畫筆(TextureBrush),再繪制用該圖像填充的圖形。
Image類在System.Drawing命名空間中。
圖像另一個非常重要的用途是雙倍緩沖的圖形編程技巧。有時要創(chuàng)建的圖形非常精細復雜,即使使用目前運行速度最快的機器,也需要很長時間才能繪制出來。觀察圖像在屏幕中一點一點地繪制出來,并不是一件令人愉快的事。這類應用程序有映射應用程序和復雜的CAD/CAM應用程序。在這個技巧中,并不在把圖形繪制在窗口中,而是繪制到一個圖像中。在完成了圖像的繪制后,再把該圖像繪制到窗口中。這個技巧就成為雙倍緩沖。一些其他的繪制技巧還涉及到在多個圖層上繪制,即首先繪制背景,再在背景的上面繪制對象,最后在對象的上面繪制文本。如果這個圖形直接在屏幕上繪制,用戶就會看到一個閃爍的效果。雙倍緩沖可以消除這種閃爍效果。
Image本身是一個抽象類,它有兩個子類:Bitmap和Metafile。
Bitmap類用于一般的圖像,有高度和寬度屬性。下面的一個小例子就是從文件中加載一個Bitmap圖像,并繪制它。也可以從該類中創(chuàng)建畫筆,再使用該畫筆創(chuàng)建一個鋼筆,以繪制線條,也可以使用該畫筆繪制文本。
位圖有幾個可能的來源?梢詮奈募屑虞d位圖,位圖也可以來自打開的流,還可以從另一個現有的圖像中創(chuàng)建位圖。位圖可以創(chuàng)建為空白的圖像,以便在其上繪制。在從文件中讀取圖像時,該圖像可以是JPEG,GIF或BMP格式。
加載圖像
使用紋理畫筆進行繪圖
使用鋼筆繪制圖像
使用圖像繪制文本
未使用雙倍緩沖
使用雙倍緩沖
例程下載
原文地址:http://www.cnblogs.com/Bear-Study-Hard/archive/2006/03/13/349100.html