问题的原因
子像素形状在像素内使用 Alpha 混合。不幸的是,没有 alpha 混合算法可以使矩形在邻接时无缝混合。
例如,如果:
- 背景颜色为白色
- 前景色为黑色,并且
- 你有两个矩形,每个矩形覆盖一个像素的一半
每个矩形将被涂成黑色,不透明度为 50%。第一个将白色像素转换为灰色。第二个将其转换为深灰色,但不是黑色。如果这些矩形在相邻像素中继续为黑色,您会在黑色中看到一个深灰色像素。
两种解决方案
解决这个问题的一般方法有两种:
- 使用单个 Geometry 来定义所有矩形,或
- 强制初始渲染具有足够高的分辨率,您的用户不会看到问题。
如何使用单个几何
如果您只有一组矩形,您可以创建一个简单的控件,该控件使用包含组合形状的单个 PathGeometry 绘制整个矩形集。为了说明这个想法,如果你有两个不同高度的矩形,如下所示:
<Rectangle Canvas.Left="0" Canvas.Top="0" Width="1.5" Height="2" Fill="Red" />
<Rectangle Canvas.Left="1.5" Canvas.Top="0" Width="1.5" Height="4" Fill="Red" />
你可以像这样用一个 PathGeometry 来渲染它:
<Path Data="M0,0 L0,2 L1.5,2 L1.5,4 L3,4 L3,0 Z" Fill="Red" />
实现这一点的一种实用方法是:
- 使用透明画笔绘制矩形,使其可点击但不可见
- 按 Z 顺序在矩形下方添加路径控件
Data
使用构造几何的转换器将 Path 控件的属性数据绑定到数据源。
如果您使用布局系统来定位矩形,您可能希望通过为每个矩形创建一个 Adorner 来使用 AdornerLayer,然后在渲染装饰器时计算第一个矩形的组合路径并使其余的不可见。
以上假设很容易从源数据生成 PathGeometry。对于更复杂的场景,可以对 Path 控件进行子类化,以在其父级的可视树中搜索指定的形状,并使用通用几何算法来计算 PathGeometry,该 PathGeometry 表示它们的并集,没有额外的边缘。
如果您的矩形将有多种颜色,您可以使用多个路径控件,每种颜色一个,或者您可以构建一个绘图对象并显示它。
下面是构造 PathGeometry 的代码结构:
var geo = new PathGeometry();
var figure = new PathFigure();
var segment = new PolyLineSegment();
segment.Points.Add(...);
segment.Points.Add(...);
segment.Points.Add(...);
segment.Points.Add(...);
segment.Points.Add(...);
figure.Segments.Add(segment);
geo.Figures.Add(figure);
如何强制初始渲染为高分辨率
以更高分辨率强制渲染:
- 在内部构建比您想要显示的图表大几倍的图表,例如通过将其包装在 ViewBox 中。
- 使用 VisualBrush 或 RenderTargetBitmap 强制您的图表单独呈现
- 将使用该 VisualBrush 绘制的 Rectangle 添加到您的 UI
请注意,通常 WPF 在使用 ViewBrush 时以所需的实际分辨率进行渲染很聪明,但它可能会被实际图表以较大尺寸实际显示在屏幕上而被欺骗,但随后会被父控件剪裁,因此您不需要实际上并没有看到太大的版本。
当然,RenderTargetBitmap 不存在这个问题,因为您指定了所需的分辨率,但知道何时重新渲染位图可能会很棘手。如果您只重新渲染数据更改,您可以使用事件,但如果您希望任何视觉更改触发重新渲染,则更加困难。