【问题标题】:How to properly render large bitmaps in WPF?如何在 WPF 中正确渲染大位图?
【发布时间】:2014-12-10 04:40:36
【问题描述】:

没想到

RenderTargetBitmap.Render(visual)

有任何副作用摘录更改位图数据本身。看起来这不是真的。在一些丑陋的渲染伪影开始发生之前,我无法重复超过 60 次。

如何在 WPF 中正确渲染大量精灵?下面是重现问题的代码。

我以这种方式生成精灵:

    BitmapSource Sprite()
    {
        var bitmap = new RenderTargetBitmap(
            500, 500,
            96, 96,
            PixelFormats.Default);

        var visual = new DrawingVisual();
        var rect = new Rect(
                    new Size(
                        bitmap.Width,
                        bitmap.Height));

        using (DrawingContext context = visual.RenderOpen())
            context.DrawLine(
                new Pen(Brushes.Red, 100),
                rect.TopLeft,
                rect.BottomRight);

        bitmap.Render(visual);
        bitmap.Freeze();
        return bitmap;
    }

这是渲染其中许多的画布:

    public BitmapSource Canvas
    {
        get
        {
            var bitmap = new RenderTargetBitmap(
                1980, 1080,
                96, 96,
                PixelFormats.Default);

            var tiles = 70;
            for (int i = 0; i < tiles; i++)
            {
                var visual = new DrawingVisual();
                var rect = new Rect(
                    bitmap.Width / tiles * i,
                    0,
                    bitmap.Width / tiles,
                    bitmap.Height);

                using (DrawingContext context = visual.RenderOpen())
                    context.DrawImage(Sprite(), rect);

                bitmap.Render(visual);
            }

            bitmap.Freeze();
            return bitmap;
        }
    }

我可以在数据绑定到 Canvas 属性时看到这张奇怪的图片...

【问题讨论】:

  • 为什么不将这 70 个图块渲染到一个 DrawingVisual 中,然后只调用一次 bitmap.Render 呢?您只需将for 循环移动到using (DrawingContext ...) 块中。您还可以通过将线条直接绘制到“外部”DrawingContext 来完全保存“内部”RenderTargetBitmaps。
  • 我无法直接渲染图块,因为这意味着单个视觉对象同时保持对 70 个或更多位图的引用。我会用完内存。不幸的是,我确实需要“内部”RenderTargetBitmaps,因为 Tiles 是我的应用程序的输入栅格数据。
  • 1) 我无法重现您的问题,即使使用 tiles = 2000。 2) 在单个 Visual 中保留对精灵的所有引用时,我可以重现内存不足异常 - 但大约 855 个图块而不是 70 个图块。您使用的是哪种图形硬件?
  • 它看起来像一个版本的东西。 .NET Framework 4.0 或更高版本有这个问题。
  • 我可以在 Win 8.1 上重现(针对 x86 和 x64)。即使绘制一个存储的精灵(每次都不是不同的)......在大约 70 次迭代时,它开始在您的代码中显示工件。

标签: c# .net wpf wpf-4.5


【解决方案1】:

这里是一个使用InteropBitmap的例子:

 public InteropBitmapHelper(ColorSpace colorSpace, int bpp, int width, int height, uint byteCount)
        {
            _currentColorSpace = colorSpace;
            _pixelFormat = GetPixelFormat(colorSpace, bpp);

            if (_pixelFormat == PixelFormats.Rgb24 || _pixelFormat == PixelFormats.Rgb48 || _pixelFormat == PixelFormats.Bgr32 ||
                _pixelFormat == PixelFormats.Bgr24 || _pixelFormat == PixelFormats.Bgr565 ||
                _pixelFormat == PixelFormats.Gray16 || _pixelFormat == PixelFormats.Gray8)
            {
                int strideWidth = (width % 4 == 0) ? width : width - width % 4 + 4;
                if (byteCount != strideWidth * height * (_pixelFormat.BitsPerPixel / 8))
                {
                    strideWidth = width;
                }
                _stride = strideWidth * _pixelFormat.BitsPerPixel / 8;

                _byteCount = (uint)((_stride) * height * ((short)bpp).NumberOfBytes());

                ColorFileMapping = CreateFileMapping(new IntPtr(-1), IntPtr.Zero, 0x04, 0, _byteCount, null);

                if (ColorFileMapping == IntPtr.Zero)
                {
                    var res=GetLastError();
                    IPDevLoggerWrapper.Error("Could not generate InteropBitmap "+res);
                    return;
                }
                ViewerImageData = MapViewOfFile(ColorFileMapping, 0xF001F, 0, 0, _byteCount);
                InteropBitmap = Imaging.CreateBitmapSourceFromMemorySection(ColorFileMapping,
                                                                            width,
                                                                            height,
                                                                            _pixelFormat,
                                                                            _stride,
                                                                            0) as InteropBitmap;
            }
            else
            {
                LoggerWrapper.Error("The image format is not supported");
                return;
            }
        }

这是我将它连接到 xaml 的方式。

<Canvas     
    x:Name="content" 
    Width="{Binding ElementName=MainImage, Path=ActualWidth}" 
    Height="{Binding ElementName=MainImage, Path=ActualHeight}" 
    Background="Transparent"
    MouseEnter="ImageMouseEnter" 
    MouseLeave="ImageMouseLeave"
    RenderTransformOrigin ="0.5,0.5">
    <Image x:Name="MainImage"  Source="{Binding Source}" RenderOptions.BitmapScalingMode="{Binding ViewerRenderingMode }"/>

如果您需要更多信息,请告诉我。

【讨论】:

  • 实际上我没有任何大小合理的持久图像。我需要将多个高分辨率精灵(由 WriteableBitmap 表示)组合到一个低分辨率 BitmapSource。我的精灵真的很大,所以它们只需要一次生成一个。问题在于将精灵渲染到单个较小的 RenderTargetBitmap (Canvas) 以组合它们;从 .NET 4.0 开始,它基本上无法正常工作。微软表示它看起来像一个 bug,并指派产品团队开发人员修复它。
猜你喜欢
  • 2019-08-02
  • 1970-01-01
  • 1970-01-01
  • 2013-06-30
  • 1970-01-01
  • 1970-01-01
  • 2015-10-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多