diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor2.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor2.cs
index 300a773311..23b81d2bad 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor2.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor2.cs
@@ -66,32 +66,335 @@ internal sealed class DownScalingComponentProcessor2 : ComponentProcessor
[MethodImpl(InliningOptions.ShortMethod)]
public static void ScaledCopyTo(ref Block8x8F block, ref float destRef, int destStrideWidth, int horizontalScale, int verticalScale)
{
- // TODO: Optimize: implement all cases with scale-specific, loopless code!
+ if (horizontalScale == 1 && verticalScale == 1)
+ {
+ CopyTo1x1Scale(ref block, ref destRef, (uint)destStrideWidth);
+ return;
+ }
+
+ if (horizontalScale == 2 && verticalScale == 2)
+ {
+ CopyTo2x2Scale(ref block, ref destRef, (uint)destStrideWidth);
+ return;
+ }
+
+ if (horizontalScale == 2 && verticalScale == 1)
+ {
+ CopyTo2x1Scale(ref block, ref destRef, (uint)destStrideWidth);
+ return;
+ }
+
+ if (horizontalScale == 1 && verticalScale == 2)
+ {
+ CopyTo1x2Scale(ref block, ref destRef, (uint)destStrideWidth);
+ return;
+ }
+
+ if (horizontalScale == 4 && verticalScale == 1)
+ {
+ CopyTo4x1Scale(ref block, ref destRef, (uint)destStrideWidth);
+ return;
+ }
+
+ if (horizontalScale == 4 && verticalScale == 2)
+ {
+ CopyTo4x2Scale(ref block, ref destRef, (uint)destStrideWidth);
+ return;
+ }
+
+ if (horizontalScale == 1 && verticalScale == 4)
+ {
+ CopyTo1x4Scale(ref block, ref destRef, (uint)destStrideWidth);
+ return;
+ }
+
+ if (horizontalScale == 2 && verticalScale == 4)
+ {
+ CopyTo2x4Scale(ref block, ref destRef, (uint)destStrideWidth);
+ return;
+ }
+
+ if (horizontalScale == 4 && verticalScale == 4)
+ {
+ CopyTo4x4Scale(ref block, ref destRef, (uint)destStrideWidth);
+ return;
+ }
+
+ // The common 1x, 2x, and 4x integral scales are specialized above.
+ // Uncommon legal factor-3 scales use the generic fallback.
CopyArbitraryScale(ref block, ref destRef, (uint)destStrideWidth, (uint)horizontalScale, (uint)verticalScale);
+ }
+
+ ///
+ /// Copies a 4x4 reduced block directly into the destination buffer when no chroma expansion is needed.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void CopyTo1x1Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
+ {
+ ref float sourceBase = ref Unsafe.As(ref block);
+
+ CopyRow4(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
+ CopyRow4(ref sourceBase, ref areaOrigin, 1u, 1u, areaStride);
+ CopyRow4(ref sourceBase, ref areaOrigin, 2u, 2u, areaStride);
+ CopyRow4(ref sourceBase, ref areaOrigin, 3u, 3u, areaStride);
+ }
+
+ ///
+ /// Copies a 4x4 reduced block into the destination buffer while doubling only the horizontal axis.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void CopyTo2x1Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
+ {
+ ref float sourceBase = ref Unsafe.As(ref block);
+
+ WidenRow4(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
+ WidenRow4(ref sourceBase, ref areaOrigin, 1u, 1u, areaStride);
+ WidenRow4(ref sourceBase, ref areaOrigin, 2u, 2u, areaStride);
+ WidenRow4(ref sourceBase, ref areaOrigin, 3u, 3u, areaStride);
+ }
+
+ ///
+ /// Copies a 4x4 reduced block into the destination buffer while doubling only the vertical axis.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void CopyTo1x2Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
+ {
+ ref float sourceBase = ref Unsafe.As(ref block);
+
+ CopyRow4(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
+ CopyRow4(ref sourceBase, ref areaOrigin, 0u, 1u, areaStride);
+ CopyRow4(ref sourceBase, ref areaOrigin, 1u, 2u, areaStride);
+ CopyRow4(ref sourceBase, ref areaOrigin, 1u, 3u, areaStride);
+ CopyRow4(ref sourceBase, ref areaOrigin, 2u, 4u, areaStride);
+ CopyRow4(ref sourceBase, ref areaOrigin, 2u, 5u, areaStride);
+ CopyRow4(ref sourceBase, ref areaOrigin, 3u, 6u, areaStride);
+ CopyRow4(ref sourceBase, ref areaOrigin, 3u, 7u, areaStride);
+ }
+
+ ///
+ /// Copies a 4x4 reduced block into the destination buffer while doubling both axes.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void CopyTo2x2Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
+ {
+ ref float sourceBase = ref Unsafe.As(ref block);
+
+ WidenRow4(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
+ WidenRow4(ref sourceBase, ref areaOrigin, 0u, 1u, areaStride);
+ WidenRow4(ref sourceBase, ref areaOrigin, 1u, 2u, areaStride);
+ WidenRow4(ref sourceBase, ref areaOrigin, 1u, 3u, areaStride);
+ WidenRow4(ref sourceBase, ref areaOrigin, 2u, 4u, areaStride);
+ WidenRow4(ref sourceBase, ref areaOrigin, 2u, 5u, areaStride);
+ WidenRow4(ref sourceBase, ref areaOrigin, 3u, 6u, areaStride);
+ WidenRow4(ref sourceBase, ref areaOrigin, 3u, 7u, areaStride);
+ }
- [MethodImpl(InliningOptions.ColdPath)]
- static void CopyArbitraryScale(ref Block8x8F block, ref float areaOrigin, uint areaStride, uint horizontalScale, uint verticalScale)
+ ///
+ /// Copies a 4x4 reduced block into the destination buffer while quadrupling only the horizontal axis.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void CopyTo4x1Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
+ {
+ ref float sourceBase = ref Unsafe.As(ref block);
+
+ ExpandRow4(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
+ ExpandRow4(ref sourceBase, ref areaOrigin, 1u, 1u, areaStride);
+ ExpandRow4(ref sourceBase, ref areaOrigin, 2u, 2u, areaStride);
+ ExpandRow4(ref sourceBase, ref areaOrigin, 3u, 3u, areaStride);
+ }
+
+ ///
+ /// Copies a 4x4 reduced block into the destination buffer while quadrupling horizontally and doubling vertically.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void CopyTo4x2Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
+ {
+ ref float sourceBase = ref Unsafe.As(ref block);
+
+ ExpandRow4(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
+ ExpandRow4(ref sourceBase, ref areaOrigin, 0u, 1u, areaStride);
+ ExpandRow4(ref sourceBase, ref areaOrigin, 1u, 2u, areaStride);
+ ExpandRow4(ref sourceBase, ref areaOrigin, 1u, 3u, areaStride);
+ ExpandRow4(ref sourceBase, ref areaOrigin, 2u, 4u, areaStride);
+ ExpandRow4(ref sourceBase, ref areaOrigin, 2u, 5u, areaStride);
+ ExpandRow4(ref sourceBase, ref areaOrigin, 3u, 6u, areaStride);
+ ExpandRow4(ref sourceBase, ref areaOrigin, 3u, 7u, areaStride);
+ }
+
+ ///
+ /// Copies a 4x4 reduced block into the destination buffer while quadrupling only the vertical axis.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void CopyTo1x4Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
+ {
+ ref float sourceBase = ref Unsafe.As(ref block);
+
+ CopyRow4(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
+ CopyRow4(ref sourceBase, ref areaOrigin, 0u, 1u, areaStride);
+ CopyRow4(ref sourceBase, ref areaOrigin, 0u, 2u, areaStride);
+ CopyRow4(ref sourceBase, ref areaOrigin, 0u, 3u, areaStride);
+ CopyRow4(ref sourceBase, ref areaOrigin, 1u, 4u, areaStride);
+ CopyRow4(ref sourceBase, ref areaOrigin, 1u, 5u, areaStride);
+ CopyRow4(ref sourceBase, ref areaOrigin, 1u, 6u, areaStride);
+ CopyRow4(ref sourceBase, ref areaOrigin, 1u, 7u, areaStride);
+ CopyRow4(ref sourceBase, ref areaOrigin, 2u, 8u, areaStride);
+ CopyRow4(ref sourceBase, ref areaOrigin, 2u, 9u, areaStride);
+ CopyRow4(ref sourceBase, ref areaOrigin, 2u, 10u, areaStride);
+ CopyRow4(ref sourceBase, ref areaOrigin, 2u, 11u, areaStride);
+ CopyRow4(ref sourceBase, ref areaOrigin, 3u, 12u, areaStride);
+ CopyRow4(ref sourceBase, ref areaOrigin, 3u, 13u, areaStride);
+ CopyRow4(ref sourceBase, ref areaOrigin, 3u, 14u, areaStride);
+ CopyRow4(ref sourceBase, ref areaOrigin, 3u, 15u, areaStride);
+ }
+
+ ///
+ /// Copies a 4x4 reduced block into the destination buffer while doubling horizontally and quadrupling vertically.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void CopyTo2x4Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
+ {
+ ref float sourceBase = ref Unsafe.As(ref block);
+
+ WidenRow4(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
+ WidenRow4(ref sourceBase, ref areaOrigin, 0u, 1u, areaStride);
+ WidenRow4(ref sourceBase, ref areaOrigin, 0u, 2u, areaStride);
+ WidenRow4(ref sourceBase, ref areaOrigin, 0u, 3u, areaStride);
+ WidenRow4(ref sourceBase, ref areaOrigin, 1u, 4u, areaStride);
+ WidenRow4(ref sourceBase, ref areaOrigin, 1u, 5u, areaStride);
+ WidenRow4(ref sourceBase, ref areaOrigin, 1u, 6u, areaStride);
+ WidenRow4(ref sourceBase, ref areaOrigin, 1u, 7u, areaStride);
+ WidenRow4(ref sourceBase, ref areaOrigin, 2u, 8u, areaStride);
+ WidenRow4(ref sourceBase, ref areaOrigin, 2u, 9u, areaStride);
+ WidenRow4(ref sourceBase, ref areaOrigin, 2u, 10u, areaStride);
+ WidenRow4(ref sourceBase, ref areaOrigin, 2u, 11u, areaStride);
+ WidenRow4(ref sourceBase, ref areaOrigin, 3u, 12u, areaStride);
+ WidenRow4(ref sourceBase, ref areaOrigin, 3u, 13u, areaStride);
+ WidenRow4(ref sourceBase, ref areaOrigin, 3u, 14u, areaStride);
+ WidenRow4(ref sourceBase, ref areaOrigin, 3u, 15u, areaStride);
+ }
+
+ ///
+ /// Copies a 4x4 reduced block into the destination buffer while quadrupling both axes.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void CopyTo4x4Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
+ {
+ ref float sourceBase = ref Unsafe.As(ref block);
+
+ ExpandRow4(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
+ ExpandRow4(ref sourceBase, ref areaOrigin, 0u, 1u, areaStride);
+ ExpandRow4(ref sourceBase, ref areaOrigin, 0u, 2u, areaStride);
+ ExpandRow4(ref sourceBase, ref areaOrigin, 0u, 3u, areaStride);
+ ExpandRow4(ref sourceBase, ref areaOrigin, 1u, 4u, areaStride);
+ ExpandRow4(ref sourceBase, ref areaOrigin, 1u, 5u, areaStride);
+ ExpandRow4(ref sourceBase, ref areaOrigin, 1u, 6u, areaStride);
+ ExpandRow4(ref sourceBase, ref areaOrigin, 1u, 7u, areaStride);
+ ExpandRow4(ref sourceBase, ref areaOrigin, 2u, 8u, areaStride);
+ ExpandRow4(ref sourceBase, ref areaOrigin, 2u, 9u, areaStride);
+ ExpandRow4(ref sourceBase, ref areaOrigin, 2u, 10u, areaStride);
+ ExpandRow4(ref sourceBase, ref areaOrigin, 2u, 11u, areaStride);
+ ExpandRow4(ref sourceBase, ref areaOrigin, 3u, 12u, areaStride);
+ ExpandRow4(ref sourceBase, ref areaOrigin, 3u, 13u, areaStride);
+ ExpandRow4(ref sourceBase, ref areaOrigin, 3u, 14u, areaStride);
+ ExpandRow4(ref sourceBase, ref areaOrigin, 3u, 15u, areaStride);
+ }
+
+ ///
+ /// Copies one four-sample row from the reduced block to the destination row.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void CopyRow4(ref float sourceBase, ref float areaOrigin, nuint sourceRow, nuint destRow, uint areaStride)
+ {
+ ref float source = ref Unsafe.Add(ref sourceBase, sourceRow * 8u);
+ ref float dest = ref Unsafe.Add(ref areaOrigin, destRow * areaStride);
+
+ Unsafe.CopyBlock(
+ ref Unsafe.As(ref dest),
+ ref Unsafe.As(ref source),
+ 4u * sizeof(float));
+ }
+
+ ///
+ /// Expands one four-sample row to eight samples by duplicating each source value horizontally.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void WidenRow4(ref float sourceBase, ref float areaOrigin, nuint sourceRow, nuint destRow, uint areaStride)
+ {
+ ref float source = ref Unsafe.Add(ref sourceBase, sourceRow * 8u);
+ ref float dest = ref Unsafe.Add(ref areaOrigin, destRow * areaStride);
+
+ float value0 = source;
+ float value1 = Unsafe.Add(ref source, 1u);
+ float value2 = Unsafe.Add(ref source, 2u);
+ float value3 = Unsafe.Add(ref source, 3u);
+
+ dest = value0;
+ Unsafe.Add(ref dest, 1u) = value0;
+ Unsafe.Add(ref dest, 2u) = value1;
+ Unsafe.Add(ref dest, 3u) = value1;
+ Unsafe.Add(ref dest, 4u) = value2;
+ Unsafe.Add(ref dest, 5u) = value2;
+ Unsafe.Add(ref dest, 6u) = value3;
+ Unsafe.Add(ref dest, 7u) = value3;
+ }
+
+ ///
+ /// Expands one four-sample row to sixteen samples by duplicating each source value four times horizontally.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void ExpandRow4(ref float sourceBase, ref float areaOrigin, nuint sourceRow, nuint destRow, uint areaStride)
+ {
+ ref float source = ref Unsafe.Add(ref sourceBase, sourceRow * 8u);
+ ref float dest = ref Unsafe.Add(ref areaOrigin, destRow * areaStride);
+
+ float value0 = source;
+ float value1 = Unsafe.Add(ref source, 1u);
+ float value2 = Unsafe.Add(ref source, 2u);
+ float value3 = Unsafe.Add(ref source, 3u);
+
+ dest = value0;
+ Unsafe.Add(ref dest, 1u) = value0;
+ Unsafe.Add(ref dest, 2u) = value0;
+ Unsafe.Add(ref dest, 3u) = value0;
+ Unsafe.Add(ref dest, 4u) = value1;
+ Unsafe.Add(ref dest, 5u) = value1;
+ Unsafe.Add(ref dest, 6u) = value1;
+ Unsafe.Add(ref dest, 7u) = value1;
+ Unsafe.Add(ref dest, 8u) = value2;
+ Unsafe.Add(ref dest, 9u) = value2;
+ Unsafe.Add(ref dest, 10u) = value2;
+ Unsafe.Add(ref dest, 11u) = value2;
+ Unsafe.Add(ref dest, 12u) = value3;
+ Unsafe.Add(ref dest, 13u) = value3;
+ Unsafe.Add(ref dest, 14u) = value3;
+ Unsafe.Add(ref dest, 15u) = value3;
+ }
+
+ ///
+ /// Replicates each reduced sample into an arbitrary integral expansion rectangle for uncommon subsampling ratios.
+ ///
+ [MethodImpl(InliningOptions.ColdPath)]
+ private static void CopyArbitraryScale(ref Block8x8F block, ref float areaOrigin, uint areaStride, uint horizontalScale, uint verticalScale)
+ {
+ for (nuint y = 0u; y < 4u; y++)
{
- for (nuint y = 0; y < 4; y++)
+ nuint yy = y * verticalScale;
+ nuint y8 = y * 8u;
+
+ for (nuint x = 0u; x < 4u; x++)
{
- nuint yy = y * verticalScale;
- nuint y8 = y * 8;
+ nuint xx = x * horizontalScale;
- for (nuint x = 0; x < 4; x++)
- {
- nuint xx = x * horizontalScale;
+ float value = block[y8 + x];
- float value = block[y8 + x];
+ for (nuint i = 0u; i < verticalScale; i++)
+ {
+ nuint baseIdx = ((yy + i) * areaStride) + xx;
- for (nuint i = 0; i < verticalScale; i++)
+ for (nuint j = 0u; j < horizontalScale; j++)
{
- nuint baseIdx = ((yy + i) * areaStride) + xx;
-
- for (nuint j = 0; j < horizontalScale; j++)
- {
- // area[xx + j, yy + i] = value;
- Unsafe.Add(ref areaOrigin, baseIdx + j) = value;
- }
+ // area[xx + j, yy + i] = value;
+ Unsafe.Add(ref areaOrigin, baseIdx + j) = value;
}
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor4.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor4.cs
index 7984169902..a645a88c90 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor4.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor4.cs
@@ -66,32 +66,277 @@ internal sealed class DownScalingComponentProcessor4 : ComponentProcessor
[MethodImpl(InliningOptions.ShortMethod)]
public static void ScaledCopyTo(ref Block8x8F block, ref float destRef, int destStrideWidth, int horizontalScale, int verticalScale)
{
- // TODO: Optimize: implement all cases with scale-specific, loopless code!
+ if (horizontalScale == 1 && verticalScale == 1)
+ {
+ CopyTo1x1Scale(ref block, ref destRef, (uint)destStrideWidth);
+ return;
+ }
+
+ if (horizontalScale == 2 && verticalScale == 2)
+ {
+ CopyTo2x2Scale(ref block, ref destRef, (uint)destStrideWidth);
+ return;
+ }
+
+ if (horizontalScale == 2 && verticalScale == 1)
+ {
+ CopyTo2x1Scale(ref block, ref destRef, (uint)destStrideWidth);
+ return;
+ }
+
+ if (horizontalScale == 1 && verticalScale == 2)
+ {
+ CopyTo1x2Scale(ref block, ref destRef, (uint)destStrideWidth);
+ return;
+ }
+
+ if (horizontalScale == 4 && verticalScale == 1)
+ {
+ CopyTo4x1Scale(ref block, ref destRef, (uint)destStrideWidth);
+ return;
+ }
+
+ if (horizontalScale == 4 && verticalScale == 2)
+ {
+ CopyTo4x2Scale(ref block, ref destRef, (uint)destStrideWidth);
+ return;
+ }
+
+ if (horizontalScale == 1 && verticalScale == 4)
+ {
+ CopyTo1x4Scale(ref block, ref destRef, (uint)destStrideWidth);
+ return;
+ }
+
+ if (horizontalScale == 2 && verticalScale == 4)
+ {
+ CopyTo2x4Scale(ref block, ref destRef, (uint)destStrideWidth);
+ return;
+ }
+
+ if (horizontalScale == 4 && verticalScale == 4)
+ {
+ CopyTo4x4Scale(ref block, ref destRef, (uint)destStrideWidth);
+ return;
+ }
+
+ // The common 1x, 2x, and 4x integral scales are specialized above.
+ // Uncommon legal factor-3 scales use the generic fallback.
CopyArbitraryScale(ref block, ref destRef, (uint)destStrideWidth, (uint)horizontalScale, (uint)verticalScale);
+ }
+
+ ///
+ /// Copies a 2x2 reduced block directly into the destination buffer when no chroma expansion is needed.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void CopyTo1x1Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
+ {
+ ref float sourceBase = ref Unsafe.As(ref block);
+
+ CopyRow2(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
+ CopyRow2(ref sourceBase, ref areaOrigin, 1u, 1u, areaStride);
+ }
+
+ ///
+ /// Copies a 2x2 reduced block into the destination buffer while doubling only the horizontal axis.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void CopyTo2x1Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
+ {
+ ref float sourceBase = ref Unsafe.As(ref block);
+
+ WidenRow2(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
+ WidenRow2(ref sourceBase, ref areaOrigin, 1u, 1u, areaStride);
+ }
+
+ ///
+ /// Copies a 2x2 reduced block into the destination buffer while doubling only the vertical axis.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void CopyTo1x2Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
+ {
+ ref float sourceBase = ref Unsafe.As(ref block);
+
+ CopyRow2(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
+ CopyRow2(ref sourceBase, ref areaOrigin, 0u, 1u, areaStride);
+ CopyRow2(ref sourceBase, ref areaOrigin, 1u, 2u, areaStride);
+ CopyRow2(ref sourceBase, ref areaOrigin, 1u, 3u, areaStride);
+ }
+
+ ///
+ /// Copies a 2x2 reduced block into the destination buffer while doubling both axes.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void CopyTo2x2Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
+ {
+ ref float sourceBase = ref Unsafe.As(ref block);
+
+ WidenRow2(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
+ WidenRow2(ref sourceBase, ref areaOrigin, 0u, 1u, areaStride);
+ WidenRow2(ref sourceBase, ref areaOrigin, 1u, 2u, areaStride);
+ WidenRow2(ref sourceBase, ref areaOrigin, 1u, 3u, areaStride);
+ }
- [MethodImpl(InliningOptions.ColdPath)]
- static void CopyArbitraryScale(ref Block8x8F block, ref float areaOrigin, uint areaStride, uint horizontalScale, uint verticalScale)
+ ///
+ /// Copies a 2x2 reduced block into the destination buffer while quadrupling only the horizontal axis.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void CopyTo4x1Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
+ {
+ ref float sourceBase = ref Unsafe.As(ref block);
+
+ ExpandRow2(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
+ ExpandRow2(ref sourceBase, ref areaOrigin, 1u, 1u, areaStride);
+ }
+
+ ///
+ /// Copies a 2x2 reduced block into the destination buffer while quadrupling horizontally and doubling vertically.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void CopyTo4x2Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
+ {
+ ref float sourceBase = ref Unsafe.As(ref block);
+
+ ExpandRow2(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
+ ExpandRow2(ref sourceBase, ref areaOrigin, 0u, 1u, areaStride);
+ ExpandRow2(ref sourceBase, ref areaOrigin, 1u, 2u, areaStride);
+ ExpandRow2(ref sourceBase, ref areaOrigin, 1u, 3u, areaStride);
+ }
+
+ ///
+ /// Copies a 2x2 reduced block into the destination buffer while quadrupling only the vertical axis.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void CopyTo1x4Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
+ {
+ ref float sourceBase = ref Unsafe.As(ref block);
+
+ CopyRow2(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
+ CopyRow2(ref sourceBase, ref areaOrigin, 0u, 1u, areaStride);
+ CopyRow2(ref sourceBase, ref areaOrigin, 0u, 2u, areaStride);
+ CopyRow2(ref sourceBase, ref areaOrigin, 0u, 3u, areaStride);
+ CopyRow2(ref sourceBase, ref areaOrigin, 1u, 4u, areaStride);
+ CopyRow2(ref sourceBase, ref areaOrigin, 1u, 5u, areaStride);
+ CopyRow2(ref sourceBase, ref areaOrigin, 1u, 6u, areaStride);
+ CopyRow2(ref sourceBase, ref areaOrigin, 1u, 7u, areaStride);
+ }
+
+ ///
+ /// Copies a 2x2 reduced block into the destination buffer while doubling horizontally and quadrupling vertically.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void CopyTo2x4Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
+ {
+ ref float sourceBase = ref Unsafe.As(ref block);
+
+ WidenRow2(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
+ WidenRow2(ref sourceBase, ref areaOrigin, 0u, 1u, areaStride);
+ WidenRow2(ref sourceBase, ref areaOrigin, 0u, 2u, areaStride);
+ WidenRow2(ref sourceBase, ref areaOrigin, 0u, 3u, areaStride);
+ WidenRow2(ref sourceBase, ref areaOrigin, 1u, 4u, areaStride);
+ WidenRow2(ref sourceBase, ref areaOrigin, 1u, 5u, areaStride);
+ WidenRow2(ref sourceBase, ref areaOrigin, 1u, 6u, areaStride);
+ WidenRow2(ref sourceBase, ref areaOrigin, 1u, 7u, areaStride);
+ }
+
+ ///
+ /// Copies a 2x2 reduced block into the destination buffer while quadrupling both axes.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void CopyTo4x4Scale(ref Block8x8F block, ref float areaOrigin, uint areaStride)
+ {
+ ref float sourceBase = ref Unsafe.As(ref block);
+
+ ExpandRow2(ref sourceBase, ref areaOrigin, 0u, 0u, areaStride);
+ ExpandRow2(ref sourceBase, ref areaOrigin, 0u, 1u, areaStride);
+ ExpandRow2(ref sourceBase, ref areaOrigin, 0u, 2u, areaStride);
+ ExpandRow2(ref sourceBase, ref areaOrigin, 0u, 3u, areaStride);
+ ExpandRow2(ref sourceBase, ref areaOrigin, 1u, 4u, areaStride);
+ ExpandRow2(ref sourceBase, ref areaOrigin, 1u, 5u, areaStride);
+ ExpandRow2(ref sourceBase, ref areaOrigin, 1u, 6u, areaStride);
+ ExpandRow2(ref sourceBase, ref areaOrigin, 1u, 7u, areaStride);
+ }
+
+ ///
+ /// Copies one two-sample row from the reduced block to the destination row.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void CopyRow2(ref float sourceBase, ref float areaOrigin, nuint sourceRow, nuint destRow, uint areaStride)
+ {
+ ref float source = ref Unsafe.Add(ref sourceBase, sourceRow * 8u);
+ ref float dest = ref Unsafe.Add(ref areaOrigin, destRow * areaStride);
+
+ Unsafe.CopyBlock(
+ ref Unsafe.As(ref dest),
+ ref Unsafe.As(ref source),
+ 2u * sizeof(float));
+ }
+
+ ///
+ /// Expands one two-sample row to four samples by duplicating each source value horizontally.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void WidenRow2(ref float sourceBase, ref float areaOrigin, nuint sourceRow, nuint destRow, uint areaStride)
+ {
+ ref float source = ref Unsafe.Add(ref sourceBase, sourceRow * 8u);
+ ref float dest = ref Unsafe.Add(ref areaOrigin, destRow * areaStride);
+
+ float value0 = source;
+ float value1 = Unsafe.Add(ref source, 1u);
+
+ dest = value0;
+ Unsafe.Add(ref dest, 1u) = value0;
+ Unsafe.Add(ref dest, 2u) = value1;
+ Unsafe.Add(ref dest, 3u) = value1;
+ }
+
+ ///
+ /// Expands one two-sample row to eight samples by duplicating each source value four times horizontally.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void ExpandRow2(ref float sourceBase, ref float areaOrigin, nuint sourceRow, nuint destRow, uint areaStride)
+ {
+ ref float source = ref Unsafe.Add(ref sourceBase, sourceRow * 8u);
+ ref float dest = ref Unsafe.Add(ref areaOrigin, destRow * areaStride);
+
+ float value0 = source;
+ float value1 = Unsafe.Add(ref source, 1u);
+
+ dest = value0;
+ Unsafe.Add(ref dest, 1u) = value0;
+ Unsafe.Add(ref dest, 2u) = value0;
+ Unsafe.Add(ref dest, 3u) = value0;
+ Unsafe.Add(ref dest, 4u) = value1;
+ Unsafe.Add(ref dest, 5u) = value1;
+ Unsafe.Add(ref dest, 6u) = value1;
+ Unsafe.Add(ref dest, 7u) = value1;
+ }
+
+ ///
+ /// Replicates each reduced sample into an arbitrary integral expansion rectangle for uncommon subsampling ratios.
+ ///
+ [MethodImpl(InliningOptions.ColdPath)]
+ private static void CopyArbitraryScale(ref Block8x8F block, ref float areaOrigin, uint areaStride, uint horizontalScale, uint verticalScale)
+ {
+ for (nuint y = 0u; y < 2u; y++)
{
- for (nuint y = 0; y < 2; y++)
+ nuint yy = y * verticalScale;
+ nuint y8 = y * 8u;
+
+ for (nuint x = 0u; x < 2u; x++)
{
- nuint yy = y * verticalScale;
- nuint y8 = y * 8;
+ nuint xx = x * horizontalScale;
- for (nuint x = 0; x < 2; x++)
- {
- nuint xx = x * horizontalScale;
+ float value = block[y8 + x];
- float value = block[y8 + x];
+ for (nuint i = 0u; i < verticalScale; i++)
+ {
+ nuint baseIdx = ((yy + i) * areaStride) + xx;
- for (nuint i = 0; i < verticalScale; i++)
+ for (nuint j = 0u; j < horizontalScale; j++)
{
- nuint baseIdx = ((yy + i) * areaStride) + xx;
-
- for (nuint j = 0; j < horizontalScale; j++)
- {
- // area[xx + j, yy + i] = value;
- Unsafe.Add(ref areaOrigin, baseIdx + j) = value;
- }
+ // area[xx + j, yy + i] = value;
+ Unsafe.Add(ref areaOrigin, baseIdx + j) = value;
}
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor8.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor8.cs
index f3b09e6b49..ef17bf002c 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor8.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor8.cs
@@ -63,16 +63,56 @@ internal sealed class DownScalingComponentProcessor8 : ComponentProcessor
return;
}
+ if (horizontalScale == 2 && verticalScale == 1)
+ {
+ CopyTo2x1Scale(value, ref destRef);
+ return;
+ }
+
+ if (horizontalScale == 1 && verticalScale == 2)
+ {
+ CopyTo1x2Scale(value, ref destRef, (uint)destStrideWidth);
+ return;
+ }
+
if (horizontalScale == 2 && verticalScale == 2)
{
- destRef = value;
- Unsafe.Add(ref destRef, 1) = value;
- Unsafe.Add(ref destRef, 0 + (uint)destStrideWidth) = value;
- Unsafe.Add(ref destRef, 1 + (uint)destStrideWidth) = value;
+ CopyTo2x2Scale(value, ref destRef, (uint)destStrideWidth);
+ return;
+ }
+
+ if (horizontalScale == 4 && verticalScale == 1)
+ {
+ CopyTo4x1Scale(value, ref destRef);
+ return;
+ }
+
+ if (horizontalScale == 4 && verticalScale == 2)
+ {
+ CopyTo4x2Scale(value, ref destRef, (uint)destStrideWidth);
return;
}
- // TODO: Optimize: implement all cases with scale-specific, loopless code!
+ if (horizontalScale == 1 && verticalScale == 4)
+ {
+ CopyTo1x4Scale(value, ref destRef, (uint)destStrideWidth);
+ return;
+ }
+
+ if (horizontalScale == 2 && verticalScale == 4)
+ {
+ CopyTo2x4Scale(value, ref destRef, (uint)destStrideWidth);
+ return;
+ }
+
+ if (horizontalScale == 4 && verticalScale == 4)
+ {
+ CopyTo4x4Scale(value, ref destRef, (uint)destStrideWidth);
+ return;
+ }
+
+ // The common 1x, 2x, and 4x integral scales are specialized above.
+ // Uncommon legal factor-3 scales use the generic fallback.
for (nuint y = 0; y < (uint)verticalScale; y++)
{
for (nuint x = 0; x < (uint)horizontalScale; x++)
@@ -83,4 +123,108 @@ internal sealed class DownScalingComponentProcessor8 : ComponentProcessor
destRef = ref Unsafe.Add(ref destRef, (uint)destStrideWidth);
}
}
+
+ ///
+ /// Writes a single source value to two horizontally adjacent samples.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void CopyTo2x1Scale(float value, ref float areaOrigin)
+ {
+ areaOrigin = value;
+ Unsafe.Add(ref areaOrigin, 1u) = value;
+ }
+
+ ///
+ /// Writes a single source value to two vertically adjacent samples.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void CopyTo1x2Scale(float value, ref float areaOrigin, uint areaStride)
+ {
+ areaOrigin = value;
+ Unsafe.Add(ref areaOrigin, areaStride) = value;
+ }
+
+ ///
+ /// Writes a single source value to a 2x2 rectangle.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void CopyTo2x2Scale(float value, ref float areaOrigin, uint areaStride)
+ {
+ areaOrigin = value;
+ Unsafe.Add(ref areaOrigin, 1u) = value;
+ Unsafe.Add(ref areaOrigin, areaStride) = value;
+ Unsafe.Add(ref areaOrigin, areaStride + 1u) = value;
+ }
+
+ ///
+ /// Writes a single source value to four horizontally adjacent samples.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void CopyTo4x1Scale(float value, ref float areaOrigin)
+ {
+ areaOrigin = value;
+ Unsafe.Add(ref areaOrigin, 1u) = value;
+ Unsafe.Add(ref areaOrigin, 2u) = value;
+ Unsafe.Add(ref areaOrigin, 3u) = value;
+ }
+
+ ///
+ /// Writes a single source value to a 4x2 rectangle.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void CopyTo4x2Scale(float value, ref float areaOrigin, uint areaStride)
+ {
+ CopyTo4x1Scale(value, ref areaOrigin);
+
+ ref float nextRow = ref Unsafe.Add(ref areaOrigin, areaStride);
+ CopyTo4x1Scale(value, ref nextRow);
+ }
+
+ ///
+ /// Writes a single source value to four vertically adjacent samples.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void CopyTo1x4Scale(float value, ref float areaOrigin, uint areaStride)
+ {
+ areaOrigin = value;
+ Unsafe.Add(ref areaOrigin, areaStride) = value;
+ Unsafe.Add(ref areaOrigin, areaStride * 2u) = value;
+ Unsafe.Add(ref areaOrigin, areaStride * 3u) = value;
+ }
+
+ ///
+ /// Writes a single source value to a 2x4 rectangle.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void CopyTo2x4Scale(float value, ref float areaOrigin, uint areaStride)
+ {
+ CopyTo2x1Scale(value, ref areaOrigin);
+
+ ref float row1 = ref Unsafe.Add(ref areaOrigin, areaStride);
+ CopyTo2x1Scale(value, ref row1);
+
+ ref float row2 = ref Unsafe.Add(ref areaOrigin, areaStride * 2u);
+ CopyTo2x1Scale(value, ref row2);
+
+ ref float row3 = ref Unsafe.Add(ref areaOrigin, areaStride * 3u);
+ CopyTo2x1Scale(value, ref row3);
+ }
+
+ ///
+ /// Writes a single source value to a 4x4 rectangle.
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void CopyTo4x4Scale(float value, ref float areaOrigin, uint areaStride)
+ {
+ CopyTo4x1Scale(value, ref areaOrigin);
+
+ ref float row1 = ref Unsafe.Add(ref areaOrigin, areaStride);
+ CopyTo4x1Scale(value, ref row1);
+
+ ref float row2 = ref Unsafe.Add(ref areaOrigin, areaStride * 2u);
+ CopyTo4x1Scale(value, ref row2);
+
+ ref float row3 = ref Unsafe.Add(ref areaOrigin, areaStride * 3u);
+ CopyTo4x1Scale(value, ref row3);
+ }
}