C#图形绘制教程:从基础到高级渲染技术


一、图形绘制基础概念

1.1 图形绘制技术栈对比

技术平台命名空间主要特点适用场景
GDI+System.Drawing基于CPU渲染,简单易用,功能有限WinForms应用简单图形需求
WPF绘图System.Windows.Media基于DirectX硬件加速,矢量图形,支持分辨率无关渲染现代化WPF应用复杂UI
SkiaSharpSkiaSharp跨平台2D图形库,高性能,支持多种后端跨平台移动和桌面应用
ImageSharpSixLabors.ImageSharp纯托管图像处理库,不依赖系统图形接口服务器端图像处理
Direct2DSharpDX / 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绘图优化

  1. 渲染层级优化
  • 使用DrawingVisual替代复杂控件
  • 对静态内容应用BitmapCache
  • 合理使用VisualBrush
  1. 硬件加速配置
   <!-- 启用硬件加速 -->
   <Window ... RenderOptions.ProcessRenderMode="Auto">
  1. 脏矩形技术
   // 只重绘需要更新的区域
   protected override void OnRender(DrawingContext dc)
   {
       Rect invalidRect = CalculateInvalidRect();
       dc.PushClip(new RectangleGeometry(invalidRect));

       // 绘制逻辑

       dc.Pop();
   }

4.2 GDI+性能技巧

  1. 双缓冲技术
   // 在控件构造函数中
   SetStyle(ControlStyles.OptimizedDoubleBuffer | 
           ControlStyles.AllPaintingInWmPaint |
           ControlStyles.UserPaint, true);
  1. 批量操作
   // 使用GraphicsPath批量绘制
   GraphicsPath path = new GraphicsPath();
   path.AddLines(points);
   path.AddEllipse(rect);
   g.DrawPath(Pens.Black, path);
  1. 图像处理优化
   // 使用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 渲染调试技巧

  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);
       }
   }
  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 性能分析工具

  1. WPF性能套件
  • Perforator:分析渲染性能
  • Visual Profiler:可视化树性能分析
  • ETW Tracing:事件跟踪分析
  1. Visual Studio诊断工具
  • GPU使用率
  • CPU采样
  • 内存分析
  1. 第三方工具
  • RenderDoc:图形API调试
  • NVIDIA Nsight:GPU性能分析

结语:图形技术演进趋势

  1. AI增强渲染
  • 基于神经网络的实时风格迁移
  • AI超分辨率图形处理
  1. WebGPU集成
  • 浏览器高性能图形与C#互操作
  • WebAssembly中的硬件加速图形
  1. 跨平台统一
  • .NET MAUI图形抽象层
  • 共享SkiaSharp后端
  1. 实时协作图形
  • 多用户协同绘图技术
  • 云渲染与本地合成的混合模式
  1. 3D与AR融合
  • 增强现实中的C#图形绘制
  • 3D可视化集成2D图形元素

掌握C#图形绘制技术不仅能够创建丰富的可视化应用,更能深入理解计算机图形学基本原理。从简单的几何图形到复杂的粒子系统,从静态图像处理到实时数据可视化,这些技能在现代应用开发中具有不可替代的价值。


发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注