Skip to content

Commit efe2cee

Browse files
Added ExtractCel method to extract texture from individual cels (#24)
* Adds ExtractCel method ExtractCel methods allow consumers to extract the contents of a single cel into a texture object without having to flatten the entire frame. * Remove invalid character * Add example of using ExtractCel * Bump version to 1.6.0
1 parent 5bcd1da commit efe2cee

File tree

7 files changed

+213
-3
lines changed

7 files changed

+213
-3
lines changed

Diff for: AsepriteDotNet.sln

+7
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProcessorExample", "example
5858
EndProject
5959
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoGameExample", "examples\MonoGameExample\MonoGameExample.csproj", "{6FA09867-535B-4200-9D0A-88A2987BF033}"
6060
EndProject
61+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExtractCelExample", "examples\ExtractCelExample\ExtractCelExample.csproj", "{11D9BF92-538C-4B1D-B996-22E64F3F3ECF}"
62+
EndProject
6163
Global
6264
GlobalSection(SolutionConfigurationPlatforms) = preSolution
6365
Debug|Any CPU = Debug|Any CPU
@@ -96,6 +98,10 @@ Global
9698
{6FA09867-535B-4200-9D0A-88A2987BF033}.Debug|Any CPU.Build.0 = Debug|Any CPU
9799
{6FA09867-535B-4200-9D0A-88A2987BF033}.Release|Any CPU.ActiveCfg = Release|Any CPU
98100
{6FA09867-535B-4200-9D0A-88A2987BF033}.Release|Any CPU.Build.0 = Release|Any CPU
101+
{11D9BF92-538C-4B1D-B996-22E64F3F3ECF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
102+
{11D9BF92-538C-4B1D-B996-22E64F3F3ECF}.Debug|Any CPU.Build.0 = Debug|Any CPU
103+
{11D9BF92-538C-4B1D-B996-22E64F3F3ECF}.Release|Any CPU.ActiveCfg = Release|Any CPU
104+
{11D9BF92-538C-4B1D-B996-22E64F3F3ECF}.Release|Any CPU.Build.0 = Release|Any CPU
99105
EndGlobalSection
100106
GlobalSection(SolutionProperties) = preSolution
101107
HideSolutionNode = FALSE
@@ -110,6 +116,7 @@ Global
110116
{E43B057B-37B4-4C77-AC25-376380318AEF} = {E55B2B0D-9615-434F-942A-6C496A02E617}
111117
{DAD91498-F650-44C7-AAB8-A3454E084E5D} = {E55B2B0D-9615-434F-942A-6C496A02E617}
112118
{6FA09867-535B-4200-9D0A-88A2987BF033} = {E55B2B0D-9615-434F-942A-6C496A02E617}
119+
{11D9BF92-538C-4B1D-B996-22E64F3F3ECF} = {E55B2B0D-9615-434F-942A-6C496A02E617}
113120
EndGlobalSection
114121
GlobalSection(ExtensibilityGlobals) = postSolution
115122
SolutionGuid = {ABE689B5-F304-403F-A1E4-9973EB2F5FBD}

Diff for: Directory.Build.props

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
<NeutralLanguage>en</NeutralLanguage>
2121
<ImplicitUsings>enable</ImplicitUsings>
2222
<Nullable>enable</Nullable>
23-
<Version>1.5.0</Version>
23+
<Version>1.6.0</Version>
2424
</PropertyGroup>
2525

2626
<!-- Setup Code Analysis using the .editorconfig file -->

Diff for: README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
A Cross Platform C# Library for Reading Aseprite Files
55

66
[![main](https://github.com/AristurtleDev/AsepriteDotNet/actions/workflows/main.yml/badge.svg)](https://github.com/AristurtleDev/AsepriteDotNet/actions/workflows/main.yml)
7-
[![Nuget 1.5.0](https://img.shields.io/nuget/v/AsepriteDotNet?color=blue&style=flat-square)](https://www.nuget.org/packages/AsepriteDotNet/1.5.0)
7+
[![Nuget 1.6.0](https://img.shields.io/nuget/v/AsepriteDotNet?color=blue&style=flat-square)](https://www.nuget.org/packages/AsepriteDotNet/1.6.0)
88
[![License: MIT](https://img.shields.io/badge/📃%20license-MIT-blue?style=flat)](LICENSE)
99
[![Twitter](https://img.shields.io/badge/%20-Share%20On%20Twitter-555?style=flat&logo=twitter)](https://twitter.com/intent/tweet?text=AsepriteDotNet%20by%20%40aristurtledev%0A%0AA%20new%20cross-platform%20library%20in%20C%23%20for%20reading%20Aseprite%20.ase%2F.aseprite%20files.%20https%3A%2F%2Fgithub.com%2FAristurtleDev%2FAsepriteDotNet%0A%0A%23aseprite%20%23dotnet%20%23csharp%20%23oss%0A)
1010
</h1>
@@ -27,7 +27,7 @@ A Cross Platform C# Library for Reading Aseprite Files
2727
# Installation
2828
Install via NuGet
2929
```
30-
dotnet add package AsepriteDotNet --version 1.5.0
30+
dotnet add package AsepriteDotNet --version 1.6.0
3131
```
3232

3333
# Usage

Diff for: examples/ExtractCelExample/ExtractCelExample.csproj

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
</Project>

Diff for: examples/ExtractCelExample/Program.cs

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
using AsepriteDotNet;
2+
using AsepriteDotNet.Aseprite;
3+
using AsepriteDotNet.IO;
4+
5+
AsepriteFile aseFile = AsepriteFileLoader.FromFile("adventurer.aseprite");
6+
Texture head = aseFile.ExtractCel(0, "head");
7+
PngWriter.SaveTo("head.png", head.Size.Width, head.Size.Height, head.Pixels.ToArray());
+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Copyright (c) Christopher Whitley. All rights reserved.
2+
// Licensed under the MIT license.
3+
// See LICENSE file in the project root for full license information.
4+
5+
using AsepriteDotNet.Aseprite.Types;
6+
7+
namespace AsepriteDotNet.Aseprite;
8+
9+
/// <summary>
10+
/// Provides extension methods for <see cref="AsepriteFile"/> instances.
11+
/// </summary>
12+
public static class AsepriteFileExtensions
13+
{
14+
/// <summary>
15+
/// Extracts pixel data from a specific cel in a specified frame of an <see cref="AsepriteFile"/> and returns it as
16+
/// a <see cref="Texture"/>.
17+
/// </summary>
18+
/// <remarks>
19+
/// Cel index of a frame starts at zero with the bottom most and goes up. If a layer in a frame does not have cel
20+
/// data, then the frame does not have that cel. For instance, if your Aseprite file has 10 layers, but frame 0
21+
/// does not have pixels on layer 2, then frame 0 will only have 9 cels and not the full 10 cels.
22+
///
23+
/// Due to this, it may be easier to use the <see cref="ExtractCel(AsepriteFile, int, string, string?)"/> method
24+
/// to specify the layer name to extract the cel from.
25+
/// </remarks>
26+
/// <param name="file">
27+
/// The <see cref="AsepriteFile"/> containing the frame and cel from which to extract the pixel data.
28+
/// </param>
29+
/// <param name="frameIndex">
30+
/// The index of the frame containing the cel from which to extract the pixel data.
31+
/// </param>
32+
/// <param name="celIndex">
33+
/// The index of the cel within the specified frame from which to extract the pixel data.
34+
/// </param>
35+
/// <param name="name">
36+
/// Optional name for the extracted texture. If not provided, a default name is generated.
37+
/// </param>
38+
/// <returns>A <see cref="Texture"/> object containing the extracted pixel data.</returns>
39+
/// <exception cref="ArgumentNullException">Thrown when the input <see cref="AsepriteFile"/> is null.</exception>
40+
public static Texture ExtractCel(this AsepriteFile file, int frameIndex, int celIndex, string? name = null)
41+
{
42+
ArgumentNullException.ThrowIfNull(file);
43+
name ??= $"{file.Name}_frame{frameIndex}_cel{celIndex}";
44+
AsepriteCel cel = file.Frames[frameIndex].Cels[celIndex];
45+
return cel.ExtractCel(name);
46+
47+
}
48+
49+
/// <summary>
50+
/// Extracts pixel data from a specific layer in a specified frame of an <see cref="AsepriteFile"/> and returns it
51+
/// as a <see cref="Texture"/>.
52+
/// </summary>
53+
/// <param name="file">
54+
/// The <see cref="AsepriteFile"/> containing the frame and layer from which to extract the pixel data.
55+
/// </param>
56+
/// <param name="frameIndex">The index of the frame from which to extract the pixel data.</param>
57+
/// <param name="layerName">The name of the layer from which to extract the pixel data.</param>
58+
/// <param name="name">Optional name for the extracted texture. If not provided, a default name is generated.</param>
59+
/// <returns>A <see cref="Texture"/>. object containing the extracted pixel data.</returns>
60+
/// <exception cref="ArgumentNullException">
61+
/// Thrown when the input <see cref="AsepriteFile"/> is <see langword="null"/>.
62+
/// </exception>
63+
/// <exception cref="ArgumentException">Thrown when the specified layer cannot be located.</exception>
64+
public static Texture ExtractCel(this AsepriteFile file, int frameIndex, string layerName, string? name = null)
65+
{
66+
ArgumentNullException.ThrowIfNull(file);
67+
name ??= $"{file.Name}_frame{frameIndex}_{layerName}_cel";
68+
AsepriteCel? cel = null;
69+
AsepriteFrame frame = file.Frames[frameIndex];
70+
Parallel.For(0, frame.Cels.Length, new ParallelOptions() { MaxDegreeOfParallelism = Environment.ProcessorCount }, (i, loopState) =>
71+
{
72+
if (frame.Cels[i].Layer.Name.Equals(layerName, StringComparison.Ordinal))
73+
{
74+
cel = frame.Cels[i];
75+
loopState.Break();
76+
}
77+
});
78+
79+
if (cel is null)
80+
{
81+
throw new ArgumentException($"Unable to locate cel in frame {frameIndex} on a layer called '{layerName}'", layerName);
82+
}
83+
84+
return cel.ExtractCel(name);
85+
}
86+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// Copyright (c) Christopher Whitley. All rights reserved.
2+
// Licensed under the MIT license.
3+
// See LICENSE file in the project root for full license information.
4+
5+
using AsepriteDotNet.Common;
6+
7+
namespace AsepriteDotNet.Aseprite.Types;
8+
9+
/// <summary>
10+
/// Provides extension methods for <see cref="AsepriteCel"/> instances.
11+
/// </summary>
12+
public static class AsepriteCelExtensions
13+
{
14+
/// <summary>
15+
/// Extracts pixel data from an <see cref="AsepriteCel"/> and returns it as a <see cref="Texture"/>.
16+
/// </summary>
17+
/// <param name="cel">The <see cref="AsepriteCel"/> containing the pixel data to extract.</param>
18+
/// <param name="name">
19+
/// Optional name for the extracted <see cref="Texture"/>. If not provided, an empty string is used.
20+
/// </param>
21+
/// <returns>A <see cref="Texture"/> object containing the extracted pixel data.</returns>
22+
/// <exception cref="ArgumentNullException">
23+
/// Thrown when the input <see cref="AsepriteCel"/> is <see langword="null"/>.
24+
/// </exception>
25+
/// <exception cref="InvalidOperationException">
26+
/// Thrown when the <see cref="AsepriteCel"/> does not contain pixel data.
27+
/// </exception>
28+
public static Texture ExtractCel(this AsepriteCel cel, string? name = null)
29+
{
30+
ArgumentNullException.ThrowIfNull(cel);
31+
32+
33+
if (cel is AsepriteImageCel imageCel)
34+
{
35+
return imageCel.ExtractCel(name);
36+
}
37+
38+
if (cel is AsepriteTilemapCel tilemapCel)
39+
{
40+
return tilemapCel.ExtractCel(name);
41+
}
42+
43+
throw new InvalidOperationException("This cel does not contain pixel data");
44+
}
45+
46+
/// <summary>
47+
/// Extracts pixel data from an <see cref="AsepriteImageCel"/> and returns it as a <see cref="Texture"/>.
48+
/// </summary>
49+
/// <param name="cel">The <see cref="AsepriteImageCel"/> containing the pixel data to extract.</param>
50+
/// <param name="name">
51+
/// Optional name for the extracted <see cref="Texture"/>. If not provided, an empty string is used.
52+
/// </param>
53+
/// <returns>A <see cref="Texture"/> object containing the extracted pixel data.</returns>
54+
/// <exception cref="ArgumentNullException">
55+
/// Thrown when the input <see cref="AsepriteImageCel"/> is <see langword="null"/>.
56+
/// </exception>
57+
public static Texture ExtractCel(this AsepriteImageCel cel, string? name = null)
58+
{
59+
ArgumentNullException.ThrowIfNull(cel);
60+
61+
return new Texture(name ?? string.Empty, cel.Size, cel.Pixels.ToArray());
62+
}
63+
64+
/// <summary>
65+
/// Extracts pixel data from an <see cref="AsepriteTilemapCel"/> and returns it as a <see cref="Texture"/>.
66+
/// </summary>
67+
/// <param name="cel">The <see cref="AsepriteTilemapCel"/> containing the pixel data to extract.</param>
68+
/// <param name="name">
69+
/// Optional name for the extracted <see cref="Texture"/>. If not provided, an empty string is used.
70+
/// </param>
71+
/// <returns>A <see cref="Texture"/> object containing the extracted pixel data.</returns>
72+
/// <exception cref="ArgumentNullException">
73+
/// Thrown when the input <see cref="AsepriteTilemapCel"/> is <see langword="null"/>.
74+
/// </exception>
75+
public static Texture ExtractCel(this AsepriteTilemapCel cel, string? name = null)
76+
{
77+
ArgumentNullException.ThrowIfNull(cel);
78+
79+
AsepriteTilemapLayer layer = (AsepriteTilemapLayer)cel.Layer;
80+
AsepriteTileset tileset = layer.Tileset;
81+
Size size = new Size(cel.Size.Width * tileset.TileSize.Width, cel.Size.Height * tileset.TileSize.Height);
82+
Rgba32[] pixels = new Rgba32[size.Width * size.Height];
83+
84+
Parallel.For(0, cel.Tiles.Length, new ParallelOptions() { MaxDegreeOfParallelism = Environment.ProcessorCount }, i =>
85+
{
86+
AsepriteTile tile = cel.Tiles[i];
87+
int column = i % cel.Size.Width;
88+
int row = i / cel.Size.Width;
89+
Parallel.For(0, tileset.Pixels.Length, new ParallelOptions() { MaxDegreeOfParallelism = Environment.ProcessorCount }, j =>
90+
{
91+
int px = (j % tileset.TileSize.Width) + (column + tileset.TileSize.Height);
92+
int py = (j / tileset.TileSize.Width) + (row + tileset.TileSize.Height);
93+
int index = py * size.Width + px;
94+
pixels[index] = tileset.Pixels[j];
95+
});
96+
});
97+
98+
return new Texture(name ?? string.Empty, size, pixels);
99+
}
100+
}

0 commit comments

Comments
 (0)