private void ReadAsOldPalleteA(AsepriteReader reader) { // Read the number of packets ushort numberOfPackets = reader.ReadWORD(); List <AsepritePaletteColor> colors = new List <AsepritePaletteColor>(); for (int i = 0; i < numberOfPackets; i++) { // Read the number of palette entries to skip from the last packet // But we're going to ignore the value since we don't need it. _ = reader.ReadByte(); // Read the number of colors in the packet int numberOfColors = reader.ReadByte(); if (numberOfColors == 0) { numberOfColors = 256; } for (int c = 0; c < numberOfColors; c++) { colors.Add(new AsepritePaletteColor(reader, false)); } } Colors = colors.ToArray(); }
/// <summary> /// Reads a <see cref="AsepriteLayerChunk"/> from the underlying /// stream of the provided <see cref="AsepriteReader"/> instance. /// </summary> /// <param name="reader"> /// The <see cref="AsepriteReader"/> instance being used to read the /// Aseprite file. /// </param> private void ReadLayerChunk(AsepriteReader reader) { AsepriteLayerChunk layer = new AsepriteLayerChunk(reader); File.Layers.Add(layer); reader.LastChunkRead = layer; }
/// <summary> /// Creates a new <see cref="AsepritePaletteColor"/> instance. /// </summary> /// <param name="reader"> /// The <see cref="AsepriteReader"/> instance being used to read the /// Aseprite file. /// </param> internal AsepritePaletteColor(AsepriteReader reader, bool isNewPalette) { if (isNewPalette) { // Only read flag if this is the new palette type. _flag = (AsepritePaletteFlags)reader.ReadWORD(); } Red = reader.ReadByte(); Green = reader.ReadByte(); Blue = reader.ReadByte(); if (isNewPalette) { // Only read alpha value if this is the new palette type Alpha = reader.ReadByte(); } else { Alpha = 255; } PackedValue = Utils.BytesToPacked(Red, Green, Blue, Alpha); // If the HasName flag is present, we need to read the string value, however // this seems to always never exist. Not sure what the field is for, but may // just be some compatibility thing with named palette formats like .gpl. if ((_flag & AsepritePaletteFlags.HasName) != 0) { Name = reader.ReadString(); } }
private void Load() { if (!Directory.Exists(config.Directory)) { throw new DirectoryNotFoundException(config.Directory); } foreach (var fileName in Directory.GetFiles(config.Directory)) { var extension = Path.GetExtension(fileName); if (extension != ".ase" && extension != ".aseprite") { continue; } var name = Path.GetFileNameWithoutExtension(fileName); aseprites.TryGetValue(name, out var sprite); if (sprite == null) { sprite = new Aseprite(); aseprites[name] = sprite; } AsepriteReader.ReadFromFile(fileName, sprite); } }
/// <summary> /// Proces method that is called by the content pipeline. /// </summary> /// <remarks> /// If you are using this processor without the assistance of the Content Pipeline Tool, /// then the other Process(AsepriteImporterResult) overload should be used instead. /// </remarks> /// <param name="input"> /// The <see cref="AsepriteImporterResult"/> instance created as part /// of the import process. /// </param> /// <param name="context"></param> /// <returns> /// A new <see cref="AsepriteDocumentProcessorResult"/> instance containing the result /// of the processing. /// </returns> public override AsepriteDocumentProcessorResult Process(AsepriteImporterResult input, ContentProcessorContext context) { AsepriteDocumentProcessorOptions options = new AsepriteDocumentProcessorOptions { BorderPadding = BorderPadding, IgnoreEmptyFrames = IgnoreEmptyFrames, InnerPadding = InnerPadding, MergeDuplicateFrames = MergeDuplicateFrames, OnlyVisibleLayers = OnlyVisibleLayers, SheetType = SheetType, Spacing = Spacing }; // Read the aseprite document from the stream. AsepriteDocument doc; using (MemoryStream stream = new MemoryStream(input.Data)) { using (AsepriteReader reader = new AsepriteReader(stream)) { doc = new AsepriteDocument(reader);; } } // Convert the document into an AnimationProcessorResult. AsepriteDocumentProcessorResult result = new AsepriteDocumentProcessorResult(doc, options); return(result); }
/// <summary> /// Reads a <see cref="AsepritePaletteChunk"/> from the underlying stream /// of the provided <see cref="AsepriteReader"/> instance. /// </summary> /// <param name="reader"> /// The <see cref="AsepriteReader"/> instance being used to read the /// Aseprite file. /// </param> private void ReadPaletteChunk(AsepriteReader reader) { AsepritePaletteChunk palette = new AsepritePaletteChunk(reader, true); File.Palette = palette; reader.LastChunkRead = palette; }
private void ReadOldPalleteAChunk(AsepriteReader reader) { AsepritePaletteChunk palette = new AsepritePaletteChunk(reader, false); File.Palette = palette; reader.LastChunkRead = palette; }
/// <summary> /// Creates a new <see cref="AsepriteLayerChunk"/> instance. /// </summary> /// <param name="reader"> /// The <see cref="AsepriteReader"/> instance being used to read the /// Aseprite file. /// </param> internal AsepriteLayerChunk(AsepriteReader reader) { // Read the flags that are set for this layer. Flags = (AsepriteLayerFlags)reader.ReadWORD(); // Read the type of layer we're dealing with. Type = (AsepriteLayerType)reader.ReadWORD(); // Read the sub level of this layer. If this layer is not a // child layer, this value will be 0. ChildLevel = reader.ReadWORD(); // Per ase file spec, default layer width in pixels is ignored _ = reader.ReadWORD(); // Per ase file spec, default layer height in pixels is ignored _ = reader.ReadWORD(); // Read the blend mode used by this layer. BlendMode = (AsepriteBlendMode)reader.ReadWORD(); // Read the opacity level for this layer. Opacity = reader.ReadByte(); // Per ase file spec, ignroe next 3 bytes, they are reserved for future use reader.Ignore(3); // Read the name of this layer. Name = reader.ReadString(); }
/// <summary> /// Reads a <see cref="AsepriteSliceChunk"/> from the underlying stream /// of the provided <see cref="AsepriteReader"/> instance. /// </summary> /// <param name="reader"> /// The <see cref="AsepriteReader"/> instance being used to read the /// Aseprite file. /// </param> private void ReadSliceChunk(AsepriteReader reader) { AsepriteSliceChunk slice = new AsepriteSliceChunk(reader); File.Slices.Add(slice); reader.LastChunkRead = slice; }
/// <summary> /// Reads a <see cref="AsepriteCelChunk"/> from the underlying stream /// of the provided <see cref="AsepriteReader"/> instance. /// </summary> /// <param name="reader"> /// The <see cref="AsepriteReader"/> instance being used to read the /// Aseprite file. /// </param> /// <param name="dataSize"> /// The size of the cel chunks data, in bytes. /// </param> private void ReadCelChunk(AsepriteReader reader, int dataSize) { AsepriteCelChunk cel = new AsepriteCelChunk(reader, this, dataSize); Cels.Add(cel); reader.LastChunkRead = cel; }
/// <summary> /// Creates a new <see cref="AsepriteHeader"/> instance. /// </summary> /// <param name="reader"> /// The <see cref="AsepriteReader"/> instance being used to read the /// Aseprite file. /// </param> internal AsepriteHeader(AsepriteReader reader) { // Get the basestream position of the reader long headerStart = reader.BaseStream.Position; // Header is 128 bytes. Calcualte the base stream position that is // at the end of the header long headerEnd = headerStart + 128; // Read but ignore the file size _ = reader.ReadDWORD(); // Read and validate the magic number if (reader.ReadWORD() != 0xA5E0) { throw new Exception($"File given does not appear to be a valid .ase/.aseprite file!"); } FrameCount = reader.ReadWORD(); Width = reader.ReadWORD(); Height = reader.ReadWORD(); ColorDepth = (AsepriteColorDepth)(reader.ReadWORD() / 8); _flags = (AsepriteHeaderFlags)reader.ReadDWORD(); // Per ase file specs, the speed field is deprecated, so we'll // ignroe it. _ = reader.ReadWORD(); // Per ase file specs, next two DWORDs are ignored. _ = reader.ReadDWORD(); _ = reader.ReadDWORD(); TransparentIndex = reader.ReadByte(); // Per ase file specs, next 3 bytes are ignored reader.Ignore(3); ColorCount = reader.ReadWORD(); // We're going to ignore the rest of the header as we don't need // the information. For documentation though, the following is // what we are skipping // // Pixel Width // Pixel Height // X position of the grid // Y position of the grid // Grid width (zero if there is no grid, grid size is 16x16 on Aseprite default) // Grid height (zero if there is no grid // 84 bytes which are reserved for future use. // // Since we are skipping all this, we'll just set the reader's basestream to // the end of the header position we calculated earlier reader.BaseStream.Position = headerEnd; }
/// <summary> /// Creates a new <see cref = "AsepritePaletteChunk" /> instance. /// </ summary > /// < param name="reader"> /// The<see cref="AsepriteReader"/> instance being used to read the /// Aseprite file. /// </param> internal AsepritePaletteChunk(AsepriteReader reader, bool isNewPalette) { if (isNewPalette) { ReadAsNewPalette(reader); } else { ReadAsOldPalleteA(reader); } }
/// <summary> /// Reads a <see cref="AsepriteTagChunk"/> from the underlying stream /// of the provided <see cref="AsepriteReader"/> instance. /// </summary> /// <param name="reader"> /// The <see cref="AsepriteReader"/> instance being used to read the /// Aseprite file. /// </param> private void ReadTagChunk(AsepriteReader reader) { ushort tagCount = reader.ReadWORD(); // Per ase file spec, ignore the next eight bytes, they are reserved for future use. reader.Ignore(8); for (int i = 0; i < tagCount; i++) { AsepriteTagChunk tag = new AsepriteTagChunk(reader); File.Tags.Add(tag); } }
/// <summary> /// Creates a new <see cref="AsepriteDocument"/> instance. /// </summary> /// <param name="reader"> /// The <see cref="AsepriteReader"/> instance being used to /// read the Aseprite document file. /// </param> internal AsepriteDocument(AsepriteReader reader) { Frames = new List <AsepriteFrame>(); Slices = new List <AsepriteSliceChunk>(); Tags = new List <AsepriteTagChunk>(); Layers = new List <AsepriteLayerChunk>(); Header = new AsepriteHeader(reader); for (int i = 0; i < Header.FrameCount; i++) { Frames.Add(new AsepriteFrame(this, reader)); } }
/// <summary> /// Creates a new <see cref="AsepriteSliceChunk"/> instance. /// </summary> /// <param name="reader"> /// The <see cref="AsepriteReader"/> instance being used to read the /// Aseprite file. /// </param> internal AsepriteSliceChunk(AsepriteReader reader) { TotalKeys = (int)reader.ReadDWORD(); Flags = (AsepriteSliceFlags)reader.ReadDWORD(); // Per ase file spec, ignore the next DWORD, it's "reserved" _ = reader.ReadDWORD(); Name = reader.ReadString(); Keys = new AsepriteSliceKey[TotalKeys]; for (int i = 0; i < TotalKeys; i++) { Keys[i] = new AsepriteSliceKey(reader, Flags); } }
/// <summary> /// Reads the userdata values for this chunk from the provided /// reader. /// </summary> /// <param name="reader"> /// The <see cref="AsepriteReader"/> instance being used to read the /// Aseprite file. /// </param> internal void ReadUserData(AsepriteReader reader) { AsepriteUserDataFlags flags = (AsepriteUserDataFlags)reader.ReadDWORD(); if ((flags & AsepriteUserDataFlags.HasText) != 0) { UserDataText = reader.ReadString(); } if ((flags & AsepriteUserDataFlags.HasColor) != 0) { UserDataColor = new byte[4]; UserDataColor[0] = reader.ReadByte(); UserDataColor[1] = reader.ReadByte(); UserDataColor[2] = reader.ReadByte(); UserDataColor[3] = reader.ReadByte(); } }
/// <summary> /// Creates a new <see cref="AsepriteTagChunk"/> instance. /// </summary> /// <param name="reader"> /// The <see cref="AsepriteReader"/> instance being used to read the /// Aseprite file. /// </param> internal AsepriteTagChunk(AsepriteReader reader) { From = reader.ReadWORD(); To = reader.ReadWORD(); Direction = (AsepriteLoopDirection)reader.ReadByte(); // Per ase file spec, ignore next 8 bytes, they are reserved for future use reader.Ignore(8); ColorR = reader.ReadByte(); ColorG = reader.ReadByte(); ColorB = reader.ReadByte(); // Per ase file spec, ignore next byte, it's just an extra one set to zero reader.Ignore(1); Name = reader.ReadString(); }
private void ReadAsNewPalette(AsepriteReader reader) { // Read the size value int size = (int)reader.ReadDWORD(); // Read the first index value. int firstIndex = (int)reader.ReadDWORD(); // Read the last index value. int lastIndex = (int)reader.ReadDWORD(); // Per ase file spec, ignore next 8 bytes, they are reserved for future use reader.Ignore(8); Colors = new AsepritePaletteColor[size]; for (int i = 0; i < lastIndex - firstIndex + 1; i++) { Colors[i] = new AsepritePaletteColor(reader, true); } }
/// <summary> /// Creates a new <see cref="AsepriteSliceKey"/> instance. /// </summary> /// <param name="reader"> /// The <see cref="AsepriteReader"/> instance being used to read the /// Aseprite file. /// </param> /// <param name="flags"> /// The <see cref="AsepriteSliceFlags"/> value of the slice this /// slice key belongs to. /// </param> internal AsepriteSliceKey(AsepriteReader reader, AsepriteSliceFlags flags) { Frame = (int)reader.ReadDWORD(); X = reader.ReadLONG(); Y = reader.ReadLONG(); Width = (int)reader.ReadDWORD(); Height = (int)reader.ReadDWORD(); if ((flags & AsepriteSliceFlags.HasNinePatch) != 0) { CenterX = reader.ReadLONG(); CenterY = reader.ReadLONG(); Width = (int)reader.ReadDWORD(); Height = (int)reader.ReadDWORD(); } if ((flags & AsepriteSliceFlags.HasPivot) != 0) { PivotX = reader.ReadLONG(); PivotY = reader.ReadLONG(); } }
/// <summary> /// Creates a new <see cref="AsepriteFrame"/> instance. /// </summary> /// <param name="file"> /// The <see cref="AsepriteDocument"/> instance this frame belongs to. /// </param> /// <param name="reader"> /// The <see cref="AsepriteReader"/> instance being used to read the /// Aseprite file. /// </param> internal AsepriteFrame(AsepriteDocument file, AsepriteReader reader) { Cels = new List <AsepriteCelChunk>(); File = file; // Ignore the total bytes in frame value, we don't need it _ = reader.ReadDWORD(); // Read and validate the magic number if (reader.ReadWORD() != 0xF1FA) { throw new Exception($"Invalid frame header, please ensure the .ase/.aseprite file is valid"); } // The next field contains the chunk count, but this is the old chunk count field int oldChunkCount = reader.ReadWORD(); // Aseprite stores the frame duration value as milliseconds Duration = reader.ReadWORD(); // Per ase file spec, the next two bytes are reserved for future use reader.Ignore(2); // This field contains the chunk count, but is the new chunk count field. uint newChunkCount = reader.ReadDWORD(); // Per ase file spec, if the new chunk count filed is 0, then we use the old field // value instad. int totalChunks = newChunkCount == 0 ? oldChunkCount : (int)newChunkCount; // A value indicating if the new palette chunk was found. When it is found // then we can skip reading the old palette chunk. bool newPaletteChunkFound = false; for (int i = 0; i < totalChunks; i++) { long chunkStart = reader.BaseStream.Position; uint chunkSize = reader.ReadDWORD(); long chunkEnd = chunkStart + chunkSize; AsepriteChunkType chunkType = (AsepriteChunkType)reader.ReadWORD(); System.Diagnostics.Debug.WriteLine($"Chunk Type: {chunkType}"); switch (chunkType) { case AsepriteChunkType.Layer: ReadLayerChunk(reader); break; case AsepriteChunkType.Cel: ReadCelChunk(reader, (int)chunkSize - 6); break; case AsepriteChunkType.Tags: ReadTagChunk(reader); break; case AsepriteChunkType.OldPaletteA: if (newPaletteChunkFound) { reader.BaseStream.Position = chunkEnd; } else { ReadOldPalleteAChunk(reader); } break; case AsepriteChunkType.Palette: ReadPaletteChunk(reader); newPaletteChunkFound = true; break; case AsepriteChunkType.UserData: ReadUserDataChunk(reader); break; case AsepriteChunkType.Slice: ReadSliceChunk(reader); break; case AsepriteChunkType.OldPaletteB: // Ignore case AsepriteChunkType.CelExtra: // Ignore case AsepriteChunkType.ColorProfile: // Ignore case AsepriteChunkType.Mask: // Ignore case AsepriteChunkType.Path: // Ignore // Since we are ignoreing these chunk types, we need to ensure that the // reader's basestream position is set to where the end of the ignored // chunk would be. reader.BaseStream.Position = chunkEnd; break; default: throw new Exception("Invalid chunk type detected"); } } }
/// <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); } }
/// <summary> /// Reads the <see cref="IAsepriteUserData"/> from the underlying stream /// of the provided <see cref="AsepriteReader"/> instance and applies /// it to the most reacently read <see cref="AsepriteChunk"/> /// </summary> /// <param name="reader"> /// The <see cref="AsepriteReader"/> instance being used to read the /// Aseprite file. /// </param> private void ReadUserDataChunk(AsepriteReader reader) { reader.LastChunkRead.ReadUserData(reader); }