Daniel Peñalba
Daniel Peñalba

Reputation: 31857

How to detect if a bitmap has Alpha channel in .NET

Someone could recommend a better way to detect if a bitmap has Alpha channel and if it's used? This method works for me, but could be inefficient if the image is very large, because it loops all the pixels in the worst case:

private static bool IsAlphaBitmap(Bitmap bmp, out BitmapData bmpData)
{
    Rectangle bmpBounds = new Rectangle(0, 0, bmp.Width, bmp.Height);

    bmpData = bmp.LockBits(bmpBounds, ImageLockMode.ReadOnly, bmp.PixelFormat);

    try
    {
        for (int y = 0; y <= bmpData.Height - 1; y++)
        {
            for (int x = 0; x <= bmpData.Width - 1; x++)
            {
                Color pixelColor = Color.FromArgb(
                    Marshal.ReadInt32(
                       bmpData.Scan0, (bmpData.Stride * y) + (4 * x)));

                if (pixelColor.A > 0 & pixelColor.A < 255)
                {
                    return true;
                }
            }
        }
    }
    finally
    {
        bmp.UnlockBits(bmpData);
    }

    return false;
}
  1. Is this the way to proceed or is there any optimal solution?
  2. Is this solution quick enough with very large files?

Thank you very much

Upvotes: 0

Views: 2923

Answers (3)

Elmo
Elmo

Reputation: 6471

You won't find a solution better than this, it took me hours to optimize:

public bool IsAlphaBitmap(ref System.Drawing.Imaging.BitmapData BmpData)
{
    byte[] Bytes = new byte[BmpData.Height * BmpData.Stride];
    Marshal.Copy(BmpData.Scan0, Bytes, 0, Bytes.Length);
    for (var p = 3; p < Bytes.Length; p += 4) {
        if (Bytes[p] != 255) return true;
    }
    return false;
}

Upvotes: 2

olegz
olegz

Reputation: 1222

Here's the example based on yours.

private static bool IsAlphaBitmap(Bitmap bmp, out BitmapData bmpData)
{
    var bmpBounds = new Rectangle(0, 0, bmp.Width, bmp.Height);
    bmpData = bmp.LockBits(bmpBounds, ImageLockMode.ReadOnly, bmp.PixelFormat);

    try
    {
        var rowDataLength = bmpData.Width * 4;  // for 32ARGB bitmap
        var buffer = new byte[rowDataLength];

        for (var y = 0; y < bmpData.Height; y++)
        {
            Marshal.Copy((IntPtr) ((int)bmpData.Scan0 + bmpData.Stride*y), buffer, 0, rowDataLength);

            for (int p = 0; p < rowDataLength; p += 4)
            {
                if (buffer[p] > 0 && buffer[p] < 255)
                    return true;
            }
        }
    }
    finally
    {
        bmp.UnlockBits(bmpData);
    }

    return false;
}

Upvotes: 1

vulkanino
vulkanino

Reputation: 9134

Didn't try this, but the idea is to work with pointers:

unsafe
{
   byte* ptrAlpha = ((byte*)bmpData.Scan0.ToPointer()) + 3;
   for (int i = bmpData.Width * bmpData.Height; i > 0; --i)  // prefix-- should be faster
   {
      if ( *ptrAlpha < 255 )
          return true;

      ptrAlpha += 4;
   }
}

Upvotes: 1

Related Questions