/// <summary> /// Creates a new <see cref="AsepriteCelChunk"/> instance. /// <param name="reader"> /// The <see cref="AsepriteReader"/> instance being used to read the /// Aseprite file. /// </param> /// <param name="frame"> /// The <see cref="AsepriteFrame"/> this cel is contained within. /// </param> /// <param name="dataSize"> /// The total byte size of the data for this cel chunk. /// </param> internal AsepriteCelChunk(AsepriteReader reader, AsepriteFrame frame, int dataSize) { // We need to cache the position of the reader before reading any data so we can // calculate the amount of data to read later for the pixel info. long readerPos = reader.BaseStream.Position; LayerIndex = reader.ReadWORD(); X = reader.ReadSHORT(); Y = reader.ReadSHORT(); Opacity = reader.ReadByte(); CelType = (AsepriteCelType)reader.ReadWORD(); // Per ase file spec, ignore next 7 bytes, they are reserved for future use. reader.Ignore(7); if (CelType == AsepriteCelType.Raw || CelType == AsepriteCelType.Compressed) { Width = reader.ReadWORD(); Height = reader.ReadWORD(); // Calculate the remaning data to read in the cel chunk long bytesToRead = dataSize - (reader.BaseStream.Position - readerPos); // Read the remaning bytes into a buffer byte[] buffer = reader.ReadBytes((int)bytesToRead); if (CelType == AsepriteCelType.Raw) { // For raw cel, the buffer is the raw pixel data PixelData = new byte[buffer.Length]; Buffer.BlockCopy(buffer, 0, PixelData, 0, buffer.Length); } else { // For compressed, we need to deflate the buffer. First, we'll put it in a // memory stream to work with MemoryStream compressedStream = new MemoryStream(buffer); // The first 2 bytes of the compressed stream are the zlib header informaiton, // and we need to ignore them before we attempt to deflate _ = compressedStream.ReadByte(); _ = compressedStream.ReadByte(); // Now we can deflate the compressed stream using (MemoryStream decompressedStream = new MemoryStream()) { using (DeflateStream deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress)) { deflateStream.CopyTo(decompressedStream); PixelData = decompressedStream.ToArray(); } } } Pixels = new uint[Width * Height]; if (frame.File.Header.ColorDepth == AsepriteColorDepth.RGBA) { for (int i = 0, b = 0; i < Pixels.Length; i++, b += 4) { Pixels[i] = Utils.BytesToPacked(PixelData[b], PixelData[b + 1], PixelData[b + 2], PixelData[b + 3]); } } else if (frame.File.Header.ColorDepth == AsepriteColorDepth.Grayscale) { for (int i = 0, b = 0; i < Pixels.Length; i++, b += 2) { Pixels[i] = Utils.BytesToPacked(PixelData[b], PixelData[b], PixelData[b], PixelData[b + 1]); } } else if (frame.File.Header.ColorDepth == AsepriteColorDepth.Indexed) { for (int i = 0; i < Pixels.Length; i++) { int paletteIndex = PixelData[i]; if (paletteIndex == frame.File.Header.TransparentIndex) { Pixels[i] = Utils.BytesToPacked(0, 0, 0, 0); } else { AsepritePaletteColor paletteColor = frame.File.Palette.Colors[paletteIndex]; Microsoft.Xna.Framework.Color color = new Microsoft.Xna.Framework.Color(paletteColor.PackedValue); Pixels[i] = Utils.BytesToPacked(paletteColor.Red, paletteColor.Green, paletteColor.Blue, paletteColor.Alpha); } } } else { throw new Exception($"Unrecognized color depth mode. {frame.File.Header.ColorDepth}"); } } else if (CelType == AsepriteCelType.Linked) { ushort linkedFrame = reader.ReadWORD(); // Get a refrence to the cel this cel is linked to. LinkedCel = frame.File.Frames[linkedFrame].Cels .FirstOrDefault(c => c.LayerIndex == LayerIndex); } }