mirror of https://github.com/SixLabors/ImageSharp
committed by
GitHub
26 changed files with 937 additions and 590 deletions
@ -0,0 +1,98 @@ |
|||
using System.Runtime.InteropServices; |
|||
|
|||
namespace SixLabors.ImageSharp.Common.Tuples |
|||
{ |
|||
/// <summary>
|
|||
/// Contains value type tuples of 8 elements.
|
|||
/// TODO: We should T4 this stuff to be DRY
|
|||
/// </summary>
|
|||
internal static class Tuple8 |
|||
{ |
|||
/// <summary>
|
|||
/// Value type tuple of 8 <see cref="uint"/>-s
|
|||
/// </summary>
|
|||
[StructLayout(LayoutKind.Explicit, Size = 8 * sizeof(uint))] |
|||
public struct OfUInt32 |
|||
{ |
|||
[FieldOffset(0 * sizeof(uint))] |
|||
public uint V0; |
|||
|
|||
[FieldOffset(1 * sizeof(uint))] |
|||
public uint V1; |
|||
|
|||
[FieldOffset(2 * sizeof(uint))] |
|||
public uint V2; |
|||
|
|||
[FieldOffset(3 * sizeof(uint))] |
|||
public uint V3; |
|||
|
|||
[FieldOffset(4 * sizeof(uint))] |
|||
public uint V4; |
|||
|
|||
[FieldOffset(5 * sizeof(uint))] |
|||
public uint V5; |
|||
|
|||
[FieldOffset(6 * sizeof(uint))] |
|||
public uint V6; |
|||
|
|||
[FieldOffset(7 * sizeof(uint))] |
|||
public uint V7; |
|||
|
|||
public override string ToString() |
|||
{ |
|||
return $"[{this.V0},{this.V1},{this.V2},{this.V3},{this.V4},{this.V5},{this.V6},{this.V7}]"; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Value type tuple of 8 <see cref="byte"/>-s
|
|||
/// </summary>
|
|||
[StructLayout(LayoutKind.Explicit, Size = 8)] |
|||
public struct OfByte |
|||
{ |
|||
[FieldOffset(0)] |
|||
public byte V0; |
|||
|
|||
[FieldOffset(1)] |
|||
public byte V1; |
|||
|
|||
[FieldOffset(2)] |
|||
public byte V2; |
|||
|
|||
[FieldOffset(3)] |
|||
public byte V3; |
|||
|
|||
[FieldOffset(4)] |
|||
public byte V4; |
|||
|
|||
[FieldOffset(5)] |
|||
public byte V5; |
|||
|
|||
[FieldOffset(6)] |
|||
public byte V6; |
|||
|
|||
[FieldOffset(7)] |
|||
public byte V7; |
|||
|
|||
public override string ToString() |
|||
{ |
|||
return $"[{this.V0},{this.V1},{this.V2},{this.V3},{this.V4},{this.V5},{this.V6},{this.V7}]"; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sets the values of this tuple by casting all elements of the given <see cref="OfUInt32"/> tuple to <see cref="byte"/>.
|
|||
/// </summary>
|
|||
public void LoadFrom(ref OfUInt32 i) |
|||
{ |
|||
this.V0 = (byte)i.V0; |
|||
this.V1 = (byte)i.V1; |
|||
this.V2 = (byte)i.V2; |
|||
this.V3 = (byte)i.V3; |
|||
this.V4 = (byte)i.V4; |
|||
this.V5 = (byte)i.V5; |
|||
this.V6 = (byte)i.V6; |
|||
this.V7 = (byte)i.V7; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,75 @@ |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
namespace SixLabors.ImageSharp.Common.Tuples |
|||
{ |
|||
/// <summary>
|
|||
/// Its faster to process multiple Vector4-s together, so let's pair them!
|
|||
/// On AVX2 this pair should be convertible to <see cref="Vector{T}"/> of <see cref="float"/>!
|
|||
/// </summary>
|
|||
[StructLayout(LayoutKind.Sequential)] |
|||
internal struct Vector4Pair |
|||
{ |
|||
public Vector4 A; |
|||
|
|||
public Vector4 B; |
|||
|
|||
private static readonly Vector4 Scale = new Vector4(1 / 255f); |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public void MultiplyInplace(float value) |
|||
{ |
|||
this.A *= value; |
|||
this.B *= value; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public void AddInplace(Vector4 value) |
|||
{ |
|||
this.A += value; |
|||
this.B += value; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public void AddInplace(ref Vector4Pair other) |
|||
{ |
|||
this.A += other.A; |
|||
this.B += other.B; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Downscale method, specific to Jpeg color conversion. Works only if Vector{float}.Count == 4!
|
|||
/// TODO: Move it somewhere else.
|
|||
/// </summary>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
internal void RoundAndDownscalePreAvx2() |
|||
{ |
|||
ref Vector<float> a = ref Unsafe.As<Vector4, Vector<float>>(ref this.A); |
|||
a = a.FastRound(); |
|||
|
|||
ref Vector<float> b = ref Unsafe.As<Vector4, Vector<float>>(ref this.B); |
|||
b = b.FastRound(); |
|||
|
|||
// Downscale by 1/255
|
|||
this.A *= Scale; |
|||
this.B *= Scale; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// AVX2-only Downscale method, specific to Jpeg color conversion.
|
|||
/// TODO: Move it somewhere else.
|
|||
/// </summary>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
internal void RoundAndDownscaleAvx2() |
|||
{ |
|||
ref Vector<float> self = ref Unsafe.As<Vector4Pair, Vector<float>>(ref this); |
|||
Vector<float> v = self; |
|||
v = v.FastRound(); |
|||
|
|||
// Downscale by 1/255
|
|||
v *= new Vector<float>(1 / 255f); |
|||
self = v; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,146 @@ |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
// ReSharper disable InconsistentNaming
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Common |
|||
{ |
|||
internal partial struct Block8x8F |
|||
{ |
|||
/// <summary>
|
|||
/// Copy block data into the destination color buffer pixel area with the provided horizontal and vertical.
|
|||
/// </summary>
|
|||
public void CopyTo(BufferArea<float> area, int horizontalScale, int verticalScale) |
|||
{ |
|||
if (horizontalScale == 1 && verticalScale == 1) |
|||
{ |
|||
this.CopyTo(area); |
|||
return; |
|||
} |
|||
else if (horizontalScale == 2 && verticalScale == 2) |
|||
{ |
|||
this.CopyTo2x2(area); |
|||
return; |
|||
} |
|||
|
|||
// TODO: Optimize: implement all the cases with loopless special code! (T4?)
|
|||
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++) |
|||
{ |
|||
for (int j = 0; j < horizontalScale; j++) |
|||
{ |
|||
area[xx + j, yy + i] = value; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|||
public void CopyTo(BufferArea<float> area) |
|||
{ |
|||
ref byte selfBase = ref Unsafe.As<Block8x8F, byte>(ref this); |
|||
ref byte destBase = ref Unsafe.As<float, byte>(ref area.GetReferenceToOrigo()); |
|||
int destStride = area.Stride * 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 CopyTo2x2(BufferArea<float> area) |
|||
{ |
|||
ref float destBase = ref area.GetReferenceToOrigo(); |
|||
int destStride = area.Stride; |
|||
|
|||
this.WidenCopyImpl2x2(ref destBase, 0, destStride); |
|||
this.WidenCopyImpl2x2(ref destBase, 1, destStride); |
|||
this.WidenCopyImpl2x2(ref destBase, 2, destStride); |
|||
this.WidenCopyImpl2x2(ref destBase, 3, destStride); |
|||
this.WidenCopyImpl2x2(ref destBase, 4, destStride); |
|||
this.WidenCopyImpl2x2(ref destBase, 5, destStride); |
|||
this.WidenCopyImpl2x2(ref destBase, 6, destStride); |
|||
this.WidenCopyImpl2x2(ref destBase, 7, destStride); |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private void WidenCopyImpl2x2(ref float destBase, int row, int destStride) |
|||
{ |
|||
ref Vector4 selfLeft = ref Unsafe.Add(ref this.V0L, 2 * row); |
|||
ref Vector4 selfRight = ref Unsafe.Add(ref selfLeft, 1); |
|||
ref float destLocalOrigo = ref Unsafe.Add(ref destBase, row * 2 * destStride); |
|||
|
|||
Unsafe.Add(ref destLocalOrigo, 0) = selfLeft.X; |
|||
Unsafe.Add(ref destLocalOrigo, 1) = selfLeft.X; |
|||
Unsafe.Add(ref destLocalOrigo, 2) = selfLeft.Y; |
|||
Unsafe.Add(ref destLocalOrigo, 3) = selfLeft.Y; |
|||
Unsafe.Add(ref destLocalOrigo, 4) = selfLeft.Z; |
|||
Unsafe.Add(ref destLocalOrigo, 5) = selfLeft.Z; |
|||
Unsafe.Add(ref destLocalOrigo, 6) = selfLeft.W; |
|||
Unsafe.Add(ref destLocalOrigo, 7) = selfLeft.W; |
|||
|
|||
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 0) = selfRight.X; |
|||
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 1) = selfRight.X; |
|||
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 2) = selfRight.Y; |
|||
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 3) = selfRight.Y; |
|||
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 4) = selfRight.Z; |
|||
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 5) = selfRight.Z; |
|||
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 6) = selfRight.W; |
|||
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 7) = selfRight.W; |
|||
|
|||
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 0) = selfLeft.X; |
|||
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 1) = selfLeft.X; |
|||
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 2) = selfLeft.Y; |
|||
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 3) = selfLeft.Y; |
|||
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 4) = selfLeft.Z; |
|||
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 5) = selfLeft.Z; |
|||
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 6) = selfLeft.W; |
|||
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 7) = selfLeft.W; |
|||
|
|||
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 0) = selfRight.X; |
|||
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 1) = selfRight.X; |
|||
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 2) = selfRight.Y; |
|||
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 3) = selfRight.Y; |
|||
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 4) = selfRight.Z; |
|||
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 5) = selfRight.Z; |
|||
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 6) = selfRight.W; |
|||
Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 7) = selfRight.W; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static void WidenCopyImpl(ref Vector4 s, ref float destBase) |
|||
{ |
|||
Unsafe.Add(ref destBase, 0) = s.X; |
|||
Unsafe.Add(ref destBase, 1) = s.X; |
|||
Unsafe.Add(ref destBase, 2) = s.Y; |
|||
Unsafe.Add(ref destBase, 3) = s.Y; |
|||
Unsafe.Add(ref destBase, 4) = s.Z; |
|||
Unsafe.Add(ref destBase, 5) = s.Z; |
|||
Unsafe.Add(ref destBase, 6) = s.W; |
|||
Unsafe.Add(ref destBase, 7) = s.W; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue