Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it safe (pun intended) to copy bitmap regions using lockbits this way?

I think I've found a MUCH faster way to copy bitmaps in c#. (If it's valid, I'm sure I wasn't the first but I haven't seen it anywhere yet.)

The simplest way I can think to ask this is to assert what I based my idea on and if no one shoots holes in it, assume the idea is sound:

    void FastCopyRegion(Bitmap CopyMe, ref Bitmap IntoMe, Rectangle CopyArea)
    {
        //`IntoMe` need not be declared `ref` but it brings
        //   attention to the fact it will be modified
        Debug.Assert(CopyMe.PixelFormat == IntoMe.PixelFormat,
           "PixelFormat mismatch, we could have a problem");
        Debug.Assert(CopyMe.Width == IntoMe.Width,    //This check does not verify
           "Stride mismatch, we could have a problem");// sign of `stride` match
        BitmapData copyData = CopyMe.LockBits(CopyArea,
            ImageLockMode.ReadWrite, CopyMe.PixelFormat);
        IntoMe.UnlockBits(copyData);
    }

1) LockBits simply copies a block of pixel data out of a bitmap to fixed memory to be edited and copied back in using UnlockBits

2) Using LockBits does not affect the copied memory block so it should have no effect on the image copied from.

3) Since you never enter unsafe code, there should be no risk of corrupting memory.

Possible holes I see:

1) If the PixelFormats of the two bitmaps are different, this method may not always copy correctly. However, since LockBits requires specifying a pixelformat, it seems this is handled. (If so, hooray for that overhead the other 99.9% of the time we're not switching pixelformats! /EndSarcasm)

2) If the stride of the two bitmaps doesn't match, there could be a problem (because stride is the outer for loop's incrementor in the copy operation.) This problem would limit copying to bitmaps with equal stride.

Edit: I think assertion #2 must be wrong... I just found an error when trying to later access the bitmap passed through CopyMe. Workaround below, but I'm not sure if it leaves a block of fixed memory lying around. (memory leak alert!)

    void FastCopyRegion(Bitmap CopyMe, ref Bitmap IntoMe, Rectangle CopyArea)
    {
        //`IntoMe` need not be declared `ref` but it brings attention to the fact it will be modified
        Debug.Assert(CopyMe.PixelFormat == IntoMe.PixelFormat, "PixelFormat mismatch, we could have a problem");
        Debug.Assert(CopyMe.Width == IntoMe.Width, "Width mismatch, we could have a problem");
        BitmapData copyD = IntoMe.LockBits(CopyArea, ImageLockMode.ReadWrite, CopyMe.PixelFormat);
        BitmapData copyData = CopyMe.LockBits(CopyArea, ImageLockMode.ReadWrite, CopyMe.PixelFormat);
        CopyMe.UnlockBits(copyData);
        IntoMe.UnlockBits(copyData);
    }
like image 386
AppFzx Avatar asked Feb 07 '13 14:02

AppFzx


1 Answers

Use Bitmap.Clone() instead. GDI+ tends to not report exceptions and the resulting bugs will be very hard to track.

A very fast way to copy images into a bitmap is with Graphics.DrawImage() as long as you don't convert pixel format or scale the image.

like image 181
Still.Tony Avatar answered Nov 14 '22 22:11

Still.Tony