Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly render large bitmaps in WPF?

Tags:

c#

.net

wpf

wpf-4.5

I did not expect

RenderTargetBitmap.Render(visual)

to have any side effects excerpt changing the bitmap data itself. It looks like it is not true. I am unable to repeat it more than 60 times before some ugly rendering artifacts start to happen.

How to properly render a lot of sprites in WPF? Below is the code to reproduce the problem.

I generate sprites this way:

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

Here is the canvas to render many of them:

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

I can see this strange picture while being data bound to Canvas property ...

enter image description here

like image 417
Dmitry Nogin Avatar asked Dec 10 '14 04:12

Dmitry Nogin


1 Answers

Here is an example for using 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;
            }
        }

Here is how I connect it to the 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 }"/>

Please let me know if you need more info.

like image 56
Gilad Avatar answered Oct 08 '22 02:10

Gilad