一、图形绘制基础概念
1.1 图形绘制技术栈对比
技术平台 | 命名空间 | 主要特点 | 适用场景 |
---|---|---|---|
GDI+ | System.Drawing | 基于CPU渲染,简单易用,功能有限 | WinForms应用简单图形需求 |
WPF绘图 | System.Windows.Media | 基于DirectX硬件加速,矢量图形,支持分辨率无关渲染 | 现代化WPF应用复杂UI |
SkiaSharp | SkiaSharp | 跨平台2D图形库,高性能,支持多种后端 | 跨平台移动和桌面应用 |
ImageSharp | SixLabors.ImageSharp | 纯托管图像处理库,不依赖系统图形接口 | 服务器端图像处理 |
Direct2D | SharpDX / Vortice.Direct2D | 底层DirectX接口,极致性能 | 高性能图形应用和游戏开发 |
1.2 核心绘图对象模型
WPF绘图核心类:
DrawingContext
:绘图指令的轻量级上下文Visual
:可视元素的基类,支持可视化树DrawingVisual
:轻量绘图可视化对象Geometry
:定义形状的几何图形(PathGeometry等)Brush
:填充区域的画刷(SolidColorBrush等)Pen
:绘制轮廓的画笔
GDI+核心类:
Graphics
:绘图表面Bitmap
:位图对象Pen
:绘制线条和轮廓Brush
:填充图形内部Font
:文本绘制
二、WPF图形绘制实战
2.1 基本形状绘制
protected override void OnRender(DrawingContext dc)
{
base.OnRender(dc);
// 绘制矩形
dc.DrawRectangle(
Brushes.LightBlue,
new Pen(Brushes.DarkBlue, 2),
new Rect(10, 10, 100, 60));
// 绘制椭圆
dc.DrawEllipse(
Brushes.Yellow,
new Pen(Brushes.Gold, 3),
new Point(200, 50), 50, 30);
// 绘制文本
dc.DrawText(
new FormattedText("Hello WPF",
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
new Typeface("Arial"),
24, Brushes.Black),
new Point(10, 100));
// 绘制自定义路径
PathGeometry path = new PathGeometry();
PathFigure figure = new PathFigure { StartPoint = new Point(50, 150) };
figure.Segments.Add(new LineSegment(new Point(150, 150), true));
figure.Segments.Add(new ArcSegment(
new Point(200, 200),
new Size(50, 50), 0, false, SweepDirection.Clockwise, true));
path.Figures.Add(figure);
dc.DrawGeometry(
Brushes.Transparent,
new Pen(Brushes.Red, 3),
path);
}
2.2 高级图形技术
2.2.1 可视化对象重用
public class CustomVisualHost : FrameworkElement
{
private readonly VisualCollection _visuals;
public CustomVisualHost()
{
_visuals = new VisualCollection(this);
CreateVisuals();
}
private void CreateVisuals()
{
DrawingVisual visual = new DrawingVisual();
using (DrawingContext dc = visual.RenderOpen())
{
dc.DrawRectangle(Brushes.White, null, new Rect(0, 0, ActualWidth, ActualHeight));
Random rand = new Random();
for (int i = 0; i < 100; i++)
{
dc.DrawEllipse(
new SolidColorBrush(Color.FromRgb(
(byte)rand.Next(256),
(byte)rand.Next(256),
(byte)rand.Next(256))),
null,
new Point(rand.Next((int)ActualWidth), rand.Next((int)ActualHeight)),
rand.Next(5, 20), rand.Next(5, 20));
}
}
_visuals.Add(visual);
}
protected override int VisualChildrenCount => _visuals.Count;
protected override Visual GetVisualChild(int index)
{
if (index < 0 || index >= _visuals.Count)
throw new ArgumentOutOfRangeException();
return _visuals[index];
}
}
2.2.2 几何图形组合
// 使用CombinedGeometry创建复杂形状
Geometry CreateStarGeometry(Point center, double outerRadius, double innerRadius, int points)
{
PathGeometry geometry = new PathGeometry();
double angle = Math.PI / points;
double currentAngle = -Math.PI / 2; // 从12点方向开始
PathFigure figure = new PathFigure
{
StartPoint = new Point(
center.X + outerRadius * Math.Cos(currentAngle),
center.Y + outerRadius * Math.Sin(currentAngle)),
IsClosed = true
};
for (int i = 0; i < points * 2; i++)
{
double radius = i % 2 == 0 ? outerRadius : innerRadius;
currentAngle += angle;
figure.Segments.Add(new LineSegment(
new Point(
center.X + radius * Math.Cos(currentAngle),
center.Y + radius * Math.Sin(currentAngle)),
true));
}
geometry.Figures.Add(figure);
return geometry;
}
// 使用示例
var starGeometry = CreateStarGeometry(new Point(100, 100), 80, 40, 5);
dc.DrawGeometry(Brushes.Gold, new Pen(Brushes.DarkGoldenrod, 2), starGeometry);
三、GDI+图形绘制详解
3.1 基本绘图操作
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g = e.Graphics;
// 抗锯齿
g.SmoothingMode = SmoothingMode.AntiAlias;
// 绘制矩形
g.FillRectangle(Brushes.LightGreen, 10, 10, 100, 60);
g.DrawRectangle(Pens.DarkGreen, 10, 10, 100, 60);
// 绘制椭圆
g.FillEllipse(Brushes.LightCoral, 150, 10, 100, 60);
g.DrawEllipse(Pens.DarkRed, 150, 10, 100, 60);
// 绘制文本
g.DrawString("Hello GDI+",
new Font("Arial", 16),
Brushes.Black,
10, 100);
// 绘制多边形
Point[] points = { new Point(50, 150), new Point(150, 150), new Point(200, 200) };
g.FillPolygon(Brushes.LightBlue, points);
g.DrawPolygon(Pens.DarkBlue, points);
// 绘制曲线
Point[] curvePoints = { /* 点集合 */ };
g.DrawCurve(Pens.Purple, curvePoints);
}
3.2 图像处理技术
// 加载和显示图像
Image image = Image.FromFile("sample.jpg");
g.DrawImage(image, new Rectangle(10, 10, 200, 200));
// 图像处理 - 灰度化
Bitmap grayImage = new Bitmap(image.Width, image.Height);
for (int y = 0; y < image.Height; y++)
{
for (int x = 0; x < image.Width; x++)
{
Color pixel = ((Bitmap)image).GetPixel(x, y);
int grayValue = (int)(pixel.R * 0.3 + pixel.G * 0.59 + pixel.B * 0.11);
grayImage.SetPixel(x, y, Color.FromArgb(grayValue, grayValue, grayValue));
}
}
g.DrawImage(grayImage, new Rectangle(220, 10, 200, 200));
// 更高效的图像处理方式(使用LockBits)
Bitmap fastProcessed = ProcessImage((Bitmap)image);
g.DrawImage(fastProcessed, new Rectangle(430, 10, 200, 200));
四、性能优化策略
4.1 WPF绘图优化
- 渲染层级优化:
- 使用
DrawingVisual
替代复杂控件 - 对静态内容应用
BitmapCache
- 合理使用
VisualBrush
- 硬件加速配置:
<!-- 启用硬件加速 -->
<Window ... RenderOptions.ProcessRenderMode="Auto">
- 脏矩形技术:
// 只重绘需要更新的区域
protected override void OnRender(DrawingContext dc)
{
Rect invalidRect = CalculateInvalidRect();
dc.PushClip(new RectangleGeometry(invalidRect));
// 绘制逻辑
dc.Pop();
}
4.2 GDI+性能技巧
- 双缓冲技术:
// 在控件构造函数中
SetStyle(ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint, true);
- 批量操作:
// 使用GraphicsPath批量绘制
GraphicsPath path = new GraphicsPath();
path.AddLines(points);
path.AddEllipse(rect);
g.DrawPath(Pens.Black, path);
- 图像处理优化:
// 使用LockBits进行高效像素操作
BitmapData bmpData = image.LockBits(
new Rectangle(0, 0, image.Width, image.Height),
ImageLockMode.ReadWrite,
image.PixelFormat);
// 直接操作内存
unsafe
{
byte* ptr = (byte*)bmpData.Scan0;
// 像素处理逻辑
}
image.UnlockBits(bmpData);
五、高级图形技术
5.1 粒子系统实现
public class ParticleSystem
{
private readonly List<Particle> _particles = new();
private readonly Random _random = new();
public void Update()
{
// 添加新粒子
if (_random.NextDouble() < 0.3)
{
_particles.Add(new Particle
{
Position = new Point(100, 100),
Velocity = new Vector(
(_random.NextDouble() - 0.5) * 5,
_random.NextDouble() * -10),
Life = 1.0,
Size = _random.Next(3, 8),
Color = Color.FromRgb(
(byte)_random.Next(200, 256),
(byte)_random.Next(100, 200),
(byte)_random.Next(50, 100))
});
}
// 更新现有粒子
for (int i = _particles.Count - 1; i >= 0; i--)
{
var p = _particles[i];
p.Position += p.Velocity;
p.Velocity += new Vector(0, 0.1); // 重力
p.Life -= 0.02;
if (p.Life <= 0)
_particles.RemoveAt(i);
}
}
public void Draw(DrawingContext dc)
{
foreach (var p in _particles)
{
double opacity = p.Life;
var brush = new SolidColorBrush(p.Color) { Opacity = opacity };
dc.DrawEllipse(
brush,
null,
p.Position,
p.Size * opacity,
p.Size * opacity);
}
}
}
public class Particle
{
public Point Position { get; set; }
public Vector Velocity { get; set; }
public double Life { get; set; }
public double Size { get; set; }
public Color Color { get; set; }
}
5.2 实时数据可视化
public class LiveChart : FrameworkElement
{
private readonly List<double> _values = new();
private readonly DispatcherTimer _timer;
public LiveChart()
{
_timer = new DispatcherTimer(TimeSpan.FromMilliseconds(100),
DispatcherPriority.Background,
OnTimerTick,
Dispatcher);
// 模拟数据更新
_timer.Start();
}
private void OnTimerTick(object sender, EventArgs e)
{
// 添加新数据点
_values.Add(Math.Sin(Environment.TickCount / 500.0) * 50 + 50);
// 保持固定数量数据点
if (_values.Count > 100)
_values.RemoveAt(0);
InvalidateVisual(); // 触发重绘
}
protected override void OnRender(DrawingContext dc)
{
base.OnRender(dc);
// 绘制背景
dc.DrawRectangle(Brushes.WhiteSmoke, null, new Rect(RenderSize));
if (_values.Count < 2) return;
// 计算缩放比例
double xScale = ActualWidth / 100.0;
double yScale = ActualHeight / 100.0;
// 创建路径
PathGeometry geometry = new PathGeometry();
PathFigure figure = new PathFigure
{
StartPoint = new Point(0, ActualHeight - _values[0] * yScale)
};
for (int i = 1; i < _values.Count; i++)
{
figure.Segments.Add(new LineSegment(
new Point(i * xScale, ActualHeight - _values[i] * yScale),
true));
}
geometry.Figures.Add(figure);
// 绘制路径
dc.DrawGeometry(null, new Pen(Brushes.Blue, 2), geometry);
// 绘制刻度
for (int i = 0; i <= 100; i += 10)
{
dc.DrawText(
new FormattedText(i.ToString(),
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
new Typeface("Arial"),
10, Brushes.Gray),
new Point(i * xScale, ActualHeight - 15));
}
}
}
六、跨平台图形方案
6.1 使用SkiaSharp绘制
// 在WPF中使用SkiaSharp
public class SkiaControl : FrameworkElement
{
private readonly SKElement _skiaElement;
public SkiaControl()
{
_skiaElement = new SKElement();
_skiaElement.PaintSurface += OnPaintSurface;
AddVisualChild(_skiaElement);
}
protected override int VisualChildrenCount => 1;
protected override Visual GetVisualChild(int index) => _skiaElement;
private void OnPaintSurface(object sender, SKPaintSurfaceEventArgs e)
{
SKSurface surface = e.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear(SKColors.White);
// 绘制渐变背景
using (var paint = new SKPaint())
{
paint.Shader = SKShader.CreateLinearGradient(
new SKPoint(0, 0),
new SKPoint(e.Info.Width, e.Info.Height),
new[] { SKColors.LightBlue, SKColors.Purple },
new[] { 0f, 1f },
SKShaderTileMode.Clamp);
canvas.DrawRect(new SKRect(0, 0, e.Info.Width, e.Info.Height), paint);
}
// 绘制文本
using (var textPaint = new SKPaint())
{
textPaint.Color = SKColors.White;
textPaint.TextSize = 40;
textPaint.IsAntialias = true;
canvas.DrawText("Hello SkiaSharp", 50, 50, textPaint);
}
}
}
6.2 使用ImageSharp处理图像
// 服务器端图像处理
public void ProcessImage(Stream input, Stream output)
{
using (var image = Image.Load(input))
{
// 调整大小
image.Mutate(x => x.Resize(new ResizeOptions
{
Size = new Size(800, 600),
Mode = ResizeMode.Max
}));
// 应用滤镜
image.Mutate(x => x
.GaussianBlur(2f)
.Brightness(1.2f)
.Contrast(1.1f));
// 添加水印
image.Mutate(x => x.DrawText(
new TextGraphicsOptions
{
TextOptions = new TextOptions
{
HorizontalAlignment = HorizontalAlignment.Right,
VerticalAlignment = VerticalAlignment.Bottom
}
},
"Copyright © 2023",
SystemFonts.CreateFont("Arial", 20),
Color.FromRgba(255, 255, 255, 150),
new PointF(image.Width - 10, image.Height - 10)));
// 保存结果
image.SaveAsJpeg(output);
}
}
七、调试与性能分析
7.1 渲染调试技巧
- 可视化树检查:
// 输出可视化树结构
public static void DumpVisualTree(Visual visual, int indent = 0)
{
Debug.WriteLine(new string(' ', indent * 2) + visual.GetType().Name);
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++)
{
var child = VisualTreeHelper.GetChild(visual, i) as Visual;
if (child != null)
DumpVisualTree(child, indent + 1);
}
}
- 帧率监控:
// WPF帧率计数器
public class FpsCounter : FrameworkElement
{
private int _frameCount;
private DateTime _lastUpdate;
private double _currentFps;
public FpsCounter()
{
CompositionTarget.Rendering += OnRendering;
}
private void OnRendering(object sender, EventArgs e)
{
_frameCount++;
var now = DateTime.Now;
if ((now - _lastUpdate).TotalSeconds >= 1)
{
_currentFps = _frameCount / (now - _lastUpdate).TotalSeconds;
_frameCount = 0;
_lastUpdate = now;
InvalidateVisual();
}
}
protected override void OnRender(DrawingContext dc)
{
dc.DrawText(
new FormattedText($"FPS: {_currentFps:0.0}",
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
new Typeface("Arial"),
12, Brushes.Red),
new Point(0, 0));
}
}
7.2 性能分析工具
- WPF性能套件:
- Perforator:分析渲染性能
- Visual Profiler:可视化树性能分析
- ETW Tracing:事件跟踪分析
- Visual Studio诊断工具:
- GPU使用率
- CPU采样
- 内存分析
- 第三方工具:
- RenderDoc:图形API调试
- NVIDIA Nsight:GPU性能分析
结语:图形技术演进趋势
- AI增强渲染:
- 基于神经网络的实时风格迁移
- AI超分辨率图形处理
- WebGPU集成:
- 浏览器高性能图形与C#互操作
- WebAssembly中的硬件加速图形
- 跨平台统一:
- .NET MAUI图形抽象层
- 共享SkiaSharp后端
- 实时协作图形:
- 多用户协同绘图技术
- 云渲染与本地合成的混合模式
- 3D与AR融合:
- 增强现实中的C#图形绘制
- 3D可视化集成2D图形元素
掌握C#图形绘制技术不仅能够创建丰富的可视化应用,更能深入理解计算机图形学基本原理。从简单的几何图形到复杂的粒子系统,从静态图像处理到实时数据可视化,这些技能在现代应用开发中具有不可替代的价值。