Repository navigation
Sdcb.SimdCodecs 1.1.0
Sdcb.SimdCodecs 1.1.0 adds two codecs, BMP and ICO / CUR, each with a decoder and an encoder. Both are pure managed, NativeAOT- and browser-wasm-compatible, and SIMD-accelerated, with a bit-identical scalar fallback.
dotnet add package Sdcb.SimdCodecs # the facade now decodes .bmp, .ico and .cur too
dotnet add package Sdcb.SimdCodecs.Bmp # or just the codec you need
dotnet add package Sdcb.SimdCodecs.IcoNew: Sdcb.SimdCodecs.Bmp
- Decode handles every DIB variant in practical use:
- Headers: OS/2 1.x core, OS/2 2.x (16–64 bytes), Windows
BITMAPINFOHEADER, and V2–V5. - Pixel formats: 1/2/4/8-bit palettes, and 16/24/32-bit pixels with arbitrary
BITFIELDS/ALPHABITFIELDSmasks. - Compression: RLE4 and RLE8 with deltas, OS/2 RLE24 and Huffman 1D, and embedded PNG.
- Also: top-down rows, V5 embedded ICC profiles, OS/2 bitmap arrays, and bare DIBs.
DecodeTo/TryDecodeIntowrite into your buffer in any pixel layout and allocate nothing.
- Headers: OS/2 1.x core, OS/2 2.x (16–64 bytes), Windows
- Encode writes 1/4/8-bit palettes, RLE4 and RLE8, 5-5-5 and 5-6-5, 24-bit and 32-bit BGRA.
- Palettes are exact when the image has few enough colours and quantised otherwise.
- Headers can be core, V3, V4 or V5, and V5 headers can carry an ICC profile.
- A
Streamoverload writes one row at a time.
- Where Windows Imaging Component (WIC), ImageMagick and browsers disagree, the package documents its choice in a Decisions table in its README. One example is whether the 4th byte of a 32-bit
BI_RGBpixel is alpha.
New: Sdcb.SimdCodecs.Ico
- Reads and writes Windows icons (
.ico) and cursors (.cur).- Each entry can be PNG or a DIB with an AND mask.
- Cursors carry hot spots.
- The facade treats each entry as a frame (
TryOpenFrames).TryDecodereturns the largest entry, breaking ties by colour depth. ImageFormat.Curis new..bmp,.icoand.curfiles now decode through the facade instead of being only recognised.
Performance
Measured single-threaded on an AMD Ryzen 7 5800X with .NET 10, using 1920×1080 images:
| Sdcb.SimdCodecs | WIC | ImageSharp 3.1.12 | |
|---|---|---|---|
| BMP decode, geometric mean of 22 layouts (time relative to WIC) | 0.20× | 1× | 1.62× |
| BMP encode, 5-6-5 | 2.5 ms | — | 14.4 ms |
| BMP encode, 8-bit palette from a photo (quantised) | 97 ms | — | 208 ms |
WIC encodes only 24- and 32-bit BMP, and our encoder is more than 20× faster at both. ImageSharp is still faster at one thing: writing 24- or 32-bit output to a stream from RGBA input. It takes 0.28 ms and 0.23 ms against our 0.33 ms and 0.41 ms. From B, G, R(, A) input, we are faster (0.20 ms) or on par (0.23 ms). Full tables are in benchmarks/Sdcb.SimdCodecs.Bmp.Benchmarks/results.
Correctness
- Output matches WIC pixel for pixel on a corpus covering every BMP variant, and on every entry of the test icons. It is also checked against Magick.NET and against independently hand-assembled streams.
- Each vector kernel produces identical output on its AVX2, Vector128 and scalar paths. NativeAOT and Mono AOT produce the same output as JIT.
- The decoder passed a 300,000-iteration fuzz run.
Other changes
- PNG:
TryReadInfo/TryDecodeused to throw on an IHDR whose bit depth is not allowed for its colour type. They now returnfalse, as theTry*contract requires. The BMP fuzzer found this through embedded PNG. - GIF: The palette quantizer is now shared with BMP. Its palette lookup is faster on large images, and the output is byte-for-byte unchanged.
Packages
Sdcb.SimdCodecs (facade), .Abstractions, .Jpeg, .Jpeg.GainMap, .Jpeg.IccInjector, .Png, .Tiff, .Webp, .Gif, .Bmp, .Ico, .Jpeg2000, .Jbig2, .Exif, .Color.Icc. All 15 packages are on NuGet under the MIT licence.
Full changelog: v1.0.1...v1.1.0