C# combine bitmaps fast for UI - c#

Basically I'm making an animated UI from scratch. Everything on the UI is derived from a ScreenThing base class which can store a bitmap used to draw that thing, as well as its position. When I want to draw the UI I create a new blank Bitmap of the right size, create a Graphics object from that bitmap, then pass that Graphics object to all of the ScreenThings Draw() method to draw themselves. When that's done, I print the bitmap to the screen with a PictureBox control.
This all works great, but it's too slow. I want this to happen at least 30 times per second so the animation is smooth, but it's taking more than 33 milliseconds for all of this to happen. I've seen that the reason for this slowness is the locking and unlocking of the bitmap memory. I've tried some code I found online that unlocks the bitmaps and makes a new GetPixel and SetPixel function and tried combining the images pixel by pixel like below, but this took even longer. (code below was just a test, it doesn't place the image in the right place)
for (int i = 0; i < images.Count; i++)
{
FastBitmap foreground = new FastBitmap(images[i]);
foreground.LockImage();
for (int x = 0; x < images[0].Width; x++)
{
for (int y = 0; y < images[0].Height; y++)
{
output.SetPixel(x, y, foreground.GetPixel(x, y));
}
}
foreground.UnlockImage();
}
So what's the best way to do this? Is it possible to use C# to draw a pretty big image (like 1024x768) in real time like this? If all else fails, I guess I could figure out a way to only draw the parts that have changed, but I'd love to avoid doing that if a brute force redraw of everything is possible. Thanks a lot!

You should never use bitmaps to draw real-time animations. Just create a CustomControl, and override its OnPaint method like this
protected override void OnPaint(PaintEventArgs pe)
{
}
there you can use pe.Graphics to do any drawing you want and no copying to screen is required.
I've used it and if you're drawing routines are reasonably fast, that will be smooth.
Also, remember to call SetStyle(ControlStyles.OptimizedDoubleBuffer, true); in the control constructor to get double buffering

The pixel-by-pixel approach is the worst you can do. Instead you should profile you application. This is simple: Run it at full workload (100% cpu) and pause the debugger 10 times. Look at where it stops most, that is your hotspot that you have to optimize.

Related

Image draw speed

i am working on a game, but currently I am running benchmarks.
If anyone can help me on this matter, I would greatly appreciate it.
What I am doing, is I fire the paint event on a panel when I click the start button, with this code:
private void startToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
pnlArea.Invalidate();
}
catch (Exception)
{
throw;
}
}
I then do this in my paint event:
private void pnlArea_Paint(object sender, PaintEventArgs e)
{
try
{
stopwatch = new Stopwatch();
// Begin timing
stopwatch.Start();
if (gameStatus == GameStatus.PlaceHead)
{
e.Graphics.DrawImage(dictHead["HeadRight"], 100, 100, 15, 15);
}
//e.Graphics.Clear(Color.White);
if (gameStatus == GameStatus.GameTest)
{
int x = 0;
int y = 0;
for (int i = 0; i < 5000; i++)
{
x += 15;
if (x > 1000)
{
x = 0;
y += 15;
}
e.Graphics.DrawImage(body.Value, x, y, 15, 15);
}
}
toolTimer.Text = Math.Round((stopwatch.Elapsed.TotalMilliseconds / 1000), 2).ToString() + "s";
// Stop timing
stopwatch.Stop();
}
catch (Exception)
{
throw;
}
}
This is the body part I am drawing in the code above:
This is the exact size --> 15px x 15px
but this takes up to 1.2 seconds sometimes!!!
is there a way I can improve this?
this is a sample of the end result screen:
You need to think about how to minimise the number of drawing calls you make. Currently you draw 5000 small boxes in order to produce a grid. Every time you draw a box you execute several instructions and then call a graphics method to render a scaled image. This is a lot of overhead for each grid square.
So the first thing you could look at would be finding more efficient ways to draw the image - for example, DrawImageUnscaled might work faster than DrawImage and achieve the result you want. But this is optimisation of an inefficient algorithm - what you need to do to get a real performance benefit is see if you can adopt a new, more efficient, algorithm.
If you must render using bitmaps, then look at how the pattern repeats - could you make a bigger bitmap that provides a 4x4 or 16x16 group of cells, and render that? Or a bitmap that represents a whole column or row? Then you might render with 50 calls instead of 5000.
But if you don't need to use bitmap rendering, you may be able to do much better. For example, if you gfx.Clear(backgroundColor) and then draw about 140 black lines down and across, you can create the same display with only 141 calls. Or if you draw about 70 rectangles you can effectively do 2 lines per call This massively reduces the number of method calls you have to make, and allows the graphics system to draw more pixels in a burst, using highly optimised line rendering and rectangle rendering routines (in fact, the rectangle may work out significantly faster than a generalised line due to the system knowing that the lines are always vertical and horizontal).
(If there are bits that do not follow this pattern, then can you still render the background grid and then draw the changes on top?)
Next, if only small areas of the image change from one frame to the next, then your algorithm will draw 5,000 boxes even though 4999 of them are not changing (or 70 rectangles when 1 would suffice). So you can improve matters greatly if you (a) only invalidate the part of the view that needs to change, and (b) write your rendering routine to work out which grid squares are outside the clip bounds and therefore are pointless to draw. This could reduce your updates to drawing 1 rectangle instead of 5000 every frame. (Another way to achieve the same thing would be to keep the image in an offscreen bitmap, and just draw changes onto it. When you render this to the main screen display, the graphics card will clip it for you and achieve the same result - a much faster redraw speed)
It's all about achieving the same display by being "lazy" and thinking laterally to do as little work as possible. (Getting a computer to go faster always boils down to asking it to do less)
In addition to the information everyone gave me, I came to the conclution to double buffer the panel. This fixed my problem -->
class DoubleBufferedPanel : Panel { public DoubleBufferedPanel() : base() { DoubleBuffered = true; } }
And I just used this double buffered panel instead.
New benchmark with no flickering at all! :

Drawing a single pixel or line at a time in WPF and SHOW it

I'm migrating my ray tracer to C# and WPF, but I'm having trouble with the simplest of things: How to display each pixel (or line) on a canvas or bitmap.
I want to be able to see every new pixel (or at least every finished line) when they're done, and not wait until the whole image has been created.
Like many RayTracers, I have a central loop-in-a-loop, which 'scans' the screen:
for(int y = 0; y < height; y++)
{
for(int x = 0; x < width; x++)
{
pixelRGB = SendRay(...);
// PLEASE help me update the image here ! :-)
DrawPixelAndLetTheUserSeeIt();
}
}
I've tried using WritableBitmap or writing directly to the canvas but regardles,
the image isn't updated/shown, until the entire loop has finished.
I suspect is has something to do with threads, but I can't wrap my head around it.
I'm not familiar at all with WPF, so please excuse me if i'm off-topic.
May there be some double-buffer here that prevents you to control when the graphical update is effectively done ?
If so you may want to trigger yourself the swaping of the buffer to graphics surface.

Using Bitmap in Bouncing Balls

I have a WinForm application 'Bouncing Balls' , and I need to paint the balls
on a bitmap and present the bitmap on this form.
I have a plusButton that adds new ball, and i'm saving each new ball in a list.
Now, the Form_Paint method is telling to each ball to draw himself, it works fine
until there are a lot of balls and the all application become very slow..
Here is my Code:
The paint method of the form code:
private void Form1_Paint(object sender, PaintEventArgs e)
{
ballsArray.drawImage(bmp,e, ClientRectangle);
}
NOTE: ballsArray is from type AllBalls, this is a class that wraps the ball methods, inside his c'tor i'm creating a list that keeps each ball. the bmp, is created when the form is loading - on Form_Load() method.
The drawImage of ballsArray code:
public void drawImage(Bitmap bmp,PaintEventArgs e, Rectangle r)
{
foreach (Ball b in allBalls)
{
b.drawImage(bmp,e, r);
}
}
The drawImage of Ball code:
public void drawImage(Bitmap bmp, PaintEventArgs e, Rectangle r)
{
using (Graphics g = Graphics.FromImage(bmp))
{
e.Graphics.FillEllipse(brush, ballLocation);
g.DrawImage(bmp, 0, 0);
}
}
NOTE: ballLocation is a rectangle that represent the location of the ball in each
step of movement..
So what I'm doing wrong? What causing the application to be slowly?
I have a constraint to draw everything on the bitmap and present it on the form.
I'm also passing the bitmap that I create when the form is loading, because I need to draw each on it.
Some basic techniques to make this fast:
Don't double-buffer yourself and especially don't double-buffer twice. The double-buffering you get by setting the form's DoubleBuffer property to true is superior to most any double-buffering you'd do yourself. The buffer is highly optimized to work efficiently with your video adapter's settings. So completely drop your bmp variable and draw to the e.Graphics you got from the Paint event handler argument.
You are not using the passed r argument. Possibly intended to support clipping invisible balls. The one you want to pass is e.ClipRectangle, you can skip painting balls that are completely outside of this rectangle. While that's an optimization, it isn't one that's commonly useful when you use the Aero theme and you do get inconsistent redraw rates so you might want to skip that one.
It isn't very clear why you use both Graphics.FillEllipse and Graphics.DrawImage when you draw the ball. The image ought to overlap the circle so just remove FillEllipse.
Pay a lot of attention to the Bitmap object that stores the ball graphic. First thing you want to make sure is that it is drawn with the exact size of the image so it doesn't have to be rescaled. Rescaling is very expensive. While you don't have any rescaling in your DrawImage() call, you will still get it if the resolution of the bitmap is not the same as the resolution of your video adapter. The next step will solve that
The pixel format of the ball bitmap is very important. You want one that permits copying the bitmap straight to video memory without any format conversion. On any modern machine, that format is PixelFormat.Format32bppPArgb. The difference is enormous, it draws ten times faster than any of the other ones. You won't get this format from an image resource you added, you'll have to create that bitmap when your program starts up. Check this answer for the required code.
You ought to be able to render at least 15 times faster when you follow these guidelines. If that's still enough then you do need to turn to DirectX, it has the unbeatable advantage of being able to store the ball graphic in video memory so you don't get the expensive blt from main memory to video memory.
DrawImage on Paint (or for that matter on MouseMove) is very bad design.
Graphics.DrawImage is expensive operation, and to the screen it is extra expensive.
To improve your user experience (slowness), You should paint on MouseDown/MouseUp events.
In addition, First draw to MemoryBuffer in your drawImage method and after preparing the final image, draw it once on the UI. This technique is known as double buffering.
Don't Flicker! Double Buffer! - CodeProject
In addition you can also look at BitBlit Native API for fast color/image transfer to screen.
A minimalistic c# example is here
Enable double-buffering on your form (DoubleBuffered = true).

Render Large Canvases in UserControl

I've been having trouble trying to implement this for a couple of days now. I've searched extensively on similar questions in regards to what I'm trying to do but I haven't come across a question that helps my issues directly.
Basically I'm rendering tiles onto a grid on my UserControl class. This is for my Tile Engine based world editor I'm developing. Here is a screenshot of an open world document and some tiles brushed on.
Initially, I was going to use a Bitmap in my control that would be the world's preview canvas. Using a brush tool for example, when you move your mouse and have the left button down, it sets the nearest tile beneath your cursor to the brush's tile, and paints it on the layer bitmap. The control's OnPaint method is overridden to where the layer bitmap is draw with respect to the paint event's clipping rectangle.
The issue with this method is that when dealing with large worlds, the bitmap will be extremely large. I need this application to be versatile with world sizes, and it's quite obvious there are performance issues when rendering large bitmaps onto the control each time it's invalidated.
Currently, I'm drawing the tiles onto the control directly in my control's overridden OnPaint event. This is great because it doesn't require a lot of memory. For example, a (1000, 1000) world at (20, 20) per tile (total canvas size is (20000, 20000)) runs at about 18mb of memory for the whole application. While not memory intensive, it's pretty processor intensive because every time the control is invalidated it iterates through every tile in the viewport. This produces a very annoying flicker.
What I want to accomplish is a way to meet in the middle as far as memory usage and performance. Essentially double buffer the world so that there isn't flickering when the control is redrawn (form resize, focus and blur, scrolling, etc). Take Photoshop for example - how does it render the open document when it overflows the container viewport?
For reference, here's my control's OnPaint override that is using the direct draw method mentioned above.
getRenderBounds returns a rectangle relative to PaintEventArgs.ClipRectangle that is used to render visible tiles, instead of looping through all the tiles in the world and checking if it's visible.
protected override void OnPaint(PaintEventArgs e)
{
WorldSettings settings = worldSettings();
Rectangle bounds = getRenderBounds(e.ClipRectangle),
drawLocation = new Rectangle(Point.Empty, settings.TileSize);
e.Graphics.InterpolationMode =
System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
e.Graphics.SmoothingMode =
System.Drawing.Drawing2D.SmoothingMode.None;
e.Graphics.PixelOffsetMode =
System.Drawing.Drawing2D.PixelOffsetMode.None;
e.Graphics.CompositingQuality =
System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
for (int x = bounds.X; x < bounds.Width; x++)
{
for (int y = bounds.Y; y < bounds.Height; y++)
{
if (!inWorld(x, y))
continue;
Tile tile = getTile(x, y);
if (tile == null)
continue;
drawLocation.X = x * settings.TileSize.Width;
drawLocation.Y = y * settings.TileSize.Height;
e.Graphics.DrawImage(img,
drawLocation,
tileRectangle,
GraphicsUnit.Pixel);
}
}
}
Just comment if you need some more context from my code.
The trick is to not use a big bitmap for this at all. You only need a bitmap covering the visible area. Then you draw whatever is visible.
To achieve this you will need to maintain the data separately from the bitmap. This can be a simple array or an array/list with a simple class holding information for each block such as world position.
When your block is within the visible area then you draw it. You may or may not have to iterate through the whole array, but that isn't really a problem (you can also calculate the visible array on a separate thread). You can also make the function more intelligent by creating region indexes so you don't iterate all blocks.
To add a new block to the array, calculate it's canvas position to world coordinates, add it and then render the array again (or the area where the block is drawn).
This is how controls with scrollable areas are drawn by the system too.
Enable double-buffering will keep it clear and flicker-less.
In this case I would also use a panel with separate scroll bars and calculate the scroll-bars' relative position.

Graphics are too slow in my project

I am developing zoo simulator project. It contains three thing types to draw: a map, animal environments and the animals themselves. The map is too big to fit on screen, player needs to move screen to see other parts of it. I am using a timer to draw. On its tick, it calls Invalidate() for the form being drawing on. In ZooForm_Paint method, I first draw every thing in the map on mapBuffer Bitmap. Since mapBuffer is too big to fit on screen, I draw (on screen) the part of mapBuffer the player is where.
Unfortunately, it seems that drawing everything in the map (although it may not be viewed) on mapBuffer slows the game. Can I draw my evironments and animals without need to draw entire map first?
How?
My code:
public void DrawGame(Graphics g, ref Point locationOnMap)
{
this.drawBufferMap();
this.drawMapLocation(g, ref locationOnMap);
}
private void drawBufferMap()
{
Bitmap buffer = new Bitmap(this.map.Size.Width, this.map.Size.Height);
using (Graphics graphics = Graphics.FromImage(buffer))
{
graphics.DrawImageUnscaled(this.map.Picture, new Point()); // draw entire map
foreach (var item in this.zoo.Environments) // draw all env.
{
graphics.DrawImageUnscaled(item.Picture, item.Bounds.Location);
}
foreach (var item in this.zoo.ILocatables) // draw all ILocatables
{
graphics.DrawImageUnscaled(item.Picture, item.Location);
}
}
if (this.mapBuffer != null)
{
this.mapBuffer.Dispose();
}
this.mapBuffer = buffer;
}
private void drawMapLocation(Graphics g, ref Point location)
{
g.DrawImage(this.mapBuffer, new Rectangle(0, 0, viewSize.Width, viewSize.Height),
new Rectangle(location.X, location.Y, viewSize.Width, viewSize.Height), GraphicsUnit.Pixel);
}
I don't think you are going to get any easy solutions. I can offer a few tips and opinions:
You seem to be creating a new BitMap every time you paint the screen. This is definitely not a good idea, as large bitmaps are absolutely huge in terms of memory. What you probably want to do is create one when your game loads, and then simply clear it and repaint it at every frame. I think this is probably one of the bigger performance issues you have.
There are a number of optimisations you could make afterwards. E.g. you are "rendering" the image that you will end up painting to the screen on the user interface thread. If the rendering process takes long, this will be noticeable. Typically this work happens on a background thread, and then the UI thread just checks if it can repaint using the new image. (I am simplifying things greatly here).
For graphics intensive applications, WinForms is not a particularly good environment, as others have pointed out. You will not get any hardware acceleration at all. Moving to XNA is one option, but if your application is also quite rich in terms of standard WinForms screens and controls, this is probably not an easy option. Another suggested alternative might be WPF, where you might be able to get away with using transformations to move things around, which are hardware accelerated, and are not too dissimilar to a WinForms application (well, you don't need to implement your own buttons, etc).
Hope this helps a bit.
As Daniel pointed out: creating a new bitmap each time you need to draw your map will decrease performance. Reuse the same bitmap over and over instead.
Creating a bitmap larger that you need is also very bad for performance. If you need it to scroll around, then it's fine. But if you paint a new image each time anyway, then you should just create it exactly the same size you need. Then you can call Graphics.TranslateTransform to compensate for the new coordinates so you can leave your existion code unchanged.
This will make it possible for GDI+ to clip your graphics and simply just don't draw things outside your map bitmap - which will speed things up.

Categories