public Png(Stream s) { byte[] internalBuffer = new byte[8]; // Check header signature s.Read(internalBuffer, 0, Signature.Length); for (int i = 0; i < Signature.Length; i++) { if (internalBuffer[i] != Signature[i]) { throw new InvalidDataException("Invalid PNG file - header signature mismatch"); } } // Load image bool headerParsed = false; bool isPaletted = false; bool is24Bit = false; RawList <byte> data = new RawList <byte>(); EmbeddedData = new Dictionary <string, string>(); while (true) { int length = ReadInt32BigEndian(s, ref internalBuffer); s.Read(internalBuffer, 0, 4); string type = Encoding.ASCII.GetString(internalBuffer, 0, 4); if (!headerParsed && type != "IHDR") { throw new InvalidDataException("Invalid PNG file - header does not appear first"); } int blockEndPosition = (int)s.Position + length; switch (type) { case "IHDR": { if (headerParsed) { throw new InvalidDataException("Invalid PNG file - duplicate header"); } Width = ReadInt32BigEndian(s, ref internalBuffer); Height = ReadInt32BigEndian(s, ref internalBuffer); byte bitDepth = s.ReadUInt8(ref internalBuffer); PngColorType colorType = (PngColorType)s.ReadUInt8(ref internalBuffer); isPaletted = IsPaletted(bitDepth, colorType); is24Bit = (colorType == PngColorType.Color); var dataLength = Width * Height; if (!isPaletted) { dataLength *= 4; } Data = new byte[dataLength]; byte compression = s.ReadUInt8(ref internalBuffer); /*byte filter = */ s.ReadUInt8(ref internalBuffer); byte interlace = s.ReadUInt8(ref internalBuffer); if (compression != 0) { throw new InvalidDataException("Compression method (" + compression + ") not supported"); } if (interlace != 0) { throw new InvalidDataException("Interlacing (" + interlace + ") not supported"); } headerParsed = true; break; } case "PLTE": { Palette = new ColorRgba[256]; for (int i = 0; i < length / 3; i++) { byte r = s.ReadUInt8(ref internalBuffer); byte g = s.ReadUInt8(ref internalBuffer); byte b = s.ReadUInt8(ref internalBuffer); Palette[i] = new ColorRgba(r, g, b); } break; } case "tRNS": { if (Palette == null) { throw new InvalidDataException("Non-palette indexed images are not supported"); } for (var i = 0; i < length; i++) { Palette[i].A = s.ReadUInt8(ref internalBuffer); } break; } case "IDAT": { int newLength = data.Count + length; data.Count = newLength; s.Read(data.Data, newLength - length, length); break; } case "tEXt": { byte[] content = new byte[length]; s.Read(content, 0, length); for (int i = 0; i < length; i++) { if (content[i] == 0) { string key = Encoding.ASCII.GetString(content, 0, i); string value = Encoding.ASCII.GetString(content, i + 1, length - (i + 1)); EmbeddedData.Add(key, value); break; } } break; } case "IEND": { using (var ms = new MemoryStream(data.Data)) { ms.Position += 2; using (var ds = new DeflateStream(ms, CompressionMode.Decompress, true)) { int pxStride = (isPaletted ? 1 : (is24Bit ? 3 : 4)); int srcStride = Width * pxStride; int dstStride = Width * (isPaletted ? 1 : 4); byte[] buffer = new byte[srcStride]; byte[] bufferPrev = new byte[srcStride]; for (var y = 0; y < Height; y++) { // Read filter PngFilter filter = (PngFilter)ds.ReadUInt8(ref internalBuffer); // Read data ds.Read(buffer, 0, srcStride); for (var i = 0; i < srcStride; i++) { if (i < pxStride) { buffer[i] = UnapplyFilter(filter, buffer[i], 0, bufferPrev[i], 0); } else { buffer[i] = UnapplyFilter(filter, buffer[i], buffer[i - pxStride], bufferPrev[i], bufferPrev[i - pxStride]); } } if (is24Bit) { for (var i = 0; i < buffer.Length / 3; i++) { Buffer.BlockCopy(buffer, 3 * i, Data, y * dstStride + 4 * i, 3); Data[y * dstStride + 4 * i + 3] = 255; } } else { Buffer.BlockCopy(buffer, 0, Data, y * dstStride, srcStride); } bufferPrev = buffer; } } } return; } default: { //Console.WriteLine("Unknown PNG section: " + type); s.Position += length; break; } } if (s.Position != blockEndPosition) { throw new InvalidDataException("Block " + type + " has incorrect length"); } // Skip CRC s.Position += 4; } }