本文讨论了C#代码中图形图像处理的基本操作,包括Image、Bitmap、Graphics、ImageFormat等类型的应用,以及图片格式转换、缩放、旋转和翻转的基本操作。
本文使用的图形图像处理资源定义在System.Drawing命名空间及其子命名空间中,代码中注意引用。其中,Image为抽象类,可以使用一些静态方法操作;Bitmap类可以按像素处理图像,绘制时需要关联Gruphics对象操作,如绘制像素、线条、图形、填充图形、图像等。
处理图像时还需要注意分辨率,使用DPI,即每英寸的点数(像素数),在位图中包含水平方向和垂直方向的分辨率。稍后讨论相关应用。
绘制图形时还需要关注的类型包括:
下面的代码会创建一个200*200像素的正方形图像,并由四个不同颜色的小正方形组成。
using System.Drawing; using System.Drawing.Imaging; namespace csfx_demo { class Program { static void Main(string[] args) { using (Bitmap bmp = new Bitmap(200, 200)) { using (Graphics g = Graphics.FromImage(bmp)) { g.FillRectangle(Brushes.GhostWhite, 0, 0, 100, 100); g.FillRectangle(Brushes.Red, 100, 0, 100, 100); g.FillRectangle(Brushes.Blue, 0, 100, 100, 100); g.FillRectangle(Brushes.Black, 100, 100, 100, 100); bmp.Save(@"d:\block.bmp", ImageFormat.Bmp); } } } } }
代码绘制的图案如下图所示。
代码中,首先定义了200*200像素的位图对象(Bitmap类型),Bitmap()构造函数的参数分别是图像的宽度和高度,单位为像素;然后,使用Graphics.FromImage()方法创建了基于bmp对象的图形对象(Graphics类型);接下来,使用Graphics对象的FillRectangle()方法绘制了四个颜色填充的矩形,方法的参数分别是格式刷对象、左上角水平坐标、左上角垂直坐标、宽度、高度,坐标和尺寸单位均为像素。
Graphics对象完成图像绘制后,使用Bitmap对象的Save()方法将图像保存到指定的文件,其中,参数一指定文件的路径,参数二指定保存的图像格式,这里使用位图格式。ImageFormat类定义在System.Drawing.Imaging命名空间,其中,使用一系列静态属性定义了所支持的图像格式。
此外,在绘制图像的坐标系中,左上角为原点(0,0),如下图所示。
接下来的常用操作会封装到tImage和tCanvas类,它们都定义在cfx.image命名空间。其中,tImage类定义为静态类,定义了图像和图片文件的常用操作;tCanvas类则用于图像的绘制。
ImageFormat类中,静态属性指定图片格式,处理图片文件时,可以根据扩展名判断格式;下面的代码(cfx/image/tImage.cs),在tImage类中定义GetImageFormat()方法,其功能是根据文件的扩展名返回图像格式。
using System; using System.IO; using System.Drawing; using System.Drawing.Imaging; namespace cfx.image { public static class tImage { // 根据文件扩展名返回图片格式 public static ImageFormat GetImageFormat(string filename) { string ext = Path.GetExtension(filename).ToLower(); if (ext == ".jpeg" || ext == ".jpg") return ImageFormat.Jpeg; else if (ext == ".png") return ImageFormat.Png; else if (ext == ".gif") return ImageFormat.Gif; else if (ext == ".ico" || ext == ".icon") return ImageFormat.Icon; else return ImageFormat.Bmp; } } }
代码中只处理了JPEG、PNG、GIF、Icon和BMP五种格式,如果需要也可以添加更多的支持代码,如Emf、Exif、Tiff、Wmf格式。
下面的代码(cfx/image/tImage.cs)创建了转换图片文件格式的操作方法。
using System; using System.IO; using System.Drawing; using System.Drawing.Imaging; namespace cfx.image { public static class tImage { // 其它代码 // 将图片文件转换格式 public static bool Convert(string source, string target) { try { using (Image img = Image.FromFile(source)) { img.Save(target, GetImageFormat(target)); return true; } } catch { return false; } } // 将图像对象转换格式 public static bool Convert(Image img, string target) { try { img.Save(target, GetImageFormat(target)); return true; } catch { return false; } } // public static bool Convert(Stream s, string target) { try { Image img = Image.FromStream(s); img.Save(target, GetImageFormat(target)); return true; } catch { return false; } } } }
在tImage类中定义了三个重载版本的Convert()方法,可以将图片文件、Image对象、Stream对象另存为指定的图片格式文件;代码中用了Image对象的Save()方法,其中,参数一为保存的图片文件路径,参数二为保存的图片格式,这里,使用GetImageFormat()方法自动识别文件格式。图片保存成功时,Convert()方法会返回true,否则返回false。
下面的代码,在Program.cs文件中测试tImage.Convert()方法的应用。
using System; using cfx.image; namespace csfx_demo { class Program { static void Main(string[] args) { bool result = tImage.Convert(@"d:\block.bmp", @"d:\1.jpg"); Console.WriteLine(result); } } }
代码会将d:\block.bmp文件转换为JPEG格式,并保存到d:\1.jpg文件;成功转换后会显示True,此时可以在d:盘下看到1.jpg文件,其尺寸要比block.bmp文件小很多。
转换图片文件格式的同时,也可以对图片尺寸进行缩放,如下面的代码,会在tImage类中添加一系列重载版本的Resize()方法。
using System; using System.IO; using System.Drawing; using System.Drawing.Imaging; namespace cfx.image { public static class tImage { // 其它代码 // public static bool Resize(string source, string target, int width, int height) { try { using (Image img = Image.FromFile(source)) { using (Bitmap newImg = new Bitmap(img, width, height)) { newImg.Save(target, GetImageFormat(target)); return true; } } } catch { return false; } } // public static bool Resize(Image img, string target, int width, int height) { try { using (Bitmap newImg = new Bitmap(img, width, height)) { newImg.Save(target, GetImageFormat(target)); return true; } } catch { return false; } } // public static bool Resize(Stream s, string target, int width, int height) { try { using (Image img = Image.FromStream(s)) { using (Bitmap newImg = new Bitmap(img, width, height)) { newImg.Save(target, GetImageFormat(target)); return true; } } } catch { return false; } } // 图片缩放 public static bool Resize(string source, string target, float scale) { try { using (Image img = Image.FromFile(source)) { using (Bitmap newImg = new Bitmap(img, (int)(img.Width * scale), (int)(img.Height * scale))) { newImg.Save(target, GetImageFormat(target)); return true; } } } catch { return false; } } // public static bool Resize(Image img, string target, float scale) { try { using (Bitmap newImg = new Bitmap(img, (int)(img.Width * scale), (int)(img.Height * scale))) { newImg.Save(target, GetImageFormat(target)); return true; } } catch { return false; } } // public static bool Resize(Stream s, string target, float scale) { try { using (Image img = Image.FromStream(s)) { using (Bitmap newImg = new Bitmap(img, (int)(img.Width * scale), (int)(img.Height * scale))) { newImg.Save(target, GetImageFormat(target)); return true; } } } catch { return false; } } } }
代码虽不少,但并不难理解,方法的参数中,图像源分别使用了图片文件路径、Image对象和Stream对象;保存目标(target参数)均为图片文件路径;缩放的方法有两种,一是指定宽度和高度尺寸(width和height参数),另一种是按比例缩放(scale参数)。
下面的代码,在Program.cs文件中测试图片的缩放操作。
using cfx.image; namespace csfx_demo { class Program { static void Main(string[] args) { tImage.Resize(@"d:\block.bmp", @"d:\2.jpg", 0.5F); tImage.Resize(@"d:\block.bmp", @"d:\3.jpg", 2F); } } }
代码将d:\block.bmp文件缩放为0.5倍和2倍,分别保存在d:盘下的2.jpg和3.jpg文件。可以通过文件属性中的“详细信息”页查看文件的尺寸分别是100*100像素和400*400像素。
工作中,在转换图片尺寸时还可以指定宽度,然后根据比例计算出高度;或者指定高度,然后根据比例计算出宽度;如下面的代码(cfx/image/tImage.cs)。
using System; using System.IO; using System.Drawing; using System.Drawing.Imaging; namespace cfx.image { public static class tImage { // 其它代码 // 设置宽度,高度自动按比例设置 public static bool ResizeByWidth(string source, string target, int width) { try { using (Image img = Image.FromFile(source)) { // 计算比例 float scale = (float)img.Width / width; // using (Bitmap newImg = new Bitmap(img, width, (int)(img.Height / scale))) { newImg.Save(target, GetImageFormat(target)); return true; } } } catch { return false; } } // 设置宽度,高度自动按比例设置 public static bool ResizeByWidth(Image img, string target, int width) { try { // 计算比例 float scale = (float)img.Width / width; // using (Bitmap newImg = new Bitmap(img, width, (int)(img.Height / scale))) { newImg.Save(target, GetImageFormat(target)); return true; } } catch { return false; } } // // 设置宽度,高度自动按比例设置 public static bool ResizeByWidth(Stream s, string target, int width) { try { using (Image img = Image.FromStream(s)) { // 计算比例 float scale = (float)img.Width / width; // using (Bitmap newImg = new Bitmap(img, width, (int)(img.Height / scale))) { newImg.Save(target, GetImageFormat(target)); return true; } } } catch { return false; } } // 设置高度,宽度自动按比例设置 public static bool ResizeByHeight(string source, string target, int height) { try { using (Image img = Image.FromFile(source)) { // 计算比例 float scale = (float)img.Height / height; // using (Bitmap newImg = new Bitmap(img, (int)(img.Width / scale), height)) { newImg.Save(target, GetImageFormat(target)); return true; } } } catch { return false; } } // public static bool ResizeByHeight(Image img, string target, int height) { try { // 计算比例 float scale = (float)img.Height / height; // using (Bitmap newImg = new Bitmap(img, (int)(img.Width / scale), height)) { newImg.Save(target, GetImageFormat(target)); return true; } } catch { return false; } } // public static bool ResizeByHeight(Stream s, string target, int height) { try { using (Image img = Image.FromStream(s)) { // 计算比例 float scale = (float)img.Height / height; // using (Bitmap newImg = new Bitmap(img, (int)(img.Width / scale), height)) { newImg.Save(target, GetImageFormat(target)); return true; } } } catch { return false; } } // } }
代码中,ResizeByWidth()和ResizeByHeight()方法分别按指定的宽度和高度缩放图片,并保持宽度和高度的原始比例。
图片按角度旋转(rotate)和翻转(flip)操作中,如果旋转90度、180度、270度,可以直接使用Bitmap对象的RotateFlip()方法,同时可以执行水平翻转和垂直翻转操作。RotateFlip()方法的参数只有一个,定义为RotateFlipType枚举类型,用于指定旋转和翻转的方式。下面的代码演示了RotateFlip()方法的基本应用。
using System.Drawing; using System.Drawing.Imaging; namespace csfx_demo { class Program { static void Main(string[] args) { using(Bitmap bmp = new Bitmap(@"d:\block.bmp")) { bmp.RotateFlip(RotateFlipType.Rotate90FlipNone); bmp.Save(@"d:\4.jpg", ImageFormat.Jpeg); } } } }
代码会将d:\block.bmp图片旋转90度,并保存到d:\4.jpg文件中,效果如下图所示。
下面的代码会对图片进行水平翻转操作。
using System.Drawing; using System.Drawing.Imaging; namespace csfx_demo { class Program { static void Main(string[] args) { using(Bitmap bmp = new Bitmap(@"d:\block.bmp")) { bmp.RotateFlip(RotateFlipType.RotateNoneFlipX); bmp.Save(@"d:\5.jpg", ImageFormat.Jpeg); } } } }
代码会将d:\block.bmp图片水平翻转,并保存到d:\5.jpg文件中,效果如下图所示。
RotateFlipType枚举值有很多,前半部分使用Rotate定义了旋转的角度,不执行旋转操作时使用RotateNone,而Rotate90、Rotate180、Rotate270分别是顺时针旋转90度、180度和270度;枚举值的后半部分使用Flip定义了翻转方式,不执行翻转操作时使用FlipNone,水平翻转使用FlipX,垂直翻转使用FlipY,同时执行水平翻转和垂直翻转则使用FlipXY。实际应用中,可以使用Rotate和Flip组合的枚举值指定旋转和翻转操作方式。