0

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

Daniel Peñalba
  • 30,507
  • 32
  • 137
  • 219
  • 1
    Have you tried Image.IsAlphaPixelFormat – Digvijay Feb 15 '12 at 10:33
  • Also seems like your problem has been solved here on StackOverflow before: http://stackoverflow.com/questions/3064854/determine-if-alpha-channel-is-used-in-an-image – Digvijay Feb 15 '12 at 10:35
  • @Digvijay: IsAlphaPixelFormat won't work for me because I need to be not only if the bitmap has alpha channel, also if it's used. – Daniel Peñalba Feb 15 '12 at 10:43
  • @Digvijay: The link you have provided also loops all the pixels. What I'm asking is if is there any other solution wihtout loop the pixels. Thanks – Daniel Peñalba Feb 15 '12 at 10:46
  • You don't need to convert int32 value to color, so just take scanline for every Y and just loop through all Alpha bytes (slightly modify what vulkanino suggests). – olegz Feb 15 '12 at 10:47
  • @olegz: Could you provide an example please? – Daniel Peñalba Feb 15 '12 at 11:32
  • I asked a similar question without a fast solution here http://stackoverflow.com/questions/2569538/detecting-if-a-png-image-file-is-a-transparent-image – kenny Feb 15 '12 at 15:40

3 Answers3

2

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;
}
MgSam
  • 12,139
  • 19
  • 64
  • 95
Elmo
  • 6,409
  • 16
  • 72
  • 140
  • I'm not an expert on Bitmap manipulation, but this will throw an exception if BmpData.Stride property has a negative value. I've tried the code with native hBitmaps of 16x16 with alpha channel that I got by calling IShellItemImageFactory.GetImage(), and stride is -64. On the other side this other solution worked perfect for me: https://stackoverflow.com/a/9291151/1248295 – ElektroStudios Mar 24 '21 at 11:00
1

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;
   }
}
vulkanino
  • 9,074
  • 7
  • 44
  • 71
  • It is impossible to compile unsafe in my company, sorry, so this answer is not valid to me, should be better to perform ani API call – Daniel Peñalba Feb 15 '12 at 10:40
1

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;
}
olegz
  • 1,189
  • 10
  • 20