From 79e8ce7b1367e2c8ae3cc2c3ed1cdddfafc2be2b Mon Sep 17 00:00:00 2001 From: winscripter <142818255+winscripter@users.noreply.github.com> Date: Thu, 6 Aug 2026 13:15:07 +0400 Subject: [PATCH] Add Move to Front transform See inverse_mtf-inl.h --- .../Formats/Jxl/Processing/JxlInverseMtf.cs | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 src/ImageSharp/Formats/Jxl/Processing/JxlInverseMtf.cs diff --git a/src/ImageSharp/Formats/Jxl/Processing/JxlInverseMtf.cs b/src/ImageSharp/Formats/Jxl/Processing/JxlInverseMtf.cs new file mode 100644 index 000000000..6638f8600 --- /dev/null +++ b/src/ImageSharp/Formats/Jxl/Processing/JxlInverseMtf.cs @@ -0,0 +1,91 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; + +namespace SixLabors.ImageSharp.Formats.Jxl.Processing; + +/// +/// Inverse Move to Front implementation +/// +internal static class JxlInverseMtf +{ + // NOTE: here we use Vector512 to store 64 bytes in a + // more efficient manner. However, it doesn't necessarily + // require 512-bit CPU vector support. + // If the user's CPU has 256-bit vectors, the JIT will emit + // such instructions for each half. Likewise, if the user's + // CPU only goes up to 128-bit vectors, the JIT will emit + // 128-bit vector code for each quarter. And if the CPU + // doesn't support SIMD at all, the JIT will emit scalar + // instructions. + public static void MoveToFront(Span v, byte index) + { + byte value = v[index]; + byte i = index; + + ref byte vR = ref MemoryMarshal.GetReference(v); + + if (i < 4) + { + for (; i != 0; --i) + { + v[i] = v[i - 1]; + } + } + else + { + int tail = i & 63; + + if (tail != 0) + { + i -= (byte)tail; + Vector512 vec = Vector512.LoadUnsafe(ref Unsafe.Add(ref vR, i)); + Vector512 prev = Vector512.LoadUnsafe(ref Unsafe.Add(ref vR, i + 1)); + + // TODO: optimize this? + Span maskBytes = stackalloc byte[64]; + + for (int j = 0; j < 64; j++) + { + maskBytes[j] = (byte)(j < tail ? 0xFF : 0); + } + + Vector512 mask = Vector512.Create(maskBytes); + Vector512 filter = Vector512.ConditionalSelect(mask, vec, prev); + filter.StoreUnsafe(ref Unsafe.Add(ref vR, i + 1)); + } + + while (i != 0) + { + i -= 64; + Vector512 vec = Vector512.LoadUnsafe(ref Unsafe.Add(ref vR, i)); + vec.StoreUnsafe(ref Unsafe.Add(ref vR, i + 1)); + } + } + + v[0] = value; + } + + public static void InverseMoveToFrontTransform(Span v, int vLength) + { + Span mtf = stackalloc byte[256 + 64]; + for (int i = 0; i < 256; i++) + { + mtf[i] = (byte)i; + } + + for (int i = 0; i < vLength; i++) + { + byte index = v[i]; + v[i] = mtf[index]; + + if (index != 0) + { + MoveToFront(mtf, index); + } + } + } +}