第7章 C#圖形圖像編程基礎
本章主要介紹使用C#進行圖形圖像編程基礎,其中包括GDI+繪圖基礎、C#圖像處理基礎以及簡單的圖像處理技術。
7.1 GDI+繪圖基礎
編寫圖形程序時需要使用GDI(Graphics Device Interface,圖形設備接口),從程序設計的角度看,GDI包括兩部分:一部分是GDI對象,另一部分是GDI函數。GDI對象定義了GDI函數使用的工具和環境變量,而GDI函數使用GDI對象繪制各種圖形,在C#中,進行圖形程序編寫時用到的是GDI+(Graphice Device Interface Plus圖形設備接口)版本,GDI+是GDI的進一步擴展,它使我們編程更加方便。
7.1.1 GDI+概述
GDI+是微軟在Windows 2000以后操作系統中提供的新的圖形設備接口,其通過一套部署為托管代碼的類來展現,這套類被稱為GDI+的“托管類接口”,GDI+主要提供了以下三類服務:
(1) 二維矢量圖形:GDI+提供了存儲圖形基元自身信息的類(或結構體)、存儲圖形基元繪制方式信息的類以及實際進行繪制的類。
(2) 圖像處理:大多數圖片都難以划定為直線和曲線的集合,無法使用二維矢量圖形方式進行處理。因此,GDI+為我們提供了Bitmap、Image等類,它們可用於顯示、操作和保存BMP、JPG、GIF等圖像格式。
(3) 文字顯示:GDI+支持使用各種字體、字號和樣式來顯示文本。
我們要進行圖形編程,就必須先講解Graphics類,同時我們還必須掌握Pen、Brush和Rectangle這幾種類。
GDI+比GDI優越主要表現在兩個方面:第(一)GDI+通過提供新功能(例如:漸變畫筆和alpha混合)擴展了GDI的功能;第(二)修訂了編程模型,使圖形編程更加簡易靈活。
7.1.2 Graphics類
Graphics類封裝一個GDI+繪圖圖面,提供將對象繪制到顯示設備的方法,Graphics與特定的設備上下文關聯。畫圖方法都被包括在Graphics類中,在畫任何對象(例如:Circle,Rectangle)時,我們首先要創建一個Graphics類實例,這個實例相當於建立了一塊畫布,有了畫布才可以用各種畫圖方法進行繪圖。
繪圖程序的設計過程一般分為兩個步驟:(一)創建Graphics對象;(二)使用Graphics對象的方法繪圖、顯示文本或處理圖像。
通常我們使用下述三種方法來創建一個Graphics對象。
方法一、利用控件或窗體的Paint事件中的PainEventArgs
在窗體或控件的Paint事件中接收對圖形對象的引用,作為PaintEventArgs(PaintEventArgs指定繪制控件所用的Graphics)的一部分,在為控件創建繪制代碼時,通常會使用此方法來獲取對圖形對象的引用。
例如:
//窗體的Paint事件的響應方法
private void form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
}
也可以直接重載控件或窗體的OnPaint方法,具體代碼如下所示:
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
}
Paint事件在重繪控件時發生。
方法二、調用某控件或窗體的CreateGraphics方法
調用某控件或窗體的CreateGraphics方法以獲取對Graphics對象的引用,該對象表示該控件或窗體的繪圖圖面。如果想在已存在的窗體或控件上繪圖,通常會使用此方法。
例如:
Graphics g = this.CreateGraphics();
方法三、調用Graphics類的FromImage靜態方法
由從Image繼承的任何對象創建Graphics對象。在需要更改已存在的圖像時,通常會使用此方法。
例如:
//名為“g1.jpg”的圖片位於當前路徑下
Image img = Image.FromFile("g1.jpg");//建立Image對象
Graphics g = Graphics.FromImage(img);//創建Graphics對象
1.Graphics類的方法成員
有了一個Graphics的對象引用后,就可以利用該對象的成員進行各種各樣圖形的繪制,表7.1列出了Graphics類的常用方法成員。
表7.1 Graphics類常用方法
名稱 |
說明 |
DrawArc |
畫弧。 |
DrawBezier |
畫立體的貝爾塞曲線。 |
DrawBeziers |
畫連續立體的貝爾塞曲線。 |
DrawClosedCurve |
畫閉合曲線。 |
DrawCurve |
畫曲線。 |
DrawEllipse |
畫橢圓。 |
DrawImage |
畫圖像。 |
DrawLine |
畫線。 |
DrawPath |
通過路徑畫線和曲線。 |
DrawPie |
畫餅形。 |
DrawPolygon |
畫多邊形。 |
DrawRectangle |
畫矩形。 |
DrawString |
繪制文字。 |
FillEllipse |
填充橢圓。 |
FillPath |
填充路徑。 |
FillPie |
填充餅圖。 |
FillPolygon |
填充多邊形。 |
FillRectangle |
填充矩形。 |
FillRectangles |
填充矩形組。 |
FillRegion |
填充區域。 |
在.NET中,GDI+的所有繪圖功能都包括在System、System.Drawing、System.Drawing.Imaging、System.Drawing.Darwing2D和System.Drawing.Text等命名空間中,因此在開始用GDI+類之前,需要先引用相應的命名空間。
2.引用命名空間
在C#應用程序中使用using命令已用給定的命名空間或類,下面是一個C#應用程序引用命名空間的例子:
using System;
using System.Collections.Generic;
using System.Data;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
7.1.3 常用畫圖對象
在創建了Graphics對象后,就可以用它開始繪圖了,可以畫線、填充圖形、顯示文本等等,其中主要用到的對象還有:
l Pen:用來用patterns、colors或者bitmaps進行填充。
l Color:用來畫線和多邊形,包括矩形、圓和餅形。
l Font:用來給文字設置字體格式。
l Brush:用來描述顏色。
l Rectangle:矩形結構通常用來在窗體上畫矩形。
l Point:描述一對有序的x,y兩個坐標值。
1.Pen類
Pen用來繪制指定寬度和樣式的直線。使用DashStyle屬性繪制幾種虛線,可以使用各種填充樣式(包括純色和紋理)來填充Pen繪制的直線,填充模式取決於畫筆或用作填充對象的紋理。
使用畫筆時,需要先實例化一個畫筆對象,主要有以下幾種方法。
用指定的顏色實例化一只畫筆的方法如下:
public Pen(Color);
用指定的畫刷實例化一只畫筆的方法如下:
public Pen(Brush);
用指定的畫刷和寬度實例化一只畫筆的方法如下:
public Pen(Brush, float);
用指定的顏色和寬度實例化一只畫筆的方法如下:
public Pen(Color, float);
實例化畫筆的語句格式如下:
Pen pn=new Pen(Color.Blue);
或者Pen pn=new Pen(Color.Blue,100);
Pen常用的屬性有以下幾個,如表7.2所示:
表7.2 Pen常用屬性
名稱 |
說明 |
Alignment |
獲得或者設置畫筆的對齊方式。 |
Brush |
獲得或者設置畫筆的屬性。 |
Color |
獲得或者設置畫筆的顏色。 |
Width |
獲得或者設置畫筆的寬度。 |
2.Color結構
在自然界中,顏色大都由透明度(A)和三基色(R,G,B)所組成。在GDI+中,通過Color結構封裝對顏色的定義,Color結構中,除了提供(A,R,G,B)以外,還提供許多系統定義的顏色,如Pink(粉顏色),另外,還提供許多靜態成員,用於對顏色進行操作。Color結構的基本屬性如表7.3所示。
表7.3 顏色的基本屬性
名稱 |
說明 |
A |
獲取此Color結構的alpha分量值,取值(0~255)。 |
B |
獲取此Color結構的藍色分量值,取值(0~255)。 |
G |
獲取此Color結構的綠色分量值,取值(0~255)。 |
R |
獲取此Color結構的紅色分量值,取值(0~255)。 |
Name |
獲取此Color結構的名稱,這將返回用戶定義的顏色的名稱或已知顏色的名稱(如果該顏色是從某個名稱創建的),對於自定義的顏色,將返回RGB值。 |
Color結構的基本(靜態)方法如表7.4所示
表7.4 顏色的基本方法
名稱 |
說明 |
FromArgb |
從四個8位ARGB分量(alpha、紅色、綠色和藍色)值創建Color結構。 |
FromKnowColor |
從指定的預定義顏色創建一個Color結構。 |
FromName |
從預定義顏色的指定名稱創建一個Color結構。 |
Color結構變量可以通過已有顏色構造,也可以通過RGB建立,例如:
Color clr1 = Color.FromArgb(122,25,255);
Color clr2 = Color.FromKnowColor(KnowColor.Brown);//KnownColor為枚舉類型
Color clr3 = Color.FromName("SlateBlue");
在圖像處理中一般需要獲取或設置像素的顏色值,獲取一幅圖像的某個像素顏色值的具體步驟如下:
(1)定義Bitmap
Bitmap myBitmap = new Bitmap("c:\\MyImages\\TestImage.bmp");
(2)定義一個顏色變量把在指定位置所取得的像素值存入顏色變量中
Color c = new Color();
c = myBitmap.GetPixel(10,10);//獲取此Bitmap中指定像素的顏色。
(3)將顏色值分解出單色分量值
int r,g,b;
r= c.R;
g=c.G;
b=c.B;
3.Font類
Font類定義特定文本格式,包括字體、字號和字形屬性。Font類的常用構造函數是public Font(string 字體名,float 字號,FontStyle 字形),其中字號和字體為可選項和public Font(string 字體名,float 字號),其中“字體名”為Font的FontFamily的字符串表示形式。下面是定義一個Font對象的例子代碼:
FontFamily fontFamily = new FontFamily("Arial");
Font font = new Font(fontFamily,16,FontStyle.Regular,GraphicsUnit.Pixel);
字體常用屬性如表7.5所示。
表7.5 字體的常用屬性
名稱 |
說明 |
Bold |
是否為粗體。 |
FontFamily |
字體成員。 |
Height |
字體高。 |
Italic |
是否為斜體。 |
Name |
字體名稱。 |
Size |
字體尺寸。 |
SizeInPoints |
獲取此 Font對象的字號,以磅為單位。 |
Strikeout |
是否有刪除線。 |
Style |
字體類型。 |
Underline |
是否有下划線。 |
Unit |
字體尺寸單位。 |
4.Brush類
Brush類是一個抽象的基類,因此它不能被實例化,我們總是用它的派生類進行實例化一個畫刷對象,當我們對圖形內部進行填充操作時就會用到畫刷,關於畫刷在7.1.5中有詳細講解。
5.Rectangle結構
存儲一組整數,共四個,表示一個矩形的位置和大小。矩形結構通常用來在窗體上畫矩形,除了利用它的構造函數構造矩形對象外,還可以使用Rectangle結構的屬性成員,其屬性成員如表7.6所示。
表7.6 Rectangle結構屬性
名稱 |
說明 |
Bottom |
底端坐標 |
Height |
矩形高 |
IsEmpty |
測試矩形寬和高是否為0 |
Left |
矩形左邊坐標 |
Location |
矩形的位置 |
Right |
矩形右邊坐標 |
Size |
矩形尺寸. |
Top |
矩形頂端坐標 |
Width |
矩形寬 |
X |
矩形左上角頂點X坐標 |
Y |
矩形左上角頂點Y坐標 |
Retangle結構的構造函數有以下兩個:
//用指定的位置和大小初始化Rectangle類的新實例。
public Retangle(Point,Size);//Size結構存儲一個有序整數對,通常為矩形的寬度和高度。
和
public Rectangle(int,int,int,int);
6.Point結構
用指定坐標初始化Point類的新實例。這個結構很像C++中的Point結構,它描述了一對有序的x,y兩個坐標值,其構造函數為:public Point(int x,int y);其中x為該點的水平位置;y為該點的水垂直位置。下面是構造Point對象的例子代碼:
Point pt1=new Point(30,30);
Point pt2=new Point(110,100);
7.1.4 基本圖形繪制舉例
1.畫一個矩形
【例7.1】建立一個項目,在窗體上畫一個矩形,通過直接在Form1類中重載OnPaint函數的方法來實現。
圖7.1 畫一個矩形
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics ;
Rectangle rect = new Rectangle(50, 30, 100, 100);
LinearGradientBrush lBrush = new LinearGradientBrush(rect, Color.Red,Color.Yellow,LinearGradientMode.BackwardDiagonal);
g.FillRectangle(lBrush, rect);
}
運行結果如圖7.1所示。
2.畫一個弧
【例7.2】畫一個弧形。
弧形函數格式如下:
public void DrawArc(Pen pen,Rectangle rect,Float startArgle,Float sweepAngle);
直接在Form1類中重載OnPaint函數
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics ;
Pen pn = new Pen( Color.Blue);
Rectangle rect = new Rectangle(50, 50, 200,100);
g.DrawArc(pn,rect,12,84);
}
運行結果如圖7.2所示。
圖7.2 畫一個弧
3.畫線
【例7.3】畫一條線。
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics ;
Pen pn = new Pen(Color.Blue);
Point pt1 = new Point(30,30);
Point pt2 = new Point(110,100);
g.DrawLine(pn,pt1,pt2);
}
運行結果如圖7.3所示。
圖7.3畫一條線
4.畫橢圓
【例7.4】畫一個橢圓。
圖7.4畫一個橢圓
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics ;
Pen pn = new Pen( Color.Blue, 100 );
Rectangle rect = new Rectangle(50, 50, 200, 100);
g.DrawEllipse( pn, rect );
}
運行結果如圖7.4所示。
5.輸出文本
【例7.5】輸出文本。
protected override void OnPaint(PaintEventArgs e)
{
Font fnt = new Font("Verdana", 16);
Graphics g = e.Graphics;
g.DrawString("GDI+ World", fnt, new SolidBrush(Color.Red), 14,10);
}
運行結果如圖7.5所示。
圖7.5 輸出文本
6.填充路徑
【例7.6】填充路徑。
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
g.FillRectangle(new SolidBrush(Color.White), ClientRectangle);
GraphicsPath path = new GraphicsPath(new Point[] {
new Point(40, 140), new Point(275, 200),
new Point(105, 225), new Point(190, 300),
new Point(50, 350), new Point(20, 180), },
new byte[] {
(byte)PathPointType.Start,
(byte)PathPointType.Bezier,
(byte)PathPointType.Bezier,
(byte)PathPointType.Bezier,
(byte)PathPointType.Line,
(byte)PathPointType.Line,
});
PathGradientBrush pgb = new PathGradientBrush(path);
pgb.SurroundColors = new Color[]
{
Color.Green,Color.Yellow,Color.Red, Color.Blue,
Color.Orange, Color.White,
};
g.FillPath(pgb, path);
}
運行結果如圖7.6所示。
圖7.6 填充路徑
注意:GraphicsPath類位於命名空間System.Drawing.Drawing2D中,表示一系列相互連接的直線和曲線。
7.1.5 畫刷和畫刷類型
Brush類型是一個抽象類,所以它不能被實例化,也就是不能直接應用,但是我們可以利用它的派生類,如:HatchBrush、SolidBrush和TextureBrush等。畫刷類型一般在System.Drawing命名空間中,如果應用HatchBrush和GradientBrush畫刷,需要在程序中引入System.Drawing.Drawing2D命名空間。
1.SolidBrush(單色畫刷)
它是一種一般的畫刷,通常只用一種顏色去填充GDI+圖形,例如:
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
SolidBrush sdBrush1 = new SolidBrush(Color.Red);
SolidBrush sdBrush2 = new SolidBrush(Color.Green);
SolidBrush sdBrush3 = new SolidBrush(Color.Blue);
g.FillEllipse(sdBrush2, 20, 40, 60, 70);
Rectangle rect = new Rectangle(0, 0, 200, 100);
g.FillPie(sdBrush3, 0, 0, 200, 40, 0.0f, 30.0f );
PointF point1 = new PointF(50.0f, 250.0f);
PointF point2 = new PointF(100.0f, 25.0f);
PointF point3 = new PointF(150.0f, 40.0f);
PointF point4 = new PointF(250.0f, 50.0f);
PointF point5 = new PointF(300.0f, 100.0f);
PointF[] curvePoints = {point1, point2, point3, point4,point5 };
g.FillPolygon(sdBrush1, curvePoints);
}
運行結果如圖7.7所示。
圖7.7 SolidBrush應用
2.HatchBrush(陰影畫刷)
HatchBrush類位於System.Drawing.Drawing2D命名空間中。陰影畫刷有兩種顏色:前景色和背景色,以及6種陰影。前景色定義線條的顏色,背景色定各線條之間間隙的顏色。HatchBrush類有兩個構造函數:
l public HatchBrush(HatchStyle,Color forecolor);
l public HatchBrush(HatchStyle,Color forecolor,Color backcolor);
HatchStyle枚舉值指定可用於HatchBrush對象的不同圖案。
HatchStyle的主要成員如表7.7所示。
表7.7 HatchStyle主要成員
名稱 |
說明 |
BackwardDiagonal |
從右上到左下的對角線的線條圖案。 |
Cross |
指定交叉的水平線和垂直線。 |
DarkDownwardDiagonal |
指定從頂點到底點向右傾斜的對角線,其兩邊夾角比ForwardDiagonal小50%,寬度是其兩倍。此陰影圖案不是鋸齒消除的。 |
DarkHorizontal |
指定水平線的兩邊夾角比Horizontal小50%並且寬度是Horizontal的兩倍。 |
DarkUpwardDiagonal |
指定從頂點到底點向左傾斜的對角線,其兩邊夾角比BackwardDiagonal小50%,寬度是其兩倍,但這些直線不是鋸齒消除的。 |
DarkVertical |
指定垂直線的兩邊夾角比Vertical小50%並且寬度是其兩倍。 |
DashedDownwardDiagonal |
指定虛線對角線,這些對角線從頂點到底點向右傾斜。 |
DashedHorizontal |
指定虛線水平線。 |
DashedUpwardDiagonal |
指定虛線對角線,這些對角線從頂點到底點向左傾斜。 |
DashedVertical |
指定虛線垂直線。 |
DiagonalBrick |
指定具有分層磚塊外觀的陰影,它從頂點到底點向左傾斜。 |
DiagonalCross |
交叉對角線的圖案。 |
Divot |
指定具有草皮層外觀的陰影。 |
ForwardDiagonal |
從左上到右下的對角線的線條圖案。 |
Horizontal |
水平線的圖案。 |
HorizontalBrick |
指定具有水平分層磚塊外觀的陰影。 |
LargeGrid |
指定陰影樣式Cross。 |
LightHorizontal |
指定水平線,其兩邊夾角比Horizontal小50%。 |
LightVertical |
指定垂直線的兩邊夾角比Vertical小50%。 |
Max |
指定陰影樣式SolidDiamond。 |
Min |
指定陰影樣式Horizontal。 |
NarrowHorizontal |
指定水平線的兩邊夾角比陰影樣式Horizontal小 75%(或者比LightHorizontal小25%)。 |
NarrowVertical |
指定垂直線的兩邊夾角比陰影樣式Vertical小 75%(或者比LightVertica小25%)。 |
OutlinedDiamond |
指定互相交叉的正向對角線和反向對角線,但這些對角線不是鋸齒消除的。 |
Percent05 |
指定5%陰影。前景色與背景色的比例為5:100。 |
Percent90 |
指定90%陰影。前景色與背景色的比例為90:100。 |
Plaid |
指定具有格子花呢材料外觀的陰影。 |
Shingle |
指定帶有對角分層鵝卵石外觀的陰影,它從頂點到底點向右傾斜。 |
SmallCheckerBoard |
指定帶有棋盤外觀的陰影。 |
SmallConfetti |
指定帶有五彩紙屑外觀的陰影。 |
SolidDiamond |
指定具有對角放置的棋盤外觀的陰影。 |
Sphere |
指定具有球體彼此相鄰放置的外觀的陰影。 |
Trellis |
指定具有格架外觀的陰影。 |
Vertical |
垂直線的圖案。 |
Wave |
指定由代字號“~”構成的水平線。 |
Weave |
指定具有織物外觀的陰影。 |
下面代碼顯示了HatchBrush畫刷的使用。
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
HatchBrush hBrush1 = new HatchBrush(HatchStyle.DiagonalCross, Color.Chocolate, Color.Red);
HatchBrush hBrush2 = new HatchBrush(HatchStyle.DashedHorizontal,
Color.Green, Color.Black);
HatchBrush hBrush3 = new HatchBrush(HatchStyle.Weave,
Color.BlueViolet, Color.Blue);
g.FillEllipse(hBrush1, 20, 80, 60, 20);
Rectangle rect = new Rectangle(0, 0, 200, 100);
g.FillPie(hBrush3, 0, 0, 200, 40, 0.0f, 30.0f );
PointF point1 = new PointF(50.0f, 250.0f);
PointF point2 = new PointF(100.0f, 25.0f);
PointF point3 = new PointF(150.0f, 40.0f);
PointF point4 = new PointF(250.0f, 50.0f);
PointF point5 = new PointF(300.0f, 100.0f);
PointF[] curvePoints = {point1, point2, point3, point4, point5 };
g.FillPolygon(hBrush2, curvePoints);
}
運行結果如圖7.8所示。
圖7.8 HatchBrush應用
3.TextureBrush(紋理畫刷)
紋理畫刷擁有圖案,並且通常使用它來填充封閉的圖形。為了對它初始化,可以使用一個已經存在的別人設計好了的圖案,或使用常用的設計程序設計的自己的圖案,同時應該使圖案存儲為常用圖形文件格式,如BMP格式文件。這里有一個設計好的位圖,被存儲為Papers.bmp文件。
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
//根據文件名創建原始大小的bitmap對象
Bitmap bitmap = new Bitmap("D:\\mm.jpg");
//將其縮放到當前窗體大小
bitmap = new Bitmap(bitmap, this.ClientRectangle.Size);
TextureBrush myBrush = new TextureBrush(bitmap);
g.FillEllipse(myBrush, this.ClientRectangle);
}
運行結果如圖7.9所示。
圖7.9 TextTureBursh應用
4.LinearGradientBrush和PathGradientBrush(漸變畫刷)
漸變畫刷類似與實心畫刷,因為它也是基於顏色的,與實心畫刷不同的是:漸變畫刷使用兩種顏色;它的主要特點是:在使用過程中,一種顏色在一端,而另外一種顏色在另一端,在中間,兩種顏色融合產生過渡或衰減的效果。
漸變畫刷有兩種:線性畫刷和路徑畫刷(LinearGradientBrush和PathGradientBrush)。
其中LinearGradientBrush可以顯示線性漸變效果,而PathGradientBrush是路徑漸變的可以顯示比較具有彈性的漸變效果。
(1)LinearGradientBrush類
LinearGradientBrush類構造函數如下:
public LinearGradientBrush(Point point1,Point point2,Color color1,Color color2)
參數說明:
point1:表示線性漸變起始點的Point結構。
point2:表示線性漸變終結點的Point結構。
color1:表示線性漸變起始色的Color結構。
color2:表示線性漸變結束色的Color結構。
代碼如下:
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
LinearGradientBrush myBrush = new LinearGradientBrush(this.ClientRectangle, Color.White, Color.Blue, LinearGradientMode.Vertical);
g.FillRectangle(myBrush, this.ClientRectangle);
}
運行結果如圖7.10所示。
圖7.10 LinearGradientBrush的應用
(2)PathGradientBrush類
PathGradientBrush類的構造函數如下:
public PathGradientBrush (GraphicsPath path);
參數說明:
path:GraphicsPath,定義此PathGradientBrush填充的區域。
例子代碼如下:
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Point centerPoint = new Point(150, 100);
int R = 60;
GraphicsPath path = new GraphicsPath();
path.AddEllipse(centerPoint.X-R,centerPoint.Y-R,2*R,2*R);
PathGradientBrush brush = new PathGradientBrush(path);
//指定路徑中心點
brush.CenterPoint = centerPoint;
//指定路徑中心的顏色
brush.CenterColor = Color.Red;
//Color類型的數組指定與路徑上每個頂點的顏色
brush.SurroundColors = new Color[] { Color.Plum };
g.FillEllipse(brush,centerPoint.X-R,centerPoint.Y-R,2*R,2* R);
centerPoint = new Point(350, 100);
R = 20;
path = new GraphicsPath();
path.AddEllipse(centerPoint.X-R,centerPoint.Y-R,2*R,2*R);
path.AddEllipse(centerPoint.X-2*R,centerPoint.Y-2*R,4*R,4* R);
path.AddEllipse(centerPoint.X-3*R,centerPoint.Y-3*R,6*R,6* R);
brush = new PathGradientBrush(path);
brush.CenterPoint = centerPoint;
brush.CenterColor = Color.Red;
brush.SurroundColors = new Color[] { Color.Black, Color.Blue, Color.Green };
g.FillPath(brush, path);
}
運行結果如圖7.11所示。
圖7.11 PathGradientBrush應用
7.2 C#圖像處理基礎
本節主要介紹C#圖像處理基礎知識以及對圖像的基本處理方法和技巧,主要包括圖像的加載、變換和保存等操作。
7.2.1 C#圖像處理概述
1.圖像文件的類型
GDI+支持的圖像格式有BMP、GIF、JPEG、EXIF、PNG、TIFF、ICON、WMF、EMF等,幾乎涵蓋了所有的常用圖像格式,使用GDI+可以顯示和處理多種格式的圖像文件。
2.圖像類
GDI+提供了Image、Bitmap和Metafile等類用於圖像處理,為用戶進行圖像格式的加載、變換和保存等操作提供了方便。
(1)Image類
Image類是為Bitmap和Metafile的類提供功能的抽象基類。
(2)Metafile類
定義圖形圖元文件,圖元文件包含描述一系列圖形操作的記錄,這些操作可以被記錄(構造)和被回放(顯示)。
(3)Bitmap類
封裝GDI+位圖,此位圖由圖形圖像及其屬性的像素數據組成,Bitmap是用於處理由像素數據定義的圖像的對象,它屬於System.Drawing命名空間,該命名空間提供了對GDI+基本圖形功能的訪問。Bitmap類常用方法和屬性如表7.8所示。
表7.8 Bitmap常用屬性和方法
名稱 |
說明 |
公共屬性 |
|
Height |
獲取此Image對象的高度。 |
RawFormat |
獲取此Image對象的格式。 |
Size |
獲取此Image對象的寬度和高度。 |
Width |
獲取此Image對象的寬度。 |
公共方法 |
|
GetPixel |
獲取此Bitmap中指定像素的顏色。 |
MakeTransparent |
使默認的透明顏色對此Bitmap透明。 |
RotateFlip |
旋轉、翻轉或者同事旋轉和翻轉Image對象。 |
Save |
將Image對象以指定的格式保存到指定的Stream對象。 |
SetPixel |
設置Bitmap對象中指定像素的顏色。 |
SetPropertyItem |
將指定的屬性項設置為指定的值。 |
SetResolution |
設置此Bitmap的分辨率。 |
Bitmap類有多種構造函數,因此可以通過多種形式建立Bitmap對象,例如:
從指定的現有圖像建立Bitmap對象
Bitmap box1 =new Bitmap(pictureBox1.Image);
從指定的圖像文件建立Bitmap對象,其中“C:\MyImages\TestImage.bmp”已存在的圖像文件
Bitmap box2 =new Bitmap("C:\\MyImages\\TestImage.bmp");
從現有的Bitmap對象建立新的Bitmap對象
Bitmap box3 = new Bitmap(box1);
7.2.2 圖像的輸入和保存
1.圖像的輸入
在窗體或圖形框內輸入圖像有兩種方式:(一)在窗體設計時使用圖形框對象的Image屬性輸入;(二)在程序中通過打開文件對話框輸入。
方法(一)、窗體設計時使用圖形框對象的Image屬性輸入
窗體設計時使用對象的Image屬性輸入圖像的操作如下:
(1)在窗體上,建立一個圖形框對象(pictureBox1),選擇圖形框對象屬性中的Image屬性,如圖7.12所示。
(2)單擊Image屬性右側的【…】,彈出一個“選擇資源”窗口,在該窗口中選擇“本地資源”,單擊【導入(M)...】將彈出一個“打開”對話框,如圖7.13所示。
圖7.12 Image屬性
圖7.13“打開”對話框
(3)選擇圖像文件后,單擊【打開】按鈕。
方法(二)、使用“打開文件”對話框輸入圖像
在窗體上添加一個命令按鈕(button1)和一個圖形框對象(pictureBox1),雙擊命令按鈕,在響應方法中輸入如下代碼:
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog ofdlg = new OpenFileDialog();
ofdlg.Filter = "BMP File(*.bmp)|*.bmp";
if (ofdlg.ShowDialog() == DialogResult.OK)
{
Bitmap image = new Bitmap(ofdlg.FileName);
pictureBox1.Image = image;
}
}
執行該程序時,使用“打開文件”對話框,選擇圖像文件,該圖像將會被打開,並顯示在pictureBox1圖像框中。
【例7.7】圖像輸入。
采用方法(二)來實現圖像的輸入。
設計步驟如下:
(1)建立如圖7.14所示的項目界面,在窗體上加入【打開圖像】命令按鈕和一個PictureBox控件。
(2)雙擊【打開圖像】命令按鈕,編輯按鈕的單擊事件響應函數,其代碼同方法(二)中所寫代碼,在此不再重復。
圖7.14 圖像輸入
(3)運行后單擊【打開圖像】按鈕,彈出一個“打開文件”對話框,選擇圖象文件名,運行結果如圖7.15所示。
圖7.15 運行結果
2.圖像的保存
保存圖像的步驟如下:
(1)當使用按鈕和保存對話框保存文件時,加入保存按鈕和PictureBox控件,窗體設計如圖7.16所示。
(2)保存命令鈕的單擊事件的響應函數代碼如下:
private void button2_Click(object sender, EventArgs e)
{
string str;
Bitmap box1 = new Bitmap(pictureBox1.Image);
SaveFileDialog sfdlg = new SaveFileDialog();
sfdlg.Filter = "bmp文件(*.BMP)|*.BMP|All File(*.*)|*.*";
sfdlg.ShowDialog();
str = sfdlg.FileName;
box1.Save(str);
}
執行該過程時,將打開“另存為”對話框,如圖7.17所示。
圖7.16 保存圖像
圖7.17 “另存為”對話框
選擇圖像文件的保存路徑。
3.圖像格式的轉換
使用Bitmap對象的Save方法,可以把打開的圖像保存為不同的文件格式,從而實現圖像格式的轉換。在上述例子中添加一個命令按鈕,雙擊該命令按鈕,編輯其相應代碼如下:
private void button3_Click(object sender, EventArgs e)
{
string str;
Bitmap box1 = new Bitmap(pictureBox1.Image);
SaveFileDialog sfdlg = new SaveFileDialog();
sfdlg.Filter = "bmp文件(*.jpeg)|*.jpeg|All File(*.*)|*.*";
sfdlg.ShowDialog();
str = sfdlg.FileName;
box1.Save(str,System.Drawing.Imaging.ImageFormat.Jpeg);
}
Bitmap對象的Save方法中的第二個參數指定了圖像保存的格式。
Imaging.ImageFormat支持的格式如表7.9所示:
表7.9 Imaging.ImageFormat支持的格式
名稱 |
說明 |
Bmp |
獲取位圖圖像格式(BMP)。 |
Emf |
獲取增強型Windows圖元文件圖像格式(EMF)。 |
Exif |
獲取可交換圖像文件(Exif)格式。 |
Gif |
獲取圖形交換格式(GIF)圖像格式。 |
Guid |
獲取表示此ImageForma 對象的Guid結構。 |
Icon |
獲取Windows圖標圖像格式。 |
Jpeg |
獲取聯合圖像專家組(JPEG)圖像格式。 |
MemoryBmp |
獲取內存位圖圖像格式。 |
Png |
獲取W3C可移植網絡圖形(PNG)圖像格式。 |
Tiff |
獲取標簽圖像文件格式(TIFF)圖像格式。 |
Wmf |
獲取Windows圖元文件(WMF)圖像格式。 |
7.2.3 圖像的拷貝和粘貼
圖像拷貝和粘貼是圖像處理的基本操作之一,通常有兩種方法來完成圖像的拷貝和粘貼:一種可以使用剪貼板拷貝和粘貼圖像,一種使用AxPictureClip控件拷貝和粘貼圖像。
1.使用剪貼板拷貝和粘貼圖像
剪貼板是在Windwos系統中單獨預留出來的一塊內存,它用來暫時存放在Windwos應用程序間要交換的數據,使用剪貼板對象可以輕松實現應用程序間的數據交換,這些數據包括圖像或文本。在C#中,剪貼板通過Clipboard類來實現,Clipboard類的常用方法如表7.10所示。
表7.10 Clipboard類常用方法
名稱 |
說明 |
Clear |
從剪貼板中移除所有數據。 |
ContainsData |
指示剪貼板中是否存在指定格式的數據,或可轉換成此格式的數據。 |
ContainsImage |
指示剪貼板中是否存在 Bitmap 格式或可轉換成此格式的數據。 |
ContainsText |
已重載。指示剪貼板中是否存在文本數據。 |
GetData |
從剪貼板中檢索指定格式的數據。 |
GetDataObject |
檢索當前位於系統剪貼板中的數據。 |
GetFileDropList |
從剪貼板中檢索文件名的集合。 |
GetImage |
檢索剪貼板上的圖像。 |
GetText |
已重載。從剪貼板中檢索文本數據。 |
SetAudio |
已重載。將WaveAudio格式的數據添加到剪貼板中。 |
SetData |
將指定格式的數據添加到剪貼板中。 |
SetDataObject |
已重載。將數據置於系統剪貼板中。 |
SetImage |
將Bitmap格式的Image添加到剪貼板中。 |
SetText |
已重載。將文本數據添加到剪貼板中。 |
剪貼板的使用主要有一下兩個步驟:
l 將數據置於剪貼板中。
l 從剪貼板中檢索數據。
下面簡要介紹剪貼板的使用。
(1)將數據置於剪貼板中
可以通過SetDataObject方法將數據置於剪貼板中,SetDataObject方法有以下三種形式的定義:
l Clipboard.SetDataObject(Object):將非持久性數據置於系統剪貼板中。由.NET Compact Framework支持。
l Clipboard.SetDataObject(Object,Boolean):將數據置於系統剪貼板中,並指定在退出應用程序后是否將數據保留在剪貼板中。
l Clboard.SetDataObject(Object,Boolean,Int32,Int32):嘗試指定的次數,以將數據置於系統剪貼板中,且兩次嘗試之間具有指定的延遲,可以選擇在退出應用程序后將數據保留在剪貼板中。
將字符串置於剪貼板中的語句如下所示:
string str = "Mahesh writing data to the Clipboard";
Clipboard.SetDataObject(str)
(2)從剪貼板中檢索數據
可以通過GetDataObject方法從剪貼板中檢索數據,它將返回IdataObject,其定義如下:
public static IDataObject GetDataObject();
首先使用IdataObject對象的GetDataPresent方法檢測剪貼板上存放的是什么類型的數據,然后是使用IdataObject對象的GetData方法獲取剪貼板上相應的數據類型的數據。下面使用GetDataObject方法從剪貼板中檢索出字符串數據。
例如:
IDataObject iData = Clipboard.GetDataObject();
if (iData.GetDataPresent(DataFormats.Text))
{
string str =(String)iData.GetData(DataFormats.Text);
}
【例7.8】使用剪貼板拷貝和粘貼圖像。
(1)建立如圖7.18所示的窗體。在窗體上天加兩個圖片框控件和兩個命令按鈕控件。利用第一個圖片框的屬性窗口為其輸入圖像。
(2)雙擊【復制】命令按鈕,輸入如下代碼,將圖像置於剪貼板中。
private void button1_Click(object sender, EventArgs e)
{
Clipboard.SetDataObject(pictureBox1.Image);
}
(3)雙擊【粘貼】命令按鈕,輸入如下代碼,從剪貼板中檢索出圖像,並顯示於第二個圖片框中。
private void button2_Click(object sender, EventArgs e)
{
IDataObject iData = Clipboard.GetDataObject();
if (iData.GetDataPresent(DataFormats.Bitmap))
{
pictureBox2.Image = (Bitmap)iData.GetData
(DataFormats.Bitmap);
}
}
(4)運行程序,首先單擊【復制】命令按鈕,然后單擊【粘貼】命令按鈕,運行結果如圖7.19所示。
圖7.18 剪貼板窗體設計
圖7.19 剪貼板圖像復制
2.使用AxPictureClip控件拷貝和粘貼圖像
AxPictureClip控件不是常規控件,而是一個ActiveX控件。因此,工具箱中沒有該控件,要想使用該控件,必須把該控件添加到工具箱中,具體步驟如下:
(1)右鍵單擊工具箱的空白處,在彈出的快捷菜單中選擇【選擇項】菜單項,則彈出“選擇工具箱項”對話框,如圖7.20所示。
圖7.20 添加AxPictureClip控件
(2)在該對話框中的【COM組件】選項卡中選擇【Microsoft Picture Clip Control,version6】項,並單擊【確定】按鈕,該控件就添加到工具箱中了。
AxPictureClip控件可用於隨機訪問方法或者枚舉訪問方法指定源位圖中剪切區域如下:
l 使用隨機訪問方法來作為剪切區域選擇源位圖的任何部分。通過使用ClipX和ClipY屬性指定的剪切區域左上角,ClipHeight和ClipWidth屬性確定剪切區域的區域。當想要查看位圖的一部分此方法很有用。
l 使用枚舉訪問方法可以分成的行和列數指定源位圖。結果是圖片的統一矩陣單元編號0、1、2和等等,通過使用GraphicCell屬性來訪問單個單元。當源位圖圖像與要訪問單獨的調色板包含這種方法非常有用。
【例7.9】使用AxPictureClip控件剪切和粘貼圖像。
(1)建立如圖7.21示的窗體。在窗體上天加兩個圖片框控件和兩個命令按鈕控件。
(2)雙擊【打開】命令按鈕,輸入如下代碼,將圖像打開。
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog ofdlg = new OpenFileDialog();
ofdlg.Filter = "BMP File(*.bmp)|*.bmp";
if (ofdlg.ShowDialog() == DialogResult.OK)
{
Bitmap image = new Bitmap(ofdlg.FileName);
pictureBox1.Image = image;
}
}
(3)雙擊【復制與粘貼】命令按鈕,輸入如下代碼,將圖像復制到第二個圖片框中。
private void button2_Click(object sender, EventArgs e)
{
//使用枚舉訪問方法
axPictureClip1.Picture = pictureBox1.Image;
axPictureClip1.Cols = 6;//將圖片分成6列
axPictureClip1.Rows = 3;//將圖片分成3行
try
{
pictureBox2.Image = axPictureClip1.get_GraphicCell(0);//取出第一個圖像塊
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
(4)運行程序,單擊【打開】命令按鈕,選擇一個圖像文件打開,如圖7.21所示,然后單擊【復制與粘貼】命令按鈕,運行結果如圖7.22所示。
圖7.21 打開圖片后效果
(5)使用隨機訪問方法。
也可以使用隨機訪問方法,只需將上述例子中的【復制與粘貼】命令按鈕的響應方法改為如下代碼即可:
private void button2_Click(object sender, EventArgs e)
{
//使用隨機訪問方法
axPictureClip1.Picture = pictureBox1.Image;
axPictureClip1.ClipX = 15;
axPictureClip1.ClipY = 15;
axPictureClip1.ClipHeight = 50;
axPictureClip1.ClipWidth = 50;
pictureBox2.Image = axPictureClip1.Clip;//
}
圖7.22 粘貼后效果
注意:如果沒有PICCLP32.OCX控件,需要自行下載或復制PICCLP32.OCX到本機,並通過注冊程序REGSVR32注冊該組件。例如,如果該文件在C:\WINDOWS\system32\路徑下,可以通過如下命令行語句實現注冊:REGSVR32 C:\WINDOWS\system32\PICCLP32.OCX
7.2.4 彩色圖像處理
1.圖像的分辨率
所謂分辨率就是指畫面的解析度,由多少像素構成,數值越大,圖像也就越清晰。我們通常所看到的分辨率都以乘法形式表現的,例如800*600,其中“800”表示屏幕上水平方向顯示的點數,“600”表示垂直方向的點數。圖像分辨率越大,越能表現更豐富的細節。圖像的分辨率決定了圖像與原物的逼進程度,對同一大小的圖像,其像素數越多,即將圖像分割的越細,圖像越清晰,稱之為分辨率高,反之為分辨率低,分辨率的高低取決於采樣操作。例如,對於一幅256*256分辨率的圖像,采用變換的方法可以實現不同分辨率顯示。
【例7.10】將256*256分辨率的圖像變換為64*64分辨率。
算法說明:將256*256分辨率的圖像變換為64*64分辨率方法是將源圖像分成4*4的子圖像塊,然后將該4*4子圖像塊的所有像素的顏色按F(i,j)的顏色值進行設定,達到降低分辨率的目的。
建立一個如圖7.23所示界面的項目,【分辨率】命令按鈕的響應方法的代碼設計如下:
private void button3_Click(object sender, EventArgs e)
{
Color c = new Color();
//把圖片框中的圖片給一個Bitmap類型
Bitmap box1 = new Bitmap(pictureBox1.Image);
Bitmap box2 = new Bitmap(pictureBox1.Image);
int r, g, b, size, k1, k2, xres, yres, i, j;
xres = pictureBox1.Image.Width;
yres = pictureBox1.Image.Height;
size = 4;
for (i = 0; i <= xres - 1; i += size)
{
for (j = 0; j <= yres - 1; j += size)
{
c = box1.GetPixel(i, j);
r = c.R;
g = c.G;
b = c.B;
//用FromArgb把整型轉換成顏色值
Color cc = Color.FromArgb(r, g, b);
for (k1 = 0; k1 <= size - 1; k1++)
{
for (k2 = 0; k2 <= size - 1; k2++)
{
if (i + k1 < pictureBox1.Image.Width)
box2.SetPixel(i + k1, j + k2, cc);
}
}
}
}
pictureBox2.Refresh();//刷新
pictureBox2.Image = box2;//圖片賦到圖片框中
}
輸入圖像分辨率為256*256像素,轉換為64*64分辨率圖像如圖7.23所示。
圖7.23 分辨率
2.彩色圖像變換灰度圖像
(1)彩色位圖圖像的顏色
圖像像素的顏色是由三種基本色顏色,即紅(R)、綠(G)、藍(B)有機組合而成的,稱為三基色。每種基色可取0~255的值,因此由三基色可組合成(256*256*256)1677萬種顏色,每種顏色都有其對應的R、G、B值。例如,常見的7種顏色及其對應的R、G、B值如表7.11所示。
表7.11 常見的7種顏色及其對應的R、G、B值
顏色名 |
R值 |
G值 |
B值 |
紅 |
255 |
0 |
0 |
藍 |
0 |
0 |
255 |
綠 |
0 |
255 |
0 |
白 |
255 |
255 |
255 |
黃 |
255 |
255 |
0 |
黑 |
0 |
0 |
0 |
青 |
0 |
255 |
255 |
品紅 |
255 |
0 |
255 |
(2)彩色圖像顏色值的獲取
在使用C#系統處理彩色圖像時,使用Bitmap類的GetPixel方法獲取圖像上指定像素的顏色值,格式為:
Color c = new Color();
c = box1.GetPixel(i,j);
其中,(i,j)為獲得顏色的坐標位置。GetPixel方法取得指定位置的顏色值並返回一個長整型的整數。例如,求圖片框1中圖像在位置(i,j)的像素顏色值c時,可寫為:
Color c=new Color();
c = box1.GetPixel(i,j);
(3)彩色位圖顏色值分解
像素顏色值c是一個長整型的數值,占4個字節,最上位字節的值為“0”,其它3個下位字節依次為B、G、R,值為0~255。
從從值分解出R、G、B值可直接使用:
Color c =new Color();
c= box1.GetPixel(i,j);
r=c.R;
g=c.G;
b=c.B;
(4)圖像像素顏色的設定
設置像素可使用SetPixel方法。用法如下:
Color c1=Color.FromArgb(rr,gg,bb);
Box2.SetPixel(i+k1,j+k2,c1);
【例7.11】彩色圖像生成灰度圖像。
算法說明:將彩色圖像像素的顏色值分解為三基色R、G、B,求其和的平均值,然后使用SetPixel方法以該平均值參數生成圖像。
其相應的代碼設計如下:
c = b.GetPixel(i,j);
r = c.b;
g = c.G;
b = c.B;
cc = (int)((r+g+b)/3);
if (cc<0)cc=0;
f (cc>255)cc=255;
Color c1 = Color.FromArgb(cc,cc,cc);
B1.SetPixel(i,j,c1);
(1)在上例中增加一個【灰度圖像】命令按鈕。
(2)雙擊該按鈕,編輯其響應方法的代碼如下:
private void button4_Click(object sender, EventArgs e)
{
Color c = new Color();
//把圖片框1中的圖片給一個Bitmap類型
Bitmap b = new Bitmap(pictureBox1.Image);
Bitmap b1 = new Bitmap(pictureBox1.Image);
int rr, gg, bb, cc;
for (int i = 0; i < pictureBox1.Width; i++)
{
for (int j = 0; j < pictureBox1.Height; j++)
{
c = b.GetPixel(i,j);
rr = c.R;
gg = c.G;
bb = c.B;
cc = (int)((rr + gg + bb) / 3);
if (cc < 0) cc = 0;
if (cc > 255) cc = 255;
//用FromArgb把整型轉換成顏色值
Color c1 = Color.FromArgb(cc, cc, cc);
b1.SetPixel(i,j,c1);
}
pictureBox2.Refresh();//刷新
pictureBox2.Image = b1;//圖片賦給圖片框2
}
}
(3)運行程序,程序運行結果如圖7.24所示。
圖7.24 灰度圖像
3.灰度圖像處理
【例7.12】改善對比度。
算法說明:本例根據特定的輸入輸出灰度轉換關系,增強了圖像灰度,處理后圖像的中等灰度值增大,圖像變亮。
注意:本例中描述對比度改善的輸入、輸出灰度值對應關系的程序段為:
lev= 80;
wid =100;
for (x=0;x<256;x+=1)
{
lut[x]=255;
}
for (x=lev;x<(lev+wid);x++)
{
dm =((double)(x-lev)/(double)wid)*255f;
lut [x] =(int)(dm);
}
(1)在窗體上添加一個“對比度”命令按鈕。
(2)雙擊“對比度”命令按鈕,編輯代碼如下:
private void button5_Click(object sender, EventArgs e)
{
Color c = new Color();
Bitmap box1 = new Bitmap(pictureBox1.Image);
Bitmap box2 = new Bitmap(pictureBox1.Image);
int rr, x, m, lev, wid;
int[] lut = new int[256];
int [,,]pic=new int[600,600,3];
double dm;
lev = 80;
wid = 100;
for (x = 0; x < 256; x += 1)
{
lut[x] = 255;
}
for (x = lev; x < (lev + wid); x++)
{
dm = ((double)(x - lev) / (double)wid) * 255f;
lut[x] = (int)dm;
}
for (int i = 0; i < pictureBox1.Image.Width - 1; i++)
{
for (int j = 0; j < pictureBox1.Image.Height; j++)
{
c = box1.GetPixel(i, j);
pic[i, j, 0] = c.R;
pic[i, j, 1] = c.G;
pic[i, j, 2] = c.B;
}
}
for (int i = 0; i < pictureBox1.Image.Width - 1; i++)
{
for (int j = 0; j < pictureBox1.Image.Height; j++)
{
m = pic[i, j, 0];
rr=lut[m];
Color c1 = Color.FromArgb(rr, rr, rr);
box2.SetPixel(i,j,c1);
}
pictureBox2.Refresh();
pictureBox2.Image = box2;
}
}
(3)運行程序,運行結果如圖7.25所示。
圖7.25 改善對比度
本章小結
本章主要講述了C#下的圖形圖像基礎知識,對圖形的繪制,圖像的處理和音頻視頻等多媒體的使用方法;在圖片處理方面.NET提供了一個GDI+,功能十分強大,能完成對圖像的全方位處理。
思考與練習(習題)
1.繪制一個圖形需要哪些基本步驟?
2.在窗體上繪制圖形有哪些方法?
3.如何構造一個顏色對象?
4.打開圖像有哪些方法?
5.如何轉換圖像格式?
我用C#的GDI+在FORM窗體上drawimage顯示一個圖像,然后我想在同一窗體上再drawimage另一張圖像,目的是在同一個from窗體中刪除一張圖像后再顯示另一張圖像,如此不斷反復操作,但是Graphics的Dispose方法只是用來釋放對象,並不能清空畫板,請問我應該用什么方法來實現這種效果呢?
別直接畫在窗體是,弄一個PictureBox 畫在這上面!~
每次重畫之前,可以重繪一下PictureBox如: PictureBox1.Invalidate().
//取得圖片大小
System.Drawing.Size size = new System.Drawing.Size((int)image.Width, (int)image.Height);
//新建一個bmp圖片
System.Drawing.Image bitmap = new System.Drawing.Bitmap(size.Width, size.Height);
//新建一個畫板
System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bitmap);
//設置高質量插值法
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Default;
//設置高質量,低速度呈現平滑程度
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.Default;
//清空一下畫布
g.Clear(System.Drawing.Color.White);
//在指定位置畫圖
g.DrawImage(image, new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height),
new System.Drawing.Rectangle(0, 0, image.Width, image.Height),
System.Drawing.GraphicsUnit.Pixel);
//文字水印
System.Drawing.Graphics G=System.Drawing.Graphics.FromImage(bitmap);
System.Drawing.Font f=new Font("宋體",10);
System.Drawing.Brush b=new SolidBrush(Color.Black);
G.DrawString("hi.baidu.com/emixo",f,b,10,10);
G.Dispose();
this.button1.ClientRectangle//常用來獲取當前客戶端的矩形大小
Point p=this.button1.Location;//獲取控件相對窗體的坐標;
this.ClientSize = new Size(120,120);//獲取或者設置控件的大小
暴漏事件的寫法
private EventHandler _OnButtonClick = null;
[EditorBrowsable(EditorBrowsableState.Always)]
[Browsable(true)]
public event EventHandler OnButtonClick//定義
{
add { _OnButtonClick = new EventHandler(value); }
remove { _OnButtonClick -= new EventHandler(value); }
}
private void btOk_Click(object sender, EventArgs e)
{
if (_OnButtonClick != null)
_OnButtonClick(this, e);執行
}
uc.OnButtonClick += new EventHandler(myControl1_Click);//注冊方法
Paint事件
C#中,窗體、容器、控件的繪制有着一定的順序。首先繪制容器里的控件,然后繪制窗體上的容器,最后再繪制窗體。可以在控件、容器及窗體的Paint事件處理程序中加入MessageBox.Show方法,然后就可以看出它們的執行順序:Control.Paint--->Container.Paint--->Form.Paint
大多數控件都有Paint事件,但有一些控件不具有,如ListBox控件。System.Windows.Forms.Control類是所有控件(沒有完全考察)的基類,所以,直接繼承它的控件都有Paint事件。但是ListBox是繼承至System.Windows.Forms.ListControl,雖然ListControl也繼承至Control類,但是它屏蔽掉了Paint事件。(不知道是怎么實現的???)
Load事件
我們再來看看Load事件和Paint事件的執行順序。用同樣的方法可以發現:窗體的構造方法先於Load事件,Load事件先於Paint事件發生。所以,一定要注意你的一些初始化代碼的放置位置。初始化代碼最好放在窗體的構造方法中,並且要在InitializeComponent()方法之后。只有窗體或者UserControl這類的最上層的Windows界面才具有Load事件。
Refresh方法
每次調用窗體的Refresh()方法時,都將按照控件、容器、窗體的順序觸發它們的Paint事件,執行相應的事件處理程序。也可以只調用某個控件的Refresh方法,而不重繪整個窗體。如果觸發某個Container的Paint事件,那么Container內的所有控件的Paint事件也將被觸發。
Refresh:強制控件使其工作區無效並立即重繪自己和任何子控件。==Invalidate + Update
Invalidate: 使控件的特定區域(可以自己設置區域,從而提高性能)無效並向控件發送繪制消息
Update:使控件重繪其工作區內的無效區域。 立即調用Paint事件
Paint:無處不在