Link to home
Start Free TrialLog in
Avatar of yaronusa
yaronusa

asked on

Can I make short GDI+ code in csharp c# code more efficient ??

I need to know how to make the short code snippet more efficient.

There is an inner and outer loop, and basically the code draws squares, one next to the other. Then it figures out if it should draw two more lines (in form of a cross), and if it should draw one last triangle. Most of the work is done inside the two loops though.

When the code draws a grid of 200 by 200 squares, it's good. It can even draw a grid 350 by 350 squares (122500 squares), but with a little over 1 second delay.

Anything simple I can do to improve this??? :) Thanks!
protected override void OnPaint(PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.FillRectangle(Brushes.White, this.ClientRectangle);
 
            int cellHeight = this.CellHeight * this.Zoom;
            int cellWidth = this.CellWidth * this.Zoom;
            int nbrColumns = this.Columns;
            int nbrRows = this.Rows;
            Size scrollOffset;
 
            // Get negative x & y coordinates of scroll
            scrollOffset = new Size(this.AutoScrollPosition);
 
            // Draw cells
            for (int row = 0; row < nbrRows; row++)
            {
                for (int col = 0; col < nbrColumns; col++)
                {
                    Point cellLocation = new Point(col * cellWidth + scrollOffset.Width, 
                                                   row * cellHeight + scrollOffset.Height);
 
                    // Define cell parameter
                    Rectangle cellRect = new Rectangle(cellLocation.X,
                                                       cellLocation.Y,
                                                       cellWidth, cellHeight);
 
                    // Only draw cell if it needs to be painted
                    if (cellRect.IntersectsWith(e.ClipRectangle))
                    {
                        g.FillRectangle(new SolidBrush(
                                _grid.getFillColor(col, row)), cellRect);
                        g.DrawRectangle(new Pen(
                                _grid.getBorderColor(col, row), 1), cellRect);
                    }    
                } 
            }

Open in new window

Avatar of Jaime Olivares
Jaime Olivares
Flag of Peru image

you can try to pre-build the fillcolor and bordercolor objects, this will save you much time.
Avatar of yaronusa
yaronusa

ASKER

I should say that every square will have a different color.

What did you mean by pre-build? I'm kind of newbie to GDI+. Can you give me a quick example? I'm trying to research your suggestion as I write this. Thanks.

you can store the different-colored  brushes in a collection, so you don't have to create them every time.
Also, if you can use the standard brushes (those in the Brushes class) provided by .net to avoid to create them at all.
 
I just tried to use standard brushes and pens (predefined/pre-built), and there is no difference in performance.

I'm starting to think that maybe the GDI code is efficient, and it just takes the computer about 1 second to do 122,550 executions (350 loops by 350 loops)? I dunno...

Here is the code I just tried:
if (cellRect.IntersectsWith(e.ClipRectangle))
{
    g.FillRectangle(Brushes.White, cellRect);
    g.DrawRectangle(Pens.Black, cellRect);
}    

Open in new window

well, 122,550 executions is really a heavy task, even sophisticated CAD software take a while to paint such kind of objects. But with a better video card you will notice a performance difference.
 
One additional comment. Building your grid for first time can take you a time, but if you want to paint the same another time, you can save the grid in a buffered graphics context, then further repaints will take just miliseconds.
If I commented out the two GDI+ drawing lines, then executions was very quick. The problem I noticed was with the e.cliprectangle -- it was unnecessarily huge!

You are right, the first build of the grid takes a long time (1.2 seconds), but because the e.cliprectangle is huge, when the grid does repaint (when a user generated event fires) it takes 1.2 seconds, even though I am double buffering.

So to solve this two ways: (1) get a graphic card (2) find a way to calculate and reduce the area which needs repainting, which I can find out without the cliprectangle.

Following your advice: I have the Intel(R) Q35 Express Chipset Family with Intel Dual Core 4400 @ 2.00Ghz...

last question: which graphic card do you recommend that will make a difference? Thanks for all your help.

>>when the grid does repaint (when a user generated event fires) it takes 1.2 seconds, even though I am double buffering.
Before you buy a new card, how are you double buffering?
 
 
Have a look to this article. You should use BufferedGraphicsContext:
http://msdn.microsoft.com/en-us/library/b367a457.aspx
Well, I could pick up a card from Circuit City and pop it in, and if it makes it faster, problem done, if not, then I could return it. But it would be nice to not need it. Is this the right way to double buffer?
InitializeComponent();
 
    // The following SetStyles are needed for double buffering:
    this.SetStyle(ControlStyles.DoubleBuffer | 
                  ControlStyles.AllPaintingInWmPaint | 
                  ControlStyles.UserPaint, true);

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of Jaime Olivares
Jaime Olivares
Flag of Peru image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I'm a bit curious about what you said so I'm looking into it. let me say that the double buffer code above belongs to a user control constructor. Maybe I'll try to use bufferedgraphicscontext in paint event, but I must say that I get no flickering, I see the update graphics in one instant, it just that it takes a second to get that update, so I know I'm getting some kind of double buffering...

Anyways, I think you've set me on the right direction and have given me all the information I need for my question, and I have some choices and a few things to learn, so thanks, 'till next time.
Thanks.
the doublebuffer property will allow you to paint smoothly, not fast
The buffered graphicscontext will allow you to paint in a memory buffer, and "remember" the image to paint in further events, this will make your repaint faster, just you need a boolean variable to know if you want to rebuild the image or repaint previous image.
 
 
Avatar of Jose Parrot
Let me add a bit comment on BufferedGraphicsContext.
Unfortunatelly, BufferedGraphicsContext is limited for performance improvement. BufferedGraphicsContext main purpose is to avoid flickering. By using two buffers, while we draw in one of them, the other is read to be shown. When we finish to draw, we swap the buffers, thus never racing the program and the refresh circuity to write into the video memory.  By writing in the not shown buffer, we avoid a lot of waiting steps, so we have some improvement in speed.

Actually, the entire process of writing into video memory is a slow process. First because we don't write there directly, instead, we call a GDI function to do it for us. As you are using GDI+, the process is slower because it is GDI under .NET, the most slow graphics interface in the world.

To make it worst, the programming language is C#, the second slower thing in the world. Just to give you an idea, a math calculation (matrix multiplication) with C++ is around 30 times faster than the same routine in C#.

But things can be still worse. Drawing small rectangles is a slow procedure (the only thing slower than that is to SetPixel(x,y) one by one).

So, we are in front of a combination of slow things: .NET, its encapsulated GDI+, C# and a lot of small filled rectangles.

The good part in C# and .NET is the richness of controls and features, besides a very good set of programming tools. But if you are looking for performace, these aren't the right tools. The faster environment is made of C++ ("native" C++, not the Microsoft slow CLR) and a powerful library like OpenGL or DirectX. OpenGL is a stable defined library, DirectX is changed everyday by Microsoft.

You can also program C# with DirectX, by using C# for the non math parts and DX for the graphics intensive ones. Such combination can result good.

Finally, by looking your program source code, I notice a good code. Your code is pretty correct and well structured. Be sure, the lack of performance isn't a code fault. Actually the obtained results are coherent regard the used tools.

Jose
>>BufferedGraphicsContext main purpose is to avoid flickering
But there are other purposes besides this one, as explained earlier, not for the first paint but for further repaints. the buffered graphics will store the image of what we are painting, so next repaint you just copy the image to screen, avoiding to paint all the rectangles again.
Jaime,

Please accept my appologies for the misunderstanding of my comment, not intended to refutate or to criticize your suggestion, by the way absolutely correct.
Actually I have addressed my comment to the bad combination of slow environments if compared to 80+ fps I use to achieve for complex graphics in OpenGL and C++.

Regards,
Jose
No, problem,
Indeed the answer depends on the kind of graphics is made. I suspected the graphic will not be so dynamic and can keep in the gdi+ arena.