Copy Graphics content to bitmap
Asked Answered
F

3

6

I try to copy the contents of a graphics object to a bitmap. I'm using this code

public static class GraphicsBitmapConverter
{
    [DllImport("gdi32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);

    public static Bitmap GraphicsToBitmap(Graphics g, Rectangle bounds)
    {
        Bitmap bmp = new Bitmap(bounds.Width, bounds.Height);

        using (Graphics bmpGrf = Graphics.FromImage(bmp))
        {
            IntPtr hdc1 = g.GetHdc();
            IntPtr hdc2 = bmpGrf.GetHdc();

            BitBlt(hdc2, 0, 0, bmp.Width, bmp.Height, hdc1, 0, 0, TernaryRasterOperations.SRCCOPY);

            g.ReleaseHdc(hdc1);
            bmpGrf.ReleaseHdc(hdc2);
        }

        return bmp;
    }
}

If I use the methode like this

Graphics g = button1.CreateGraphics();
var bmp = GraphicsBitmapConverter.GraphicsToBitmap(g, Rectangle.Truncate(g.VisibleClipBounds));

the bitmap contains the content. But if I draw on the graphics object before calling the methodes the bitmap is blank:

using (Bitmap bmp = new Bitmap(100, 100))
{
    using (Graphics g = Graphics.FromImage(bmp))
    {
        g.FillRectangle(Brushes.Red, 10, 10, 50, 50);
        g.FillRectangle(Brushes.Blue, 20, 20, 50, 50);
        g.FillRectangle(Brushes.Green, 0, 0, bmp.Width, bmp.Height);

        var bmp2 = GraphicsBitmapConverter.GraphicsToBitmap(g, Rectangle.Truncate(g.VisibleClipBounds));
    }
}

Why does it work in the first case and not in the latter?

Firewater answered 6/4, 2011 at 15:5 Comment(2)
Are you checking the return value of BitBlt()? Maybe it is failing and could lead to clues.Edmonton
the code is incompleteAphelion
O
1

As I understand it

    Graphics g = Graphics.FromImage(bmp)

creates a Graphics context for drawing into the image, whereas

    Graphics g = button1.CreateGraphics();

creates one for a control that's already been drawn to the screen. You could draw your dynamically created Bitmap to a PictureBox & then do the same thing as for your button, something like

        using (Bitmap bmp = new Bitmap(100, 100)) 
            {
            using (Graphics g = Graphics.FromImage(bmp)) 
                {
                g.FillRectangle(Brushes.Green, 0, 0, bmp.Width, bmp.Height);
                g.FillRectangle(Brushes.Red, 10, 10, 50, 50);
                g.FillRectangle(Brushes.Blue, 20, 20, 50, 50);

                pictureBox1.Image = bmp;
                pictureBox1.Update(); // force an update before doing anything with it
                Graphics g2 = pictureBox1.CreateGraphics();
                var bmp2 = GraphicsBitmapConverter.GraphicsToBitmap(g2, Rectangle.Truncate(g.VisibleClipBounds)); 
                // bmp2 now has the same contents as bmp1
                pictureBox1.Image = null; // bmp is about to be Disposed so remove the reference to it
                }
            }

Of course this just re-creates a Bitmap that you already have so I assume you have some other use in mind.

Odrick answered 6/4, 2011 at 16:40 Comment(0)
C
0

Did you try to flush you graphics?

using (Bitmap bmp = new Bitmap(100, 100))
{
    using (Graphics g = Graphics.FromImage(bmp))
    {
        g.FillRectangle(Brushes.Red, 10, 10, 50, 50);
        g.FillRectangle(Brushes.Blue, 20, 20, 50, 50);
        g.FillRectangle(Brushes.Green, 0, 0, bmp.Width, bmp.Height);

        g.Flush(); // !!

        var bmp2 = GraphicsBitmapConverter.GraphicsToBitmap(g, Rectangle.Truncate(g.VisibleClipBounds));
    }
}
Chronister answered 20/4, 2011 at 12:41 Comment(0)
C
0

You do it the hard way. Obviously since the same code succeeds and fails at different calls, the graphics use a different and temporary space to store information until ask to finalize with create graphics - else it should be read in both cases.

Try using

gr.Save(); 

for graphics, perhaps this will work prior to create.

However when I want to get drawing in progress I just clone the graphics image using:

public void CopyBackgroundLayer(ref Bitmap bitmap)
{
    Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
    bitmap = new Bitmap(buffer.Clone(rect, Primary.BackGround.PixelFormat));
}

So meanwhile I add stuff to a graphic object I call this where clones the image the graphic object draws (buffer at the example) to get the bitmap up to any point before adding more stuff.

Cringle answered 8/2, 2021 at 14:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.