mirror of https://github.com/SixLabors/ImageSharp
63 changed files with 1281 additions and 1131 deletions
@ -1,82 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Tuples |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Its faster to process multiple Vector4-s together, so let's pair them!
|
|
||||
/// On AVX2 this pair should be convertible to <see cref="Vector{T}"/> of <see cref="float"/>!
|
|
||||
/// TODO: Investigate defining this as union with an Octet.OfSingle type.
|
|
||||
/// </summary>
|
|
||||
[StructLayout(LayoutKind.Sequential)] |
|
||||
internal struct Vector4Pair |
|
||||
{ |
|
||||
public Vector4 A; |
|
||||
|
|
||||
public Vector4 B; |
|
||||
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
public void MultiplyInplace(float value) |
|
||||
{ |
|
||||
this.A *= value; |
|
||||
this.B *= value; |
|
||||
} |
|
||||
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
public void AddInplace(Vector4 value) |
|
||||
{ |
|
||||
this.A += value; |
|
||||
this.B += value; |
|
||||
} |
|
||||
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
public void AddInplace(ref Vector4Pair other) |
|
||||
{ |
|
||||
this.A += other.A; |
|
||||
this.B += other.B; |
|
||||
} |
|
||||
|
|
||||
/// <summary>.
|
|
||||
/// Downscale method, specific to Jpeg color conversion. Works only if Vector{float}.Count == 4! /// TODO: Move it somewhere else.
|
|
||||
/// </summary>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
internal void RoundAndDownscalePreVector8(float downscaleFactor) |
|
||||
{ |
|
||||
ref Vector<float> a = ref Unsafe.As<Vector4, Vector<float>>(ref this.A); |
|
||||
a = a.FastRound(); |
|
||||
|
|
||||
ref Vector<float> b = ref Unsafe.As<Vector4, Vector<float>>(ref this.B); |
|
||||
b = b.FastRound(); |
|
||||
|
|
||||
// Downscale by 1/factor
|
|
||||
var scale = new Vector4(1 / downscaleFactor); |
|
||||
this.A *= scale; |
|
||||
this.B *= scale; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// AVX2-only Downscale method, specific to Jpeg color conversion.
|
|
||||
/// TODO: Move it somewhere else.
|
|
||||
/// </summary>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
internal void RoundAndDownscaleVector8(float downscaleFactor) |
|
||||
{ |
|
||||
ref Vector<float> self = ref Unsafe.As<Vector4Pair, Vector<float>>(ref this); |
|
||||
Vector<float> v = self; |
|
||||
v = v.FastRound(); |
|
||||
|
|
||||
// Downscale by 1/factor
|
|
||||
v *= new Vector<float>(1 / downscaleFactor); |
|
||||
self = v; |
|
||||
} |
|
||||
|
|
||||
public override string ToString() |
|
||||
{ |
|
||||
return $"{nameof(Vector4Pair)}({this.A}, {this.B})"; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,18 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|
||||
{ |
|
||||
internal abstract partial class JpegColorConverter |
|
||||
{ |
|
||||
internal abstract class Avx2JpegColorConverter : VectorizedJpegColorConverter |
|
||||
{ |
|
||||
protected Avx2JpegColorConverter(JpegColorSpace colorSpace, int precision) |
|
||||
: base(colorSpace, precision, 8) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
protected sealed override bool IsAvailable => SimdUtils.HasAvx2; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,18 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|
||||
{ |
|
||||
internal abstract partial class JpegColorConverter |
|
||||
{ |
|
||||
internal abstract class BasicJpegColorConverter : JpegColorConverter |
|
||||
{ |
|
||||
protected BasicJpegColorConverter(JpegColorSpace colorSpace, int precision) |
|
||||
: base(colorSpace, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
protected override bool IsAvailable => true; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,52 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
#if SUPPORTS_RUNTIME_INTRINSICS
|
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using System.Runtime.Intrinsics; |
||||
|
using System.Runtime.Intrinsics.X86; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
||||
|
{ |
||||
|
internal abstract partial class JpegColorConverterBase |
||||
|
{ |
||||
|
internal sealed class FromCmykAvx : JpegColorConverterAvx |
||||
|
{ |
||||
|
public FromCmykAvx(int precision) |
||||
|
: base(JpegColorSpace.Cmyk, precision) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public override void ConvertToRgbInplace(in ComponentValues values) |
||||
|
{ |
||||
|
ref Vector256<float> c0Base = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
||||
|
ref Vector256<float> c1Base = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
||||
|
ref Vector256<float> c2Base = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
||||
|
ref Vector256<float> c3Base = |
||||
|
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
||||
|
|
||||
|
// Used for the color conversion
|
||||
|
var scale = Vector256.Create(1 / (this.MaximumValue * this.MaximumValue)); |
||||
|
|
||||
|
nint n = values.Component0.Length / Vector256<float>.Count; |
||||
|
for (nint i = 0; i < n; i++) |
||||
|
{ |
||||
|
ref Vector256<float> c = ref Unsafe.Add(ref c0Base, i); |
||||
|
ref Vector256<float> m = ref Unsafe.Add(ref c1Base, i); |
||||
|
ref Vector256<float> y = ref Unsafe.Add(ref c2Base, i); |
||||
|
Vector256<float> k = Unsafe.Add(ref c3Base, i); |
||||
|
|
||||
|
k = Avx.Multiply(k, scale); |
||||
|
c = Avx.Multiply(c, k); |
||||
|
m = Avx.Multiply(m, k); |
||||
|
y = Avx.Multiply(y, k); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
#endif
|
||||
@ -1,60 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|
||||
using System.Runtime.Intrinsics; |
|
||||
using System.Runtime.Intrinsics.X86; |
|
||||
using static SixLabors.ImageSharp.SimdUtils; |
|
||||
#endif
|
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|
||||
{ |
|
||||
internal abstract partial class JpegColorConverter |
|
||||
{ |
|
||||
internal sealed class FromCmykAvx2 : Avx2JpegColorConverter |
|
||||
{ |
|
||||
public FromCmykAvx2(int precision) |
|
||||
: base(JpegColorSpace.Cmyk, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
protected override void ConvertCoreVectorizedInplace(in ComponentValues values) |
|
||||
{ |
|
||||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|
||||
ref Vector256<float> c0Base = |
|
||||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|
||||
ref Vector256<float> c1Base = |
|
||||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|
||||
ref Vector256<float> c2Base = |
|
||||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|
||||
ref Vector256<float> c3Base = |
|
||||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
|
||||
|
|
||||
// Used for the color conversion
|
|
||||
var scale = Vector256.Create(1 / this.MaximumValue); |
|
||||
|
|
||||
nint n = values.Component0.Length / 8; |
|
||||
for (nint i = 0; i < n; i++) |
|
||||
{ |
|
||||
ref Vector256<float> c = ref Unsafe.Add(ref c0Base, i); |
|
||||
ref Vector256<float> m = ref Unsafe.Add(ref c1Base, i); |
|
||||
ref Vector256<float> y = ref Unsafe.Add(ref c2Base, i); |
|
||||
Vector256<float> k = Unsafe.Add(ref c3Base, i); |
|
||||
|
|
||||
k = Avx.Multiply(k, scale); |
|
||||
c = Avx.Multiply(Avx.Multiply(c, k), scale); |
|
||||
m = Avx.Multiply(Avx.Multiply(m, k), scale); |
|
||||
y = Avx.Multiply(Avx.Multiply(y, k), scale); |
|
||||
} |
|
||||
#endif
|
|
||||
} |
|
||||
|
|
||||
protected override void ConvertCoreInplace(in ComponentValues values) => |
|
||||
FromCmykBasic.ConvertCoreInplace(values, this.MaximumValue); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,51 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
||||
|
{ |
||||
|
internal abstract partial class JpegColorConverterBase |
||||
|
{ |
||||
|
internal sealed class FromCmykVector : JpegColorConverterVector |
||||
|
{ |
||||
|
public FromCmykVector(int precision) |
||||
|
: base(JpegColorSpace.Cmyk, precision) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
protected override void ConvertCoreVectorizedInplace(in ComponentValues values) |
||||
|
{ |
||||
|
ref Vector<float> cBase = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
||||
|
ref Vector<float> mBase = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
||||
|
ref Vector<float> yBase = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
||||
|
ref Vector<float> kBase = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
||||
|
|
||||
|
var scale = new Vector<float>(1 / (this.MaximumValue * this.MaximumValue)); |
||||
|
|
||||
|
nint n = values.Component0.Length / Vector<float>.Count; |
||||
|
for (nint i = 0; i < n; i++) |
||||
|
{ |
||||
|
ref Vector<float> c = ref Unsafe.Add(ref cBase, i); |
||||
|
ref Vector<float> m = ref Unsafe.Add(ref mBase, i); |
||||
|
ref Vector<float> y = ref Unsafe.Add(ref yBase, i); |
||||
|
Vector<float> k = Unsafe.Add(ref kBase, i); |
||||
|
|
||||
|
k *= scale; |
||||
|
c *= k; |
||||
|
m *= k; |
||||
|
y *= k; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected override void ConvertCoreInplace(in ComponentValues values) => |
||||
|
FromCmykScalar.ConvertCoreInplace(values, this.MaximumValue); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,53 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
using SixLabors.ImageSharp.Tuples; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|
||||
{ |
|
||||
internal abstract partial class JpegColorConverter |
|
||||
{ |
|
||||
internal sealed class FromCmykVector8 : Vector8JpegColorConverter |
|
||||
{ |
|
||||
public FromCmykVector8(int precision) |
|
||||
: base(JpegColorSpace.Cmyk, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
protected override void ConvertCoreVectorizedInplace(in ComponentValues values) |
|
||||
{ |
|
||||
ref Vector<float> cBase = |
|
||||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|
||||
ref Vector<float> mBase = |
|
||||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|
||||
ref Vector<float> yBase = |
|
||||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|
||||
ref Vector<float> kBase = |
|
||||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
|
||||
|
|
||||
var scale = new Vector<float>(1 / this.MaximumValue); |
|
||||
|
|
||||
// Walking 8 elements at one step:
|
|
||||
nint n = values.Component0.Length / 8; |
|
||||
for (nint i = 0; i < n; i++) |
|
||||
{ |
|
||||
ref Vector<float> c = ref Unsafe.Add(ref cBase, i); |
|
||||
ref Vector<float> m = ref Unsafe.Add(ref mBase, i); |
|
||||
ref Vector<float> y = ref Unsafe.Add(ref yBase, i); |
|
||||
Vector<float> k = Unsafe.Add(ref kBase, i) * scale; |
|
||||
|
|
||||
c = (c * k) * scale; |
|
||||
m = (m * k) * scale; |
|
||||
y = (y * k) * scale; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
protected override void ConvertCoreInplace(in ComponentValues values) => |
|
||||
FromCmykBasic.ConvertCoreInplace(values, this.MaximumValue); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,47 +1,39 @@ |
|||||
// Copyright (c) Six Labors.
|
// Copyright (c) Six Labors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
using System; |
#if SUPPORTS_RUNTIME_INTRINSICS
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
using System.Runtime.CompilerServices; |
||||
using System.Runtime.InteropServices; |
using System.Runtime.InteropServices; |
||||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|
||||
using System.Runtime.Intrinsics; |
using System.Runtime.Intrinsics; |
||||
using System.Runtime.Intrinsics.X86; |
using System.Runtime.Intrinsics.X86; |
||||
using static SixLabors.ImageSharp.SimdUtils; |
|
||||
#endif
|
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
||||
{ |
{ |
||||
internal abstract partial class JpegColorConverter |
internal abstract partial class JpegColorConverterBase |
||||
{ |
{ |
||||
internal sealed class FromGrayscaleAvx2 : Avx2JpegColorConverter |
internal sealed class FromGrayscaleAvx : JpegColorConverterAvx |
||||
{ |
{ |
||||
public FromGrayscaleAvx2(int precision) |
public FromGrayscaleAvx(int precision) |
||||
: base(JpegColorSpace.Grayscale, precision) |
: base(JpegColorSpace.Grayscale, precision) |
||||
{ |
{ |
||||
} |
} |
||||
|
|
||||
protected override void ConvertCoreVectorizedInplace(in ComponentValues values) |
public override void ConvertToRgbInplace(in ComponentValues values) |
||||
{ |
{ |
||||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|
||||
ref Vector256<float> c0Base = |
ref Vector256<float> c0Base = |
||||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
||||
|
|
||||
// Used for the color conversion
|
// Used for the color conversion
|
||||
var scale = Vector256.Create(1 / this.MaximumValue); |
var scale = Vector256.Create(1 / this.MaximumValue); |
||||
|
|
||||
nint n = values.Component0.Length / 8; |
nint n = values.Component0.Length / Vector256<float>.Count; |
||||
for (nint i = 0; i < n; i++) |
for (nint i = 0; i < n; i++) |
||||
{ |
{ |
||||
ref Vector256<float> c0 = ref Unsafe.Add(ref c0Base, i); |
ref Vector256<float> c0 = ref Unsafe.Add(ref c0Base, i); |
||||
c0 = Avx.Multiply(c0, scale); |
c0 = Avx.Multiply(c0, scale); |
||||
} |
} |
||||
#endif
|
|
||||
} |
} |
||||
|
|
||||
protected override void ConvertCoreInplace(in ComponentValues values) => |
|
||||
FromGrayscaleBasic.ScaleValues(values.Component0, this.MaximumValue); |
|
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
|
#endif
|
||||
@ -1,53 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|
||||
{ |
|
||||
internal abstract partial class JpegColorConverter |
|
||||
{ |
|
||||
internal sealed class FromGrayscaleBasic : BasicJpegColorConverter |
|
||||
{ |
|
||||
public FromGrayscaleBasic(int precision) |
|
||||
: base(JpegColorSpace.Grayscale, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public override void ConvertToRgbInplace(in ComponentValues values) => |
|
||||
ScaleValues(values.Component0, this.MaximumValue); |
|
||||
|
|
||||
internal static void ScaleValues(Span<float> values, float maxValue) |
|
||||
{ |
|
||||
Span<Vector4> vecValues = MemoryMarshal.Cast<float, Vector4>(values); |
|
||||
|
|
||||
var scaleVector = new Vector4(1 / maxValue); |
|
||||
|
|
||||
for (int i = 0; i < vecValues.Length; i++) |
|
||||
{ |
|
||||
vecValues[i] *= scaleVector; |
|
||||
} |
|
||||
|
|
||||
values = values.Slice(vecValues.Length * 4); |
|
||||
if (!values.IsEmpty) |
|
||||
{ |
|
||||
float scaleValue = 1f / maxValue; |
|
||||
values[0] *= scaleValue; |
|
||||
|
|
||||
if ((uint)values.Length > 1) |
|
||||
{ |
|
||||
values[1] *= scaleValue; |
|
||||
|
|
||||
if ((uint)values.Length > 2) |
|
||||
{ |
|
||||
values[2] *= scaleValue; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,34 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
||||
|
{ |
||||
|
internal abstract partial class JpegColorConverterBase |
||||
|
{ |
||||
|
internal sealed class FromGrayscaleScalar : JpegColorConverterScalar |
||||
|
{ |
||||
|
public FromGrayscaleScalar(int precision) |
||||
|
: base(JpegColorSpace.Grayscale, precision) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public override void ConvertToRgbInplace(in ComponentValues values) => |
||||
|
ConvertCoreInplace(values.Component0, this.MaximumValue); |
||||
|
|
||||
|
internal static void ConvertCoreInplace(Span<float> values, float maxValue) |
||||
|
{ |
||||
|
ref float valuesRef = ref MemoryMarshal.GetReference(values); |
||||
|
float scale = 1 / maxValue; |
||||
|
|
||||
|
for (nint i = 0; i < values.Length; i++) |
||||
|
{ |
||||
|
Unsafe.Add(ref valuesRef, i) *= scale; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,38 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
||||
|
{ |
||||
|
internal abstract partial class JpegColorConverterBase |
||||
|
{ |
||||
|
internal sealed class FromGrayScaleVector : JpegColorConverterVector |
||||
|
{ |
||||
|
public FromGrayScaleVector(int precision) |
||||
|
: base(JpegColorSpace.Grayscale, precision) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
protected override void ConvertCoreVectorizedInplace(in ComponentValues values) |
||||
|
{ |
||||
|
ref Vector<float> cBase = |
||||
|
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
||||
|
|
||||
|
var scale = new Vector<float>(1 / this.MaximumValue); |
||||
|
|
||||
|
nint n = values.Component0.Length / Vector<float>.Count; |
||||
|
for (nint i = 0; i < n; i++) |
||||
|
{ |
||||
|
ref Vector<float> c0 = ref Unsafe.Add(ref cBase, i); |
||||
|
c0 *= scale; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected override void ConvertCoreInplace(in ComponentValues values) => |
||||
|
FromGrayscaleScalar.ConvertCoreInplace(values.Component0, this.MaximumValue); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,32 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.InteropServices; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|
||||
{ |
|
||||
internal abstract partial class JpegColorConverter |
|
||||
{ |
|
||||
internal sealed class FromRgbBasic : BasicJpegColorConverter |
|
||||
{ |
|
||||
public FromRgbBasic(int precision) |
|
||||
: base(JpegColorSpace.RGB, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public override void ConvertToRgbInplace(in ComponentValues values) |
|
||||
{ |
|
||||
ConvertCoreInplace(values, this.MaximumValue); |
|
||||
} |
|
||||
|
|
||||
internal static void ConvertCoreInplace(ComponentValues values, float maxValue) |
|
||||
{ |
|
||||
FromGrayscaleBasic.ScaleValues(values.Component0, maxValue); |
|
||||
FromGrayscaleBasic.ScaleValues(values.Component1, maxValue); |
|
||||
FromGrayscaleBasic.ScaleValues(values.Component2, maxValue); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,26 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
||||
|
{ |
||||
|
internal abstract partial class JpegColorConverterBase |
||||
|
{ |
||||
|
internal sealed class FromRgbScalar : JpegColorConverterScalar |
||||
|
{ |
||||
|
public FromRgbScalar(int precision) |
||||
|
: base(JpegColorSpace.RGB, precision) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public override void ConvertToRgbInplace(in ComponentValues values) => |
||||
|
ConvertCoreInplace(values, this.MaximumValue); |
||||
|
|
||||
|
internal static void ConvertCoreInplace(ComponentValues values, float maxValue) |
||||
|
{ |
||||
|
FromGrayscaleScalar.ConvertCoreInplace(values.Component0, maxValue); |
||||
|
FromGrayscaleScalar.ConvertCoreInplace(values.Component1, maxValue); |
||||
|
FromGrayscaleScalar.ConvertCoreInplace(values.Component2, maxValue); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,42 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Numerics; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|
||||
{ |
|
||||
internal abstract partial class JpegColorConverter |
|
||||
{ |
|
||||
internal sealed class FromYCbCrBasic : BasicJpegColorConverter |
|
||||
{ |
|
||||
public FromYCbCrBasic(int precision) |
|
||||
: base(JpegColorSpace.YCbCr, precision) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
public override void ConvertToRgbInplace(in ComponentValues values) |
|
||||
=> ConvertCoreInplace(values, this.MaximumValue, this.HalfValue); |
|
||||
|
|
||||
internal static void ConvertCoreInplace(in ComponentValues values, float maxValue, float halfValue) |
|
||||
{ |
|
||||
Span<float> c0 = values.Component0; |
|
||||
Span<float> c1 = values.Component1; |
|
||||
Span<float> c2 = values.Component2; |
|
||||
|
|
||||
var scale = 1 / maxValue; |
|
||||
|
|
||||
for (int i = 0; i < c0.Length; i++) |
|
||||
{ |
|
||||
float y = c0[i]; |
|
||||
float cb = c1[i] - halfValue; |
|
||||
float cr = c2[i] - halfValue; |
|
||||
|
|
||||
c0[i] = MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero) * scale; |
|
||||
c1[i] = MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero) * scale; |
|
||||
c2[i] = MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero) * scale; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,50 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
||||
|
{ |
||||
|
internal abstract partial class JpegColorConverterBase |
||||
|
{ |
||||
|
internal sealed class FromYCbCrScalar : JpegColorConverterScalar |
||||
|
{ |
||||
|
// TODO: comments, derived from ITU-T Rec. T.871
|
||||
|
internal const float RCrMult = 1.402f; |
||||
|
internal const float GCbMult = (float)(0.114 * 1.772 / 0.587); |
||||
|
internal const float GCrMult = (float)(0.299 * 1.402 / 0.587); |
||||
|
internal const float BCbMult = 1.772f; |
||||
|
|
||||
|
public FromYCbCrScalar(int precision) |
||||
|
: base(JpegColorSpace.YCbCr, precision) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public override void ConvertToRgbInplace(in ComponentValues values) |
||||
|
=> ConvertCoreInplace(values, this.MaximumValue, this.HalfValue); |
||||
|
|
||||
|
internal static void ConvertCoreInplace(in ComponentValues values, float maxValue, float halfValue) |
||||
|
{ |
||||
|
Span<float> c0 = values.Component0; |
||||
|
Span<float> c1 = values.Component1; |
||||
|
Span<float> c2 = values.Component2; |
||||
|
|
||||
|
float scale = 1 / maxValue; |
||||
|
|
||||
|
for (int i = 0; i < c0.Length; i++) |
||||
|
{ |
||||
|
float y = c0[i]; |
||||
|
float cb = c1[i] - halfValue; |
||||
|
float cr = c2[i] - halfValue; |
||||
|
|
||||
|
// r = y + (1.402F * cr);
|
||||
|
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
||||
|
// b = y + (1.772F * cb);
|
||||
|
c0[i] = MathF.Round(y + (RCrMult * cr), MidpointRounding.AwayFromZero) * scale; |
||||
|
c1[i] = MathF.Round(y - (GCbMult * cb) - (GCrMult * cr), MidpointRounding.AwayFromZero) * scale; |
||||
|
c2[i] = MathF.Round(y + (BCbMult * cb), MidpointRounding.AwayFromZero) * scale; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,88 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
using SixLabors.ImageSharp.Tuples; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|
||||
{ |
|
||||
internal abstract partial class JpegColorConverter |
|
||||
{ |
|
||||
internal sealed class FromYCbCrVector4 : VectorizedJpegColorConverter |
|
||||
{ |
|
||||
public FromYCbCrVector4(int precision) |
|
||||
: base(JpegColorSpace.YCbCr, precision, 8) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
protected override bool IsAvailable => SimdUtils.HasVector4; |
|
||||
|
|
||||
protected override void ConvertCoreVectorizedInplace(in ComponentValues values) |
|
||||
{ |
|
||||
DebugGuard.IsTrue(values.Component0.Length % 8 == 0, nameof(values), "Length should be divisible by 8!"); |
|
||||
|
|
||||
ref Vector4Pair c0Base = |
|
||||
ref Unsafe.As<float, Vector4Pair>(ref MemoryMarshal.GetReference(values.Component0)); |
|
||||
ref Vector4Pair c1Base = |
|
||||
ref Unsafe.As<float, Vector4Pair>(ref MemoryMarshal.GetReference(values.Component1)); |
|
||||
ref Vector4Pair c2Base = |
|
||||
ref Unsafe.As<float, Vector4Pair>(ref MemoryMarshal.GetReference(values.Component2)); |
|
||||
|
|
||||
var chromaOffset = new Vector4(-this.HalfValue); |
|
||||
var maxValue = this.MaximumValue; |
|
||||
|
|
||||
// Walking 8 elements at one step:
|
|
||||
nint n = values.Component0.Length / 8; |
|
||||
|
|
||||
for (nint i = 0; i < n; i++) |
|
||||
{ |
|
||||
// y = yVals[i];
|
|
||||
ref Vector4Pair c0 = ref Unsafe.Add(ref c0Base, i); |
|
||||
|
|
||||
// cb = cbVals[i] - halfValue);
|
|
||||
ref Vector4Pair c1 = ref Unsafe.Add(ref c1Base, i); |
|
||||
c1.AddInplace(chromaOffset); |
|
||||
|
|
||||
// cr = crVals[i] - halfValue;
|
|
||||
ref Vector4Pair c2 = ref Unsafe.Add(ref c2Base, i); |
|
||||
c2.AddInplace(chromaOffset); |
|
||||
|
|
||||
// r = y + (1.402F * cr);
|
|
||||
Vector4Pair r = c0; |
|
||||
Vector4Pair tmp = c2; |
|
||||
tmp.MultiplyInplace(1.402F); |
|
||||
r.AddInplace(ref tmp); |
|
||||
|
|
||||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|
||||
Vector4Pair g = c0; |
|
||||
tmp = c1; |
|
||||
tmp.MultiplyInplace(-0.344136F); |
|
||||
g.AddInplace(ref tmp); |
|
||||
tmp = c2; |
|
||||
tmp.MultiplyInplace(-0.714136F); |
|
||||
g.AddInplace(ref tmp); |
|
||||
|
|
||||
// b = y + (1.772F * cb);
|
|
||||
Vector4Pair b = c0; |
|
||||
tmp = c1; |
|
||||
tmp.MultiplyInplace(1.772F); |
|
||||
b.AddInplace(ref tmp); |
|
||||
|
|
||||
r.RoundAndDownscalePreVector8(maxValue); |
|
||||
g.RoundAndDownscalePreVector8(maxValue); |
|
||||
b.RoundAndDownscalePreVector8(maxValue); |
|
||||
|
|
||||
c0 = r; |
|
||||
c1 = g; |
|
||||
c2 = b; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
protected override void ConvertCoreInplace(in ComponentValues values) |
|
||||
=> FromYCbCrBasic.ConvertCoreInplace(values, this.MaximumValue, this.HalfValue); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,18 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|
||||
{ |
|
||||
internal abstract partial class JpegColorConverter |
|
||||
{ |
|
||||
internal abstract class Vector8JpegColorConverter : VectorizedJpegColorConverter |
|
||||
{ |
|
||||
protected Vector8JpegColorConverter(JpegColorSpace colorSpace, int precision) |
|
||||
: base(colorSpace, precision, 8) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
protected sealed override bool IsAvailable => SimdUtils.HasVector8; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,47 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Numerics; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|
||||
{ |
|
||||
internal abstract partial class JpegColorConverter |
|
||||
{ |
|
||||
internal abstract class VectorizedJpegColorConverter : JpegColorConverter |
|
||||
{ |
|
||||
private readonly int vectorSize; |
|
||||
|
|
||||
protected VectorizedJpegColorConverter(JpegColorSpace colorSpace, int precision, int vectorSize) |
|
||||
: base(colorSpace, precision) |
|
||||
{ |
|
||||
this.vectorSize = vectorSize; |
|
||||
} |
|
||||
|
|
||||
public override void ConvertToRgbInplace(in ComponentValues values) |
|
||||
{ |
|
||||
int length = values.Component0.Length; |
|
||||
int remainder = values.Component0.Length % this.vectorSize; |
|
||||
int simdCount = length - remainder; |
|
||||
if (simdCount > 0) |
|
||||
{ |
|
||||
// This implementation is actually AVX specific.
|
|
||||
// An AVX register is capable of storing 8 float-s.
|
|
||||
if (!this.IsAvailable) |
|
||||
{ |
|
||||
throw new InvalidOperationException( |
|
||||
"This converter can be used only on architecture having 256 byte floating point SIMD registers!"); |
|
||||
} |
|
||||
|
|
||||
this.ConvertCoreVectorizedInplace(values.Slice(0, simdCount)); |
|
||||
} |
|
||||
|
|
||||
this.ConvertCoreInplace(values.Slice(simdCount, remainder)); |
|
||||
} |
|
||||
|
|
||||
protected virtual void ConvertCoreVectorizedInplace(in ComponentValues values) => throw new NotImplementedException(); |
|
||||
|
|
||||
protected virtual void ConvertCoreInplace(in ComponentValues values) => throw new NotImplementedException(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,32 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
#if SUPPORTS_RUNTIME_INTRINSICS
|
||||
|
using System.Runtime.Intrinsics.X86; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
||||
|
{ |
||||
|
internal abstract partial class JpegColorConverterBase |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// <see cref="JpegColorConverterBase"/> abstract base for implementations
|
||||
|
/// based on <see cref="Avx"/> instructions.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// Converters of this family would expect input buffers lengths to be
|
||||
|
/// divisible by 8 without a remainder.
|
||||
|
/// This is guaranteed by real-life data as jpeg stores pixels via 8x8 blocks.
|
||||
|
/// DO NOT pass test data of invalid size to these converters as they
|
||||
|
/// potentially won't do a bound check and return a false positive result.
|
||||
|
/// </remarks>
|
||||
|
internal abstract class JpegColorConverterAvx : JpegColorConverterBase |
||||
|
{ |
||||
|
protected JpegColorConverterAvx(JpegColorSpace colorSpace, int precision) |
||||
|
: base(colorSpace, precision) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public override bool IsAvailable => Avx.IsSupported; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
#endif
|
||||
@ -0,0 +1,22 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
||||
|
{ |
||||
|
internal abstract partial class JpegColorConverterBase |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// <see cref="JpegColorConverterBase"/> abstract base for implementations
|
||||
|
/// based on scalar instructions.
|
||||
|
/// </summary>
|
||||
|
internal abstract class JpegColorConverterScalar : JpegColorConverterBase |
||||
|
{ |
||||
|
protected JpegColorConverterScalar(JpegColorSpace colorSpace, int precision) |
||||
|
: base(colorSpace, precision) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public override bool IsAvailable => true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,59 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Numerics; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
||||
|
{ |
||||
|
internal abstract partial class JpegColorConverterBase |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// <see cref="JpegColorConverterBase"/> abstract base for implementations
|
||||
|
/// based on <see cref="Vector"/> API.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// Converters of this family can work with data of any size.
|
||||
|
/// Even though real life data is guaranteed to be of size
|
||||
|
/// divisible by 8 newer SIMD instructions like AVX512 won't work with
|
||||
|
/// such data out of the box. These converters have fallback code
|
||||
|
/// for 'remainder' data.
|
||||
|
/// </remarks>
|
||||
|
internal abstract class JpegColorConverterVector : JpegColorConverterBase |
||||
|
{ |
||||
|
protected JpegColorConverterVector(JpegColorSpace colorSpace, int precision) |
||||
|
: base(colorSpace, precision) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public sealed override bool IsAvailable => Vector.IsHardwareAccelerated && Vector<float>.Count % 4 == 0; |
||||
|
|
||||
|
public override void ConvertToRgbInplace(in ComponentValues values) |
||||
|
{ |
||||
|
DebugGuard.IsTrue(this.IsAvailable, $"{this.GetType().Name} converter is not supported on current hardware."); |
||||
|
|
||||
|
int length = values.Component0.Length; |
||||
|
int remainder = (int)((uint)length % (uint)Vector<float>.Count); |
||||
|
|
||||
|
// Jpeg images are guaranteed to have pixel strides at least 8 pixels wide
|
||||
|
// Thus there's no need to check whether simdCount is greater than zero
|
||||
|
int simdCount = length - remainder; |
||||
|
this.ConvertCoreVectorizedInplace(values.Slice(0, simdCount)); |
||||
|
|
||||
|
// Jpeg images width is always divisible by 8 without a remainder
|
||||
|
// so it's safe to say SSE/AVX implementations would never have
|
||||
|
// 'remainder' pixels
|
||||
|
// But some exotic simd implementations e.g. AVX-512 can have
|
||||
|
// remainder pixels
|
||||
|
if (remainder > 0) |
||||
|
{ |
||||
|
this.ConvertCoreInplace(values.Slice(simdCount, remainder)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected virtual void ConvertCoreVectorizedInplace(in ComponentValues values) => throw new NotImplementedException(); |
||||
|
|
||||
|
protected virtual void ConvertCoreInplace(in ComponentValues values) => throw new NotImplementedException(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue