public Png(Stream s) { if (!Verify(s)) { throw new InvalidDataException("PNG Signature is bogus"); } s.Position += 8; var headerParsed = false; var isPaletted = false; var is24Bit = false; var data = new List <byte>(); for (;;) { var length = IPAddress.NetworkToHostOrder(s.ReadInt32()); var type = Encoding.UTF8.GetString(s.ReadBytes(4)); var content = s.ReadBytes(length); /*var crc = */ s.ReadInt32(); if (!headerParsed && type != "IHDR") { throw new InvalidDataException("Invalid PNG file - header does not appear first."); } using (var ms = new MemoryStream(content)) { switch (type) { case "IHDR": { if (headerParsed) { throw new InvalidDataException("Invalid PNG file - duplicate header."); } Width = IPAddress.NetworkToHostOrder(ms.ReadInt32()); Height = IPAddress.NetworkToHostOrder(ms.ReadInt32()); var bitDepth = ms.ReadUInt8(); var colorType = (PngColorType)ms.ReadByte(); isPaletted = IsPaletted(bitDepth, colorType); is24Bit = colorType == PngColorType.Color; var dataLength = Width * Height; if (!isPaletted) { dataLength *= 4; } Data = new byte[dataLength]; var compression = ms.ReadByte(); /*var filter = */ ms.ReadByte(); var interlace = ms.ReadByte(); if (compression != 0) { throw new InvalidDataException("Compression method not supported"); } if (interlace != 0) { throw new InvalidDataException("Interlacing not supported"); } headerParsed = true; break; } case "PLTE": { Palette = new Color[256]; for (var i = 0; i < length / 3; i++) { var r = ms.ReadByte(); var g = ms.ReadByte(); var b = ms.ReadByte(); Palette[i] = Color.FromArgb(r, g, b); } break; } case "tRNS": { if (Palette == null) { throw new InvalidDataException("Non-Palette indexed PNG are not supported."); } for (var i = 0; i < length; i++) { Palette[i] = Color.FromArgb(ms.ReadByte(), Palette[i]); } break; } case "IDAT": { data.AddRange(content); break; } case "tEXt": { var key = ms.ReadASCIIZ(); EmbeddedData.Add(key, ms.ReadASCII(length - key.Length - 1)); break; } case "IEND": { using (var ns = new MemoryStream(data.ToArray())) { using (var ds = new InflaterInputStream(ns)) { var pxStride = isPaletted ? 1 : is24Bit ? 3 : 4; var srcStride = Width * pxStride; var destStride = Width * (isPaletted ? 1 : 4); var prevLine = new byte[srcStride]; for (var y = 0; y < Height; y++) { var filter = (PngFilter)ds.ReadByte(); var line = ds.ReadBytes(srcStride); for (var i = 0; i < srcStride; i++) { line[i] = i < pxStride ? UnapplyFilter(filter, line[i], 0, prevLine[i], 0) : UnapplyFilter(filter, line[i], line[i - pxStride], prevLine[i], prevLine[i - pxStride]); } if (is24Bit) { // Fold alpha channel into RGB data for (var i = 0; i < line.Length / 3; i++) { Array.Copy(line, 3 * i, Data, y * destStride + 4 * i, 3); Data[y * destStride + 4 * i + 3] = 255; } } else { Array.Copy(line, 0, Data, y * destStride, line.Length); } prevLine = line; } } } if (isPaletted && Palette == null) { throw new InvalidDataException("Non-Palette indexed PNG are not supported."); } return; } } } } }
public Png(Stream s) { if (!Verify(s)) { throw new InvalidDataException("PNG Signature is bogus"); } s.Position += 8; var headerParsed = false; var data = new List <byte>(); Type = SpriteFrameType.Rgba32; while (true) { var length = IPAddress.NetworkToHostOrder(s.ReadInt32()); var type = Encoding.UTF8.GetString(s.ReadBytes(4)); var content = s.ReadBytes(length); /*var crc = */ s.ReadInt32(); if (!headerParsed && type != "IHDR") { throw new InvalidDataException("Invalid PNG file - header does not appear first."); } using (var ms = new MemoryStream(content)) { switch (type) { case "IHDR": { if (headerParsed) { throw new InvalidDataException("Invalid PNG file - duplicate header."); } Width = IPAddress.NetworkToHostOrder(ms.ReadInt32()); Height = IPAddress.NetworkToHostOrder(ms.ReadInt32()); var bitDepth = ms.ReadUInt8(); var colorType = (PngColorType)ms.ReadByte(); if (IsPaletted(bitDepth, colorType)) { Type = SpriteFrameType.Indexed8; } else if (colorType == PngColorType.Color) { Type = SpriteFrameType.Rgb24; } Data = new byte[Width * Height * PixelStride]; var compression = ms.ReadByte(); /*var filter = */ ms.ReadByte(); var interlace = ms.ReadByte(); if (compression != 0) { throw new InvalidDataException("Compression method not supported"); } if (interlace != 0) { throw new InvalidDataException("Interlacing not supported"); } headerParsed = true; break; } case "PLTE": { Palette = new Color[256]; for (var i = 0; i < length / 3; i++) { var r = ms.ReadByte(); var g = ms.ReadByte(); var b = ms.ReadByte(); Palette[i] = Color.FromArgb(r, g, b); } break; } case "tRNS": { if (Palette == null) { throw new InvalidDataException("Non-Palette indexed PNG are not supported."); } for (var i = 0; i < length; i++) { Palette[i] = Color.FromArgb(ms.ReadByte(), Palette[i]); } break; } case "IDAT": { data.AddRange(content); break; } case "tEXt": { var key = ms.ReadASCIIZ(); EmbeddedData.Add(key, ms.ReadASCII(length - key.Length - 1)); break; } case "IEND": { using (var ns = new MemoryStream(data.ToArray())) { using (var ds = new InflaterInputStream(ns)) { var pxStride = PixelStride; var rowStride = Width * pxStride; var prevLine = new byte[rowStride]; for (var y = 0; y < Height; y++) { var filter = (PngFilter)ds.ReadByte(); var line = ds.ReadBytes(rowStride); for (var i = 0; i < rowStride; i++) { line[i] = i < pxStride ? UnapplyFilter(filter, line[i], 0, prevLine[i], 0) : UnapplyFilter(filter, line[i], line[i - pxStride], prevLine[i], prevLine[i - pxStride]); } Array.Copy(line, 0, Data, y * rowStride, rowStride); prevLine = line; } } } if (Type == SpriteFrameType.Indexed8 && Palette == null) { throw new InvalidDataException("Non-Palette indexed PNG are not supported."); } return; } } } } }