本文讨论格式刷、填充图形绘制、图形坐标系的变换等操作,并将常用操作封装到tCanvas类,绘制操作中会使用更直观的毫米作为单位。
下面的代码,首先来看tCanvas类的基本定义。
using System; using System.Drawing; using System.Drawing.Imaging; using System.Drawing.Drawing2D; namespace cfx.image { public class tCanvas { protected Bitmap myBmp; protected Graphics myG; // 记录画线时的坐标 protected float curX = 0F, curY = 0F; // protected float dpmm; // public tCanvas(float width, float height, float dpi = 300F) { // 每毫米点数 dpmm = dpi / 25.4F; // 初始化图像 myBmp = new Bitmap((int)(width * dpmm), (int)(height * dpmm)); myBmp.SetResolution(dpi, dpi); // myG = Graphics.FromImage(myBmp); // 以毫米为单位 myG.PageUnit = GraphicsUnit.Millimeter; // 高质量 myG.InterpolationMode = InterpolationMode.High; myG.SmoothingMode = SmoothingMode.HighQuality; } // public static tCanvas Create(float width, float height, float dpi = 300F) { return new tCanvas(width, height, dpi); } // 清理 public tCanvas Clear() { myG.Clear(Color.Transparent); return this; } public tCanvas Clear(Color cl) { myG.Clear(cl); return this; } // 保存图片文件 public tCanvas Save(string filename) { myBmp.Save(filename, tImage.GetImageFormat(filename)); return this; } } }
tCanvas类中,首先定义了几个字段,其中,myBmp为位图对象(Bitmap类型),myG为图形绘制对象(Graphics类型);curX和curY用于记录线条绘制时的当前坐标,定义为float类型;dpmm定义为每毫米多少像素的分辨率,定义为float类型。
构造函数和Create()静态方法用于创建tCanvas实例,参数分别是图像的宽度、高度和分辨率,其中宽度和高度使用毫米为单位,分辨默认使用300DPI,即每英寸300点。构造函数中,对myBmp、myG对象进行了初始化,设置了myG对象在绘制时使用毫米为单位,并设置图像在缩放、旋转、抗锯齿等方面使用高质量图像模式。
Clear()方法用于清理画布,不指定背景色时默认使用透明色(Transparent)。方法会返回当前实例。
Save()方法会将myBmp图像保存到图片文件,方法中使用了tImage.GetImageFormat()方法自动判断文件格式。方法同样返回当前实例。
下面讨论格式刷和填充图形的绘制。
下面的代码(cfx/image/tCanvas.cs),封装了常用格式刷对象创建和填充图形绘制方法。
using System; using System.Drawing; using System.Drawing.Imaging; using System.Drawing.Drawing2D; namespace cfx.image { public class tCanvas { // 其它代码 // 创建格式颜色刷对象 public static Brush GetBrush(int red, int green, int blue) { return new SolidBrush(Color.FromArgb(red, green, blue)); } // 根据图片文件创建纹理格式刷 public static Brush GetBrush(string filename) { return new TextureBrush(Image.FromFile(filename)); } // 根据图像创建纹理格式刷 public static Brush GetBrush(Image img) { return new TextureBrush(img); } // 创建线性格式刷 public static Brush GetBrush(int width, int height, Color c1, Color c2, float angle,WrapMode wrapMode = WrapMode.Tile) { return new LinearGradientBrush( new Rectangle(0,0, width, height), c1, c2, angle) { WrapMode = wrapMode }; } // public static Brush GetBrush(int width, int height, Color c1, Color c2, LinearGradientMode mode= LinearGradientMode.Horizontal, WrapMode wrapMode = WrapMode.Tile) { return new LinearGradientBrush( new Rectangle(0,0, width, height), c1, c2, mode) { WrapMode = wrapMode }; } // // 填充矩形 public tCanvas FillRectangle(Brush b, float x, float y, float width, float height) { myG.FillRectangle(b, x, y, width, height); return this; } // 填充椭圆 public tCanvas FillEllipse(Brush b, float x, float y, float width, float height) { myG.FillEllipse(b, x, y, width, height); return this; } // 填充圆形,中心坐标和半径 public tCanvas FillCircle(Brush b, float cx, float cy, float r) { float d = r * 2; return FillEllipse(b, cx - r, cy - r, d, d); } } }
代码中,首先定义了一系列GetBrush()方法,用于创建常用的格式刷对象,包括以下重载版本:
FillRectangle()方法用于绘制填充矩形,参数包括格式刷、矩形左上角x和y坐标、宽度(width)和高度(height)。
FillEllipse()方法用于绘制填充椭圆,其参数为格式刷,以及椭圆外切矩形的左上角x和y坐标、宽度(width)和高度(height)。
FillCircle()方法用于绘制填充圆形,其参数为格式刷,圆点x和y坐标、半径。
下面的代码,在Program.cs测试填充图形的绘制。
using System.Drawing; using cfx.image; namespace csfx_demo { class Program { static void Main(string[] args) { tCanvas.Create(150,100).Clear(Color.White) .FillCircle(Brushes.Blue, 50, 50, 25) .Save(@"d:\99.png"); } } }
代码中会创建150*100毫米的画布对象,分辨率使用默认的300DPI;然后调用Clear()方法将背景色设置为灰色,并在(50,50)毫米处绘制了一个半径为25毫米的蓝色圆形;最后,将绘制的图片保存到d:\99.png文件。绘制效果如下图所示。
下面的代码会创建一个渐变色填充的矩形。
using System.Drawing; using cfx.image; namespace csfx_demo { class Program { static void Main(string[] args) { tCanvas.Create(150, 100).Clear(Color.Gray) .FillRectangle(tCanvas.GetBrush(100, 50, Color.Yellow, Color.Red), 0, 0, 100, 50) .Save(@"d:\99.png"); } } }
代码中使用了默认的水平渐变模式,绘制图形如下。
下面的代码(cfx/image/tCanvas.cs)封装绘制填充多边形和封闭图形的方法。
using System; using System.Drawing; using System.Drawing.Imaging; using System.Drawing.Drawing2D; namespace cfx.image { public class tCanvas { // 其它代码 // 填充多边形 public tCanvas FillPolygon(Brush b, PointF[] pts) { myG.FillPolygon(b, pts); return this; } // 填充封闭图形 public tCanvas FillClosedCurve(Brush b, PointF[] pts, float tension = 0F, FillMode fillMode = FillMode.Alternate) { myG.FillClosedCurve(b, pts, fillMode,tension); return this; } } }
代码中,FillPolygon()方法用于绘制填充多边形,参数一为格式刷,参数二为包含多边形顶点坐标的PointF数组。
FillClosedCurve()方法绘制封闭图形,参数一为格式刷;参数二为顶点坐标;参数三为边的曲度(张力),其值为0时绘制为直线线段,大于0时绘制为曲线;参数四指定填充模式。
下面的代码,在Program.cs文件中测试填充多边形的绘制。
using System.Drawing; using cfx.image; namespace csfx_demo { class Program { static void Main(string[] args) { PointF[] pts = new PointF[] { new PointF(10,10), new PointF(60,60), new PointF(100,30), }; // tCanvas.Create(150, 100).Clear(Color.Gray) .FillPolygon(Brushes.Blue,pts) .Save(@"d:\99.png"); } } }
代码通过三个点绘制了填充三角形,绘制效果如下图所示。
下面的代码绘制了边为曲线的封闭图形。
using System.Drawing; using cfx.image; namespace csfx_demo { class Program { static void Main(string[] args) { PointF[] pts = new PointF[] { new PointF(10,10), new PointF(60,60), new PointF(100,30), }; // tCanvas.Create(150, 100).Clear(Color.Gray) .FillClosedCurve(Brushes.Blue,pts,0.5F) .Save(@"d:\99.png"); } } }
绘制效果如下图所示。
下面的代码,在tImage类中添加绘制填充扇形的方法。
// 填充扇形 public tCanvas FillPie(Brush b, float cx, float cy, float r, float startAngle, float sweepAngle) { float d = r * 2; myG.FillPie(b, cx - r, cy - r, d, d, startAngle, sweepAngle); return this; }
Graphics对象的FillPie()方法中,参数分别是格式刷对象、扇形所在圆(或椭圆)外切矩形左上角X和Y坐标、矩形宽度和高度,开始角度、旋转的角度(即扇形的角度)。这里,tImage.FillPie()方法绘制的是位于圆形的扇形,参数分别是格式刷、圆心坐标、半径、开始角度和旋转角度。下面的代码,在Program.cs文件中测试填充扇形的绘制。
using System.Drawing; using cfx.image; namespace csfx_demo { class Program { static void Main(string[] args) { tCanvas.Create(150, 100).Clear(Color.Gray) .FillPie(Brushes.Blue, 75, 50, 50, 0, 45) .Save(@"d:\99.png"); } } }
代码绘制效果如下图所示。
请注意,绘制扇形时,角度0同样是水平向右的方向,顺时针为正角度。
对于扇形的绘制,还可以扩展一些操作,如下面的代码。
public tCanvas FillPie(Brush b,float x,float y, float width,float height, float startAngle,float sweepAngle) { myG.FillPie(b, x, y, width, height, startAngle, sweepAngle); return this; } // 绘制饼图 public static Brush[] myColorBrush = new Brush[] { Brushes.Orange,Brushes.AliceBlue,Brushes.OrangeRed, Brushes.GreenYellow,Brushes.LightBlue,Brushes.LightGray, Brushes.Brown,Brushes.LightSteelBlue,Brushes.Chocolate }; // public tCanvas FillPie(float x, float y, float width, float height, float[] data) { // 求数据之和 float sum = 0F; for (int i = 0; i < data.Length; i++) sum += data[i]; // 根据数据占比计算旋转角度并绘制扇形 float curAngle = 0F, sweepAngle; Random rnd = new Random(); for (int i = 0; i < data.Length; i++) { sweepAngle = data[i] / sum * 360; Brush b = (i < myColorBrush.Length) ? myColorBrush[i] : GetBrush(rnd.Next(1,255), rnd.Next(1, 255), rnd.Next(1, 255)); myG.FillPie(b, x, y, width, height, curAngle, sweepAngle); curAngle += sweepAngle; } // return this; }
代码中添加了两个FillPie()方法的重载版本,其中,第一个版本参数与Graphics对象的FillPie()方法参数相对应;第二个版本可以通过一组数据绘制饼图,其中使用了myColorBrush静态字段作为默认的颜色格式刷,请注意,绘制扇形时,如果数据索引小于myColorBrush元素数量则使用myColorBrush中定义的颜色格式刷,否则使用随机颜色格式刷,这里,随机颜色中的R、G、B取值在1到254之间,不会出现白色和黑色。工作中,如果需要显示饼图的图例,可以在方法中使用输出参数保存使用过的颜色格式刷列表。
下面的代码,在Program.cs文件中测试饼图的绘制。
using System.Drawing; using cfx.image; namespace csfx_demo { class Program { static void Main(string[] args) { float[] data = { 51, 30, 15, 30, 26 }; tCanvas.Create(150, 100).Clear(Color.Gray) .FillPie(0,0,100,100,data) .Save(@"d:\99.png"); } } }
为了方便操作,还可以在tCanvas类中创建一些常用图形的绘制方法,如下面的代码就是绘制填充正方形的方法。
// 填充正方形 public tCanvas FillSquare(Brush b, float cx, float cy, float side) { float side2 = side / 2; myG.FillRectangle(b, cx - side2, cy - side2, side, side); return this; }
FillSquqre()方法中,参数分别是格式刷、正方形中心的x和y坐标,以及正方形的边长。
接下来,在tImage类中封装一些坐标系变换相关的方法,如下面的代码。
// 旋转变换 public tCanvas RotateTransform(float angle) { myG.RotateTransform(angle); return this; } // 原点平移 public tCanvas TranslateTransform(float dx, float dy) { myG.TranslateTransform(dx, dy); return this; } // 缩放变换 public tCanvas ScaleTransform(float scale) { myG.ScaleTransform(scale, scale); return this; } // 重置变换 public tCanvas ResetTransform() { myG.ResetTransform(); return this; }
代码中定义的方法包括:
绘制旋转45度的正方形可以使用如下代码。
using System.Drawing; using cfx.image; namespace csfx_demo { class Program { static void Main(string[] args) { tCanvas.Create(150, 100).Clear(Color.Gray) .TranslateTransform(75, 50) .RotateTransform(45) .FillSquare(Brushes.Red, 0, 0, 60) .ResetTransform() .Save(@"d:\99.png"); } } }
绘制结果如下图所示。
代码中,首先创建了150*100毫米的画布,并设置背景色为灰色。然后,通过TranslateTransform()方法将原点(0,0)位置向右移动75毫米,向下移动50毫米;接下来通过RotateTransform()方法旋转45度,并绘制正方形,此时原点在图形的中间位置,旋转也会以图形中心为准进行。最后,恢复变换前的状态,并保存图片文件。