开发者

Is there a way to find out about a png image transparency by reading the image header?

Is there a way to find out ab开发者_高级运维out a png image transparency by reading the image header?


Binary access

Following my comment about the poor performance of GetPixel for each pixel, I tried to write a snippet which finds if there are transparent pixels or not in an image (including PNG). Here it is.

public static bool IsImageTransparent(string fullName)
{
    using (Bitmap bitmap = Bitmap.FromFile(fullName) as Bitmap)
    {
        bool isTransparent;

        // Not sure if the following enumeration is correct. Maybe some formats do not actually allow transparency.
        PixelFormat[] formatsWithAlpha = new[] { PixelFormat.Indexed, PixelFormat.Gdi, PixelFormat.Alpha, PixelFormat.PAlpha, PixelFormat.Canonical, PixelFormat.Format1bppIndexed, PixelFormat.Format4bppIndexed, PixelFormat.Format8bppIndexed, PixelFormat.Format16bppArgb1555, PixelFormat.Format32bppArgb, PixelFormat.Format32bppPArgb, PixelFormat.Format64bppArgb, PixelFormat.Format64bppPArgb };

        if (formatsWithAlpha.Contains(bitmap.PixelFormat))
        {
            // There might be transparency.
            BitmapData binaryImage = bitmap.LockBits(new Rectangle(Point.Empty, bitmap.Size), ImageLockMode.ReadOnly, PixelFormat.Format64bppArgb);

            unsafe
            {
                byte* pointerToImageData = (byte*)binaryImage.Scan0;
                int numberOfPixels = bitmap.Width * bitmap.Height;

                isTransparent = false;

                // 8 bytes = 64 bits, since our image is 64bppArgb.
                for (int i = 0; i < numberOfPixels * 8; i += 8)
                {
                    // Check the last two bytes (transparency channel). First six bytes are for R, G and B channels. (0, 32) means 100% opacity.
                    if (pointerToImageData[i + 6] != 0 || pointerToImageData[i + 7] != 32)
                    {
                        isTransparent = true;
                        break;
                    }
                }
            }

            bitmap.UnlockBits(binaryImage);
        }
        else
        {
            // No transparency available for this image.
            isTransparent = false;
        }

        return isTransparent;
    }
}

Pros:

  • Binary access, much faster than GetPixel,
  • Does not require additional libraries nor WPF,
  • Works with any format supported by GDI+: BMP, GIF, JPEG, PNG, TIFF, Exif, WMF, and EMF.

Cons:

  • Requires unsafe,
  • Is slower than reading PNG file directly.

Palettes

A less manual approach would be to use palettes. There might probably exist some .NET Framework or third party libraries which let you do that. I tried the following (using WPF):

using (Stream imageStreamSource = new FileStream(fullName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
    PngBitmapDecoder decoder = new PngBitmapDecoder(imageStreamSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
    BitmapSource bitmapSource = decoder.Frames[0];

    return bitmapSource.Palette.Colors.Any(c => c.A != 0);
}

but I does not work, since bitmapSource.Palette is null most of the time. Further, using palettes will heavily decrease performance, compared to the first snippet, since every color must be loaded into a list of colors before proceeding.


Find out what? If the image HAS transparency? You could check the bit depth, 24 bit (RGB) usually means there is no transparency, and 32 bit (RGBA) means there is an opacity/transparency layer


You can sometimes tell that it definitely doesn't have transparency if either the bit depth is 24 or if it's lower and none of the palette members contain a relevant alpha value (you don't say whether you want to count partial transparency as transparent or not).

However, to be sure that there actually is some transparency, does require one to examine the entire image. Hence, it's O(n) for the stream size (which is roughly O(x * y) for the image size), but with a possible short-cut from the header for some cases.


If the png is indexed then you can check the TRNS chunk Png chunks description. If not then you need the get it pixel by pixel as you do it in that method.


Thanks everyone. Got it to work by using ChrisF's link Determine if Alpha Channel is Used in an Image Thanks ChrisF.

Here is my code:

    private bool IsImageTransparent(Bitmap image)
    {
        for (int i = 0; i < image.Width; i++)
            for (int j = 0; j < image.Height; j++)
        {
            var pixel = image.GetPixel(i, j);
            if (pixel.A != 255)
                return true;
        } 
        return false;
    }
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜