mirror of https://github.com/SixLabors/ImageSharp
107 changed files with 3778 additions and 4479 deletions
@ -0,0 +1,159 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
// ReSharper disable UseObjectOrCollectionInitializer
|
|||
// ReSharper disable InconsistentNaming
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
|||
{ |
|||
internal partial struct Block8x8F |
|||
{ |
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public void ScaledCopyFrom(ref float areaOrigin, int areaStride) => |
|||
CopyFrom1x1Scale(ref Unsafe.As<float, byte>(ref areaOrigin), ref Unsafe.As<Block8x8F, byte>(ref this), areaStride); |
|||
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public void ScaledCopyTo(ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale) |
|||
{ |
|||
if (horizontalScale == 1 && verticalScale == 1) |
|||
{ |
|||
CopyTo1x1Scale(ref Unsafe.As<Block8x8F, byte>(ref this), ref Unsafe.As<float, byte>(ref areaOrigin), areaStride); |
|||
return; |
|||
} |
|||
|
|||
if (horizontalScale == 2 && verticalScale == 2) |
|||
{ |
|||
this.CopyTo2x2Scale(ref areaOrigin, areaStride); |
|||
return; |
|||
} |
|||
|
|||
// TODO: Optimize: implement all cases with scale-specific, loopless code!
|
|||
this.CopyArbitraryScale(ref areaOrigin, areaStride, horizontalScale, verticalScale); |
|||
} |
|||
|
|||
private void CopyTo2x2Scale(ref float areaOrigin, int areaStride) |
|||
{ |
|||
ref Vector2 destBase = ref Unsafe.As<float, Vector2>(ref areaOrigin); |
|||
int destStride = (int)((uint)areaStride / 2); |
|||
|
|||
WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 0, destStride); |
|||
WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 1, destStride); |
|||
WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 2, destStride); |
|||
WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 3, destStride); |
|||
WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 4, destStride); |
|||
WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 5, destStride); |
|||
WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 6, destStride); |
|||
WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 7, destStride); |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
static void WidenCopyRowImpl2x2(ref Vector4 selfBase, ref Vector2 destBase, nint row, nint destStride) |
|||
{ |
|||
ref Vector4 sLeft = ref Unsafe.Add(ref selfBase, 2 * row); |
|||
ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); |
|||
|
|||
nint offset = 2 * row * destStride; |
|||
ref Vector4 dTopLeft = ref Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset)); |
|||
ref Vector4 dBottomLeft = ref Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset + destStride)); |
|||
|
|||
var xyLeft = new Vector4(sLeft.X); |
|||
xyLeft.Z = sLeft.Y; |
|||
xyLeft.W = sLeft.Y; |
|||
|
|||
var zwLeft = new Vector4(sLeft.Z); |
|||
zwLeft.Z = sLeft.W; |
|||
zwLeft.W = sLeft.W; |
|||
|
|||
var xyRight = new Vector4(sRight.X); |
|||
xyRight.Z = sRight.Y; |
|||
xyRight.W = sRight.Y; |
|||
|
|||
var zwRight = new Vector4(sRight.Z); |
|||
zwRight.Z = sRight.W; |
|||
zwRight.W = sRight.W; |
|||
|
|||
dTopLeft = xyLeft; |
|||
Unsafe.Add(ref dTopLeft, 1) = zwLeft; |
|||
Unsafe.Add(ref dTopLeft, 2) = xyRight; |
|||
Unsafe.Add(ref dTopLeft, 3) = zwRight; |
|||
|
|||
dBottomLeft = xyLeft; |
|||
Unsafe.Add(ref dBottomLeft, 1) = zwLeft; |
|||
Unsafe.Add(ref dBottomLeft, 2) = xyRight; |
|||
Unsafe.Add(ref dBottomLeft, 3) = zwRight; |
|||
} |
|||
} |
|||
|
|||
[MethodImpl(InliningOptions.ColdPath)] |
|||
private void CopyArbitraryScale(ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale) |
|||
{ |
|||
for (int y = 0; y < 8; y++) |
|||
{ |
|||
int yy = y * verticalScale; |
|||
int y8 = y * 8; |
|||
|
|||
for (int x = 0; x < 8; x++) |
|||
{ |
|||
int xx = x * horizontalScale; |
|||
|
|||
float value = this[y8 + x]; |
|||
nint baseIdx = (yy * areaStride) + xx; |
|||
|
|||
for (nint i = 0; i < verticalScale; i++, baseIdx += areaStride) |
|||
{ |
|||
for (nint j = 0; j < horizontalScale; j++) |
|||
{ |
|||
// area[xx + j, yy + i] = value;
|
|||
Unsafe.Add(ref areaOrigin, baseIdx + j) = value; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
private static void CopyTo1x1Scale(ref byte origin, ref byte dest, int areaStride) |
|||
{ |
|||
int destStride = areaStride * sizeof(float); |
|||
|
|||
CopyRowImpl(ref origin, ref dest, destStride, 0); |
|||
CopyRowImpl(ref origin, ref dest, destStride, 1); |
|||
CopyRowImpl(ref origin, ref dest, destStride, 2); |
|||
CopyRowImpl(ref origin, ref dest, destStride, 3); |
|||
CopyRowImpl(ref origin, ref dest, destStride, 4); |
|||
CopyRowImpl(ref origin, ref dest, destStride, 5); |
|||
CopyRowImpl(ref origin, ref dest, destStride, 6); |
|||
CopyRowImpl(ref origin, ref dest, destStride, 7); |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
static void CopyRowImpl(ref byte origin, ref byte dest, int destStride, int row) |
|||
{ |
|||
origin = ref Unsafe.Add(ref origin, row * 8 * sizeof(float)); |
|||
dest = ref Unsafe.Add(ref dest, row * destStride); |
|||
Unsafe.CopyBlock(ref dest, ref origin, 8 * sizeof(float)); |
|||
} |
|||
} |
|||
|
|||
private static void CopyFrom1x1Scale(ref byte origin, ref byte dest, int areaStride) |
|||
{ |
|||
int destStride = areaStride * sizeof(float); |
|||
|
|||
CopyRowImpl(ref origin, ref dest, destStride, 0); |
|||
CopyRowImpl(ref origin, ref dest, destStride, 1); |
|||
CopyRowImpl(ref origin, ref dest, destStride, 2); |
|||
CopyRowImpl(ref origin, ref dest, destStride, 3); |
|||
CopyRowImpl(ref origin, ref dest, destStride, 4); |
|||
CopyRowImpl(ref origin, ref dest, destStride, 5); |
|||
CopyRowImpl(ref origin, ref dest, destStride, 6); |
|||
CopyRowImpl(ref origin, ref dest, destStride, 7); |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
static void CopyRowImpl(ref byte origin, ref byte dest, int sourceStride, int row) |
|||
{ |
|||
origin = ref Unsafe.Add(ref origin, row * sourceStride); |
|||
dest = ref Unsafe.Add(ref dest, row * 8 * sizeof(float)); |
|||
Unsafe.CopyBlock(ref dest, ref origin, 8 * sizeof(float)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,147 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
// ReSharper disable UseObjectOrCollectionInitializer
|
|||
// ReSharper disable InconsistentNaming
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
|||
{ |
|||
internal partial struct Block8x8F |
|||
{ |
|||
/// <summary>
|
|||
/// Copy block data into the destination color buffer pixel area with the provided horizontal and vertical scale factors.
|
|||
/// </summary>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public void ScaledCopyTo(in Buffer2DRegion<float> region, int horizontalScale, int verticalScale) |
|||
{ |
|||
ref float areaOrigin = ref region.GetReferenceToOrigin(); |
|||
this.ScaledCopyTo(ref areaOrigin, region.Stride, horizontalScale, verticalScale); |
|||
} |
|||
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public void ScaledCopyTo(ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale) |
|||
{ |
|||
if (horizontalScale == 1 && verticalScale == 1) |
|||
{ |
|||
this.Copy1x1Scale(ref areaOrigin, areaStride); |
|||
return; |
|||
} |
|||
|
|||
if (horizontalScale == 2 && verticalScale == 2) |
|||
{ |
|||
this.Copy2x2Scale(ref areaOrigin, areaStride); |
|||
return; |
|||
} |
|||
|
|||
// TODO: Optimize: implement all cases with scale-specific, loopless code!
|
|||
this.CopyArbitraryScale(ref areaOrigin, areaStride, horizontalScale, verticalScale); |
|||
} |
|||
|
|||
public void Copy1x1Scale(ref float areaOrigin, int areaStride) |
|||
{ |
|||
ref byte selfBase = ref Unsafe.As<Block8x8F, byte>(ref this); |
|||
ref byte destBase = ref Unsafe.As<float, byte>(ref areaOrigin); |
|||
int destStride = areaStride * sizeof(float); |
|||
|
|||
CopyRowImpl(ref selfBase, ref destBase, destStride, 0); |
|||
CopyRowImpl(ref selfBase, ref destBase, destStride, 1); |
|||
CopyRowImpl(ref selfBase, ref destBase, destStride, 2); |
|||
CopyRowImpl(ref selfBase, ref destBase, destStride, 3); |
|||
CopyRowImpl(ref selfBase, ref destBase, destStride, 4); |
|||
CopyRowImpl(ref selfBase, ref destBase, destStride, 5); |
|||
CopyRowImpl(ref selfBase, ref destBase, destStride, 6); |
|||
CopyRowImpl(ref selfBase, ref destBase, destStride, 7); |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static void CopyRowImpl(ref byte selfBase, ref byte destBase, int destStride, int row) |
|||
{ |
|||
ref byte s = ref Unsafe.Add(ref selfBase, row * 8 * sizeof(float)); |
|||
ref byte d = ref Unsafe.Add(ref destBase, row * destStride); |
|||
Unsafe.CopyBlock(ref d, ref s, 8 * sizeof(float)); |
|||
} |
|||
|
|||
private void Copy2x2Scale(ref float areaOrigin, int areaStride) |
|||
{ |
|||
ref Vector2 destBase = ref Unsafe.As<float, Vector2>(ref areaOrigin); |
|||
int destStride = areaStride / 2; |
|||
|
|||
this.WidenCopyRowImpl2x2(ref destBase, 0, destStride); |
|||
this.WidenCopyRowImpl2x2(ref destBase, 1, destStride); |
|||
this.WidenCopyRowImpl2x2(ref destBase, 2, destStride); |
|||
this.WidenCopyRowImpl2x2(ref destBase, 3, destStride); |
|||
this.WidenCopyRowImpl2x2(ref destBase, 4, destStride); |
|||
this.WidenCopyRowImpl2x2(ref destBase, 5, destStride); |
|||
this.WidenCopyRowImpl2x2(ref destBase, 6, destStride); |
|||
this.WidenCopyRowImpl2x2(ref destBase, 7, destStride); |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private void WidenCopyRowImpl2x2(ref Vector2 destBase, int row, int destStride) |
|||
{ |
|||
ref Vector4 sLeft = ref Unsafe.Add(ref this.V0L, 2 * row); |
|||
ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); |
|||
|
|||
int offset = 2 * row * destStride; |
|||
ref Vector4 dTopLeft = ref Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset)); |
|||
ref Vector4 dBottomLeft = ref Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset + destStride)); |
|||
|
|||
var xyLeft = new Vector4(sLeft.X); |
|||
xyLeft.Z = sLeft.Y; |
|||
xyLeft.W = sLeft.Y; |
|||
|
|||
var zwLeft = new Vector4(sLeft.Z); |
|||
zwLeft.Z = sLeft.W; |
|||
zwLeft.W = sLeft.W; |
|||
|
|||
var xyRight = new Vector4(sRight.X); |
|||
xyRight.Z = sRight.Y; |
|||
xyRight.W = sRight.Y; |
|||
|
|||
var zwRight = new Vector4(sRight.Z); |
|||
zwRight.Z = sRight.W; |
|||
zwRight.W = sRight.W; |
|||
|
|||
dTopLeft = xyLeft; |
|||
Unsafe.Add(ref dTopLeft, 1) = zwLeft; |
|||
Unsafe.Add(ref dTopLeft, 2) = xyRight; |
|||
Unsafe.Add(ref dTopLeft, 3) = zwRight; |
|||
|
|||
dBottomLeft = xyLeft; |
|||
Unsafe.Add(ref dBottomLeft, 1) = zwLeft; |
|||
Unsafe.Add(ref dBottomLeft, 2) = xyRight; |
|||
Unsafe.Add(ref dBottomLeft, 3) = zwRight; |
|||
} |
|||
|
|||
[MethodImpl(InliningOptions.ColdPath)] |
|||
private void CopyArbitraryScale(ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale) |
|||
{ |
|||
for (int y = 0; y < 8; y++) |
|||
{ |
|||
int yy = y * verticalScale; |
|||
int y8 = y * 8; |
|||
|
|||
for (int x = 0; x < 8; x++) |
|||
{ |
|||
int xx = x * horizontalScale; |
|||
|
|||
float value = this[y8 + x]; |
|||
|
|||
for (int i = 0; i < verticalScale; i++) |
|||
{ |
|||
int baseIdx = ((yy + i) * areaStride) + xx; |
|||
|
|||
for (int j = 0; j < horizontalScale; j++) |
|||
{ |
|||
// area[xx + j, yy + i] = value;
|
|||
Unsafe.Add(ref areaOrigin, baseIdx + j) = value; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,99 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using System.Runtime.Intrinsics; |
|||
using System.Runtime.Intrinsics.X86; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
|||
{ |
|||
internal abstract partial class JpegColorConverterBase |
|||
{ |
|||
internal sealed class CmykAvx : JpegColorConverterAvx |
|||
{ |
|||
public CmykAvx(int precision) |
|||
: base(JpegColorSpace.Cmyk, precision) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
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); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|||
=> ConvertFromRgb(in values, this.MaximumValue, rLane, gLane, bLane); |
|||
|
|||
public static void ConvertFromRgb(in ComponentValues values, float maxValue, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|||
{ |
|||
ref Vector256<float> destC = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
ref Vector256<float> destM = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|||
ref Vector256<float> destY = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|||
ref Vector256<float> destK = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
|||
|
|||
ref Vector256<float> srcR = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(rLane)); |
|||
ref Vector256<float> srcG = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(gLane)); |
|||
ref Vector256<float> srcB = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(bLane)); |
|||
|
|||
var scale = Vector256.Create(maxValue); |
|||
|
|||
nint n = values.Component0.Length / Vector256<float>.Count; |
|||
for (nint i = 0; i < n; i++) |
|||
{ |
|||
Vector256<float> ctmp = Avx.Subtract(scale, Unsafe.Add(ref srcR, i)); |
|||
Vector256<float> mtmp = Avx.Subtract(scale, Unsafe.Add(ref srcG, i)); |
|||
Vector256<float> ytmp = Avx.Subtract(scale, Unsafe.Add(ref srcB, i)); |
|||
Vector256<float> ktmp = Avx.Min(ctmp, Avx.Min(mtmp, ytmp)); |
|||
|
|||
Vector256<float> kMask = Avx.CompareNotEqual(ktmp, scale); |
|||
|
|||
ctmp = Avx.And(Avx.Divide(Avx.Subtract(ctmp, ktmp), Avx.Subtract(scale, ktmp)), kMask); |
|||
mtmp = Avx.And(Avx.Divide(Avx.Subtract(mtmp, ktmp), Avx.Subtract(scale, ktmp)), kMask); |
|||
ytmp = Avx.And(Avx.Divide(Avx.Subtract(ytmp, ktmp), Avx.Subtract(scale, ktmp)), kMask); |
|||
|
|||
Unsafe.Add(ref destC, i) = Avx.Subtract(scale, Avx.Multiply(ctmp, scale)); |
|||
Unsafe.Add(ref destM, i) = Avx.Subtract(scale, Avx.Multiply(mtmp, scale)); |
|||
Unsafe.Add(ref destY, i) = Avx.Subtract(scale, Avx.Multiply(ytmp, scale)); |
|||
Unsafe.Add(ref destK, i) = Avx.Subtract(scale, ktmp); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
#endif
|
|||
@ -0,0 +1,82 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
|||
{ |
|||
internal abstract partial class JpegColorConverterBase |
|||
{ |
|||
internal sealed class CmykScalar : JpegColorConverterScalar |
|||
{ |
|||
public CmykScalar(int precision) |
|||
: base(JpegColorSpace.Cmyk, precision) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void ConvertToRgbInplace(in ComponentValues values) => |
|||
ConvertToRgbInplace(values, this.MaximumValue); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void ConvertFromRgb(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b) |
|||
=> ConvertFromRgb(values, this.MaximumValue, r, g, b); |
|||
|
|||
public static void ConvertToRgbInplace(in ComponentValues values, float maxValue) |
|||
{ |
|||
Span<float> c0 = values.Component0; |
|||
Span<float> c1 = values.Component1; |
|||
Span<float> c2 = values.Component2; |
|||
Span<float> c3 = values.Component3; |
|||
|
|||
float scale = 1 / (maxValue * maxValue); |
|||
for (int i = 0; i < c0.Length; i++) |
|||
{ |
|||
float c = c0[i]; |
|||
float m = c1[i]; |
|||
float y = c2[i]; |
|||
float k = c3[i]; |
|||
|
|||
k *= scale; |
|||
c0[i] = c * k; |
|||
c1[i] = m * k; |
|||
c2[i] = y * k; |
|||
} |
|||
} |
|||
|
|||
public static void ConvertFromRgb(in ComponentValues values, float maxValue, Span<float> r, Span<float> g, Span<float> b) |
|||
{ |
|||
Span<float> c = values.Component0; |
|||
Span<float> m = values.Component1; |
|||
Span<float> y = values.Component2; |
|||
Span<float> k = values.Component3; |
|||
|
|||
for (int i = 0; i < c.Length; i++) |
|||
{ |
|||
float ctmp = 255f - r[i]; |
|||
float mtmp = 255f - g[i]; |
|||
float ytmp = 255f - b[i]; |
|||
float ktmp = MathF.Min(MathF.Min(ctmp, mtmp), ytmp); |
|||
|
|||
if (ktmp >= 255f) |
|||
{ |
|||
ctmp = 0f; |
|||
mtmp = 0f; |
|||
ytmp = 0f; |
|||
} |
|||
else |
|||
{ |
|||
ctmp = (ctmp - ktmp) / (255f - ktmp); |
|||
mtmp = (mtmp - ktmp) / (255f - ktmp); |
|||
ytmp = (ytmp - ktmp) / (255f - ktmp); |
|||
} |
|||
|
|||
c[i] = maxValue - (ctmp * maxValue); |
|||
m[i] = maxValue - (mtmp * maxValue); |
|||
y[i] = maxValue - (ytmp * maxValue); |
|||
k[i] = maxValue - ktmp; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,106 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
|||
{ |
|||
internal abstract partial class JpegColorConverterBase |
|||
{ |
|||
internal sealed class CmykVector : JpegColorConverterVector |
|||
{ |
|||
public CmykVector(int precision) |
|||
: base(JpegColorSpace.Cmyk, precision) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void ConvertToRgbInplaceVectorized(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; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void ConvertToRgbInplaceScalarRemainder(in ComponentValues values) |
|||
=> CmykScalar.ConvertToRgbInplace(values, this.MaximumValue); |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void ConvertFromRgbVectorized(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b) |
|||
=> ConvertFromRgbInplaceVectorized(in values, this.MaximumValue, r, g, b); |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b) |
|||
=> ConvertFromRgbInplaceRemainder(values, this.MaximumValue, r, g, b); |
|||
|
|||
public static void ConvertFromRgbInplaceVectorized(in ComponentValues values, float maxValue, Span<float> r, Span<float> g, Span<float> b) |
|||
{ |
|||
ref Vector<float> destC = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
ref Vector<float> destM = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|||
ref Vector<float> destY = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|||
ref Vector<float> destK = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
|||
|
|||
ref Vector<float> srcR = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(r)); |
|||
ref Vector<float> srcG = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(g)); |
|||
ref Vector<float> srcB = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(b)); |
|||
|
|||
// Used for the color conversion
|
|||
var scale = new Vector<float>(maxValue); |
|||
|
|||
nint n = values.Component0.Length / Vector<float>.Count; |
|||
for (nint i = 0; i < n; i++) |
|||
{ |
|||
Vector<float> ctmp = scale - Unsafe.Add(ref srcR, i); |
|||
Vector<float> mtmp = scale - Unsafe.Add(ref srcG, i); |
|||
Vector<float> ytmp = scale - Unsafe.Add(ref srcB, i); |
|||
Vector<float> ktmp = Vector.Min(ctmp, Vector.Min(mtmp, ytmp)); |
|||
|
|||
var kMask = Vector.Equals(ktmp, scale); |
|||
ctmp = Vector.AndNot((ctmp - ktmp) / (scale - ktmp), kMask.As<int, float>()); |
|||
mtmp = Vector.AndNot((mtmp - ktmp) / (scale - ktmp), kMask.As<int, float>()); |
|||
ytmp = Vector.AndNot((ytmp - ktmp) / (scale - ktmp), kMask.As<int, float>()); |
|||
|
|||
Unsafe.Add(ref destC, i) = scale - (ctmp * scale); |
|||
Unsafe.Add(ref destM, i) = scale - (mtmp * scale); |
|||
Unsafe.Add(ref destY, i) = scale - (ytmp * scale); |
|||
Unsafe.Add(ref destK, i) = scale - ktmp; |
|||
} |
|||
} |
|||
|
|||
public static void ConvertFromRgbInplaceRemainder(in ComponentValues values, float maxValue, Span<float> r, Span<float> g, Span<float> b) |
|||
=> CmykScalar.ConvertFromRgb(values, maxValue, r, g, b); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,72 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using System.Runtime.Intrinsics; |
|||
using System.Runtime.Intrinsics.X86; |
|||
using static SixLabors.ImageSharp.SimdUtils; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
|||
{ |
|||
internal abstract partial class JpegColorConverterBase |
|||
{ |
|||
internal sealed class GrayscaleAvx : JpegColorConverterAvx |
|||
{ |
|||
public GrayscaleAvx(int precision) |
|||
: base(JpegColorSpace.Grayscale, precision) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void ConvertToRgbInplace(in ComponentValues values) |
|||
{ |
|||
ref Vector256<float> c0Base = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
|
|||
// Used for the color conversion
|
|||
var scale = Vector256.Create(1 / this.MaximumValue); |
|||
|
|||
nint n = values.Component0.Length / Vector256<float>.Count; |
|||
for (nint i = 0; i < n; i++) |
|||
{ |
|||
ref Vector256<float> c0 = ref Unsafe.Add(ref c0Base, i); |
|||
c0 = Avx.Multiply(c0, scale); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|||
{ |
|||
ref Vector256<float> destLuminance = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
|
|||
ref Vector256<float> srcRed = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(rLane)); |
|||
ref Vector256<float> srcGreen = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(gLane)); |
|||
ref Vector256<float> srcBlue = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(bLane)); |
|||
|
|||
// Used for the color conversion
|
|||
var f0299 = Vector256.Create(0.299f); |
|||
var f0587 = Vector256.Create(0.587f); |
|||
var f0114 = Vector256.Create(0.114f); |
|||
|
|||
nint n = values.Component0.Length / Vector256<float>.Count; |
|||
for (nint i = 0; i < n; i++) |
|||
{ |
|||
ref Vector256<float> r = ref Unsafe.Add(ref srcRed, i); |
|||
ref Vector256<float> g = ref Unsafe.Add(ref srcGreen, i); |
|||
ref Vector256<float> b = ref Unsafe.Add(ref srcBlue, i); |
|||
|
|||
// luminocity = (0.299 * r) + (0.587 * g) + (0.114 * b)
|
|||
Unsafe.Add(ref destLuminance, i) = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(Avx.Multiply(f0114, b), f0587, g), f0299, r); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
#endif
|
|||
@ -0,0 +1,51 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
|||
{ |
|||
internal abstract partial class JpegColorConverterBase |
|||
{ |
|||
internal sealed class GrayscaleScalar : JpegColorConverterScalar |
|||
{ |
|||
public GrayscaleScalar(int precision) |
|||
: base(JpegColorSpace.Grayscale, precision) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void ConvertToRgbInplace(in ComponentValues values) |
|||
=> ConvertToRgbInplace(values.Component0, this.MaximumValue); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void ConvertFromRgb(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b) |
|||
=> ConvertCoreInplaceFromRgb(values, r, g, b); |
|||
|
|||
internal static void ConvertToRgbInplace(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; |
|||
} |
|||
} |
|||
|
|||
internal static void ConvertCoreInplaceFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|||
{ |
|||
Span<float> c0 = values.Component0; |
|||
|
|||
for (int i = 0; i < c0.Length; i++) |
|||
{ |
|||
// luminocity = (0.299 * r) + (0.587 * g) + (0.114 * b)
|
|||
float luma = (0.299f * rLane[i]) + (0.587f * gLane[i]) + (0.114f * bLane[i]); |
|||
c0[i] = luma; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,74 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
|||
{ |
|||
internal abstract partial class JpegColorConverterBase |
|||
{ |
|||
internal sealed class GrayScaleVector : JpegColorConverterVector |
|||
{ |
|||
public GrayScaleVector(int precision) |
|||
: base(JpegColorSpace.Grayscale, precision) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void ConvertToRgbInplaceVectorized(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; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void ConvertToRgbInplaceScalarRemainder(in ComponentValues values) |
|||
=> GrayscaleScalar.ConvertToRgbInplace(values.Component0, this.MaximumValue); |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void ConvertFromRgbVectorized(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|||
{ |
|||
ref Vector<float> destLuma = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
|
|||
ref Vector<float> srcR = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(rLane)); |
|||
ref Vector<float> srcG = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(gLane)); |
|||
ref Vector<float> srcB = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(bLane)); |
|||
|
|||
var rMult = new Vector<float>(0.299f); |
|||
var gMult = new Vector<float>(0.587f); |
|||
var bMult = new Vector<float>(0.114f); |
|||
|
|||
nint n = values.Component0.Length / Vector<float>.Count; |
|||
for (nint i = 0; i < n; i++) |
|||
{ |
|||
Vector<float> r = Unsafe.Add(ref srcR, i); |
|||
Vector<float> g = Unsafe.Add(ref srcR, i); |
|||
Vector<float> b = Unsafe.Add(ref srcR, i); |
|||
|
|||
// luminocity = (0.299 * r) + (0.587 * g) + (0.114 * b)
|
|||
Unsafe.Add(ref destLuma, i) = (rMult * r) + (gMult * g) + (bMult * b); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b) |
|||
=> GrayscaleScalar.ConvertCoreInplaceFromRgb(values, r, g, b); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
|||
{ |
|||
internal abstract partial class JpegColorConverterBase |
|||
{ |
|||
internal sealed class RgbScalar : JpegColorConverterScalar |
|||
{ |
|||
public RgbScalar(int precision) |
|||
: base(JpegColorSpace.RGB, precision) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void ConvertToRgbInplace(in ComponentValues values) |
|||
=> ConvertToRgbInplace(values, this.MaximumValue); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void ConvertFromRgb(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b) |
|||
=> ConvertFromRgb(values, r, g, b); |
|||
|
|||
internal static void ConvertToRgbInplace(ComponentValues values, float maxValue) |
|||
{ |
|||
GrayscaleScalar.ConvertToRgbInplace(values.Component0, maxValue); |
|||
GrayscaleScalar.ConvertToRgbInplace(values.Component1, maxValue); |
|||
GrayscaleScalar.ConvertToRgbInplace(values.Component2, maxValue); |
|||
} |
|||
|
|||
internal static void ConvertFromRgb(ComponentValues values, Span<float> r, Span<float> g, Span<float> b) |
|||
{ |
|||
r.CopyTo(values.Component0); |
|||
g.CopyTo(values.Component1); |
|||
b.CopyTo(values.Component2); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,125 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using System.Runtime.Intrinsics; |
|||
using System.Runtime.Intrinsics.X86; |
|||
using static SixLabors.ImageSharp.SimdUtils; |
|||
|
|||
// ReSharper disable ImpureMethodCallOnReadonlyValueField
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
|||
{ |
|||
internal abstract partial class JpegColorConverterBase |
|||
{ |
|||
internal sealed class YCbCrAvx : JpegColorConverterAvx |
|||
{ |
|||
public YCbCrAvx(int precision) |
|||
: base(JpegColorSpace.YCbCr, precision) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
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)); |
|||
|
|||
// Used for the color conversion
|
|||
var chromaOffset = Vector256.Create(-this.HalfValue); |
|||
var scale = Vector256.Create(1 / this.MaximumValue); |
|||
var rCrMult = Vector256.Create(YCbCrScalar.RCrMult); |
|||
var gCbMult = Vector256.Create(-YCbCrScalar.GCbMult); |
|||
var gCrMult = Vector256.Create(-YCbCrScalar.GCrMult); |
|||
var bCbMult = Vector256.Create(YCbCrScalar.BCbMult); |
|||
|
|||
// Walking 8 elements at one step:
|
|||
nint n = values.Component0.Length / Vector256<float>.Count; |
|||
for (nint i = 0; i < n; i++) |
|||
{ |
|||
// y = yVals[i];
|
|||
// cb = cbVals[i] - 128F;
|
|||
// cr = crVals[i] - 128F;
|
|||
ref Vector256<float> c0 = ref Unsafe.Add(ref c0Base, i); |
|||
ref Vector256<float> c1 = ref Unsafe.Add(ref c1Base, i); |
|||
ref Vector256<float> c2 = ref Unsafe.Add(ref c2Base, i); |
|||
|
|||
Vector256<float> y = c0; |
|||
Vector256<float> cb = Avx.Add(c1, chromaOffset); |
|||
Vector256<float> cr = Avx.Add(c2, chromaOffset); |
|||
|
|||
// r = y + (1.402F * cr);
|
|||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|||
// b = y + (1.772F * cb);
|
|||
Vector256<float> r = HwIntrinsics.MultiplyAdd(y, cr, rCrMult); |
|||
Vector256<float> g = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); |
|||
Vector256<float> b = HwIntrinsics.MultiplyAdd(y, cb, bCbMult); |
|||
|
|||
r = Avx.Multiply(Avx.RoundToNearestInteger(r), scale); |
|||
g = Avx.Multiply(Avx.RoundToNearestInteger(g), scale); |
|||
b = Avx.Multiply(Avx.RoundToNearestInteger(b), scale); |
|||
|
|||
c0 = r; |
|||
c1 = g; |
|||
c2 = b; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|||
{ |
|||
ref Vector256<float> destY = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
ref Vector256<float> destCb = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|||
ref Vector256<float> destCr = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|||
|
|||
ref Vector256<float> srcR = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(rLane)); |
|||
ref Vector256<float> srcG = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(gLane)); |
|||
ref Vector256<float> srcB = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(bLane)); |
|||
|
|||
// Used for the color conversion
|
|||
var chromaOffset = Vector256.Create(this.HalfValue); |
|||
|
|||
var f0299 = Vector256.Create(0.299f); |
|||
var f0587 = Vector256.Create(0.587f); |
|||
var f0114 = Vector256.Create(0.114f); |
|||
var fn0168736 = Vector256.Create(-0.168736f); |
|||
var fn0331264 = Vector256.Create(-0.331264f); |
|||
var fn0418688 = Vector256.Create(-0.418688f); |
|||
var fn0081312F = Vector256.Create(-0.081312F); |
|||
var f05 = Vector256.Create(0.5f); |
|||
|
|||
nint n = values.Component0.Length / Vector256<float>.Count; |
|||
for (nint i = 0; i < n; i++) |
|||
{ |
|||
Vector256<float> r = Unsafe.Add(ref srcR, i); |
|||
Vector256<float> g = Unsafe.Add(ref srcG, i); |
|||
Vector256<float> b = Unsafe.Add(ref srcB, i); |
|||
|
|||
// y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b)
|
|||
// cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b)
|
|||
// cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b)
|
|||
Vector256<float> y = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(Avx.Multiply(f0114, b), f0587, g), f0299, r); |
|||
Vector256<float> cb = Avx.Add(chromaOffset, HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(Avx.Multiply(f05, b), fn0331264, g), fn0168736, r)); |
|||
Vector256<float> cr = Avx.Add(chromaOffset, HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(Avx.Multiply(fn0081312F, b), fn0418688, g), f05, r)); |
|||
|
|||
Unsafe.Add(ref destY, i) = y; |
|||
Unsafe.Add(ref destCb, i) = cb; |
|||
Unsafe.Add(ref destCr, i) = cr; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
#endif
|
|||
@ -0,0 +1,76 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
|||
{ |
|||
internal abstract partial class JpegColorConverterBase |
|||
{ |
|||
internal sealed class YCbCrScalar : JpegColorConverterScalar |
|||
{ |
|||
// 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 YCbCrScalar(int precision) |
|||
: base(JpegColorSpace.YCbCr, precision) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void ConvertToRgbInplace(in ComponentValues values) |
|||
=> ConvertToRgbInplace(values, this.MaximumValue, this.HalfValue); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void ConvertFromRgb(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b) |
|||
=> ConvertFromRgb(values, this.HalfValue, r, g, b); |
|||
|
|||
public static void ConvertToRgbInplace(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; |
|||
} |
|||
} |
|||
|
|||
public static void ConvertFromRgb(in ComponentValues values, float halfValue, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|||
{ |
|||
Span<float> y = values.Component0; |
|||
Span<float> cb = values.Component1; |
|||
Span<float> cr = values.Component2; |
|||
|
|||
for (int i = 0; i < y.Length; i++) |
|||
{ |
|||
float r = rLane[i]; |
|||
float g = gLane[i]; |
|||
float b = bLane[i]; |
|||
|
|||
// y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b)
|
|||
// cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b)
|
|||
// cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b)
|
|||
y[i] = (0.299f * r) + (0.587f * g) + (0.114f * b); |
|||
cb[i] = halfValue - (0.168736f * r) - (0.331264f * g) + (0.5f * b); |
|||
cr[i] = halfValue + (0.5f * r) - (0.418688f * g) - (0.081312f * b); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,128 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
// ReSharper disable ImpureMethodCallOnReadonlyValueField
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
|||
{ |
|||
internal abstract partial class JpegColorConverterBase |
|||
{ |
|||
internal sealed class YCbCrVector : JpegColorConverterVector |
|||
{ |
|||
public YCbCrVector(int precision) |
|||
: base(JpegColorSpace.YCbCr, precision) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void ConvertToRgbInplaceVectorized(in ComponentValues values) |
|||
{ |
|||
ref Vector<float> c0Base = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
ref Vector<float> c1Base = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|||
ref Vector<float> c2Base = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|||
|
|||
var chromaOffset = new Vector<float>(-this.HalfValue); |
|||
|
|||
var scale = new Vector<float>(1 / this.MaximumValue); |
|||
var rCrMult = new Vector<float>(YCbCrScalar.RCrMult); |
|||
var gCbMult = new Vector<float>(-YCbCrScalar.GCbMult); |
|||
var gCrMult = new Vector<float>(-YCbCrScalar.GCrMult); |
|||
var bCbMult = new Vector<float>(YCbCrScalar.BCbMult); |
|||
|
|||
nint n = values.Component0.Length / Vector<float>.Count; |
|||
for (nint i = 0; i < n; i++) |
|||
{ |
|||
// y = yVals[i];
|
|||
// cb = cbVals[i] - 128F;
|
|||
// cr = crVals[i] - 128F;
|
|||
ref Vector<float> c0 = ref Unsafe.Add(ref c0Base, i); |
|||
ref Vector<float> c1 = ref Unsafe.Add(ref c1Base, i); |
|||
ref Vector<float> c2 = ref Unsafe.Add(ref c2Base, i); |
|||
Vector<float> y = Unsafe.Add(ref c0Base, i); |
|||
Vector<float> cb = Unsafe.Add(ref c1Base, i) + chromaOffset; |
|||
Vector<float> cr = Unsafe.Add(ref c2Base, i) + chromaOffset; |
|||
|
|||
// r = y + (1.402F * cr);
|
|||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|||
// b = y + (1.772F * cb);
|
|||
Vector<float> r = y + (cr * rCrMult); |
|||
Vector<float> g = y + (cb * gCbMult) + (cr * gCrMult); |
|||
Vector<float> b = y + (cb * bCbMult); |
|||
|
|||
r = r.FastRound(); |
|||
g = g.FastRound(); |
|||
b = b.FastRound(); |
|||
r *= scale; |
|||
g *= scale; |
|||
b *= scale; |
|||
|
|||
c0 = r; |
|||
c1 = g; |
|||
c2 = b; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void ConvertToRgbInplaceScalarRemainder(in ComponentValues values) |
|||
=> YCbCrScalar.ConvertToRgbInplace(values, this.MaximumValue, this.HalfValue); |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void ConvertFromRgbVectorized(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|||
{ |
|||
ref Vector<float> destY = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
ref Vector<float> destCb = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|||
ref Vector<float> destCr = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|||
|
|||
ref Vector<float> srcR = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(rLane)); |
|||
ref Vector<float> srcG = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(gLane)); |
|||
ref Vector<float> srcB = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(bLane)); |
|||
|
|||
var chromaOffset = new Vector<float>(this.HalfValue); |
|||
|
|||
var rYMult = new Vector<float>(0.299f); |
|||
var gYMult = new Vector<float>(0.587f); |
|||
var bYMult = new Vector<float>(0.114f); |
|||
|
|||
var rCbMult = new Vector<float>(0.168736f); |
|||
var gCbMult = new Vector<float>(0.331264f); |
|||
var bCbMult = new Vector<float>(0.5f); |
|||
|
|||
var rCrMult = new Vector<float>(0.5f); |
|||
var gCrMult = new Vector<float>(0.418688f); |
|||
var bCrMult = new Vector<float>(0.081312f); |
|||
|
|||
nint n = values.Component0.Length / Vector<float>.Count; |
|||
for (nint i = 0; i < n; i++) |
|||
{ |
|||
Vector<float> r = Unsafe.Add(ref srcR, i); |
|||
Vector<float> g = Unsafe.Add(ref srcG, i); |
|||
Vector<float> b = Unsafe.Add(ref srcB, i); |
|||
|
|||
// y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b)
|
|||
// cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b)
|
|||
// cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b)
|
|||
Unsafe.Add(ref destY, i) = (rYMult * r) + (gYMult * g) + (bYMult * b); |
|||
Unsafe.Add(ref destCb, i) = chromaOffset - (rCbMult * r) - (gCbMult * g) + (bCbMult * b); |
|||
Unsafe.Add(ref destCr, i) = chromaOffset + (rCrMult * r) - (gCrMult * g) - (bCrMult * b); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b) |
|||
=> YCbCrScalar.ConvertFromRgb(values, this.HalfValue, r, g, b); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,136 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using System.Runtime.Intrinsics; |
|||
using System.Runtime.Intrinsics.X86; |
|||
using static SixLabors.ImageSharp.SimdUtils; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
|||
{ |
|||
internal abstract partial class JpegColorConverterBase |
|||
{ |
|||
internal sealed class YccKAvx : JpegColorConverterAvx |
|||
{ |
|||
public YccKAvx(int precision) |
|||
: base(JpegColorSpace.Ycck, precision) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
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> kBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
|||
|
|||
// Used for the color conversion
|
|||
var chromaOffset = Vector256.Create(-this.HalfValue); |
|||
var scale = Vector256.Create(1 / (this.MaximumValue * this.MaximumValue)); |
|||
var max = Vector256.Create(this.MaximumValue); |
|||
var rCrMult = Vector256.Create(YCbCrScalar.RCrMult); |
|||
var gCbMult = Vector256.Create(-YCbCrScalar.GCbMult); |
|||
var gCrMult = Vector256.Create(-YCbCrScalar.GCrMult); |
|||
var bCbMult = Vector256.Create(YCbCrScalar.BCbMult); |
|||
|
|||
// Walking 8 elements at one step:
|
|||
nint n = values.Component0.Length / Vector256<float>.Count; |
|||
for (nint i = 0; i < n; i++) |
|||
{ |
|||
// y = yVals[i];
|
|||
// cb = cbVals[i] - 128F;
|
|||
// cr = crVals[i] - 128F;
|
|||
// k = kVals[i] / 256F;
|
|||
ref Vector256<float> c0 = ref Unsafe.Add(ref c0Base, i); |
|||
ref Vector256<float> c1 = ref Unsafe.Add(ref c1Base, i); |
|||
ref Vector256<float> c2 = ref Unsafe.Add(ref c2Base, i); |
|||
Vector256<float> y = c0; |
|||
Vector256<float> cb = Avx.Add(c1, chromaOffset); |
|||
Vector256<float> cr = Avx.Add(c2, chromaOffset); |
|||
Vector256<float> scaledK = Avx.Multiply(Unsafe.Add(ref kBase, i), scale); |
|||
|
|||
// r = y + (1.402F * cr);
|
|||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|||
// b = y + (1.772F * cb);
|
|||
Vector256<float> r = HwIntrinsics.MultiplyAdd(y, cr, rCrMult); |
|||
Vector256<float> g = |
|||
HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); |
|||
Vector256<float> b = HwIntrinsics.MultiplyAdd(y, cb, bCbMult); |
|||
|
|||
r = Avx.Subtract(max, Avx.RoundToNearestInteger(r)); |
|||
g = Avx.Subtract(max, Avx.RoundToNearestInteger(g)); |
|||
b = Avx.Subtract(max, Avx.RoundToNearestInteger(b)); |
|||
|
|||
r = Avx.Multiply(r, scaledK); |
|||
g = Avx.Multiply(g, scaledK); |
|||
b = Avx.Multiply(b, scaledK); |
|||
|
|||
c0 = r; |
|||
c1 = g; |
|||
c2 = b; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void ConvertFromRgb(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|||
{ |
|||
// rgb -> cmyk
|
|||
CmykAvx.ConvertFromRgb(in values, this.MaximumValue, rLane, gLane, bLane); |
|||
|
|||
// cmyk -> ycck
|
|||
ref Vector256<float> destY = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
ref Vector256<float> destCb = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|||
ref Vector256<float> destCr = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|||
|
|||
ref Vector256<float> srcR = ref destY; |
|||
ref Vector256<float> srcG = ref destCb; |
|||
ref Vector256<float> srcB = ref destCr; |
|||
|
|||
// Used for the color conversion
|
|||
var maxSampleValue = Vector256.Create(this.MaximumValue); |
|||
|
|||
var chromaOffset = Vector256.Create(this.HalfValue); |
|||
|
|||
var f0299 = Vector256.Create(0.299f); |
|||
var f0587 = Vector256.Create(0.587f); |
|||
var f0114 = Vector256.Create(0.114f); |
|||
var fn0168736 = Vector256.Create(-0.168736f); |
|||
var fn0331264 = Vector256.Create(-0.331264f); |
|||
var fn0418688 = Vector256.Create(-0.418688f); |
|||
var fn0081312F = Vector256.Create(-0.081312F); |
|||
var f05 = Vector256.Create(0.5f); |
|||
|
|||
nint n = values.Component0.Length / Vector256<float>.Count; |
|||
for (nint i = 0; i < n; i++) |
|||
{ |
|||
Vector256<float> r = Avx.Subtract(maxSampleValue, Unsafe.Add(ref srcR, i)); |
|||
Vector256<float> g = Avx.Subtract(maxSampleValue, Unsafe.Add(ref srcG, i)); |
|||
Vector256<float> b = Avx.Subtract(maxSampleValue, Unsafe.Add(ref srcB, i)); |
|||
|
|||
// y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b)
|
|||
// cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b)
|
|||
// cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b)
|
|||
Vector256<float> y = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(Avx.Multiply(f0114, b), f0587, g), f0299, r); |
|||
Vector256<float> cb = Avx.Add(chromaOffset, HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(Avx.Multiply(f05, b), fn0331264, g), fn0168736, r)); |
|||
Vector256<float> cr = Avx.Add(chromaOffset, HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(Avx.Multiply(fn0081312F, b), fn0418688, g), f05, r)); |
|||
|
|||
Unsafe.Add(ref destY, i) = y; |
|||
Unsafe.Add(ref destCb, i) = cb; |
|||
Unsafe.Add(ref destCr, i) = cr; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
#endif
|
|||
@ -0,0 +1,80 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
|||
{ |
|||
internal abstract partial class JpegColorConverterBase |
|||
{ |
|||
internal sealed class YccKScalar : JpegColorConverterScalar |
|||
{ |
|||
// 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 YccKScalar(int precision) |
|||
: base(JpegColorSpace.Ycck, precision) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void ConvertToRgbInplace(in ComponentValues values) |
|||
=> ConvertToRgpInplace(values, this.MaximumValue, this.HalfValue); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void ConvertFromRgb(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b) |
|||
=> ConvertFromRgb(values, this.HalfValue, this.MaximumValue, r, g, b); |
|||
|
|||
public static void ConvertToRgpInplace(in ComponentValues values, float maxValue, float halfValue) |
|||
{ |
|||
Span<float> c0 = values.Component0; |
|||
Span<float> c1 = values.Component1; |
|||
Span<float> c2 = values.Component2; |
|||
Span<float> c3 = values.Component3; |
|||
|
|||
float scale = 1 / (maxValue * maxValue); |
|||
|
|||
for (int i = 0; i < values.Component0.Length; i++) |
|||
{ |
|||
float y = c0[i]; |
|||
float cb = c1[i] - halfValue; |
|||
float cr = c2[i] - halfValue; |
|||
float scaledK = c3[i] * scale; |
|||
|
|||
// r = y + (1.402F * cr);
|
|||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|||
// b = y + (1.772F * cb);
|
|||
c0[i] = (maxValue - MathF.Round(y + (RCrMult * cr), MidpointRounding.AwayFromZero)) * scaledK; |
|||
c1[i] = (maxValue - MathF.Round(y - (GCbMult * cb) - (GCrMult * cr), MidpointRounding.AwayFromZero)) * scaledK; |
|||
c2[i] = (maxValue - MathF.Round(y + (BCbMult * cb), MidpointRounding.AwayFromZero)) * scaledK; |
|||
} |
|||
} |
|||
|
|||
public static void ConvertFromRgb(in ComponentValues values, float halfValue, float maxValue, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|||
{ |
|||
// rgb -> cmyk
|
|||
CmykScalar.ConvertFromRgb(in values, maxValue, rLane, gLane, bLane); |
|||
|
|||
// cmyk -> ycck
|
|||
Span<float> c = values.Component0; |
|||
Span<float> m = values.Component1; |
|||
Span<float> y = values.Component2; |
|||
|
|||
for (int i = 0; i < y.Length; i++) |
|||
{ |
|||
float r = maxValue - c[i]; |
|||
float g = maxValue - m[i]; |
|||
float b = maxValue - y[i]; |
|||
|
|||
// k value is passed untouched from rgb -> cmyk conversion
|
|||
c[i] = (0.299f * r) + (0.587f * g) + (0.114f * b); |
|||
m[i] = halfValue - (0.168736f * r) - (0.331264f * g) + (0.5f * b); |
|||
y[i] = halfValue + (0.5f * r) - (0.418688f * g) - (0.081312f * b); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,138 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
|||
{ |
|||
internal abstract partial class JpegColorConverterBase |
|||
{ |
|||
internal sealed class YccKVector : JpegColorConverterVector |
|||
{ |
|||
public YccKVector(int precision) |
|||
: base(JpegColorSpace.Ycck, precision) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void ConvertToRgbInplaceVectorized(in ComponentValues values) |
|||
{ |
|||
ref Vector<float> c0Base = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
ref Vector<float> c1Base = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|||
ref Vector<float> c2Base = |
|||
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 chromaOffset = new Vector<float>(-this.HalfValue); |
|||
var scale = new Vector<float>(1 / (this.MaximumValue * this.MaximumValue)); |
|||
var max = new Vector<float>(this.MaximumValue); |
|||
var rCrMult = new Vector<float>(YCbCrScalar.RCrMult); |
|||
var gCbMult = new Vector<float>(-YCbCrScalar.GCbMult); |
|||
var gCrMult = new Vector<float>(-YCbCrScalar.GCrMult); |
|||
var bCbMult = new Vector<float>(YCbCrScalar.BCbMult); |
|||
|
|||
nint n = values.Component0.Length / Vector<float>.Count; |
|||
for (nint i = 0; i < n; i++) |
|||
{ |
|||
// y = yVals[i];
|
|||
// cb = cbVals[i] - 128F;
|
|||
// cr = crVals[i] - 128F;
|
|||
// k = kVals[i] / 256F;
|
|||
ref Vector<float> c0 = ref Unsafe.Add(ref c0Base, i); |
|||
ref Vector<float> c1 = ref Unsafe.Add(ref c1Base, i); |
|||
ref Vector<float> c2 = ref Unsafe.Add(ref c2Base, i); |
|||
|
|||
Vector<float> y = c0; |
|||
Vector<float> cb = c1 + chromaOffset; |
|||
Vector<float> cr = c2 + chromaOffset; |
|||
Vector<float> scaledK = Unsafe.Add(ref kBase, i) * scale; |
|||
|
|||
// r = y + (1.402F * cr);
|
|||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|||
// b = y + (1.772F * cb);
|
|||
Vector<float> r = y + (cr * rCrMult); |
|||
Vector<float> g = y + (cb * gCbMult) + (cr * gCrMult); |
|||
Vector<float> b = y + (cb * bCbMult); |
|||
|
|||
r = (max - r.FastRound()) * scaledK; |
|||
g = (max - g.FastRound()) * scaledK; |
|||
b = (max - b.FastRound()) * scaledK; |
|||
|
|||
c0 = r; |
|||
c1 = g; |
|||
c2 = b; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void ConvertToRgbInplaceScalarRemainder(in ComponentValues values) |
|||
=> YccKScalar.ConvertToRgpInplace(values, this.MaximumValue, this.HalfValue); |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void ConvertFromRgbVectorized(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane) |
|||
{ |
|||
// rgb -> cmyk
|
|||
CmykVector.ConvertFromRgbInplaceVectorized(in values, this.MaximumValue, rLane, gLane, bLane); |
|||
|
|||
// cmyk -> ycck
|
|||
ref Vector<float> destY = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
ref Vector<float> destCb = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|||
ref Vector<float> destCr = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|||
|
|||
ref Vector<float> srcR = ref destY; |
|||
ref Vector<float> srcG = ref destCb; |
|||
ref Vector<float> srcB = ref destCr; |
|||
|
|||
var maxSampleValue = new Vector<float>(this.MaximumValue); |
|||
|
|||
var chromaOffset = new Vector<float>(this.HalfValue); |
|||
|
|||
var rYMult = new Vector<float>(0.299f); |
|||
var gYMult = new Vector<float>(0.587f); |
|||
var bYMult = new Vector<float>(0.114f); |
|||
|
|||
var rCbMult = new Vector<float>(0.168736f); |
|||
var gCbMult = new Vector<float>(0.331264f); |
|||
var bCbMult = new Vector<float>(0.5f); |
|||
|
|||
var rCrMult = new Vector<float>(0.5f); |
|||
var gCrMult = new Vector<float>(0.418688f); |
|||
var bCrMult = new Vector<float>(0.081312f); |
|||
|
|||
nint n = values.Component0.Length / Vector<float>.Count; |
|||
for (nint i = 0; i < n; i++) |
|||
{ |
|||
Vector<float> r = maxSampleValue - Unsafe.Add(ref srcR, i); |
|||
Vector<float> g = maxSampleValue - Unsafe.Add(ref srcG, i); |
|||
Vector<float> b = maxSampleValue - Unsafe.Add(ref srcB, i); |
|||
|
|||
// y = 0 + (0.299 * r) + (0.587 * g) + (0.114 * b)
|
|||
// cb = 128 - (0.168736 * r) - (0.331264 * g) + (0.5 * b)
|
|||
// cr = 128 + (0.5 * r) - (0.418688 * g) - (0.081312 * b)
|
|||
Unsafe.Add(ref destY, i) = (rYMult * r) + (gYMult * g) + (bYMult * b); |
|||
Unsafe.Add(ref destCb, i) = chromaOffset - (rCbMult * r) - (gCbMult * g) + (bCbMult * b); |
|||
Unsafe.Add(ref destCr, i) = chromaOffset + (rCrMult * r) - (gCrMult * g) - (bCrMult * b); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b) |
|||
{ |
|||
// rgb -> cmyk
|
|||
CmykScalar.ConvertFromRgb(in values, this.MaximumValue, r, g, b); |
|||
|
|||
// cmyk -> ycck
|
|||
YccKScalar.ConvertFromRgb(in values, this.HalfValue, this.MaximumValue, r, g, b); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,7 +1,7 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
|||
{ |
|||
internal abstract partial class JpegColorConverterBase |
|||
{ |
|||
@ -0,0 +1,131 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
|||
{ |
|||
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) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether this converter is supported on current hardware.
|
|||
/// </summary>
|
|||
public static bool IsSupported => Vector.IsHardwareAccelerated && Vector<float>.Count % 4 == 0; |
|||
|
|||
/// <inheritdoc/>
|
|||
public sealed override bool IsAvailable => IsSupported; |
|||
|
|||
public override int ElementsPerBatch => Vector<float>.Count; |
|||
|
|||
/// <inheritdoc/>
|
|||
public sealed 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); |
|||
|
|||
int simdCount = length - remainder; |
|||
if (simdCount > 0) |
|||
{ |
|||
this.ConvertToRgbInplaceVectorized(values.Slice(0, simdCount)); |
|||
} |
|||
|
|||
// Jpeg images width is always divisible by 8 without a remainder
|
|||
// so it's safe to say SSE/AVX1/AVX2 implementations would never have
|
|||
// 'remainder' pixels
|
|||
// But some exotic simd implementations e.g. AVX-512 can have
|
|||
// remainder pixels
|
|||
if (remainder > 0) |
|||
{ |
|||
this.ConvertToRgbInplaceScalarRemainder(values.Slice(simdCount, remainder)); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public sealed override void ConvertFromRgb(in ComponentValues values, Span<float> r, Span<float> g, Span<float> b) |
|||
{ |
|||
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); |
|||
|
|||
int simdCount = length - remainder; |
|||
if (simdCount > 0) |
|||
{ |
|||
this.ConvertFromRgbVectorized( |
|||
values.Slice(0, simdCount), |
|||
r.Slice(0, simdCount), |
|||
g.Slice(0, simdCount), |
|||
b.Slice(0, simdCount)); |
|||
} |
|||
|
|||
// Jpeg images width is always divisible by 8 without a remainder
|
|||
// so it's safe to say SSE/AVX1/AVX2 implementations would never have
|
|||
// 'remainder' pixels
|
|||
// But some exotic simd implementations e.g. AVX-512 can have
|
|||
// remainder pixels
|
|||
if (remainder > 0) |
|||
{ |
|||
this.ConvertFromRgbScalarRemainder( |
|||
values.Slice(simdCount, remainder), |
|||
r.Slice(simdCount, remainder), |
|||
g.Slice(simdCount, remainder), |
|||
b.Slice(simdCount, remainder)); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts planar jpeg component values in <paramref name="values"/>
|
|||
/// to RGB color space inplace using <see cref="Vector"/> API.
|
|||
/// </summary>
|
|||
/// <param name="values">The input/ouptut as a stack-only <see cref="ComponentValues"/> struct</param>
|
|||
protected abstract void ConvertToRgbInplaceVectorized(in ComponentValues values); |
|||
|
|||
/// <summary>
|
|||
/// Converts remainder of the planar jpeg component values after
|
|||
/// conversion in <see cref="ConvertToRgbInplaceVectorized(in ComponentValues)"/>.
|
|||
/// </summary>
|
|||
/// <param name="values">The input/ouptut as a stack-only <see cref="ComponentValues"/> struct</param>
|
|||
protected abstract void ConvertToRgbInplaceScalarRemainder(in ComponentValues values); |
|||
|
|||
/// <summary>
|
|||
/// Converts RGB lanes to jpeg component values using <see cref="Vector"/> API.
|
|||
/// </summary>
|
|||
/// <param name="values">Jpeg component values.</param>
|
|||
/// <param name="rLane">Red colors lane.</param>
|
|||
/// <param name="gLane">Green colors lane.</param>
|
|||
/// <param name="bLane">Blue colors lane.</param>
|
|||
protected abstract void ConvertFromRgbVectorized(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane); |
|||
|
|||
/// <summary>
|
|||
/// Converts remainder of RGB lanes to jpeg component values after
|
|||
/// conversion in <see cref="ConvertFromRgbVectorized(in ComponentValues, Span{float}, Span{float}, Span{float})"/>.
|
|||
/// </summary>
|
|||
/// <param name="values">Jpeg component values.</param>
|
|||
/// <param name="rLane">Red colors lane.</param>
|
|||
/// <param name="gLane">Green colors lane.</param>
|
|||
/// <param name="bLane">Blue colors lane.</param>
|
|||
protected abstract void ConvertFromRgbScalarRemainder(in ComponentValues values, Span<float> rLane, Span<float> gLane, Span<float> bLane); |
|||
} |
|||
} |
|||
} |
|||
@ -1,52 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
#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,43 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverterBase |
|||
{ |
|||
internal sealed class FromCmykScalar : JpegColorConverterScalar |
|||
{ |
|||
public FromCmykScalar(int precision) |
|||
: base(JpegColorSpace.Cmyk, precision) |
|||
{ |
|||
} |
|||
|
|||
public override void ConvertToRgbInplace(in ComponentValues values) => |
|||
ConvertCoreInplace(values, this.MaximumValue); |
|||
|
|||
internal static void ConvertCoreInplace(in ComponentValues values, float maxValue) |
|||
{ |
|||
Span<float> c0 = values.Component0; |
|||
Span<float> c1 = values.Component1; |
|||
Span<float> c2 = values.Component2; |
|||
Span<float> c3 = values.Component3; |
|||
|
|||
float scale = 1 / (maxValue * maxValue); |
|||
for (int i = 0; i < c0.Length; i++) |
|||
{ |
|||
float c = c0[i]; |
|||
float m = c1[i]; |
|||
float y = c2[i]; |
|||
float k = c3[i]; |
|||
|
|||
k *= scale; |
|||
c0[i] = c * k; |
|||
c1[i] = m * k; |
|||
c2[i] = y * k; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,51 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
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,39 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
#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 FromGrayscaleAvx : JpegColorConverterAvx |
|||
{ |
|||
public FromGrayscaleAvx(int precision) |
|||
: base(JpegColorSpace.Grayscale, precision) |
|||
{ |
|||
} |
|||
|
|||
public override void ConvertToRgbInplace(in ComponentValues values) |
|||
{ |
|||
ref Vector256<float> c0Base = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
|
|||
// Used for the color conversion
|
|||
var scale = Vector256.Create(1 / this.MaximumValue); |
|||
|
|||
nint n = values.Component0.Length / Vector256<float>.Count; |
|||
for (nint i = 0; i < n; i++) |
|||
{ |
|||
ref Vector256<float> c0 = ref Unsafe.Add(ref c0Base, i); |
|||
c0 = Avx.Multiply(c0, scale); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
#endif
|
|||
@ -1,34 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
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; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,38 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
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,26 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
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,74 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using System.Runtime.Intrinsics; |
|||
using System.Runtime.Intrinsics.X86; |
|||
using static SixLabors.ImageSharp.SimdUtils; |
|||
|
|||
// ReSharper disable ImpureMethodCallOnReadonlyValueField
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverterBase |
|||
{ |
|||
internal sealed class FromYCbCrAvx : JpegColorConverterAvx |
|||
{ |
|||
public FromYCbCrAvx(int precision) |
|||
: base(JpegColorSpace.YCbCr, 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)); |
|||
|
|||
// Used for the color conversion
|
|||
var chromaOffset = Vector256.Create(-this.HalfValue); |
|||
var scale = Vector256.Create(1 / this.MaximumValue); |
|||
var rCrMult = Vector256.Create(FromYCbCrScalar.RCrMult); |
|||
var gCbMult = Vector256.Create(-FromYCbCrScalar.GCbMult); |
|||
var gCrMult = Vector256.Create(-FromYCbCrScalar.GCrMult); |
|||
var bCbMult = Vector256.Create(FromYCbCrScalar.BCbMult); |
|||
|
|||
// Walking 8 elements at one step:
|
|||
nint n = values.Component0.Length / Vector256<float>.Count; |
|||
for (nint i = 0; i < n; i++) |
|||
{ |
|||
// y = yVals[i];
|
|||
// cb = cbVals[i] - 128F;
|
|||
// cr = crVals[i] - 128F;
|
|||
ref Vector256<float> c0 = ref Unsafe.Add(ref c0Base, i); |
|||
ref Vector256<float> c1 = ref Unsafe.Add(ref c1Base, i); |
|||
ref Vector256<float> c2 = ref Unsafe.Add(ref c2Base, i); |
|||
|
|||
Vector256<float> y = c0; |
|||
Vector256<float> cb = Avx.Add(c1, chromaOffset); |
|||
Vector256<float> cr = Avx.Add(c2, chromaOffset); |
|||
|
|||
// r = y + (1.402F * cr);
|
|||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|||
// b = y + (1.772F * cb);
|
|||
Vector256<float> r = HwIntrinsics.MultiplyAdd(y, cr, rCrMult); |
|||
Vector256<float> g = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); |
|||
Vector256<float> b = HwIntrinsics.MultiplyAdd(y, cb, bCbMult); |
|||
|
|||
r = Avx.Multiply(Avx.RoundToNearestInteger(r), scale); |
|||
g = Avx.Multiply(Avx.RoundToNearestInteger(g), scale); |
|||
b = Avx.Multiply(Avx.RoundToNearestInteger(b), scale); |
|||
|
|||
c0 = r; |
|||
c1 = g; |
|||
c2 = b; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
#endif
|
|||
@ -1,50 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
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,74 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
// ReSharper disable ImpureMethodCallOnReadonlyValueField
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverterBase |
|||
{ |
|||
internal sealed class FromYCbCrVector : JpegColorConverterVector |
|||
{ |
|||
public FromYCbCrVector(int precision) |
|||
: base(JpegColorSpace.YCbCr, precision) |
|||
{ |
|||
} |
|||
|
|||
protected override void ConvertCoreVectorizedInplace(in ComponentValues values) |
|||
{ |
|||
ref Vector<float> c0Base = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
ref Vector<float> c1Base = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|||
ref Vector<float> c2Base = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|||
|
|||
var chromaOffset = new Vector<float>(-this.HalfValue); |
|||
|
|||
var scale = new Vector<float>(1 / this.MaximumValue); |
|||
var rCrMult = new Vector<float>(FromYCbCrScalar.RCrMult); |
|||
var gCbMult = new Vector<float>(-FromYCbCrScalar.GCbMult); |
|||
var gCrMult = new Vector<float>(-FromYCbCrScalar.GCrMult); |
|||
var bCbMult = new Vector<float>(FromYCbCrScalar.BCbMult); |
|||
|
|||
nint n = values.Component0.Length / Vector<float>.Count; |
|||
for (nint i = 0; i < n; i++) |
|||
{ |
|||
// y = yVals[i];
|
|||
// cb = cbVals[i] - 128F;
|
|||
// cr = crVals[i] - 128F;
|
|||
ref Vector<float> c0 = ref Unsafe.Add(ref c0Base, i); |
|||
ref Vector<float> c1 = ref Unsafe.Add(ref c1Base, i); |
|||
ref Vector<float> c2 = ref Unsafe.Add(ref c2Base, i); |
|||
Vector<float> y = Unsafe.Add(ref c0Base, i); |
|||
Vector<float> cb = Unsafe.Add(ref c1Base, i) + chromaOffset; |
|||
Vector<float> cr = Unsafe.Add(ref c2Base, i) + chromaOffset; |
|||
|
|||
// r = y + (1.402F * cr);
|
|||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|||
// b = y + (1.772F * cb);
|
|||
Vector<float> r = y + (cr * rCrMult); |
|||
Vector<float> g = y + (cb * gCbMult) + (cr * gCrMult); |
|||
Vector<float> b = y + (cb * bCbMult); |
|||
|
|||
r = r.FastRound(); |
|||
g = g.FastRound(); |
|||
b = b.FastRound(); |
|||
r *= scale; |
|||
g *= scale; |
|||
b *= scale; |
|||
|
|||
c0 = r; |
|||
c1 = g; |
|||
c2 = b; |
|||
} |
|||
} |
|||
|
|||
protected override void ConvertCoreInplace(in ComponentValues values) => |
|||
FromYCbCrScalar.ConvertCoreInplace(values, this.MaximumValue, this.HalfValue); |
|||
} |
|||
} |
|||
} |
|||
@ -1,82 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using System.Runtime.Intrinsics; |
|||
using System.Runtime.Intrinsics.X86; |
|||
using static SixLabors.ImageSharp.SimdUtils; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverterBase |
|||
{ |
|||
internal sealed class FromYccKAvx : JpegColorConverterAvx |
|||
{ |
|||
public FromYccKAvx(int precision) |
|||
: base(JpegColorSpace.Ycck, 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> kBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
|||
|
|||
// Used for the color conversion
|
|||
var chromaOffset = Vector256.Create(-this.HalfValue); |
|||
var scale = Vector256.Create(1 / (this.MaximumValue * this.MaximumValue)); |
|||
var max = Vector256.Create(this.MaximumValue); |
|||
var rCrMult = Vector256.Create(FromYCbCrScalar.RCrMult); |
|||
var gCbMult = Vector256.Create(-FromYCbCrScalar.GCbMult); |
|||
var gCrMult = Vector256.Create(-FromYCbCrScalar.GCrMult); |
|||
var bCbMult = Vector256.Create(FromYCbCrScalar.BCbMult); |
|||
|
|||
// Walking 8 elements at one step:
|
|||
nint n = values.Component0.Length / Vector256<float>.Count; |
|||
for (nint i = 0; i < n; i++) |
|||
{ |
|||
// y = yVals[i];
|
|||
// cb = cbVals[i] - 128F;
|
|||
// cr = crVals[i] - 128F;
|
|||
// k = kVals[i] / 256F;
|
|||
ref Vector256<float> c0 = ref Unsafe.Add(ref c0Base, i); |
|||
ref Vector256<float> c1 = ref Unsafe.Add(ref c1Base, i); |
|||
ref Vector256<float> c2 = ref Unsafe.Add(ref c2Base, i); |
|||
Vector256<float> y = c0; |
|||
Vector256<float> cb = Avx.Add(c1, chromaOffset); |
|||
Vector256<float> cr = Avx.Add(c2, chromaOffset); |
|||
Vector256<float> scaledK = Avx.Multiply(Unsafe.Add(ref kBase, i), scale); |
|||
|
|||
// r = y + (1.402F * cr);
|
|||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|||
// b = y + (1.772F * cb);
|
|||
Vector256<float> r = HwIntrinsics.MultiplyAdd(y, cr, rCrMult); |
|||
Vector256<float> g = |
|||
HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); |
|||
Vector256<float> b = HwIntrinsics.MultiplyAdd(y, cb, bCbMult); |
|||
|
|||
r = Avx.Subtract(max, Avx.RoundToNearestInteger(r)); |
|||
g = Avx.Subtract(max, Avx.RoundToNearestInteger(g)); |
|||
b = Avx.Subtract(max, Avx.RoundToNearestInteger(b)); |
|||
|
|||
r = Avx.Multiply(r, scaledK); |
|||
g = Avx.Multiply(g, scaledK); |
|||
b = Avx.Multiply(b, scaledK); |
|||
|
|||
c0 = r; |
|||
c1 = g; |
|||
c2 = b; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
#endif
|
|||
@ -1,43 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverterBase |
|||
{ |
|||
internal sealed class FromYccKScalar : JpegColorConverterScalar |
|||
{ |
|||
public FromYccKScalar(int precision) |
|||
: base(JpegColorSpace.Ycck, 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; |
|||
Span<float> c3 = values.Component3; |
|||
|
|||
float scale = 1 / (maxValue * maxValue); |
|||
|
|||
for (int i = 0; i < values.Component0.Length; i++) |
|||
{ |
|||
float y = c0[i]; |
|||
float cb = c1[i] - halfValue; |
|||
float cr = c2[i] - halfValue; |
|||
float scaledK = c3[i] * scale; |
|||
|
|||
c0[i] = (maxValue - MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero)) * scaledK; |
|||
c1[i] = (maxValue - MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero)) * scaledK; |
|||
c2[i] = (maxValue - MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * scaledK; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,75 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
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 FromYccKVector : JpegColorConverterVector |
|||
{ |
|||
public FromYccKVector(int precision) |
|||
: base(JpegColorSpace.Ycck, precision) |
|||
{ |
|||
} |
|||
|
|||
protected override void ConvertCoreVectorizedInplace(in ComponentValues values) |
|||
{ |
|||
ref Vector<float> c0Base = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
ref Vector<float> c1Base = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|||
ref Vector<float> c2Base = |
|||
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 chromaOffset = new Vector<float>(-this.HalfValue); |
|||
var scale = new Vector<float>(1 / (this.MaximumValue * this.MaximumValue)); |
|||
var max = new Vector<float>(this.MaximumValue); |
|||
var rCrMult = new Vector<float>(FromYCbCrScalar.RCrMult); |
|||
var gCbMult = new Vector<float>(-FromYCbCrScalar.GCbMult); |
|||
var gCrMult = new Vector<float>(-FromYCbCrScalar.GCrMult); |
|||
var bCbMult = new Vector<float>(FromYCbCrScalar.BCbMult); |
|||
|
|||
nint n = values.Component0.Length / Vector<float>.Count; |
|||
for (nint i = 0; i < n; i++) |
|||
{ |
|||
// y = yVals[i];
|
|||
// cb = cbVals[i] - 128F;
|
|||
// cr = crVals[i] - 128F;
|
|||
// k = kVals[i] / 256F;
|
|||
ref Vector<float> c0 = ref Unsafe.Add(ref c0Base, i); |
|||
ref Vector<float> c1 = ref Unsafe.Add(ref c1Base, i); |
|||
ref Vector<float> c2 = ref Unsafe.Add(ref c2Base, i); |
|||
|
|||
Vector<float> y = c0; |
|||
Vector<float> cb = c1 + chromaOffset; |
|||
Vector<float> cr = c2 + chromaOffset; |
|||
Vector<float> scaledK = Unsafe.Add(ref kBase, i) * scale; |
|||
|
|||
// r = y + (1.402F * cr);
|
|||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|||
// b = y + (1.772F * cb);
|
|||
Vector<float> r = y + (cr * rCrMult); |
|||
Vector<float> g = y + (cb * gCbMult) + (cr * gCrMult); |
|||
Vector<float> b = y + (cb * bCbMult); |
|||
|
|||
r = (max - r.FastRound()) * scaledK; |
|||
g = (max - g.FastRound()) * scaledK; |
|||
b = (max - b.FastRound()) * scaledK; |
|||
|
|||
c0 = r; |
|||
c1 = g; |
|||
c2 = b; |
|||
} |
|||
} |
|||
|
|||
protected override void ConvertCoreInplace(in ComponentValues values) => |
|||
FromYccKScalar.ConvertCoreInplace(values, this.MaximumValue, this.HalfValue); |
|||
} |
|||
} |
|||
} |
|||
@ -1,61 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
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 sealed override int ElementsPerBatch => Vector<float>.Count; |
|||
|
|||
public sealed 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(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,116 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System; |
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a single frame component.
|
|||
/// </summary>
|
|||
internal class Component : IDisposable |
|||
{ |
|||
private readonly MemoryAllocator memoryAllocator; |
|||
|
|||
public Component(MemoryAllocator memoryAllocator, int horizontalFactor, int verticalFactor, int quantizationTableIndex) |
|||
{ |
|||
this.memoryAllocator = memoryAllocator; |
|||
|
|||
this.HorizontalSamplingFactor = horizontalFactor; |
|||
this.VerticalSamplingFactor = verticalFactor; |
|||
this.SamplingFactors = new Size(horizontalFactor, verticalFactor); |
|||
|
|||
this.QuantizationTableIndex = quantizationTableIndex; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets DC coefficient predictor.
|
|||
/// </summary>
|
|||
public int DcPredictor { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the horizontal sampling factor.
|
|||
/// </summary>
|
|||
public int HorizontalSamplingFactor { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the vertical sampling factor.
|
|||
/// </summary>
|
|||
public int VerticalSamplingFactor { get; } |
|||
|
|||
public Buffer2D<Block8x8> SpectralBlocks { get; private set; } |
|||
|
|||
public Size SubSamplingDivisors { get; private set; } |
|||
|
|||
public int QuantizationTableIndex { get; } |
|||
|
|||
public Size SizeInBlocks { get; private set; } |
|||
|
|||
public Size SamplingFactors { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the number of blocks per line.
|
|||
/// </summary>
|
|||
public int WidthInBlocks { get; private set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the number of blocks per column.
|
|||
/// </summary>
|
|||
public int HeightInBlocks { get; private set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the index for the DC Huffman table.
|
|||
/// </summary>
|
|||
public int DcTableId { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the index for the AC Huffman table.
|
|||
/// </summary>
|
|||
public int AcTableId { get; set; } |
|||
|
|||
/// <inheritdoc/>
|
|||
public void Dispose() |
|||
{ |
|||
this.SpectralBlocks?.Dispose(); |
|||
this.SpectralBlocks = null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes component for future buffers initialization.
|
|||
/// </summary>
|
|||
/// <param name="frame">asdfasdf.</param>
|
|||
/// <param name="maxSubFactorH">Maximal horizontal subsampling factor among all the components.</param>
|
|||
/// <param name="maxSubFactorV">Maximal vertical subsampling factor among all the components.</param>
|
|||
public void Init(JpegFrame frame, int maxSubFactorH, int maxSubFactorV) |
|||
{ |
|||
uint widthInBlocks = ((uint)frame.PixelWidth + 7) / 8; |
|||
uint heightInBlocks = ((uint)frame.PixelHeight + 7) / 8; |
|||
|
|||
this.WidthInBlocks = (int)MathF.Ceiling( |
|||
(float)widthInBlocks * this.HorizontalSamplingFactor / maxSubFactorH); |
|||
|
|||
this.HeightInBlocks = (int)MathF.Ceiling( |
|||
(float)heightInBlocks * this.VerticalSamplingFactor / maxSubFactorV); |
|||
|
|||
int blocksPerLineForMcu = frame.McusPerLine * this.HorizontalSamplingFactor; |
|||
int blocksPerColumnForMcu = frame.McusPerColumn * this.VerticalSamplingFactor; |
|||
this.SizeInBlocks = new Size(blocksPerLineForMcu, blocksPerColumnForMcu); |
|||
|
|||
this.SubSamplingDivisors = new Size(maxSubFactorH, maxSubFactorV).DivideBy(this.SamplingFactors); |
|||
|
|||
if (this.SubSamplingDivisors.Width == 0 || this.SubSamplingDivisors.Height == 0) |
|||
{ |
|||
JpegThrowHelper.ThrowBadSampling(); |
|||
} |
|||
} |
|||
|
|||
public void AllocateSpectral(bool fullScan) |
|||
{ |
|||
int spectralAllocWidth = this.SizeInBlocks.Width; |
|||
int spectralAllocHeight = fullScan ? this.SizeInBlocks.Height : this.VerticalSamplingFactor; |
|||
|
|||
this.SpectralBlocks = this.memoryAllocator.Allocate2D<Block8x8>(spectralAllocWidth, spectralAllocHeight); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,231 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using System.Runtime.Intrinsics; |
|||
using System.Runtime.Intrinsics.X86; |
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|||
{ |
|||
internal class ComponentProcessor : IDisposable |
|||
{ |
|||
private readonly Size blockAreaSize; |
|||
|
|||
private readonly Component component; |
|||
|
|||
private Block8x8F quantTable; |
|||
|
|||
public ComponentProcessor(MemoryAllocator memoryAllocator, Component component, Size postProcessorBufferSize, Block8x8F quantTable) |
|||
{ |
|||
this.component = component; |
|||
this.quantTable = quantTable; |
|||
|
|||
this.component = component; |
|||
this.blockAreaSize = component.SubSamplingDivisors * 8; |
|||
|
|||
// alignment of 8 so each block stride can be sampled from a single 'ref pointer'
|
|||
this.ColorBuffer = memoryAllocator.Allocate2DOveraligned<float>( |
|||
postProcessorBufferSize.Width, |
|||
postProcessorBufferSize.Height, |
|||
8, |
|||
AllocationOptions.Clean); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the temporary working buffer of color values.
|
|||
/// </summary>
|
|||
public Buffer2D<float> ColorBuffer { get; } |
|||
|
|||
public void CopyColorBufferToBlocks(int spectralStep) |
|||
{ |
|||
Buffer2D<Block8x8> spectralBuffer = this.component.SpectralBlocks; |
|||
int destAreaStride = this.ColorBuffer.Width; |
|||
int yBlockStart = spectralStep * this.component.SamplingFactors.Height; |
|||
|
|||
Block8x8F workspaceBlock = default; |
|||
|
|||
// handle subsampling
|
|||
Size subsamplingFactors = this.component.SubSamplingDivisors; |
|||
if (subsamplingFactors.Width != 1 || subsamplingFactors.Height != 1) |
|||
{ |
|||
this.PackColorBuffer(); |
|||
} |
|||
|
|||
int blocksRowsPerStep = this.component.SamplingFactors.Height; |
|||
|
|||
for (int y = 0; y < blocksRowsPerStep; y++) |
|||
{ |
|||
int yBuffer = y * this.blockAreaSize.Height; |
|||
Span<float> colorBufferRow = this.ColorBuffer.DangerousGetRowSpan(yBuffer); |
|||
Span<Block8x8> blockRow = spectralBuffer.DangerousGetRowSpan(yBlockStart + y); |
|||
for (int xBlock = 0; xBlock < spectralBuffer.Width; xBlock++) |
|||
{ |
|||
// load 8x8 block from 8 pixel strides
|
|||
int xColorBufferStart = xBlock * 8; |
|||
workspaceBlock.ScaledCopyFrom( |
|||
ref colorBufferRow[xColorBufferStart], |
|||
destAreaStride); |
|||
|
|||
// level shift via -128f
|
|||
workspaceBlock.AddInPlace(-128f); |
|||
|
|||
// FDCT
|
|||
FloatingPointDCT.TransformFDCT(ref workspaceBlock); |
|||
|
|||
// Quantize and save to spectral blocks
|
|||
Block8x8F.Quantize(ref workspaceBlock, ref blockRow[xBlock], ref this.quantTable); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public Span<float> GetColorBufferRowSpan(int row) |
|||
=> this.ColorBuffer.DangerousGetRowSpan(row); |
|||
|
|||
public void Dispose() |
|||
=> this.ColorBuffer.Dispose(); |
|||
|
|||
private void PackColorBuffer() |
|||
{ |
|||
Size factors = this.component.SubSamplingDivisors; |
|||
|
|||
int packedWidth = this.ColorBuffer.Width / factors.Width; |
|||
|
|||
float averageMultiplier = 1f / (factors.Width * factors.Height); |
|||
for (int i = 0; i < this.ColorBuffer.Height; i += factors.Height) |
|||
{ |
|||
Span<float> sourceRow = this.ColorBuffer.DangerousGetRowSpan(i); |
|||
|
|||
// vertical sum
|
|||
for (int j = 1; j < factors.Height; j++) |
|||
{ |
|||
SumVertical(sourceRow, this.ColorBuffer.DangerousGetRowSpan(i + j)); |
|||
} |
|||
|
|||
// horizontal sum
|
|||
SumHorizontal(sourceRow, factors.Width); |
|||
|
|||
// calculate average
|
|||
MultiplyToAverage(sourceRow, averageMultiplier); |
|||
|
|||
// copy to the first 8 slots
|
|||
sourceRow.Slice(0, packedWidth).CopyTo(this.ColorBuffer.DangerousGetRowSpan(i / factors.Height)); |
|||
} |
|||
|
|||
static void SumVertical(Span<float> target, Span<float> source) |
|||
{ |
|||
if (Avx.IsSupported) |
|||
{ |
|||
ref Vector256<float> targetVectorRef = ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(target)); |
|||
ref Vector256<float> sourceVectorRef = ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(source)); |
|||
|
|||
// Spans are guaranteed to be multiple of 8 so no extra 'remainder' steps are needed
|
|||
nint count = source.Length / Vector256<float>.Count; |
|||
for (nint i = 0; i < count; i++) |
|||
{ |
|||
Unsafe.Add(ref targetVectorRef, i) = Avx.Add(Unsafe.Add(ref targetVectorRef, i), Unsafe.Add(ref sourceVectorRef, i)); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
ref Vector<float> targetVectorRef = ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(target)); |
|||
ref Vector<float> sourceVectorRef = ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(source)); |
|||
|
|||
nint count = source.Length / Vector<float>.Count; |
|||
for (nint i = 0; i < count; i++) |
|||
{ |
|||
Unsafe.Add(ref targetVectorRef, i) += Unsafe.Add(ref sourceVectorRef, i); |
|||
} |
|||
|
|||
ref float targetRef = ref MemoryMarshal.GetReference(target); |
|||
ref float sourceRef = ref MemoryMarshal.GetReference(source); |
|||
for (nint i = count * Vector<float>.Count; i < source.Length; i++) |
|||
{ |
|||
Unsafe.Add(ref targetRef, i) += Unsafe.Add(ref sourceRef, i); |
|||
} |
|||
} |
|||
} |
|||
|
|||
static void SumHorizontal(Span<float> target, int factor) |
|||
{ |
|||
Span<float> source = target; |
|||
if (Avx2.IsSupported) |
|||
{ |
|||
ref Vector256<float> targetRef = ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(target)); |
|||
|
|||
// Ideally we need to use log2: Numerics.Log2((uint)factor)
|
|||
// but division by 2 works just fine in this case
|
|||
int haddIterationsCount = (int)((uint)factor / 2); |
|||
|
|||
// Transform spans so that it only contains 'remainder'
|
|||
// values for the scalar fallback code
|
|||
int scalarRemainder = target.Length % (Vector<float>.Count * factor); |
|||
int touchedCount = target.Length - scalarRemainder; |
|||
source = source.Slice(touchedCount); |
|||
target = target.Slice(touchedCount / factor); |
|||
|
|||
uint length = (uint)touchedCount / (uint)Vector256<float>.Count; |
|||
|
|||
for (int i = 0; i < haddIterationsCount; i++) |
|||
{ |
|||
length /= 2; |
|||
|
|||
for (nuint j = 0; j < length; j++) |
|||
{ |
|||
nuint indexLeft = j * 2; |
|||
nuint indexRight = indexLeft + 1; |
|||
Vector256<float> sum = Avx.HorizontalAdd(Unsafe.Add(ref targetRef, indexLeft), Unsafe.Add(ref targetRef, indexRight)); |
|||
Unsafe.Add(ref targetRef, j) = Avx2.Permute4x64(sum.AsDouble(), 0b11_01_10_00).AsSingle(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// scalar remainder
|
|||
for (int i = 0; i < source.Length / factor; i++) |
|||
{ |
|||
target[i] = source[i * factor]; |
|||
for (int j = 1; j < factor; j++) |
|||
{ |
|||
target[i] += source[(i * factor) + j]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
static void MultiplyToAverage(Span<float> target, float multiplier) |
|||
{ |
|||
if (Avx.IsSupported) |
|||
{ |
|||
ref Vector256<float> targetVectorRef = ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(target)); |
|||
|
|||
// Spans are guaranteed to be multiple of 8 so no extra 'remainder' steps are needed
|
|||
nint count = target.Length / Vector256<float>.Count; |
|||
var multiplierVector = Vector256.Create(multiplier); |
|||
for (nint i = 0; i < count; i++) |
|||
{ |
|||
Unsafe.Add(ref targetVectorRef, i) = Avx.Multiply(Unsafe.Add(ref targetVectorRef, i), multiplierVector); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
ref Vector<float> targetVectorRef = ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(target)); |
|||
|
|||
nint count = target.Length / Vector<float>.Count; |
|||
var multiplierVector = new Vector<float>(multiplier); |
|||
for (nint i = 0; i < count; i++) |
|||
{ |
|||
Unsafe.Add(ref targetVectorRef, i) *= multiplierVector; |
|||
} |
|||
|
|||
ref float targetRef = ref MemoryMarshal.GetReference(target); |
|||
for (nint i = count * Vector<float>.Count; i < target.Length; i++) |
|||
{ |
|||
Unsafe.Add(ref targetRef, i) *= multiplier; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|||
{ |
|||
internal class JpegComponentConfig |
|||
{ |
|||
public JpegComponentConfig(byte id, int hsf, int vsf, int quantIndex, int dcIndex, int acIndex) |
|||
{ |
|||
this.Id = id; |
|||
this.HorizontalSampleFactor = hsf; |
|||
this.VerticalSampleFactor = vsf; |
|||
this.QuantizatioTableIndex = quantIndex; |
|||
this.DcTableSelector = dcIndex; |
|||
this.AcTableSelector = acIndex; |
|||
} |
|||
|
|||
public byte Id { get; } |
|||
|
|||
public int HorizontalSampleFactor { get; } |
|||
|
|||
public int VerticalSampleFactor { get; } |
|||
|
|||
public int QuantizatioTableIndex { get; } |
|||
|
|||
public int DcTableSelector { get; } |
|||
|
|||
public int AcTableSelector { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|||
{ |
|||
internal class JpegFrameConfig |
|||
{ |
|||
public JpegFrameConfig(JpegColorSpace colorType, JpegEncodingColor encodingColor, JpegComponentConfig[] components, JpegHuffmanTableConfig[] huffmanTables, JpegQuantizationTableConfig[] quantTables) |
|||
{ |
|||
this.ColorType = colorType; |
|||
this.EncodingColor = encodingColor; |
|||
this.Components = components; |
|||
this.HuffmanTables = huffmanTables; |
|||
this.QuantizationTables = quantTables; |
|||
|
|||
this.MaxHorizontalSamplingFactor = components[0].HorizontalSampleFactor; |
|||
this.MaxVerticalSamplingFactor = components[0].VerticalSampleFactor; |
|||
for (int i = 1; i < components.Length; i++) |
|||
{ |
|||
JpegComponentConfig component = components[i]; |
|||
this.MaxHorizontalSamplingFactor = Math.Max(this.MaxHorizontalSamplingFactor, component.HorizontalSampleFactor); |
|||
this.MaxVerticalSamplingFactor = Math.Max(this.MaxVerticalSamplingFactor, component.VerticalSampleFactor); |
|||
} |
|||
} |
|||
|
|||
public JpegColorSpace ColorType { get; } |
|||
|
|||
public JpegEncodingColor EncodingColor { get; } |
|||
|
|||
public JpegComponentConfig[] Components { get; } |
|||
|
|||
public JpegHuffmanTableConfig[] HuffmanTables { get; } |
|||
|
|||
public JpegQuantizationTableConfig[] QuantizationTables { get; } |
|||
|
|||
public int MaxHorizontalSamplingFactor { get; } |
|||
|
|||
public int MaxVerticalSamplingFactor { get; } |
|||
|
|||
public byte? AdobeColorTransformMarkerFlag { get; set; } = null; |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|||
{ |
|||
internal class JpegHuffmanTableConfig |
|||
{ |
|||
public JpegHuffmanTableConfig(int @class, int destIndex, HuffmanSpec table) |
|||
{ |
|||
this.Class = @class; |
|||
this.DestinationIndex = destIndex; |
|||
this.Table = table; |
|||
} |
|||
|
|||
public int Class { get; } |
|||
|
|||
public int DestinationIndex { get; } |
|||
|
|||
public HuffmanSpec Table { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|||
{ |
|||
internal class JpegQuantizationTableConfig |
|||
{ |
|||
public JpegQuantizationTableConfig(int destIndex, ReadOnlySpan<byte> quantizationTable) |
|||
{ |
|||
this.DestinationIndex = destIndex; |
|||
this.Table = Block8x8.Load(quantizationTable); |
|||
} |
|||
|
|||
public int DestinationIndex { get; } |
|||
|
|||
public Block8x8 Table { get; } |
|||
} |
|||
} |
|||
@ -1,35 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|||
{ |
|||
/// <summary>
|
|||
/// Enumerates the Huffman tables
|
|||
/// </summary>
|
|||
internal enum HuffIndex |
|||
{ |
|||
/// <summary>
|
|||
/// The DC luminance huffman table index
|
|||
/// </summary>
|
|||
LuminanceDC = 0, |
|||
|
|||
// ReSharper disable UnusedMember.Local
|
|||
|
|||
/// <summary>
|
|||
/// The AC luminance huffman table index
|
|||
/// </summary>
|
|||
LuminanceAC = 1, |
|||
|
|||
/// <summary>
|
|||
/// The DC chrominance huffman table index
|
|||
/// </summary>
|
|||
ChrominanceDC = 2, |
|||
|
|||
/// <summary>
|
|||
/// The AC chrominance huffman table index
|
|||
/// </summary>
|
|||
ChrominanceAC = 3, |
|||
|
|||
// ReSharper restore UnusedMember.Local
|
|||
} |
|||
} |
|||
@ -0,0 +1,85 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System; |
|||
using SixLabors.ImageSharp.Advanced; |
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|||
{ |
|||
/// <summary>
|
|||
/// Represent a single jpeg frame.
|
|||
/// </summary>
|
|||
internal sealed class JpegFrame : IDisposable |
|||
{ |
|||
public JpegFrame(Image image, JpegFrameConfig frameConfig, bool interleaved) |
|||
{ |
|||
this.ColorSpace = frameConfig.ColorType; |
|||
|
|||
this.Interleaved = interleaved; |
|||
|
|||
this.PixelWidth = image.Width; |
|||
this.PixelHeight = image.Height; |
|||
|
|||
MemoryAllocator allocator = image.GetConfiguration().MemoryAllocator; |
|||
|
|||
JpegComponentConfig[] componentConfigs = frameConfig.Components; |
|||
this.Components = new Component[componentConfigs.Length]; |
|||
for (int i = 0; i < this.Components.Length; i++) |
|||
{ |
|||
JpegComponentConfig componentConfig = componentConfigs[i]; |
|||
this.Components[i] = new Component(allocator, componentConfig.HorizontalSampleFactor, componentConfig.VerticalSampleFactor, componentConfig.QuantizatioTableIndex) |
|||
{ |
|||
DcTableId = componentConfig.DcTableSelector, |
|||
AcTableId = componentConfig.AcTableSelector, |
|||
}; |
|||
|
|||
this.BlocksPerMcu += componentConfig.HorizontalSampleFactor * componentConfig.VerticalSampleFactor; |
|||
} |
|||
|
|||
int maxSubFactorH = frameConfig.MaxHorizontalSamplingFactor; |
|||
int maxSubFactorV = frameConfig.MaxVerticalSamplingFactor; |
|||
this.McusPerLine = (int)Numerics.DivideCeil((uint)image.Width, (uint)maxSubFactorH * 8); |
|||
this.McusPerColumn = (int)Numerics.DivideCeil((uint)image.Height, (uint)maxSubFactorV * 8); |
|||
|
|||
for (int i = 0; i < this.Components.Length; i++) |
|||
{ |
|||
Component component = this.Components[i]; |
|||
component.Init(this, maxSubFactorH, maxSubFactorV); |
|||
} |
|||
} |
|||
|
|||
public JpegColorSpace ColorSpace { get; } |
|||
|
|||
public bool Interleaved { get; } |
|||
|
|||
public int PixelHeight { get; } |
|||
|
|||
public int PixelWidth { get; } |
|||
|
|||
public Component[] Components { get; } |
|||
|
|||
public int McusPerLine { get; } |
|||
|
|||
public int McusPerColumn { get; } |
|||
|
|||
public int BlocksPerMcu { get; } |
|||
|
|||
public void Dispose() |
|||
{ |
|||
for (int i = 0; i < this.Components.Length; i++) |
|||
{ |
|||
this.Components[i].Dispose(); |
|||
} |
|||
} |
|||
|
|||
public void AllocateComponents(bool fullScan) |
|||
{ |
|||
for (int i = 0; i < this.Components.Length; i++) |
|||
{ |
|||
Component component = this.Components[i]; |
|||
component.AllocateSpectral(fullScan); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,127 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
using System.Runtime.Intrinsics; |
|||
using System.Runtime.Intrinsics.X86; |
|||
#endif
|
|||
using SixLabors.ImageSharp.Advanced; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|||
{ |
|||
/// <summary>
|
|||
/// On-stack worker struct to efficiently encapsulate the TPixel -> L8 -> Y conversion chain of 8x8 pixel blocks.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel type to work on</typeparam>
|
|||
internal ref struct LuminanceForwardConverter<TPixel> |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
/// <summary>
|
|||
/// Number of pixels processed per single <see cref="Convert(int, int, ref RowOctet{TPixel})"/> call
|
|||
/// </summary>
|
|||
private const int PixelsPerSample = 8 * 8; |
|||
|
|||
/// <summary>
|
|||
/// The Y component
|
|||
/// </summary>
|
|||
public Block8x8F Y; |
|||
|
|||
/// <summary>
|
|||
/// Temporal 64-pixel span to hold unconverted TPixel data.
|
|||
/// </summary>
|
|||
private readonly Span<TPixel> pixelSpan; |
|||
|
|||
/// <summary>
|
|||
/// Temporal 64-byte span to hold converted <see cref="L8"/> data.
|
|||
/// </summary>
|
|||
private readonly Span<L8> l8Span; |
|||
|
|||
/// <summary>
|
|||
/// Sampled pixel buffer size.
|
|||
/// </summary>
|
|||
private readonly Size samplingAreaSize; |
|||
|
|||
/// <summary>
|
|||
/// <see cref="Configuration"/> for internal operations.
|
|||
/// </summary>
|
|||
private readonly Configuration config; |
|||
|
|||
public LuminanceForwardConverter(ImageFrame<TPixel> frame) |
|||
{ |
|||
this.Y = default; |
|||
|
|||
this.pixelSpan = new TPixel[PixelsPerSample].AsSpan(); |
|||
this.l8Span = new L8[PixelsPerSample].AsSpan(); |
|||
|
|||
this.samplingAreaSize = new Size(frame.Width, frame.Height); |
|||
this.config = frame.GetConfiguration(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets size of sampling area from given frame pixel buffer.
|
|||
/// </summary>
|
|||
private static Size SampleSize => new(8, 8); |
|||
|
|||
/// <summary>
|
|||
/// Converts a 8x8 image area inside 'pixels' at position (x,y) placing the result members of the structure (<see cref="Y"/>)
|
|||
/// </summary>
|
|||
public void Convert(int x, int y, ref RowOctet<TPixel> currentRows) |
|||
{ |
|||
YCbCrForwardConverter<TPixel>.LoadAndStretchEdges(currentRows, this.pixelSpan, new Point(x, y), SampleSize, this.samplingAreaSize); |
|||
|
|||
PixelOperations<TPixel>.Instance.ToL8(this.config, this.pixelSpan, this.l8Span); |
|||
|
|||
ref Block8x8F yBlock = ref this.Y; |
|||
ref L8 l8Start = ref MemoryMarshal.GetReference(this.l8Span); |
|||
|
|||
if (RgbToYCbCrConverterVectorized.IsSupported) |
|||
{ |
|||
ConvertAvx(ref l8Start, ref yBlock); |
|||
} |
|||
else |
|||
{ |
|||
ConvertScalar(ref l8Start, ref yBlock); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts 8x8 L8 pixel matrix to 8x8 Block of floats using Avx2 Intrinsics.
|
|||
/// </summary>
|
|||
/// <param name="l8Start">Start of span of L8 pixels with size of 64</param>
|
|||
/// <param name="yBlock">8x8 destination matrix of Luminance(Y) converted data</param>
|
|||
private static void ConvertAvx(ref L8 l8Start, ref Block8x8F yBlock) |
|||
{ |
|||
Debug.Assert(RgbToYCbCrConverterVectorized.IsSupported, "AVX2 is required to run this converter"); |
|||
|
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
ref Vector128<byte> l8ByteSpan = ref Unsafe.As<L8, Vector128<byte>>(ref l8Start); |
|||
ref Vector256<float> destRef = ref yBlock.V0; |
|||
|
|||
const int bytesPerL8Stride = 8; |
|||
for (nint i = 0; i < 8; i++) |
|||
{ |
|||
Unsafe.Add(ref destRef, i) = Avx2.ConvertToVector256Single(Avx2.ConvertToVector256Int32(Unsafe.AddByteOffset(ref l8ByteSpan, bytesPerL8Stride * i))); |
|||
} |
|||
#endif
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts 8x8 L8 pixel matrix to 8x8 Block of floats.
|
|||
/// </summary>
|
|||
/// <param name="l8Start">Start of span of L8 pixels with size of 64</param>
|
|||
/// <param name="yBlock">8x8 destination matrix of Luminance(Y) converted data</param>
|
|||
private static void ConvertScalar(ref L8 l8Start, ref Block8x8F yBlock) |
|||
{ |
|||
for (int i = 0; i < Block8x8F.Size; i++) |
|||
{ |
|||
ref L8 c = ref Unsafe.Add(ref l8Start, i); |
|||
yBlock[i] = c.PackedValue; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|||
{ |
|||
/// <summary>
|
|||
/// Enumerates the quantization tables.
|
|||
/// </summary>
|
|||
internal enum QuantIndex |
|||
{ |
|||
/// <summary>
|
|||
/// The luminance quantization table index.
|
|||
/// </summary>
|
|||
Luminance = 0, |
|||
|
|||
/// <summary>
|
|||
/// The chrominance quantization table index.
|
|||
/// </summary>
|
|||
Chrominance = 1, |
|||
} |
|||
} |
|||
@ -1,165 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
using System.Runtime.Intrinsics; |
|||
using System.Runtime.Intrinsics.X86; |
|||
#endif
|
|||
using SixLabors.ImageSharp.Advanced; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|||
{ |
|||
/// <summary>
|
|||
/// On-stack worker struct to convert TPixel -> Rgb24 of 8x8 pixel blocks.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel type to work on.</typeparam>
|
|||
internal ref struct RgbForwardConverter<TPixel> |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
/// <summary>
|
|||
/// Number of pixels processed per single <see cref="Convert(int, int, ref RowOctet{TPixel})"/> call
|
|||
/// </summary>
|
|||
private const int PixelsPerSample = 8 * 8; |
|||
|
|||
/// <summary>
|
|||
/// Total byte size of processed pixels converted from TPixel to <see cref="Rgb24"/>
|
|||
/// </summary>
|
|||
private const int RgbSpanByteSize = PixelsPerSample * 3; |
|||
|
|||
/// <summary>
|
|||
/// The Red component.
|
|||
/// </summary>
|
|||
public Block8x8F R; |
|||
|
|||
/// <summary>
|
|||
/// The Green component.
|
|||
/// </summary>
|
|||
public Block8x8F G; |
|||
|
|||
/// <summary>
|
|||
/// The Blue component.
|
|||
/// </summary>
|
|||
public Block8x8F B; |
|||
|
|||
/// <summary>
|
|||
/// Temporal 64-byte span to hold unconverted TPixel data.
|
|||
/// </summary>
|
|||
private readonly Span<TPixel> pixelSpan; |
|||
|
|||
/// <summary>
|
|||
/// Temporal 64-byte span to hold converted Rgb24 data.
|
|||
/// </summary>
|
|||
private readonly Span<Rgb24> rgbSpan; |
|||
|
|||
/// <summary>
|
|||
/// Sampled pixel buffer size.
|
|||
/// </summary>
|
|||
private readonly Size samplingAreaSize; |
|||
|
|||
/// <summary>
|
|||
/// <see cref="Configuration"/> for internal operations.
|
|||
/// </summary>
|
|||
private readonly Configuration config; |
|||
|
|||
public RgbForwardConverter(ImageFrame<TPixel> frame) |
|||
{ |
|||
this.R = default; |
|||
this.G = default; |
|||
this.B = default; |
|||
|
|||
// temporal pixel buffers
|
|||
this.pixelSpan = new TPixel[PixelsPerSample].AsSpan(); |
|||
this.rgbSpan = MemoryMarshal.Cast<byte, Rgb24>(new byte[RgbSpanByteSize + RgbToYCbCrConverterVectorized.AvxCompatibilityPadding].AsSpan()); |
|||
|
|||
// frame data
|
|||
this.samplingAreaSize = new Size(frame.Width, frame.Height); |
|||
this.config = frame.GetConfiguration(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets size of sampling area from given frame pixel buffer.
|
|||
/// </summary>
|
|||
private static Size SampleSize => new(8, 8); |
|||
|
|||
/// <summary>
|
|||
/// Converts a 8x8 image area inside 'pixels' at position (x, y) to Rgb24.
|
|||
/// </summary>
|
|||
public void Convert(int x, int y, ref RowOctet<TPixel> currentRows) |
|||
{ |
|||
YCbCrForwardConverter<TPixel>.LoadAndStretchEdges(currentRows, this.pixelSpan, new Point(x, y), SampleSize, this.samplingAreaSize); |
|||
|
|||
PixelOperations<TPixel>.Instance.ToRgb24(this.config, this.pixelSpan, this.rgbSpan); |
|||
|
|||
ref Block8x8F redBlock = ref this.R; |
|||
ref Block8x8F greenBlock = ref this.G; |
|||
ref Block8x8F blueBlock = ref this.B; |
|||
|
|||
if (RgbToYCbCrConverterVectorized.IsSupported) |
|||
{ |
|||
ConvertAvx(this.rgbSpan, ref redBlock, ref greenBlock, ref blueBlock); |
|||
} |
|||
else |
|||
{ |
|||
ConvertScalar(this.rgbSpan, ref redBlock, ref greenBlock, ref blueBlock); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts 8x8 RGB24 pixel matrix to 8x8 Block of floats using Avx2 Intrinsics.
|
|||
/// </summary>
|
|||
/// <param name="rgbSpan">Span of Rgb24 pixels with size of 64</param>
|
|||
/// <param name="rBlock">8x8 destination matrix of Red converted data</param>
|
|||
/// <param name="gBlock">8x8 destination matrix of Blue converted data</param>
|
|||
/// <param name="bBlock">8x8 destination matrix of Green converted data</param>
|
|||
private static void ConvertAvx(Span<Rgb24> rgbSpan, ref Block8x8F rBlock, ref Block8x8F gBlock, ref Block8x8F bBlock) |
|||
{ |
|||
Debug.Assert(RgbToYCbCrConverterVectorized.IsSupported, "AVX2 is required to run this converter"); |
|||
|
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
ref Vector256<byte> rgbByteSpan = ref Unsafe.As<Rgb24, Vector256<byte>>(ref MemoryMarshal.GetReference(rgbSpan)); |
|||
ref Vector256<float> redRef = ref rBlock.V0; |
|||
ref Vector256<float> greenRef = ref gBlock.V0; |
|||
ref Vector256<float> blueRef = ref bBlock.V0; |
|||
var zero = Vector256.Create(0).AsByte(); |
|||
|
|||
var extractToLanesMask = Unsafe.As<byte, Vector256<uint>>(ref MemoryMarshal.GetReference(RgbToYCbCrConverterVectorized.MoveFirst24BytesToSeparateLanes)); |
|||
var extractRgbMask = Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(RgbToYCbCrConverterVectorized.ExtractRgb)); |
|||
Vector256<byte> rgb, rg, bx; |
|||
|
|||
const int bytesPerRgbStride = 24; |
|||
for (nint i = 0; i < 8; i++) |
|||
{ |
|||
rgb = Avx2.PermuteVar8x32(Unsafe.AddByteOffset(ref rgbByteSpan, bytesPerRgbStride * i).AsUInt32(), extractToLanesMask).AsByte(); |
|||
|
|||
rgb = Avx2.Shuffle(rgb, extractRgbMask); |
|||
|
|||
rg = Avx2.UnpackLow(rgb, zero); |
|||
bx = Avx2.UnpackHigh(rgb, zero); |
|||
|
|||
Unsafe.Add(ref redRef, i) = Avx.ConvertToVector256Single(Avx2.UnpackLow(rg, zero).AsInt32()); |
|||
Unsafe.Add(ref greenRef, i) = Avx.ConvertToVector256Single(Avx2.UnpackHigh(rg, zero).AsInt32()); |
|||
Unsafe.Add(ref blueRef, i) = Avx.ConvertToVector256Single(Avx2.UnpackLow(bx, zero).AsInt32()); |
|||
} |
|||
#endif
|
|||
} |
|||
|
|||
private static void ConvertScalar(Span<Rgb24> rgbSpan, ref Block8x8F redBlock, ref Block8x8F greenBlock, ref Block8x8F blueBlock) |
|||
{ |
|||
ref Rgb24 rgbStart = ref MemoryMarshal.GetReference(rgbSpan); |
|||
|
|||
for (int i = 0; i < Block8x8F.Size; i++) |
|||
{ |
|||
Rgb24 c = Unsafe.Add(ref rgbStart, (nint)(uint)i); |
|||
|
|||
redBlock[i] = c.R; |
|||
greenBlock[i] = c.G; |
|||
blueBlock[i] = c.B; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,237 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|||
{ |
|||
/// <summary>
|
|||
/// Provides 8-bit lookup tables for converting from Rgb to YCbCr colorspace.
|
|||
/// Methods to build the tables are based on libjpeg implementation.
|
|||
/// </summary>
|
|||
internal unsafe struct RgbToYCbCrConverterLut |
|||
{ |
|||
/// <summary>
|
|||
/// The red luminance table
|
|||
/// </summary>
|
|||
public fixed int YRTable[256]; |
|||
|
|||
/// <summary>
|
|||
/// The green luminance table
|
|||
/// </summary>
|
|||
public fixed int YGTable[256]; |
|||
|
|||
/// <summary>
|
|||
/// The blue luminance table
|
|||
/// </summary>
|
|||
public fixed int YBTable[256]; |
|||
|
|||
/// <summary>
|
|||
/// The red blue-chrominance table
|
|||
/// </summary>
|
|||
public fixed int CbRTable[256]; |
|||
|
|||
/// <summary>
|
|||
/// The green blue-chrominance table
|
|||
/// </summary>
|
|||
public fixed int CbGTable[256]; |
|||
|
|||
/// <summary>
|
|||
/// The blue blue-chrominance table
|
|||
/// B=>Cb and R=>Cr are the same
|
|||
/// </summary>
|
|||
public fixed int CbBTable[256]; |
|||
|
|||
/// <summary>
|
|||
/// The green red-chrominance table
|
|||
/// </summary>
|
|||
public fixed int CrGTable[256]; |
|||
|
|||
/// <summary>
|
|||
/// The blue red-chrominance table
|
|||
/// </summary>
|
|||
public fixed int CrBTable[256]; |
|||
|
|||
// Speediest right-shift on some machines and gives us enough accuracy at 4 decimal places.
|
|||
private const int ScaleBits = 16; |
|||
|
|||
private const int CBCrOffset = 128 << ScaleBits; |
|||
|
|||
private const int Half = 1 << (ScaleBits - 1); |
|||
|
|||
/// <summary>
|
|||
/// Initializes the YCbCr tables
|
|||
/// </summary>
|
|||
/// <returns>The initialized <see cref="RgbToYCbCrConverterLut"/></returns>
|
|||
public static RgbToYCbCrConverterLut Create() |
|||
{ |
|||
RgbToYCbCrConverterLut tables = default; |
|||
|
|||
for (int i = 0; i <= 255; i++) |
|||
{ |
|||
// The values for the calculations are left scaled up since we must add them together before rounding.
|
|||
tables.YRTable[i] = Fix(0.299F) * i; |
|||
tables.YGTable[i] = Fix(0.587F) * i; |
|||
tables.YBTable[i] = (Fix(0.114F) * i) + Half; |
|||
tables.CbRTable[i] = (-Fix(0.168735892F)) * i; |
|||
tables.CbGTable[i] = (-Fix(0.331264108F)) * i; |
|||
|
|||
// We use a rounding fudge - factor of 0.5 - epsilon for Cb and Cr.
|
|||
// This ensures that the maximum output will round to 255
|
|||
// not 256, and thus that we don't have to range-limit.
|
|||
//
|
|||
// B=>Cb and R=>Cr tables are the same
|
|||
tables.CbBTable[i] = (Fix(0.5F) * i) + CBCrOffset + Half - 1; |
|||
|
|||
tables.CrGTable[i] = (-Fix(0.418687589F)) * i; |
|||
tables.CrBTable[i] = (-Fix(0.081312411F)) * i; |
|||
} |
|||
|
|||
return tables; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private float CalculateY(byte r, byte g, byte b) |
|||
{ |
|||
// float y = (0.299F * r) + (0.587F * g) + (0.114F * b);
|
|||
return (this.YRTable[r] + this.YGTable[g] + this.YBTable[b]) >> ScaleBits; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private float CalculateCb(byte r, byte g, byte b) |
|||
{ |
|||
// float cb = 128F + ((-0.168736F * r) - (0.331264F * g) + (0.5F * b));
|
|||
return (this.CbRTable[r] + this.CbGTable[g] + this.CbBTable[b]) >> ScaleBits; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private float CalculateCr(byte r, byte g, byte b) |
|||
{ |
|||
// float cr = 128F + ((0.5F * r) - (0.418688F * g) - (0.081312F * b));
|
|||
return (this.CbBTable[r] + this.CrGTable[g] + this.CrBTable[b]) >> ScaleBits; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts Rgb24 pixels into YCbCr color space with 4:4:4 subsampling sampling of luminance and chroma.
|
|||
/// </summary>
|
|||
/// <param name="rgbSpan">Span of Rgb24 pixel data</param>
|
|||
/// <param name="yBlock">Resulting Y values block</param>
|
|||
/// <param name="cbBlock">Resulting Cb values block</param>
|
|||
/// <param name="crBlock">Resulting Cr values block</param>
|
|||
public void Convert444(Span<Rgb24> rgbSpan, ref Block8x8F yBlock, ref Block8x8F cbBlock, ref Block8x8F crBlock) |
|||
{ |
|||
ref Rgb24 rgbStart = ref rgbSpan[0]; |
|||
|
|||
for (int i = 0; i < Block8x8F.Size; i++) |
|||
{ |
|||
Rgb24 c = Unsafe.Add(ref rgbStart, i); |
|||
|
|||
yBlock[i] = this.CalculateY(c.R, c.G, c.B); |
|||
cbBlock[i] = this.CalculateCb(c.R, c.G, c.B); |
|||
crBlock[i] = this.CalculateCr(c.R, c.G, c.B); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts Rgb24 pixels into YCbCr color space with 4:2:0 subsampling of luminance and chroma.
|
|||
/// </summary>
|
|||
/// <remarks>Calculates 2 out of 4 luminance blocks and half of chroma blocks. This method must be called twice per 4x 8x8 DCT blocks with different row param.</remarks>
|
|||
/// <param name="rgbSpan">Span of Rgb24 pixel data</param>
|
|||
/// <param name="yBlockLeft">First or "left" resulting Y block</param>
|
|||
/// <param name="yBlockRight">Second or "right" resulting Y block</param>
|
|||
/// <param name="cbBlock">Resulting Cb values block</param>
|
|||
/// <param name="crBlock">Resulting Cr values block</param>
|
|||
/// <param name="row">Row index of the 16x16 block, 0 or 1</param>
|
|||
public void Convert420(Span<Rgb24> rgbSpan, ref Block8x8F yBlockLeft, ref Block8x8F yBlockRight, ref Block8x8F cbBlock, ref Block8x8F crBlock, int row) |
|||
{ |
|||
DebugGuard.MustBeBetweenOrEqualTo(row, 0, 1, nameof(row)); |
|||
|
|||
ref float yBlockLeftRef = ref Unsafe.As<Block8x8F, float>(ref yBlockLeft); |
|||
ref float yBlockRightRef = ref Unsafe.As<Block8x8F, float>(ref yBlockRight); |
|||
|
|||
// 0-31 or 32-63
|
|||
// upper or lower part
|
|||
int chromaWriteOffset = row * (Block8x8F.Size / 2); |
|||
ref float cbBlockRef = ref Unsafe.Add(ref Unsafe.As<Block8x8F, float>(ref cbBlock), chromaWriteOffset); |
|||
ref float crBlockRef = ref Unsafe.Add(ref Unsafe.As<Block8x8F, float>(ref crBlock), chromaWriteOffset); |
|||
|
|||
ref Rgb24 rgbStart = ref rgbSpan[0]; |
|||
|
|||
for (int i = 0; i < 8; i += 2) |
|||
{ |
|||
int yBlockWriteOffset = i * 8; |
|||
ref Rgb24 stride = ref Unsafe.Add(ref rgbStart, i * 16); |
|||
|
|||
int chromaOffset = 8 * (i / 2); |
|||
|
|||
// left
|
|||
this.ConvertChunk420( |
|||
ref stride, |
|||
ref Unsafe.Add(ref yBlockLeftRef, yBlockWriteOffset), |
|||
ref Unsafe.Add(ref cbBlockRef, chromaOffset), |
|||
ref Unsafe.Add(ref crBlockRef, chromaOffset)); |
|||
|
|||
// right
|
|||
this.ConvertChunk420( |
|||
ref Unsafe.Add(ref stride, 8), |
|||
ref Unsafe.Add(ref yBlockRightRef, yBlockWriteOffset), |
|||
ref Unsafe.Add(ref cbBlockRef, chromaOffset + 4), |
|||
ref Unsafe.Add(ref crBlockRef, chromaOffset + 4)); |
|||
} |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private void ConvertChunk420(ref Rgb24 stride, ref float yBlock, ref float cbBlock, ref float crBlock) |
|||
{ |
|||
// jpeg 8x8 blocks are processed as 16x16 blocks with 16x8 subpasses (this is done for performance reasons)
|
|||
// each row is 16 pixels wide thus +16 stride reference offset
|
|||
// resulting luminance (Y`) are sampled at original resolution thus +8 reference offset
|
|||
for (int k = 0; k < 8; k += 2) |
|||
{ |
|||
ref float yBlockRef = ref Unsafe.Add(ref yBlock, k); |
|||
|
|||
// top row
|
|||
Rgb24 px0 = Unsafe.Add(ref stride, k); |
|||
Rgb24 px1 = Unsafe.Add(ref stride, k + 1); |
|||
yBlockRef = this.CalculateY(px0.R, px0.G, px0.B); |
|||
Unsafe.Add(ref yBlockRef, 1) = this.CalculateY(px1.R, px1.G, px1.B); |
|||
|
|||
// bottom row
|
|||
Rgb24 px2 = Unsafe.Add(ref stride, k + 16); |
|||
Rgb24 px3 = Unsafe.Add(ref stride, k + 17); |
|||
Unsafe.Add(ref yBlockRef, 8) = this.CalculateY(px2.R, px2.G, px2.B); |
|||
Unsafe.Add(ref yBlockRef, 9) = this.CalculateY(px3.R, px3.G, px3.B); |
|||
|
|||
// chroma average for 2x2 pixel block
|
|||
Unsafe.Add(ref cbBlock, k / 2) = this.CalculateAverageCb(px0, px1, px2, px3); |
|||
Unsafe.Add(ref crBlock, k / 2) = this.CalculateAverageCr(px0, px1, px2, px3); |
|||
} |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private float CalculateAverageCb(Rgb24 px0, Rgb24 px1, Rgb24 px2, Rgb24 px3) |
|||
{ |
|||
return 0.25f |
|||
* (this.CalculateCb(px0.R, px0.G, px0.B) |
|||
+ this.CalculateCb(px1.R, px1.G, px1.B) |
|||
+ this.CalculateCb(px2.R, px2.G, px2.B) |
|||
+ this.CalculateCb(px3.R, px3.G, px3.B)); |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private float CalculateAverageCr(Rgb24 px0, Rgb24 px1, Rgb24 px2, Rgb24 px3) |
|||
{ |
|||
return 0.25f |
|||
* (this.CalculateCr(px0.R, px0.G, px0.B) |
|||
+ this.CalculateCr(px1.R, px1.G, px1.B) |
|||
+ this.CalculateCr(px2.R, px2.G, px2.B) |
|||
+ this.CalculateCr(px3.R, px3.G, px3.B)); |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static int Fix(float x) |
|||
=> (int)((x * (1L << ScaleBits)) + 0.5F); |
|||
} |
|||
} |
|||
@ -1,259 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System; |
|||
using System.Diagnostics; |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using System.Runtime.Intrinsics; |
|||
using System.Runtime.Intrinsics.X86; |
|||
#endif
|
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|||
{ |
|||
internal static class RgbToYCbCrConverterVectorized |
|||
{ |
|||
public static bool IsSupported |
|||
{ |
|||
get |
|||
{ |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
return Avx2.IsSupported; |
|||
#else
|
|||
return false; |
|||
#endif
|
|||
} |
|||
} |
|||
|
|||
public static int AvxCompatibilityPadding |
|||
{ |
|||
// rgb byte matrices contain 8 strides by 8 pixels each, thus 64 pixels total
|
|||
// Strides are stored sequentially - one big span of 64 * 3 = 192 bytes
|
|||
// Each stride has exactly 3 * 8 = 24 bytes or 3 * 8 * 8 = 192 bits
|
|||
// Avx registers are 256 bits so rgb span will be loaded with extra 64 bits from the next stride:
|
|||
// stride 0 0 - 192 -(+64bits)-> 256
|
|||
// stride 1 192 - 384 -(+64bits)-> 448
|
|||
// stride 2 384 - 576 -(+64bits)-> 640
|
|||
// stride 3 576 - 768 -(+64bits)-> 832
|
|||
// stride 4 768 - 960 -(+64bits)-> 1024
|
|||
// stride 5 960 - 1152 -(+64bits)-> 1216
|
|||
// stride 6 1152 - 1344 -(+64bits)-> 1408
|
|||
// stride 7 1344 - 1536 -(+64bits)-> 1600 <-- READ ACCESS VIOLATION
|
|||
//
|
|||
// Total size of the 64 pixel rgb span: 64 * 3 * 8 = 1536 bits, avx operations require 1600 bits
|
|||
// This is not permitted - we are reading foreign memory
|
|||
//
|
|||
// 8 byte padding to rgb byte span will solve this problem without extra code in converters
|
|||
get |
|||
{ |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
if (IsSupported) |
|||
{ |
|||
return 8; |
|||
} |
|||
#endif
|
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
|
|||
internal static ReadOnlySpan<byte> MoveFirst24BytesToSeparateLanes => new byte[] |
|||
{ |
|||
0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 6, 0, 0, 0, |
|||
3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 7, 0, 0, 0 |
|||
}; |
|||
|
|||
internal static ReadOnlySpan<byte> ExtractRgb => new byte[] |
|||
{ |
|||
0, 3, 6, 9, 1, 4, 7, 10, 2, 5, 8, 11, 0xFF, 0xFF, 0xFF, 0xFF, |
|||
0, 3, 6, 9, 1, 4, 7, 10, 2, 5, 8, 11, 0xFF, 0xFF, 0xFF, 0xFF |
|||
}; |
|||
#endif
|
|||
|
|||
/// <summary>
|
|||
/// Converts 8x8 Rgb24 pixel matrix to YCbCr pixel matrices with 4:4:4 subsampling
|
|||
/// </summary>
|
|||
/// <remarks>Total size of rgb span must be 200 bytes</remarks>
|
|||
/// <param name="rgbSpan">Span of rgb pixels with size of 64</param>
|
|||
/// <param name="yBlock">8x8 destination matrix of Luminance(Y) converted data</param>
|
|||
/// <param name="cbBlock">8x8 destination matrix of Chrominance(Cb) converted data</param>
|
|||
/// <param name="crBlock">8x8 destination matrix of Chrominance(Cr) converted data</param>
|
|||
public static void Convert444(ReadOnlySpan<Rgb24> rgbSpan, ref Block8x8F yBlock, ref Block8x8F cbBlock, ref Block8x8F crBlock) |
|||
{ |
|||
Debug.Assert(IsSupported, "AVX2 is required to run this converter"); |
|||
|
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
var f0299 = Vector256.Create(0.299f); |
|||
var f0587 = Vector256.Create(0.587f); |
|||
var f0114 = Vector256.Create(0.114f); |
|||
var fn0168736 = Vector256.Create(-0.168736f); |
|||
var fn0331264 = Vector256.Create(-0.331264f); |
|||
var f128 = Vector256.Create(128f); |
|||
var fn0418688 = Vector256.Create(-0.418688f); |
|||
var fn0081312F = Vector256.Create(-0.081312F); |
|||
var f05 = Vector256.Create(0.5f); |
|||
var zero = Vector256.Create(0).AsByte(); |
|||
|
|||
ref Vector256<byte> rgbByteSpan = ref Unsafe.As<Rgb24, Vector256<byte>>(ref MemoryMarshal.GetReference(rgbSpan)); |
|||
ref Vector256<float> destYRef = ref yBlock.V0; |
|||
ref Vector256<float> destCbRef = ref cbBlock.V0; |
|||
ref Vector256<float> destCrRef = ref crBlock.V0; |
|||
|
|||
var extractToLanesMask = Unsafe.As<byte, Vector256<uint>>(ref MemoryMarshal.GetReference(MoveFirst24BytesToSeparateLanes)); |
|||
var extractRgbMask = Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(ExtractRgb)); |
|||
Vector256<byte> rgb, rg, bx; |
|||
Vector256<float> r, g, b; |
|||
|
|||
const int bytesPerRgbStride = 24; |
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
rgb = Avx2.PermuteVar8x32(Unsafe.AddByteOffset(ref rgbByteSpan, (IntPtr)(bytesPerRgbStride * i)).AsUInt32(), extractToLanesMask).AsByte(); |
|||
|
|||
rgb = Avx2.Shuffle(rgb, extractRgbMask); |
|||
|
|||
rg = Avx2.UnpackLow(rgb, zero); |
|||
bx = Avx2.UnpackHigh(rgb, zero); |
|||
|
|||
r = Avx.ConvertToVector256Single(Avx2.UnpackLow(rg, zero).AsInt32()); |
|||
g = Avx.ConvertToVector256Single(Avx2.UnpackHigh(rg, zero).AsInt32()); |
|||
b = Avx.ConvertToVector256Single(Avx2.UnpackLow(bx, zero).AsInt32()); |
|||
|
|||
// (0.299F * r) + (0.587F * g) + (0.114F * b);
|
|||
Unsafe.Add(ref destYRef, i) = SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(f0114, b), f0587, g), f0299, r); |
|||
|
|||
// 128F + ((-0.168736F * r) - (0.331264F * g) + (0.5F * b))
|
|||
Unsafe.Add(ref destCbRef, i) = Avx.Add(f128, SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(f05, b), fn0331264, g), fn0168736, r)); |
|||
|
|||
// 128F + ((0.5F * r) - (0.418688F * g) - (0.081312F * b))
|
|||
Unsafe.Add(ref destCrRef, i) = Avx.Add(f128, SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(fn0081312F, b), fn0418688, g), f05, r)); |
|||
} |
|||
#endif
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts 16x8 Rgb24 pixels matrix to 2 Y 8x8 matrices with 4:2:0 subsampling
|
|||
/// </summary>
|
|||
public static void Convert420(ReadOnlySpan<Rgb24> rgbSpan, ref Block8x8F yBlockLeft, ref Block8x8F yBlockRight, ref Block8x8F cbBlock, ref Block8x8F crBlock, int row) |
|||
{ |
|||
Debug.Assert(IsSupported, "AVX2 is required to run this converter"); |
|||
|
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
var f0299 = Vector256.Create(0.299f); |
|||
var f0587 = Vector256.Create(0.587f); |
|||
var f0114 = Vector256.Create(0.114f); |
|||
var fn0168736 = Vector256.Create(-0.168736f); |
|||
var fn0331264 = Vector256.Create(-0.331264f); |
|||
var f128 = Vector256.Create(128f); |
|||
var fn0418688 = Vector256.Create(-0.418688f); |
|||
var fn0081312F = Vector256.Create(-0.081312F); |
|||
var f05 = Vector256.Create(0.5f); |
|||
var zero = Vector256.Create(0).AsByte(); |
|||
|
|||
ref Vector256<byte> rgbByteSpan = ref Unsafe.As<Rgb24, Vector256<byte>>(ref MemoryMarshal.GetReference(rgbSpan)); |
|||
|
|||
int destOffset = row * 4; |
|||
|
|||
ref Vector256<float> destCbRef = ref Unsafe.Add(ref Unsafe.As<Block8x8F, Vector256<float>>(ref cbBlock), destOffset); |
|||
ref Vector256<float> destCrRef = ref Unsafe.Add(ref Unsafe.As<Block8x8F, Vector256<float>>(ref crBlock), destOffset); |
|||
|
|||
var extractToLanesMask = Unsafe.As<byte, Vector256<uint>>(ref MemoryMarshal.GetReference(MoveFirst24BytesToSeparateLanes)); |
|||
var extractRgbMask = Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(ExtractRgb)); |
|||
Vector256<byte> rgb, rg, bx; |
|||
Vector256<float> r, g, b; |
|||
|
|||
Span<Vector256<float>> rDataLanes = stackalloc Vector256<float>[4]; |
|||
Span<Vector256<float>> gDataLanes = stackalloc Vector256<float>[4]; |
|||
Span<Vector256<float>> bDataLanes = stackalloc Vector256<float>[4]; |
|||
|
|||
const int bytesPerRgbStride = 24; |
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
// 16x2 => 8x1
|
|||
// left 8x8 column conversions
|
|||
for (int j = 0; j < 4; j += 2) |
|||
{ |
|||
rgb = Avx2.PermuteVar8x32(Unsafe.AddByteOffset(ref rgbByteSpan, (IntPtr)(bytesPerRgbStride * ((i * 4) + j))).AsUInt32(), extractToLanesMask).AsByte(); |
|||
|
|||
rgb = Avx2.Shuffle(rgb, extractRgbMask); |
|||
|
|||
rg = Avx2.UnpackLow(rgb, zero); |
|||
bx = Avx2.UnpackHigh(rgb, zero); |
|||
|
|||
r = Avx.ConvertToVector256Single(Avx2.UnpackLow(rg, zero).AsInt32()); |
|||
g = Avx.ConvertToVector256Single(Avx2.UnpackHigh(rg, zero).AsInt32()); |
|||
b = Avx.ConvertToVector256Single(Avx2.UnpackLow(bx, zero).AsInt32()); |
|||
|
|||
int yBlockVerticalOffset = (i * 2) + ((j & 2) >> 1); |
|||
|
|||
// (0.299F * r) + (0.587F * g) + (0.114F * b);
|
|||
Unsafe.Add(ref yBlockLeft.V0, yBlockVerticalOffset) = SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(f0114, b), f0587, g), f0299, r); |
|||
|
|||
rDataLanes[j] = r; |
|||
gDataLanes[j] = g; |
|||
bDataLanes[j] = b; |
|||
} |
|||
|
|||
// 16x2 => 8x1
|
|||
// right 8x8 column conversions
|
|||
for (int j = 1; j < 4; j += 2) |
|||
{ |
|||
rgb = Avx2.PermuteVar8x32(Unsafe.AddByteOffset(ref rgbByteSpan, (IntPtr)(bytesPerRgbStride * ((i * 4) + j))).AsUInt32(), extractToLanesMask).AsByte(); |
|||
|
|||
rgb = Avx2.Shuffle(rgb, extractRgbMask); |
|||
|
|||
rg = Avx2.UnpackLow(rgb, zero); |
|||
bx = Avx2.UnpackHigh(rgb, zero); |
|||
|
|||
r = Avx.ConvertToVector256Single(Avx2.UnpackLow(rg, zero).AsInt32()); |
|||
g = Avx.ConvertToVector256Single(Avx2.UnpackHigh(rg, zero).AsInt32()); |
|||
b = Avx.ConvertToVector256Single(Avx2.UnpackLow(bx, zero).AsInt32()); |
|||
|
|||
int yBlockVerticalOffset = (i * 2) + ((j & 2) >> 1); |
|||
|
|||
// (0.299F * r) + (0.587F * g) + (0.114F * b);
|
|||
Unsafe.Add(ref yBlockRight.V0, yBlockVerticalOffset) = SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(f0114, b), f0587, g), f0299, r); |
|||
|
|||
rDataLanes[j] = r; |
|||
gDataLanes[j] = g; |
|||
bDataLanes[j] = b; |
|||
} |
|||
|
|||
r = Scale16x2_8x1(rDataLanes); |
|||
g = Scale16x2_8x1(gDataLanes); |
|||
b = Scale16x2_8x1(bDataLanes); |
|||
|
|||
// 128F + ((-0.168736F * r) - (0.331264F * g) + (0.5F * b))
|
|||
Unsafe.Add(ref destCbRef, i) = Avx.Add(f128, SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(f05, b), fn0331264, g), fn0168736, r)); |
|||
|
|||
// 128F + ((0.5F * r) - (0.418688F * g) - (0.081312F * b))
|
|||
Unsafe.Add(ref destCrRef, i) = Avx.Add(f128, SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(fn0081312F, b), fn0418688, g), f05, r)); |
|||
} |
|||
#endif
|
|||
} |
|||
|
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
/// <summary>
|
|||
/// Scales 16x2 matrix to 8x1 using 2x2 average
|
|||
/// </summary>
|
|||
/// <param name="v">Input matrix consisting of 4 256bit vectors</param>
|
|||
/// <returns>256bit vector containing upper and lower scaled parts of the input matrix</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
internal static Vector256<float> Scale16x2_8x1(ReadOnlySpan<Vector256<float>> v) |
|||
{ |
|||
Debug.Assert(Avx2.IsSupported, "AVX2 is required to run this converter"); |
|||
DebugGuard.IsTrue(v.Length == 4, "Input span must consist of 4 elements"); |
|||
|
|||
var f025 = Vector256.Create(0.25f); |
|||
|
|||
Vector256<float> left = Avx.Add(v[0], v[2]); |
|||
Vector256<float> right = Avx.Add(v[1], v[3]); |
|||
Vector256<float> avg2x2 = Avx.Multiply(Avx.HorizontalAdd(left, right), f025); |
|||
|
|||
return Avx2.Permute4x64(avg2x2.AsDouble(), 0b11_01_10_00).AsSingle(); |
|||
} |
|||
#endif
|
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|||
{ |
|||
/// <summary>
|
|||
/// Converter used to convert pixel data to jpeg spectral data.
|
|||
/// </summary>
|
|||
internal abstract class SpectralConverter |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,149 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System; |
|||
using System.Buffers; |
|||
using System.Linq; |
|||
using SixLabors.ImageSharp.Advanced; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|||
{ |
|||
/// <inheritdoc/>
|
|||
internal class SpectralConverter<TPixel> : SpectralConverter, IDisposable |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
private readonly ComponentProcessor[] componentProcessors; |
|||
|
|||
private readonly int pixelRowsPerStep; |
|||
|
|||
private int pixelRowCounter; |
|||
|
|||
private readonly Buffer2D<TPixel> pixelBuffer; |
|||
|
|||
private readonly IMemoryOwner<float> redLane; |
|||
|
|||
private readonly IMemoryOwner<float> greenLane; |
|||
|
|||
private readonly IMemoryOwner<float> blueLane; |
|||
|
|||
private readonly int alignedPixelWidth; |
|||
|
|||
private readonly JpegColorConverterBase colorConverter; |
|||
|
|||
public SpectralConverter(JpegFrame frame, Image<TPixel> image, Block8x8F[] dequantTables) |
|||
{ |
|||
MemoryAllocator allocator = image.GetConfiguration().MemoryAllocator; |
|||
|
|||
// iteration data
|
|||
int majorBlockWidth = frame.Components.Max((component) => component.SizeInBlocks.Width); |
|||
int majorVerticalSamplingFactor = frame.Components.Max((component) => component.SamplingFactors.Height); |
|||
|
|||
const int blockPixelHeight = 8; |
|||
this.pixelRowsPerStep = majorVerticalSamplingFactor * blockPixelHeight; |
|||
|
|||
// pixel buffer of the image
|
|||
this.pixelBuffer = image.GetRootFramePixelBuffer(); |
|||
|
|||
// component processors from spectral to Rgb24
|
|||
const int blockPixelWidth = 8; |
|||
this.alignedPixelWidth = majorBlockWidth * blockPixelWidth; |
|||
var postProcessorBufferSize = new Size(this.alignedPixelWidth, this.pixelRowsPerStep); |
|||
this.componentProcessors = new ComponentProcessor[frame.Components.Length]; |
|||
for (int i = 0; i < this.componentProcessors.Length; i++) |
|||
{ |
|||
Component component = frame.Components[i]; |
|||
this.componentProcessors[i] = new ComponentProcessor( |
|||
allocator, |
|||
component, |
|||
postProcessorBufferSize, |
|||
dequantTables[component.QuantizationTableIndex]); |
|||
} |
|||
|
|||
this.redLane = allocator.Allocate<float>(this.alignedPixelWidth, AllocationOptions.Clean); |
|||
this.greenLane = allocator.Allocate<float>(this.alignedPixelWidth, AllocationOptions.Clean); |
|||
this.blueLane = allocator.Allocate<float>(this.alignedPixelWidth, AllocationOptions.Clean); |
|||
|
|||
// color converter from Rgb24 to YCbCr
|
|||
this.colorConverter = JpegColorConverterBase.GetConverter(colorSpace: frame.ColorSpace, precision: 8); |
|||
} |
|||
|
|||
public void ConvertStrideBaseline() |
|||
{ |
|||
// Codestyle suggests expression body but it
|
|||
// also requires empty line before comments
|
|||
// which looks ugly with expression bodies thus this warning disable
|
|||
#pragma warning disable IDE0022
|
|||
// Convert next pixel stride using single spectral `stride'
|
|||
// Note that zero passing eliminates the need of virtual call
|
|||
// from JpegComponentPostProcessor
|
|||
this.ConvertStride(spectralStep: 0); |
|||
#pragma warning restore IDE0022
|
|||
} |
|||
|
|||
public void ConvertFull() |
|||
{ |
|||
int steps = (int)Numerics.DivideCeil((uint)this.pixelBuffer.Height, (uint)this.pixelRowsPerStep); |
|||
for (int i = 0; i < steps; i++) |
|||
{ |
|||
this.ConvertStride(i); |
|||
} |
|||
} |
|||
|
|||
private void ConvertStride(int spectralStep) |
|||
{ |
|||
int start = this.pixelRowCounter; |
|||
int end = start + this.pixelRowsPerStep; |
|||
|
|||
int pixelBufferLastVerticalIndex = this.pixelBuffer.Height - 1; |
|||
|
|||
// Pixel strides must be padded with the last pixel of the stride
|
|||
int paddingStartIndex = this.pixelBuffer.Width; |
|||
int paddedPixelsCount = this.alignedPixelWidth - this.pixelBuffer.Width; |
|||
|
|||
Span<float> rLane = this.redLane.GetSpan(); |
|||
Span<float> gLane = this.greenLane.GetSpan(); |
|||
Span<float> bLane = this.blueLane.GetSpan(); |
|||
|
|||
for (int yy = start; yy < end; yy++) |
|||
{ |
|||
int y = yy - this.pixelRowCounter; |
|||
|
|||
// Unpack TPixel to r/g/b planes
|
|||
int srcIndex = Math.Min(yy, pixelBufferLastVerticalIndex); |
|||
Span<TPixel> sourceRow = this.pixelBuffer.DangerousGetRowSpan(srcIndex); |
|||
PixelOperations<TPixel>.Instance.UnpackIntoRgbPlanes(rLane, gLane, bLane, sourceRow); |
|||
|
|||
rLane.Slice(paddingStartIndex).Fill(rLane[paddingStartIndex - 1]); |
|||
gLane.Slice(paddingStartIndex).Fill(gLane[paddingStartIndex - 1]); |
|||
bLane.Slice(paddingStartIndex).Fill(bLane[paddingStartIndex - 1]); |
|||
|
|||
// Convert from rgb24 to target pixel type
|
|||
var values = new JpegColorConverterBase.ComponentValues(this.componentProcessors, y); |
|||
this.colorConverter.ConvertFromRgb(values, rLane, gLane, bLane); |
|||
} |
|||
|
|||
// Convert pixels to spectral
|
|||
for (int i = 0; i < this.componentProcessors.Length; i++) |
|||
{ |
|||
this.componentProcessors[i].CopyColorBufferToBlocks(spectralStep); |
|||
} |
|||
|
|||
this.pixelRowCounter = end; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public void Dispose() |
|||
{ |
|||
foreach (ComponentProcessor cpp in this.componentProcessors) |
|||
{ |
|||
cpp.Dispose(); |
|||
} |
|||
|
|||
this.redLane.Dispose(); |
|||
this.greenLane.Dispose(); |
|||
this.blueLane.Dispose(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,121 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System; |
|||
using System.Runtime.InteropServices; |
|||
using SixLabors.ImageSharp.Advanced; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|||
{ |
|||
/// <summary>
|
|||
/// On-stack worker struct to efficiently encapsulate the TPixel -> Rgb24 -> YCbCr conversion chain of 8x8 pixel blocks.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel type to work on</typeparam>
|
|||
internal ref struct YCbCrForwardConverter420<TPixel> |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
/// <summary>
|
|||
/// Number of pixels processed per single <see cref="Convert(int, int, ref RowOctet{TPixel}, int)"/> call
|
|||
/// </summary>
|
|||
private const int PixelsPerSample = 16 * 8; |
|||
|
|||
/// <summary>
|
|||
/// Total byte size of processed pixels converted from TPixel to <see cref="Rgb24"/>
|
|||
/// </summary>
|
|||
private const int RgbSpanByteSize = PixelsPerSample * 3; |
|||
|
|||
/// <summary>
|
|||
/// The left Y component
|
|||
/// </summary>
|
|||
public Block8x8F YLeft; |
|||
|
|||
/// <summary>
|
|||
/// The left Y component
|
|||
/// </summary>
|
|||
public Block8x8F YRight; |
|||
|
|||
/// <summary>
|
|||
/// The Cb component
|
|||
/// </summary>
|
|||
public Block8x8F Cb; |
|||
|
|||
/// <summary>
|
|||
/// The Cr component
|
|||
/// </summary>
|
|||
public Block8x8F Cr; |
|||
|
|||
/// <summary>
|
|||
/// The color conversion tables
|
|||
/// </summary>
|
|||
private RgbToYCbCrConverterLut colorTables; |
|||
|
|||
/// <summary>
|
|||
/// Temporal 16x8 block to hold TPixel data
|
|||
/// </summary>
|
|||
private readonly Span<TPixel> pixelSpan; |
|||
|
|||
/// <summary>
|
|||
/// Temporal RGB block
|
|||
/// </summary>
|
|||
private readonly Span<Rgb24> rgbSpan; |
|||
|
|||
/// <summary>
|
|||
/// Sampled pixel buffer size
|
|||
/// </summary>
|
|||
private readonly Size samplingAreaSize; |
|||
|
|||
/// <summary>
|
|||
/// <see cref="Configuration"/> for internal operations
|
|||
/// </summary>
|
|||
private readonly Configuration config; |
|||
|
|||
public YCbCrForwardConverter420(ImageFrame<TPixel> frame) |
|||
{ |
|||
// matrices would be filled during convert calls
|
|||
this.YLeft = default; |
|||
this.YRight = default; |
|||
this.Cb = default; |
|||
this.Cr = default; |
|||
|
|||
// temporal pixel buffers
|
|||
this.pixelSpan = new TPixel[PixelsPerSample].AsSpan(); |
|||
this.rgbSpan = MemoryMarshal.Cast<byte, Rgb24>(new byte[RgbSpanByteSize + RgbToYCbCrConverterVectorized.AvxCompatibilityPadding].AsSpan()); |
|||
|
|||
// frame data
|
|||
this.samplingAreaSize = new Size(frame.Width, frame.Height); |
|||
this.config = frame.GetConfiguration(); |
|||
|
|||
// conversion vector fallback data
|
|||
if (!RgbToYCbCrConverterVectorized.IsSupported) |
|||
{ |
|||
this.colorTables = RgbToYCbCrConverterLut.Create(); |
|||
} |
|||
else |
|||
{ |
|||
this.colorTables = default; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets size of sampling area from given frame pixel buffer.
|
|||
/// </summary>
|
|||
private static Size SampleSize => new(16, 8); |
|||
|
|||
public void Convert(int x, int y, ref RowOctet<TPixel> currentRows, int idx) |
|||
{ |
|||
YCbCrForwardConverter<TPixel>.LoadAndStretchEdges(currentRows, this.pixelSpan, new Point(x, y), SampleSize, this.samplingAreaSize); |
|||
|
|||
PixelOperations<TPixel>.Instance.ToRgb24(this.config, this.pixelSpan, this.rgbSpan); |
|||
|
|||
if (RgbToYCbCrConverterVectorized.IsSupported) |
|||
{ |
|||
RgbToYCbCrConverterVectorized.Convert420(this.rgbSpan, ref this.YLeft, ref this.YRight, ref this.Cb, ref this.Cr, idx); |
|||
} |
|||
else |
|||
{ |
|||
this.colorTables.Convert420(this.rgbSpan, ref this.YLeft, ref this.YRight, ref this.Cb, ref this.Cr, idx); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,122 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System; |
|||
using System.Runtime.InteropServices; |
|||
using SixLabors.ImageSharp.Advanced; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|||
{ |
|||
/// <summary>
|
|||
/// On-stack worker struct to efficiently encapsulate the TPixel -> Rgb24 -> YCbCr conversion chain of 8x8 pixel blocks.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel type to work on</typeparam>
|
|||
internal ref struct YCbCrForwardConverter444<TPixel> |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
/// <summary>
|
|||
/// Number of pixels processed per single <see cref="Convert(int, int, ref RowOctet{TPixel})"/> call
|
|||
/// </summary>
|
|||
private const int PixelsPerSample = 8 * 8; |
|||
|
|||
/// <summary>
|
|||
/// Total byte size of processed pixels converted from TPixel to <see cref="Rgb24"/>
|
|||
/// </summary>
|
|||
private const int RgbSpanByteSize = PixelsPerSample * 3; |
|||
|
|||
/// <summary>
|
|||
/// The Y component
|
|||
/// </summary>
|
|||
public Block8x8F Y; |
|||
|
|||
/// <summary>
|
|||
/// The Cb component
|
|||
/// </summary>
|
|||
public Block8x8F Cb; |
|||
|
|||
/// <summary>
|
|||
/// The Cr component
|
|||
/// </summary>
|
|||
public Block8x8F Cr; |
|||
|
|||
/// <summary>
|
|||
/// The color conversion tables
|
|||
/// </summary>
|
|||
private RgbToYCbCrConverterLut colorTables; |
|||
|
|||
/// <summary>
|
|||
/// Temporal 64-byte span to hold unconverted TPixel data
|
|||
/// </summary>
|
|||
private readonly Span<TPixel> pixelSpan; |
|||
|
|||
/// <summary>
|
|||
/// Temporal 64-byte span to hold converted Rgb24 data
|
|||
/// </summary>
|
|||
private readonly Span<Rgb24> rgbSpan; |
|||
|
|||
/// <summary>
|
|||
/// Sampled pixel buffer size
|
|||
/// </summary>
|
|||
private readonly Size samplingAreaSize; |
|||
|
|||
/// <summary>
|
|||
/// <see cref="Configuration"/> for internal operations
|
|||
/// </summary>
|
|||
private readonly Configuration config; |
|||
|
|||
public YCbCrForwardConverter444(ImageFrame<TPixel> frame) |
|||
{ |
|||
// matrices would be filled during convert calls
|
|||
this.Y = default; |
|||
this.Cb = default; |
|||
this.Cr = default; |
|||
|
|||
// temporal pixel buffers
|
|||
this.pixelSpan = new TPixel[PixelsPerSample].AsSpan(); |
|||
this.rgbSpan = MemoryMarshal.Cast<byte, Rgb24>(new byte[RgbSpanByteSize + RgbToYCbCrConverterVectorized.AvxCompatibilityPadding].AsSpan()); |
|||
|
|||
// frame data
|
|||
this.samplingAreaSize = new Size(frame.Width, frame.Height); |
|||
this.config = frame.GetConfiguration(); |
|||
|
|||
// conversion vector fallback data
|
|||
if (!RgbToYCbCrConverterVectorized.IsSupported) |
|||
{ |
|||
this.colorTables = RgbToYCbCrConverterLut.Create(); |
|||
} |
|||
else |
|||
{ |
|||
this.colorTables = default; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets size of sampling area from given frame pixel buffer.
|
|||
/// </summary>
|
|||
private static Size SampleSize => new(8, 8); |
|||
|
|||
/// <summary>
|
|||
/// Converts a 8x8 image area inside 'pixels' at position (x,y) placing the result members of the structure (<see cref="Y"/>, <see cref="Cb"/>, <see cref="Cr"/>)
|
|||
/// </summary>
|
|||
public void Convert(int x, int y, ref RowOctet<TPixel> currentRows) |
|||
{ |
|||
YCbCrForwardConverter<TPixel>.LoadAndStretchEdges(currentRows, this.pixelSpan, new Point(x, y), SampleSize, this.samplingAreaSize); |
|||
|
|||
PixelOperations<TPixel>.Instance.ToRgb24(this.config, this.pixelSpan, this.rgbSpan); |
|||
|
|||
ref Block8x8F yBlock = ref this.Y; |
|||
ref Block8x8F cbBlock = ref this.Cb; |
|||
ref Block8x8F crBlock = ref this.Cr; |
|||
|
|||
if (RgbToYCbCrConverterVectorized.IsSupported) |
|||
{ |
|||
RgbToYCbCrConverterVectorized.Convert444(this.rgbSpan, ref yBlock, ref cbBlock, ref crBlock); |
|||
} |
|||
else |
|||
{ |
|||
this.colorTables.Convert444(this.rgbSpan, ref yBlock, ref cbBlock, ref crBlock); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,61 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|||
{ |
|||
internal static class YCbCrForwardConverter<TPixel> |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
public static void LoadAndStretchEdges(RowOctet<TPixel> source, Span<TPixel> dest, Point start, Size sampleSize, Size totalSize) |
|||
{ |
|||
DebugGuard.MustBeBetweenOrEqualTo(start.X, 0, totalSize.Width - 1, nameof(start.X)); |
|||
DebugGuard.MustBeBetweenOrEqualTo(start.Y, 0, totalSize.Height - 1, nameof(start.Y)); |
|||
|
|||
int width = Math.Min(sampleSize.Width, totalSize.Width - start.X); |
|||
int height = Math.Min(sampleSize.Height, totalSize.Height - start.Y); |
|||
|
|||
uint byteWidth = (uint)(width * Unsafe.SizeOf<TPixel>()); |
|||
int remainderXCount = sampleSize.Width - width; |
|||
|
|||
ref byte blockStart = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<TPixel, byte>(dest)); |
|||
int rowSizeInBytes = sampleSize.Width * Unsafe.SizeOf<TPixel>(); |
|||
|
|||
for (int y = 0; y < height; y++) |
|||
{ |
|||
Span<TPixel> row = source[y]; |
|||
|
|||
ref byte s = ref Unsafe.As<TPixel, byte>(ref row[start.X]); |
|||
ref byte d = ref Unsafe.Add(ref blockStart, y * rowSizeInBytes); |
|||
|
|||
Unsafe.CopyBlock(ref d, ref s, byteWidth); |
|||
|
|||
ref TPixel last = ref Unsafe.Add(ref Unsafe.As<byte, TPixel>(ref d), width - 1); |
|||
|
|||
for (int x = 1; x <= remainderXCount; x++) |
|||
{ |
|||
Unsafe.Add(ref last, x) = last; |
|||
} |
|||
} |
|||
|
|||
int remainderYCount = sampleSize.Height - height; |
|||
|
|||
if (remainderYCount == 0) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
ref byte lastRowStart = ref Unsafe.Add(ref blockStart, (height - 1) * rowSizeInBytes); |
|||
|
|||
for (int y = 1; y <= remainderYCount; y++) |
|||
{ |
|||
ref byte remStart = ref Unsafe.Add(ref lastRowStart, rowSizeInBytes * y); |
|||
Unsafe.CopyBlock(ref remStart, ref lastRowStart, (uint)rowSizeInBytes); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,15 +1,13 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder |
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
|||
{ |
|||
/// <summary>
|
|||
/// Identifies the colorspace of a Jpeg image.
|
|||
/// </summary>
|
|||
internal enum JpegColorSpace |
|||
{ |
|||
Undefined = 0, |
|||
|
|||
/// <summary>
|
|||
/// Color space with 1 component.
|
|||
/// </summary>
|
|||
@ -0,0 +1,196 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using SixLabors.ImageSharp.Formats.Jpeg.Components; |
|||
using SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg |
|||
{ |
|||
/// <summary>
|
|||
/// Image encoder for writing an image to a stream as a jpeg.
|
|||
/// </summary>
|
|||
internal sealed unsafe partial class JpegEncoderCore |
|||
{ |
|||
private static JpegFrameConfig[] CreateFrameConfigs() |
|||
{ |
|||
var defaultLuminanceHuffmanDC = new JpegHuffmanTableConfig(@class: 0, destIndex: 0, HuffmanSpec.LuminanceDC); |
|||
var defaultLuminanceHuffmanAC = new JpegHuffmanTableConfig(@class: 1, destIndex: 0, HuffmanSpec.LuminanceAC); |
|||
var defaultChrominanceHuffmanDC = new JpegHuffmanTableConfig(@class: 0, destIndex: 1, HuffmanSpec.ChrominanceDC); |
|||
var defaultChrominanceHuffmanAC = new JpegHuffmanTableConfig(@class: 1, destIndex: 1, HuffmanSpec.ChrominanceAC); |
|||
|
|||
var defaultLuminanceQuantTable = new JpegQuantizationTableConfig(0, Quantization.LuminanceTable); |
|||
var defaultChrominanceQuantTable = new JpegQuantizationTableConfig(1, Quantization.ChrominanceTable); |
|||
|
|||
var yCbCrHuffmanConfigs = new JpegHuffmanTableConfig[] |
|||
{ |
|||
defaultLuminanceHuffmanDC, |
|||
defaultLuminanceHuffmanAC, |
|||
defaultChrominanceHuffmanDC, |
|||
defaultChrominanceHuffmanAC, |
|||
}; |
|||
|
|||
var yCbCrQuantTableConfigs = new JpegQuantizationTableConfig[] |
|||
{ |
|||
defaultLuminanceQuantTable, |
|||
defaultChrominanceQuantTable, |
|||
}; |
|||
|
|||
return new JpegFrameConfig[] |
|||
{ |
|||
// YCbCr 4:4:4
|
|||
new JpegFrameConfig( |
|||
JpegColorSpace.YCbCr, |
|||
JpegEncodingColor.YCbCrRatio444, |
|||
new JpegComponentConfig[] |
|||
{ |
|||
new JpegComponentConfig(id: 1, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
|||
new JpegComponentConfig(id: 2, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), |
|||
new JpegComponentConfig(id: 3, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), |
|||
}, |
|||
yCbCrHuffmanConfigs, |
|||
yCbCrQuantTableConfigs), |
|||
|
|||
// YCbCr 4:2:2
|
|||
new JpegFrameConfig( |
|||
JpegColorSpace.YCbCr, |
|||
JpegEncodingColor.YCbCrRatio422, |
|||
new JpegComponentConfig[] |
|||
{ |
|||
new JpegComponentConfig(id: 1, hsf: 2, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
|||
new JpegComponentConfig(id: 2, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), |
|||
new JpegComponentConfig(id: 3, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), |
|||
}, |
|||
yCbCrHuffmanConfigs, |
|||
yCbCrQuantTableConfigs), |
|||
|
|||
// YCbCr 4:2:0
|
|||
new JpegFrameConfig( |
|||
JpegColorSpace.YCbCr, |
|||
JpegEncodingColor.YCbCrRatio420, |
|||
new JpegComponentConfig[] |
|||
{ |
|||
new JpegComponentConfig(id: 1, hsf: 2, vsf: 2, quantIndex: 0, dcIndex: 0, acIndex: 0), |
|||
new JpegComponentConfig(id: 2, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), |
|||
new JpegComponentConfig(id: 3, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), |
|||
}, |
|||
yCbCrHuffmanConfigs, |
|||
yCbCrQuantTableConfigs), |
|||
|
|||
// YCbCr 4:1:1
|
|||
new JpegFrameConfig( |
|||
JpegColorSpace.YCbCr, |
|||
JpegEncodingColor.YCbCrRatio411, |
|||
new JpegComponentConfig[] |
|||
{ |
|||
new JpegComponentConfig(id: 1, hsf: 4, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
|||
new JpegComponentConfig(id: 2, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), |
|||
new JpegComponentConfig(id: 3, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), |
|||
}, |
|||
yCbCrHuffmanConfigs, |
|||
yCbCrQuantTableConfigs), |
|||
|
|||
// YCbCr 4:1:0
|
|||
new JpegFrameConfig( |
|||
JpegColorSpace.YCbCr, |
|||
JpegEncodingColor.YCbCrRatio410, |
|||
new JpegComponentConfig[] |
|||
{ |
|||
new JpegComponentConfig(id: 1, hsf: 4, vsf: 2, quantIndex: 0, dcIndex: 0, acIndex: 0), |
|||
new JpegComponentConfig(id: 2, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), |
|||
new JpegComponentConfig(id: 3, hsf: 1, vsf: 1, quantIndex: 1, dcIndex: 1, acIndex: 1), |
|||
}, |
|||
yCbCrHuffmanConfigs, |
|||
yCbCrQuantTableConfigs), |
|||
|
|||
// Luminance
|
|||
new JpegFrameConfig( |
|||
JpegColorSpace.Grayscale, |
|||
JpegEncodingColor.Luminance, |
|||
new JpegComponentConfig[] |
|||
{ |
|||
new JpegComponentConfig(id: 0, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
|||
}, |
|||
new JpegHuffmanTableConfig[] |
|||
{ |
|||
defaultLuminanceHuffmanDC, |
|||
defaultLuminanceHuffmanAC |
|||
}, |
|||
new JpegQuantizationTableConfig[] |
|||
{ |
|||
defaultLuminanceQuantTable |
|||
}), |
|||
|
|||
// Rgb
|
|||
new JpegFrameConfig( |
|||
JpegColorSpace.RGB, |
|||
JpegEncodingColor.Rgb, |
|||
new JpegComponentConfig[] |
|||
{ |
|||
new JpegComponentConfig(id: 82, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
|||
new JpegComponentConfig(id: 71, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
|||
new JpegComponentConfig(id: 66, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
|||
}, |
|||
new JpegHuffmanTableConfig[] |
|||
{ |
|||
defaultLuminanceHuffmanDC, |
|||
defaultLuminanceHuffmanAC |
|||
}, |
|||
new JpegQuantizationTableConfig[] |
|||
{ |
|||
defaultLuminanceQuantTable |
|||
}) |
|||
{ |
|||
AdobeColorTransformMarkerFlag = JpegConstants.Adobe.ColorTransformUnknown |
|||
}, |
|||
|
|||
// Cmyk
|
|||
new JpegFrameConfig( |
|||
JpegColorSpace.Cmyk, |
|||
JpegEncodingColor.Cmyk, |
|||
new JpegComponentConfig[] |
|||
{ |
|||
new JpegComponentConfig(id: 1, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
|||
new JpegComponentConfig(id: 2, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
|||
new JpegComponentConfig(id: 3, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
|||
new JpegComponentConfig(id: 4, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
|||
}, |
|||
new JpegHuffmanTableConfig[] |
|||
{ |
|||
defaultLuminanceHuffmanDC, |
|||
defaultLuminanceHuffmanAC |
|||
}, |
|||
new JpegQuantizationTableConfig[] |
|||
{ |
|||
defaultLuminanceQuantTable |
|||
}) |
|||
{ |
|||
AdobeColorTransformMarkerFlag = JpegConstants.Adobe.ColorTransformUnknown, |
|||
}, |
|||
|
|||
// YccK
|
|||
new JpegFrameConfig( |
|||
JpegColorSpace.Ycck, |
|||
JpegEncodingColor.Ycck, |
|||
new JpegComponentConfig[] |
|||
{ |
|||
new JpegComponentConfig(id: 1, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
|||
new JpegComponentConfig(id: 2, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
|||
new JpegComponentConfig(id: 3, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
|||
new JpegComponentConfig(id: 4, hsf: 1, vsf: 1, quantIndex: 0, dcIndex: 0, acIndex: 0), |
|||
}, |
|||
new JpegHuffmanTableConfig[] |
|||
{ |
|||
defaultLuminanceHuffmanDC, |
|||
defaultLuminanceHuffmanAC |
|||
}, |
|||
new JpegQuantizationTableConfig[] |
|||
{ |
|||
defaultLuminanceQuantTable |
|||
}) |
|||
{ |
|||
AdobeColorTransformMarkerFlag = JpegConstants.Adobe.ColorTransformYcck, |
|||
}, |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using SixLabors.ImageSharp.Formats.Jpeg.Components; |
|||
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors |
|||
{ |
|||
/// <summary>
|
|||
/// Spectral converter for YCbCr TIFF's which use the JPEG compression.
|
|||
/// The jpeg data should be always treated as RGB color space.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
|
|||
internal sealed class RgbJpegSpectralConverter<TPixel> : SpectralConverter<TPixel> |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="RgbJpegSpectralConverter{TPixel}"/> class.
|
|||
/// This Spectral converter will always convert the pixel data to RGB color.
|
|||
/// </summary>
|
|||
/// <param name="configuration">The configuration.</param>
|
|||
public RgbJpegSpectralConverter(Configuration configuration) |
|||
: base(configuration) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override JpegColorConverterBase GetColorConverter(JpegFrame frame, IRawJpegData jpegData) => JpegColorConverterBase.GetConverter(JpegColorSpace.RGB, frame.Precision); |
|||
} |
|||
} |
|||
@ -1,56 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System; |
|||
using BenchmarkDotNet.Attributes; |
|||
using SixLabors.ImageSharp.Formats.Jpeg.Components; |
|||
using SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Benchmarks.Format.Jpeg.Components.Encoder |
|||
{ |
|||
public class YCbCrForwardConverterBenchmark |
|||
{ |
|||
private RgbToYCbCrConverterLut converter; |
|||
private Rgb24[] data; |
|||
|
|||
[GlobalSetup] |
|||
public void Setup() |
|||
{ |
|||
this.converter = RgbToYCbCrConverterLut.Create(); |
|||
|
|||
var r = new Random(42); |
|||
this.data = new Rgb24[64]; |
|||
|
|||
var d = new byte[3]; |
|||
for (int i = 0; i < this.data.Length; i++) |
|||
{ |
|||
r.NextBytes(d); |
|||
this.data[i] = new Rgb24(d[0], d[1], d[2]); |
|||
} |
|||
} |
|||
|
|||
[Benchmark(Baseline = true)] |
|||
public void ConvertLut() |
|||
{ |
|||
Block8x8F y = default; |
|||
Block8x8F cb = default; |
|||
Block8x8F cr = default; |
|||
|
|||
this.converter.Convert444(this.data.AsSpan(), ref y, ref cb, ref cr); |
|||
} |
|||
|
|||
[Benchmark] |
|||
public void ConvertVectorized() |
|||
{ |
|||
Block8x8F y = default; |
|||
Block8x8F cb = default; |
|||
Block8x8F cr = default; |
|||
|
|||
if (RgbToYCbCrConverterVectorized.IsSupported) |
|||
{ |
|||
RgbToYCbCrConverterVectorized.Convert444(this.data.AsSpan(), ref y, ref cb, ref cr); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,240 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Benchmarks |
|||
{ |
|||
public partial class RgbToYCbCr |
|||
{ |
|||
/// <summary>
|
|||
/// Scaled integer RGBA to YCbCr lookup tables
|
|||
/// </summary>
|
|||
private static class LookupTables |
|||
{ |
|||
public static readonly int[] Y0 = |
|||
{ |
|||
0, 306, 612, 918, 1224, 1530, 1836, 2142, 2448, 2754, 3060, 3366, 3672, 3978, 4284, |
|||
4590, 4896, 5202, 5508, 5814, 6120, 6426, 6732, 7038, 7344, 7650, 7956, 8262, 8568, |
|||
8874, 9180, 9486, 9792, 10098, 10404, 10710, 11016, 11322, 11628, 11934, 12240, |
|||
12546, 12852, 13158, 13464, 13770, 14076, 14382, 14688, 14994, 15300, 15606, 15912, |
|||
16218, 16524, 16830, 17136, 17442, 17748, 18054, 18360, 18666, 18972, 19278, 19584, |
|||
19890, 20196, 20502, 20808, 21114, 21420, 21726, 22032, 22338, 22644, 22950, 23256, |
|||
23562, 23868, 24174, 24480, 24786, 25092, 25398, 25704, 26010, 26316, 26622, 26928, |
|||
27234, 27540, 27846, 28152, 28458, 28764, 29070, 29376, 29682, 29988, 30294, 30600, |
|||
30906, 31212, 31518, 31824, 32130, 32436, 32742, 33048, 33354, 33660, 33966, 34272, |
|||
34578, 34884, 35190, 35496, 35802, 36108, 36414, 36720, 37026, 37332, 37638, 37944, |
|||
38250, 38556, 38862, 39168, 39474, 39780, 40086, 40392, 40698, 41004, 41310, 41616, |
|||
41922, 42228, 42534, 42840, 43146, 43452, 43758, 44064, 44370, 44676, 44982, 45288, |
|||
45594, 45900, 46206, 46512, 46818, 47124, 47430, 47736, 48042, 48348, 48654, 48960, |
|||
49266, 49572, 49878, 50184, 50490, 50796, 51102, 51408, 51714, 52020, 52326, 52632, |
|||
52938, 53244, 53550, 53856, 54162, 54468, 54774, 55080, 55386, 55692, 55998, 56304, |
|||
56610, 56916, 57222, 57528, 57834, 58140, 58446, 58752, 59058, 59364, 59670, 59976, |
|||
60282, 60588, 60894, 61200, 61506, 61812, 62118, 62424, 62730, 63036, 63342, 63648, |
|||
63954, 64260, 64566, 64872, 65178, 65484, 65790, 66096, 66402, 66708, 67014, 67320, |
|||
67626, 67932, 68238, 68544, 68850, 69156, 69462, 69768, 70074, 70380, 70686, 70992, |
|||
71298, 71604, 71910, 72216, 72522, 72828, 73134, 73440, 73746, 74052, 74358, 74664, |
|||
74970, 75276, 75582, 75888, 76194, 76500, 76806, 77112, 77418, 77724, 78030 |
|||
}; |
|||
|
|||
public static readonly int[] Y1 = |
|||
{ |
|||
0, 601, 1202, 1803, 2404, 3005, 3606, 4207, 4808, 5409, 6010, 6611, 7212, 7813, 8414, |
|||
9015, 9616, 10217, 10818, 11419, 12020, 12621, 13222, 13823, 14424, 15025, 15626, |
|||
16227, 16828, 17429, 18030, 18631, 19232, 19833, 20434, 21035, 21636, 22237, 22838, |
|||
23439, 24040, 24641, 25242, 25843, 26444, 27045, 27646, 28247, 28848, 29449, 30050, |
|||
30651, 31252, 31853, 32454, 33055, 33656, 34257, 34858, 35459, 36060, 36661, 37262, |
|||
37863, 38464, 39065, 39666, 40267, 40868, 41469, 42070, 42671, 43272, 43873, 44474, |
|||
45075, 45676, 46277, 46878, 47479, 48080, 48681, 49282, 49883, 50484, 51085, 51686, |
|||
52287, 52888, 53489, 54090, 54691, 55292, 55893, 56494, 57095, 57696, 58297, 58898, |
|||
59499, 60100, 60701, 61302, 61903, 62504, 63105, 63706, 64307, 64908, 65509, 66110, |
|||
66711, 67312, 67913, 68514, 69115, 69716, 70317, 70918, 71519, 72120, 72721, 73322, |
|||
73923, 74524, 75125, 75726, 76327, 76928, 77529, 78130, 78731, 79332, 79933, 80534, |
|||
81135, 81736, 82337, 82938, 83539, 84140, 84741, 85342, 85943, 86544, 87145, 87746, |
|||
88347, 88948, 89549, 90150, 90751, 91352, 91953, 92554, 93155, 93756, 94357, 94958, |
|||
95559, 96160, 96761, 97362, 97963, 98564, 99165, 99766, 100367, 100968, 101569, |
|||
102170, 102771, 103372, 103973, 104574, 105175, 105776, 106377, 106978, 107579, |
|||
108180, 108781, 109382, 109983, 110584, 111185, 111786, 112387, 112988, 113589, |
|||
114190, 114791, 115392, 115993, 116594, 117195, 117796, 118397, 118998, 119599, |
|||
120200, 120801, 121402, 122003, 122604, 123205, 123806, 124407, 125008, 125609, |
|||
126210, 126811, 127412, 128013, 128614, 129215, 129816, 130417, 131018, 131619, |
|||
132220, 132821, 133422, 134023, 134624, 135225, 135826, 136427, 137028, 137629, |
|||
138230, 138831, 139432, 140033, 140634, 141235, 141836, 142437, 143038, 143639, |
|||
144240, 144841, 145442, 146043, 146644, 147245, 147846, 148447, 149048, 149649, |
|||
150250, 150851, 151452, 152053, 152654, 153255 |
|||
}; |
|||
|
|||
public static readonly int[] Y2 = |
|||
{ |
|||
0, 117, 234, 351, 468, 585, 702, 819, 936, 1053, 1170, 1287, 1404, 1521, 1638, 1755, |
|||
1872, 1989, 2106, 2223, 2340, 2457, 2574, 2691, 2808, 2925, 3042, 3159, 3276, 3393, |
|||
3510, 3627, 3744, 3861, 3978, 4095, 4212, 4329, 4446, 4563, 4680, 4797, 4914, 5031, |
|||
5148, 5265, 5382, 5499, 5616, 5733, 5850, 5967, 6084, 6201, 6318, 6435, 6552, 6669, |
|||
6786, 6903, 7020, 7137, 7254, 7371, 7488, 7605, 7722, 7839, 7956, 8073, 8190, 8307, |
|||
8424, 8541, 8658, 8775, 8892, 9009, 9126, 9243, 9360, 9477, 9594, 9711, 9828, 9945, |
|||
10062, 10179, 10296, 10413, 10530, 10647, 10764, 10881, 10998, 11115, 11232, 11349, |
|||
11466, 11583, 11700, 11817, 11934, 12051, 12168, 12285, 12402, 12519, 12636, 12753, |
|||
12870, 12987, 13104, 13221, 13338, 13455, 13572, 13689, 13806, 13923, 14040, 14157, |
|||
14274, 14391, 14508, 14625, 14742, 14859, 14976, 15093, 15210, 15327, 15444, 15561, |
|||
15678, 15795, 15912, 16029, 16146, 16263, 16380, 16497, 16614, 16731, 16848, 16965, |
|||
17082, 17199, 17316, 17433, 17550, 17667, 17784, 17901, 18018, 18135, 18252, 18369, |
|||
18486, 18603, 18720, 18837, 18954, 19071, 19188, 19305, 19422, 19539, 19656, 19773, |
|||
19890, 20007, 20124, 20241, 20358, 20475, 20592, 20709, 20826, 20943, 21060, 21177, |
|||
21294, 21411, 21528, 21645, 21762, 21879, 21996, 22113, 22230, 22347, 22464, 22581, |
|||
22698, 22815, 22932, 23049, 23166, 23283, 23400, 23517, 23634, 23751, 23868, 23985, |
|||
24102, 24219, 24336, 24453, 24570, 24687, 24804, 24921, 25038, 25155, 25272, 25389, |
|||
25506, 25623, 25740, 25857, 25974, 26091, 26208, 26325, 26442, 26559, 26676, 26793, |
|||
26910, 27027, 27144, 27261, 27378, 27495, 27612, 27729, 27846, 27963, 28080, 28197, |
|||
28314, 28431, 28548, 28665, 28782, 28899, 29016, 29133, 29250, 29367, 29484, 29601, |
|||
29718, 29835 |
|||
}; |
|||
|
|||
public static readonly int[] Cb0 = |
|||
{ |
|||
0, -172, -344, -516, -688, -860, -1032, -1204, -1376, -1548, -1720, -1892, |
|||
-2064, -2236, -2408, -2580, -2752, -2924, -3096, -3268, -3440, -3612, |
|||
-3784, -3956, -4128, -4300, -4472, -4644, -4816, -4988, -5160, -5332, |
|||
-5504, -5676, -5848, -6020, -6192, -6364, -6536, -6708, -6880, -7052, |
|||
-7224, -7396, -7568, -7740, -7912, -8084, -8256, -8428, -8600, -8772, |
|||
-8944, -9116, -9288, -9460, -9632, -9804, -9976, -10148, -10320, -10492, |
|||
-10664, -10836, -11008, -11180, -11352, -11524, -11696, -11868, -12040, |
|||
-12212, -12384, -12556, -12728, -12900, -13072, -13244, -13416, -13588, |
|||
-13760, -13932, -14104, -14276, -14448, -14620, -14792, -14964, -15136, |
|||
-15308, -15480, -15652, -15824, -15996, -16168, -16340, -16512, -16684, |
|||
-16856, -17028, -17200, -17372, -17544, -17716, -17888, -18060, -18232, |
|||
-18404, -18576, -18748, -18920, -19092, -19264, -19436, -19608, -19780, |
|||
-19952, -20124, -20296, -20468, -20640, -20812, -20984, -21156, -21328, |
|||
-21500, -21672, -21844, -22016, -22188, -22360, -22532, -22704, -22876, |
|||
-23048, -23220, -23392, -23564, -23736, -23908, -24080, -24252, -24424, |
|||
-24596, -24768, -24940, -25112, -25284, -25456, -25628, -25800, -25972, |
|||
-26144, -26316, -26488, -26660, -26832, -27004, -27176, -27348, -27520, |
|||
-27692, -27864, -28036, -28208, -28380, -28552, -28724, -28896, -29068, |
|||
-29240, -29412, -29584, -29756, -29928, -30100, -30272, -30444, -30616, |
|||
-30788, -30960, -31132, -31304, -31476, -31648, -31820, -31992, -32164, |
|||
-32336, -32508, -32680, -32852, -33024, -33196, -33368, -33540, -33712, |
|||
-33884, -34056, -34228, -34400, -34572, -34744, -34916, -35088, -35260, |
|||
-35432, -35604, -35776, -35948, -36120, -36292, -36464, -36636, -36808, |
|||
-36980, -37152, -37324, -37496, -37668, -37840, -38012, -38184, -38356, |
|||
-38528, -38700, -38872, -39044, -39216, -39388, -39560, -39732, -39904, |
|||
-40076, -40248, -40420, -40592, -40764, -40936, -41108, -41280, -41452, |
|||
-41624, -41796, -41968, -42140, -42312, -42484, -42656, -42828, -43000, |
|||
-43172, -43344, -43516, -43688, -43860 |
|||
}; |
|||
|
|||
public static readonly int[] Cb1 = |
|||
{ |
|||
0, 339, 678, 1017, 1356, 1695, 2034, 2373, 2712, 3051, 3390, 3729, 4068, |
|||
4407, 4746, 5085, 5424, 5763, 6102, 6441, 6780, 7119, 7458, 7797, 8136, |
|||
8475, 8814, 9153, 9492, 9831, 10170, 10509, 10848, 11187, 11526, 11865, |
|||
12204, 12543, 12882, 13221, 13560, 13899, 14238, 14577, 14916, 15255, |
|||
15594, 15933, 16272, 16611, 16950, 17289, 17628, 17967, 18306, 18645, |
|||
18984, 19323, 19662, 20001, 20340, 20679, 21018, 21357, 21696, 22035, |
|||
22374, 22713, 23052, 23391, 23730, 24069, 24408, 24747, 25086, 25425, |
|||
25764, 26103, 26442, 26781, 27120, 27459, 27798, 28137, 28476, 28815, |
|||
29154, 29493, 29832, 30171, 30510, 30849, 31188, 31527, 31866, 32205, |
|||
32544, 32883, 33222, 33561, 33900, 34239, 34578, 34917, 35256, 35595, |
|||
35934, 36273, 36612, 36951, 37290, 37629, 37968, 38307, 38646, 38985, |
|||
39324, 39663, 40002, 40341, 40680, 41019, 41358, 41697, 42036, 42375, |
|||
42714, 43053, 43392, 43731, 44070, 44409, 44748, 45087, 45426, 45765, |
|||
46104, 46443, 46782, 47121, 47460, 47799, 48138, 48477, 48816, 49155, |
|||
49494, 49833, 50172, 50511, 50850, 51189, 51528, 51867, 52206, 52545, |
|||
52884, 53223, 53562, 53901, 54240, 54579, 54918, 55257, 55596, 55935, |
|||
56274, 56613, 56952, 57291, 57630, 57969, 58308, 58647, 58986, 59325, |
|||
59664, 60003, 60342, 60681, 61020, 61359, 61698, 62037, 62376, 62715, |
|||
63054, 63393, 63732, 64071, 64410, 64749, 65088, 65427, 65766, 66105, |
|||
66444, 66783, 67122, 67461, 67800, 68139, 68478, 68817, 69156, 69495, |
|||
69834, 70173, 70512, 70851, 71190, 71529, 71868, 72207, 72546, 72885, |
|||
73224, 73563, 73902, 74241, 74580, 74919, 75258, 75597, 75936, 76275, |
|||
76614, 76953, 77292, 77631, 77970, 78309, 78648, 78987, 79326, 79665, |
|||
80004, 80343, 80682, 81021, 81360, 81699, 82038, 82377, 82716, 83055, |
|||
83394, 83733, 84072, 84411, 84750, 85089, 85428, 85767, 86106, 86445 |
|||
}; |
|||
|
|||
public static readonly int[] Cb2Cr0 = |
|||
{ |
|||
0, 512, 1024, 1536, 2048, 2560, 3072, 3584, 4096, 4608, 5120, 5632, 6144, |
|||
6656, 7168, 7680, 8192, 8704, 9216, 9728, 10240, 10752, 11264, 11776, |
|||
12288, 12800, 13312, 13824, 14336, 14848, 15360, 15872, 16384, 16896, |
|||
17408, 17920, 18432, 18944, 19456, 19968, 20480, 20992, 21504, 22016, |
|||
22528, 23040, 23552, 24064, 24576, 25088, 25600, 26112, 26624, 27136, |
|||
27648, 28160, 28672, 29184, 29696, 30208, 30720, 31232, 31744, 32256, |
|||
32768, 33280, 33792, 34304, 34816, 35328, 35840, 36352, 36864, 37376, |
|||
37888, 38400, 38912, 39424, 39936, 40448, 40960, 41472, 41984, 42496, |
|||
43008, 43520, 44032, 44544, 45056, 45568, 46080, 46592, 47104, 47616, |
|||
48128, 48640, 49152, 49664, 50176, 50688, 51200, 51712, 52224, 52736, |
|||
53248, 53760, 54272, 54784, 55296, 55808, 56320, 56832, 57344, 57856, |
|||
58368, 58880, 59392, 59904, 60416, 60928, 61440, 61952, 62464, 62976, |
|||
63488, 64000, 64512, 65024, 65536, 66048, 66560, 67072, 67584, 68096, |
|||
68608, 69120, 69632, 70144, 70656, 71168, 71680, 72192, 72704, 73216, |
|||
73728, 74240, 74752, 75264, 75776, 76288, 76800, 77312, 77824, 78336, |
|||
78848, 79360, 79872, 80384, 80896, 81408, 81920, 82432, 82944, 83456, |
|||
83968, 84480, 84992, 85504, 86016, 86528, 87040, 87552, 88064, 88576, |
|||
89088, 89600, 90112, 90624, 91136, 91648, 92160, 92672, 93184, 93696, |
|||
94208, 94720, 95232, 95744, 96256, 96768, 97280, 97792, 98304, 98816, |
|||
99328, 99840, 100352, 100864, 101376, 101888, 102400, 102912, 103424, |
|||
103936, 104448, 104960, 105472, 105984, 106496, 107008, 107520, 108032, |
|||
108544, 109056, 109568, 110080, 110592, 111104, 111616, 112128, 112640, |
|||
113152, 113664, 114176, 114688, 115200, 115712, 116224, 116736, 117248, |
|||
117760, 118272, 118784, 119296, 119808, 120320, 120832, 121344, 121856, |
|||
122368, 122880, 123392, 123904, 124416, 124928, 125440, 125952, 126464, |
|||
126976, 127488, 128000, 128512, 129024, 129536, 130048, 130560 |
|||
}; |
|||
|
|||
public static readonly int[] Cr1 = |
|||
{ |
|||
0, 429, 858, 1287, 1716, 2145, 2574, 3003, 3432, 3861, 4290, 4719, 5148, |
|||
5577, 6006, 6435, 6864, 7293, 7722, 8151, 8580, 9009, 9438, 9867, 10296, |
|||
10725, 11154, 11583, 12012, 12441, 12870, 13299, 13728, 14157, 14586, |
|||
15015, 15444, 15873, 16302, 16731, 17160, 17589, 18018, 18447, 18876, |
|||
19305, 19734, 20163, 20592, 21021, 21450, 21879, 22308, 22737, 23166, |
|||
23595, 24024, 24453, 24882, 25311, 25740, 26169, 26598, 27027, 27456, |
|||
27885, 28314, 28743, 29172, 29601, 30030, 30459, 30888, 31317, 31746, |
|||
32175, 32604, 33033, 33462, 33891, 34320, 34749, 35178, 35607, 36036, |
|||
36465, 36894, 37323, 37752, 38181, 38610, 39039, 39468, 39897, 40326, |
|||
40755, 41184, 41613, 42042, 42471, 42900, 43329, 43758, 44187, 44616, |
|||
45045, 45474, 45903, 46332, 46761, 47190, 47619, 48048, 48477, 48906, |
|||
49335, 49764, 50193, 50622, 51051, 51480, 51909, 52338, 52767, 53196, |
|||
53625, 54054, 54483, 54912, 55341, 55770, 56199, 56628, 57057, 57486, |
|||
57915, 58344, 58773, 59202, 59631, 60060, 60489, 60918, 61347, 61776, |
|||
62205, 62634, 63063, 63492, 63921, 64350, 64779, 65208, 65637, 66066, |
|||
66495, 66924, 67353, 67782, 68211, 68640, 69069, 69498, 69927, 70356, |
|||
70785, 71214, 71643, 72072, 72501, 72930, 73359, 73788, 74217, 74646, |
|||
75075, 75504, 75933, 76362, 76791, 77220, 77649, 78078, 78507, 78936, |
|||
79365, 79794, 80223, 80652, 81081, 81510, 81939, 82368, 82797, 83226, |
|||
83655, 84084, 84513, 84942, 85371, 85800, 86229, 86658, 87087, 87516, |
|||
87945, 88374, 88803, 89232, 89661, 90090, 90519, 90948, 91377, 91806, |
|||
92235, 92664, 93093, 93522, 93951, 94380, 94809, 95238, 95667, 96096, |
|||
96525, 96954, 97383, 97812, 98241, 98670, 99099, 99528, 99957, 100386, |
|||
100815, 101244, 101673, 102102, 102531, 102960, 103389, 103818, 104247, |
|||
104676, 105105, 105534, 105963, 106392, 106821, 107250, 107679, 108108, |
|||
108537, 108966, 109395 |
|||
}; |
|||
|
|||
public static readonly int[] Cr2 = |
|||
{ |
|||
0, 83, 166, 249, 332, 415, 498, 581, 664, 747, 830, 913, 996, 1079, 1162, |
|||
1245, 1328, 1411, 1494, 1577, 1660, 1743, 1826, 1909, 1992, 2075, 2158, |
|||
2241, 2324, 2407, 2490, 2573, 2656, 2739, 2822, 2905, 2988, 3071, 3154, |
|||
3237, 3320, 3403, 3486, 3569, 3652, 3735, 3818, 3901, 3984, 4067, 4150, |
|||
4233, 4316, 4399, 4482, 4565, 4648, 4731, 4814, 4897, 4980, 5063, 5146, |
|||
5229, 5312, 5395, 5478, 5561, 5644, 5727, 5810, 5893, 5976, 6059, 6142, |
|||
6225, 6308, 6391, 6474, 6557, 6640, 6723, 6806, 6889, 6972, 7055, 7138, |
|||
7221, 7304, 7387, 7470, 7553, 7636, 7719, 7802, 7885, 7968, 8051, 8134, |
|||
8217, 8300, 8383, 8466, 8549, 8632, 8715, 8798, 8881, 8964, 9047, 9130, |
|||
9213, 9296, 9379, 9462, 9545, 9628, 9711, 9794, 9877, 9960, 10043, 10126, |
|||
10209, 10292, 10375, 10458, 10541, 10624, 10707, 10790, 10873, 10956, |
|||
11039, 11122, 11205, 11288, 11371, 11454, 11537, 11620, 11703, 11786, |
|||
11869, 11952, 12035, 12118, 12201, 12284, 12367, 12450, 12533, 12616, |
|||
12699, 12782, 12865, 12948, 13031, 13114, 13197, 13280, 13363, 13446, |
|||
13529, 13612, 13695, 13778, 13861, 13944, 14027, 14110, 14193, 14276, |
|||
14359, 14442, 14525, 14608, 14691, 14774, 14857, 14940, 15023, 15106, |
|||
15189, 15272, 15355, 15438, 15521, 15604, 15687, 15770, 15853, 15936, |
|||
16019, 16102, 16185, 16268, 16351, 16434, 16517, 16600, 16683, 16766, |
|||
16849, 16932, 17015, 17098, 17181, 17264, 17347, 17430, 17513, 17596, |
|||
17679, 17762, 17845, 17928, 18011, 18094, 18177, 18260, 18343, 18426, |
|||
18509, 18592, 18675, 18758, 18841, 18924, 19007, 19090, 19173, 19256, |
|||
19339, 19422, 19505, 19588, 19671, 19754, 19837, 19920, 20003, 20086, |
|||
20169, 20252, 20335, 20418, 20501, 20584, 20667, 20750, 20833, 20916, |
|||
20999, 21082, 21165 |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
@ -1,359 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using BenchmarkDotNet.Attributes; |
|||
|
|||
using SixLabors.ImageSharp.Formats.Jpeg.Components; |
|||
|
|||
namespace SixLabors.ImageSharp.Benchmarks |
|||
{ |
|||
public partial class RgbToYCbCr |
|||
{ |
|||
private const int InputColorCount = 64; |
|||
|
|||
private const int InputByteCount = InputColorCount * 3; |
|||
|
|||
private static readonly Vector3 VectorY = new Vector3(0.299F, 0.587F, 0.114F); |
|||
|
|||
private static readonly Vector3 VectorCb = new Vector3(-0.168736F, 0.331264F, 0.5F); |
|||
|
|||
private static readonly Vector3 VectorCr = new Vector3(0.5F, 0.418688F, 0.081312F); |
|||
|
|||
private static class ScaledCoeffs |
|||
{ |
|||
public static readonly int[] Y = |
|||
{ |
|||
306, 601, 117, 0, |
|||
306, 601, 117, 0, |
|||
}; |
|||
|
|||
public static readonly int[] Cb = |
|||
{ |
|||
-172, 339, 512, 0, |
|||
-172, 339, 512, 0, |
|||
}; |
|||
|
|||
public static readonly int[] Cr = |
|||
{ |
|||
512, 429, 83, 0, |
|||
512, 429, 83, 0, |
|||
}; |
|||
|
|||
public static class SelectLeft |
|||
{ |
|||
public static readonly int[] Y = |
|||
{ |
|||
1, 1, 1, 0, |
|||
0, 0, 0, 0, |
|||
}; |
|||
|
|||
public static readonly int[] Cb = |
|||
{ |
|||
1, -1, 1, 0, |
|||
0, 0, 0, 0, |
|||
}; |
|||
|
|||
public static readonly int[] Cr = |
|||
{ |
|||
1, -1, -1, 0, |
|||
0, 0, 0, 0, |
|||
}; |
|||
} |
|||
|
|||
public static class SelectRight |
|||
{ |
|||
public static readonly int[] Y = |
|||
{ |
|||
0, 0, 0, 0, |
|||
1, 1, 1, 0, |
|||
}; |
|||
|
|||
public static readonly int[] Cb = |
|||
{ |
|||
0, 0, 0, 0, |
|||
1, -1, 1, 0, |
|||
}; |
|||
|
|||
public static readonly int[] Cr = |
|||
{ |
|||
0, 0, 0, 0, |
|||
1, -1, -1, 0, |
|||
}; |
|||
} |
|||
} |
|||
|
|||
private static class OnStackInputCache |
|||
{ |
|||
public unsafe struct Byte |
|||
{ |
|||
public fixed byte Data[InputByteCount * 3]; |
|||
|
|||
public static Byte Create(byte[] data) |
|||
{ |
|||
Byte result = default; |
|||
for (int i = 0; i < data.Length; i++) |
|||
{ |
|||
result.Data[i] = data[i]; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public struct Result |
|||
{ |
|||
internal Block8x8F Y; |
|||
internal Block8x8F Cb; |
|||
internal Block8x8F Cr; |
|||
} |
|||
|
|||
// The operation is defined as "RGBA -> YCbCr Transform a stream of bytes into a stream of floats"
|
|||
// We need to benchmark the whole operation, to get true results, not missing any side effects!
|
|||
private byte[] inputSourceRGB; |
|||
|
|||
private int[] inputSourceRGBAsInteger; |
|||
|
|||
[GlobalSetup] |
|||
public void Setup() |
|||
{ |
|||
// Console.WriteLine("Vector<int>.Count: " + Vector<int>.Count);
|
|||
this.inputSourceRGB = new byte[InputByteCount]; |
|||
for (int i = 0; i < this.inputSourceRGB.Length; i++) |
|||
{ |
|||
this.inputSourceRGB[i] = (byte)(42 + i); |
|||
} |
|||
|
|||
this.inputSourceRGBAsInteger = new int[InputByteCount + Vector<int>.Count]; // Filling this should be part of the measured operation
|
|||
} |
|||
|
|||
[Benchmark(Baseline = true, Description = "Floating Point Conversion")] |
|||
public unsafe void RgbaToYcbCrScalarFloat() |
|||
{ |
|||
// Copy the input to the stack:
|
|||
var input = OnStackInputCache.Byte.Create(this.inputSourceRGB); |
|||
|
|||
// On-stack output:
|
|||
Result result = default; |
|||
var yPtr = (float*)&result.Y; |
|||
var cbPtr = (float*)&result.Cb; |
|||
var crPtr = (float*)&result.Cr; |
|||
|
|||
for (int i = 0; i < InputColorCount; i++) |
|||
{ |
|||
int i3 = i * 3; |
|||
float r = input.Data[i3 + 0]; |
|||
float g = input.Data[i3 + 1]; |
|||
float b = input.Data[i3 + 2]; |
|||
|
|||
*yPtr++ = (0.299F * r) + (0.587F * g) + (0.114F * b); |
|||
*cbPtr++ = 128 + ((-0.168736F * r) - (0.331264F * g) + (0.5F * b)); |
|||
*crPtr++ = 128 + ((0.5F * r) - (0.418688F * g) - (0.081312F * b)); |
|||
} |
|||
} |
|||
|
|||
[Benchmark(Description = "Simd Floating Point Conversion")] |
|||
public unsafe void RgbaToYcbCrSimdFloat() |
|||
{ |
|||
// Copy the input to the stack:
|
|||
var input = OnStackInputCache.Byte.Create(this.inputSourceRGB); |
|||
|
|||
// On-stack output:
|
|||
Result result = default; |
|||
var yPtr = (float*)&result.Y; |
|||
var cbPtr = (float*)&result.Cb; |
|||
var crPtr = (float*)&result.Cr; |
|||
|
|||
for (int i = 0; i < InputColorCount; i++) |
|||
{ |
|||
int i3 = i * 3; |
|||
|
|||
var vectorRgb = new Vector3( |
|||
input.Data[i3 + 0], |
|||
input.Data[i3 + 1], |
|||
input.Data[i3 + 2]); |
|||
|
|||
Vector3 vectorY = VectorY * vectorRgb; |
|||
Vector3 vectorCb = VectorCb * vectorRgb; |
|||
Vector3 vectorCr = VectorCr * vectorRgb; |
|||
|
|||
*yPtr++ = vectorY.X + vectorY.Y + vectorY.Z; |
|||
*cbPtr++ = 128 + (vectorCb.X - vectorCb.Y + vectorCb.Z); |
|||
*crPtr++ = 128 + (vectorCr.X - vectorCr.Y - vectorCr.Z); |
|||
} |
|||
} |
|||
|
|||
[Benchmark(Description = "Scaled Integer Conversion + Vector<int>")] |
|||
public unsafe void RgbaToYcbCrScaledIntegerSimd() |
|||
{ |
|||
// Copy the input to the stack:
|
|||
|
|||
// On-stack output:
|
|||
Result result = default; |
|||
var yPtr = (float*)&result.Y; |
|||
var cbPtr = (float*)&result.Cb; |
|||
var crPtr = (float*)&result.Cr; |
|||
|
|||
var yCoeffs = new Vector<int>(ScaledCoeffs.Y); |
|||
var cbCoeffs = new Vector<int>(ScaledCoeffs.Cb); |
|||
var crCoeffs = new Vector<int>(ScaledCoeffs.Cr); |
|||
|
|||
for (int i = 0; i < this.inputSourceRGB.Length; i++) |
|||
{ |
|||
this.inputSourceRGBAsInteger[i] = this.inputSourceRGB[i]; |
|||
} |
|||
|
|||
for (int i = 0; i < InputColorCount; i += 2) |
|||
{ |
|||
var rgb = new Vector<int>(this.inputSourceRGBAsInteger, i * 3); |
|||
|
|||
Vector<int> y = yCoeffs * rgb; |
|||
Vector<int> cb = cbCoeffs * rgb; |
|||
Vector<int> cr = crCoeffs * rgb; |
|||
|
|||
*yPtr++ = (y[0] + y[1] + y[2]) >> 10; |
|||
*cbPtr++ = 128 + ((cb[0] - cb[1] + cb[2]) >> 10); |
|||
*crPtr++ = 128 + ((cr[0] - cr[1] - cr[2]) >> 10); |
|||
|
|||
*yPtr++ = (y[4] + y[5] + y[6]) >> 10; |
|||
*cbPtr++ = 128 + ((cb[4] - cb[5] + cb[6]) >> 10); |
|||
*crPtr++ = 128 + ((cr[4] - cr[5] - cr[6]) >> 10); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// This should perform better. Coreclr emitted Vector.Dot() code lacks the vectorization even with IsHardwareAccelerated == true.
|
|||
/// Kept this benchmark because maybe it will be improved in a future CLR release.
|
|||
/// <see>
|
|||
/// <cref>https://www.gamedev.net/topic/673396-c-systemnumericsvectors-slow/</cref>
|
|||
/// </see>
|
|||
/// </summary>
|
|||
[Benchmark(Description = "Scaled Integer Conversion + Vector<int> + Dot Product")] |
|||
public unsafe void RgbaToYcbCrScaledIntegerSimdWithDotProduct() |
|||
{ |
|||
// Copy the input to the stack:
|
|||
|
|||
// On-stack output:
|
|||
Result result = default; |
|||
float* yPtr = (float*)&result.Y; |
|||
float* cbPtr = (float*)&result.Cb; |
|||
float* crPtr = (float*)&result.Cr; |
|||
|
|||
var yCoeffs = new Vector<int>(ScaledCoeffs.Y); |
|||
var cbCoeffs = new Vector<int>(ScaledCoeffs.Cb); |
|||
var crCoeffs = new Vector<int>(ScaledCoeffs.Cr); |
|||
|
|||
var leftY = new Vector<int>(ScaledCoeffs.SelectLeft.Y); |
|||
var leftCb = new Vector<int>(ScaledCoeffs.SelectLeft.Cb); |
|||
var leftCr = new Vector<int>(ScaledCoeffs.SelectLeft.Cr); |
|||
|
|||
var rightY = new Vector<int>(ScaledCoeffs.SelectRight.Y); |
|||
var rightCb = new Vector<int>(ScaledCoeffs.SelectRight.Cb); |
|||
var rightCr = new Vector<int>(ScaledCoeffs.SelectRight.Cr); |
|||
|
|||
for (int i = 0; i < this.inputSourceRGB.Length; i++) |
|||
{ |
|||
this.inputSourceRGBAsInteger[i] = this.inputSourceRGB[i]; |
|||
} |
|||
|
|||
for (int i = 0; i < InputColorCount; i += 2) |
|||
{ |
|||
var rgb = new Vector<int>(this.inputSourceRGBAsInteger, i * 3); |
|||
|
|||
Vector<int> y = yCoeffs * rgb; |
|||
Vector<int> cb = cbCoeffs * rgb; |
|||
Vector<int> cr = crCoeffs * rgb; |
|||
|
|||
VectorizedConvertImpl(ref yPtr, ref cbPtr, ref crPtr, y, cb, cr, leftY, leftCb, leftCr); |
|||
VectorizedConvertImpl(ref yPtr, ref cbPtr, ref crPtr, y, cb, cr, rightY, rightCb, rightCr); |
|||
} |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static unsafe void VectorizedConvertImpl( |
|||
ref float* yPtr, |
|||
ref float* cbPtr, |
|||
ref float* crPtr, |
|||
Vector<int> y, |
|||
Vector<int> cb, |
|||
Vector<int> cr, |
|||
Vector<int> yAgg, |
|||
Vector<int> cbAgg, |
|||
Vector<int> crAgg) |
|||
{ |
|||
int ySum = Vector.Dot(y, yAgg); |
|||
int cbSum = Vector.Dot(cb, cbAgg); |
|||
int crSum = Vector.Dot(cr, crAgg); |
|||
*yPtr++ = ySum >> 10; |
|||
*cbPtr++ = 128 + (cbSum >> 10); |
|||
*crPtr++ = 128 + (crSum >> 10); |
|||
} |
|||
|
|||
[Benchmark(Description = "Scaled Integer Conversion")] |
|||
public unsafe void RgbaToYcbCrScaledInteger() |
|||
{ |
|||
// Copy the input to the stack:
|
|||
var input = OnStackInputCache.Byte.Create(this.inputSourceRGB); |
|||
|
|||
// On-stack output:
|
|||
Result result = default; |
|||
float* yPtr = (float*)&result.Y; |
|||
float* cbPtr = (float*)&result.Cb; |
|||
float* crPtr = (float*)&result.Cr; |
|||
|
|||
for (int i = 0; i < InputColorCount; i++) |
|||
{ |
|||
int i3 = i * 3; |
|||
int r = input.Data[i3 + 0]; |
|||
int g = input.Data[i3 + 1]; |
|||
int b = input.Data[i3 + 2]; |
|||
|
|||
// Scale by 1024, add .5F and truncate value
|
|||
int y0 = 306 * r; // (0.299F * 1024) + .5F
|
|||
int y1 = 601 * g; // (0.587F * 1024) + .5F
|
|||
int y2 = 117 * b; // (0.114F * 1024) + .5F
|
|||
|
|||
int cb0 = -172 * r; // (-0.168736F * 1024) + .5F
|
|||
int cb1 = 339 * g; // (0.331264F * 1024) + .5F
|
|||
int cb2 = 512 * b; // (0.5F * 1024) + .5F
|
|||
|
|||
int cr0 = 512 * r; // (0.5F * 1024) + .5F
|
|||
int cr1 = 429 * g; // (0.418688F * 1024) + .5F
|
|||
int cr2 = 83 * b; // (0.081312F * 1024) + .5F
|
|||
|
|||
*yPtr++ = (y0 + y1 + y2) >> 10; |
|||
*cbPtr++ = 128 + ((cb0 - cb1 + cb2) >> 10); |
|||
*crPtr++ = 128 + ((cr0 - cr1 - cr2) >> 10); |
|||
} |
|||
} |
|||
|
|||
[Benchmark(Description = "Scaled Integer LUT Conversion")] |
|||
public unsafe void RgbaToYcbCrScaledIntegerLut() |
|||
{ |
|||
// Copy the input to the stack:
|
|||
var input = OnStackInputCache.Byte.Create(this.inputSourceRGB); |
|||
|
|||
// On-stack output:
|
|||
Result result = default; |
|||
float* yPtr = (float*)&result.Y; |
|||
float* cbPtr = (float*)&result.Cb; |
|||
float* crPtr = (float*)&result.Cr; |
|||
|
|||
for (int i = 0; i < InputColorCount; i++) |
|||
{ |
|||
int i3 = i * 3; |
|||
|
|||
int r = input.Data[i3 + 0]; |
|||
int g = input.Data[i3 + 1]; |
|||
int b = input.Data[i3 + 2]; |
|||
|
|||
// TODO: Maybe concatenating all the arrays in LookupTables to a flat one can improve this!
|
|||
*yPtr++ = (LookupTables.Y0[r] + LookupTables.Y1[g] + LookupTables.Y2[b]) >> 10; |
|||
*cbPtr++ = 128 + ((LookupTables.Cb0[r] - LookupTables.Cb1[g] + LookupTables.Cb2Cr0[b]) >> 10); |
|||
*crPtr++ = 128 + ((LookupTables.Cb2Cr0[r] - LookupTables.Cr1[g] - LookupTables.Cr2[b]) >> 10); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,97 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
// Uncomment this to turn unit tests into benchmarks:
|
|||
// #define BENCHMARKING
|
|||
using SixLabors.ImageSharp.Formats.Jpeg.Components; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; |
|||
|
|||
using Xunit; |
|||
using Xunit.Abstractions; |
|||
|
|||
// ReSharper disable InconsistentNaming
|
|||
namespace SixLabors.ImageSharp.Tests.Formats.Jpg |
|||
{ |
|||
[Trait("Format", "Jpg")] |
|||
public partial class Block8x8FTests |
|||
{ |
|||
public class CopyToBufferArea : JpegFixture |
|||
{ |
|||
public CopyToBufferArea(ITestOutputHelper output) |
|||
: base(output) |
|||
{ |
|||
} |
|||
|
|||
private static void VerifyAllZeroOutsideSubArea(Buffer2D<float> buffer, int subX, int subY, int horizontalFactor = 1, int verticalFactor = 1) |
|||
{ |
|||
for (int y = 0; y < 20; y++) |
|||
{ |
|||
for (int x = 0; x < 20; x++) |
|||
{ |
|||
if (x < subX || x >= subX + (8 * horizontalFactor) || y < subY || y >= subY + (8 * verticalFactor)) |
|||
{ |
|||
Assert.Equal(0, buffer[x, y]); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public void Copy1x1Scale() |
|||
{ |
|||
Block8x8F block = CreateRandomFloatBlock(0, 100); |
|||
|
|||
using (Buffer2D<float> buffer = Configuration.Default.MemoryAllocator.Allocate2D<float>(20, 20, AllocationOptions.Clean)) |
|||
{ |
|||
Buffer2DRegion<float> region = buffer.GetRegion(5, 10, 8, 8); |
|||
block.Copy1x1Scale(ref region.GetReferenceToOrigin(), region.Stride); |
|||
|
|||
Assert.Equal(block[0, 0], buffer[5, 10]); |
|||
Assert.Equal(block[1, 0], buffer[6, 10]); |
|||
Assert.Equal(block[0, 1], buffer[5, 11]); |
|||
Assert.Equal(block[0, 7], buffer[5, 17]); |
|||
Assert.Equal(block[63], buffer[12, 17]); |
|||
|
|||
VerifyAllZeroOutsideSubArea(buffer, 5, 10); |
|||
} |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(1, 1)] |
|||
[InlineData(1, 2)] |
|||
[InlineData(2, 1)] |
|||
[InlineData(2, 2)] |
|||
[InlineData(4, 2)] |
|||
[InlineData(4, 4)] |
|||
public void CopyTo(int horizontalFactor, int verticalFactor) |
|||
{ |
|||
Block8x8F block = CreateRandomFloatBlock(0, 100); |
|||
|
|||
var start = new Point(50, 50); |
|||
|
|||
using (Buffer2D<float> buffer = Configuration.Default.MemoryAllocator.Allocate2D<float>(100, 100, AllocationOptions.Clean)) |
|||
{ |
|||
Buffer2DRegion<float> region = buffer.GetRegion(start.X, start.Y, 8 * horizontalFactor, 8 * verticalFactor); |
|||
block.ScaledCopyTo(region, horizontalFactor, verticalFactor); |
|||
|
|||
for (int y = 0; y < 8 * verticalFactor; y++) |
|||
{ |
|||
for (int x = 0; x < 8 * horizontalFactor; x++) |
|||
{ |
|||
int yy = y / verticalFactor; |
|||
int xx = x / horizontalFactor; |
|||
|
|||
float expected = block[xx, yy]; |
|||
float actual = region[x, y]; |
|||
|
|||
Assert.Equal(expected, actual); |
|||
} |
|||
} |
|||
|
|||
VerifyAllZeroOutsideSubArea(buffer, start.X, start.Y, horizontalFactor, verticalFactor); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue