473,322 Members | 1,703 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,322 software developers and data experts.

The Mighty BitBlt

/*
BitBlt.cs

C# code using P/Invoke
I have good reasons to use P/Invoke to get access to the Win32 API
function BitBlt(). But I have trouble understanding the workings of it.

Below is a small, compilable and runnable program that shows the
problem. In words, it may seem complicated, but once you see it, it's
obvious.

First, I create a Bitmap with a green background and a red circle in the
middle. This Bitmap I alter slightly and copy it to the screen, four
times. Twice with Graphics.Drawimage(), twice with BitBlt() (so fast!).

The first attempt in the left-top corner with DrawImage() poses no problem.

The second attempt next to it with BitBlt() shows an Empty Black Square.

Then I copy part of the screen to the in-memory Bitmap. When I copy the
in-memory Bitmap back to the screen with BitBlt(), only the part copied
from the screen shows. The rest is black. This you see in the
left-bottom corner.

Finally, on the right-bottom corner, I use DrawImage() to show the
now-altered Bitmap on screen. The Bitmap is there, but only part of it
is BitBltable.
The question is:

How can I arrange things so that I can directly BitBlt an in-memory
Bitmap to the screen?

*/
namespace MyNamespace
{
using System.Drawing;
using System.Windows.Forms;
class MyForm:Form
{
[System.Runtime.InteropServices.DllImport("gdi32.dl l")]
public static extern long BitBlt
(
System.IntPtr a,
int b,
int c,
int d,
int e,
System.IntPtr f,
int g,
int h,
int i
);
MyForm()
{
Text="The Mighty BitBlt";
}
override protected void OnPaint(PaintEventArgs a)
{
//Create Bitmap with Red Circle on Green Background
Bitmap bitmap=new Bitmap(100,100,a.Graphics);
Graphics graphics=Graphics.FromImage(bitmap);
graphics.Clear(Color.Green);
graphics.FillEllipse(Brushes.Red,10,10,80,80);
//Left-Top: Draw the newly made Bitmap with DrawImage()
//Result: Green Square with Red Circle Inside
a.Graphics.DrawImage(bitmap,0,0);
//Get Handle to Screen-Graphics and to Bitmap-Graphics
System.IntPtr hdc_screen=a.Graphics.GetHdc();
System.IntPtr hdc_bitmap=graphics.GetHdc();
//Right-Top: Copy Bitmap to Screen with BitBlt()
//Result: Black Empty Square
BitBlt(hdc_screen,100,0,100,100,hdc_bitmap,0,0,0xC C0020);
//Copy Part of Screen to Bitmap with BitBlt()
BitBlt(hdc_bitmap,50,50,50,50,hdc_screen,0,0,0xCC0 020);
//Left-Bottom: Copy Bitmap to Screen with BitBlt()
//Result: Three Quarters Black, Quarter Circle Showing
BitBlt(hdc_screen,0,100,100,100,hdc_bitmap,0,0,0xC C0020);
//Get Rid of Handles and Temporary Graphics Object
a.Graphics.ReleaseHdc(hdc_screen);
graphics.ReleaseHdc(hdc_bitmap);
graphics.Dispose();
//Right-Bottom: Copy Bitmap to Screen with DrawImage()
//Result: Red PacMan with Quarter Pie in Corner
a.Graphics.DrawImage(bitmap,100,100);

}
[System.STAThread]
static void Main()
{
Application.Run(new MyForm());
}
}
}

Sep 6 '07 #1
6 7451
Martijn Mulder wrote:
/*
BitBlt.cs

C# code using P/Invoke

I have good reasons to use P/Invoke to get access to the Win32 API
function BitBlt(). But I have trouble understanding the workings of it.
I don't think the problem is with your use of BitBlt via p/invoke. If
you move the BitBlt code to a managed C++ DLL, still passing it the HDC
you created from the Graphics instance you got from the Bitmap, the same
results occur.
[...]
The question is:

How can I arrange things so that I can directly BitBlt an in-memory
Bitmap to the screen?
I don't have a specific answer to that, unfortunately.

I can tell you that if instead of creating an HDC from the Graphics
gotten from the Bitmap, you get an HBITMAP from the Bitmap, and then
create a new DC (CreateCompatibleDC) and select that HBITMAP into the
DC, then the calls to BitBlt do what you expect.

One minor "gotcha" here is that the HBITMAP you get is not a reference
to the original Bitmap instance. So the line of code that modifies the
bitmap only operates on the HBITMAP; the original Bitmap remains unchanged.

It seems obvious to me that there's something about the DC you get from
the Graphics instance that is interfering with the BitBlt from working
correctly. But it's not the use of BitBlt that's the problem.

Oddly, the DC obviously is connected to the Bitmap instance, since the
Bitmap is modified in the BitBlt that copies the 50x50 section to the
lower-right corner of the Bitmap. There's something about the DC that's
wrong and which is preventing the BitBlt from copying the bits that are
clearly there to the screen as desired.

You may want to look into the specific drawing mode set in the DC or
other state in the DC (maybe pixel depth?). I might play around with it
later, but it's been awhile since I've done native Win32 drawing stuff
so the likelihood of me coming up with the answer isn't as high as of
you figuring it out yourself. :)

Maybe someone else reading this will have some insight.

Pete
Sep 6 '07 #2
Martjin, what are the good reasons to BitBlt from memory? I am not
challenging, just asking. I use a different approach for drawing - it is in
essence double buffering, only implement in my code, as opposed to selecting
'DoubleBuffered' in the form editor. I create two identical buffers:
BufferedGraphicsContext currentContext = BufferedGraphicsManager.Current;

BufferedGraphics memBuffer1;

BufferedGraphics memBuffer2;

memBuffer1 = currentContext.Allocate(this.CreateGraphics(),
this.ClientRectangle);

memBuffer2 = currentContext.Allocate(this.CreateGraphics(),
this.ClientRectangle);

memBuffer2 stores the original image. memBuffer1 is used for drawing. Within
the Paint procedure I first copy the contents of memBuffer1 to memBuffer2.

System.Drawing.Graphics g = memBuffer1.Graphics;

this.memBuffer2.Render(g);

Then draw bitmaps in memBuffer1.

g.DrawImage(myBitmap,x,y);

I also draw shapes, text. Since it is done in memory I can do whatever I
like. I rotoate some bitmaps. When I am done I simply copy the contents of
memBuffer1 to the form calling:

memBuffer1.Render();

I believe it is a well-known technique. In my Win32 programs I used BitBlt
quite a lot. In .NET I found other ways more efficient for my purposes. In
your case I see no reason for BitBlt() not working with BufferedGraphics.

Michael



"Martijn Mulder" <i@mwrote in message
news:46***********************@news.wanadoo.nl...
/*
BitBlt.cs

C# code using P/Invoke
I have good reasons to use P/Invoke to get access to the Win32 API
function BitBlt(). But I have trouble understanding the workings of it.

Below is a small, compilable and runnable program that shows the problem.
In words, it may seem complicated, but once you see it, it's obvious.

First, I create a Bitmap with a green background and a red circle in the
middle. This Bitmap I alter slightly and copy it to the screen, four
times. Twice with Graphics.Drawimage(), twice with BitBlt() (so fast!).

The first attempt in the left-top corner with DrawImage() poses no
problem.

The second attempt next to it with BitBlt() shows an Empty Black Square.

Then I copy part of the screen to the in-memory Bitmap. When I copy the
in-memory Bitmap back to the screen with BitBlt(), only the part copied
from the screen shows. The rest is black. This you see in the left-bottom
corner.

Finally, on the right-bottom corner, I use DrawImage() to show the
now-altered Bitmap on screen. The Bitmap is there, but only part of it is
BitBltable.
The question is:

How can I arrange things so that I can directly BitBlt an in-memory Bitmap
to the screen?

*/
namespace MyNamespace
{
using System.Drawing;
using System.Windows.Forms;
class MyForm:Form
{
[System.Runtime.InteropServices.DllImport("gdi32.dl l")]
public static extern long BitBlt
(
System.IntPtr a,
int b,
int c,
int d,
int e,
System.IntPtr f,
int g,
int h,
int i
);
MyForm()
{
Text="The Mighty BitBlt";
}
override protected void OnPaint(PaintEventArgs a)
{
//Create Bitmap with Red Circle on Green Background
Bitmap bitmap=new Bitmap(100,100,a.Graphics);
Graphics graphics=Graphics.FromImage(bitmap);
graphics.Clear(Color.Green);
graphics.FillEllipse(Brushes.Red,10,10,80,80);
//Left-Top: Draw the newly made Bitmap with DrawImage()
//Result: Green Square with Red Circle Inside
a.Graphics.DrawImage(bitmap,0,0);
//Get Handle to Screen-Graphics and to Bitmap-Graphics
System.IntPtr hdc_screen=a.Graphics.GetHdc();
System.IntPtr hdc_bitmap=graphics.GetHdc();
//Right-Top: Copy Bitmap to Screen with BitBlt()
//Result: Black Empty Square
BitBlt(hdc_screen,100,0,100,100,hdc_bitmap,0,0,0xC C0020);
//Copy Part of Screen to Bitmap with BitBlt()
BitBlt(hdc_bitmap,50,50,50,50,hdc_screen,0,0,0xCC0 020);
//Left-Bottom: Copy Bitmap to Screen with BitBlt()
//Result: Three Quarters Black, Quarter Circle Showing
BitBlt(hdc_screen,0,100,100,100,hdc_bitmap,0,0,0xC C0020);
//Get Rid of Handles and Temporary Graphics Object
a.Graphics.ReleaseHdc(hdc_screen);
graphics.ReleaseHdc(hdc_bitmap);
graphics.Dispose();
//Right-Bottom: Copy Bitmap to Screen with DrawImage()
//Result: Red PacMan with Quarter Pie in Corner
a.Graphics.DrawImage(bitmap,100,100);

}
[System.STAThread]
static void Main()
{
Application.Run(new MyForm());
}
}
}

Sep 7 '07 #3
Michael Rubinstein schreef:
Martjin, what are the good reasons to BitBlt from memory? I am not
challenging, just asking. I use a different approach for drawing - it is in
essence double buffering, only implement in my code, as opposed to selecting
'DoubleBuffered' in the form editor. I create two identical buffers:
<SNIP code that I will study shortly>

Michael,

The decision to use Win32 API code was based on a dramatic difference
when I compared Graphics.DrawImage() with BitBlt(). See the code below,
you can compile and run it as-is.

But now, after some sleep, and being notified of the existence of class
BufferedGraphicsContent (never heard of before. Really) it seems there
are ways to achieve what I want within the .NET framework. And that's
way to go, of course.

Here is some code to time test DrawImage() vs. BitBlt():

/*
BitBlt.cs
*/
namespace MyNamespace
{
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
class MyForm:Form
{
[System.Runtime.InteropServices.DllImport("gdi32.dl l")]
public static extern long BitBlt
(
System.IntPtr a,
int b,
int c,
int d,
int e,
System.IntPtr f,
int g,
int h,
int i
);
const int A_BIG_NUMBER=50000;
Bitmap bitmap;
Stopwatch stopwatch;
float first;
float second;
public MyForm()
{
bitmap=new Bitmap(55,55);
Graphics a=Graphics.FromImage(bitmap);
a.Clear(BackColor);
a.FillEllipse(Brushes.Red,21,21,34,34);
a.Dispose();
}
void FirstAlternative(Graphics a)
{
for(int i=0;i<A_BIG_NUMBER;i++)
{
a.DrawImage(bitmap,0,0);
}
}
void SecondAlternative(Graphics a)
{
System.IntPtr intptr=a.GetHdc();
for(int i=0;i<A_BIG_NUMBER;i++)
{
BitBlt(intptr,55,0,55,55,intptr,0,0,13369376);
}
a.ReleaseHdc(intptr);
}
override protected void OnPaint(PaintEventArgs a)
{
a.Graphics.DrawString
(
"Wait...",
new Font("Times",12),
new SolidBrush(Color.Black),
new Point(5,90)
);

stopwatch=Stopwatch.StartNew();
FirstAlternative(a.Graphics);
stopwatch.Stop();
first=stopwatch.ElapsedMilliseconds/1000f;

stopwatch=Stopwatch.StartNew();
SecondAlternative(a.Graphics);
stopwatch.Stop();
second=stopwatch.ElapsedMilliseconds/1000f;

a.Graphics.DrawString
(
"Finished",
new Font("Times",12),
new SolidBrush(Color.Black),
new Point(5,120)
);

a.Graphics.DrawString
(
"First Alternative\nusing DrawImage() took "+first+" seconds",
new Font("Times",12),
new SolidBrush(Color.Black),
new Point(5,150)
);

a.Graphics.DrawString
(
"Second Alternative\nusing BitBlt() took "+second+" seconds",
new Font("Times",12),
new SolidBrush(Color.Black),
new Point(5,200)
);

}
[System.STAThread]
static void Main()
{
Application.Run(new MyForm());
}
}
}


Sep 7 '07 #4
Hi Martjin. When drawing is done in a memory buffer speed is rarely an
issue. The benefits become 'visible' (not literally) when you have to
perform multiple drawings operations, like transparently drawing multiple
images on top of a bigger image plus adding some shapes and text. I have not
done comparative tests, like the one you show, however I have programs
written in Win32, later re-written in .Net, and handling the same graphics.
..Net programs using buffered graphics perform noticeable better on the same
machine. Programs I am referring to are assets (vehicles, people) tracking.
The canvas I am drawing at is a geographical map.

Michael

"Martijn Mulder" <i@mwrote in message
news:46***********************@news.wanadoo.nl...
Michael Rubinstein schreef:
> Martjin, what are the good reasons to BitBlt from memory? I am not
challenging, just asking. I use a different approach for drawing - it is
in essence double buffering, only implement in my code, as opposed to
selecting 'DoubleBuffered' in the form editor. I create two identical
buffers:

<SNIP code that I will study shortly>

Michael,

The decision to use Win32 API code was based on a dramatic difference when
I compared Graphics.DrawImage() with BitBlt(). See the code below, you can
compile and run it as-is.

But now, after some sleep, and being notified of the existence of class
BufferedGraphicsContent (never heard of before. Really) it seems there are
ways to achieve what I want within the .NET framework. And that's way to
go, of course.

Here is some code to time test DrawImage() vs. BitBlt():

/*
BitBlt.cs
*/
namespace MyNamespace
{
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
class MyForm:Form
{
[System.Runtime.InteropServices.DllImport("gdi32.dl l")]
public static extern long BitBlt
(
System.IntPtr a,
int b,
int c,
int d,
int e,
System.IntPtr f,
int g,
int h,
int i
);
const int A_BIG_NUMBER=50000;
Bitmap bitmap;
Stopwatch stopwatch;
float first;
float second;
public MyForm()
{
bitmap=new Bitmap(55,55);
Graphics a=Graphics.FromImage(bitmap);
a.Clear(BackColor);
a.FillEllipse(Brushes.Red,21,21,34,34);
a.Dispose();
}
void FirstAlternative(Graphics a)
{
for(int i=0;i<A_BIG_NUMBER;i++)
{
a.DrawImage(bitmap,0,0);
}
}
void SecondAlternative(Graphics a)
{
System.IntPtr intptr=a.GetHdc();
for(int i=0;i<A_BIG_NUMBER;i++)
{
BitBlt(intptr,55,0,55,55,intptr,0,0,13369376);
}
a.ReleaseHdc(intptr);
}
override protected void OnPaint(PaintEventArgs a)
{
a.Graphics.DrawString
(
"Wait...",
new Font("Times",12),
new SolidBrush(Color.Black),
new Point(5,90)
);

stopwatch=Stopwatch.StartNew();
FirstAlternative(a.Graphics);
stopwatch.Stop();
first=stopwatch.ElapsedMilliseconds/1000f;

stopwatch=Stopwatch.StartNew();
SecondAlternative(a.Graphics);
stopwatch.Stop();
second=stopwatch.ElapsedMilliseconds/1000f;

a.Graphics.DrawString
(
"Finished",
new Font("Times",12),
new SolidBrush(Color.Black),
new Point(5,120)
);

a.Graphics.DrawString
(
"First Alternative\nusing DrawImage() took "+first+" seconds",
new Font("Times",12),
new SolidBrush(Color.Black),
new Point(5,150)
);

a.Graphics.DrawString
(
"Second Alternative\nusing BitBlt() took "+second+" seconds",
new Font("Times",12),
new SolidBrush(Color.Black),
new Point(5,200)
);

}
[System.STAThread]
static void Main()
{
Application.Run(new MyForm());
}
}
}


Sep 7 '07 #5
Michael,
One very good reason for using BitBlit lies in its use of the ROP codes (other than SRCCOPY), which it appears are not available in BufferedGraphics -- unless (hopefully) I'm wrong.

Can anyone confirm?
fred
Jun 27 '08 #6
Even with BufferedGraphics, BitBlt is faster. I am saying this because with BufferedGraphics you still use g.DrawImage(myBitmap,x,y); wich is slower than BitBlt. I have done tests!

If you want to use BitBlt then you must use this approach:

[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
[DllImport("gdi32.dll", ExactSpelling = true)]
public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
[DllImport("gdi32.dll", ExactSpelling = true)]
public static extern IntPtr DeleteObject(IntPtr hObject);
[DllImport("gdi32.dll")]
private static extern bool BitBlt(IntPtr hdcDest,Int32 nXDest,Int32 nYDest,Int32 nWidth,Int32 nHeight,IntPtr hdcSrc,Int32 nXSrc,Int32 nYSrc,UInt32 dwRop);
private const uint SRCCOPY = 0x00CC0020;

IntPtr dcSrc = IntPtr.Zero;
Graphics gSrc = null;
IntPtr hBitmap = IntPtr.Zero;
private Bitmap backImage = null;

public Bitmap BackImage
{
get { return backImage; }
set
{
backImage = value;

if (dcSrc != IntPtr.Zero)
{
gSrc.ReleaseHdc(dcSrc);
gSrc.Dispose();
DeleteObject(hBitmap);
dcSrc = IntPtr.Zero;
}

if (backImage != null)
{
hBitmap = backImage.GetHbitmap();

Graphics clientDC = this.CreateGraphics();
IntPtr hdc = clientDC.GetHdc();
IntPtr memdc = CreateCompatibleDC(hdc);

SelectObject(memdc, hBitmap);
gSrc = Graphics.FromHdc(memdc);
dcSrc = gSrc.GetHdc();

clientDC.ReleaseHdc(hdc);
clientDC.Dispose();
}

this.Invalidate();
}
}

protected override void OnPaint(PaintEventArgs e)
{
Rectangle r = e.ClipRectangle;
if (backImage != null && showBackground)
{
if (r.X backImage.Width || r.Y backImage.Height)
return;
if (r.X + r.Width backImage.Width)
r.Width = backImage.Width - r.X;
if (r.Y + r.Height backImage.Height)
r.Height = backImage.Height - r.Y;

//e.Graphics.DrawImage(backImage, r, r, GraphicsUnit.Pixel); // the slower GDI+ way

IntPtr dcDst = e.Graphics.GetHdc();
BitBlt(dcDst, r.X, r.Y, r.Width, r.Height, dcSrc, r.X, r.Y, SRCCOPY);// the faster GDI BitBlt way
e.Graphics.ReleaseHdc(dcDst);
}
}

My problem is how to i release the hBitmap wich is as big as backImage but i'm sure i will find soon.
Jun 27 '08 #7

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

2
by: DraguVaso | last post by:
Hi, In the override of the Paint-method of a DataGridTextBoxColumn I want to show an image with BitBlt, to see what I can gain there on performance. The problem is: It doesn't show me the image...
5
by: JackS | last post by:
I am trying to use GDI32 bitblt to write a bitmap to a control's window, but all I get is an empty rectangle of some rop-dependent color. In short, I use the following logic in a paint event handler:...
7
by: VB Programmer | last post by:
I am using the BitBlt operation to capture various controls into jpegs. It's almost like a screen capture, but of just the control. (This is a VB.NET application.) Because of BitBlt limitations...
6
by: Larry Serflaten | last post by:
Can anyone see something wrong with the code below? Friend Sub MapUpdate(ByRef Artwork As Bitmap, ByRef Player As MapObject) If grxForm Is Nothing Then grxForm = Me.CreateGraphics Dim hdcDst...
7
by: matt | last post by:
Hallo, I have to create a simple sprite movement; in VB6 I used the API BitBlt and all was very good. Using the NET graphical methods (not API) , the result is slow. Do you have a NET...
3
by: fripper | last post by:
Is there a way to use the windows bitblt function in a VB 2005 app? bitblt requires device context parameters for the source and destination controls but those are not used in VB 2005. Is there...
5
by: =?Utf-8?B?UmljYXJkbyBGdXJ0YWRv?= | last post by:
I'm trying to copy a part of an image (a rectangle) width the bitblt api function, bu i receive an error when i try to do it. The error is the following, that i will try to translate because its in...
1
by: =?Utf-8?B?Y3JhbmtlX2JveQ==?= | last post by:
Hi Folks, I'm not sure where this post belongs since I'm using managed vc.net, but the issue is around GDI BitBlt. Here is a summary of the problem: - I am trying to copy a bitmap of my main...
0
by: crankeboy | last post by:
Hi Folks, My setup: Visual Studio Express 2008, VC++, .NET, BitBlt Here is a summary of the problem: - I am trying to copy a bitmap of my main form into a picture box. - To do this, I bitblt...
0
by: DolphinDB | last post by:
Tired of spending countless mintues downsampling your data? Look no further! In this article, you’ll learn how to efficiently downsample 6.48 billion high-frequency records to 61 million...
0
by: ryjfgjl | last post by:
ExcelToDatabase: batch import excel into database automatically...
0
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
1
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
0
by: jfyes | last post by:
As a hardware engineer, after seeing that CEIWEI recently released a new tool for Modbus RTU Over TCP/UDP filtering and monitoring, I actively went to its official website to take a look. It turned...
1
by: CloudSolutions | last post by:
Introduction: For many beginners and individual users, requiring a credit card and email registration may pose a barrier when starting to use cloud servers. However, some cloud server providers now...
1
by: Shællîpôpï 09 | last post by:
If u are using a keypad phone, how do u turn on JavaScript, to access features like WhatsApp, Facebook, Instagram....
0
by: af34tf | last post by:
Hi Guys, I have a domain whose name is BytesLimited.com, and I want to sell it. Does anyone know about platforms that allow me to list my domain in auction for free. Thank you
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 3 Apr 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome former...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.