Tedd.FIC 0.1.13

dotnet add package Tedd.FIC --version 0.1.13
                    
NuGet\Install-Package Tedd.FIC -Version 0.1.13
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Tedd.FIC" Version="0.1.13" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Tedd.FIC" Version="0.1.13" />
                    
Directory.Packages.props
<PackageReference Include="Tedd.FIC" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Tedd.FIC --version 0.1.13
                    
#r "nuget: Tedd.FIC, 0.1.13"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Tedd.FIC@0.1.13
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Tedd.FIC&version=0.1.13
                    
Install as a Cake Addin
#tool nuget:?package=Tedd.FIC&version=0.1.13
                    
Install as a Cake Tool

Tedd.FIC

Build NuGet NuGet downloads

Tedd.FIC is a lossless image format for tightly packed RGB and RGBA pixels, with a .NET library and a JavaScript module. Its independent strips can encode and decode in parallel in .NET. The .fic file container starts with FIC followed by a NUL byte, supports optional Deflate, GZip, Zstandard, or Brotli payload compression, and can preserve EXIF metadata.

Across the 5,000-image corpus below, FIC Fast produced 24.6% fewer bytes than SkiaSharp PNG and spent 53× less aggregate time encoding the same decoded pixels. Results depend on image content, codec settings, hardware, and runtime; run the included benchmark on representative images before making a format choice.

Install

dotnet add package Tedd.FIC

The current package targets .NET 11 RC1 or later.

Use

using Tedd.FIC;

// rgb contains width * height * 3 bytes in row-major RGB order.
byte[] file = Fic.Encode(rgb, width, height, channels: 3, effort: FicEffort.Fast);
File.WriteAllBytes("image.fic", file);

byte[] pixels = Fic.Decode(File.ReadAllBytes("image.fic"),
    out int decodedWidth, out int decodedHeight, out int channels);

RGBA uses channels: 4. FicEffort.Default trades encoding time for size; FicEffort.Max tries an additional byte mode. The decoder accepts all effort levels. threads controls strip parallelism; encoded bytes are independent of the thread count.

The default FicCompression.Auto uses no outer compression for FicEffort.Fast, Zstandard quality 2 for FicEffort.Default, and Brotli quality 5 for FicEffort.Max. Explicit FicCompression.Zstd at Fast effort uses quality 1. Choose None, Deflate, Gzip, Zstd, or Brotli with the compression argument. The encoder stores the raw FICQ payload when compression would increase its size. Uncompressed output uses version 00; compressed requests use version 02.

FicEffort.Max encodes with two strip partitions and retains the smaller complete file, including outer compression when requested. This adds encoding time and temporary memory but cannot increase output size relative to its original 64-row pass.

Pass zstdLevel: 3 with compression: FicCompression.Zstd to favor size over encoding speed. The default level is determined by effort; this override does not change the format byte.

byte[] file = Fic.Encode(rgb, width, height, channels: 3,
    effort: FicEffort.Fast, compression: FicCompression.Zstd, zstdLevel: 3);

// Caller-owned output buffer; the outer compressor writes to a Span<byte>.
byte[] buffer = new byte[checked((int)Fic.GetMaxEncodedLength(width, height, 3))];
if (!Fic.TryEncode(rgb, width, height, 3, buffer, out int written,
    effort: FicEffort.Fast, compression: FicCompression.Zstd, zstdLevel: 3))
    throw new InvalidOperationException("Buffer too small");

EXIF is passed as a raw TIFF EXIF payload and returned unchanged. Callers obtain this payload from an image metadata parser; FIC does not interpret EXIF tags.

byte[] file = Fic.Encode(rgba, width, height, channels: 4, exif: exifTiffBytes);
if (Fic.TryGetExif(file, out ReadOnlySpan<byte> exif))
    Console.WriteLine($"EXIF bytes: {exif.Length}");

For untrusted files, use Fic.TryGetInfo to inspect dimensions before pixel allocation, or Fic.Decode with a suitable maxPixels limit. Compressed payloads are limited to 256 MiB after expansion by default; the maxCompressedPayloadBytes argument can change that limit. Fic.TryDecode writes into a caller-owned buffer and verifies the pixel checksum by default.

JavaScript

The JavaScript module works in browsers and Node.js. encode and decode support uncompressed, Deflate, GZip, Zstandard, and Brotli payloads without platform compression APIs. decodeAsync remains available for existing callers. The module accepts all current Fast and Compact strip codecs and verifies the pixel CRC-32C. Its encoder writes Fast-tier literal strips, so its output may be larger than the optimized .NET encoder's output. A requested outer codec is stored only when it reduces the payload size.

The TypeScript source generates the official JavaScript module and its type declarations. Run npm ci --prefix ts and npm run build --prefix ts from the repository root.

import { encode, decode, decodeAsync, getInfo } from './js/fic.js';

const file = encode(rgba, width, height, 4, new Uint8Array(), 'Zstd'); // Uint8Array of RGBA pixels
const info = getInfo(file);                   // dimensions before decoding
const { pixels, width: w, height: h, channels } = decode(file);
const response = await fetch('from-dotnet.fic');
const decodedFromDotNet = await decodeAsync(new Uint8Array(await response.arrayBuffer()));

The browser converter accepts JPEG, PNG, WebP, and FIC files. It reports actual file-size ratios, displays the converted image, and provides a download. Browser-produced WebP and JPEG may be lossy.

Container

Offset Field
0–3 ASCII FIC followed by NUL (00)
4 Container version: 00, 01, or 02
5 Versions 01/02: compression type
5–8 (00), 6–9 (01/02) EXIF payload length, unsigned little-endian 32-bit integer
10–13 (02) Expanded FICQ payload length, unsigned little-endian 32-bit integer
9… (00), 10… (01), 14… (02) Raw EXIF TIFF payload, at most 16 MiB
next… Lossless image payload with strip table and pixel CRC-32C

EXIF is optional (length = 0). Version 00 stores FICQ uncompressed. The decoder also accepts version 01 with 0 = none and 1 = Zstandard under the FIC signature. Version 02 assigns 0 = none, 1 = raw Deflate, 2 = GZip, 3 = Zstandard, 4 = Brotli. Compression covers only FICQ; EXIF bytes precede it. The expanded FICQ length is checked against the configurable 256 MiB default limit before allocation. The decoder rejects unknown versions, compression types, and malformed lengths. The pixel checksum covers decoded pixels, not EXIF bytes.

Benchmarks

Outer compression on real images

The span-based Fic.TryEncode and Fic.TryDecode APIs were measured on 58 losslessly decoded RGBA images: six large camera photos, 32 COCO images, and 20 distinct graphics. Every effort was tested with no outer compression and all four codecs. Deflate and GZip used quality 6, explicit Zstandard used quality 1 at Fast and quality 2 at Default/Max, and Brotli used quality 5. A separate run tested Zstandard quality 3 on the photos. Source decoding, file I/O, and caller-owned output buffer allocation were excluded. Each image/setting had one warmup and three timed runs; times below sum per-image medians. Every output was decoded and compared byte for byte with its input. Measurements used an AMD Ryzen 9 5950X, Windows, and .NET 11 RC1.

FIC effort Outer codec Output MB Saved vs none Encode ms Decode ms
Fast None 59.821 — 255 146
Fast Deflate 54.258 9.30% 2,012 300
Fast GZip 54.259 9.30% 2,013 306
Fast Zstandard q1 54.755 8.47% 324 196
Fast Brotli q5 53.857 9.97% 2,186 462
Default None 45.638 — 331 230
Default Deflate 43.981 3.63% 1,666 355
Default GZip 43.982 3.63% 1,670 359
Default Zstandard q2 44.493 2.51% 388 248
Default Brotli q5 43.712 4.22% 996 449
Max None 45.635 — 339 224
Max Deflate 43.978 3.63% 1,692 355
Max GZip 43.979 3.63% 1,691 359
Max Zstandard q2 44.492 2.50% 411 248
Max Brotli q5 43.709 4.22% 1,028 449

On the six large photos with Fast FICQ, Zstandard q1 saved 9.67% in 237 ms, Zstandard q3 saved 10.34% in 469 ms, Deflate q6 saved 10.78% in 1,482 ms, and Brotli q5 saved 11.60% in 1,961 ms. Zstandard q3 was the fastest measured option to exceed 10% on that photo subset. The 10% threshold was not reached by any tested codec on the full 58-image set. FicEffort.Fast defaults to no outer compression.

The outer compressors use .NET 11 span APIs and write into caller-owned buffers. The whole encoder is not allocation-free: the FICQ strip encoders still allocate internal scratch. The local CSV retains per-image sizes, timings, and measured thread allocations. The selection manifest remains local because it contains private file names. Re-run with tools/OuterCompressionStudy and a corpus selection manifest.

Real-image corpus

The corpus benchmark selects 1,000 decodable images without replacement from each of Google Photos, test2017, train2017, unlabeled2017, and val2017 under I:\Images. It shuffles each folder with seed 20260926, writes the complete selection before timing, and uses that same list for all six codecs. Two unreadable JPEG candidates were excluded while selecting the 5,000-image sample. The manifest stays local because it contains file names. Its SHA-256 is dd7f86a141af583b7c0f28f40cfd5d057d6fb32ec77574cc3543be32b06c8531.

Images retain their original dimensions. Source JPEG decoding is outside the timed sections. Eight independent images run concurrently; reported encode and decode seconds are sums of per-image codec operation times under that load, not wall-clock duration. Speed in MP/s is total megapixels divided by that sum. All codecs receive the same decoded RGBA pixels without source metadata. FIC and PNG outputs are verified byte for byte against the decoded input. JPEG outputs use quality 90 and are lossy, so their sizes are not equivalent-quality comparisons. Results below were measured on an AMD Ryzen 9 5950X (16 cores, 32 logical processors), Windows 10.0.26200, and .NET 11 preview with ImageSharp 3.1.12 and SkiaSharp 4.151.2.

Across all 5,000 images (11,880.902 megapixels):

Codec Output GB Encode s Decode s
FIC Fast 10.607 62.101 30.976
FIC Default 7.904 66.572 63.411
ImageSharp PNG 13.772 4,007.196 164.190
SkiaSharp PNG 14.070 3,312.809 244.089
ImageSharp JPEG q90 (lossy) 2.526 109.804 81.188
SkiaSharp JPEG q90 (lossy) 2.469 246.971 181.548

Lossless output size by folder, in decimal MB (1,000 images per row):

Folder FIC Fast FIC Default ImageSharp PNG SkiaSharp PNG
Google Photos 8,843.9 6,425.5 11,568.4 11,915.1
test2017 439.8 369.0 550.3 538.5
train2017 439.4 368.9 549.7 537.0
unlabeled2017 449.3 377.5 561.1 548.0
val2017 434.6 362.7 542.9 530.9

Summed per-image operation time by folder (1,000 images per row):

Folder FIC Fast encode s SkiaSharp PNG encode s FIC Fast decode s SkiaSharp PNG decode s
Google Photos 53.582 3,035.826 28.163 214.977
test2017 2.132 68.950 0.694 7.220
train2017 2.097 69.046 0.693 7.283
unlabeled2017 2.197 69.535 0.727 7.394
val2017 2.093 69.452 0.699 7.215

FIC Default produced 43.8% fewer bytes than SkiaSharp PNG overall, with 49.8× less summed encode time. The folder selector on the project site shows lossless sizes for FIC and PNG, plus encode and decode speeds for all six codecs.

The original JPEG files total 3.700 GB. Their smaller size reflects lossy compression; the FIC and PNG figures are lossless encodings of the decoded pixels.

dotnet run --project src/Tedd.FIC.Benchmark/Tedd.FIC.Benchmark.csproj -c Release -- --corpus I:\Images 1000 20260926 8

Synthetic reference

Measured on AMD Ryzen 9 5950X, Windows 10.0.26200, .NET 11 RC1, 512×512 RGBA synthetic images. Each cell is the median of three launches, each with 100 warmups and seven timed repetitions per operation. FIC used no outer compression. Encoding includes allocation of the output buffer; decoding includes materializing pixels. The benchmark checks lossless round trips. JPEG at quality 90 is lossy, so its sizes are not directly comparable as equivalent output quality.

Image Codec Bytes Encode ms Decode ms
Gradient FIC Fast 292,045 1.920 0.680
Gradient FIC Default 235,802 1.154 1.052
Gradient ImageSharp PNG 391,293 112.318 5.762
Gradient SkiaSharp PNG 353,053 88.311 6.395
Gradient ImageSharp JPEG q90 63,462 2.973 2.197
Gradient SkiaSharp JPEG q90 58,310 6.798 5.156
Graphics FIC Fast 1,977 1.420 0.075
Graphics FIC Default 1,633 2.735 0.079
Graphics ImageSharp PNG 2,368 7.545 1.260
Graphics SkiaSharp PNG 2,426 15.735 2.569
Graphics ImageSharp JPEG q90 7,352 1.454 1.006
Graphics SkiaSharp JPEG q90 3,686 4.496 2.953
Noise FIC Fast 835,271 1.899 0.662
Noise FIC Default 934,915 7.544 2.300
Noise ImageSharp PNG 900,102 64.602 5.587
Noise SkiaSharp PNG 918,060 50.227 8.072
Noise ImageSharp JPEG q90 235,387 5.566 4.350
Noise SkiaSharp JPEG q90 216,215 10.901 6.856

The benchmark uses ImageSharp 3.1.12 and SkiaSharp 4.151.2. Run it with:

dotnet run --project src/Tedd.FIC.Benchmark/Tedd.FIC.Benchmark.csproj -c Release -- 7

These generated samples are useful for regression checks, not a substitute for a representative photo and graphics corpus. ImageSharp has separate licensing terms; consult its package license before adopting it in an application.

Build

dotnet test src/Tedd.FIC.Tests/Tedd.FIC.Tests.csproj -c Release
dotnet pack src/Tedd.FIC/Tedd.FIC.csproj -c Release

License: MIT.

Product Compatible and additional computed target framework versions.
.NET net11.0 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net11.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Tedd.FIC:

Package Downloads
Tedd.RectanglePacker

The fastest rectangle packer for .NET in the published comparison benchmarks. Highly optimized, high-performance MaxRects and Skyline packing with parallel atlas-size search. Builds texture atlases from raw pixels or Tedd.FIC images with optional padding and caller-owned buffers. Performance varies by workload and hardware.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.13 213 9/29/2026
0.1.8 100 9/26/2026
0.1.2 130 9/30/2026