一关于GDI+
从本质上来看GDI+为开发者提供了一组实现与各种设备(例如监视器打印机及其它具有图形化能力但不及涉及这些图形细节的设备)进行交互的库函数GDI+的本质在于它能够替代开发人员实现与例如显示器及其它外设的交互而从开发者角度来看要实现与这些设备的直接交互却是一项艰巨的任务
下图展示了GDI+在开发人员与上述设备之间起着重要的中介作用其中GDI+为我们包办了几乎一切—从把一个简单的字符串HelloWorld打印到控制台到绘制直线矩形甚至是打印一个完整的表单等
)thisstylewidth=; border=>
图GDI+担当着重要的中介作用
那么GDI+是如何工作的呢?为了弄清这个问题让我们来分析一个示例—绘制一条线段实质上一条线段就是一个从一个开始位置(XY)到一个结束位置(XnYn)的一系列像素点的集合为了画出这样的一条线段设备(在本例中指显示器)需要知道相应的设备坐标或物理坐标
然而开发人员不是直接告诉该设备而是调用GDI+的drawLine()方法然后由GDI+在内存(即视频内存)中绘制一条从点A到点B的直线GDI+读取点A和点B的位置然后把它们转换成一个像素序列并且指令监视器显示该像素序列简言之GDI+把设备独立的调用转换成了一个设备可理解的形式或者实现相反方向的转换
至此我们已经简单了解了GDI+的工作机理现在让我们开始探讨如何实现一些基本的图像操作
二图像操作—缩略图缩放与保存
在本文示例中我们将实现如下的任务
创建缩略图
缩放一个加载的图像
保存一个操作中的图像
a) 创建缩略图
缩略图是图像的浓缩版本典型情况下一幅缩略图图像的尺寸为×像素在GDI+中一个图像的缩略图可以通过使用Image类的GetThumbnailImage()方法来创建其函数原型如下
borderColorDark=#ffffff cellPadding= width= align=center borderColorLight=black border=>ee>
public Image GetThumbnailImage (
int thumbWidth
int thumbHeight
GetThumbnailImageAbort callback
IntPtr callbackData
)
第一个参数相应于缩略图的宽度第二个参数相应于生成的缩略图的高度第三个参数是一个ImageGetThumbnailImageAbort委托在 GDI+ 版中不使用此委托即便如此也必须创建一个委托并在该参数中传递对此委托的引用第四个参数同样没有使用但是也需要提供以实现兼容性注意第四个参数必须为IntPtrZero
如果前两个参数(也就是宽度和高度)都为的话那么GDI+返回一个嵌入式缩略图否则使用系统定义尺寸创建该缩略图例如如果img是一个图像类的实例并且使用的宽度和高度都是系统定义的创建一个缩略图的语句应该如下所示
borderColorDark=#ffffff cellPadding= width= align=center borderColorLight=black border=>ee>
Image thumbNailImage = imgGetThumbnailImage(tnCallBackIntPtrZero);
在此thumbNailImage包含返回的缩略图而tnCallback是一个相应于ImageGetThumbnailImageAbort的函数其定义如下
borderColorDark=#ffffff cellPadding= width= align=center borderColorLight=black border=>ee>
//必须调用它但是没有使用
style=fontsize:pt;fontfamily:Verdana>publicbool tnCallbackMethod()
{
return false;
}
b) 缩放一个加载的图像
缩放是放大或缩小一个图像的过程—通过在图像尺寸上乘以缩放因子实现其中缩放因子=期望的图像尺寸/当前图像尺寸例如要把一个图像放大%则当前尺寸必须乘以%(%=/=)为了缩小一个图像到%则当前尺寸必须乘以%或(/=倍)
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系统的
假定你想使用名字checkergif保存一个图像那么相应的实现语句将是
borderColorDark=#ffffff cellPadding= width= align=center borderColorLight=black border=>ee>
curImageSave(checkergifImageFormatGif);
这里curImage对应于Image类的实例
在下一节中我将对前面开发的这个应用程序进行扩展
三实际开发中的图像操作
下面我们来讨论实际中的使用情况我将在本文示例应用程序中添加下列功能
以用户指定的格式保存图像
根据从菜单下选择的百分比放大图像
创建一个加载图像的略缩图
相应的菜单操作如下所示
mnuSave—文件菜单下保存图像的子菜单
mnuZoom—放大图像%
mnuThumbNail—创建图像的一个略缩图
下面是处理菜单项mnuSave的Click事件相应的方法
borderColorDark=#ffffff cellPadding= width= align=center borderColorLight=black border=>ee>
private void mnuSave_Click(object senderSystemEventArgs e)
{
//如果图像已经创建
if(curImage == null)
return;
//调用SaveFileDialog对话框
SaveFileDialog saveDlg = new SaveFileDialog();
saveDlgTitle = Save Image As;
saveDlgOverwritePrompt = true;
saveDlgCheckPathExists = true;
saveDlgFilter =
Bitmap File(*bmp)|*bmp| +
Gif File(*gif)|*gif| +
JPEG File(*jpg)|*jpg| +
PNG File(*png)|*png ;
saveDlgShowHelp = true;
//如果选择则进行保存
if(saveDlgShowDialog() == DialogResultOK)
{
//得到用户选择的文件名
string fileName = saveDlgFileName;
//得到文件扩展名
string strFilExtn =fileNameRemove(fileNameLength );
//保存文件
switch(strFilExtn)
{
case bmp:
curImageSave(fileName ImageFormatBmp);
break;
case jpg:
curImageSave(fileName ImageFormatJpeg);
break;
case gif:
curImageSave(fileName ImageFormatGif);
break;
case tif:
curImageSave(fileName ImageFormatTiff);
break;
case png:
curImageSave(fileName ImageFormatPng);
break;
default:
break;
}
}
}
首先以可接收的扩展名显示这个保存对话框然后由从该对话框返回的文件名检索相应的扩展名最后根据该扩展名使用相应的图像格式参数调用Save()方法
接下来我们分析菜单项mnuZoom相应的处理器首先让我们在应用程序级添加下列以粗体显示的一行
borderColorDark=#ffffff cellPadding= width= align=center borderColorLight=black border=>ee>
private double curZoom=;
private Image curImage=null;//用于存储当前图像
private int i = ;//用于把屏幕重画操作与缩略图绘制部分区别开来
然后必须对mnuLoad处理代码作少许调整如下所示
private void mnuLoad_Click(object senderSystemEventArgs e)
{
//创建OpenFileDialog
OpenFileDialog opnDlg = new OpenFileDialog();
//设置一个图像类型过滤器
opnDlgFilter =
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;
opnDlgTitle = 打开图像文件;
opnDlgShowHelp = true;
//如果OK选择它
if(opnDlgShowDialog() == DialogResultOK)
{
//读取当前选择的文件名
curFileName = opnDlgFileName;
//使用ImageFromFile创建图像对象
try
{
curImage = ImageFromFile(curFileName);
}
catch(Exception exp)
{
MessageBoxShow(expMessage);
}
}
//改变AutoScrollMinSize属性
thisAutoScrollMinSize = new Size
((int)(curImageWidth * curZoom)
(int)(curImageHeight * curZoom));
i++;
//重新绘制表单
Invalidate();
}
注意在此新添加的代码分别在原来的图像宽度和高度上乘以放大因子以生成一个放大的图像然后必须相应地修改paint事件的处理器如下所示
borderColorDark=#ffffff cellPadding= width= align=center borderColorLight=black border=>ee>
private void Form_Paint(object sender PaintEventArgs e)
{
if (curImage != null && i==)
{
Graphics g = thisCreateGraphics();
gDrawImage(curImage new Rectangle
(AutoScrollPositionX
AutoScrollPositionY
(int)(thisClientRectangleWidth * curZoom)
(int)(ClientRectangleHeight * curZoom)));
}
}
该图像应该有根据放大因子的相应的高度和宽度下面我们来看一下mnuZoom菜单项相应的事件处理器
borderColorDark=#ffffff cellPadding= width= align=center borderColorLight=black border=>ee>
private void mnu_Click(object senderSystemEventArgs e)
{
if(curImage != null)
{
curZoom = (double)/;
i++;
Invalidate();
}
}
最后我们来看一下mnuThumbNail菜单项相应的事件处理器
borderColorDark=#ffffff cellPadding= width= align=center borderColorLight=black border=>ee>
private void mnuThumbnail_Click(object sender EventArgs e)
{
if(curImage != null)
{
i++;
//回调
ImageGetThumbnailImageAbort tnCallBack =
new ImageGetThumbnailImageAbort(tnCallbackMethod);
//得到缩略图图像
Image thumbNailImage = curImageGetThumbnailImage
( tnCallBack IntPtrZero);
//创建一个Graphics对象
Graphics tmpg = thisCreateGraphics();
tmpgClear(thisBackColor);
//画缩略图图像
tmpgDrawImage(thumbNailImage thumbNailImageWidth thumbNailImageHeight);
//释放掉Graphics对象
tmpgDispose();
}
}
在此我们首先创建一个GetThumbnailImageAbort类型的变量并且赋给它值tnCallbackMethod()—这是通过传递给该方法GetThumbnailImageAbort实现的然后它创建一个新的Image类的实例以存储GetThumbnailImage方法返回的图像—此后这个方法将用于把缩略图绘制到屏幕上
四小结
在本文中我仅讨论了NET C#环境下关于GDI+编程的一些基本的实用操作片断在以后的文章中我们将逐渐展开对NET GDI+编程高级特征的探讨
【注】 ①本文源码在Windows XP Professonal+VS环境下调试通过
②本示例中私有变量的i的引入仅为了把屏幕重绘与缩略图绘制区别开来读者可考虑其它更巧妙的办法
③图像重绘及滚动过程中出现屏幕抖动现象读者可结合有关图像绘制双缓沖技术予以改进