diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml
index 0e093a8347..c8f3997946 100644
--- a/.github/workflows/build-and-test.yml
+++ b/.github/workflows/build-and-test.yml
@@ -17,11 +17,11 @@ jobs:
- os: ubuntu-latest
framework: netcoreapp3.1
runtime: -x64
- codecov: false
+ codecov: true
- os: windows-latest
framework: netcoreapp3.1
runtime: -x64
- codecov: true
+ codecov: false
- os: windows-latest
framework: netcoreapp2.1
runtime: -x64
diff --git a/Directory.Build.props b/Directory.Build.props
index 0f9c5bdde2..c4610d0ed3 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -120,6 +120,7 @@
https://api.nuget.org/v3/index.json;
https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json;
+ https://www.myget.org/F/coverlet-dev/api/v3/index.json;
true
$(MSBuildThisFileDirectory)shared-infrastructure/SixLabors.snk
diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets
index d1875262d3..9b8be05b56 100644
--- a/src/Directory.Build.targets
+++ b/src/Directory.Build.targets
@@ -21,16 +21,25 @@
-
+
+ $([System.IO.Path]::Combine('$(IntermediateOutputPath)','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)'))
+
+
+
+
+
+
+
+
+ DependsOnTargets="InitializeSourceRootMappedPaths"
+ Returns="@(_LocalTopLevelSourceRoot)"
+ Condition="'$(DeterministicSourcePaths)' == 'true'">
<_LocalTopLevelSourceRoot Include="@(SourceRoot)" Condition="'%(SourceRoot.NestedRoot)' == ''"/>
-
+
false
@@ -62,7 +71,7 @@
-
+
@@ -74,7 +83,7 @@
SkipUnchangedFiles = "true"
DestinationFolder="..\..\" />
-
+
-
+
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
index f6f5903684..10cbee5e6f 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
@@ -10,12 +10,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
internal partial struct Block8x8F
{
- ///
- /// Transpose the block into the destination block.
+ ///
+ /// Fallback method to transpose a block into the destination block on non AVX supported CPUs.
///
/// The destination block
[MethodImpl(InliningOptions.ShortMethod)]
- public void TransposeInto(ref Block8x8F d)
+ public void TransposeIntoFallback(ref Block8x8F d)
{
d.V0L.X = V0L.X;
d.V1L.X = V0L.Y;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt
index 6ee0540213..f47d9106ee 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt
@@ -23,12 +23,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
internal partial struct Block8x8F
{
- ///
- /// Transpose the block into the destination block.
+ ///
+ /// Fallback method to transpose a block into the destination block on non AVX supported CPUs.
///
/// The destination block
[MethodImpl(InliningOptions.ShortMethod)]
- public void TransposeInto(ref Block8x8F d)
+ public void TransposeIntoFallback(ref Block8x8F d)
{
<#
PushIndent(" ");
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
index b7835d6706..547e116230 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
@@ -6,6 +6,10 @@ using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+#if SUPPORTS_RUNTIME_INTRINSICS
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+#endif
using System.Text;
// ReSharper disable InconsistentNaming
@@ -596,5 +600,98 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
DebugGuard.MustBeLessThan(idx, Size, nameof(idx));
DebugGuard.MustBeGreaterThanOrEqualTo(idx, 0, nameof(idx));
}
+
+ ///
+ /// Transpose the block into the destination block.
+ ///
+ /// The destination block
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void TransposeInto(ref Block8x8F d)
+ {
+#if SUPPORTS_RUNTIME_INTRINSICS
+ if (Avx.IsSupported)
+ {
+ this.TransposeIntoAvx(ref d);
+ }
+ else
+#endif
+ {
+ this.TransposeIntoFallback(ref d);
+ }
+ }
+
+#if SUPPORTS_RUNTIME_INTRINSICS
+ ///
+ /// AVX-only variant for executing .
+ ///
+ ///
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void TransposeIntoAvx(ref Block8x8F d)
+ {
+ Vector256 r0 = Avx.InsertVector128(
+ Unsafe.As>(ref this.V0L).ToVector256(),
+ Unsafe.As>(ref this.V4L),
+ 1);
+
+ Vector256 r1 = Avx.InsertVector128(
+ Unsafe.As>(ref this.V1L).ToVector256(),
+ Unsafe.As>(ref this.V5L),
+ 1);
+
+ Vector256 r2 = Avx.InsertVector128(
+ Unsafe.As>(ref this.V2L).ToVector256(),
+ Unsafe.As>(ref this.V6L),
+ 1);
+
+ Vector256 r3 = Avx.InsertVector128(
+ Unsafe.As>(ref this.V3L).ToVector256(),
+ Unsafe.As>(ref this.V7L),
+ 1);
+
+ Vector256 r4 = Avx.InsertVector128(
+ Unsafe.As>(ref this.V0R).ToVector256(),
+ Unsafe.As>(ref this.V4R),
+ 1);
+
+ Vector256 r5 = Avx.InsertVector128(
+ Unsafe.As>(ref this.V1R).ToVector256(),
+ Unsafe.As>(ref this.V5R),
+ 1);
+
+ Vector256 r6 = Avx.InsertVector128(
+ Unsafe.As>(ref this.V2R).ToVector256(),
+ Unsafe.As>(ref this.V6R),
+ 1);
+
+ Vector256 r7 = Avx.InsertVector128(
+ Unsafe.As>(ref this.V3R).ToVector256(),
+ Unsafe.As>(ref this.V7R),
+ 1);
+
+ Vector256 t0 = Avx.UnpackLow(r0, r1);
+ Vector256 t2 = Avx.UnpackLow(r2, r3);
+ Vector256 v = Avx.Shuffle(t0, t2, 0x4E);
+ Unsafe.As>(ref d.V0L) = Avx.Blend(t0, v, 0xCC);
+ Unsafe.As>(ref d.V1L) = Avx.Blend(t2, v, 0x33);
+
+ Vector256 t4 = Avx.UnpackLow(r4, r5);
+ Vector256 t6 = Avx.UnpackLow(r6, r7);
+ v = Avx.Shuffle(t4, t6, 0x4E);
+ Unsafe.As>(ref d.V4L) = Avx.Blend(t4, v, 0xCC);
+ Unsafe.As>(ref d.V5L) = Avx.Blend(t6, v, 0x33);
+
+ Vector256 t1 = Avx.UnpackHigh(r0, r1);
+ Vector256 t3 = Avx.UnpackHigh(r2, r3);
+ v = Avx.Shuffle(t1, t3, 0x4E);
+ Unsafe.As>(ref d.V2L) = Avx.Blend(t1, v, 0xCC);
+ Unsafe.As>(ref d.V3L) = Avx.Blend(t3, v, 0x33);
+
+ Vector256 t5 = Avx.UnpackHigh(r4, r5);
+ Vector256 t7 = Avx.UnpackHigh(r6, r7);
+ v = Avx.Shuffle(t5, t7, 0x4E);
+ Unsafe.As>(ref d.V6L) = Avx.Blend(t5, v, 0xCC);
+ Unsafe.As>(ref d.V7L) = Avx.Blend(t7, v, 0x33);
+ }
+#endif
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.cs b/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.cs
index ee06f2bdeb..d0b373609b 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
@@ -50,8 +50,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// Temporary block provided by the caller
public static void TransformIDCT(ref Block8x8F src, ref Block8x8F dest, ref Block8x8F temp)
{
- // TODO: Transpose is a bottleneck now. We need full AVX support to optimize it:
- // https://github.com/dotnet/corefx/issues/22940
src.TransposeInto(ref temp);
IDCT8x4_LeftPart(ref temp, ref dest);
@@ -340,4 +338,4 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
dest.MultiplyInplace(C_0_125);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs
index 43023c9382..6d95d51b32 100644
--- a/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Binarization/AdaptiveThresholdProcessor{TPixel}.cs
@@ -67,7 +67,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization
ref TPixel color = ref Unsafe.Add(ref rowRef, x);
color.ToRgba32(ref rgb);
- sum += (ulong)(rgb.R + rgb.G + rgb.G);
+ sum += (ulong)(rgb.R + rgb.G + rgb.B);
+
if (x - startX != 0)
{
intImage[x - startX, y - startY] = intImage[x - startX - 1, y - startY] + sum;
diff --git a/tests/Directory.Build.targets b/tests/Directory.Build.targets
index e9e93a855f..335f3d106c 100644
--- a/tests/Directory.Build.targets
+++ b/tests/Directory.Build.targets
@@ -28,10 +28,10 @@
-
+
-
+
diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Transpose.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Transpose.cs
new file mode 100644
index 0000000000..ae1b23df92
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Transpose.cs
@@ -0,0 +1,45 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using BenchmarkDotNet.Attributes;
+using SixLabors.ImageSharp.Formats.Jpeg.Components;
+
+namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
+{
+ public class Block8x8F_Transpose
+ {
+ private static readonly Block8x8F Source = Create8x8FloatData();
+
+ [Benchmark(Baseline=true)]
+ public void TransposeIntoVector4()
+ {
+ var dest = default(Block8x8F);
+ Source.TransposeIntoFallback(ref dest);
+ }
+
+#if SUPPORTS_RUNTIME_INTRINSICS
+ [Benchmark]
+ public void TransposeIntoAvx()
+ {
+ var dest = default(Block8x8F);
+ Source.TransposeIntoAvx(ref dest);
+ }
+#endif
+
+ private static Block8x8F Create8x8FloatData()
+ {
+ var result = new float[64];
+ for (int i = 0; i < 8; i++)
+ {
+ for (int j = 0; j < 8; j++)
+ {
+ result[(i * 8) + j] = (i * 10) + j;
+ }
+ }
+
+ var source = default(Block8x8F);
+ source.LoadFrom(result);
+ return source;
+ }
+ }
+}
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
index 722521f98d..73a68063c0 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
@@ -163,7 +163,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
}
[Fact]
- public void TransposeInto()
+ public void TransposeIntoFallback()
{
float[] expected = Create8x8FloatData();
ReferenceImplementations.Transpose8x8(expected);
@@ -172,7 +172,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
source.LoadFrom(Create8x8FloatData());
var dest = default(Block8x8F);
- source.TransposeInto(ref dest);
+ source.TransposeIntoFallback(ref dest);
float[] actual = new float[64];
dest.ScaledCopyTo(actual);
@@ -180,6 +180,26 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
Assert.Equal(expected, actual);
}
+#if SUPPORTS_RUNTIME_INTRINSICS
+ [Fact]
+ public void TransposeIntoAvx()
+ {
+ float[] expected = Create8x8FloatData();
+ ReferenceImplementations.Transpose8x8(expected);
+
+ var source = default(Block8x8F);
+ source.LoadFrom(Create8x8FloatData());
+
+ var dest = default(Block8x8F);
+ source.TransposeIntoAvx(ref dest);
+
+ float[] actual = new float[64];
+ dest.ScaledCopyTo(actual);
+
+ Assert.Equal(expected, actual);
+ }
+#endif
+
private class BufferHolder
{
public Block8x8F Buffer;
diff --git a/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs b/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs
index 8efac7593d..f4f800107f 100644
--- a/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs
+++ b/tests/ImageSharp.Tests/Processing/Binarization/AdaptiveThresholdTests.cs
@@ -100,6 +100,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization
[Theory]
[WithFile(TestImages.Png.Bradley01, PixelTypes.Rgba32)]
[WithFile(TestImages.Png.Bradley02, PixelTypes.Rgba32)]
+ [WithFile(TestImages.Png.Ducky, PixelTypes.Rgba32)]
public void AdaptiveThreshold_Works(TestImageProvider provider)
where TPixel : unmanaged, IPixel
{
diff --git a/tests/Images/External b/tests/Images/External
index 6a00308067..cc6465910d 160000
--- a/tests/Images/External
+++ b/tests/Images/External
@@ -1 +1 @@
-Subproject commit 6a003080674d1fedc66292c13ce5a357b2a33083
+Subproject commit cc6465910d092319ef9bf4e99698a0649996d3c5