Slashy
Slashy

Reputation: 1881

C# bitblt bitmap render to control

I work on a small real-time project where a fast bitmap rendering technique is quite necessary. I need to display many(hundreds) small blocks in a picturebox per second,i found the bitblt example from the pinvoke.net website.

I use a while loop(right now it's infinite ),to retrieve a particular bitmap,and then calling the Invalidate() method to trigger the Paint event.

This is my code:

    protected override void OnPaint(PaintEventArgs e)
    {
        IntPtr pTarget = e.Graphics.GetHdc();
        IntPtr pSource = CreateCompatibleDC(pTarget);
        IntPtr pOrig = SelectObject(pSource, bmp.GetHbitmap());
        BitBlt(pTarget, 0, 0, bmp.Width, bmp.Height, pSource, 0, 0, TernaryRasterOperations.SRCCOPY);
        DeleteObject(pOrig);
        DeleteDC(pSource);
        e.Graphics.ReleaseHdc(pTarget);
    }
    private void Display()
    {
        while (true)
        {
            frame = desktopDuplicator.GetLatestFrame();
            if (frame != null)
            {
                bmp = frame.DesktopImage;//retrieve image.
                this.Invoke(new Action(() => this.Invalidate()));//trigger the repaint event
            }

        }
    }

It works fine for few seconds,then i'm getting an System.ArgumentException on this line:

BitBlt(pTarget, 0, 0, bmp.Width, bmp.Height, pSource, 0, 0, TernaryRasterOperations.SRCCOPY);

Does anyone have an idea what is wrong here? i keep releasing the used resources(in the paint event)...why i'm getting this error?

Thanks in advance.

Upvotes: 1

Views: 2677

Answers (2)

Ivan Stoev
Ivan Stoev

Reputation: 205909

Does anyone have an idea what is wrong here? i keep releasing the used resources(in the paint event)...why i'm getting this error?

Actually you are not releasing all the used resources, specifically the bitmap handle returned by the bmp.GetHbitmap() call. The correct sequence is to select back the original default bitmap handle into device context and then delete your bitmap handle, as explained in the SelectObject documentation:

This function returns the previously selected object of the specified type. An application should always replace a new object with the original, default object after it has finished drawing with the new object.

IntPtr targetDC = e.Graphics.GetHdc();
IntPtr sourceDC = CreateCompatibleDC(targetDC);
IntPtr sourceBitmap = bmp.GetHbitmap();
IntPtr originalBitmap = SelectObject(sourceDC, sourceBitmap);
BitBlt(targetDC, 0, 0, bmp.Width, bmp.Height, sourceDC, 0, 0, TernaryRasterOperations.SRCCOPY);
SelectObject(sourceDC, originalBitmap);
DeleteObject(sourceBitmap);
DeleteDC(sourceDC);
e.Graphics.ReleaseHdc(targetDC);

Upvotes: 2

Rowland Shaw
Rowland Shaw

Reputation: 38128

From the documentation for the Bitmap.GetHbitmap method:

You are responsible for calling the GDI DeleteObject method to free the memory used by the GDI bitmap object.

You don't currently appear to be calling that, which will lead to a leak. You should call DeleteObject once you're done with the resource, so maybe something like:

protected override void OnPaint(PaintEventArgs e)
{
    IntPtr pTarget = e.Graphics.GetHdc();
    IntPtr pSource = CreateCompatibleDC(pTarget);
    IntPtr pOrig = SelectObject(pSource, bmp.GetHbitmap());
    BitBlt(pTarget, 0, 0, bmp.Width, bmp.Height, pSource, 0, 0, TernaryRasterOperations.SRCCOPY);
    DeleteObject(pOrig);
    DeleteDC(pSource);
    e.Graphics.ReleaseHdc(pTarget);
}

Upvotes: 1

Related Questions