6

I have a PNG file which has 8-bit color depth as evidenced by file properties:

enter image description here

Yes, when I open the file

var filePath = "00050-w600.png";
var bitmap = new Bitmap(filePath);
Console.WriteLine(bitmap.PixelFormat);

I get Format32bppArgb. I also looked in the PropertyIdList and PropertyItems properties but didn't see anything obvious.

So how do I extract the Bit Depth from the PNG?

P.S. None of the framework methods seem to work. System.Windows.Media.Imaging.BitmapSource might work but it's only in WPF and .NET Core 3. I need this for .NET 4.x and .NET Core 2.x.

P.P.S. I just needed to know whether the PNG is 8 bit or not, so I wrote a sure fire method to check if anyone needs it - should work in any framework.

public static bool IsPng8BitColorDepth(string filePath)
{
    const int COLOR_TYPE_BITS_8 = 3;
    const int COLOR_DEPTH_8 = 8;
    int startReadPosition = 24;
    int colorDepthPositionOffset = 0;
    int colorTypePositionOffset = 1;

    try
    {
        using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
        {
            fs.Position = startReadPosition; 

            byte[] buffer = new byte[2];
            fs.Read(buffer, 0, 2);

            int colorDepthValue = buffer[colorDepthPositionOffset];
            int colorTypeValue = buffer[colorTypePositionOffset];

            return colorDepthValue == COLOR_DEPTH_8 && colorTypeValue == COLOR_TYPE_BITS_8;
        }
    } 
    catch (Exception)
    {
        return false;
    }
}

   Color   Allowed       Interpretation
   Type    Bit Depths

   0       1,2,4,8,16    Each pixel value is a grayscale level.

   2       8,16          Each pixel value is an R,G,B series.

   3       1,2,4,8       Each pixel value is a palette index;
                         a PLTE chunk must appear.

   4       8,16          Each pixel value is a grayscale level,
                         followed by an alpha channel level.

   6       8,16          Each pixel value is an R,G,B series,
                         followed by an alpha channel level.
Jimi
  • 29,621
  • 8
  • 43
  • 61
AngryHacker
  • 59,598
  • 102
  • 325
  • 594
  • not the problem, but i am assuming you are disposing said bitmap. otherwise you have written a perfect `OutOfMemoryException` application – TheGeneral May 04 '19 at 00:08
  • [Image Property Tag Constants](https://learn.microsoft.com/en-us/windows/desktop/gdiplus/-gdiplus-constant-image-property-tag-constants). See, for example, `PropertyTagSamplesPerPixel`, `PropertyTagMaxSampleValue` in the description section. [Image.GetPropertyItem()](https://learn.microsoft.com/en-us/dotnet/api/system.drawing.image.getpropertyitem) method. – Jimi May 04 '19 at 00:51
  • @Jimi The image doesn't seem to have that property. Here is the sample [image](https://imgur.com/a/2KXc9cB). It only has properties 0x0303, 0x0301, 0x5110, 0x5111, 0x5112, which correspond to PropertyTagSRGBRenderingIntent, PropertyTagGamma. PropertyTagPixelUnit, PropertyTagPixelPerUnitX, PropertyTagPixelPerUnitY. None of these look especially promising. – AngryHacker May 04 '19 at 01:03
  • 1
    (Sorry, I meant byte `0x18`). You can always get that information from the IHDR header: the bit depth it's byte `0x18`. The previous 8 bytes (4 + 4) store the image width and height. You can validate this value comparing the following byte (`0x19`). Your image has a ColorType = 3, meaning the allowed bit depths are (1,2,4,8). – Jimi May 04 '19 at 01:27
  • 1
    I forgot to mention. If you use a `System.Windows.Media.Imaging.BitmapSource` instead of the Bitmap class, this class provides this information for free. See here a Bitmap analysis implementation, using the System.Media & C. namespaces: [How to determine if an Image is Grayscale](https://stackoverflow.com/a/49481035/7444103). – Jimi May 04 '19 at 01:38
  • This is a 8bit greyscale image with 256 colors. It has only 1 channel. Opnening it as the default 32bit argb is not losing anything but some memory. – TaW May 04 '19 at 09:04
  • @Jimi Fetching the value at location worked. I updated the question. – AngryHacker May 05 '19 at 00:56
  • How come you went for byte 25 (`0x19`) instead of byte 24 (`0x18`)? Byte 25 is the ColorType. ColorDepth is byte 24, which would return `8` *inspecting* the bitmap you provided. IMO, you should get that value and *validate* it using byte 25. If byte 25's value != 3 then there's a problem even if byte 24 returned 8. – Jimi May 05 '19 at 02:12
  • @Jimi Oh, ok, I misunderstood. I amended the code. However, one thing I noticed is that byte 24 is 8 regardless of whether the image is 8 bit or 32 bit (but byte 25 is 6 in that case). Why is that? – AngryHacker May 06 '19 at 16:52
  • I've edited your question, adding some informations on this matter (just remove it you don't care to have it here). As described in that table, if byte 24 = 8 and byte 25 = 6, it means that the Image is an RGB table followed by an Alpha map, thus `Bit Depth = 8 x [R.G.B] => 8 x 3 = 32`. If byte `24 = 8` and byte `25 = 3`, it's a paletted bitmap where each pixel is a palette entry, so `Bit Depth = 8 x [P(e)] => 8 x 1 = 8` – Jimi May 07 '19 at 09:32
  • @Jimi Ah, ok, makes sense now. And just to clarify, you meant `Bit Depth = 8 x [R.G.B.A] => 8 x 4 = 32`, correct? – AngryHacker May 07 '19 at 16:28
  • 1
    Yes, sorry, that's sloppy editing: I wrote more than what's allowed then cut the wrong piece. Quoting myself: *an RGB table followed by an Alpha map* is of course = 4 *pieces*, not 3 :) – Jimi May 07 '19 at 18:10
  • 1
    Related: there's a bug in the .Net framework where any 8-bit png that has a palette transparency information (tRNS) chunk is loaded as being high colour. See [this question and its answer](https://stackoverflow.com/q/24074641/395685). – Nyerguds Jul 25 '19 at 04:59
  • By the way, given how StackOverflow works, you should post that `P.P.S.` part as answer and accept it. Though, you might want to add a little header check to ensure the input is actually a png file. The current code looks _very_ naive in that aspect. – Nyerguds Jul 25 '19 at 08:16

0 Answers0