From c4c4bf292298c026df470db1a17ff826515fd95d Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 5 Sep 2026 01:26:41 +1000 Subject: [PATCH] Clamp histogram luminance indices --- .../Common/Helpers/ColorNumerics.cs | 12 +++- .../HistogramEqualizationTests.cs | 55 +++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Common/Helpers/ColorNumerics.cs b/src/ImageSharp/Common/Helpers/ColorNumerics.cs index 0b88aa5b07..d13eead453 100644 --- a/src/ImageSharp/Common/Helpers/ColorNumerics.cs +++ b/src/ImageSharp/Common/Helpers/ColorNumerics.cs @@ -27,7 +27,17 @@ internal static class ColorNumerics /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetBT709Luminance(Vector4 vector, int luminanceLevels) - => (int)MathF.Round(Vector4.Dot(vector, Bt709) * (luminanceLevels - 1)); + { + float luminance = Vector4.Dot(vector, Bt709); + if (float.IsNaN(luminance)) + { + return 0; + } + + // Floating-point pixel formats can contain values outside their normalized range. + luminance = Math.Clamp(luminance, 0F, 1F); + return (int)MathF.Round(luminance * (luminanceLevels - 1)); + } /// /// Gets the luminance from the rgb components using the formula diff --git a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs index c39648d014..917d68d025 100644 --- a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs +++ b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs @@ -14,6 +14,18 @@ public class HistogramEqualizationTests { private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.0456F); + [Fact] + public void GlobalHistogramEqualization_ClampsDecodedFloatingPointSamples() + { + byte[] data = BuildFloatTiff(8, 2F); + + using Image image = Image.Load(data); + image.Mutate(x => x.HistogramEqualization()); + + Assert.Equal(8, image.Width); + Assert.Equal(1, image.Height); + } + [Theory] [InlineData(256)] [InlineData(65536)] @@ -238,4 +250,47 @@ public class HistogramEqualizationTests ValidatorComparer.VerifySimilarity(referenceResult, processed); } + + private static byte[] BuildFloatTiff(int width, float sample) + { + const int headerLength = 8; + const int entryCount = 10; + const int ifdLength = 2 + (entryCount * 12) + 4; + uint pixelOffset = headerLength + ifdLength; + + using MemoryStream stream = new(); + using BinaryWriter writer = new(stream); + + writer.Write((byte)0x49); + writer.Write((byte)0x49); + writer.Write((ushort)42); + writer.Write((uint)headerLength); + writer.Write((ushort)entryCount); + WriteEntry(writer, 256, 4, 1, (uint)width); + WriteEntry(writer, 257, 4, 1, 1); + WriteEntry(writer, 258, 3, 1, 32); + WriteEntry(writer, 259, 3, 1, 1); + WriteEntry(writer, 262, 3, 1, 1); + WriteEntry(writer, 273, 4, 1, pixelOffset); + WriteEntry(writer, 277, 3, 1, 1); + WriteEntry(writer, 278, 4, 1, 1); + WriteEntry(writer, 279, 4, 1, (uint)(width * sizeof(float))); + WriteEntry(writer, 339, 3, 1, 3); + writer.Write(0U); + + for (int i = 0; i < width; i++) + { + writer.Write(sample); + } + + return stream.ToArray(); + + static void WriteEntry(BinaryWriter writer, ushort tag, ushort type, uint count, uint value) + { + writer.Write(tag); + writer.Write(type); + writer.Write(count); + writer.Write(value); + } + } }