Exemplo n.º 1
0
        protected override AsepriteFile Read(ContentReader input, AsepriteFile existingInstance)
        {
            if (existingInstance != null)
            {
                return(existingInstance);
            }

            int width  = input.ReadInt32();
            int height = input.ReadInt32();

            AsepriteFile aseprite = new AsepriteFile {
                Width  = width,
                Height = height
            };

            // Layers
            int                  layersCount = input.ReadInt32();
            AsepriteLayer        layer;
            List <AsepriteLayer> layersByIndex = new List <AsepriteLayer>();

            for (int i = 0; i < layersCount; i++)
            {
                layer = new AsepriteLayer {
                    Name      = input.ReadString(),
                    Opacity   = input.ReadSingle(),
                    BlendMode = (AsepriteLayer.BlendModes)input.ReadInt32(),
                    Visible   = input.ReadBoolean()
                };

                aseprite.Layers.Add(layer);
                // Use this for referencing cels down further
                layersByIndex.Add(layer);
            }

            // Frames
            AsepriteFrame frame;
            AsepriteCel   cel;

            int framesCount = input.ReadInt32();
            int celsCount;
            int celOriginX;
            int celOriginY;
            int celWidth;
            int celHeight;
            int textureWidth  = framesCount * width;
            int textureHeight = layersCount * height;

            Color[] pixelData = new Color[textureWidth * textureHeight];

            for (int f = 0; f < framesCount; f++)
            {
                frame = new AsepriteFrame {
                    Duration = input.ReadSingle()
                };

                // Cels
                celsCount = input.ReadInt32();

                for (int i = 0; i < celsCount; i++)
                {
                    int layerIndex = input.ReadInt32();

                    cel = new AsepriteCel {
                        Layer = layersByIndex[layerIndex]
                                // ClipRect = new Rectangle(f * width, layerIndex * height, width, height)
                    };

                    frame.Cels.Add(cel);

                    // Get info for the texture
                    celOriginX = input.ReadInt32();
                    celOriginY = input.ReadInt32();
                    celWidth   = input.ReadInt32();
                    celHeight  = input.ReadInt32();

                    var addX = cel.X;
                    var addY = cel.Y;

                    for (int celY = celOriginY; celY < celOriginY + celHeight; celY++)
                    {
                        for (int celX = celOriginX; celX < celOriginX + celWidth; celX++)
                        {
                            var pixel = input.ReadColor();
                            var ind   = (f * width) + celX + addX + ((i * height) + celY + addY) * textureWidth;

                            // fixme: debug if check, figure out why texture loads wrong
                            if (ind > -1 && ind < pixelData.Length)
                            {
                                pixelData[ind] = pixel;
                            }
                        }
                    }
                }

                aseprite.Frames.Add(frame);
            }

            // Dump our pixels into the texture
            aseprite.Texture = new Texture2D(GraphicsDevice, textureWidth, textureHeight);
            aseprite.Texture.SetData(pixelData);

            // Animations

            int animationsCount = input.ReadInt32();
            AsepriteAnimation animation;

            for (int i = 0; i < animationsCount; i++)
            {
                animation = new AsepriteAnimation {
                    Name       = input.ReadString(),
                    FirstFrame = input.ReadInt32(),
                    LastFrame  = input.ReadInt32(),
                    Directions = (AsepriteTag.LoopDirections)input.ReadByte()
                };

                aseprite.Animations.Add(animation.Name, animation);
            }

            // If no animations were added then just add one
            // that covers all frames
            if (aseprite.Animations.Count == 0)
            {
                animation = new AsepriteAnimation {
                    Name       = "Idle",
                    FirstFrame = 0,
                    LastFrame  = aseprite.Frames.Count - 1
                };

                aseprite.Animations.Add(animation.Name, animation);
            }

            return(aseprite);
        }
Exemplo n.º 2
0
        public AsepriteFile(string filename, ContentBuildLogger logger)
        {
            using (var stream = File.OpenRead(filename)) {
                var reader = new BinaryReader(stream);

                #region File helpers

                // Helpers for translating the Aseprite file format reference
                // See: https://github.com/aseprite/aseprite/blob/master/docs/ase-file-specs.md
                byte BYTE()
                {
                    return(reader.ReadByte());
                }

                ushort WORD()
                {
                    return(reader.ReadUInt16());
                }

                short SHORT()
                {
                    return(reader.ReadInt16());
                }

                uint DWORD()
                {
                    return(reader.ReadUInt32());
                }

                long LONG()
                {
                    return(reader.ReadInt32());
                }

                string STRING()
                {
                    return(Encoding.UTF8.GetString(BYTES(WORD())));
                }

                byte[] BYTES(int number)
                {
                    return(reader.ReadBytes(number));
                }

                void SEEK(int number)
                {
                    reader.BaseStream.Position += number;
                }

                #endregion

                #region Consume header

                int frameCount;

                {
                    DWORD();

                    // Magic number
                    var magic = WORD();

                    if (magic != 0xA5e0)
                    {
                        throw new Exception("File doesn't appear to be from Aseprite.");
                    }

                    // Basic info
                    frameCount = WORD();

                    Width  = WORD();
                    Height = WORD();

                    Mode = (Modes)(WORD() / 8);

                    logger?.LogMessage($"Cels are {Width}x{Height}, mode is {Mode}");

                    // Ignore a bunch of stuff
                    DWORD();                    // Flags
                    WORD();                     // Speed (deprecated)
                    DWORD();                    // 0
                    DWORD();                    // 0
                    BYTE();                     // Palette entry
                    SEEK(3);                    // Ignore these bytes
                    WORD();                     // Number of colors (0 means 256 for old sprites)
                    BYTE();                     // Pixel width
                    BYTE();                     // Pixel height
                    SEEK(92);                   // For Future
                }

                #endregion

                #region Actual data

                // Some temporary holders
                var colorBuffer = new byte[Width * Height * (int)Mode];
                var palette     = new Color[256];

                IUserData lastUserData = null;

                for (int i = 0; i < frameCount; i++)
                {
                    var frame = new AsepriteFrame();
                    Frames.Add(frame);

                    long frameStart;
                    long frameEnd;
                    int  chunkCount;

                    // Frame header
                    {
                        frameStart = reader.BaseStream.Position;
                        frameEnd   = frameStart + DWORD();
                        WORD();                         // Magic number (always 0xF1FA)

                        chunkCount     = WORD();
                        frame.Duration = WORD() / 1000f;
                        SEEK(6);                         // For future (set to zero)
                    }

                    for (int j = 0; j < chunkCount; j++)
                    {
                        long   chunkStart;
                        long   chunkEnd;
                        Chunks chunkType;

                        // Chunk header
                        {
                            chunkStart = reader.BaseStream.Position;
                            chunkEnd   = chunkStart + DWORD();
                            chunkType  = (Chunks)WORD();
                        }

                        // Layer
                        if (chunkType == Chunks.Layer)
                        {
                            var layer = new AsepriteLayer();
                            layer.Flag       = (AsepriteLayer.Flags)WORD();
                            layer.Type       = (AsepriteLayer.Types)WORD();
                            layer.ChildLevel = WORD();

                            WORD();                             // width
                            WORD();                             // height

                            layer.BlendMode = (AsepriteLayer.BlendModes)WORD();
                            layer.Opacity   = BYTE() / 255f;
                            SEEK(3);
                            layer.Name = STRING();

                            lastUserData = layer;
                            Layers.Add(layer);
                        }
                        else if (chunkType == Chunks.Cel)
                        {
                            // Cell
                            var cel = new AsepriteCel();

                            var layerIndex = WORD();
                            cel.Layer   = Layers[layerIndex];                           // Layer is row (Frame is column)
                            cel.X       = SHORT();
                            cel.Y       = SHORT();
                            cel.Opacity = BYTE() / 255f;

                            var celType = (CelTypes)WORD();
                            SEEK(7);

                            if (celType == CelTypes.RawCel || celType == CelTypes.CompressedImage)
                            {
                                cel.Width  = WORD();
                                cel.Height = WORD();

                                var byteCount = cel.Width * cel.Height * (int)Mode;

                                if (celType == CelTypes.RawCel)
                                {
                                    reader.Read(colorBuffer, 0, byteCount);
                                }
                                else
                                {
                                    SEEK(2);
                                    var deflate = new DeflateStream(reader.BaseStream, CompressionMode.Decompress);

                                    deflate.Read(colorBuffer, 0, byteCount);
                                }

                                cel.Pixels = new Color[cel.Width * cel.Height];
                                ConvertBytesToPixels(colorBuffer, cel.Pixels, palette);
                            }
                            else if (celType == CelTypes.LinkedCel)
                            {
                                var targetFrame = WORD();                                 // Frame position to link with

                                // Grab the cel from a previous frame
                                var targetCel = Frames[targetFrame].Cels.Where(c => c.Layer == Layers[layerIndex]).First();

                                cel.Width  = targetCel.Width;
                                cel.Height = targetCel.Height;
                                cel.Pixels = targetCel.Pixels;
                            }

                            lastUserData = cel;
                            frame.Cels.Add(cel);
                        }
                        else if (chunkType == Chunks.Palette)
                        {
                            // Palette

                            var size  = DWORD();
                            var start = DWORD();
                            var end   = DWORD();
                            SEEK(8);

                            for (int c = 0; c < (end - start) + 1; c++)
                            {
                                var hasName = Calc.IsBitSet(WORD(), 0);
                                palette[start + c] = new Color(BYTE(), BYTE(), BYTE(), BYTE());

                                if (hasName)
                                {
                                    STRING();                                     // Color name
                                }
                            }
                        }
                        else if (chunkType == Chunks.UserData)
                        {
                            // User data

                            if (lastUserData != null)
                            {
                                var flags = DWORD();

                                if (Calc.IsBitSet(flags, 0))
                                {
                                    lastUserData.UserDataText = STRING();
                                }
                                else if (Calc.IsBitSet(flags, 1))
                                {
                                    lastUserData.UserDataColor = new Color(BYTE(), BYTE(), BYTE(), BYTE());
                                }
                            }
                        }
                        else if (chunkType == Chunks.FrameTags)
                        {
                            // Tag (animation reference)

                            var tagsCount = WORD();
                            SEEK(8);

                            for (int t = 0; t < tagsCount; t++)
                            {
                                var tag = new AsepriteTag();

                                tag.From          = WORD();
                                tag.To            = WORD();
                                tag.LoopDirection = (AsepriteTag.LoopDirections)BYTE();
                                SEEK(8);
                                tag.Color = new Color(BYTE(), BYTE(), BYTE(), (byte)255);
                                SEEK(1);
                                tag.Name = STRING();

                                Tags.Add(tag);
                            }
                        }
                        else if (chunkType == Chunks.Slice)
                        {
                            // Slice

                            var slicesCount = DWORD();
                            var flags       = DWORD();
                            DWORD();
                            var name = STRING();

                            for (int s = 0; s < slicesCount; s++)
                            {
                                var slice = new AsepriteSlice();
                                slice.Name    = name;
                                slice.Frame   = (int)DWORD();
                                slice.OriginX = (int)LONG();
                                slice.OriginY = (int)LONG();
                                slice.Width   = (int)DWORD();
                                slice.Height  = (int)DWORD();

                                // 9 slice
                                if (Calc.IsBitSet(flags, 0))
                                {
                                    LONG();                                     // Center X position (relative to slice bounds)
                                    LONG();                                     // Center Y position
                                    DWORD();                                    // Center width
                                    DWORD();                                    // Center height
                                }
                                else if (Calc.IsBitSet(flags, 1))
                                {
                                    // Pivot

                                    slice.Pivot = new Point((int)DWORD(), (int)DWORD());
                                }

                                lastUserData = slice;
                                Slices.Add(slice);
                            }
                        }

                        reader.BaseStream.Position = chunkEnd;
                    }

                    reader.BaseStream.Position = frameEnd;
                }

                #endregion
            }

            if (logger == null)
            {
                return;
            }

            // Log out what we found
            logger.LogMessage("Layers:");

            foreach (var layer in Layers)
            {
                logger.LogMessage($"\t{layer.Name}");
            }

            logger.LogMessage("Animations:");

            foreach (var animation in Tags)
            {
                if (animation.To == animation.From)
                {
                    logger.LogMessage($"\t{animation.Name} => {animation.From + 1}");
                }
                else
                {
                    logger.LogMessage($"\t{animation.Name} => {animation.From + 1} - {animation.To + 1}");
                }
            }
        }