[.net] Is there a faster way using GDI+? (done)

Started by
14 comments, last by nprz 18 years, 3 months ago
I was optimistic that I could port my old VB6 rpg to C# without too much headache. Even masking was made simple using GDI+, but I have run into a problem that I am hoping some people have experience with. It seems to be pretty slow (at least the way I'm using it). This is my drawing loop. Is the DrawImage function extremely slow? Is there an alternative way to draw from an image to the graphic?

Bitmap bmp = new Bitmap(@"Background.bmp");
Room room = new Room(1);
Bitmap BackBuffer;
Graphics DrawingArea;

// in the form construction area
BackBuffer = new Bitmap(800, 600);
DrawingArea = Graphics.FromImage(BackBuffer);

void DrawMap(Bitmap bmp, Room r)
{
    for (int x = 0; x < 30; ++x)
    {
        for (int y = 0; y < 20; ++y)
        {

            Rectangle rect = new Rectangle(x * 30, y * 30, 30, 30);
            Pair p = r.getGround(posx + x, posy + y);
            DrawingArea.DrawImage(bmp, rect, (int)p.First * 30, (int)p.Second * 30, 30, 30, GraphicsUnit.Pixel);
        }
    }

    Graphics g = this.CreateGraphics();
    g.DrawImageUnscaled(BackBuffer, 0, 0);

}



[Edited by - nprz on December 30, 2005 4:09:26 PM]
Advertisement
Funny to see this thread, I was just moaning to people about the speed of gdi+.

I think its fine for doing small window/forms based stuff, but I have serious doubts about it for anything even semi complex and realtime. I'm not using it directly for rendering my games, but instead just for loading and manipulating textures. Even here it seems to be amazingly slow. Case in point ==> [link]http://www.gamedev.net/community/forums/topic.asp?topic_id=365331&whichpage=1??[/link]

I would suggest either managed directx, or Tao.Sdl.

*EDIT* hmmm can't seem to get that url to show... can't be bothered to sort it out either..o well :) *EDIT*
-----------------------------www.devcrunch.com - independant developer forums
Hi. Consider using the BitBlt API from Win32. It's pretty easy to use and to tie into the GDI+. Here is some existing code I have modified which could help.

		[DllImport("gdi32.dll")]		public static extern bool BitBlt(IntPtr hObject, int nXDest, int nYDest, int nWidth,			int nHeight, IntPtr hObjSource, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);		public enum TernaryRasterOperations		{			SRCCOPY     = 0x00CC0020, /* dest = source*/			SRCPAINT    = 0x00EE0086, /* dest = source OR dest*/			SRCAND      = 0x008800C6, /* dest = source AND dest*/			SRCINVERT   = 0x00660046, /* dest = source XOR dest*/			SRCERASE    = 0x00440328, /* dest = source AND (NOT dest )*/			NOTSRCCOPY  = 0x00330008, /* dest = (NOT source)*/			NOTSRCERASE = 0x001100A6, /* dest = (NOT src) AND (NOT dest) */			MERGECOPY   = 0x00C000CA, /* dest = (source AND pattern)*/			MERGEPAINT  = 0x00BB0226, /* dest = (NOT source) OR dest*/			PATCOPY     = 0x00F00021, /* dest = pattern*/			PATPAINT    = 0x00FB0A09, /* dest = DPSnoo*/			PATINVERT   = 0x005A0049, /* dest = pattern XOR dest*/			DSTINVERT   = 0x00550009, /* dest = (NOT dest)*/			BLACKNESS   = 0x00000042, /* dest = BLACK*/			WHITENESS   = 0x00FF0062, /* dest = WHITE*/		};		[DllImport("gdi32.dll", ExactSpelling=true, 			 SetLastError=true)]		public static extern IntPtr CreateCompatibleDC(IntPtr hDC);		[DllImport("gdi32.dll", ExactSpelling=true, 			 SetLastError=true)]		public static extern bool DeleteDC(IntPtr hdc);		[DllImport("gdi32.dll", ExactSpelling=true)]		public static extern IntPtr SelectObject(IntPtr hDC, 			IntPtr hObject);		private void Blit(Image source, Image destination)		{			// Create a graphics object for the picture box			Graphics grphTarget = Graphics.FromImage(source);			// Get the picture box HDC			IntPtr grphTargetDc = grphTarget.GetHdc();			// Create a GDI device compatible DC from the DC of the GDI+ DC			IntPtr pSource = CreateCompatibleDC(grphTargetDc);			// Replaces the old DC with a DC to the bitmap of the picture in the picture box			SelectObject(pSource, ((Bitmap)destination).GetHbitmap());			// BitBlt  ** Add loops here **			BitBlt(grphTargetDc, 0, 0, 32, 32, pSource, 0, 0, TernaryRasterOperations.SRCCOPY);						// Delete the GDI DC			DeleteDC(pSource);			// Release the target DC			grphTarget.ReleaseHdc(grphTargetDc);			// Dispose of the target graphic object			grphTarget.Dispose();		}

A little note about the code, please don't call Blit for every tile becuase that would hurt its performace greatly. Instead, add your loops inside the function, this means you only have to get the device contexts once per frame.

Hope that helps.
Quote:Original post by Shuryou
Hi. Consider using the BitBlt API from Win32. It's pretty easy to use and to tie into the GDI+. Here is some existing code I have modified which could help.

*** Source Snippet Removed ***
A little note about the code, please don't call Blit for every tile becuase that would hurt its performace greatly. Instead, add your loops inside the function, this means you only have to get the device contexts once per frame.

Hope that helps.


I love you [smile]. Thank you, that helped a ton!
I was searching earlier for how to do that but could never get it to work because I didn't know which Images to use, etc. With your function, I was able to use my Bitmap and Backbuffer and it was perfect.

@POedBoy: Even GDI methods for pixel manipulation were slow (GetPixel, SetPixel), unlike GDI methods for altering images (BitBlt).
OK, I have an additional question in relation to Shuryou's post.
If I use BitBlt to draw then it seems that I lose any automatic transparency feature that I had.

Is there any way to do this without a huge slowdown and hopefully without having to create a mask of the bitmap?

If I have to make a mask, do I have to use black as the transparent color or can I use magenta?
Quote:Original post by nprz
OK, I have an additional question in relation to Shuryou's post.
If I use BitBlt to draw then it seems that I lose any automatic transparency feature that I had.

Is there any way to do this without a huge slowdown and hopefully without having to create a mask of the bitmap?

If I have to make a mask, do I have to use black as the transparent color or can I use magenta?


Unfortunately, the old GDI didn't have a really good way of dealing with transparency. So you will have to create mask bitmaps, but they can be any colour like magenta.

Now, drawing tiles I assume is what takes a lot of CPU time and BitBlt solved that, so do you think its possible you could use the GDI+ functions for drawing your sprites seeing as how there probably won't be as many?

If not, here is some code I found on how to mask an image:
    SelectObject(hdcMem, g_hbmMask);    BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCAND);    SelectObject(hdcMem, g_hbmBall);    BitBlt(hdc, 0, bm.bmHeight, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCPAINT);

So the first BitBlt you draw the mask, and the second BitBlt you draw the image.
You're probably too late in the development of that to switch to SDL.NET, but it would most definately speed up things. [wink]
Rob Loach [Website] [Projects] [Contact]
Try using an "Optimized DoubleBuffer", I think this will speed up things a little but I'm not sure.
Setstyle(Controlstyles.OptimizedDoubleBuffer);
Once again MS CLR fails to deliver on its promise, wasting yet another game developer's precious time.


Quote:Original post by Rob Loach
You're probably too late in the development of that to switch to SDL.NET, but it would most definately speed up things. [wink]


Hah, you think I'm too far to switch [smile]. Actually I'm not far at all, I want to make sure the game will run before trying to build it. So with SDL.NET I'm still forced to make a new window to run the program in or can I use the existing form and draw on top of it? If switching to SDL.NET means I have to use everything in SDL.NET, then I might as well learn DirectX [grin]. -edit- (Not serious about the DirectX thing), I think I might be able to manage with SDL.NET even if I can't use drag-drop controls for those forms.

@Shuryou: I plan on using the transparencies quite a bit and a forest of trees slows down the drawing if I use GDI+'s method. Also using that BitBlt method for transparencies require that I use black as my transparency color or it won't work.

@AP: I think Microsoft would rather me use their DirectX for making a game. I think I'm misusing their tools at the moment.

[Edited by - nprz on December 30, 2005 3:06:00 PM]

This topic is closed to new replies.

Advertisement