private byte[] Decode(string path) { var result = new byte[0]; using (var stream = new System.IO.FileStream(path, System.IO.FileMode.Open)) { var header = new PngHeader(stream); var chunks = new List <PngChunk>(); while (stream.Position < stream.Length) { var chunk = new PngChunk(stream); chunks.Add(chunk); if (chunk.Type == "IEND") { break; } } var ihdr = chunks.Where(a => a.Type == "IHDR").FirstOrDefault(); var ihdrData = ihdr.IRDRInfo; Width = ihdrData.Width; Height = ihdrData.Height; var data = new List <byte>(); for (var i = 0; i < chunks.Count; i++) { if (chunks[i].Type == "IDAT") { data.AddRange(chunks[i].Data); } } var pngData = new List <byte>(); // The first byte 0x78 is called CMF, and the value means CM = 8, CINFO = 7. CM = 8 denotes the "deflate" // compression method with a window size up to 32K. This is the method used by gzip and PNG. CINFO = 7 indicates a // 32K window size. // The 0x9C is called FLG and the value means FLEVEL = 2, CHECK = 28. // The information in FLEVEL is not needed for decompression; it is there to indicate if recompression might be worthwhile. // CHECK is set to whatever value is necessary such that CMF*256 + FLG is a multiple of 31 // we'll ignore the 2 bytees using (var deflate = new System.IO.Compression.DeflateStream(new System.IO.MemoryStream(data.Skip(2).Take(data.Count - 2).ToArray()), System.IO.Compression.CompressionMode.Decompress)) { var buffer = new byte[32768]; while (deflate.CanRead) { int count = deflate.Read(buffer, 0, buffer.Length); pngData.AddRange(buffer.Take(count)); if (count < 32768) { break; } } } var bytesPerPixel = 3; _dataStructure = ImageDataStructure.Rgb; switch (ihdrData.ColorType) { case PngChunk.ColorTypeCode.ColorUsedAlphaChannelUsed: bytesPerPixel = 4; _dataStructure = ImageDataStructure.Rgba; break; } result = new byte[Width * Height * bytesPerPixel]; var previousScanLine = new byte[Width * bytesPerPixel]; var scanLine = new byte[Width * bytesPerPixel]; var pngArray = pngData.ToArray(); var dataLineWithFilter = Width * bytesPerPixel + 1; for (var i = 0; i < Height; i++) { // first byte of each line specifies the filter var filter = (PngChunk.FilterTypeCode)pngArray[dataLineWithFilter * i]; // copy the data of the scan line Array.Copy(pngArray, dataLineWithFilter * i + 1, scanLine, 0, scanLine.Length); previousScanLine = Defilter(filter, scanLine, previousScanLine, bytesPerPixel); Array.Copy(previousScanLine, 0, result, scanLine.Length * i, scanLine.Length); } } return(result); }
public bool Save(string path) { try { using (var stream = new System.IO.FileStream(path, System.IO.FileMode.Create)) { var header = new PngHeader(); stream.Write(header.Data, 0, header.Data.Length); PngChunk.Write(stream, "IHDR", PngChunk.CreateIHDRData(new PngChunk.IHDRData() { Width = TransformedWidth, Height = TransformedHeight, BitDepth = 8, ColorType = PngChunk.ColorTypeCode.ColorUsedAlphaChannelUsed, CompressionMethod = PngChunk.CompressionMethodCode.Deflate, FilterMethod = PngChunk.FilterTypeCode.None, InterlaceMethod = PngChunk.InterlaceMethodCode.None })); DataStructure = ImageDataStructure.Rgba; var imageData = new byte[Data.Length + TransformedHeight]; for (int i = 0; i < TransformedHeight; i++) { imageData[i * TransformedWidth * 4 + i] = (byte)PngChunk.FilterTypeCode.None; Array.Copy(Data, i * TransformedWidth * 4, imageData, i * TransformedWidth * 4 + i + 1, TransformedWidth * 4); } using (var idatStream = new MemoryStream()) { idatStream.WriteByte(0x78); idatStream.WriteByte(0x9C); using (var deflate = new System.IO.Compression.DeflateStream(idatStream, System.IO.Compression.CompressionMode.Compress)) { var window = 32768; var count = 0; while (count * window < imageData.Length) { var buffer = imageData.Skip(count * window).Take(window).ToArray(); deflate.Write(buffer, 0, buffer.Length); count++; } deflate.Dispose(); PngChunk.Write(stream, "IDAT", idatStream.ToArray()); } } PngChunk.Write(stream, "IEND", new byte[0]); } } catch (Exception) { return(false); } return(true); }