如何在图像上绘制多段线 (WPF)

How can I draw a Polyline onto an Image (WPF)

我已经尝试了几种不同的方法来解决这个问题,但似乎无法找到一个有效的组合。

正在用 C# 创建 WPF 应用,Visual Studio。

System.Windows.Shapes.Polyline 非常适合实时绘制到 Canvas,但我希望能够以更高分辨率绘制到然后我可以将其渲染到图像上的非可视组件。

如果我在 UI 中可见的 Canvas 上创建折线,则效果很好:

// Make rendertarget size of full page
RenderTargetBitmap rtb = new RenderTargetBitmap((int)wPage, (int)hPage, 96, 96, PixelFormats.Default);
// Render the polyline
rtb.Render(lineVirt); 
// Apply to background image
imgBG.Source = rtb;

但是,如果我在 Canvas 上创建一条在 UI 上不可见的多段线,则不会对图像进行任何渲染。这可能是足够公平的。我的猜测是该组件认识到它是不可见的,因此不会费心去渲染。

我曾考虑过将 Canvas 置于 UI 中的某处并置于其他控件之下,但这似乎是一种可怕的 hack。

本质上,我需要的只是一种干净快速的方法,可以在图像上绘制指定宽度和颜色的多点线。我以为 Polyline 会很好用,但似乎只能在可见容器中使用。

我有哪些选择?

您的 canvas 需要尺码,因此某人或某物必须 Arrange 它。这可能已经足以让它渲染,但将任意视觉效果渲染到位图的唯一可靠方法实际上是 place them in the visual tree of a window that's displayed and thus laid out by WPF. You can then render to the bitmap in a deferred task at ContextIdle priority 以确保布局完整。

您根本不需要呈现 Canvas 或任何其他可见面板。

只需使用可视层可用的基本绘图原语。

下面的 DrawGeometry 方法将 Geometry 绘制到 BitmapSource 上,使用位图的渲染大小,即考虑其 DPI 的大小,以及 returns 生成的 BitmapSource。

public static BitmapSource DrawGeometry(
    BitmapSource source, Pen pen, Geometry geometry)
{
    var visual = new DrawingVisual();
    var rect = new Rect(0, 0, source.Width, source.Height);

    using (var dc = visual.RenderOpen())
    {
        dc.DrawImage(source, rect);
        dc.DrawGeometry(null, pen, geometry);
    }

    var target = new RenderTargetBitmap(
        (int)rect.Width, (int)rect.Height, 96, 96, PixelFormats.Default);

    target.Render(visual);
    return target;
}

为了绘制位图的像素单元并因此忽略它的 DPI,修改方法如下:

var rect = new Rect(0, 0, source.PixelWidth, source.PixelHeight);

using (var dc = visual.RenderOpen())
{
    dc.DrawRectangle(new ImageBrush(source), null, rect);
    dc.DrawGeometry(null, pen, geometry);
}

下面的方法使用上面的方法绘制折线作为IEnumerable<Point>

public static BitmapSource DrawPolyline(
    BitmapSource source, Pen pen, IEnumerable<Point> points)
{
    var geometry = new PathGeometry();

    if (points.Count() >= 2)
    {
        var figure = new PathFigure { StartPoint = points.First() };
        figure.Segments.Add(new PolyLineSegment(points.Skip(1), true));
        geometry.Figures.Add(figure);
    }

    return DrawGeometry(source, pen, geometry);
}

它会像

一样使用
var source = new BitmapImage(new Uri(...));

var pen = new Pen
{
    Brush = Brushes.Blue,
    Thickness = 2,
};

var points = new List<Point>
{
    new Point(100, 100),
    new Point(1000, 100),
    new Point(1000, 1000),
    new Point(100, 1000),
    new Point(100, 100),
};

image.Source = DrawPolyline(source, pen, points);