代码之家  ›  专栏  ›  技术社区  ›  Jens

在保持亚像素精度的同时,在WPF中无缝平铺矩形?

  •  1
  • Jens  · 技术社区  · 14 年前

    我遇到了问题中描述的问题 Tiling rectangles seamlessly in WPF 但我对这里给出的答案不太满意。

    我正在画一张条形图,画的是许多相邻的矩形。根据包含它们的画布的比例,由于亚像素渲染,其中一些画布之间存在可见的小间隙。

    我从上面的问题中学习了如何使矩形与屏幕像素相匹配,从而消除了这种影响。

    不幸的是,我的图表显示的条形图可能比像素多。除了微小的间隙(表现为颜色饱和度的周期性变化),这也很好地工作。但是,如果我用屏幕像素捕捉每个条形图,大多数条形图都会消失,因此我正在寻找另一种解决方案。

    事先谢谢!

    2 回复  |  直到 14 年前
        1
  •  2
  •   Ray Burns    14 年前

    问题的原因

    亚像素形状在像素内使用alpha混合。不幸的是,没有alpha混合算法可以在邻接时实现矩形的无缝混合。

    例如,如果:

    • 背景色是白色的
    • 前景色是黑色的,并且
    • 有两个矩形,每个矩形覆盖一个像素的一半。

    每个矩形将涂成黑色,不透明度为50%。第一个将白色像素转换为灰色。第二个将其转换为深灰色,但不是黑色。如果这些矩形在相邻像素中继续为黑色,则会在黑色中看到深灰色像素。

    两种解决方案

    解决这个问题的一般方法有两种:

    1. 使用单个几何图形定义所有矩形,或
    2. 强制初始渲染达到足够高的分辨率,用户不会看到问题。

    如何使用单个几何图形

    如果只有一组矩形,则可以创建一个简单控件,该控件使用包含组合形状的单个路径几何图形绘制整个矩形集。为了说明这个想法,如果你有两个不同高度的矩形,像这样:

    <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" />
    

    您可以使用这样的单一路径几何体渲染它:

    <Path Data="M0,0 L0,2 L1.5,2 L1.5,4 L3,4 L3,0 Z" Fill="Red" />
    

    实现这一点的一个实际方法是:

    • 用透明画笔绘制矩形,使其可以单击但不可见。
    • 按Z顺序在矩形下方添加路径控件
    • 数据绑定 Data 属性,该属性使用构造几何图形的转换器来控制数据源。

    如果使用布局系统来定位矩形,则可以使用Adornerlayer,方法是为每个矩形创建一个修饰器,然后在渲染修饰器时,计算第一个矩形的组合路径并使其余矩形不可见。

    上述假设很容易从源数据生成路径几何图形。对于更复杂的场景,可以对路径控件进行子类化,以搜索其父级的可视化树以查找指定的形状,并使用通用几何算法计算路径几何图形,该几何图形表示它们的并集,而不带任何额外的边。

    如果矩形将具有多种颜色,则可以使用多个路径控件(每种颜色一个),或者可以构造一个图形对象并显示该对象。

    以下是构建路径几何体的代码结构:

    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);
    

    如何强制初始渲染为高分辨率

    要强制以更高分辨率渲染,请执行以下操作:

    1. 在内部构造的图表比要显示的图表大几倍,例如将其包装在一个视图框中。
    2. 使用VisualBrush或renderTargetBitmap强制单独呈现图表
    3. 将用该VisualBrush绘制的矩形添加到用户界面

    请注意,通常情况下,wpf在使用viewBrush时能够以实际的分辨率进行渲染,但实际的图表实际显示在屏幕上的大小更大会欺骗它,但随后会被父控件剪裁,因此实际上不会看到太大的版本。

    当然,这个问题不存在于renderTargetBitmap中,因为您指定了所需的分辨率,但是知道何时重新渲染位图可能很困难。如果只在数据更改时重新渲染,则可以使用事件,但如果希望任何视觉更改触发重新渲染,则更难。

        2
  •  1
  •   Will    14 年前

    Target 4.0, use layout rounding (带演示的博客帖子)。