C# GDI+编程之基础篇

简介:
一、 关于GDI+  
    从本质上来看,GDI+为开发者提供了一组实现与各种设备(例如监视器,打印机及其它具有图形化能力但不及涉及这些图形细节的设备)进行交互的库函数。 GDI+的本质在于,它能够替代开发人员实现与例如显示器及其它外设的交互;而从开发者角度来看,要实现与这些设备的直接交互却是一项艰巨的任务。

    下图1展示了GDI+在开发人员与上述设备之间起着重要的中介作用。其中,GDI+为我们“包办”了几乎一切—从把一个简单的字符串“HelloWorld”打印到控制台到绘制直线,矩形甚至是打印一个完整的表单等。 

            图1.GDI+担当着重要的中介作用 

    那么,GDI+是如何工作的呢?为了弄清这个问题,让我们来分析一个示例—绘制一条线段。实质上,一条线段就是一个从一个开始位置(X0,Y0)到一个结 束位置(Xn,Yn)的一系列像素点的集合。为了画出这样的一条线段,设备(在本例中指显示器)需要知道相应的设备坐标或物理坐标。

    然而,开发人员不是直接告诉该设备,而是调用GDI+的drawLine()方法,然后,由GDI+在内存(即“视频内存”)中绘制一条从点A到点B的直 线。GDI+读取点A和点B的位置,然后把它们转换成一个像素序列,并且指令监视器显示该像素序列。简言之,GDI+把设备独立的调用转换成了一个设备可 理解的形式;或者实现相反方向的转换。 
至此,我们已经简单了解了GDI+的工作机理。现在,让我们开始探讨如何实现一些基本的图像操作。
二、 图像操作—缩略图,缩放与保存  
在本文示例中,我们将实现如下的任务: 
1. 创建缩略图。 
2. 缩放一个加载的图像。 
3. 保存一个操作中的图像。 
a) 创建缩略图 
缩略图是图像的浓缩版本。典型情况下,一幅缩略图图像的尺寸为80×200像素。在GDI+中,一个图像的缩略图可以通过使用Image类的GetThumbnailImage()方法来创建。其函数原型如下: 

  
  
public Image GetThumbnailImage (
int thumbWidth,
int thumbHeight,
GetThumbnailImageAbort callback,
IntPtr callbackData
)

    第一个参数相应于缩略图的宽度;第二个参数相应于生成的缩略图的高度;第三个参数是一个Image.GetThumbnailImageAbort委托。 在 GDI+ 1.0 版中不使用此委托。即便如此,也必须创建一个委托并在该参数中传递对此委托的引用。第四个参数同样没有使用,但是也需要提供以实现兼容性。注意,第四个参 数必须为IntPtr.Zero。 
如果前两个参数(也就是宽度和高度)都为0的话,那么,GDI+返回一个嵌入式缩略图。否则,使用系统定义尺寸创建该缩略图。例如,如果img是一个图像类的实例,并且使用的宽度和高度都是系统定义的,创建一个缩略图的语句应该如下所示: 
Image thumbNailImage = img.GetThumbnailImage(0,0,tnCallBack,IntPtr.Zero); 
在此,thumbNailImage包含返回的缩略图,而tnCallback是一个相应于Image.GetThumbnailImageAbort的函数,其定义如下: 

  
  
// 必须调用它,但是没有使用 style = ' font-size:10.0pt;font-family:Verdana ' > publicbool tnCallbackMethod()
{
return false;
}

b) 缩放一个加载的图像 
缩放是放大或缩小一个图像的过程—通过在图像尺寸上乘以缩放因子实现。其中,缩放因子= 期望的图像尺寸/当前图像尺寸。例如,要把一个图像放大200%,则当前尺寸必须乘以200%(200%=200/100=2);为了缩小一个图像到 25%,则当前尺寸必须乘以25%或0.25(25/100=0.25倍)。 

c) 保存图像 
保存操作是图像操作中的关键操 作之一。在保存一个图像时,图像相应的类型信息也必须进行保存;也就是说,该图像的扩展名在这一过程中具有重要角色。每一种类型相应于一个特定的格式。实 质上,在保存一个图像时,根据该格式输出数据是非常必要的。然而,借助于GDI+ API的优势,一个对Image类的Save()方法的简单调用就可以把相应的写数据操作中所有细节省略掉。这个方法使用两个参数—被保存的图像的名字和 待保存图像的格式。该格式能够通过ImageFormat类提供的类型来指定。下列表格指定了GDI+支持的各种图像格式。 

属性 描述 
Bmp 指定BMP格式。 
Emf 指定EMF(增强的元文件格式)。 
Exif 指定EXIF格式。 
Gif 指定GIF格式。 
Guid 指定一个GUID结构,用于描述ImageFormatobject。 
Icon 指定Windows图标格式。 
Jpeg 指定JPEG格式。 
MemoryBmp 指定内存位图格式。 
Png 指定PNG格式。 
Tiff 指定TIFF格式。 
Wmf 指定WMF(Windows元文件格式)。 
其中,Emf和Wmf是特定于Windows系统的。 
假定你想使用名字“checker.gif”保存一个图像,那么,相应的实现语句将是: 
curImage.Save(“checker.gif”,ImageFormat.Gif); 
这里,curImage对应于Image类的实例。 
在下一节中,我将对前面开发的这个应用程序进行扩展。 
三、 实际开发中的图像操作  
  下面,我们来讨论实际中的使用情况。我将在本文示例应用程序中添加下列功能: 
1. 以用户指定的格式保存图像。 
2. 根据从菜单下选择的百分比放大图像。 
3. 创建一个加载图像的略缩图。 
相应的菜单操作如下所示: 
 mnuSave—文件菜单下保存图像的子菜单。 
 mnu200Zoom—放大图像200%。 
 mnuThumbNail—创建图像的一个略缩图。 

下面是处理菜单项mnuSave的Click事件相应的方法: 

  
  
private void mnuSave_Click( object sender,System.EventArgs e)
{
//如果图像已经创建 if(curImage == null)
return;
//调用SaveFileDialog对话框 SaveFileDialog saveDlg = new SaveFileDialog();
saveDlg.Title
= "Save Image As";
saveDlg.OverwritePrompt
= true;
saveDlg.CheckPathExists
= true;
saveDlg.Filter
= "Bitmap File(*.bmp)|*.bmp|" + "Gif File(*.gif)|*.gif|" + "JPEG File(*.jpg)|*.jpg|" + "PNG File(*.png)|*.png" ;
saveDlg.ShowHelp
= true;
//如果选择,则进行保存 if(saveDlg.ShowDialog() == DialogResult.OK)
{
//得到用户选择的文件名 string fileName = saveDlg.FileName;
//得到文件扩展名 string strFilExtn =fileName.Remove(0,fileName.Length - 3);
//保存文件 switch(strFilExtn)
{
case "bmp":
curImage.Save(fileName, ImageFormat.Bmp);
break;
case "jpg":
curImage.Save(fileName, ImageFormat.Jpeg);
break;
case "gif":
curImage.Save(fileName, ImageFormat.Gif);
break;
case "tif":
curImage.Save(fileName, ImageFormat.Tiff);
break;
case "png":
curImage.Save(fileName, ImageFormat.Png);
break;
default:
break;
}
}
}


首先,以可接收的扩展名显示这个保存对话框。然后,由从该对话框返回的文件名检索相应的扩展名。最后,根据该扩展名,使用相应的图像格式参数调用Save()方法。 
接下来,我们分析菜单项mnu200Zoom相应的处理器。首先,让我们在应用程序级添加下列以粗体显示的一行: 

  
  
private double curZoom = 1.0 ;
private Image curImage = null ; // 用于存储当前图像 private int i = 0 ; // 用于把屏幕重画操作与缩略图绘制部分区别开来 然后,必须对mnuLoad处理代码作少许调整,如下所示:
private void mnuLoad_Click( object sender,System.EventArgs e)
{
//创建OpenFileDialog OpenFileDialog opnDlg = new OpenFileDialog();
//设置一个图像类型过滤器 opnDlg.Filter = "All Image files|*.bmp;*.gif;*.jpg;*.ico;"+ "*.emf;,*.wmf|Bitmap Files(*.bmp;*.gif;*.jpg;"+ "*.ico)|*.bmp;*.gif;*.jpg;*.ico|"+ "Meta Files(*.emf;*.wmf;*.png)|*.emf;*.wmf;*.png";
opnDlg.Title
= "打开图像文件";
opnDlg.ShowHelp
= true;
//如果OK,选择它 if(opnDlg.ShowDialog() == DialogResult.OK)
{
//读取当前选择的文件名 curFileName = opnDlg.FileName;
//使用Image.FromFile创建图像对象 try {
curImage
= Image.FromFile(curFileName);
}
catch(Exception exp)
{
MessageBox.Show(exp.Message);
}
}
//改变AutoScrollMinSize属性 this.AutoScrollMinSize = new Size
((
int)(curImage.Width * curZoom),
(
int)(curImage.Height * curZoom));
i
++;
//重新绘制表单 Invalidate();
}


注意,在此新添加的代码分别在原来的图像宽度和高度上乘以放大因子以生成一个放大的图像。然后,必须相应地修改paint事件的处理器。如下所示: 

  
  
private void Form1_Paint( object sender, PaintEventArgs e)
{
if (curImage != null && i==0)
{
Graphics g
= this.CreateGraphics();
g.DrawImage(curImage,
new Rectangle
(AutoScrollPosition.X,
AutoScrollPosition.Y ,
(
int)(this.ClientRectangle.Width * curZoom),
(
int)(ClientRectangle.Height * curZoom)));
}
}

该图像应该有根据放大因子的相应的高度和宽度。下面,我们来看一下mnu200Zoom菜单项相应的事件处理器: 

  
  
private void mnu200_Click( object sender,System.EventArgs e)
{
if(curImage != null)
{
curZoom
= (double)200/100;
i
++;
Invalidate();
}
}

最后,我们来看一下mnuThumbNail菜单项相应的事件处理器: 

  
  
1 private void mnuThumbnail_Click( object sender, EventArgs e)
2 {
3if(curImage != null)
4{
5i++;
6//回调
7Image.GetThumbnailImageAbort tnCallBack =
8new Image.GetThumbnailImageAbort(tnCallbackMethod);
9//得到缩略图图像
10Image thumbNailImage = curImage.GetThumbnailImage
11(100, 100, tnCallBack, IntPtr.Zero);
12//创建一个Graphics对象
13Graphics tmpg = this.CreateGraphics();
14tmpg.Clear(this.BackColor);
15//画缩略图图像
16tmpg.DrawImage(thumbNailImage, 10, 10, thumbNailImage.Width, thumbNailImage.Height);
17//释放掉Graphics对象
18tmpg.Dispose();
19}

20
21}

22


    在此,我们首先创建一个GetThumbnailImageAbort类型的变量并且赋给它值tnCallbackMethod()—这是通过传递给该方 法GetThumbnailImageAbort实现的。然后,它创建一个新的Image类的实例以存储GetThumbnailImage方法返回的图 像—此后,这个方法将用于把缩略图绘制到屏幕上。
四、 小结  
  在本文中,我仅讨论了.NET C#环境下关于GDI+编程的一些基本的实用操作片断。在以后的文章中,我们将逐渐展开对.NET GDI+编程高级特征的探讨。 
【注】 
① 本文源码在Windows XP Professonal+VS2005环境下调试通过; 
② 本示例中私有变量的i的引入仅为了把屏幕重绘与缩略图绘制区别开来,读者可考虑其它更巧妙的办法; 

③ 图像重绘及滚动过程中出现屏幕抖动现象,读者可结合有关图像绘制双缓冲技术予以改进。 


















本文转自朱先忠老师51CTO博客,原文链接: http://blog.51cto.com/zhuxianzhong/59898,如需转载请自行联系原作者



相关文章
|
开发框架 前端开发 .NET
C#编程与Web开发
【4月更文挑战第21天】本文探讨了C#在Web开发中的应用,包括使用ASP.NET框架、MVC模式、Web API和Entity Framework。C#作为.NET框架的主要语言,结合这些工具,能创建动态、高效的Web应用。实际案例涉及企业级应用、电子商务和社交媒体平台。尽管面临竞争和挑战,但C#在Web开发领域的前景将持续拓展。
527 3
|
SQL 开发框架 安全
C#编程与多线程处理
【4月更文挑战第21天】探索C#多线程处理,提升程序性能与响应性。了解C#中的Thread、Task类及Async/Await关键字,掌握线程同步与安全,实践并发计算、网络服务及UI优化。跟随未来发展趋势,利用C#打造高效应用。
383 3
|
3月前
|
XML 前端开发 C#
C#编程实践:解析HTML文档并执行元素匹配
通过上述步骤,可以在C#中有效地解析HTML文档并执行元素匹配。HtmlAgilityPack提供了一个强大而灵活的工具集,可以处理各种HTML解析任务。
192 19
|
4月前
|
监控 算法 C#
C#与Halcon联合编程实现鼠标控制图像缩放、拖动及ROI绘制
C#与Halcon联合编程实现鼠标控制图像缩放、拖动及ROI绘制
569 0
|
C# 开发者
C# 一分钟浅谈:Code Contracts 与契约编程
【10月更文挑战第26天】本文介绍了 C# 中的 Code Contracts,这是一个强大的工具,用于通过契约编程增强代码的健壮性和可维护性。文章从基本概念入手,详细讲解了前置条件、后置条件和对象不变量的使用方法,并通过具体代码示例进行了说明。同时,文章还探讨了常见的问题和易错点,如忘记启用静态检查、过度依赖契约和性能影响,并提供了相应的解决建议。希望读者能通过本文更好地理解和应用 Code Contracts。
258 3
|
12月前
|
存储 安全 编译器
学懂C#编程:属性(Property)的概念定义及使用详解
通过深入理解和使用C#的属性,可以编写更清晰、简洁和高效的代码,为开发高质量的应用程序奠定基础。
857 12
|
设计模式 C# 图形学
Unity 游戏引擎 C# 编程:一分钟浅谈
本文介绍了在 Unity 游戏开发中使用 C# 的基础知识和常见问题。从 `MonoBehavior` 类的基础用法,到变量和属性的管理,再到空引用异常、资源管理和性能优化等常见问题的解决方法。文章还探讨了单例模式、事件系统和数据持久化等高级话题,旨在帮助开发者避免常见错误,提升游戏开发效率。
489 4
|
安全 程序员 编译器
C#一分钟浅谈:泛型编程基础
在现代软件开发中,泛型编程是一项关键技能,它使开发者能够编写类型安全且可重用的代码。C# 自 2.0 版本起支持泛型编程,本文将从基础概念入手,逐步深入探讨 C# 中的泛型,并通过具体实例帮助理解常见问题及其解决方法。泛型通过类型参数替代具体类型,提高了代码复用性和类型安全性,减少了运行时性能开销。文章详细介绍了如何定义泛型类和方法,并讨论了常见的易错点及解决方案,帮助读者更好地掌握这一技术。
236 11
|
API C#
C# 一分钟浅谈:文件系统编程
在软件开发中,文件系统操作至关重要。本文将带你快速掌握C#中文件系统编程的基础知识,涵盖基本概念、常见问题及解决方法。文章详细介绍了`System.IO`命名空间下的关键类库,并通过示例代码展示了路径处理、异常处理、并发访问等技巧,还提供了异步API和流压缩等高级技巧,帮助你写出更健壮的代码。
166 2
|
SQL 开发框架 安全
并发集合与任务并行库:C#中的高效编程实践
在现代软件开发中,多核处理器普及使多线程编程成为提升性能的关键。然而,传统同步模型在高并发下易引发死锁等问题。为此,.NET Framework引入了任务并行库(TPL)和并发集合,简化并发编程并增强代码可维护性。并发集合允许多线程安全访问,如`ConcurrentQueue<T>`和`ConcurrentDictionary<TKey, TValue>`,有效避免数据不一致。TPL则通过`Task`类实现异步操作,提高开发效率。正确使用这些工具可显著提升程序性能,但也需注意任务取消和异常处理等常见问题。
198 1

热门文章

最新文章