WPF InkCanvas 访问笔划下的所有像素

WPF InkCanvas access all pixels under the strokes

WPF的InkCanvas好像只能提供笔画的点(与笔画的宽高无关)。对于应用程序,我需要知道 InkCanvas 绘制的所有点。

例如,假设笔划的宽度和高度为 16。使用此笔划大小,我在 InkCanvas 上画了一个点。有没有一种简单的方法来获取这个点中的所有 256 个像素(而不是单独的这个巨点的中心点)?

我为什么关心:
在我的应用程序中,用户使用 InkCanvas 在显示一些 3D 对象的 Viewport3D 之上绘图。我想使用笔画的所有点进行光线投射,并确定 Viewport3D 中的哪些对象已被用户的笔画覆盖。

我发现了一种非常肮脏的处理方式。如果有人知道更好的方法,我将非常乐意投票并接受他们的回复作为答案。
基本上我的方法涉及获取每个笔划的 Geometry ,遍历该几何边界内的所有点并确定该点是否在几何内部。

这是我现在使用的代码:

foreach (var stroke in inkCanvas.Strokes)
{
    List<Point> pointsInside = new List<Point>();

    Geometry sketchGeo = stroke.GetGeometry();
    Rect strokeBounds = sketchGeo.Bounds;

    for (int x = (int)strokeBounds.TopLeft.X; x < (int)strokeBounds.TopRight.X + 1; x++)
        for (int y = (int)strokeBounds.TopLeft.Y; y < (int)strokeBounds.BottomLeft.Y + 1; y++)
        {
            Point p = new Point(x, y);

            if (sketchGeo.FillContains(p))
                pointsInside.Add(p);
        }
}

您可以使用 StrokeCollection 的 HitTest 方法。我将您的解决方案的性能与此实现进行了比较,发现 HitTest 方法的性能更好。您的里程等

// get our position on our parent.
var ul = TranslatePoint(new Point(0, 0), this.Parent as UIElement);

// get our area rect
var controlArea = new Rect(ul, new Point(ul.X + ActualWidth, ul.Y + ActualHeight));
            
// hit test for any strokes that have at least 5% of their length in our area
var strokes = _formInkCanvas.Strokes.HitTest(controlArea, 5);

if (strokes.Any())
{
   // do something with this new knowledge
}

您可以在此处找到文档: https://docs.microsoft.com/en-us/dotnet/api/system.windows.ink.strokecollection.hittest?view=netframework-4.7.2

此外,如果你只关心任何点是否在你的矩形中,你可以使用下面的代码。它比 StrokeCollection.HitTest 快一个数量级,因为它不关心笔画的百分比,所以它做的工作少得多。

    private bool StrokeHitTest(Rect bounds, StrokeCollection strokes)
    {
        for (int ix = 0; ix < strokes.Count; ix++)
        {
            var stroke = strokes[ix];
            var stylusPoints = stroke.DrawingAttributes.FitToCurve ? 
                stroke.GetBezierStylusPoints() : 
                stroke.StylusPoints;

            for (int i = 0; i < stylusPoints.Count; i++)
            {
                if (bounds.Contains((Point)stylusPoints[i]))
                {
                    return true;
                }
            }
        }
        return false;
    }