views:

90

answers:

6

Hi,

I am trying to build a simple graphics application in WPF C#. The purpose is to draw 10000*10000 rectangles of size 4 pixels each.

I have modified the OnRender method of the canvas to draw the rectangles. Drawings are performed for smaller number of rectangles (say 50*50 or 100*100 rectangles of 4 pixel each) but it is slowing down as I am increasing the no. of rectangles.

Following is my code:

  protected override void OnRender(DrawingContext dc)
    {
        base.OnRender(dc);

        FillCells(dc);

        if (_ShowGrids)
        {
            DrawGrid(dc); // draw grid lines
        }
    }
 void FillCells(DrawingContext dc)
    {

        int cellSize=4;

        for (int i = 0; i < MaxRow; i++)
        {
            for (int j = 0; j < MaxColumn; j++)
            {
                dc.DrawRectangle(GetRectBrush(i,j), GetRectPen(i,j), new Rect(j * cellSize , i * cellSize , cellSize - 1, cellSize - 1));

            }
        }
    }

The above code takes more than a minute to draw 1000*1000 rectangles.

Is there any method to make this process faster? Is there any other thing I can use in place of this?

Thanks.

A: 

Perhaps try overlaying the canvas with a VisualBrush.
To this visualBrush simply add the 4*4 rectangle and have it repeat in a tile mode. Alternatively you could just add the lines to it so that it doesnt overlap the edges of the rectangle... your choice :)

Your problem is in the creation of the brush... A test run indicated that this code

int limit = 10000 * 10000;
var converter = new BrushConverter();
for (int i = 0; i < limit; i++)
{
    var blueBrush = converter.ConvertFromString("Blue") as Brush;
}

took 53 seconds to run. You are trying to create 100,000,000 brushes :) If it is patternable, use a patterned visual brush, if it is not patternable... perhapse look for another solution. The overhead of storing that many brushes in memory is in the Gigabytes

TerrorAustralis
visual brush will not serve the purpose because the rectangles may be of different colors. Visual brush replicates any visual element.
Vinod Maurya
How are you coloring the rectangles? it looks in your example like they will all be blue :)
TerrorAustralis
I have modified the code now to get the color of the rects.
Vinod Maurya
+1  A: 

You don't need to recreate the brush for each iteration of the loop, since they use the same color over and over:

SolidColorBrush blueBrush = new SolidColorBrush(Colors.Blue)
SolidColorPen bluePen = new SolidColorPen(blueBrush)

for (int i = 0; i < MaxRow; i++)
{
    for (int j = 0; j < MaxColumn; j++)
    {
        dc.DrawRectangle(blueBrush, bluePen, 1), new Rect(j * cellSize , i * cellSize , cellSize - 1, cellSize - 1));
    }
}

This may speed up the loop a bit.

Fredrik Mörk
A little faster but still slow :(
Vinod Maurya
Not the real problem, but good advice.
Ed Swangren
+9  A: 

The purpose is to draw 10000*10000 rectangles of size 4 pixels each.

Do NOT draw them. That simple. This would be 40k to 40k pixels.

Most will not be visible. So they must not bee drawn. Basically only draw those that are visible in the canvas. When resizing or scrolling you repaint anyway, then do the same - only draw those that are visible.

Virtualization is the key to performance here. Take things out of the drawing loop as early as possible. Stuff not visible per definition does not need to be drawn at all.

Next alternative would be not to use a canvas. Try a bitmap. Prepare it on a separate thread, then draw this one at once.

TomTom
This is the only way to increase the performance I think. I also thought to implement this mechanism to draw but did not able to calculate the visible are of the canvas. I also asked a question for this problem here, googled but did not get the luck.
Vinod Maurya
+1. Drawing 100M of anything is insane.
alxx
+1 @alxx. But, when it comes to controls like DataGrid or some jagged Grid controls. It is absolutely fine. In some cases it may require even more than 100M.
Avatar
@Avatar: In that case virtualization does kinda the same thing.
Grozz
@Grozz Virtualization for Drawing context would be moot :)
Avatar
@Tom: Thanks, I have implemented the virtualization to achieve this and it is working like "bearings with grease". Thanks again!
Vinod Maurya
+1  A: 

You should try StreamGeometry then. http://msdn.microsoft.com/en-us/library/system.windows.media.streamgeometry.aspx

For complex geometries that don’t need to be modified after they are created, you should consider using StreamGeometry rather than PathGeometry as a performance optimization. StreamGeometry works like PathGeometry, except that it can only be filled via procedural code. Its odd name refers to an implementation detail: To use less memory (and less of the CPU), its PathFigures and PathSegments are stored as a compact byte stream rather than a graph of .NET objects.

Quoted from Adam Nathan's book WPF Unleashed.

Avatar
WHOW - that was unheard of for me. Something to read.
TomTom
+1 for WPF Unleashed. Awesome book.
Grozz
+1  A: 

One more tip on top of what everyone already said, make sure the pens and brushes are frozen - if you create the brush call Freeze before using it (brushes from the Brushes class (Brushes.White) are already frozen).

Nir
+1 in performance. Thanks!
Vinod Maurya
A: 

The bitmap approach might speed up more - BitmapSource has a Create method that takes raw data either as an array or a pointer to unsafe memory. It should be a bit faster to set values in an array than drawing actual rectangles - however you have to checkout the pixelformats to set the individual pixels correctly.

Rune Andersen