/// <returns>Returns if this was the last chunk</returns> public static bool GenerateChunk(NativeArray <byte> data, int length, ref Metadata metadata) { ChunkType?chunkType = null; if (data[0] == 73 && data[1] == 72 && data[2] == 68 && data[3] == 82) { chunkType = ChunkType.IHDR; } else if (data[0] == 80 && data[1] == 76 && data[2] == 84 && data[3] == 69) { chunkType = ChunkType.PLTE; } else if (data[0] == 73 && data[1] == 68 && data[2] == 65 && data[3] == 84) { chunkType = ChunkType.IDAT; } else if (data[0] == 73 && data[1] == 69 && data[2] == 78 && data[3] == 68) { chunkType = ChunkType.IEND; } var isCriticalChunk = (data[0] & 32) == 0; if (chunkType == null) { if (isCriticalChunk) { throw new NotImplementedException(); } return(false); } //var isPublicChunk = (chunkTypeSpan[1] & 32) == 0; //var isCompatiblePNGVersion = (chunkTypeSpan[2] & 32) == 0; //var isSafeToCopy = (chunkTypeSpan[3] & 32) == 1; // CRC var expectedCRC = BitConverterBigEndian.ToUInt32(data.Slice(4 + length, 4)); var calculatedCRC = CRC32.Calculate(data.Slice(0, 4 + length)); Debug.Assert(expectedCRC == calculatedCRC, "CRC check failed. The data seems to be damaged."); // Chunk Data switch (chunkType) { case ChunkType.IHDR: ReadIHDR(data.Slice(4, length), ref metadata); return(false); case ChunkType.IDAT: metadata.Data.AddRange(data.GetSubArray(4, length)); // TODO Replace with a native array return(false); case ChunkType.IEND: return(true); default: throw new Exception($"{nameof(chunkType)} matched no known types. Type was {GetASCIIString(data.Slice(0,4))}"); } }
public void Execute() { //var fileStreamBuffer = FileData.Slice(0, 8); // This signature indicates that the remainder of the datastream contains a single PNG image, consisting of a series of chunks beginning with an IHDR chunk and ending with an IEND chunk. //Debug.Assert(fileStreamBuffer.SequenceEqual(pngSignature), "This doesn't seem to be a PNG"); bool lastChunk; var metadata = new Metadata(FileData.Length); var offset = 8; do { var length = BitConverterBigEndian.ToUInt32(FileData.Slice(offset, 4)); var signedLength = checked ((int)length); lastChunk = ChunkGenerator.GenerateChunk(FileData.GetSubArray(offset + 4, signedLength + 8), signedLength, ref metadata); offset += 12 + signedLength; } while (!lastChunk); //var tmpScanlineColor = new Color32[metadata.Width]; NativeArray <byte> uncompressedData; var metadataData = metadata.Data; var buffer = metadataData.ToArray(); using (var zlibStream = new ZlibStream(new MemoryStream(buffer), Ionic.Zlib.CompressionMode.Decompress)) { using (var memoryStream = new MemoryStream()) { zlibStream.CopyTo(memoryStream); uncompressedData = new NativeArray <byte>(memoryStream.ToArray(), Allocator.Temp); } } Debug.Assert(uncompressedData.Length >= 1); int bytesPerPixel; switch (metadata.ColourType) { case Metadata.ColourTypeEnum.GREYSCALE: bytesPerPixel = 1; throw new NotImplementedException(); case Metadata.ColourTypeEnum.TRUECOLOUR: bytesPerPixel = 3; break; case Metadata.ColourTypeEnum.INDEXED_COLOUR: bytesPerPixel = 1; throw new NotImplementedException(); case Metadata.ColourTypeEnum.GREYSCALE_WITH_ALPHA: bytesPerPixel = 2; throw new NotImplementedException(); case Metadata.ColourTypeEnum.TRUECOLOUR_WITH_ALPHA: bytesPerPixel = 4; break; default: throw new ArgumentOutOfRangeException(); } Width[0] = checked ((int)metadata.Width); Height[0] = checked ((int)metadata.Height); RawTextureData.Resize(Width[0] * Height[0] * 4, NativeArrayOptions.UninitializedMemory); for (var aktLine = 0; aktLine < metadata.Height; aktLine++) { var firstRowBytePosition = aktLine * (Width[0] * bytesPerPixel + 1); //Color32[] lineColors = null; switch ((FilterType)uncompressedData[firstRowBytePosition]) { case FilterType.NONE: break; case FilterType.SUB: FilterLineSub(ref uncompressedData, firstRowBytePosition, Width[0], bytesPerPixel); break; case FilterType.UP: FilterLineUp(ref uncompressedData, firstRowBytePosition, Width[0], bytesPerPixel); break; case FilterType.AVERAGE: FilterLineAverage(ref uncompressedData, firstRowBytePosition, Width[0], bytesPerPixel); break; case FilterType.PAETH: FilterLinePaeth(ref uncompressedData, firstRowBytePosition, Width[0], bytesPerPixel); break; default: throw new ArgumentOutOfRangeException(); } for (var i = 0; i < Width[0]; i++) { RawTextureData[(int)((metadata.Height - 1 - aktLine) * metadata.Width * 4 + i * 4)] = uncompressedData[firstRowBytePosition + 1 + i * bytesPerPixel]; RawTextureData[(int)((metadata.Height - 1 - aktLine) * metadata.Width * 4 + i * 4 + 1)] = uncompressedData[firstRowBytePosition + 2 + i * bytesPerPixel]; RawTextureData[(int)((metadata.Height - 1 - aktLine) * metadata.Width * 4 + i * 4 + 2)] = uncompressedData[firstRowBytePosition + 3 + i * bytesPerPixel]; RawTextureData[(int)((metadata.Height - 1 - aktLine) * metadata.Width * 4 + i * 4 + 3)] = bytesPerPixel == 4 ? uncompressedData[firstRowBytePosition + 4 + i * bytesPerPixel] : byte.MaxValue; } } }