Determine if Alpha Channel is Used in an Image

前端 未结 7 1890
北恋
北恋 2020-12-06 05:29

As I\'m bringing in images into my program, I want to determine if:

  1. they have an alpha-channel
  2. if that alpha-channel is used

#1

相关标签:
7条回答
  • 2020-12-06 06:17

    Combining a bunch of methods for different types of images got me this final method, which seems to do a good job for any image you dump into it, be it a potentially transparent gif or a png containing an alpha channel. Thanks to Elmo's answer for the fast byte reading method.

    Side note: do not use Image.IsAlphaPixelFormat(bitmap.PixelFormat)): it sees indexed (paletted) formats as non-alpha-capable, while such images can in fact possess alpha. Just, not per pixel, but per palette entry. Such alpha-enabled 8-bit images do have the HasAlpha flag enabled, though, so that's still a useful check.

    [[Note: I have since vastly simplified this logic. See my other answer.]]

    public static Boolean HasTransparency(Bitmap bitmap)
    {
        // not an alpha-capable color format.
        if ((bitmap.Flags & (Int32)ImageFlags.HasAlpha) == 0)
            return false;
        // Indexed formats. Special case because one index on their palette is configured as THE transparent color.
        if (bitmap.PixelFormat == PixelFormat.Format8bppIndexed || bitmap.PixelFormat == PixelFormat.Format4bppIndexed)
        {
            ColorPalette pal = bitmap.Palette;
            // Find the transparent index on the palette.
            Int32 transCol = -1;
            for (int i = 0; i < pal.Entries.Length; i++)
            {
                Color col = pal.Entries[i];
                if (col.A != 255)
                {
                    // Color palettes should only have one index acting as transparency. Not sure if there's a better way of getting it...
                    transCol = i;
                    break;
                }
            }
            // none of the entries in the palette have transparency information.
            if (transCol == -1)
                return false;
            // Check pixels for existence of the transparent index.
            Int32 colDepth = Image.GetPixelFormatSize(bitmap.PixelFormat);
            BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
            Int32 stride = data.Stride;
            Byte[] bytes = new Byte[bitmap.Height * stride];
            Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);
            bitmap.UnlockBits(data);
            if (colDepth == 8)
            {
                // Last line index.
                Int32 lineMax = bitmap.Width - 1;
                for (Int32 i = 0; i < bytes.Length; i++)
                {
                    // Last position to process.
                    Int32 linepos = i % stride;
                    // Passed last image byte of the line. Abort and go on with loop.
                    if (linepos > lineMax)
                        continue;
                    Byte b = bytes[i];
                    if (b == transCol)
                        return true;
                }
            }
            else if (colDepth == 4)
            {
                // line size in bytes. 1-indexed for the moment.
                Int32 lineMax = bitmap.Width / 2;
                // Check if end of line ends on half a byte.
                Boolean halfByte = bitmap.Width % 2 != 0;
                // If it ends on half a byte, one more needs to be processed.
                // We subtract in the other case instead, to make it 0-indexed right away.
                if (!halfByte)
                    lineMax--;
                for (Int32 i = 0; i < bytes.Length; i++)
                {
                    // Last position to process.
                    Int32 linepos = i % stride;
                    // Passed last image byte of the line. Abort and go on with loop.
                    if (linepos > lineMax)
                        continue;
                    Byte b = bytes[i];
                    if ((b & 0x0F) == transCol)
                        return true;
                    if (halfByte && linepos == lineMax) // reached last byte of the line. If only half a byte to check on that, abort and go on with loop.
                        continue;
                    if (((b & 0xF0) >> 4) == transCol)
                        return true;
                }
            }
            return false;
        }
        if (bitmap.PixelFormat == PixelFormat.Format32bppArgb || bitmap.PixelFormat == PixelFormat.Format32bppPArgb)
        {
            BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
            Byte[] bytes = new Byte[bitmap.Height * data.Stride];
            Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);
            bitmap.UnlockBits(data);
            for (Int32 p = 3; p < bytes.Length; p += 4)
            {
                if (bytes[p] != 255)
                    return true;
            }
            return false;
        }
        // Final "screw it all" method. This is pretty slow, but it won't ever be used, unless you
        // encounter some really esoteric types not handled above, like 16bppArgb1555 and 64bppArgb.
        for (Int32 i = 0; i < bitmap.Width; i++)
        {
            for (Int32 j = 0; j < bitmap.Height; j++)
            {
                if (bitmap.GetPixel(i, j).A != 255)
                    return true;
            }
        }
        return false;
    }
    
    0 讨论(0)
提交回复
热议问题