WPF RenderTargetBitmap Missing Elements
Asked Answered
P

2

14

I have a TreeView with small icons displayed in the data template. I'm trying to save the Treeview as a PNG using RenderTargetBitmap.

The image saves correctly on small data sets. However, if the data set becomes too large, some of the icons are excluded from the final image. The magic number seems to be 200 items. It doesn't seem to matter if the tree is deep or wide, after 200 items, the icons are not rendered.

Added Code

So here is my code that I'm using to create an image.

        RenderTargetBitmap targetBitmap = new RenderTargetBitmap(
            (int)_treeView.ActualWidth,
            (int)_treeView.ActualHeight,
            96, 96, PixelFormats.Default);

        targetBitmap.Render(_treeView);

Added Screen Shot

Notice the missing icons way over on the right side of the tree. Screen shot of missing icons

Now if I collapse a few branches, thus hiding some of the other icons, then these icons are included. It's almost like RenderTargetBitmap.Render doesn't have the power to render all of the icons. Or it may have something to do with virtual panels. Screen shot of included icons

Here is a closer look. enter image description here

Plusch answered 12/3, 2013 at 18:18 Comment(11)
Can you try something for me; Just set all images to a 2x2 image. Do you still get the 200 item limit?Trochaic
@MeirionHughes Whoa! Interesting. At 2x2, no icons were rendered. I increased the size a bit and then only a few icons, like 10. So when I asked the question, the template was set to 100x100 so I changed it to 150x150 and all the icons were rendered! So what the heck is happening??Plusch
No idea... Very weird, I'm tempted to say you have some issues with virtualisation and caching. You're not doing anything with the images, like store all of the in the same bitmap or something?Trochaic
I'm creating the images from a memory stream at 256x256. If I change the size to 512x512, less icons get rendered. If I change to 100x100, more icons get rendered. This is different and opposite to changing the data template size.Plusch
Are the icons shown in your application? I mean not the rendered image, just the window hosting your tree view.Decamp
Yes the icons appear fine in the application. RenderTargetBitmap does not include all of the icons.Plusch
Can you change the pixel format to PixelFormats.Pbgra32 and save as PNG file? Does it still missing images?Selfstyled
The images are still missing.Plusch
Just an idea: what is the original size of the images? Could it be a timeout thing, because it has to "work more" to resize the images? What do you get at actual size?Autarky
That is a good thought. The Open command opens some files to create the view models, creates the thumbnail at a specified size (let's say 256X256), then data binding starts. An Image element is set to whatever size (say 150X150) and it binds to the thumbnail on the view model. I'm refactoring now to get the thumbnail at data binding time with a converter which sizes the image to the Actual Size of the image element. I don't know yet if the problem will be solved, but it's working excellent so far. I will update if I find something new.Plusch
Maybe a timeout in the layout mechanism? I would try (just to make sure the problem is not there) to explicitly layout all internal images before Rendering by using Measure(), Arrange() and UpdateLayout(). Maybe it will force lazy loading of the images which are not loaded until layouting.Hula
P
4

What I immediately noticed that you have HUGE image. Width 12000. I am surprised that you even got that close.

As MSDN states, the texture width/height are limited by DirectX texture limits.

The maximum rendered size of a XAML visual tree is restricted by the maximum dimensions of a Microsoft DirectX texture; for more info see Resource Limits (Direct3D). This limit can vary depending on the hardware whre the app runs. Very large content that exceeds this limit might be scaled to fit. If scaling limits are applied in this way, the rendered size after scaling can be queried using the PixelWidth and PixelHeight properties. For example, a 10000 by 10000 pixel XAML visual tree might be scaled to 4096 by 4096 pixels, an example of a particular limit as forced by the hardware where the app runs. http://msdn.microsoft.com/library/windows/apps/dn298548

I suspect these things:

  • Virtualization cutting off some things - I've had the exact problem in past with DataGrid, and the problem was virtualization. Your case doesn't seem like one though.
  • Too big texture can cause undefined behaviour.

You can try disabling hardware acceleration. The thing causes quite few hardcore bugs. http://msdn.microsoft.com/en-us/library/system.windows.media.renderoptions.processrendermode.aspx

Other than that - it will be tricky, but I am pretty sure that it will work beautifully:

1) start with the root object, and traverse the root object childrens recursively, until you find an object that is less than 1000 x 1000. Take picture of it using RenderTargetBitmap(BMP) and merge it to IN-MEMORY-BMP. Do it for each children.

You should be able to calculate all this stuff.

Pricking answered 27/11, 2013 at 22:40 Comment(4)
This might be the solution, but I need a few days to test. Nevertheless, it is a very good tip!Plusch
As mentioned in my comment this is not related to virtualization: While the original poster probably used virtualization, I definitively did not. I also doubt this is related to hardware acceleration since RenderTargetBitmap doesn't use hardware acceleration. This is not from the RenderTargetBitmap MSDN page but from other posts on MSDN where people were complaining about the speed of RenderTargetBitmap.Proximal
About resource limits for RenderTargetBitmap; If I target the 64bit processor I can successfully generate a 15000x15000 png image. Trying a 30000x30000 image produces a memory error when trying to save the png. Trying the 30000x30000 on a 32 bit processor produces a memory error when creating the RenderTargetBitmap. Trying a 60000x60000 image produces an error when calling Render(). Certain combination of settings generate an `general GDI+ error when trying to save.My point: errors from resource limits are not silently swallowed - so I don't think this is a resource error.Proximal
Attach a test-case that produces the error for you, and I will tell you why it happens. (exe & .sln)Pricking
K
1

For the records: there's a workaround.

Instead of rendering your Visual directly with RenderTargetBitmap, use an interim DrawingVisual. Paint your Visual into the DrawingVisual using a VisualBrush and then use RenderTargetBitmap with the DrawingVisual.

Like this:

    public BitmapSource RenderVisualToBitmap(Visual visual)
    {
        var contentBounds = VisualTreeHelper.GetContentBounds(visual);

        var drawingVisual = new DrawingVisual();
        using (var drawingContext = drawingVisual.RenderOpen())
        {
            var visualBrush = new VisualBrush(visual);
            drawingContext.DrawRectangle(visualBrush, null, contentBounds);
        }

        var renderTargetBitmap = new RenderTargetBitmap((int)contentBounds.Width, (int)contentBounds.Height, 96, 96, PixelFormats.Default);
        renderTargetBitmap.Render(drawingVisual);

        return renderTargetBitmap;
    }

Note however that as your VisualBrush gets bigger the resulting image gets more and more fuzzy (when rendering with high DPI). To work around this problem use a series of smaller VisualBrush "tiles" as described here: https://srndolha.wordpress.com/2012/10/16/exported-drawingvisual-quality-when-using-visualbrush/

Kishke answered 2/9, 2015 at 7:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.