Ani.Reader
1.2.6
See the version list below for details.
dotnet add package Ani.Reader --version 1.2.6
NuGet\Install-Package Ani.Reader -Version 1.2.6
<PackageReference Include="Ani.Reader" Version="1.2.6" />
<PackageVersion Include="Ani.Reader" Version="1.2.6" />
<PackageReference Include="Ani.Reader" />
paket add Ani.Reader --version 1.2.6
#r "nuget: Ani.Reader, 1.2.6"
#:package Ani.Reader@1.2.6
#addin nuget:?package=Ani.Reader&version=1.2.6
#tool nuget:?package=Ani.Reader&version=1.2.6
Ani.Reader
Ani.Reader is a cross-platform library designed for extracting animated cursors from .ani files, as well as from embedded resources within .exe and .dll files.
Installation
dotnet add package Ani.Reader
Requirements: .NET Standard 2.0 or later (compatible with .NET Framework 4.6.1+, .NET Core 2.0+, .NET 5+).
Key Features
- Platform-Independent Design: Extracts animated cursors from ANI, EXE, and DLL files without relying on Windows-specific functions, making it fully cross-platform.
- Supports ANI Files and PE Resources: Reads animated cursors from standalone
.anifiles as well as embedded resources within executables and DLLs. - Efficient Memory Usage: Implements lazy frame loading — individual frame image data is not decoded until it is needed.
- Multiple Animation Variants: A single ANI source can contain variants at different sizes and bit depths, each accessible as a separate
AnimationInformation. - Flexible Data Access: Supports reading from file paths, byte arrays, and streams.
- WebP Export: Animated cursors can be exported as animated WebP images via the built-in
WebPCreatorextension.
Getting Started
Reading aniData
var aniReader = new AniReader();
// Reading from a file path (most memory-efficient)
AniData[]? aniFromPath = aniReader.Read("path/to/cursor.ani");
AniData[]? aniFromDll = aniReader.Read("path/to/user32.dll");
AniData[]? aniFromExe = aniReader.Read("path/to/app.exe");
// Reading from a byte array
byte[] aniBytes = File.ReadAllBytes("path/to/cursor.ani");
AniData[]? aniFromBytes = aniReader.Read(aniBytes);
// Reading from a stream (copies the stream for independent access)
using var stream = File.OpenRead("path/to/cursor.ani");
AniData[]? aniFromStream = aniReader.Read(stream: stream, copyStream: true);
// Reading from a stream without copying (as efficient as direct file reading)
using (var streamOrigin = File.OpenRead("path/to/cursor.ani"))
{
AniData[]? aniFromStreamDirect = aniReader.Read(stream: streamOrigin, copyStream: false);
// ✅ This is as memory-efficient as reading directly from a file.
// 🔴 WARNING: All frames must be accessed before closing the stream,
// otherwise an error will occur.
}
copyStream: true→ The stream is copied, allowing access to frames even after the original stream is closed.copyStream: false→ The stream is used directly, making it as memory-efficient as reading from a file, but the stream must remain open while accessing frames.
Note: All
Read()overloads returnnullif the file does not exist, the format is unrecognised, or the data cannot be parsed.
Working with AniData
Read() returns an AniData[] because a single PE file can embed multiple animated cursors. Each AniData represents one animation and exposes its frames and animation variants.
AniData Properties
| Property | Type | Description |
|---|---|---|
Name |
string |
Derived from the file name or PE resource ID |
Origin |
AniOriginFileType |
Executable, Dll, or Ani |
TotalAnimationDuration |
TimeSpan |
Sum of all frame durations |
TotalFrames |
int |
Total number of frames in the sequence |
FrameRate |
float |
Frames per second (60 / DisplayRate) |
Frames |
ReadOnlyCollection<FrameInformation> |
Ordered sequence of frame steps including timing |
Animations |
ReadOnlyCollection<AnimationInformation> |
Available size and bit-depth variants |
FrameInformation Properties
| Property | Type | Description |
|---|---|---|
Position |
int |
Index of this step in the animation sequence |
Start |
TimeSpan |
Time offset when this frame begins |
Duration |
TimeSpan |
How long this frame is displayed |
FrameReference |
AniFrameReference |
Raw byte offset and size within the source stream |
AnimationInformation Properties
| Property | Type | Description |
|---|---|---|
Width |
int |
Frame width in pixels |
Height |
int |
Frame height in pixels |
BitCount |
int |
Bit depth (e.g. 1, 4, 8, 24, 32) |
FrameHotspots |
List<FrameHotspot> |
Cursor hotspot position per frame step |
Retrieving Frame Data
Each AnimationInformation describes one size/depth variant. Use it to retrieve the decoded PNG bytes of a specific frame:
foreach (var aniData in aniDatas)
{
var animation = aniData.Animations[0];
// Get the decoded PNG bytes for a single frame
byte[]? frameBytes = await aniData.GetFrameBytes(animation, aniData.Frames[0]);
}
Selecting the Preferred Animation Variant
AniData.PreferredAnimationIndex() returns the index of the AnimationInformation with the highest quality score, calculated as BitCount × Width × Height.
int preferredIndex = aniData.PreferredAnimationIndex();
var animation = aniData.Animations[preferredIndex];
Saving / Exporting
Saving Frames as PNG
// Save all frames of an animation variant as individual PNG files
await aniData.SaveImages("output/", animation);
Exporting as Animated WebP
// Save as an animated WebP file
await aniData.SaveAsWebP("output/cursor.webp", animation);
// Get the animated WebP as a byte array
byte[]? webpBytes = await aniData.GetWebpBytes(animation);
Configuration
By default, AniReader uses built-in decoders. You can supply a custom AniReaderConfiguration to override any part of the decoding pipeline:
var config = new AniReaderConfiguration
{
AniDecoder = new MyCustomAniDecoder(),
AniPeDecoder = new MyCustomAniPeDecoder(),
IcoReader = new IcoReader()
};
var aniReader = new AniReader(config);
AniReaderConfiguration properties:
| Property | Type | Description |
|---|---|---|
AniDecoder |
IAniDecoder |
Decoder for parsing ANI RIFF data |
AniPeDecoder |
IAniPeDecoder |
Decoder for extracting ANI resources from .exe / .dll files |
IcoReader |
IcoReader |
ICO reader used to decode individual animation frames |
Dependency Injection Support
For applications utilizing Dependency Injection, Ani.Reader provides an extension method to seamlessly register its services with the DI container.
public void ConfigureServices(IServiceCollection services)
{
services.AddAniReader();
}
Dependencies
Ani.Reader is designed with minimal external dependencies to ensure lightweight integration into your projects.
- Ico.Reader
- Magick.NET-Q16-AnyCPU (WebP export only)
- Microsoft.Extensions.DependencyInjection.Abstractions
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 is compatible. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Ico.Reader (>= 1.2.2)
- Magick.NET-Q16-AnyCPU (>= 14.13.1)
- Microsoft.Bcl.HashCode (>= 6.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.8)
-
.NETStandard 2.1
- Ico.Reader (>= 1.2.2)
- Magick.NET-Q16-AnyCPU (>= 14.13.1)
- Microsoft.Bcl.HashCode (>= 6.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.8)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.