Skip to content

Commit

Permalink
Finalized Aseprite loading support
Browse files Browse the repository at this point in the history
  • Loading branch information
Terria-K committed Nov 6, 2024
1 parent ed681b6 commit e7a9c07
Show file tree
Hide file tree
Showing 2 changed files with 188 additions and 24 deletions.
28 changes: 25 additions & 3 deletions Riateu/Core/Assets/Assets.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ internal void StartContext()
/// </summary>
/// <param name="basePath">A target folder to look for</param>
/// <returns>An <see cref="Riateu.Graphics.Atlas"/></returns>
public Ref<Atlas> CreateAtlas(string basePath)
public Ref<Atlas> CreateAtlas(string basePath, bool supportAseprite = false)
{
void Crawl(string path)
{
Expand All @@ -82,7 +82,11 @@ void Crawl(string path)
Crawl(directory);
}

var files = Directory.GetFiles(path).Where(x => x.EndsWith("png") || x.EndsWith("gif") || x.EndsWith("qoi"));
var files = Directory.GetFiles(path).Where(
x => x.EndsWith("png") ||
x.EndsWith("gif") ||
x.EndsWith("qoi") ||
(supportAseprite && x.EndsWith("aseprite")));
foreach (var file in files)
{
string name = Path.Join(path, Path.GetFileNameWithoutExtension(file)).Replace('\\', '/')
Expand All @@ -98,12 +102,30 @@ void Crawl(string path)
new AtlasItem($"{name}/{i}", image), image.Width, image.Height));
}
}
else if (Path.GetExtension(file) == ".aseprite")
{
Aseprite aseprite = new Aseprite(file);
if (aseprite.Frames.Length > 1)
{
Span<Image> images = aseprite.RenderFrames(0, aseprite.Frames.Length - 1);
for (int i = 0; i < images.Length; i++)
{
Image image = images[i];
packer.Add(new Packer<AtlasItem>.Item(
new AtlasItem($"{name}/{i}", image), image.Width, image.Height));
}
}
else
{
Image image = aseprite.RenderFrame(0);
packer.Add(new Packer<AtlasItem>.Item(new AtlasItem(name, image), image.Width, image.Height));
}
}
else
{
Image image = new Image(file);
packer.Add(new Packer<AtlasItem>.Item(new AtlasItem(name, image), image.Width, image.Height));
}

}
}
if (basePath.EndsWith(Path.DirectorySeparatorChar))
Expand Down
184 changes: 163 additions & 21 deletions Riateu/Core/Graphics/Aseprite.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@
using System;
using System.Collections.Generic;
using System.IO.Compression;
using System.Diagnostics.CodeAnalysis;

namespace Riateu.Graphics;

[Experimental("RIE001")]
public class Aseprite : IAssets
{
public enum Format : uint
Expand Down Expand Up @@ -86,6 +84,14 @@ public enum CelType : ushort
CompressedTilemap = 3
}

public enum LoopDirection : byte
{
Forward,
Reverse,
PingPong,
PingPongReverse
}

public class Layer
{
public LayerFlags Flags;
Expand All @@ -112,9 +118,22 @@ public class Frame
public List<Cel> Cels = new List<Cel>();
}

public class Tag
{
public string Name;
public int From;
public int To;
public LoopDirection Loop;
public ushort TimesRepeat;
}


private List<Layer> layers = new List<Layer>();
public Frame[] Frames;
public Tag[] Tags = Array.Empty<Tag>();
public Color[] Palletes = Array.Empty<Color>();
public int Width;
public int Height;

public Aseprite(string loadPath)
{
Expand All @@ -129,8 +148,9 @@ public Aseprite(Stream stream)

private void Load(Stream stream)
{
BinaryReader reader = new BinaryReader(stream);
using BinaryReader reader = new BinaryReader(stream);

#pragma warning disable CS8321
byte BYTE() => reader.ReadByte();
ushort WORD() => reader.ReadUInt16();
short SHORT() => reader.ReadInt16();
Expand All @@ -143,10 +163,12 @@ private void Load(Stream stream)
long LONG64() => reader.ReadInt64();
byte[] BYTES(int n) => reader.ReadBytes(n);
string STRING() => reader.ReadString();
Point POINT() => new Point(LONG(), LONG());
Point POINT() => new Point(SHORT(), SHORT());
Point SIZE() => new Point(LONG(), LONG());
Rect RECT() => new Rectangle(POINT(), SIZE());
void SKIP(int n) => BYTES(n);
void SKIP(int n) => reader.BaseStream.Seek(n, SeekOrigin.Current);
void SEEK(long n) => reader.BaseStream.Seek(n, SeekOrigin.Begin);
#pragma warning restore CS8321


uint fileSize = DWORD();
Expand All @@ -157,8 +179,8 @@ private void Load(Stream stream)
}

uint frames = WORD();
uint width = WORD();
uint height = WORD();
Width = WORD();
Height = WORD();
Format colorDepth = (Format)WORD();
DWORD(); // flags
WORD(); // Speed
Expand All @@ -177,10 +199,12 @@ private void Load(Stream stream)

this.Frames = new Frame[(int)frames];

byte[] pixelBuffer = new byte[width * height * ((int)colorDepth / 8)];
byte[] pixelBuffer = new byte[Width * Height * ((int)colorDepth / 8)];

for (int i = 0; i < frames; i++)
{
var frame = new Frame();
Frames[i] = frame;
DWORD(); // bytes in this frame
ushort frameMagic = WORD();
if (frameMagic != 0xF1FA)
Expand Down Expand Up @@ -227,12 +251,25 @@ private void Load(Stream stream)
}
case ChunkType.CelChunk: {
ushort layerIndex = WORD();
var layer = layers[layerIndex];
var celPos = POINT();
var celOpacity = BYTE();
CelType celType = (CelType)WORD();
var celZIndex = SHORT();

if (celType == CelType.CompressedTilemap)
{
SEEK((int)chunkEndData);
continue;
}

Cel cel = new Cel();
cel.Layer = layers[layerIndex];
cel.CelPos = POINT();
cel.Opacity = BYTE();
CelType celType = cel.CelType = (CelType)WORD();
cel.ZIndex = SHORT();
frame.Cels.Add(cel);
cel.Layer = layer;
cel.CelPos = celPos;
cel.Opacity = celOpacity;
cel.CelType = celType;
cel.ZIndex = celZIndex;

SKIP(5);

Expand All @@ -244,15 +281,11 @@ private void Load(Stream stream)
{
cel.Pixels = tcel.Pixels;
}
SKIP((int)chunkEndData);
continue;
}
else if (celType == CelType.CompressedTilemap)
{
SKIP((int)chunkEndData);
SEEK(chunkEndData);
continue;
}


ushort imgWidth = WORD();
ushort imgHeight = WORD();
Color[] pixels = new Color[imgWidth * imgHeight];
Expand Down Expand Up @@ -296,13 +329,122 @@ private void Load(Stream stream)
cel.Pixels = new Image(pixels, imgWidth, imgHeight);
break;
}
case ChunkType.PaletteChunk: {
case ChunkType.TagsChunk: {
var numTags = WORD();
if (numTags > Tags.Length)
{
Array.Resize(ref Tags, numTags);
}
SKIP(8);
for (int j = 0; j < numTags; j++)
{
var from = WORD();
var to = WORD();
LoopDirection dir = (LoopDirection)BYTE();
var repeat = WORD();
SKIP(6);
SKIP(3);
SKIP(1);
var tagName = STRING();
Tag tag = new Tag()
{
Name = tagName,
From = from,
To = to,
TimesRepeat = repeat,
Loop = dir
};
Tags[j] = tag;
}
break;
}
case ChunkType.PaletteChunk:
var len = (int)DWORD();
var first = (int)DWORD();
var last = (int)DWORD();
SKIP(8);

if (len > Palletes.Length)
{
Array.Resize(ref Palletes, len);
}

for (int p = first; p <= last; p++)
{
var flags = WORD();
Palletes[p] = new Color(BYTE(), BYTE(), BYTE(), BYTE());
if ((flags & p) != 0)
{
STRING();
}
}
break;

}

SKIP((int)chunkEndData);
SEEK(chunkEndData);
}
}
}

public Image RenderFrame(int index)
{
var image = new Image(Width, Height);

foreach (var layer in layers)
{
if (!layer.Flags.HasFlag(LayerFlags.Visible))
{
continue;
}

if (Frames[index].Cels.Find(cel => cel.Layer == layer) is not Cel cel)
{
continue;
}

if (cel.Pixels is not Image img)
{
continue;
}

image.CopyFrom(img, 0, 0);
}
return image;
}

public Image[] RenderFrames(int from, int to)
{
var length = to - from + 1;
var images = new Image[length];
for (int i = 0; i < length; i++)
{
images[i] = new Image(Width, Height);
}

foreach (var layer in layers)
{
if (!layer.Flags.HasFlag(LayerFlags.Visible))
{
continue;
}

for (int i = 0; i < length; i++)
{
if (Frames[from + i].Cels.Find(cel => cel.Layer == layer) is not Cel cel)
{
continue;
}

if (cel.Pixels is not Image img)
{
continue;
}

images[i].CopyFrom(img, 0, 0);
}

}
return images;
}
}

0 comments on commit e7a9c07

Please sign in to comment.