Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Help for improving the performance of GDIPlus Bitmap drawing

387 views
Skip to first unread message

Nan

unread,
Aug 5, 2008, 11:47:26 AM8/5/08
to
Hi Gurus,

Currently I encouter a performance issue in CSharp PictureBox with
GDIPlus rendering, my dataset is 78 curves, 512 points in each curve.

I enabled the doublebuffer in WinForm, first use Graphics::DrawLines
put all the curves in a memory bitmap, then draw the memory bitmap
to the OnPaint graphics.

However the bottleneck is not the DrawImage in the OnPaint function
which I found out many complains in Google search, but the DrawLines.
The cpu usage is:
1. DrawLines add up to 10%.
2. DrawImage 3%.


Besides, I enabled the double buffer by
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.DoubleBuffer, true);
in the PictureBox.

Would any one tell me how can I improve the performance of this
scenario? Thanks a lot!!!

Nan

unread,
Aug 5, 2008, 11:52:27 AM8/5/08
to
Just add one cent, I think the issue actually is Drawlines in Bitmap
spend too much CPU time, is that reasonable for my dataset?

Thanks!
Nan

Bob Powell [MVP]

unread,
Aug 6, 2008, 4:27:44 PM8/6/08
to
Hi,
First of all, to optimise drawing as much as possible, use a 32 bit PARGB
image. This has the advanage of being fast and efficient.

The number of lines and points you specify is negligable. I have example
programs on my site capable of drawing a thousand complex shapes with
different fills and lines with transparency in a real-time animation. The
bottleneck must be elsewhere.

Double buffering doesn't improve draw speed, only the appearance of the
drawing. This is a red herring and so you need not be concerned with it.

If you're drawing on a picture box please read my #1 most asked GDI+
question and the various answers for clarification.

--
Bob Powell [MVP]
Visual C#, System.Drawing

Ramuseco Limited .NET consulting
http://www.ramuseco.com

Find great Windows Forms articles in Windows Forms Tips and Tricks
http://www.bobpowell.net/tipstricks.htm

Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/faqmain.htm

All new articles provide code in C# and VB.NET.
Subscribe to the RSS feeds provided and never miss a new article.

"Nan" <muna...@gmail.com> wrote in message
news:9f550f50-65e4-414b...@b30g2000prf.googlegroups.com...

Message has been deleted
Message has been deleted

Nan

unread,
Aug 8, 2008, 3:43:24 AM8/8/08
to
Thanks so much for your prompt help, Bob!

I tried your suggest to use Format32bppPArgb, but it doesn't improve
performance a lot.
My test environment: WinXP SP2, dotnet 2.0, core2 2.33 GHz, CPU usage
monitor perfmon.msc.
My test result:
Comment out 2 drawlines functions: 3.5% CPU usage.
uncomment out 2 drawlines functions: 23% CPU usage.

I also list my test code, would you please take a quick look at:

class WndBase : PictureBox
{
private Graphics _graphics = null;
private Graphics _memGraphics = null;
private Bitmap _bitmap = null;
private Bitmap _memoryBitmapForSelectJudgment = null;
private List<PointF[]> _pointsList = null;
private const int PIXEL_COUNT = 512;
private System.Windows.Forms.Timer timer1;

public WndBase()
{
SetControlStyles();

_pointsList = new List<PointF[]>();

InitBitmapAndGraphics(ref _bitmap, ref _graphics);
InitBitmapAndGraphics(ref _memoryBitmapForSelectJudgment, ref
_memGraphics);

System.Random rd = new Random();

PointF[] tempPoints = null;
for (int i = 0; i < 6; i++ )
{
GeneratePoints(ref tempPoints, rd);
_pointsList.Add(tempPoints);
tempPoints = null;
}

InitTimer();
}

private void InitBitmapAndGraphics(ref Bitmap _bitmap, ref
Graphics _graphics)
{
_bitmap = new Bitmap(200, 200, PixelFormat.Format32bppPArgb);
_graphics = Graphics.FromImage(_bitmap);
_graphics.PageUnit = GraphicsUnit.Millimeter;
_graphics.PageScale = 0.01F;
}

private void InitTimer()
{
this.timer1 = new System.Windows.Forms.Timer();
this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
timer1.Interval = 1000;
timer1.Enabled = true;
}

private void SetControlStyles()
{
SetStyle(ControlStyles.EnableNotifyMessage, true);
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.BackColor = Color.Transparent;

// To permit double buffer


SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.DoubleBuffer, true);
}

private void GeneratePoints(ref PointF[] points, System.Random rd)
{
points = new PointF[PIXEL_COUNT];

for (int i = 0; i < PIXEL_COUNT; i++)
{
points[i].X = i * 1.1F;
points[i].Y = -(float)rd.NextDouble() * 100;
}
}

protected override void Dispose(bool disposing)
{
if (this._graphics != null)
{
this._graphics.Dispose();
this._graphics = null;
}
if (_memGraphics != null)
{
_memGraphics.Dispose();
_memGraphics = null;
}
if (_memoryBitmapForSelectJudgment != null)
{
_memoryBitmapForSelectJudgment.Dispose();
_memoryBitmapForSelectJudgment = null;
}
if (_bitmap != null)
{
_bitmap.Dispose();
_bitmap = null;
}
if (_pointsList != null)
{
_pointsList.Clear();
}
base.Dispose(disposing);
}

protected internal const int wndBaseEvent = 100; // Message ID

protected internal enum eventMessage // Event type
{
updateView, // OnUpdateView
updateViewRect, // OnUpdateView
redrawComponents, // RedrawComponents
singleClick, // OnClick
doubleClick, // OnDoubleClick
mouseMove, // OnMouseMove
mouseUp, // OnMouseUp
mouseDown, // OnMouseDown
sizeChanged, // OnSizeChanged
mouseHover, // OnMouseHover
mouseLeave // OnMouseLeave
}

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool PostMessage(
IntPtr hWnd, [MarshalAs(UnmanagedType.U4)] int msg, int
wParam, int lParam);

protected override void OnNotifyMessage(Message m)
{
if ((int)m.Msg == (int)wndBaseEvent)
{
switch ((int)m.WParam)
{
case ((int)eventMessage.redrawComponents):
OnRedrawComponentsMessageHandler();
break;
default:
break;
}
}
}

private void timer1_Tick(object sender, EventArgs e)
{
PostMessage(this.Handle,
wndBaseEvent,
(int)eventMessage.redrawComponents,
0);
}

private void OnRedrawComponentsMessageHandler()
{
try
{
_graphics = Graphics.FromImage(_bitmap);
_memGraphics =
Graphics.FromImage(_memoryBitmapForSelectJudgment);
for (int i = 0; i < 13; i++)
{
using (Pen pen = new Pen(Color.Blue, 0))
{
pen.DashStyle =
System.Drawing.Drawing2D.DashStyle.Solid;
int iColor = 0x00FF0000;
int iBlack = 0x00000000;

foreach (PointF[] points in _pointsList)
{
PointF[] tmpPoints =
TranslateToDrawPointFArray(points);
_graphics.DrawLines(pen, tmpPoints);
_memGraphics.DrawLines(pen, tmpPoints);
}
}
}

}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine(e.Message);
}

this.Invalidate(false);
this.Update();
}

protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);

Rectangle rect = this.ClientRectangle;
e.Graphics.DrawImage(_bitmap, 0, 0, rect.Width, rect.Height);
}

protected PointF[] TranslateToDrawPointFArray(PointF[] points)
{
PointF[] pointArray = new PointF[points.Length];
float yConst = Math.Abs(100);

for (int i = 0; i < points.Length; i++)
{
pointArray[i].X = points[i].X;
pointArray[i].Y = (yConst + points[i].Y) * 1.0F;
}

return pointArray;
}
}
On Aug 7, 4:27 am, "Bob Powell [MVP]" <bob@_spamkiller_bobpowell.net>
wrote:

Bob Powell [MVP]

unread,
Aug 8, 2008, 6:55:49 AM8/8/08
to
Well, it seems you have a number of misconceptions going on here.

Firstly, you maintain a bitmap which you draw in the background and which
you then draw too the backbuffer which is then drawn to the screen. This
"belt and braces" approach will consume blit times you don't need.

You have your own paint message going on.. hmm, why not just invalidate the
screen and draw your lines on the backbuffer provided?

You seem to be transforming lines by manually and iteratively translating
them. Why not just apply a transform to your drawing surface and have done
with it?

What you're doing is simple enough. You've made the implementation so
complex that it is just eating unecessary CPU cycles.

--
Bob Powell [MVP]
Visual C#, System.Drawing

Ramuseco Limited .NET consulting
http://www.ramuseco.com

Find great Windows Forms articles in Windows Forms Tips and Tricks
http://www.bobpowell.net/tipstricks.htm

Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/faqmain.htm

All new articles provide code in C# and VB.NET.
Subscribe to the RSS feeds provided and never miss a new article.

"Nan" <muna...@gmail.com> wrote in message

news:1dfbab80-16c2-4ba0...@j33g2000pri.googlegroups.com...
> Sorry, update my code snippet

> SetStyle(ControlStyles.AllPaintingInWmPaint, true);
> SetStyle(ControlStyles.UserPaint, true);
> SetStyle(ControlStyles.DoubleBuffer, true);
> }
>

Nan

unread,
Aug 8, 2008, 11:59:42 AM8/8/08
to
Bob,

Thanks again for your kindly help.
For the first comment, I don't know if you are mention I used a extra
bitmap: _memoryBitmapForSelectJudgment, and the extra graphcis
_memgraphics, if so, please forget about this, that's only for test.
For the second comment, actually, this code is legacy code from other
developers, I think why they choose to do this is because they don't
want to draw in synchronized state in case the timer tick and repaint
event from another thread even another process.

Anyway, I remove the extra bitmap and the PostMessage, but the CPU
usage is still very high: DrawLines eats 7% CPU while DrawImage eats
3% CPU, below is the updated code:

class WndBase : PictureBox
{
private Graphics _graphics = null;

private Bitmap _bitmap = null;

private List<PointF[]> _pointsList = null;
private const int PIXEL_COUNT = 512;
private System.Windows.Forms.Timer timer1;

public WndBase()
{
SetControlStyles();

_pointsList = new List<PointF[]>();

InitBitmapAndGraphics(ref _bitmap, ref _graphics);

this.Image = _bitmap;
_graphics = Graphics.FromImage(this.Image);


System.Random rd = new Random();

PointF[] tempPoints = null;
for (int i = 0; i < 6; i++ )
{
GeneratePoints(ref tempPoints, rd);
_pointsList.Add(tempPoints);
tempPoints = null;
}

InitTimer();
}

private void InitBitmapAndGraphics(ref Bitmap _bitmap, ref
Graphics _graphics)
{
_bitmap = new Bitmap(200, 200, PixelFormat.Format32bppPArgb);
_graphics = Graphics.FromImage(_bitmap);
_graphics.PageUnit = GraphicsUnit.Millimeter;
_graphics.PageScale = 0.01F;
}

private void InitTimer()
{
this.timer1 = new System.Windows.Forms.Timer();
this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
timer1.Interval = 1000;
timer1.Enabled = true;
}

private void SetControlStyles()
{


SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.BackColor = Color.Transparent;

// To permit double buffer
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.DoubleBuffer, true);
}

private void GeneratePoints(ref PointF[] points, System.Random rd)
{
points = new PointF[PIXEL_COUNT];

for (int i = 0; i < PIXEL_COUNT; i++)
{
points[i].X = i * 1.1F;
points[i].Y = -(float)rd.NextDouble() * 100;
}
}

protected override void Dispose(bool disposing)
{
if (this._graphics != null)
{
this._graphics.Dispose();
this._graphics = null;
}

if (_bitmap != null)
{
_bitmap.Dispose();
_bitmap = null;
}
if (_pointsList != null)
{
_pointsList.Clear();
}
base.Dispose(disposing);
}

private void timer1_Tick(object sender, EventArgs e)
{


for (int i = 0; i < 13; i++)
{
using (Pen pen = new Pen(Color.Blue, 0))
{
pen.DashStyle =
System.Drawing.Drawing2D.DashStyle.Solid;

foreach (PointF[] points in _pointsList)


{
PointF[] tmpPoints =
TranslateToDrawPointFArray(points);
_graphics.DrawLines(pen, tmpPoints);
}
}
}

this.Invalidate(false);
this.Update();
}

protected override void OnPaint(PaintEventArgs e)
{

Rectangle rect = this.ClientRectangle;
e.Graphics.DrawImage(_bitmap, 0, 0, rect.Width, rect.Height);

base.OnPaint(e);
}

protected PointF[] TranslateToDrawPointFArray(PointF[] points)
{
PointF[] pointArray = new PointF[points.Length];
float yConst = Math.Abs(100);

for (int i = 0; i < points.Length; i++)
{
pointArray[i].X = points[i].X;
pointArray[i].Y = (yConst + points[i].Y) * 1.0F;
}

return pointArray;
}
}

On Aug 8, 6:55 pm, "Bob Powell [MVP]" <bob@_spamkiller_bobpowell.net>
wrote:


> Well, it seems you have a number of misconceptions going on here.
>
> Firstly, you maintain a bitmap which you draw in the background and which
> you then draw too the backbuffer which is then drawn to the screen. This
> "belt and braces" approach will consume blit times you don't need.
>
> You have your own paint message going on.. hmm, why not just invalidate the
> screen and draw your lines on the backbuffer provided?
>
> You seem to be transforming lines by manually and iteratively translating
> them. Why not just apply a transform to your drawing surface and have done
> with it?
>
> What you're doing is simple enough. You've made the implementation so
> complex that it is just eating unecessary CPU cycles.
>
> --
> Bob Powell [MVP]
> Visual C#, System.Drawing
>

> Ramuseco Limited .NET consultinghttp://www.ramuseco.com
>
> Find great Windows Forms articles in Windows Forms Tips and Trickshttp://www.bobpowell.net/tipstricks.htm
>
> Answer those GDI+ questions with the GDI+ FAQhttp://www.bobpowell.net/faqmain.htm

0 new messages