using Aseprite.Chunks; using Aseprite.Utils; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; using UnityEditor; using UnityEngine; namespace Aseprite { // See file specs here: https://github.com/aseprite/aseprite/blob/master/docs/ase-file-specs.md public class AseFile { public Header Header { get; private set; } public List Frames { get; private set; } private Dictionary chunkCache = new Dictionary(); public AseFile(Stream stream) { BinaryReader reader = new BinaryReader(stream); byte[] header = reader.ReadBytes(128); Header = new Header(header); Frames = new List(); while (reader.BaseStream.Position < reader.BaseStream.Length) { Frames.Add(new Frame(this, reader)); } } public List GetChunks() where T : Chunk { List chunks = new List(); for (int i = 0; i < this.Frames.Count; i++) { List cs = this.Frames[i].GetChunks(); chunks.AddRange(cs); } return chunks; } public T GetChunk() where T : Chunk { if (!chunkCache.ContainsKey(typeof(T))) { for (int i = 0; i < this.Frames.Count; i++) { List cs = this.Frames[i].GetChunks(); if (cs.Count > 0) { chunkCache.Add(typeof(T), cs[0]); break; } } } if (chunkCache.ContainsKey(typeof(T))) { return (T)chunkCache[typeof(T)]; } else { return null; } } public Texture2D[] GetFrames() { List frames = new List(); for (int i = 0; i < Frames.Count; i++) { frames.Add(GetFrame(i)); } return frames.ToArray(); } public Texture2D[] GetLayersAsFrames() { List frames = new List(); List layers = GetChunks(); for (int i = 0; i < layers.Count; i++) { List layerFrames = GetLayerTexture(i, layers[i]); if (layerFrames.Count > 0) frames.AddRange(layerFrames); } return frames.ToArray(); } private LayerChunk GetParentLayer(LayerChunk layer) { if (layer.LayerChildLevel == 0) return null; int childLevel = layer.LayerChildLevel; List layers = GetChunks(); int index = layers.IndexOf(layer); if (index < 0) return null; for (int i = index - 1; i > 0; i--) { if (layers[i].LayerChildLevel == layer.LayerChildLevel - 1) return layers[i]; } return null; } public List GetLayerTexture(int layerIndex, LayerChunk layer) { List layers = GetChunks(); List textures = new List(); for (int frameIndex = 0; frameIndex < Frames.Count; frameIndex++) { Frame frame = Frames[frameIndex]; List cels = frame.GetChunks(); for (int i = 0; i < cels.Count; i++) { if (cels[i].LayerIndex != layerIndex) continue; LayerBlendMode blendMode = layer.BlendMode; float opacity = Mathf.Min(layer.Opacity / 255f, cels[i].Opacity / 255f); bool visibility = layer.Visible; LayerChunk parent = GetParentLayer(layer); while (parent != null) { visibility &= parent.Visible; if (visibility == false) break; parent = GetParentLayer(parent); } if (visibility == false || layer.LayerType == LayerType.Group) continue; textures.Add(GetTextureFromCel(cels[i])); } } return textures; } public Texture2D GetFrame(int index) { Frame frame = Frames[index]; Texture2D texture = Texture2DUtil.CreateTransparentTexture(Header.Width, Header.Height); List layers = GetChunks(); List cels = frame.GetChunks(); cels.Sort((ca, cb) => ca.LayerIndex.CompareTo(cb.LayerIndex)); for (int i = 0; i < cels.Count; i++) { LayerChunk layer = layers[cels[i].LayerIndex]; if (layer.LayerName.StartsWith("@")) //ignore metadata layer continue; LayerBlendMode blendMode = layer.BlendMode; float opacity = Mathf.Min(layer.Opacity / 255f, cels[i].Opacity / 255f); bool visibility = layer.Visible; LayerChunk parent = GetParentLayer(layer); while (parent != null) { visibility &= parent.Visible; if (visibility == false) break; parent = GetParentLayer(parent); } if (visibility == false || layer.LayerType == LayerType.Group) continue; Texture2D celTex = GetTextureFromCel(cels[i]); switch (blendMode) { case LayerBlendMode.Normal: texture = Texture2DBlender.Normal(texture, celTex, opacity); break; case LayerBlendMode.Multiply: texture = Texture2DBlender.Multiply(texture, celTex, opacity); break; case LayerBlendMode.Screen: texture = Texture2DBlender.Screen(texture, celTex); break; case LayerBlendMode.Overlay: texture = Texture2DBlender.Overlay(texture, celTex); break; case LayerBlendMode.Darken: texture = Texture2DBlender.Darken(texture, celTex); break; case LayerBlendMode.Lighten: texture = Texture2DBlender.Lighten(texture, celTex); break; case LayerBlendMode.ColorDodge: texture = Texture2DBlender.ColorDodge(texture, celTex); break; case LayerBlendMode.ColorBurn: texture = Texture2DBlender.ColorBurn(texture, celTex); break; case LayerBlendMode.HardLight: texture = Texture2DBlender.HardLight(texture, celTex); break; case LayerBlendMode.SoftLight: texture = Texture2DBlender.SoftLight(texture, celTex); break; case LayerBlendMode.Difference: texture = Texture2DBlender.Difference(texture, celTex); break; case LayerBlendMode.Exclusion: texture = Texture2DBlender.Exclusion(texture, celTex); break; case LayerBlendMode.Hue: texture = Texture2DBlender.Hue(texture, celTex); break; case LayerBlendMode.Saturation: texture = Texture2DBlender.Saturation(texture, celTex); break; case LayerBlendMode.Color: texture = Texture2DBlender.Color(texture, celTex); break; case LayerBlendMode.Luminosity: texture = Texture2DBlender.Luminosity(texture, celTex); break; case LayerBlendMode.Addition: texture = Texture2DBlender.Addition(texture, celTex); break; case LayerBlendMode.Subtract: texture = Texture2DBlender.Subtract(texture, celTex); break; case LayerBlendMode.Divide: texture = Texture2DBlender.Divide(texture, celTex); break; } } return texture; } public Texture2D GetTextureFromCel(CelChunk cel) { int canvasWidth = Header.Width; int canvasHeight = Header.Height; Texture2D texture = Texture2DUtil.CreateTransparentTexture(canvasWidth, canvasHeight); Color[] colors = new Color[canvasWidth * canvasHeight]; int pixelIndex = 0; int celXEnd = cel.Width + cel.X; int celYEnd = cel.Height + cel.Y; for (int y = cel.Y; y < celYEnd; y++) { if (y < 0 || y >= canvasHeight) { pixelIndex += cel.Width; continue; } for (int x = cel.X; x < celXEnd; x++) { if (x >= 0 && x < canvasWidth) { int index = (canvasHeight - 1 - y) * canvasWidth + x; colors[index] = cel.RawPixelData[pixelIndex].GetColor(); } ++pixelIndex; } } texture.SetPixels(0, 0, canvasWidth, canvasHeight, colors); texture.Apply(); return texture; } public FrameTag[] GetAnimations() { List tagChunks = this.GetChunks(); List animations = new List(); foreach (FrameTagsChunk tagChunk in tagChunks) { foreach (FrameTag tag in tagChunk.Tags) { animations.Add(tag); } } return animations.ToArray(); } public MetaData[] GetMetaData(Vector2 spritePivot, int pixelsPerUnit) { Dictionary metadatas = new Dictionary(); for (int index = 0; index < Frames.Count; index++) { List layers = GetChunks(); List cels = Frames[index].GetChunks(); cels.Sort((ca, cb) => ca.LayerIndex.CompareTo(cb.LayerIndex)); for (int i = 0; i < cels.Count; i++) { int layerIndex = cels[i].LayerIndex; LayerChunk layer = layers[layerIndex]; if (!layer.LayerName.StartsWith(MetaData.MetaDataChar)) //read only metadata layer continue; if (!metadatas.ContainsKey(layerIndex)) metadatas[layerIndex] = new MetaData(layer.LayerName); var metadata = metadatas[layerIndex]; CelChunk cel = cels[i]; Vector2 center = Vector2.zero; int pixelCount = 0; for (int y = 0; y < cel.Height; ++y) { for (int x = 0; x < cel.Width; ++x) { int texX = cel.X + x; int texY = -(cel.Y + y) + Header.Height - 1; var col = cel.RawPixelData[x + y * cel.Width]; if (col.GetColor().a > 0.1f) { center += new Vector2(texX, texY); pixelCount++; } } } if (pixelCount > 0) { center /= pixelCount; var pivot = Vector2.Scale(spritePivot, new Vector2(Header.Width, Header.Height)); var posWorld = (center - pivot) / pixelsPerUnit + Vector2.one * 0.5f / pixelsPerUnit; //center pos in middle of pixels metadata.Transforms.Add(index, posWorld); } } } return metadatas.Values.ToArray(); } public Texture2D GetTextureAtlas() { Texture2D[] frames = this.GetFrames(); Texture2D atlas = Texture2DUtil.CreateTransparentTexture(Header.Width * frames.Length, Header.Height); List spriteRects = new List(); int col = 0; int row = 0; foreach (Texture2D frame in frames) { Rect spriteRect = new Rect(col * Header.Width, atlas.height - ((row + 1) * Header.Height), Header.Width, Header.Height); atlas.SetPixels((int)spriteRect.x, (int)spriteRect.y, (int)spriteRect.width, (int)spriteRect.height, frame.GetPixels()); atlas.Apply(); spriteRects.Add(spriteRect); col++; } return atlas; } } }