diff --git a/src/ImageSharp/Common/Helpers/Numerics.cs b/src/ImageSharp/Common/Helpers/Numerics.cs
index db65b84cca..ba5c588ca5 100644
--- a/src/ImageSharp/Common/Helpers/Numerics.cs
+++ b/src/ImageSharp/Common/Helpers/Numerics.cs
@@ -879,5 +879,13 @@ namespace SixLabors.ImageSharp
(IntPtr)(int)((value * 0x07C4ACDDu) >> 27)); // uint|long -> IntPtr cast on 32-bit platforms does expensive overflow checks not needed here
}
#endif
+
+ ///
+ /// Fast division with ceiling for numbers.
+ ///
+ /// Divident value.
+ /// Divisor value.
+ /// Ceiled division result.
+ public static uint DivideCeil(uint value, uint divisor) => (value + divisor - 1) / divisor;
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs
index 9f89fd085f..3a136b4103 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs
@@ -103,8 +103,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
///
public void InitComponents()
{
- this.McusPerLine = (int)MathF.Ceiling(this.PixelWidth / 8F / this.MaxHorizontalFactor);
- this.McusPerColumn = (int)MathF.Ceiling(this.PixelHeight / 8F / this.MaxVerticalFactor);
+ this.McusPerLine = (int)Numerics.DivideCeil((uint)this.PixelWidth, (uint)this.MaxHorizontalFactor * 8);
+ this.McusPerColumn = (int)Numerics.DivideCeil((uint)this.PixelHeight, (uint)this.MaxVerticalFactor * 8);
for (int i = 0; i < this.ComponentCount; i++)
{
diff --git a/tests/ImageSharp.Tests/Common/NumericsTests.cs b/tests/ImageSharp.Tests/Common/NumericsTests.cs
index 29eae6d488..62819af493 100644
--- a/tests/ImageSharp.Tests/Common/NumericsTests.cs
+++ b/tests/ImageSharp.Tests/Common/NumericsTests.cs
@@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Tests.Common
int expected = 0;
int actual = Numerics.Log2(value);
- Assert.True(expected == actual, $"Expected: {expected}, Actual: {actual}");
+ Assert.Equal(expected, actual);
}
[Fact]
@@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Tests.Common
int expected = i;
int actual = Numerics.Log2(value);
- Assert.True(expected == actual, $"Expected: {expected}, Actual: {actual}");
+ Assert.Equal(expected, actual);
}
}
@@ -66,7 +66,35 @@ namespace SixLabors.ImageSharp.Tests.Common
int expected = Log2_ReferenceImplementation(value);
int actual = Numerics.Log2(value);
- Assert.True(expected == actual, $"Expected: {expected}, Actual: {actual}");
+ Assert.Equal(expected, actual);
+ }
+ }
+
+ private static uint DivideCeil_ReferenceImplementation(uint value, uint divisor) => (uint)MathF.Ceiling((float)value / divisor);
+
+ [Fact]
+ public void DivideCeil_DivideZero()
+ {
+ uint expected = 0;
+ uint actual = Numerics.DivideCeil(0, 100);
+
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory]
+ [InlineData(1, 100)]
+ public void DivideCeil_RandomValues(int seed, int count)
+ {
+ var rng = new Random(seed);
+ for (int i = 0; i < count; i++)
+ {
+ uint value = (uint)rng.Next();
+ uint divisor = (uint)rng.Next();
+
+ uint expected = DivideCeil_ReferenceImplementation(value, divisor);
+ uint actual = Numerics.DivideCeil(value, divisor);
+
+ Assert.True(expected == actual, $"Expected: {expected}\nActual: {actual}\n{value} / {divisor} = {expected}");
}
}
}