【问题标题】:Bitmap cache on DrawingVisual has blurry partsDrawingVisual 上的位图缓存部分模糊
【发布时间】:2014-11-26 02:25:13
【问题描述】:

最近我切换到DrawingVisuals 以提高趋势图的性能(尤其是缩放和平移)。

这是我的代码:

blocksToBeRendered = (baseItem as AvgCurve).GetStreamGeometryBlocks(ActualWidth, ActualHeight, _minPoint.X, _maxPoint.X, FixTimeStep ? _timeStep : 0, IsMainChart);

Pen stroke = new Pen((baseItem as AvgCurve).LineBrush, 1);

foreach (GeometryGroup group in blocksToBeRendered)
{
    if (group.Children.Count != 0)
    {
        if (!cachedBlocks[baseItem].Any(x => x.Children[0] == group.Children[0]))
        {
            cachedBlocks[baseItem].Add(group);

            ImprovedDrawingVisual vis = new ImprovedDrawingVisual();

            BitmapCache cache = new BitmapCache() { SnapsToDevicePixels = true };
            vis.CacheMode = cache;

            using (DrawingContext context = vis.RenderOpen())
            {
                RenderOptions.SetEdgeMode(group, EdgeMode.Aliased);
                if (group.Children.Count > 0)
                {
                    context.DrawGeometry(null, stroke, group.Children[0]);
                }
            }

            _host.VisualCollection.Add(vis);
        }
    }
}

这是ImprovedDrawingVisual

public class ImprovedDrawingVisual: DrawingVisual
{
    public ImprovedDrawingVisual()
    {
        VisualEdgeMode = EdgeMode.Aliased;
        VisualBitmapScalingMode = BitmapScalingMode.NearestNeighbor;
    }
}

现在,几何图形确实有 Transforms,这可能很重要。

发生的情况是,在没有位图缓存(1 px 线)的情况下,图表绘制得很好,但是当我打开位图缓存时,有时图表的某些部分会变得模糊。

有谁知道我该如何解决这个问题?我尝试更改DrawingVisualRenderAtScale 或关闭EdgeMode 设置,但这无济于事。

编辑:省略画笔填充几何图形以避免混淆,因为它与此处无关。

【问题讨论】:

  • 您正在使用BitmapCache(请参阅my 问题),它看起来像使用jpg-compression 的位图。也许您可以实现自己的BitmapCache(不会是jpg)?顺便说一句,要获得真正高性能的图表,我必须在 wpf 中使用 GDI+
  • 您有使用 GDI+ 的示例,我将如何从我的代码示例开始?

标签: c# caching bitmap geometry drawingvisual


【解决方案1】:

如果您决定在wpf 中尝试GDI+,那么绘图将如下所示:

using GDI = System.Drawing;

private int _counter; // count redraws

protected override void OnRender(DrawingContext context)
{
    if (Figures != null && RenderSize.Height > 0 && RenderSize.Width > 0)
        using (var bitmap = new GDI.Bitmap((int)RenderSize.Width, (int)RenderSize.Height))
        {
            using (var graphics = GDI.Graphics.FromImage(bitmap))
                foreach (var figure in Figures)
                    figure.Render(this, graphics);
            // draw image
            var hbitmap = bitmap.GetHbitmap();
            var size = bitmap.Width * bitmap.Height * 4;
            GC.AddMemoryPressure(size);
            var image = Imaging.CreateBitmapSourceFromHBitmap(hbitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
            image.Freeze();
            context.DrawImage(image, new Rect(RenderSize));
            DeleteObject(hbitmap);
            GC.RemoveMemoryPressure(size);
            // trigger garbage collecting
            if (_counter++ > 10)
                GC.Collect(3);
      }
}

这基本上将Graph 渲染到位(没有延迟wpf 渲染,嗯,只有最终image 的blitting)。您调用InvalidateVisual() 重绘图形。注意GC 东西,它们是必须的(特别是如果图形频繁重绘,则定期进行垃圾收集)。

在我的代码中,Figures 是一个非视觉对象列表(简单的类,也实现了 GDI 渲染)。你有视觉孩子。这是问题,我留给你解决。好处是性能非常高。

【讨论】:

  • 我曾经在画布上放置几何图形,每次平移或缩放时都会重新绘制几何图形。是不是更像这样?
  • 不,因为视觉效果和wpf 很慢。你在xaml 中设置了视觉效果吗?或者,换个说法,图形(线条、曲线、背景等)必须是视觉对象吗?如果答案是“否”,则将它们替换为非视觉对象,它只知道如何将 self 渲染为父 Graph
  • 我曾经有路径,而不是视觉效果。这些路径具有几何形状并被添加到画布上。
  • 在渲染过程中执行缩放/平移。有 local 坐标,例如图的点 (100,100)。还有鼠标坐标。渲染图形时,您将本地坐标转换为鼠标,然后使用 GDI 函数绘制线条或其他任何东西。 WPF 转换起初看起来不错,但你仍然有向后计算的问题(也就是鼠标坐标到什么点)。视觉效果解决了命中测试,但它们很慢
  • 谢谢。我会通过一些测试来尝试一下。我不得不扔掉/大量调整我所有的几何创建逻辑,这有点糟糕:p
猜你喜欢
  • 1970-01-01
  • 2016-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-13
  • 2019-02-21
相关资源
最近更新 更多