Beispiel #1
0
        /// <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();
        }
Beispiel #2
0
        /// <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();
        }
Beispiel #3
0
        /// <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>
        ///     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);
            }
        }
Beispiel #5
0
        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="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");
                }
            }
        }
Beispiel #7
0
        /// <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);
            }
        }