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; }
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); }
/// <summary> /// Attempts to read XX bytes of the stream. /// </summary> internal static IdatChunk Read(uint length, byte[] array) { var chunk = new IdatChunk { Length = length, //Chunk length, 4 bytes. ChunkType = "IDAT" //Chunk type, 4 bytes. }; using (var stream = new MemoryStream(array)) { //Chunk details, XX bytes. chunk.FrameData = stream.ReadBytes(length); // - 4 } return(chunk); }
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)); }
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); }
internal ApngFrame GetFrame(int index) { //Build each frame using: //Starting blocks: IHDR, tIME, zTXt, tEXt, iTXt, pHYs, sPLT, (iCCP | sRGB), sBIT, gAMA, cHRM, PLTE, tRNS, hIST, bKGD. //Image data: IDAT. //End block: IEND. var chunks = Chunks.Where(w => w.FrameGroupId == index).ToList(); var otherChunks = Chunks.Where(w => w.FrameGroupId == -1 && w.ChunkType != "IDAT").ToList(); if (!chunks.Any()) { return(null); } var frame = new ApngFrame(); //First frame • Second frame //Default image is part of the animation: fcTL + IDAT • fcTL + fdAT //Default image isn't part of the animation: IDAT • fcTL + fdAT if (chunks[0].ChunkType == "fcTL") { var fctl = FctlChunk.Read(chunks[0].Length, chunks[0].ChunkData); frame.Delay = fctl.DelayNum == 0 ? 10 : (int)(fctl.DelayNum / (fctl.DelayDen == 0 ? 100d : fctl.DelayDen) * 1000d); frame.Width = fctl.Width; frame.Height = fctl.Height; frame.Left = fctl.XOffset; frame.Top = fctl.YOffset; frame.ColorType = Ihdr.ColorType; frame.BitDepth = Ihdr.BitDepth; frame.DisposeOp = fctl.DisposeOp; frame.BlendOp = fctl.BlendOp; using (var stream = new MemoryStream()) { //Png signature, 8 bytes. stream.WriteBytes(new byte[] { 137, 80, 78, 71, 13, 10, 26, 10 }); //Image header chunk. 25 bytes. Ihdr.Write(stream, fctl.Width, fctl.Height); //Any other auxiliar chunks. foreach (var other in otherChunks) { other.Write(stream); } //Frame has multiple chunks. if (chunks.Count > 2) { var datas = new List <byte[]>(); //Data chunks. for (var i = 1; i < chunks.Count; i++) { switch (chunks[i].ChunkType) { case "fdAT": { var fdat = FdatChunk.Read(chunks[i].Length, chunks[i].ChunkData); datas.Add(fdat.FrameData); break; } case "IDAT": { var idat = IdatChunk.Read(chunks[i].Length, chunks[i].ChunkData); datas.Add(idat.FrameData); break; } } } //Write combined frame data. var length = datas.Sum(s => s.Length); stream.WriteUInt32(BitHelper.ConvertEndian((uint)length)); //4 bytes. stream.WriteBytes(Encoding.ASCII.GetBytes("IDAT")); //4 bytes. stream.WriteBytes(datas.SelectMany(s => s).ToArray()); //XX bytes. stream.WriteUInt32(BitHelper.ConvertEndian(CrcHelper.Calculate(stream.PeekBytes(stream.Position - (length + 4), length + 4)))); //CRC, 4 bytes. } else { switch (chunks[1].ChunkType) { case "fdAT": { var fdat = FdatChunk.Read(chunks[1].Length, chunks[1].ChunkData); fdat.Write(stream); break; } case "IDAT": { var idat = IdatChunk.Read(chunks[1].Length, chunks[1].ChunkData); idat.Write(stream); break; } } } //End chunk. stream.WriteUInt32(BitHelper.ConvertEndian(0u)); //Chunk length, 4 bytes. stream.WriteBytes(Encoding.ASCII.GetBytes("IEND")); //Chunk type, 4 bytes. stream.WriteUInt32(BitHelper.ConvertEndian(CrcHelper.Calculate(stream.PeekBytes(stream.Position - 4, 4)))); //CRC, 4 bytes. //Gets the whole Png. frame.ImageData = stream.ToArray(); } } else { //This is not supposed to happen. //All chunks with an FrameGroupId are grouped with a starting fcTL, ending with a IDAT or fdAT chunk. LogWriter.Log(new Exception("Missing fcTL on frame number " + index), $"It was not possible to read frame number {index}"); return(null); } return(frame); }
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); }