最近做一个微信公众号服务,有一些简单的图片处理功能。主要就是用户在页面操作,前端做一些立刻显示的效果,然后提交保存时后端真正修改原图。

我们的后端是 ASP.NET,也就是 C# 语言了,C# 本身处理图片还是比较方便的,使用 GDI+ 就好,只需要添加 System.Drawing 引用,不需要任何第三方库。于是最近也用到一些比较常用的 GDI+ 图片处理方法,就整理一下做个记录了。

这个题目大概会写几篇文章,第一篇先简单介绍一下 GDI+ 的常用对象,以及一些使用时候的注意事项,后面会挑一些项目中做过的比较有用的处理过程来介绍一下。

废话不多说,开始进入正题。


需要用到的类

使用 GDI+ 画图会用到的几个常用的类有:GraphicsBitmapImage

其中 Graphics 是画板。这个类包含了许多画图的方法,包括画图片(DrawImage),画线(DrawLine),画圆(DrawEllipse、FillEllipse),写字(DrawString)等等。简单说使用这个类可以完成我们需要的大部分工作。

生成一个 Graphics 对象需要用到 Image 或者 Bitmap

PS: Winform 下可以直接从窗体或控件的事件中引用 Graphics 对象。

比如:

    private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics; // 创建画板,这里的画板是由Form提供的.
}

不过本文讨论的是其他场景,比如 ASP.NET MVC,或单纯的控制台程序。这些时候是没有控件的,所以要用其他方法。

我一般用以下方法:

//
// 摘要:
// 从指定的 System.Drawing.Image 创建新的 System.Drawing.Graphics。
//
// 参数:
// image:
// 从中创建新 System.Drawing.Graphics 的 System.Drawing.Image。
//
// 返回结果:
// 此方法为指定的 System.Drawing.Image 返回一个新的 System.Drawing.Graphics。
//
// 异常:
// T:System.ArgumentNullException:
// image 为 null。
//
// T:System.Exception:
// image 具有索引像素格式,或者格式未定义。
public static Graphics FromImage(Image image);

其中的参数可以传入 ImageBitmap,因为 Bitmap 是继承自 Image 的。


如何创建画板

  • 如果是要对原图进行处理,比如旋转图片,添加文字等,可以直接通过原图片获得画板对象。
Image img = Image.FromFile(imgPath);
Graphics graphics = Graphics.FromImage(img);
  • 如果是要画一个新的图,可以通过要保存的图片宽、高生成画板。
Bitmap bmp = new Bitmap(width, height);
Graphics graph = Graphics.FromImage(bmp);

PS: Graphics 本身是没有提供构造函数来直接生成的。所以我们可以先创建一个需要保存图片大小的 Bitmap 位图对象,然后再获得画板对象。


如何保存画好的图片

通过调用 img.Save(savePath) 或者 bmp.Save(savePath) 即可保存对象。

PS: BitmapSave 方法是直接继承自 Image 的。


GDI+ 的坐标系

GDI+ 的坐标系是个二维坐标系,不过又有点不一样,它的原点是在左上角的。如下图:


使用 GDI+ 的一些注意事项

这里我忍不住要先吐槽一下,GDI+ 的报错信息不太友好啊。经常只是返回一个“GDI+ 中发生一般性错误。”,不能快速地根据这个错误提示定位问题。比如说没有释放图片资源时想再次访问资源会报这个错误,想要保存图片的文件夹不存在时也是提示这个错误。看不出来区别……

1. 保存到相同路径的文件时要先释放图片资源,否则会报错(GDI+中发生一般性错误)

Image img = Image.FromFile(imgPath);
Bitmap bmp = new Bitmap(img);
Graphics graphics = Graphics.FromImage(bmp);
... // 对图片进行一些处理
img.Dispose(); // 释放原图资源
bmp.Save(imgPath); // 保存到原图
graphics.Dispose(); // 图片处理过程完成,剩余资源全部释放
bmp.Dispose();

2. 使用完的资源记得要释放。可以用 try..catch..finally 或者 using 的方式,这样即使遇到代码运行报错也能及时释放资源,更加保险。

  • try..catch...finally:把释放资源的代码写到 finally 代码段里。
    Image img = Image.FromFile(imgPath);
Bitmap bmp = new Bitmap(img);
Graphics graphics = Graphics.FromImage(bmp); try
{
...
}
catch (System.Exception ex)
{
throw ex;
}
finally
{
graphics.Dispose();
bmp.Dispose();
img.Dispose();
}
  • using:使用 using 语句创建的资源会在离开 using 代码段时自动释放该资源。
    /// <summary>
/// 缩放图像
/// </summary>
/// <param name="originalImagePath">原图路径</param>
/// <param name="destWidth">目标图宽度</param>
/// <param name="destHeight">目标图高度</param>
/// <returns></returns>
public Bitmap GetThumbnail(string originalImagePath, int destWidth, int destHeight)
{
using (Image imgSource = Image.FromFile(originalImagePath))
{
return GetThumbnail(imgSource, destWidth, destHeight);
}
}

3. 要保存图片的文件夹一定要是已经存在的,否则会报错(GDI+中发生一般性错误)

eg:假设图片要保存到 D:\test\output.png

    string directory = @"D:\test\";
string fileName = "output.png"; // 检查文件夹是否存在,不存在则先创建
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
} bmp.Save(directory + fileName);

系列其他文章:

C# 使用 GDI+ 给图片添加文字,并使文字自适应矩形区域

C# 使用 GDI+ 实现添加中心旋转(任意角度)的文字

C# 使用 GDI+ 画图的更多相关文章

  1. C#-gdi画图,双缓冲画图,Paint事件的触发---ShinePans

    在使用gdi技术画图时,有时会发现图形线条不够流畅,或者在改变窗口大小时会闪烁不断的现象.(Use DoubleBuffer to solve it!)                         ...

  2. c# GDI画图 双缓冲画图分析

    双缓冲绘图分析  1.Windows 绘图原理  我们在 Windows 环境下看到各种元素,如菜单.按钮.窗口.图像,从根本上说,都是“画”出来的.这时的屏幕,就相当于一块黑板,而 Windows ...

  3. GDI+画图类Graphics的使用

    一:基础定义 #region 定义线尾.线头为箭头.字体和笔刷 Pen p = );//定义画笔 蓝色,宽度为1(坐标显示颜色) p.EndCap = LineCap.ArrowAnchor;//定义 ...

  4. GDI画图,判断鼠标点击点在某一画好的多边形、矩形、图形里

    Region.IsVisible方法 简单方便准确 private bool CheckPntInPoly(Point[] points, Point pnt) { || pnt == Point.E ...

  5. C#----GDI+画图的一些注意和细节

    画线: 在矩形rect(0,0,20,20)中的位置Point(0,10),Point(20,10)画线,也就是在矩形的中间画线,线的宽度是20的话,会发现正好线会把矩形占满,说明画线不是向下或者向上 ...

  6. 提升GDI画图的效率

    假设我们要画一个坐标图,里面可能还需要画网络线.XY各个单位的值.曲线或直线等,可能的函数代码如下: void OnPaint () { CPaintDC dc (this); DrawXY (&am ...

  7. MFC更换画笔(画刷)颜色以及画眼睛(GDI画图)

    MFC画眼睛 换画笔(画刷)颜色(参考链接:https://blog.csdn.net/sunxiving/article/details/51272001) 由于画笔一旦创建后就无法修改.所以要修改 ...

  8. WinForm之GDI画图步骤

    Graphics g = this.CreateGraphics(); //这句是创建画布g,根据窗体得到窗体的画布 Pen p = new Pen(Color.Red, 2); //这句是创建一个红 ...

  9. 【Windows编程】系列第五篇:GDI图形绘制

    上两篇我们学习了文本字符输出以及Unicode编写程序,知道如何用常见Win32输出文本字符串,这一篇我们来学习Windows编程中另一个非常重要的部分GDI图形绘图.Windows的GDI函数包含数 ...

随机推荐

  1. python数据类型——列表和元组类型

    列表类型(list) 定义一个列表类型很简单: l = ['a','b','c','d','e','f'] 变量l即为列表类型,可以用type方法查看: print(type(l)) 列表的增删改查 ...

  2. PHP 秒数 转时分秒 函数

    function secondsToHour($seconds){ if(intval($seconds) < 60) $tt ="00时00分".sprintf(" ...

  3. [转] SDP协议

    [转] SDP协议 http://blog.csdn.net/dxpqxb/article/details/18706471 1.SDP协议概述 SDP(Session Description Pro ...

  4. Hadoop的Archive归档命令使用指南

    hadoop不适合小文件的存储,小文件本省就占用了很多的metadata,就会造成namenode越来越大.Hadoop Archives的出现视为了缓解大量小文件消耗namenode内存的问题. 采 ...

  5. 【Unity3D与23种设计模式】策略模式(Strategy)

    GoF中定义: "定义一组算法,并封装每个算法,让它们之间可以彼此交换使用. 策略模式让这些算法在客户端使用它们时能更加独立." 游戏开发过程中 不同的角色会有不同的属性计算方法 ...

  6. 温故而知新-set

    set:同map一样,关联式容器.在插入时就会进行排序,主要特点如下: 1.记录元素即是key值又是value值 2.插入的时候严格排序,底层是红黑树 3.删除元素时只要操作指针节点,无需进行内存的拷 ...

  7. modal verbs(一)

    什么是modal verb?翻译成中文就是情态动词. modal的意思是模式的,情态的,形式的.Bootstrap中的模态框就是这个词modal. 情态动词翻译挺准确的,就是表达说话人的情绪,态度或语 ...

  8. 笔记:I/O流-对象序列化

    Java 语言支持一种称为对象序列化(Object Serialization)的非常通用的机制,可以将任何对象写入到流中,并在之后将其读回,首先需要支持对象序列化的类,必须继承与 Serializa ...

  9. 避免uncaughtException错误引起node.js进程崩溃

    uncaughtException 未捕获的异常, 当node.js 遇到这个错误,整个进程直接崩溃. 或许这俩个人上辈子一定是一对冤家. 或许这俩个人经历了前世500次的回眸才换来了今生的相遇,只可 ...

  10. 自动化运维工具---expec

    作为运维经常操作Linux服务器是不可避免的事情的,那么你们都是怎么管理的呢? 我们管理的方式较为复杂了,我说一下: 有一套服务器资产管理系统,所有服务器都记录在上面,包括用户名密码,内外网地址都会有 ...