views:

362

answers:

3

I'm trying to use this code to draw a Bitmap directly onto a PictureBox:

Bitmap bmp = (Bitmap)Bitmap.FromFile(@"C:\Users\Ken\Desktop\Load2.bmp");
Graphics grDest = Graphics.FromHwnd(pictureBox1.Handle);
Graphics grSrc = Graphics.FromImage(bmp);
IntPtr hdcDest = grDest.GetHdc();
IntPtr hdcSrc = grSrc.GetHdc();
BitBlt(hdcDest, 0, 0, pictureBox1.Width, pictureBox1.Height,
    hdcSrc, 0, 0, (uint)TernaryRasterOperations.SRCCOPY); // 0x00CC0020
grDest.ReleaseHdc(hdcDest);
grSrc.ReleaseHdc(hdcSrc);

but instead of rendering the Bitmap's contents it just draws a solid block of nearly-black. I'm pretty sure the problem is with the source hDC, because if I change SRCCOPY to WHITENESS in the above code, it draws a solid white block, as expected.

Note: this next snippet works fine, so there's nothing wrong with the bitmap itself:

Bitmap bmp = (Bitmap)Bitmap.FromFile(@"C:\Users\Ken\Desktop\Load2.bmp");
pictureBox1.Image = bmp;
+4  A: 

This is because a device context contains a 1x1 black bitmap until SelectObject is used. For whatever reason, Graphics.FromImage is giving you a device context that is compatible with the bitmap, but it does not automatically select the bitmap into the device context.

The following code will use SelectObject.

You should, of course, use the managed Graphics.DrawImage instead of BitBlt if possible, but I assume that you have a good reason for using BitBlt.

private void Draw()
{
    using (Bitmap bmp = (Bitmap)Bitmap.FromFile(@"C:\Jason\forest.jpg"))
    using (Graphics grDest = Graphics.FromHwnd(pictureBox1.Handle))
    using (Graphics grSrc = Graphics.FromImage(bmp))
    {
        IntPtr hdcDest = IntPtr.Zero;
        IntPtr hdcSrc = IntPtr.Zero;
        IntPtr hBitmap = IntPtr.Zero;
        IntPtr hOldObject = IntPtr.Zero;

        try
        {
            hdcDest = grDest.GetHdc();
            hdcSrc = grSrc.GetHdc();
            hBitmap = bmp.GetHbitmap();

            hOldObject = SelectObject(hdcSrc, hBitmap);
            if (hOldObject == IntPtr.Zero)
                throw new Win32Exception();

            if (!BitBlt(hdcDest, 0, 0, pictureBox1.Width, pictureBox1.Height,
                hdcSrc, 0, 0, 0x00CC0020U))
                throw new Win32Exception();
        }
        finally
        {
            if (hOldObject != IntPtr.Zero) SelectObject(hdcSrc, hOldObject);
            if (hBitmap != IntPtr.Zero) DeleteObject(hBitmap);
            if (hdcDest != IntPtr.Zero) grDest.ReleaseHdc(hdcDest);
            if (hdcSrc != IntPtr.Zero) grSrc.ReleaseHdc(hdcSrc);
        }
    }
}

[DllImport("gdi32.dll", EntryPoint = "SelectObject")]
public static extern System.IntPtr SelectObject(
    [In()] System.IntPtr hdc,
    [In()] System.IntPtr h);

[DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeleteObject(
    [In()] System.IntPtr ho);

[DllImport("gdi32.dll", EntryPoint = "BitBlt")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool BitBlt(
    [In()] System.IntPtr hdc, int x, int y, int cx, int cy,
    [In()] System.IntPtr hdcSrc, int x1, int y1, uint rop);
binarycoder
Thanks, works perfectly. Can you explain the first line in the `finally` block?
MusiGenesis
Deleting an object that is currently selected into a device context is not correct, according to MSDN. See the remarks at http://msdn.microsoft.com/en-us/library/dd162957(VS.85).aspx and http://msdn.microsoft.com/en-us/library/dd183539(VS.85).aspx. The line of code restores the original 1x1 bitmap prior to deleting the bitmap identified by hBitmap.
binarycoder
A: 

This is really usefull for me. But when I try to save not to picturebox but to a file, it save a black image...Any idea? Here the modifyed code (thanks binarycoder!):

        private void Draw()
    {

       Bitmap myBitmap = new Bitmap(500, 200,
           System.Drawing.Imaging.PixelFormat.Format24bppRgb);


        using (Bitmap bmp = (Bitmap)Bitmap.FromFile(FileName))
        using (Graphics grDest = Graphics.FromImage(myBitmap))
        using (Graphics grSrc = Graphics.FromImage(bmp))
        {
            IntPtr hdcDest = IntPtr.Zero;
            IntPtr hdcSrc = IntPtr.Zero;
            IntPtr hBitmap = IntPtr.Zero;
            IntPtr hOldObject = IntPtr.Zero;

            try
            {
                hdcDest = grDest.GetHdc();
                hdcSrc = grSrc.GetHdc();

                hBitmap = bmp.GetHbitmap();

                hOldObject = SelectObject(hdcSrc, hBitmap);
                if (hOldObject == IntPtr.Zero)
                    throw new Win32Exception();

                if (!BitBlt(hdcDest, 10, 40, 30, 80,
                    hdcSrc, 0, 0, 0x00CC0020U))
                    throw new Win32Exception();
            }
            finally
            {
                pictureBox2.Image = myBitmap; //  <--I'ts ok, black image and the portion of the one from file...
                myBitmap.Save("Test.bmp");    //  <--Not work: black image

                if (hOldObject != IntPtr.Zero) SelectObject(hdcSrc, hOldObject);
                if (hBitmap != IntPtr.Zero) DeleteObject(hBitmap);
                if (hdcDest != IntPtr.Zero) grDest.ReleaseHdc(hdcDest);
                if (hdcSrc != IntPtr.Zero) grSrc.ReleaseHdc(hdcSrc);

            }
        }
    }
Johnny
Use `PixelFormat.Format32bppArgb` (instead of `Format24bppRgb`) for `myBitmap`.
MusiGenesis
Ok with PixelFormat.Format32bppArgb something has changed: no more black background. I can still display correctly into picturebox. But the bmp I save is white (without bitblt effect). Before was black. Any Idea?
Johnny
A: 

One more issue: if (hOldObject != IntPtr.Zero) SelectObject(hdcSrc, hOldObject); if (hBitmap != IntPtr.Zero) DeleteObject(hBitmap); if (hdcDest != IntPtr.Zero) grDest.ReleaseHdc(hdcDest); if (hdcSrc != IntPtr.Zero) grSrc.ReleaseHdc(hdcSrc);

These 3 lines take aroud 100ms on big images, It's really slow...any other way to solve?

Johnny