📷 A modern, cross-platform, 2D Graphics library for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

301 lines
8.9 KiB

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Text;
namespace SixLabors.ImageSharp.Formats.Jpeg.Common
{
/// <summary>
/// Represents a Jpeg block with <see cref="short"/> coefficiens.
/// </summary>
// ReSharper disable once InconsistentNaming
internal unsafe struct Block8x8 : IEquatable<Block8x8>
{
/// <summary>
/// A number of scalar coefficients in a <see cref="Block8x8F"/>
/// </summary>
public const int Size = 64;
/// <summary>
/// A fixed size buffer holding the values.
/// See: <see>
/// <cref>https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/unsafe-code-pointers/fixed-size-buffers</cref>
/// </see>
/// </summary>
private fixed short data[Size];
/// <summary>
/// Initializes a new instance of the <see cref="Block8x8"/> struct.
/// </summary>
/// <param name="coefficients">A <see cref="Span{T}"/> of coefficients</param>
public Block8x8(Span<short> coefficients)
{
ref byte selfRef = ref Unsafe.As<Block8x8, byte>(ref this);
ref byte sourceRef = ref coefficients.NonPortableCast<short, byte>().DangerousGetPinnableReference();
Unsafe.CopyBlock(ref selfRef, ref sourceRef, Size * sizeof(short));
}
/// <summary>
/// Gets or sets a <see cref="short"/> value at the given index
/// </summary>
/// <param name="idx">The index</param>
/// <returns>The value</returns>
public short this[int idx]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
GuardBlockIndex(idx);
ref short selfRef = ref Unsafe.As<Block8x8, short>(ref this);
return Unsafe.Add(ref selfRef, idx);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
GuardBlockIndex(idx);
ref short selfRef = ref Unsafe.As<Block8x8, short>(ref this);
Unsafe.Add(ref selfRef, idx) = value;
}
}
/// <summary>
/// Gets or sets a value in a row+coulumn of the 8x8 block
/// </summary>
/// <param name="x">The x position index in the row</param>
/// <param name="y">The column index</param>
/// <returns>The value</returns>
public short this[int x, int y]
{
get => this[(y * 8) + x];
set => this[(y * 8) + x] = value;
}
public static bool operator ==(Block8x8 left, Block8x8 right)
{
return left.Equals(right);
}
public static bool operator !=(Block8x8 left, Block8x8 right)
{
return !left.Equals(right);
}
/// <summary>
/// Multiply all elements by a given <see cref="int"/>
/// </summary>
public static Block8x8 operator *(Block8x8 block, int value)
{
Block8x8 result = block;
for (int i = 0; i < Size; i++)
{
int val = result[i];
val *= value;
result[i] = (short)val;
}
return result;
}
/// <summary>
/// Divide all elements by a given <see cref="int"/>
/// </summary>
public static Block8x8 operator /(Block8x8 block, int value)
{
Block8x8 result = block;
for (int i = 0; i < Size; i++)
{
int val = result[i];
val /= value;
result[i] = (short)val;
}
return result;
}
/// <summary>
/// Add an <see cref="int"/> to all elements
/// </summary>
public static Block8x8 operator +(Block8x8 block, int value)
{
Block8x8 result = block;
for (int i = 0; i < Size; i++)
{
int val = result[i];
val += value;
result[i] = (short)val;
}
return result;
}
/// <summary>
/// Subtract an <see cref="int"/> from all elements
/// </summary>
public static Block8x8 operator -(Block8x8 block, int value)
{
Block8x8 result = block;
for (int i = 0; i < Size; i++)
{
int val = result[i];
val -= value;
result[i] = (short)val;
}
return result;
}
/// <summary>
/// Pointer-based "Indexer" (getter part)
/// </summary>
/// <param name="blockPtr">Block pointer</param>
/// <param name="idx">Index</param>
/// <returns>The scaleVec value at the specified index</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static short GetScalarAt(Block8x8* blockPtr, int idx)
{
GuardBlockIndex(idx);
short* fp = blockPtr->data;
return fp[idx];
}
/// <summary>
/// Pointer-based "Indexer" (setter part)
/// </summary>
/// <param name="blockPtr">Block pointer</param>
/// <param name="idx">Index</param>
/// <param name="value">Value</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SetScalarAt(Block8x8* blockPtr, int idx, short value)
{
GuardBlockIndex(idx);
short* fp = blockPtr->data;
fp[idx] = value;
}
/// <summary>
/// Convert to <see cref="Block8x8F"/>
/// </summary>
public Block8x8F AsFloatBlock()
{
var result = default(Block8x8F);
result.LoadFrom(ref this);
return result;
}
/// <summary>
/// Copy all elements to an array of <see cref="short"/>.
/// </summary>
public short[] ToArray()
{
short[] result = new short[Size];
this.CopyTo(result);
return result;
}
/// <summary>
/// Copy elements into 'destination' Span of <see cref="short"/> values
/// </summary>
public void CopyTo(Span<short> destination)
{
ref byte selfRef = ref Unsafe.As<Block8x8, byte>(ref this);
ref byte destRef = ref destination.NonPortableCast<short, byte>().DangerousGetPinnableReference();
Unsafe.CopyBlock(ref destRef, ref selfRef, Size * sizeof(short));
}
/// <summary>
/// Copy elements into 'destination' Span of <see cref="int"/> values
/// </summary>
public void CopyTo(Span<int> destination)
{
for (int i = 0; i < Size; i++)
{
destination[i] = this[i];
}
}
/// <summary>
/// Cast and copy <see cref="Size"/> <see cref="int"/>-s from the beginning of 'source' span.
/// </summary>
public void LoadFrom(Span<int> source)
{
for (int i = 0; i < Size; i++)
{
this[i] = (short)source[i];
}
}
[Conditional("DEBUG")]
private static void GuardBlockIndex(int idx)
{
DebugGuard.MustBeLessThan(idx, Size, nameof(idx));
DebugGuard.MustBeGreaterThanOrEqualTo(idx, 0, nameof(idx));
}
/// <inheritdoc />
public override string ToString()
{
var bld = new StringBuilder();
bld.Append('[');
for (int i = 0; i < Size; i++)
{
bld.Append(this[i]);
if (i < Size - 1)
{
bld.Append(',');
}
}
bld.Append(']');
return bld.ToString();
}
/// <inheritdoc />
public bool Equals(Block8x8 other)
{
for (int i = 0; i < Size; i++)
{
if (this[i] != other[i])
{
return false;
}
}
return true;
}
/// <inheritdoc />
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
return obj is Block8x8 && this.Equals((Block8x8)obj);
}
/// <inheritdoc />
public override int GetHashCode()
{
return (this[0] * 31) + this[1];
}
/// <summary>
/// Calculate the total sum of absoulute differences of elements in 'a' and 'b'.
/// </summary>
public static long TotalDifference(ref Block8x8 a, ref Block8x8 b)
{
long result = 0;
for (int i = 0; i < Size; i++)
{
int d = a[i] - b[i];
result += Math.Abs(d);
}
return result;
}
}
}