private int CalculateScanlineLenght(IhdrChunk ihdrChunk) { int scanlineLenght = ihdrChunk.Width; if (ihdrChunk.ColorType == E_ColorType.GRAYSCALE) { scanlineLenght *= 1; } else if (ihdrChunk.ColorType == E_ColorType.GRAYSCALE_ALPHA) { scanlineLenght *= 2; } else if (ihdrChunk.ColorType == E_ColorType.TRUECOLOR) { scanlineLenght *= 3; } else if (ihdrChunk.ColorType == E_ColorType.TRUECOLOR_ALPHA) { scanlineLenght *= 4; } else { scanlineLenght *= 1; } scanlineLenght = scanlineLenght + 1; //Adding filter byte return(scanlineLenght); }
/// <summary> /// Attempts to read 25 bytes of the stream. /// </summary> internal static IhdrChunk Read(Stream stream) { var chunk = new IhdrChunk { Length = BitHelper.ConvertEndian(stream.ReadUInt32()), //Chunk length, 4 bytes. ChunkType = Encoding.ASCII.GetString(stream.ReadBytes(4)) //Chunk type, 4 bytes. }; if (chunk.ChunkType != "IHDR") { throw new Exception("Missing IHDR chunk."); } //var pos = stream.Position; //chunk.ChunkData = stream.ReadBytes(chunk.Length); //stream.Position = pos; //Chunk details + CRC, 13 bytes + 4 bytes. chunk.Width = BitHelper.ConvertEndian(stream.ReadUInt32()); chunk.Height = BitHelper.ConvertEndian(stream.ReadUInt32()); chunk.BitDepth = (byte)stream.ReadByte(); chunk.ColorType = (byte)stream.ReadByte(); chunk.CompressionMethod = (byte)stream.ReadByte(); chunk.FilterMethod = (byte)stream.ReadByte(); chunk.InterlaceMethod = (byte)stream.ReadByte(); chunk.Crc = BitHelper.ConvertEndian(stream.ReadUInt32()); return(chunk); }
private void _read() { _magic = m_io.ReadBytes(8); if (!((KaitaiStream.ByteArrayCompare(Magic, new byte[] { 137, 80, 78, 71, 13, 10, 26, 10 }) == 0))) { throw new ValidationNotEqualError(new byte[] { 137, 80, 78, 71, 13, 10, 26, 10 }, Magic, M_Io, "/seq/0"); } _ihdrLen = m_io.ReadU4be(); if (!(IhdrLen == 13)) { throw new ValidationNotEqualError(13, IhdrLen, M_Io, "/seq/1"); } _ihdrType = m_io.ReadBytes(4); if (!((KaitaiStream.ByteArrayCompare(IhdrType, new byte[] { 73, 72, 68, 82 }) == 0))) { throw new ValidationNotEqualError(new byte[] { 73, 72, 68, 82 }, IhdrType, M_Io, "/seq/2"); } _ihdr = new IhdrChunk(m_io, this, m_root); _ihdrCrc = m_io.ReadBytes(4); _chunks = new List <Chunk>(); { var i = 0; Chunk M_; do { M_ = new Chunk(m_io, this, m_root); _chunks.Add(M_); i++; } while (!(((M_.Type == "IEND") || (M_Io.IsEof)))); } }
public DecodedPNGData(IdatChunk idatChunk, IhdrChunk ihdrChunk, PlteChunk plteChunk, string decodedIdat, string unfilteredDatastreamString, string filteredDatastreamString, byte[] unfilteredIdatDatastream, int scanlineLenght) { IdatChunk = idatChunk; IhdrChunk = ihdrChunk; PlteChunk = plteChunk; DecodedIdat = decodedIdat; UnfilteredDatastreamString = unfilteredDatastreamString; FilteredDatastreamString = filteredDatastreamString; UnfilteredIdatDatastream = unfilteredIdatDatastream; ScanlineLenght = scanlineLenght; }
internal bool ReadFrames() { //Png header, 8 bytes. if (!InternalStream.ReadBytes(8).SequenceEqual(new byte[] { 137, 80, 78, 71, 13, 10, 26, 10 })) { throw new Exception("Invalid file format, expected PNG signature not found."); } //IHDR chunk, 25 bytes. Ihdr = IhdrChunk.Read(InternalStream); //aCTl chunk, 16 bytes. Actl = ActlChunk.Read(InternalStream); //If there's no animation control chunk, it's a normal Png. if (Actl == null) { return(false); } var masterSequence = 0; var frameGroupId = -1; //Read frames. while (InternalStream.CanRead) { //Tries to read any chunk, except IEND. var chunk = Chunk.Read(InternalStream, masterSequence++); //End reached, prematurely or not. if (chunk == null || chunk.ChunkType == "IEND") { break; } //Chunks can be grouped into frames. if (new[] { "fcTL", "fdAT", "IDAT" }.Contains(chunk.ChunkType)) { if (chunk.ChunkType == "fcTL") { frameGroupId++; } chunk.FrameGroupId = frameGroupId; } Chunks.Add(chunk); } return(true); }
public async Task CreatePngFile() { var header = new IhdrChunk(752, 1334, 8, ColorType.TruecolorAlpha); var idat = new IdatChunk(header, rawRgbaData, FilterType); var end = new IendChunk(); var pngFile = new PngFile() { header, idat, end }; using (var ms = new MemoryStream()) await pngFile.WriteFileAsync(ms); }
internal DecodedPNGData DecodeByteArray(byte[] arrayByte) { IdatChunk idatChunk = ReadIdatChunk(arrayByte); PlteChunk plteChunk = ReadPlteChunk(arrayByte); IhdrChunk ihdrChunk = ReadIhdrChunk(arrayByte); int scanlineLenght = CalculateScanlineLenght(ihdrChunk); byte[] unfilteredIdatDatastream = UnfilterImageDatastream(scanlineLenght, idatChunk, ihdrChunk); string decodedBytes = PrintBytesToString(arrayByte, 8); string decodedAscii = PrintBytesToAsciiString(arrayByte); string decodedIdat = PrintBytesToString(idatChunk.DatastreamBytes, scanlineLenght); string unfilteredDatastreamString = PrintBytesToString(unfilteredIdatDatastream, scanlineLenght - 1); string filteredDatastreamString = PrintBytesToString(idatChunk.DatastreamBytes, scanlineLenght); return(new DecodedPNGData(idatChunk, ihdrChunk, plteChunk, decodedIdat, unfilteredDatastreamString, filteredDatastreamString, unfilteredIdatDatastream, scanlineLenght)); }
private void _read() { _magic = m_io.EnsureFixedContents(new byte[] { 137, 80, 78, 71, 13, 10, 26, 10 }); _ihdrLen = m_io.EnsureFixedContents(new byte[] { 0, 0, 0, 13 }); _ihdrType = m_io.EnsureFixedContents(new byte[] { 73, 72, 68, 82 }); _ihdr = new IhdrChunk(m_io, this, m_root); _ihdrCrc = m_io.ReadBytes(4); _chunks = new List <Chunk>(); { var i = 0; Chunk M_; do { M_ = new Chunk(m_io, this, m_root); _chunks.Add(M_); i++; } while (!(((M_.Type == "IEND") || (M_Io.IsEof)))); } }
public async Task Can_write_PNG_file(string imageName, FilterType type, int bitDepth) { var asm = typeof(PngFileTests).GetTypeInfo().Assembly; var resource = asm.GetManifestResourceStream(imageName); byte[] rawRgbaData; using (var ms = new MemoryStream()) { await resource.CopyToAsync(ms); rawRgbaData = ms.ToArray(); } var header = new IhdrChunk(752, 1334, bitDepth, ColorType.TruecolorAlpha); var idat = new IdatChunk(header, rawRgbaData, type); var end = new IendChunk(); var pngFile = new PngFile() { header, idat, end }; var path = Path.Combine(Path.GetDirectoryName(asm.Location), $"Zooey-{bitDepth}-{type}.png"); using (var fs = new FileStream(path, FileMode.Create)) await pngFile.WriteFileAsync(fs); Assert.True(File.Exists(path)); Assert.Equal(3, pngFile.ChunkCount); var result = ToolHelper.RunPngCheck(path); Assert.Equal(0, result.ExitCode); System.Console.Write(result.StandardOutput); }
private byte[] UnfilterImageDatastream(int scanlineLenght, IdatChunk idatChunk, IhdrChunk ihdrChunk) { //int datastreamLenght = chunkIhdr.height * chunkIhdr.width * scanlineLenght; byte[] datastreamIdat = idatChunk.DatastreamBytes; int datastreamLenght = datastreamIdat.Length; // Datastream after unfitering = size - amount of filter bytes int amountOfScanLines = ihdrChunk.Height; byte[] refilteredDatastream = new byte[datastreamLenght]; // Lenght of the scanLine without filter byte int scanLineLenWithoutFilter = scanlineLenght - 1; // pixel size (in bytes) int pixelbytesLenght = 4; E_ColorType colorType = ihdrChunk.ColorType; if (colorType == E_ColorType.GRAYSCALE) { pixelbytesLenght = 1; } else if (colorType == E_ColorType.GRAYSCALE_ALPHA) { pixelbytesLenght = 2; } else if (colorType == E_ColorType.TRUECOLOR) { pixelbytesLenght = 3; } else if (colorType == E_ColorType.TRUECOLOR_ALPHA) { pixelbytesLenght = 4; } //Actual used scanLine and unfilteredScanLine byte[] tempScanline; // Actual filtered scanline byte[] previousReconScanline; // Previous scanline which was reconstructed; byte[] unfilteredScanline = new byte[scanlineLenght - 1]; for (int datastreamScanlineIter = 0; datastreamScanlineIter < amountOfScanLines; datastreamScanlineIter++) { int datastreamIter = (datastreamScanlineIter * (scanlineLenght)); // Each iteration is on another scanline tempScanline = CutFromDatastream(datastreamIdat, datastreamIter, scanlineLenght); //Checking tempScanine FILTER TYPE: if (tempScanline[0] == 0) { // Console.WriteLine("--- Filter method 0: Non ---"); //NOTHING TO DO - FILTER 0 for (int scanLineIter = 1; scanLineIter < tempScanline.Length; scanLineIter++) { unfilteredScanline[scanLineIter - 1] = tempScanline[scanLineIter]; } } else if (tempScanline[0] == 1) { // Console.WriteLine("--- Filter method 1: Sub ---"); //Actual and previous used pixel (in bytes) byte[] tempPixel = new byte[pixelbytesLenght]; byte[] reconAPixel = new byte[pixelbytesLenght]; // recon(a) - pixel on the left to actual pixel (tempPiexl) byte[] reconXPixel = new byte[pixelbytesLenght]; // recon(x) - reconstructed actual pixel; // Pixel on the left from the first pixel in scanline stores 0 values for (int k = 0; k < pixelbytesLenght; k++) { reconAPixel[k] = 0; } for (int scanLineIter = 1; scanLineIter < tempScanline.Length; scanLineIter += pixelbytesLenght) // scanLineIter=1 becouse of filter byte { //Update pixel data for (int k = 0; k < pixelbytesLenght; k++) { tempPixel[k] = tempScanline[scanLineIter + k]; } for (int pixelIter = 0; pixelIter < pixelbytesLenght; pixelIter++) { reconXPixel[pixelIter] = (byte)(reconAPixel[pixelIter] + tempPixel[pixelIter]); unfilteredScanline[scanLineIter + pixelIter - 1] = reconXPixel[pixelIter]; reconAPixel[pixelIter] = reconXPixel[pixelIter]; } } } else if (tempScanline[0] == 2) { // Console.WriteLine("--- Filter method 2: Up ---"); //Actual and previous used pixel (in bytes) byte[] tempPixel = new byte[pixelbytesLenght]; byte[] reconBPixel = new byte[pixelbytesLenght]; // recon(b) - pixel above actual pixel (tempPiexl) if (datastreamScanlineIter == 0) { previousReconScanline = new byte[scanlineLenght]; } else { previousReconScanline = CutFromDatastream(refilteredDatastream, (datastreamIter - datastreamScanlineIter) - (scanlineLenght - 1), scanlineLenght - 1); } for (int scanLineIter = 1; scanLineIter < tempScanline.Length; scanLineIter += pixelbytesLenght) // scanLineIter=1 becouse of filter byte { //Update pixel data for (int k = 0; k < pixelbytesLenght; k++) { tempPixel[k] = tempScanline[scanLineIter + k]; } for (int k = 0; k < pixelbytesLenght; k++) { reconBPixel[k] = previousReconScanline[scanLineIter - 1 + k]; } for (int pixelIter = 0; pixelIter < pixelbytesLenght; pixelIter++) { unfilteredScanline[scanLineIter + pixelIter - 1] = (byte)(reconBPixel[pixelIter] + tempPixel[pixelIter]); } } } else if (tempScanline[0] == 3) { // Console.WriteLine("--- Filter method 3: Average ---"); byte[] tempPixel = new byte[pixelbytesLenght]; //Actual and previous used pixel (in bytes) byte[] reconBPixel = new byte[pixelbytesLenght]; // recon(b) - pixel above actual pixel (tempPiexl) byte[] reconAPixel = new byte[pixelbytesLenght]; // recon(a) - pixel on the left to actual pixel (tempPiexl) byte[] reconXPixel = new byte[pixelbytesLenght]; // recon(x) - reconstructed actual pixel; if (datastreamScanlineIter == 0) { previousReconScanline = new byte[scanlineLenght]; // stores 0 if there is no previously reconstructed scanline } else { previousReconScanline = CutFromDatastream(refilteredDatastream, (datastreamIter - datastreamScanlineIter) - (scanlineLenght - 1), scanlineLenght - 1); } // Pixel on the left from the first pixel in scanline stores 0 values for (int k = 0; k < pixelbytesLenght; k++) { reconAPixel[k] = 0; } for (int scanLineIter = 1; scanLineIter < tempScanline.Length; scanLineIter += pixelbytesLenght) // scanLineIter=1 becouse of filter byte { //Update pixel data for (int k = 0; k < pixelbytesLenght; k++) { tempPixel[k] = tempScanline[scanLineIter + k]; } for (int k = 0; k < pixelbytesLenght; k++) { reconBPixel[k] = previousReconScanline[scanLineIter - 1 + k]; } for (int pixelIter = 0; pixelIter < pixelbytesLenght; pixelIter++) { reconXPixel[pixelIter] = (byte)(tempPixel[pixelIter] + (reconBPixel[pixelIter] + reconAPixel[pixelIter]) / 2); unfilteredScanline[scanLineIter + pixelIter - 1] = reconXPixel[pixelIter]; reconAPixel[pixelIter] = reconXPixel[pixelIter]; } } } else if (tempScanline[0] == 4) { // Console.WriteLine("--- Filter method 4: PaethPredictor ---"); byte[] tempPixel = new byte[pixelbytesLenght]; byte[] reconAPixel = new byte[pixelbytesLenght]; // aPixel - pixel on the left to actual pixel byte[] reconAPixel2 = new byte[pixelbytesLenght]; byte[] reconBPixel = new byte[pixelbytesLenght]; // bPixel - pixel above actual pixel byte[] reconCPixel = new byte[pixelbytesLenght]; // cPixel - pixel on the left to bPixel pixel byte[] reconXPixel = new byte[pixelbytesLenght]; // recon(x) - reconstructed actual pixel; byte[] reconPPixel = new byte[pixelbytesLenght]; if (datastreamScanlineIter == 0) { previousReconScanline = new byte[scanlineLenght]; // stores 0 if there is no previously reconstructed scanline } else { previousReconScanline = CutFromDatastream(refilteredDatastream, (datastreamIter - datastreamScanlineIter) - (scanlineLenght - 1), scanlineLenght - 1); } for (int k = 0; k < pixelbytesLenght; k++) { // Pixel on the left from the first pixel in scanline stores 0 values reconAPixel[k] = 0; // Pixel on the above-left from the first pixel in scanline stores 0 values reconCPixel[k] = 0; // Pixel on the above from the first pixel in scanline stores 0 values reconBPixel[k] = 0; } for (int scanLineIter = 1; scanLineIter < tempScanline.Length; scanLineIter += pixelbytesLenght) // scanLineIter=1 becouse of filter byte { //Update pixel data if (datastreamScanlineIter != 0) { for (int k = 0; k < pixelbytesLenght; k++) { reconBPixel[k] = previousReconScanline[scanLineIter - 1 + k]; } } if (scanLineIter != 1) { for (int k = 0; k < pixelbytesLenght; k++) { reconCPixel[k] = previousReconScanline[scanLineIter - 1 - pixelbytesLenght + k]; } } for (int k = 0; k < pixelbytesLenght; k++) { tempPixel[k] = tempScanline[scanLineIter + k]; reconAPixel2[k] = reconAPixel[k]; reconXPixel[k] = 0; reconPPixel[k] = 0; } for (int pixelIter = 0; pixelIter < pixelbytesLenght; pixelIter++) { int p, pa, pb, pc = 0; p = (reconAPixel2[pixelIter] + reconBPixel[pixelIter] - reconCPixel[pixelIter]); pa = (Math.Abs(p - reconAPixel[pixelIter])); pb = (Math.Abs(p - reconBPixel[pixelIter])); pc = (Math.Abs(p - reconCPixel[pixelIter])); if (pa <= pb && pa <= pc) { reconPPixel[pixelIter] = reconAPixel2[pixelIter]; } else if (pb <= pc) { reconPPixel[pixelIter] = reconBPixel[pixelIter]; } else { reconPPixel[pixelIter] = reconCPixel[pixelIter]; } int x = (tempPixel[pixelIter] + reconPPixel[pixelIter]); reconXPixel[pixelIter] = (byte)(tempPixel[pixelIter] + reconPPixel[pixelIter]); unfilteredScanline[scanLineIter + pixelIter - 1] = reconXPixel[pixelIter]; reconAPixel[pixelIter] = reconXPixel[pixelIter]; } } } else { Console.WriteLine(" WRONG METHOD CODE !!!: " + tempScanline[0] + " Scanline: " + datastreamScanlineIter); //NOTHING TO DO - FILTER 0 //for (int scanLineIter = 1; scanLineIter < tempScanline.Length; scanLineIter++) //{ // unfilteredScanline[scanLineIter - 1] = tempScanline[scanLineIter]; //} } // Fill refilteredDatastream with refiltered scanLine int refilteredDatastreamIter = datastreamScanlineIter * (scanlineLenght - 1); for (int k = 0; k < scanlineLenght - 1; k++) // k=1 becouse of filter byte { refilteredDatastream[refilteredDatastreamIter + k] = unfilteredScanline[k]; } } return(refilteredDatastream); }
private ApngImage(byte[] fileBytes) { // first we try to identify the type of stream MemoryStream ms = new MemoryStream(fileBytes); Bitmap img = new Bitmap(ms); ms.Seek(0, SeekOrigin.Begin); // if GIF (animated or not ) if (ImageFormat.Gif.Equals(img.RawFormat)) { try { NumFrames = img.GetFrameCount(FrameDimension.Time); IsSimplePng = false; DefaultImage._image = new Bitmap(img); for (int i = 0; i < NumFrames; i++) { Frame f = new Frame(); byte[] times = img.GetPropertyItem(0x5100).Value; int dur = BitConverter.ToInt32(times, 4 * i); img.SelectActiveFrame(FrameDimension.Time, i); f._image = img.Clone(new Rectangle(Point.Empty, img.Size), PixelFormat.Format32bppArgb); f._delay = dur / 100.0; _frames.Add(f); } } catch { IsSimplePng = true; DefaultImage._image = img; DefaultImage._delay = 0; NumFrames = 0; _frames.Insert(0, DefaultImage); } img.Dispose(); return; } // if JPEG,... it is a static image if (!ImageFormat.Png.Equals(img.RawFormat)) { IsSimplePng = true; DefaultImage._image = img; DefaultImage._delay = 0; NumFrames = 0; _frames.Insert(0, DefaultImage); return; } // Finally process APNG files // check file signature. if (!Helper.IsBytesEqual(ms.ReadBytes(Frame.Signature.Length), Frame.Signature)) { throw new Exception("File signature incorrect."); } // Read IHDR chunk. IhdrChunk = new IhdrChunk(ms); if (IhdrChunk.ChunkType != "IHDR") { throw new Exception("IHDR chunk must located before any other chunks."); } Bitmap canvas = new Bitmap(IhdrChunk.Width, IhdrChunk.Height, PixelFormat.Format32bppArgb); Graphics graphics = Graphics.FromImage(canvas); // Now let's loop in chunks try { Chunk chunk; Frame frame = null; var otherChunks = new List <OtherChunk>(); var isIdatAlreadyParsed = false; do { if (ms.Position == ms.Length) { throw new Exception("IEND chunk expected."); } chunk = new Chunk(ms); switch (chunk.ChunkType) { case "IHDR": throw new Exception("Only single IHDR is allowed."); // break; // case "bKGD": is not processed case "acTL": if (IsSimplePng) { throw new Exception("acTL chunk must located before any IDAT and fdAT"); } AcTlChunk = new AcTlChunk(chunk); break; case "IDAT": // To be an ApngImage, acTL must located before any IDAT and fdAT. if (AcTlChunk == null) { IsSimplePng = true; } // Only default image has IDAT. DefaultImage.IhdrChunk = IhdrChunk; DefaultImage.AddIdatChunk(new IdatChunk(chunk)); isIdatAlreadyParsed = true; if (DefaultImage.FcTlChunk != null) { _frames.Insert(0, DefaultImage); DefaultImageIsAnimated = true; //graphics.DrawImage(Image.FromStream(DefaultImage.GetStream()), DefaultImage.FcTlChunk.XOffset, DefaultImage.FcTlChunk.YOffset); //DefaultImage._image = (Bitmap)(canvas.Clone()); } break; case "fcTL": // Simple PNG should ignore this. if (IsSimplePng) { continue; } if (frame != null && frame.IdatChunks.Count == 0) { throw new Exception("One frame must have only one fcTL chunk."); } // IDAT already parsed means this fcTL is used by FRAME IMAGE. if (isIdatAlreadyParsed) { // register current frame object and build a new frame object // for next use if (frame != null) { _frames.Add(frame); } frame = new Frame { IhdrChunk = IhdrChunk, FcTlChunk = new FcTlChunk(chunk) }; } // Otherwise this fcTL is used by the DEFAULT IMAGE. else { DefaultImage.FcTlChunk = new FcTlChunk(chunk); } break; case "fdAT": // Simple PNG should ignore this. if (IsSimplePng) { continue; } // fdAT is only used by frame image. if (frame == null || frame.FcTlChunk == null) { throw new Exception("fcTL chunk expected."); } frame.AddIdatChunk(new FdAtChunk(chunk).ToIdatChunk()); //graphics.DrawImage(Image.FromStream(frame.GetStream()), DefaultImage.FcTlChunk.XOffset, DefaultImage.FcTlChunk.YOffset); //DefaultImage._image = (Bitmap)(canvas.Clone()); break; case "IEND": // register last frame object if (frame != null) { _frames.Add(frame); } if (DefaultImage.IdatChunks.Count != 0) { DefaultImage.IendChunk = new IendChunk(chunk); } foreach (var f in _frames) { f.IendChunk = new IendChunk(chunk); } break; default: otherChunks.Add(new OtherChunk(chunk)); break; } } while (chunk.ChunkType != "IEND"); DefaultImage.GetImage(); // to force Default._image generation // Now we should apply every chunk in otherChunks to every frame. //_frames.ForEach(f => otherChunks.ForEach(f.AddOtherChunk)); NumFrames = _frames.Count; for (int j = 0; j < NumFrames; j++) { otherChunks.ForEach(_frames[j].AddOtherChunk); if (_frames[j].FcTlChunk.BlendOp == BlendOps.ApngBlendOpSource) { graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy; } else { graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver; } graphics.DrawImage(Image.FromStream(_frames[j].GetStream()), _frames[j].FcTlChunk.XOffset, _frames[j].FcTlChunk.YOffset); _frames[j]._image = (Bitmap)(canvas.Clone()); if (_frames[j].FcTlChunk.DisposeOp == DisposeOps.ApngDisposeOpBackground) { graphics.Clear(Color.Transparent); } else if (_frames[j].FcTlChunk.DisposeOp == DisposeOps.ApngDisposeOpPrevious && j > 0) { graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy; graphics.DrawImage(_frames[j - 1]._image, Point.Empty); } } } finally { canvas?.Dispose(); graphics?.Dispose(); } }