3
\$\begingroup\$

I have the following code below that creates a new Bitmap object the same as the original but to ensure that its PixelFormat is 24bppRgb. Does anyone know if there is a faster way to do this:

using (Bitmap bitmap = new Bitmap(image.Width, image.Height, PixelFormat.Format24bppRgb))
{
    using (Graphics g = Graphics.FromImage(bitmap))
    {
        g.DrawImage(image, new Rectangle(0, 0, bitmap.Width, bitmap.Height));
    }

    //...
\$\endgroup\$
  • 1
    \$\begingroup\$ The simple and most obvious optimization (which you may already have - hard to tell with just a snippet) is to skip out early if the image is already 24bppRgb. Other optimizations will probably involve pointer black magic and Bitmap.LockBits, and it may be best to ask for those types of optimizations on the main StackOverflow site. \$\endgroup\$ – Dan Lyons May 3 '16 at 17:41
  • \$\begingroup\$ @DanLyons I have already made that, and I would have done but I felt that this sort of question would be off topic over there. \$\endgroup\$ – TheLethalCoder May 4 '16 at 11:25
  • \$\begingroup\$ Thanks for this question. The answers have saved my day! \$\endgroup\$ – Heslacher Oct 30 '19 at 5:24
4
+50
\$\begingroup\$

To remove the alpha channel, you could use Bitmap.Clone :

public static Bitmap RemoveAlphaChannel(Bitmap bitmap) {
    var rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
    return bitmap.Clone(rect, PixelFormat.Format24bppRgb);
}

Another way is to lock the bits without the alpha channel and then copy the memory to a new bitmap:

public static Bitmap RemoveAlphaChannel(Bitmap bitmap) {
    Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
    Bitmap bitmapDest = (Bitmap)new Bitmap(bitmap.Width, bitmap.Height, PixelFormat.Format24bppRgb);
    BitmapData data = bitmap.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
    BitmapData dataDest = bitmapDest.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
    NativeMethods.CopyMemory(dataDest.Scan0, data.Scan0, (uint)data.Stride * (uint)data.Height);
    bitmap.UnlockBits(data);
    bitmapDest.UnlockBits(dataDest);
    return bitmapDest;
}

static class NativeMethods {

    const string KERNEL32 = "Kernel32.dll";

    [DllImport(KERNEL32)]
    public extern static void CopyMemory(IntPtr dest, IntPtr src, uint length);

}
|improve this answer|||||
\$\endgroup\$
  • 1
    \$\begingroup\$ First off the bitmap can be any format, whether that is 1bpp or 32bpp will your methods still work? Secondly I have tried the .Clone method before and it is 3x slower on my machine than what is provided in the question. If you're answering a question on performance at lest test your solutions. \$\endgroup\$ – TheLethalCoder May 6 '16 at 8:00
  • \$\begingroup\$ A few other things why are you casting a Bitmap to a Bitmap? Can you add some spacing into the answer I can barely read whats going on because it is all clustered up. And as far as I know it is preferable to write the whole name not abbreviate i.e. dataDest. \$\endgroup\$ – TheLethalCoder May 6 '16 at 8:05
  • \$\begingroup\$ I deleted my last comment as I realised the test I did was not fair, I gave the second method a fair test and it runs faster, thanks \$\endgroup\$ – TheLethalCoder May 6 '16 at 8:54
  • \$\begingroup\$ As stated the method removes the alpha channel which is only present in a 32bpp image. So no it won't work for a 1bpp bitmap. Second, yes I tried the .Clone and find it 30% faster compared to Graphics.DrawImage when applyed to a screenshot from a webpage. But since you didn't bother to provide a sample or a context, yes there's a chance that it is slower for your specific usage. \$\endgroup\$ – Florent B. May 6 '16 at 8:56
  • \$\begingroup\$ I didn't 'bother' because the image can be pretty much anything for my usage and on my test images it was slower. And I know that's what you stated but it seems to work fine for using other formats too, although I need more testing to be sure. \$\endgroup\$ – TheLethalCoder May 6 '16 at 9:00
1
\$\begingroup\$

Just to be complete I created the following method based on the answer by Florent. The reason being is that this one should be a bit 'safer' and also allows for more extensibility.

The try-finallys are in place to ensure as much as possible that the Bitmaps do get unlocked.

public static Bitmap ChangePixelFormat(Bitmap bitmap, PixelFormat pixelFormat)
{
    Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);

    BitmapData bitmapData = bitmap.LockBits(rect, ImageLockMode.ReadOnly, pixelFormat);
    try
    {
        Bitmap convertedBitmap = new Bitmap(bitmap.Width, bitmap.Height, pixelFormat);
        BitmapData convertedBitmapData = convertedBitmap.LockBits(rect, ImageLockMode.WriteOnly, pixelFormat);
        try
        {
            CopyMemory(convertedBitmapData.Scan0, bitmapData.Scan0, (uint)bitmapData.Stride * (uint)bitmapData.Height);
        }
        finally
        {
            convertedBitmap.UnlockBits(convertedBitmapData);
        }

        return convertedBitmap;
    }
    finally
    {
        bitmap.UnlockBits(bitmapData);
    }
}
|improve this answer|||||
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.