4

I have a problem drawing and filling rectangles in C# using GDI+. I'm trying to render a treemap and have therefore constructed a recursive algorithm which traverses the tree structure from root to leaf levels and draws a rectangle in any case, but also fills the rectangle if the node happens to be a leaf node.

The code works fine for smaller trees, but reproducably crashes for a larger data set which has 42 layers of nodes. The upper DrawRectangle call throws an OutOfMemoryException when trying to render a node below the 16th layer, independent of 32-/64-bit or debug and release configurations.

Brushes and Pens are not subject to change, so they are stored in an array outside the method. There is no problem with creating or disposing objects.

Here is the recursive method I use to render the treemap:

/// <summary>
/// Renders a certain <see cref="Tree"/> onto the canvas.
/// </summary>
/// <param name="tree">The tree node to be rendered.</param>
/// <param name="g">The <see cref="Graphics"/> canvas to render the tree to.</param>
/// <param name="bounds">The rectangle available for the specified <paramref name="tree"/>.</param>
protected void RenderTree(Tree tree, Graphics g, RectangleF bounds)
{
    if (tree.IsLeaf)
    {
        g.FillRectangle(brushes[tree.Depth], bounds);
        g.DrawRectangle(pens[tree.Depth], bounds);
    }
    else
    {
        g.DrawRectangle(pens[tree.Depth], bounds);

        if (bounds.Width >= bounds.Height)
        {
            float widthPerChild = bounds.Width / (float)tree.Children.Count;
            for (int i = 0; i < tree.Children.Count; ++i)
            {
                RenderTree(tree.Children[i], g, new RectangleF(bounds.X + i * widthPerChild, bounds.Y, widthPerChild, bounds.Height));
            }
        }
        else
        {
            float heightPerChild = bounds.Height / (float)tree.Children.Count;
            for (int i = 0; i < tree.Children.Count; ++i)
            {
                RenderTree(tree.Children[i], g, new RectangleF(bounds.X, bounds.Y + i * heightPerChild, bounds.Width, heightPerChild));
            }
        }
    }

}

Are there any ideas on what is wrong with the code? Or could it be a problem with GDI+?

Erik Philips
  • 53,428
  • 11
  • 128
  • 150
hschmauder
  • 308
  • 1
  • 9
  • 1
    Sounds like your recursion is causing OOME. You may want to consider removing the recursion. – Erik Philips Jun 04 '14 at 16:14
  • Just a thought, but perhaps try flushing the Graphics object on every pass? – Steve Danner Jun 04 '14 at 16:16
  • 1
    First check the memory usage and GDI handle count – Matt Jun 04 '14 at 16:17
  • 4
    I see no signal in this code. OOM can also be generated due to a GDI+ error, not just the GC running out of memory. Double-check your assumptions with Task Manager, Processes tab. Add the GDI Objects column and double-check that it stays well below a thousand for your process. – Hans Passant Jun 04 '14 at 16:17
  • Double check your `pens` and `brushes` collection. Make sure you correctly initialized all of them for your depth since you are having issues when you add a certain amount of nodes. You should also be able to tell us exactly what line it is blowing up on by looking at the call stack when your error is thrown. – TyCobb Jun 04 '14 at 18:06
  • 2
    Could you try to comment out the actual drawing and maybe also remove the Graphics parameter; this should help to clarify if it is indeed a GDI+ problem or not.. I tried to reproduce this, but without knowing Tree this can't be realistic. I wanted to test if removing the Graphics from the recursion would make a difference, which it didn't. I get OOM when adding 10000 children in a depth of 10000, but as my Tree is rather empty this may mean nothing at all. – TaW Jun 04 '14 at 20:30

1 Answers1

3

Thank you for all the comments; I could solve the problem. I also tested an iterative version of the drawing process and it did not change anything but gave me better debugging options. The problem seems to have been that the rectangles I tried to draw were too small (width and height > 0, but around 10^(-5)): Before drawing the rectangles, I added a threshold if the width or height of the rectangle is less than 1/1000. Of course, this makes an OutOfMemoryException not very helpful as none of the memory issues you remarked showed any suspicious behavior.

hschmauder
  • 308
  • 1
  • 9