You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
373 lines
13 KiB
C#
373 lines
13 KiB
C#
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<Frame> Frames { get; private set; }
|
|
|
|
private Dictionary<Type, Chunk> chunkCache = new Dictionary<Type, Chunk>();
|
|
|
|
public AseFile(Stream stream)
|
|
{
|
|
BinaryReader reader = new BinaryReader(stream);
|
|
byte[] header = reader.ReadBytes(128);
|
|
|
|
Header = new Header(header);
|
|
Frames = new List<Frame>();
|
|
|
|
while (reader.BaseStream.Position < reader.BaseStream.Length)
|
|
{
|
|
Frames.Add(new Frame(this, reader));
|
|
}
|
|
}
|
|
|
|
|
|
public List<T> GetChunks<T>() where T : Chunk
|
|
{
|
|
List<T> chunks = new List<T>();
|
|
|
|
for (int i = 0; i < this.Frames.Count; i++)
|
|
{
|
|
List<T> cs = this.Frames[i].GetChunks<T>();
|
|
|
|
chunks.AddRange(cs);
|
|
}
|
|
|
|
return chunks;
|
|
}
|
|
|
|
public T GetChunk<T>() where T : Chunk
|
|
{
|
|
if (!chunkCache.ContainsKey(typeof(T)))
|
|
{
|
|
for (int i = 0; i < this.Frames.Count; i++)
|
|
{
|
|
List<T> cs = this.Frames[i].GetChunks<T>();
|
|
|
|
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<Texture2D> frames = new List<Texture2D>();
|
|
|
|
for (int i = 0; i < Frames.Count; i++)
|
|
{
|
|
frames.Add(GetFrame(i));
|
|
}
|
|
|
|
return frames.ToArray();
|
|
}
|
|
|
|
|
|
public Texture2D[] GetLayersAsFrames()
|
|
{
|
|
List<Texture2D> frames = new List<Texture2D>();
|
|
List<LayerChunk> layers = GetChunks<LayerChunk>();
|
|
|
|
for (int i = 0; i < layers.Count; i++)
|
|
{
|
|
List<Texture2D> 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<LayerChunk> layers = GetChunks<LayerChunk>();
|
|
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<Texture2D> GetLayerTexture(int layerIndex, LayerChunk layer)
|
|
{
|
|
|
|
List<LayerChunk> layers = GetChunks<LayerChunk>();
|
|
List<Texture2D> textures = new List<Texture2D>();
|
|
|
|
for (int frameIndex = 0; frameIndex < Frames.Count; frameIndex++)
|
|
{
|
|
Frame frame = Frames[frameIndex];
|
|
List<CelChunk> cels = frame.GetChunks<CelChunk>();
|
|
|
|
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<LayerChunk> layers = GetChunks<LayerChunk>();
|
|
List<CelChunk> cels = frame.GetChunks<CelChunk>();
|
|
|
|
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<FrameTagsChunk> tagChunks = this.GetChunks<FrameTagsChunk>();
|
|
|
|
List<FrameTag> animations = new List<FrameTag>();
|
|
|
|
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<int, MetaData> metadatas = new Dictionary<int, MetaData>();
|
|
|
|
for (int index = 0; index < Frames.Count; index++)
|
|
{
|
|
List<LayerChunk> layers = GetChunks<LayerChunk>();
|
|
List<CelChunk> cels = Frames[index].GetChunks<CelChunk>();
|
|
|
|
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<Rect> spriteRects = new List<Rect>();
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
}
|