mirror of
https://github.com/DarthAffe/HPPH.git
synced 2026-11-12 03:49:11 +00:00
Initial commit (WIP)
This commit is contained in:
1 parent
5474273e70
commit
696f3b955f
121 files changed
+5723
-1
No files matched your search
@@ -0,0 +1,22 @@
|
||||
// ReSharper disable UnusedType.Global
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
[ColorGenerator]
|
||||
public readonly partial struct ColorRGB;
|
||||
|
||||
[ColorGenerator]
|
||||
public readonly partial struct ColorBGR;
|
||||
|
||||
[ColorGenerator]
|
||||
public readonly partial struct ColorARGB;
|
||||
|
||||
[ColorGenerator]
|
||||
public readonly partial struct ColorABGR;
|
||||
|
||||
[ColorGenerator]
|
||||
public readonly partial struct ColorRGBA;
|
||||
|
||||
[ColorGenerator]
|
||||
public readonly partial struct ColorBGRA;
|
||||
@@ -0,0 +1,34 @@
|
||||
namespace HPPH;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a generic color made of 4 bytes (alpha, red, green and blue)
|
||||
/// </summary>
|
||||
public interface IColor
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the red-component of this color.
|
||||
/// </summary>
|
||||
byte R { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the green-component of this color.
|
||||
/// </summary>
|
||||
byte G { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the blue-component of this color.
|
||||
/// </summary>
|
||||
byte B { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the alpha-component of this color.
|
||||
/// </summary>
|
||||
byte A { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the color-format of this color.
|
||||
/// </summary>
|
||||
public static virtual IColorFormat ColorFormat => throw new NotSupportedException();
|
||||
|
||||
public static virtual IColor Create(byte r, byte g, byte b, byte a) => throw new NotSupportedException();
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace HPPH;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a color format.
|
||||
/// </summary>
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public partial interface IColorFormat
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the Bytes per pixel for this color-format.
|
||||
/// </summary>
|
||||
int BytesPerPixel { get; }
|
||||
|
||||
string Name { get; }
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
// DarthAffe 05.09.2023: Based on https://github.com/CommunityToolkit/dotnet/blob/b0d6c4f9c0cfb5d860400abb00b0ca1b3e94dfa4/src/CommunityToolkit.HighPerformance/Enumerables/ReadOnlyRefEnumerable%7BT%7D.cs
|
||||
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
/// <summary>
|
||||
/// A <see langword="ref"/> <see langword="struct"/> that iterates readonly items from arbitrary memory locations.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of items to enumerate.</typeparam>
|
||||
public readonly ref struct ReadOnlyRefEnumerable<T>
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ReadOnlySpan{T}"/> instance pointing to the first item in the target memory area.
|
||||
/// </summary>
|
||||
/// <remarks>The <see cref="ReadOnlySpan{T}.Length"/> field maps to the total available length.</remarks>
|
||||
private readonly ReadOnlySpan<T> _span;
|
||||
|
||||
/// <summary>
|
||||
/// The distance between items in the sequence to enumerate.
|
||||
/// </summary>
|
||||
/// <remarks>The distance refers to <typeparamref name="T"/> items, not byte offset.</remarks>
|
||||
private readonly int _step;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total available length for the sequence.
|
||||
/// </summary>
|
||||
public int Length
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _span.Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the element at the specified zero-based index.
|
||||
/// </summary>
|
||||
/// <param name="index">The zero-based index of the element.</param>
|
||||
/// <returns>A reference to the element at the specified index.</returns>
|
||||
/// <exception cref="IndexOutOfRangeException">
|
||||
/// Thrown when <paramref name="index"/> is invalid.
|
||||
/// </exception>
|
||||
public ref readonly T this[int index]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
if ((uint)index >= (uint)Length) throw new IndexOutOfRangeException();
|
||||
|
||||
ref T r0 = ref MemoryMarshal.GetReference(_span);
|
||||
nint offset = (nint)(uint)index * (nint)(uint)_step;
|
||||
ref T ri = ref Unsafe.Add(ref r0, offset);
|
||||
|
||||
return ref ri;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the element at the specified zero-based index.
|
||||
/// </summary>
|
||||
/// <param name="index">The zero-based index of the element.</param>
|
||||
/// <returns>A reference to the element at the specified index.</returns>
|
||||
/// <exception cref="IndexOutOfRangeException">
|
||||
/// Thrown when <paramref name="index"/> is invalid.
|
||||
/// </exception>
|
||||
public ref readonly T this[Index index]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => ref this[index.GetOffset(Length)];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ReadOnlyRefEnumerable{T}"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="reference">A reference to the first item of the sequence.</param>
|
||||
/// <param name="length">The number of items in the sequence.</param>
|
||||
/// <param name="step">The distance between items in the sequence to enumerate.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal ReadOnlyRefEnumerable(in T reference, int length, int step)
|
||||
{
|
||||
this._step = step;
|
||||
|
||||
_span = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in reference), length);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc cref="System.Collections.IEnumerable.GetEnumerator"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Enumerator GetEnumerator() => new(_span, _step);
|
||||
|
||||
public T[] ToArray()
|
||||
{
|
||||
int length = _span.Length;
|
||||
|
||||
// Empty array if no data is mapped
|
||||
if (length == 0)
|
||||
return [];
|
||||
|
||||
T[] array = new T[length];
|
||||
CopyTo(array);
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the contents of this <see cref="ReadOnlyRefEnumerable{T}"/> into a destination <see cref="Span{T}"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="destination">The destination <see cref="Span{T}"/> instance.</param>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when <paramref name="destination"/> is shorter than the source <see cref="ReadOnlyRefEnumerable{T}"/> instance.
|
||||
/// </exception>
|
||||
public void CopyTo(Span<T> destination)
|
||||
{
|
||||
if (_step == 1)
|
||||
{
|
||||
_span.CopyTo(destination);
|
||||
return;
|
||||
}
|
||||
|
||||
ref T sourceRef = ref MemoryMarshal.GetReference(_span);
|
||||
int length = _span.Length;
|
||||
if ((uint)destination.Length < (uint)length)
|
||||
throw new ArgumentException("The target span is too short to copy all the current items to.");
|
||||
|
||||
ref T destinationRef = ref MemoryMarshal.GetReference(destination);
|
||||
|
||||
CopyTo(ref sourceRef, ref destinationRef, (nint)(uint)length, (nint)(uint)_step);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to copy the current <see cref="ReadOnlyRefEnumerable{T}"/> instance to a destination <see cref="Span{T}"/>.
|
||||
/// </summary>
|
||||
/// <param name="destination">The target <see cref="Span{T}"/> of the copy operation.</param>
|
||||
/// <returns>Whether or not the operation was successful.</returns>
|
||||
public bool TryCopyTo(Span<T> destination)
|
||||
{
|
||||
if (destination.Length >= _span.Length)
|
||||
{
|
||||
CopyTo(destination);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void CopyTo(ref T sourceRef, ref T destinationRef, nint length, nint sourceStep)
|
||||
{
|
||||
nint sourceOffset = 0;
|
||||
nint destinationOffset = 0;
|
||||
|
||||
while (length >= 8)
|
||||
{
|
||||
Unsafe.Add(ref destinationRef, destinationOffset + 0) = Unsafe.Add(ref sourceRef, sourceOffset);
|
||||
Unsafe.Add(ref destinationRef, destinationOffset + 1) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
|
||||
Unsafe.Add(ref destinationRef, destinationOffset + 2) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
|
||||
Unsafe.Add(ref destinationRef, destinationOffset + 3) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
|
||||
Unsafe.Add(ref destinationRef, destinationOffset + 4) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
|
||||
Unsafe.Add(ref destinationRef, destinationOffset + 5) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
|
||||
Unsafe.Add(ref destinationRef, destinationOffset + 6) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
|
||||
Unsafe.Add(ref destinationRef, destinationOffset + 7) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
|
||||
|
||||
length -= 8;
|
||||
sourceOffset += sourceStep;
|
||||
destinationOffset += 8;
|
||||
}
|
||||
|
||||
if (length >= 4)
|
||||
{
|
||||
Unsafe.Add(ref destinationRef, destinationOffset + 0) = Unsafe.Add(ref sourceRef, sourceOffset);
|
||||
Unsafe.Add(ref destinationRef, destinationOffset + 1) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
|
||||
Unsafe.Add(ref destinationRef, destinationOffset + 2) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
|
||||
Unsafe.Add(ref destinationRef, destinationOffset + 3) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
|
||||
|
||||
length -= 4;
|
||||
sourceOffset += sourceStep;
|
||||
destinationOffset += 4;
|
||||
}
|
||||
|
||||
while (length > 0)
|
||||
{
|
||||
Unsafe.Add(ref destinationRef, destinationOffset) = Unsafe.Add(ref sourceRef, sourceOffset);
|
||||
|
||||
length -= 1;
|
||||
sourceOffset += sourceStep;
|
||||
destinationOffset += 1;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// A custom enumerator type to traverse items within a <see cref="ReadOnlyRefEnumerable{T}"/> instance.
|
||||
/// </summary>
|
||||
public ref struct Enumerator
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <inheritdoc cref="ReadOnlyRefEnumerable{T}._span"/>
|
||||
private readonly ReadOnlySpan<T> _span;
|
||||
|
||||
/// <inheritdoc cref="ReadOnlyRefEnumerable{T}._step"/>
|
||||
private readonly int _step;
|
||||
|
||||
/// <summary>
|
||||
/// The current position in the sequence.
|
||||
/// </summary>
|
||||
private int _position;
|
||||
|
||||
/// <inheritdoc cref="System.Collections.Generic.IEnumerator{T}.Current"/>
|
||||
public readonly ref readonly T Current
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
ref T r0 = ref MemoryMarshal.GetReference(_span);
|
||||
|
||||
nint offset = (nint)(uint)_position * (nint)(uint)_step;
|
||||
ref T ri = ref Unsafe.Add(ref r0, offset);
|
||||
|
||||
return ref ri;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Enumerator"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="span">The <see cref="ReadOnlySpan{T}"/> instance with the info on the items to traverse.</param>
|
||||
/// <param name="step">The distance between items in the sequence to enumerate.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal Enumerator(ReadOnlySpan<T> span, int step)
|
||||
{
|
||||
this._span = span;
|
||||
this._step = step;
|
||||
|
||||
_position = -1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc cref="System.Collections.IEnumerator.MoveNext"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool MoveNext() => ++_position < _span.Length;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace HPPH;
|
||||
|
||||
internal readonly struct Generic3ByteData(byte b1, byte b2, byte b3)
|
||||
{
|
||||
public readonly byte B1 = b1;
|
||||
public readonly byte B2 = b2;
|
||||
public readonly byte B3 = b3;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace HPPH;
|
||||
|
||||
internal readonly struct Generic3ByteMinMax(byte b1Min, byte b1Max, byte b2Min, byte b2Max, byte b3Min, byte b3Max)
|
||||
{
|
||||
public readonly byte B1Min = b1Min;
|
||||
public readonly byte B1Max = b1Max;
|
||||
|
||||
public readonly byte B2Min = b2Min;
|
||||
public readonly byte B2Max = b2Max;
|
||||
|
||||
public readonly byte B3Min = b3Min;
|
||||
public readonly byte B3Max = b3Max;
|
||||
|
||||
public byte B1Range => (byte)(B1Max - B1Min);
|
||||
public byte B2Range => (byte)(B2Max - B2Min);
|
||||
public byte B3Range => (byte)(B3Max - B3Min);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace HPPH;
|
||||
|
||||
internal readonly struct Generic4ByteData(byte b1, byte b2, byte b3, byte b4)
|
||||
{
|
||||
public readonly byte B1 = b1;
|
||||
public readonly byte B2 = b2;
|
||||
public readonly byte B3 = b3;
|
||||
public readonly byte B4 = b4;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace HPPH;
|
||||
|
||||
internal readonly struct Generic4ByteMinMax(byte b1Min, byte b1Max, byte b2Min, byte b2Max, byte b3Min, byte b3Max, byte b4Min, byte b4Max)
|
||||
{
|
||||
public readonly byte B1Min = b1Min;
|
||||
public readonly byte B1Max = b1Max;
|
||||
|
||||
public readonly byte B2Min = b2Min;
|
||||
public readonly byte B2Max = b2Max;
|
||||
|
||||
public readonly byte B3Min = b3Min;
|
||||
public readonly byte B3Max = b3Max;
|
||||
|
||||
public readonly byte B4Min = b4Min;
|
||||
public readonly byte B4Max = b4Max;
|
||||
|
||||
public byte B1Range => (byte)(B1Max - B1Min);
|
||||
public byte B2Range => (byte)(B2Max - B2Min);
|
||||
public byte B3Range => (byte)(B3Max - B3Min);
|
||||
public byte B4Range => (byte)(B4Max - B4Min);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace HPPH;
|
||||
|
||||
internal readonly struct Generic4LongData(long l1, long l2, long l3, long l4)
|
||||
{
|
||||
public readonly long L1 = l1;
|
||||
public readonly long L2 = l2;
|
||||
public readonly long L3 = l3;
|
||||
public readonly long L4 = l4;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace HPPH;
|
||||
|
||||
public interface IMinMax
|
||||
{
|
||||
public byte RedMin { get; }
|
||||
public byte RedMax { get; }
|
||||
|
||||
public byte GreenMin { get; }
|
||||
public byte GreenMax { get; }
|
||||
|
||||
public byte BlueMin { get; }
|
||||
public byte BlueMax { get; }
|
||||
|
||||
public byte AlphaMin { get; }
|
||||
public byte AlphaMax { get; }
|
||||
|
||||
public byte RedRange { get; }
|
||||
public byte GreenRange { get; }
|
||||
public byte BlueRange { get; }
|
||||
public byte AlphaRange { get; }
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace HPPH;
|
||||
|
||||
public interface ISum
|
||||
{
|
||||
public long R { get; }
|
||||
public long G { get; }
|
||||
public long B { get; }
|
||||
public long A { get; }
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
/// <summary>
|
||||
/// Offers some extensions and helper-methods for the work with floats.
|
||||
/// </summary>
|
||||
internal static class HPPHExtensions
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// Defines the precision RGB.NET processes floating point comparisons in.
|
||||
/// </summary>
|
||||
public const float TOLERANCE = 1E-7f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two values are equal respecting the <see cref="TOLERANCE"/>.
|
||||
/// </summary>
|
||||
/// <param name="value1">The first value to compare.</param>
|
||||
/// <param name="value2">The first value to compare.</param>
|
||||
/// <returns><c>true</c> if the difference is smaller than the <see cref="TOLERANCE"/>; otherwise, <c>false</c>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool EqualsInTolerance(this float value1, float value2) => Math.Abs(value1 - value2) < TOLERANCE;
|
||||
|
||||
/// <summary>
|
||||
/// Clamps the provided value to be bigger or equal min and smaller or equal max.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to clamp.</param>
|
||||
/// <param name="min">The lower value of the range the value is clamped to.</param>
|
||||
/// <param name="max">The higher value of the range the value is clamped to.</param>
|
||||
/// <returns>The clamped value.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T Clamp<T>(this T value, T min, T max)
|
||||
where T : INumber<T>
|
||||
{
|
||||
// ReSharper disable ConvertIfStatementToReturnStatement - I'm not sure why, but inlining this statement reduces performance by ~10%
|
||||
if (value < min) return min;
|
||||
if (value > max) return max;
|
||||
return value;
|
||||
// ReSharper restore ConvertIfStatementToReturnStatement
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enforces the provided value to be in the specified range by wrapping it around the edges if it exceeds them.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to wrap.</param>
|
||||
/// <param name="min">The lower value of the range the value is wrapped into.</param>
|
||||
/// <param name="max">The higher value of the range the value is wrapped into.</param>
|
||||
/// <returns>The wrapped value.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T Wrap<T>(this T value, T min, T max)
|
||||
where T : INumber<T>
|
||||
{
|
||||
T range = max - min;
|
||||
|
||||
while (value >= max)
|
||||
value -= range;
|
||||
|
||||
while (value < min)
|
||||
value += range;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a normalized float value in the range [0..1] to a byte [0..255].
|
||||
/// </summary>
|
||||
/// <param name="percentage">The normalized float value to convert.</param>
|
||||
/// <returns>The byte value.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static byte GetByteValueFromPercentage(this float percentage)
|
||||
{
|
||||
if (float.IsNaN(percentage) || (percentage < 0)) return 0;
|
||||
|
||||
return (byte)(percentage >= 1.0f ? 255 : percentage * 256.0f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a byte value [0..255] to a normalized float value in the range [0..1].
|
||||
/// </summary>
|
||||
/// <param name="value">The byte value to convert.</param>
|
||||
/// <returns>The normalized float value.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float GetPercentageFromByteValue(this byte value)
|
||||
=> value == 255 ? 1.0f : (value / 256.0f);
|
||||
|
||||
#endregion
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
namespace HPPH;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
internal class ColorSortGeneratorAttribute(string dataTypeName, string sortValueName) : Attribute
|
||||
{
|
||||
public string DataTypeName { get; } = dataTypeName;
|
||||
public string SortValueName { get; } = sortValueName;
|
||||
}
|
||||
+393
@@ -0,0 +1,393 @@
|
||||
using System.Buffers;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public static unsafe partial class PixelHelper
|
||||
{
|
||||
public static partial void SortByRed<T>(Span<T> colors) where T : unmanaged, IColor
|
||||
{
|
||||
fixed (T* ptr = colors)
|
||||
{
|
||||
T* end = ptr + colors.Length;
|
||||
|
||||
Span<int> histogram = stackalloc int[256];
|
||||
histogram.Clear();
|
||||
for (T* color = ptr; color < end; color++)
|
||||
histogram[(*color).R]++;
|
||||
|
||||
T[] bucketsArray = ArrayPool<T>.Shared.Rent(colors.Length);
|
||||
try
|
||||
{
|
||||
Span<T> buckets = bucketsArray.AsSpan()[..colors.Length];
|
||||
Span<int> currentBucketIndex = stackalloc int[256];
|
||||
|
||||
int offset = 0;
|
||||
for (int i = 0; i < histogram.Length; i++)
|
||||
{
|
||||
currentBucketIndex[i] = offset;
|
||||
offset += histogram[i];
|
||||
}
|
||||
|
||||
for (T* color = ptr; color < end; color++)
|
||||
buckets[currentBucketIndex[(*color).R]++] = (*color);
|
||||
|
||||
buckets.CopyTo(colors);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<T>.Shared.Return(bucketsArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
public static partial void SortByGreen<T>(Span<T> colors) where T : unmanaged, IColor
|
||||
{
|
||||
fixed (T* ptr = colors)
|
||||
{
|
||||
T* end = ptr + colors.Length;
|
||||
|
||||
Span<int> histogram = stackalloc int[256];
|
||||
histogram.Clear();
|
||||
for (T* color = ptr; color < end; color++)
|
||||
histogram[(*color).G]++;
|
||||
|
||||
T[] bucketsArray = ArrayPool<T>.Shared.Rent(colors.Length);
|
||||
try
|
||||
{
|
||||
Span<T> buckets = bucketsArray.AsSpan()[..colors.Length];
|
||||
Span<int> currentBucketIndex = stackalloc int[256];
|
||||
|
||||
int offset = 0;
|
||||
for (int i = 0; i < histogram.Length; i++)
|
||||
{
|
||||
currentBucketIndex[i] = offset;
|
||||
offset += histogram[i];
|
||||
}
|
||||
|
||||
for (T* color = ptr; color < end; color++)
|
||||
buckets[currentBucketIndex[(*color).G]++] = (*color);
|
||||
|
||||
buckets.CopyTo(colors);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<T>.Shared.Return(bucketsArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
public static partial void SortByBlue<T>(Span<T> colors) where T : unmanaged, IColor
|
||||
{
|
||||
fixed (T* ptr = colors)
|
||||
{
|
||||
T* end = ptr + colors.Length;
|
||||
|
||||
Span<int> histogram = stackalloc int[256];
|
||||
histogram.Clear();
|
||||
for (T* color = ptr; color < end; color++)
|
||||
histogram[(*color).B]++;
|
||||
|
||||
T[] bucketsArray = ArrayPool<T>.Shared.Rent(colors.Length);
|
||||
try
|
||||
{
|
||||
Span<T> buckets = bucketsArray.AsSpan()[..colors.Length];
|
||||
Span<int> currentBucketIndex = stackalloc int[256];
|
||||
|
||||
int offset = 0;
|
||||
for (int i = 0; i < histogram.Length; i++)
|
||||
{
|
||||
currentBucketIndex[i] = offset;
|
||||
offset += histogram[i];
|
||||
}
|
||||
|
||||
for (T* color = ptr; color < end; color++)
|
||||
buckets[currentBucketIndex[(*color).B]++] = (*color);
|
||||
|
||||
buckets.CopyTo(colors);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<T>.Shared.Return(bucketsArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
public static partial void SortByAlpha<T>(Span<T> colors) where T : unmanaged, IColor
|
||||
{
|
||||
fixed (T* ptr = colors)
|
||||
{
|
||||
T* end = ptr + colors.Length;
|
||||
|
||||
Span<int> histogram = stackalloc int[256];
|
||||
histogram.Clear();
|
||||
for (T* color = ptr; color < end; color++)
|
||||
histogram[(*color).A]++;
|
||||
|
||||
T[] bucketsArray = ArrayPool<T>.Shared.Rent(colors.Length);
|
||||
try
|
||||
{
|
||||
Span<T> buckets = bucketsArray.AsSpan()[..colors.Length];
|
||||
Span<int> currentBucketIndex = stackalloc int[256];
|
||||
|
||||
int offset = 0;
|
||||
for (int i = 0; i < histogram.Length; i++)
|
||||
{
|
||||
currentBucketIndex[i] = offset;
|
||||
offset += histogram[i];
|
||||
}
|
||||
|
||||
for (T* color = ptr; color < end; color++)
|
||||
buckets[currentBucketIndex[(*color).A]++] = (*color);
|
||||
|
||||
buckets.CopyTo(colors);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<T>.Shared.Return(bucketsArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
internal static partial void SortB1(Span<Generic3ByteData> colors)
|
||||
{
|
||||
fixed (Generic3ByteData* ptr = colors)
|
||||
{
|
||||
Generic3ByteData* end = ptr + colors.Length;
|
||||
|
||||
Span<int> histogram = stackalloc int[256];
|
||||
histogram.Clear();
|
||||
for (Generic3ByteData* color = ptr; color < end; color++)
|
||||
histogram[(*color).B1]++;
|
||||
|
||||
Generic3ByteData[] bucketsArray = ArrayPool<Generic3ByteData>.Shared.Rent(colors.Length);
|
||||
try
|
||||
{
|
||||
Span<Generic3ByteData> buckets = bucketsArray.AsSpan()[..colors.Length];
|
||||
Span<int> currentBucketIndex = stackalloc int[256];
|
||||
|
||||
int offset = 0;
|
||||
for (int i = 0; i < histogram.Length; i++)
|
||||
{
|
||||
currentBucketIndex[i] = offset;
|
||||
offset += histogram[i];
|
||||
}
|
||||
|
||||
for (Generic3ByteData* color = ptr; color < end; color++)
|
||||
buckets[currentBucketIndex[(*color).B1]++] = (*color);
|
||||
|
||||
buckets.CopyTo(colors);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<Generic3ByteData>.Shared.Return(bucketsArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
internal static partial void SortB2(Span<Generic3ByteData> colors)
|
||||
{
|
||||
fixed (Generic3ByteData* ptr = colors)
|
||||
{
|
||||
Generic3ByteData* end = ptr + colors.Length;
|
||||
|
||||
Span<int> histogram = stackalloc int[256];
|
||||
histogram.Clear();
|
||||
for (Generic3ByteData* color = ptr; color < end; color++)
|
||||
histogram[(*color).B2]++;
|
||||
|
||||
Generic3ByteData[] bucketsArray = ArrayPool<Generic3ByteData>.Shared.Rent(colors.Length);
|
||||
try
|
||||
{
|
||||
Span<Generic3ByteData> buckets = bucketsArray.AsSpan()[..colors.Length];
|
||||
Span<int> currentBucketIndex = stackalloc int[256];
|
||||
|
||||
int offset = 0;
|
||||
for (int i = 0; i < histogram.Length; i++)
|
||||
{
|
||||
currentBucketIndex[i] = offset;
|
||||
offset += histogram[i];
|
||||
}
|
||||
|
||||
for (Generic3ByteData* color = ptr; color < end; color++)
|
||||
buckets[currentBucketIndex[(*color).B2]++] = (*color);
|
||||
|
||||
buckets.CopyTo(colors);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<Generic3ByteData>.Shared.Return(bucketsArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
internal static partial void SortB3(Span<Generic3ByteData> colors)
|
||||
{
|
||||
fixed (Generic3ByteData* ptr = colors)
|
||||
{
|
||||
Generic3ByteData* end = ptr + colors.Length;
|
||||
|
||||
Span<int> histogram = stackalloc int[256];
|
||||
histogram.Clear();
|
||||
for (Generic3ByteData* color = ptr; color < end; color++)
|
||||
histogram[(*color).B3]++;
|
||||
|
||||
Generic3ByteData[] bucketsArray = ArrayPool<Generic3ByteData>.Shared.Rent(colors.Length);
|
||||
try
|
||||
{
|
||||
Span<Generic3ByteData> buckets = bucketsArray.AsSpan()[..colors.Length];
|
||||
Span<int> currentBucketIndex = stackalloc int[256];
|
||||
|
||||
int offset = 0;
|
||||
for (int i = 0; i < histogram.Length; i++)
|
||||
{
|
||||
currentBucketIndex[i] = offset;
|
||||
offset += histogram[i];
|
||||
}
|
||||
|
||||
for (Generic3ByteData* color = ptr; color < end; color++)
|
||||
buckets[currentBucketIndex[(*color).B3]++] = (*color);
|
||||
|
||||
buckets.CopyTo(colors);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<Generic3ByteData>.Shared.Return(bucketsArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
internal static partial void SortB1(Span<Generic4ByteData> colors)
|
||||
{
|
||||
fixed (Generic4ByteData* ptr = colors)
|
||||
{
|
||||
Generic4ByteData* end = ptr + colors.Length;
|
||||
|
||||
Span<int> histogram = stackalloc int[256];
|
||||
histogram.Clear();
|
||||
for (Generic4ByteData* color = ptr; color < end; color++)
|
||||
histogram[(*color).B1]++;
|
||||
|
||||
Generic4ByteData[] bucketsArray = ArrayPool<Generic4ByteData>.Shared.Rent(colors.Length);
|
||||
try
|
||||
{
|
||||
Span<Generic4ByteData> buckets = bucketsArray.AsSpan()[..colors.Length];
|
||||
Span<int> currentBucketIndex = stackalloc int[256];
|
||||
|
||||
int offset = 0;
|
||||
for (int i = 0; i < histogram.Length; i++)
|
||||
{
|
||||
currentBucketIndex[i] = offset;
|
||||
offset += histogram[i];
|
||||
}
|
||||
|
||||
for (Generic4ByteData* color = ptr; color < end; color++)
|
||||
buckets[currentBucketIndex[(*color).B1]++] = (*color);
|
||||
|
||||
buckets.CopyTo(colors);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<Generic4ByteData>.Shared.Return(bucketsArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
internal static partial void SortB2(Span<Generic4ByteData> colors)
|
||||
{
|
||||
fixed (Generic4ByteData* ptr = colors)
|
||||
{
|
||||
Generic4ByteData* end = ptr + colors.Length;
|
||||
|
||||
Span<int> histogram = stackalloc int[256];
|
||||
histogram.Clear();
|
||||
for (Generic4ByteData* color = ptr; color < end; color++)
|
||||
histogram[(*color).B2]++;
|
||||
|
||||
Generic4ByteData[] bucketsArray = ArrayPool<Generic4ByteData>.Shared.Rent(colors.Length);
|
||||
try
|
||||
{
|
||||
Span<Generic4ByteData> buckets = bucketsArray.AsSpan()[..colors.Length];
|
||||
Span<int> currentBucketIndex = stackalloc int[256];
|
||||
|
||||
int offset = 0;
|
||||
for (int i = 0; i < histogram.Length; i++)
|
||||
{
|
||||
currentBucketIndex[i] = offset;
|
||||
offset += histogram[i];
|
||||
}
|
||||
|
||||
for (Generic4ByteData* color = ptr; color < end; color++)
|
||||
buckets[currentBucketIndex[(*color).B2]++] = (*color);
|
||||
|
||||
buckets.CopyTo(colors);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<Generic4ByteData>.Shared.Return(bucketsArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
internal static partial void SortB3(Span<Generic4ByteData> colors)
|
||||
{
|
||||
fixed (Generic4ByteData* ptr = colors)
|
||||
{
|
||||
Generic4ByteData* end = ptr + colors.Length;
|
||||
|
||||
Span<int> histogram = stackalloc int[256];
|
||||
histogram.Clear();
|
||||
for (Generic4ByteData* color = ptr; color < end; color++)
|
||||
histogram[(*color).B3]++;
|
||||
|
||||
Generic4ByteData[] bucketsArray = ArrayPool<Generic4ByteData>.Shared.Rent(colors.Length);
|
||||
try
|
||||
{
|
||||
Span<Generic4ByteData> buckets = bucketsArray.AsSpan()[..colors.Length];
|
||||
Span<int> currentBucketIndex = stackalloc int[256];
|
||||
|
||||
int offset = 0;
|
||||
for (int i = 0; i < histogram.Length; i++)
|
||||
{
|
||||
currentBucketIndex[i] = offset;
|
||||
offset += histogram[i];
|
||||
}
|
||||
|
||||
for (Generic4ByteData* color = ptr; color < end; color++)
|
||||
buckets[currentBucketIndex[(*color).B3]++] = (*color);
|
||||
|
||||
buckets.CopyTo(colors);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<Generic4ByteData>.Shared.Return(bucketsArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
internal static partial void SortB4(Span<Generic4ByteData> colors)
|
||||
{
|
||||
fixed (Generic4ByteData* ptr = colors)
|
||||
{
|
||||
Generic4ByteData* end = ptr + colors.Length;
|
||||
|
||||
Span<int> histogram = stackalloc int[256];
|
||||
histogram.Clear();
|
||||
for (Generic4ByteData* color = ptr; color < end; color++)
|
||||
histogram[(*color).B4]++;
|
||||
|
||||
Generic4ByteData[] bucketsArray = ArrayPool<Generic4ByteData>.Shared.Rent(colors.Length);
|
||||
try
|
||||
{
|
||||
Span<Generic4ByteData> buckets = bucketsArray.AsSpan()[..colors.Length];
|
||||
Span<int> currentBucketIndex = stackalloc int[256];
|
||||
|
||||
int offset = 0;
|
||||
for (int i = 0; i < histogram.Length; i++)
|
||||
{
|
||||
currentBucketIndex[i] = offset;
|
||||
offset += histogram[i];
|
||||
}
|
||||
|
||||
for (Generic4ByteData* color = ptr; color < end; color++)
|
||||
buckets[currentBucketIndex[(*color).B4]++] = (*color);
|
||||
|
||||
buckets.CopyTo(colors);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<Generic4ByteData>.Shared.Return(bucketsArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// ReSharper disable ConvertToAutoProperty
|
||||
// ReSharper disable ConvertToAutoPropertyWhenPossible
|
||||
// ReSharper disable ReplaceWithPrimaryConstructorParameter
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a color in 32 bit ABGR-format.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the <see cref="ColorABGR"/> class.
|
||||
/// </remarks>
|
||||
/// <param name="a">The Alpha-component of the color.</param>
|
||||
/// <param name="b">The Blue-component of the color.</param>
|
||||
/// <param name="g">The Green-component of the color.</param>
|
||||
/// <param name="r">The Red-component of the color.</param>
|
||||
[DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public readonly partial struct ColorABGR(byte a, byte b, byte g, byte r) : IColor
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <inheritdoc />
|
||||
public static IColorFormat ColorFormat => IColorFormat.ABGR;
|
||||
|
||||
private readonly byte _a = a;
|
||||
private readonly byte _b = b;
|
||||
private readonly byte _g = g;
|
||||
private readonly byte _r = r;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte R => _r;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte G => _g;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte B => _b;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte A => _a;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
|
||||
|
||||
/// <inheritdoc />
|
||||
public static IColor Create(byte r, byte g, byte b, byte a) => new ColorABGR(a, b, g, r);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// ReSharper disable ConvertToAutoProperty
|
||||
// ReSharper disable ConvertToAutoPropertyWhenPossible
|
||||
// ReSharper disable ReplaceWithPrimaryConstructorParameter
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a color in 32 bit ARGB-format.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the <see cref="ColorARGB"/> class.
|
||||
/// </remarks>
|
||||
/// <param name="a">The Alpha-component of the color.</param>
|
||||
/// <param name="r">The Red-component of the color.</param>
|
||||
/// <param name="g">The Green-component of the color.</param>
|
||||
/// <param name="b">The Blue-component of the color.</param>
|
||||
[DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public readonly partial struct ColorARGB(byte a, byte r, byte g, byte b) : IColor
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <inheritdoc />
|
||||
public static IColorFormat ColorFormat => IColorFormat.ARGB;
|
||||
|
||||
private readonly byte _a = a;
|
||||
private readonly byte _r = r;
|
||||
private readonly byte _g = g;
|
||||
private readonly byte _b = b;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte R => _r;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte G => _g;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte B => _b;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte A => _a;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
|
||||
|
||||
/// <inheritdoc />
|
||||
public static IColor Create(byte r, byte g, byte b, byte a) => new ColorARGB(a, r, g, b);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// ReSharper disable ConvertToAutoProperty
|
||||
// ReSharper disable ConvertToAutoPropertyWhenPossible
|
||||
// ReSharper disable ReplaceWithPrimaryConstructorParameter
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a color in 24 bit BGR-format.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the <see cref="ColorBGR"/> class.
|
||||
/// </remarks>
|
||||
/// <param name="b">The Blue-component of the color.</param>
|
||||
/// <param name="g">The Green-component of the color.</param>
|
||||
/// <param name="r">The Red-component of the color.</param>
|
||||
[DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public readonly partial struct ColorBGR(byte b, byte g, byte r): IColor
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <inheritdoc />
|
||||
public static IColorFormat ColorFormat => IColorFormat.BGR;
|
||||
|
||||
private readonly byte _b = b;
|
||||
private readonly byte _g = g;
|
||||
private readonly byte _r = r;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte R => _r;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte G => _g;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte B => _b;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte A => byte.MaxValue;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
|
||||
|
||||
/// <inheritdoc />
|
||||
public static IColor Create(byte r, byte g, byte b, byte a) => new ColorBGR(b, g, r);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// ReSharper disable ConvertToAutoProperty
|
||||
// ReSharper disable ConvertToAutoPropertyWhenPossible
|
||||
// ReSharper disable ReplaceWithPrimaryConstructorParameter
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a color in 32 bit BGRA-format.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the <see cref="ColorBGRA"/> class.
|
||||
/// </remarks>
|
||||
/// <param name="b">The Blue-component of the color.</param>
|
||||
/// <param name="g">The Green-component of the color.</param>
|
||||
/// <param name="r">The Red-component of the color.</param>
|
||||
/// <param name="a">The Alpha-component of the color.</param>
|
||||
[DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public readonly partial struct ColorBGRA(byte b, byte g, byte r, byte a) : IColor
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <inheritdoc />
|
||||
public static IColorFormat ColorFormat => IColorFormat.BGRA;
|
||||
|
||||
private readonly byte _b = b;
|
||||
private readonly byte _g = g;
|
||||
private readonly byte _r = r;
|
||||
private readonly byte _a = a;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte R => _r;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte G => _g;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte B => _b;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte A => _a;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
|
||||
|
||||
/// <inheritdoc />
|
||||
public static IColor Create(byte r, byte g, byte b, byte a) => new ColorBGRA(b, g, r, a);
|
||||
|
||||
#endregion
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatABGR
|
||||
{
|
||||
#region Methods
|
||||
|
||||
unsafe IColor IColorFormat.Average(ReadOnlySpan<byte> data) => PixelHelper.Average(MemoryMarshal.Cast<byte, ColorABGR>(data));
|
||||
|
||||
#endregion
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatABGR
|
||||
{
|
||||
#region Methods
|
||||
|
||||
unsafe IMinMax IColorFormat.MinMax(ReadOnlySpan<byte> data) => PixelHelper.MinMax<ColorABGR, MinMaxABGR>(MemoryMarshal.Cast<byte, ColorABGR>(data));
|
||||
|
||||
#endregion
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatABGR
|
||||
{
|
||||
#region Methods
|
||||
|
||||
unsafe IColor[] IColorFormat.CreateColorPalette(ReadOnlySpan<byte> data, int paletteSize)
|
||||
{
|
||||
ColorABGR[] colors = PixelHelper.CreateColorPalette<ColorABGR>(MemoryMarshal.Cast<byte, ColorABGR>(data), paletteSize);
|
||||
|
||||
IColor[] result = new IColor[colors.Length];
|
||||
for(int i = 0; i < colors.Length; i++)
|
||||
result[i] = colors[i];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatABGR
|
||||
{
|
||||
#region Methods
|
||||
|
||||
unsafe ISum IColorFormat.Sum(ReadOnlySpan<byte> data) => PixelHelper.Sum<ColorABGR, SumABGR>(MemoryMarshal.Cast<byte, ColorABGR>(data));
|
||||
|
||||
#endregion
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatABGR : IColorFormat
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
public static ColorFormatABGR Instance { get; } = new();
|
||||
|
||||
public int BytesPerPixel => 4;
|
||||
|
||||
public string Name => "ABGR";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
private ColorFormatABGR() {}
|
||||
|
||||
#endregion
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatARGB
|
||||
{
|
||||
#region Methods
|
||||
|
||||
unsafe IColor IColorFormat.Average(ReadOnlySpan<byte> data) => PixelHelper.Average(MemoryMarshal.Cast<byte, ColorARGB>(data));
|
||||
|
||||
#endregion
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatARGB
|
||||
{
|
||||
#region Methods
|
||||
|
||||
unsafe IMinMax IColorFormat.MinMax(ReadOnlySpan<byte> data) => PixelHelper.MinMax<ColorARGB, MinMaxARGB>(MemoryMarshal.Cast<byte, ColorARGB>(data));
|
||||
|
||||
#endregion
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatARGB
|
||||
{
|
||||
#region Methods
|
||||
|
||||
unsafe IColor[] IColorFormat.CreateColorPalette(ReadOnlySpan<byte> data, int paletteSize)
|
||||
{
|
||||
ColorARGB[] colors = PixelHelper.CreateColorPalette<ColorARGB>(MemoryMarshal.Cast<byte, ColorARGB>(data), paletteSize);
|
||||
|
||||
IColor[] result = new IColor[colors.Length];
|
||||
for(int i = 0; i < colors.Length; i++)
|
||||
result[i] = colors[i];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatARGB
|
||||
{
|
||||
#region Methods
|
||||
|
||||
unsafe ISum IColorFormat.Sum(ReadOnlySpan<byte> data) => PixelHelper.Sum<ColorARGB, SumARGB>(MemoryMarshal.Cast<byte, ColorARGB>(data));
|
||||
|
||||
#endregion
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatARGB : IColorFormat
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
public static ColorFormatARGB Instance { get; } = new();
|
||||
|
||||
public int BytesPerPixel => 4;
|
||||
|
||||
public string Name => "ARGB";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
private ColorFormatARGB() {}
|
||||
|
||||
#endregion
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatBGR
|
||||
{
|
||||
#region Methods
|
||||
|
||||
unsafe IColor IColorFormat.Average(ReadOnlySpan<byte> data) => PixelHelper.Average(MemoryMarshal.Cast<byte, ColorBGR>(data));
|
||||
|
||||
#endregion
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatBGR
|
||||
{
|
||||
#region Methods
|
||||
|
||||
unsafe IMinMax IColorFormat.MinMax(ReadOnlySpan<byte> data) => PixelHelper.MinMax<ColorBGR, MinMaxBGR>(MemoryMarshal.Cast<byte, ColorBGR>(data));
|
||||
|
||||
#endregion
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatBGR
|
||||
{
|
||||
#region Methods
|
||||
|
||||
unsafe IColor[] IColorFormat.CreateColorPalette(ReadOnlySpan<byte> data, int paletteSize)
|
||||
{
|
||||
ColorBGR[] colors = PixelHelper.CreateColorPalette<ColorBGR>(MemoryMarshal.Cast<byte, ColorBGR>(data), paletteSize);
|
||||
|
||||
IColor[] result = new IColor[colors.Length];
|
||||
for(int i = 0; i < colors.Length; i++)
|
||||
result[i] = colors[i];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatBGR
|
||||
{
|
||||
#region Methods
|
||||
|
||||
unsafe ISum IColorFormat.Sum(ReadOnlySpan<byte> data) => PixelHelper.Sum<ColorBGR, SumBGR>(MemoryMarshal.Cast<byte, ColorBGR>(data));
|
||||
|
||||
#endregion
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatBGR : IColorFormat
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
public static ColorFormatBGR Instance { get; } = new();
|
||||
|
||||
public int BytesPerPixel => 3;
|
||||
|
||||
public string Name => "BGR";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
private ColorFormatBGR() {}
|
||||
|
||||
#endregion
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatBGRA
|
||||
{
|
||||
#region Methods
|
||||
|
||||
unsafe IColor IColorFormat.Average(ReadOnlySpan<byte> data) => PixelHelper.Average(MemoryMarshal.Cast<byte, ColorBGRA>(data));
|
||||
|
||||
#endregion
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatBGRA
|
||||
{
|
||||
#region Methods
|
||||
|
||||
unsafe IMinMax IColorFormat.MinMax(ReadOnlySpan<byte> data) => PixelHelper.MinMax<ColorBGRA, MinMaxBGRA>(MemoryMarshal.Cast<byte, ColorBGRA>(data));
|
||||
|
||||
#endregion
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatBGRA
|
||||
{
|
||||
#region Methods
|
||||
|
||||
unsafe IColor[] IColorFormat.CreateColorPalette(ReadOnlySpan<byte> data, int paletteSize)
|
||||
{
|
||||
ColorBGRA[] colors = PixelHelper.CreateColorPalette<ColorBGRA>(MemoryMarshal.Cast<byte, ColorBGRA>(data), paletteSize);
|
||||
|
||||
IColor[] result = new IColor[colors.Length];
|
||||
for(int i = 0; i < colors.Length; i++)
|
||||
result[i] = colors[i];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatBGRA
|
||||
{
|
||||
#region Methods
|
||||
|
||||
unsafe ISum IColorFormat.Sum(ReadOnlySpan<byte> data) => PixelHelper.Sum<ColorBGRA, SumBGRA>(MemoryMarshal.Cast<byte, ColorBGRA>(data));
|
||||
|
||||
#endregion
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatBGRA : IColorFormat
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
public static ColorFormatBGRA Instance { get; } = new();
|
||||
|
||||
public int BytesPerPixel => 4;
|
||||
|
||||
public string Name => "BGRA";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
private ColorFormatBGRA() {}
|
||||
|
||||
#endregion
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatRGB
|
||||
{
|
||||
#region Methods
|
||||
|
||||
unsafe IColor IColorFormat.Average(ReadOnlySpan<byte> data) => PixelHelper.Average(MemoryMarshal.Cast<byte, ColorRGB>(data));
|
||||
|
||||
#endregion
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatRGB
|
||||
{
|
||||
#region Methods
|
||||
|
||||
unsafe IMinMax IColorFormat.MinMax(ReadOnlySpan<byte> data) => PixelHelper.MinMax<ColorRGB, MinMaxRGB>(MemoryMarshal.Cast<byte, ColorRGB>(data));
|
||||
|
||||
#endregion
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatRGB
|
||||
{
|
||||
#region Methods
|
||||
|
||||
unsafe IColor[] IColorFormat.CreateColorPalette(ReadOnlySpan<byte> data, int paletteSize)
|
||||
{
|
||||
ColorRGB[] colors = PixelHelper.CreateColorPalette<ColorRGB>(MemoryMarshal.Cast<byte, ColorRGB>(data), paletteSize);
|
||||
|
||||
IColor[] result = new IColor[colors.Length];
|
||||
for(int i = 0; i < colors.Length; i++)
|
||||
result[i] = colors[i];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatRGB
|
||||
{
|
||||
#region Methods
|
||||
|
||||
unsafe ISum IColorFormat.Sum(ReadOnlySpan<byte> data) => PixelHelper.Sum<ColorRGB, SumRGB>(MemoryMarshal.Cast<byte, ColorRGB>(data));
|
||||
|
||||
#endregion
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatRGB : IColorFormat
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
public static ColorFormatRGB Instance { get; } = new();
|
||||
|
||||
public int BytesPerPixel => 3;
|
||||
|
||||
public string Name => "RGB";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
private ColorFormatRGB() {}
|
||||
|
||||
#endregion
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatRGBA
|
||||
{
|
||||
#region Methods
|
||||
|
||||
unsafe IColor IColorFormat.Average(ReadOnlySpan<byte> data) => PixelHelper.Average(MemoryMarshal.Cast<byte, ColorRGBA>(data));
|
||||
|
||||
#endregion
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatRGBA
|
||||
{
|
||||
#region Methods
|
||||
|
||||
unsafe IMinMax IColorFormat.MinMax(ReadOnlySpan<byte> data) => PixelHelper.MinMax<ColorRGBA, MinMaxRGBA>(MemoryMarshal.Cast<byte, ColorRGBA>(data));
|
||||
|
||||
#endregion
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatRGBA
|
||||
{
|
||||
#region Methods
|
||||
|
||||
unsafe IColor[] IColorFormat.CreateColorPalette(ReadOnlySpan<byte> data, int paletteSize)
|
||||
{
|
||||
ColorRGBA[] colors = PixelHelper.CreateColorPalette<ColorRGBA>(MemoryMarshal.Cast<byte, ColorRGBA>(data), paletteSize);
|
||||
|
||||
IColor[] result = new IColor[colors.Length];
|
||||
for(int i = 0; i < colors.Length; i++)
|
||||
result[i] = colors[i];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatRGBA
|
||||
{
|
||||
#region Methods
|
||||
|
||||
unsafe ISum IColorFormat.Sum(ReadOnlySpan<byte> data) => PixelHelper.Sum<ColorRGBA, SumRGBA>(MemoryMarshal.Cast<byte, ColorRGBA>(data));
|
||||
|
||||
#endregion
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
namespace HPPH;
|
||||
|
||||
public sealed partial class ColorFormatRGBA : IColorFormat
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
public static ColorFormatRGBA Instance { get; } = new();
|
||||
|
||||
public int BytesPerPixel => 4;
|
||||
|
||||
public string Name => "RGBA";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
private ColorFormatRGBA() {}
|
||||
|
||||
#endregion
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
namespace HPPH;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Struct)]
|
||||
internal class ColorGeneratorAttribute : Attribute;
|
||||
@@ -0,0 +1,55 @@
|
||||
// ReSharper disable ConvertToAutoProperty
|
||||
// ReSharper disable ConvertToAutoPropertyWhenPossible
|
||||
// ReSharper disable ReplaceWithPrimaryConstructorParameter
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a color in 24 bit RGB-format.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the <see cref="ColorRGB"/> class.
|
||||
/// </remarks>
|
||||
/// <param name="r">The Red-component of the color.</param>
|
||||
/// <param name="g">The Green-component of the color.</param>
|
||||
/// <param name="b">The Blue-component of the color.</param>
|
||||
[DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public readonly partial struct ColorRGB(byte r, byte g, byte b): IColor
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <inheritdoc />
|
||||
public static IColorFormat ColorFormat => IColorFormat.RGB;
|
||||
|
||||
private readonly byte _r = r;
|
||||
private readonly byte _g = g;
|
||||
private readonly byte _b = b;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte R => _r;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte G => _g;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte B => _b;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte A => byte.MaxValue;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
|
||||
|
||||
/// <inheritdoc />
|
||||
public static IColor Create(byte r, byte g, byte b, byte a) => new ColorRGB(r, g, b);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// ReSharper disable ConvertToAutoProperty
|
||||
// ReSharper disable ConvertToAutoPropertyWhenPossible
|
||||
// ReSharper disable ReplaceWithPrimaryConstructorParameter
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a color in 32 bit RGBA-format.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the <see cref="ColorRGBA"/> class.
|
||||
/// </remarks>
|
||||
/// <param name="r">The Red-component of the color.</param>
|
||||
/// <param name="g">The Green-component of the color.</param>
|
||||
/// <param name="b">The Blue-component of the color.</param>
|
||||
/// <param name="a">The Alpha-component of the color.</param>
|
||||
[DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public readonly partial struct ColorRGBA(byte r, byte g, byte b, byte a) : IColor
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
/// <inheritdoc />
|
||||
public static IColorFormat ColorFormat => IColorFormat.RGBA;
|
||||
|
||||
private readonly byte _r = r;
|
||||
private readonly byte _g = g;
|
||||
private readonly byte _b = b;
|
||||
private readonly byte _a = a;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte R => _r;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte G => _g;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte B => _b;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte A => _a;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
|
||||
|
||||
/// <inheritdoc />
|
||||
public static IColor Create(byte r, byte g, byte b, byte a) => new ColorRGBA(r, g, b, a);
|
||||
|
||||
#endregion
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
namespace HPPH;
|
||||
|
||||
public partial interface IColorFormat
|
||||
{
|
||||
internal IColor Average(ReadOnlySpan<byte> data);
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
namespace HPPH;
|
||||
|
||||
public partial interface IColorFormat
|
||||
{
|
||||
#region Instances
|
||||
|
||||
public static ColorFormatRGB RGB => ColorFormatRGB.Instance;
|
||||
public static ColorFormatBGR BGR => ColorFormatBGR.Instance;
|
||||
public static ColorFormatARGB ARGB => ColorFormatARGB.Instance;
|
||||
public static ColorFormatABGR ABGR => ColorFormatABGR.Instance;
|
||||
public static ColorFormatRGBA RGBA => ColorFormatRGBA.Instance;
|
||||
public static ColorFormatBGRA BGRA => ColorFormatBGRA.Instance;
|
||||
|
||||
#endregion
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
namespace HPPH;
|
||||
|
||||
public partial interface IColorFormat
|
||||
{
|
||||
internal IMinMax MinMax(ReadOnlySpan<byte> data);
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
namespace HPPH;
|
||||
|
||||
public partial interface IColorFormat
|
||||
{
|
||||
internal IColor[] CreateColorPalette(ReadOnlySpan<byte> data, int paletteSize);
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
namespace HPPH;
|
||||
|
||||
public partial interface IColorFormat
|
||||
{
|
||||
internal ISum Sum(ReadOnlySpan<byte> data);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// ReSharper disable ConvertToAutoProperty
|
||||
// ReSharper disable ReplaceWithPrimaryConstructorParameter
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
[DebuggerDisplay("[A: {AlphaMin}-{AlphaMax}, R: {RedMin}-{RedMax}, G: {GreenMin}-{GreenMax}, B: {BlueMin}-{BlueMax}]")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public readonly partial struct MinMaxABGR(byte aMin, byte aMax, byte bMin, byte bMax, byte gMin, byte gMax, byte rMin, byte rMax) : IMinMax
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly byte _aMin = aMin;
|
||||
private readonly byte _aMax = aMax;
|
||||
|
||||
private readonly byte _bMin = bMin;
|
||||
private readonly byte _bMax = bMax;
|
||||
|
||||
private readonly byte _gMin = gMin;
|
||||
private readonly byte _gMax = gMax;
|
||||
|
||||
private readonly byte _rMin = rMin;
|
||||
private readonly byte _rMax = rMax;
|
||||
|
||||
public byte RedMin => _rMin;
|
||||
public byte RedMax => _rMax;
|
||||
|
||||
public byte GreenMin => _gMin;
|
||||
public byte GreenMax => _gMax;
|
||||
|
||||
public byte BlueMin => _bMin;
|
||||
public byte BlueMax => _bMax;
|
||||
|
||||
public byte AlphaMin => _aMin;
|
||||
public byte AlphaMax => _aMax;
|
||||
|
||||
public byte RedRange => (byte)(_rMax - _rMin);
|
||||
public byte GreenRange => (byte)(_gMax - _gMin);
|
||||
public byte BlueRange => (byte)(_bMax - _bMin);
|
||||
public byte AlphaRange => (byte)(_aMax - _aMin);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString() => $"[A: {AlphaMin}-{AlphaMax}, R: {RedMin}-{RedMax}, G: {GreenMin}-{GreenMax}, B: {BlueMin}-{BlueMax}]";
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// ReSharper disable ConvertToAutoProperty
|
||||
// ReSharper disable ReplaceWithPrimaryConstructorParameter
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
[DebuggerDisplay("[A: {AlphaMin}-{AlphaMax}, R: {RedMin}-{RedMax}, G: {GreenMin}-{GreenMax}, B: {BlueMin}-{BlueMax}]")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public readonly partial struct MinMaxARGB(byte aMin, byte aMax, byte rMin, byte rMax, byte gMin, byte gMax, byte bMin, byte bMax) : IMinMax
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly byte _aMin = aMin;
|
||||
private readonly byte _aMax = aMax;
|
||||
|
||||
private readonly byte _rMin = rMin;
|
||||
private readonly byte _rMax = rMax;
|
||||
|
||||
private readonly byte _gMin = gMin;
|
||||
private readonly byte _gMax = gMax;
|
||||
|
||||
private readonly byte _bMin = bMin;
|
||||
private readonly byte _bMax = bMax;
|
||||
|
||||
public byte RedMin => _rMin;
|
||||
public byte RedMax => _rMax;
|
||||
|
||||
public byte GreenMin => _gMin;
|
||||
public byte GreenMax => _gMax;
|
||||
|
||||
public byte BlueMin => _bMin;
|
||||
public byte BlueMax => _bMax;
|
||||
|
||||
public byte AlphaMin => _aMin;
|
||||
public byte AlphaMax => _aMax;
|
||||
|
||||
public byte RedRange => (byte)(_rMax - _rMin);
|
||||
public byte GreenRange => (byte)(_gMax - _gMin);
|
||||
public byte BlueRange => (byte)(_bMax - _bMin);
|
||||
public byte AlphaRange => (byte)(_aMax - _aMin);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString() => $"[A: {AlphaMin}-{AlphaMax}, R: {RedMin}-{RedMax}, G: {GreenMin}-{GreenMax}, B: {BlueMin}-{BlueMax}]";
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// ReSharper disable ConvertToAutoProperty
|
||||
// ReSharper disable ReplaceWithPrimaryConstructorParameter
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
[DebuggerDisplay("[A: {AlphaMin}-{AlphaMax}, R: {RedMin}-{RedMax}, G: {GreenMin}-{GreenMax}, B: {BlueMin}-{BlueMax}]")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public readonly partial struct MinMaxBGR(byte bMin, byte bMax, byte gMin, byte gMax, byte rMin, byte rMax) : IMinMax
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly byte _bMin = bMin;
|
||||
private readonly byte _bMax = bMax;
|
||||
|
||||
private readonly byte _gMin = gMin;
|
||||
private readonly byte _gMax = gMax;
|
||||
|
||||
private readonly byte _rMin = rMin;
|
||||
private readonly byte _rMax = rMax;
|
||||
|
||||
public byte RedMin => _rMin;
|
||||
public byte RedMax => _rMax;
|
||||
|
||||
public byte GreenMin => _gMin;
|
||||
public byte GreenMax => _gMax;
|
||||
|
||||
public byte BlueMin => _bMin;
|
||||
public byte BlueMax => _bMax;
|
||||
|
||||
public byte AlphaMin => byte.MaxValue;
|
||||
public byte AlphaMax => byte.MaxValue;
|
||||
|
||||
public byte RedRange => (byte)(_rMax - _rMin);
|
||||
public byte GreenRange => (byte)(_gMax - _gMin);
|
||||
public byte BlueRange => (byte)(_bMax - _bMin);
|
||||
public byte AlphaRange => 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString() => $"[A: {AlphaMin}-{AlphaMax}, R: {RedMin}-{RedMax}, G: {GreenMin}-{GreenMax}, B: {BlueMin}-{BlueMax}]";
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// ReSharper disable ConvertToAutoProperty
|
||||
// ReSharper disable ReplaceWithPrimaryConstructorParameter
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
[DebuggerDisplay("[A: {AlphaMin}-{AlphaMax}, R: {RedMin}-{RedMax}, G: {GreenMin}-{GreenMax}, B: {BlueMin}-{BlueMax}]")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public readonly partial struct MinMaxBGRA(byte bMin, byte bMax, byte gMin, byte gMax, byte rMin, byte rMax, byte aMin, byte aMax) : IMinMax
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly byte _bMin = bMin;
|
||||
private readonly byte _bMax = bMax;
|
||||
|
||||
private readonly byte _gMin = gMin;
|
||||
private readonly byte _gMax = gMax;
|
||||
|
||||
private readonly byte _rMin = rMin;
|
||||
private readonly byte _rMax = rMax;
|
||||
|
||||
private readonly byte _aMin = aMin;
|
||||
private readonly byte _aMax = aMax;
|
||||
|
||||
public byte RedMin => _rMin;
|
||||
public byte RedMax => _rMax;
|
||||
|
||||
public byte GreenMin => _gMin;
|
||||
public byte GreenMax => _gMax;
|
||||
|
||||
public byte BlueMin => _bMin;
|
||||
public byte BlueMax => _bMax;
|
||||
|
||||
public byte AlphaMin => _aMin;
|
||||
public byte AlphaMax => _aMax;
|
||||
|
||||
public byte RedRange => (byte)(_rMax - _rMin);
|
||||
public byte GreenRange => (byte)(_gMax - _gMin);
|
||||
public byte BlueRange => (byte)(_bMax - _bMin);
|
||||
public byte AlphaRange => (byte)(_aMax - _aMin);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString() => $"[A: {AlphaMin}-{AlphaMax}, R: {RedMin}-{RedMax}, G: {GreenMin}-{GreenMax}, B: {BlueMin}-{BlueMax}]";
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// ReSharper disable ConvertToAutoProperty
|
||||
// ReSharper disable ReplaceWithPrimaryConstructorParameter
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
[DebuggerDisplay("[A: {AlphaMin}-{AlphaMax}, R: {RedMin}-{RedMax}, G: {GreenMin}-{GreenMax}, B: {BlueMin}-{BlueMax}]")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public readonly partial struct MinMaxRGB(byte rMin, byte rMax, byte gMin, byte gMax, byte bMin, byte bMax) : IMinMax
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly byte _rMin = rMin;
|
||||
private readonly byte _rMax = rMax;
|
||||
|
||||
private readonly byte _gMin = gMin;
|
||||
private readonly byte _gMax = gMax;
|
||||
|
||||
private readonly byte _bMin = bMin;
|
||||
private readonly byte _bMax = bMax;
|
||||
|
||||
public byte RedMin => _rMin;
|
||||
public byte RedMax => _rMax;
|
||||
|
||||
public byte GreenMin => _gMin;
|
||||
public byte GreenMax => _gMax;
|
||||
|
||||
public byte BlueMin => _bMin;
|
||||
public byte BlueMax => _bMax;
|
||||
|
||||
public byte AlphaMin => byte.MaxValue;
|
||||
public byte AlphaMax => byte.MaxValue;
|
||||
|
||||
public byte RedRange => (byte)(_rMax - _rMin);
|
||||
public byte GreenRange => (byte)(_gMax - _gMin);
|
||||
public byte BlueRange => (byte)(_bMax - _bMin);
|
||||
public byte AlphaRange => 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString() => $"[A: {AlphaMin}-{AlphaMax}, R: {RedMin}-{RedMax}, G: {GreenMin}-{GreenMax}, B: {BlueMin}-{BlueMax}]";
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// ReSharper disable ConvertToAutoProperty
|
||||
// ReSharper disable ReplaceWithPrimaryConstructorParameter
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
[DebuggerDisplay("[A: {AlphaMin}-{AlphaMax}, R: {RedMin}-{RedMax}, G: {GreenMin}-{GreenMax}, B: {BlueMin}-{BlueMax}]")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public readonly partial struct MinMaxRGBA(byte rMin, byte rMax, byte gMin, byte gMax, byte bMin, byte bMax, byte aMin, byte aMax) : IMinMax
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly byte _rMin = rMin;
|
||||
private readonly byte _rMax = rMax;
|
||||
|
||||
private readonly byte _gMin = gMin;
|
||||
private readonly byte _gMax = gMax;
|
||||
|
||||
private readonly byte _bMin = bMin;
|
||||
private readonly byte _bMax = bMax;
|
||||
|
||||
private readonly byte _aMin = aMin;
|
||||
private readonly byte _aMax = aMax;
|
||||
|
||||
public byte RedMin => _rMin;
|
||||
public byte RedMax => _rMax;
|
||||
|
||||
public byte GreenMin => _gMin;
|
||||
public byte GreenMax => _gMax;
|
||||
|
||||
public byte BlueMin => _bMin;
|
||||
public byte BlueMax => _bMax;
|
||||
|
||||
public byte AlphaMin => _aMin;
|
||||
public byte AlphaMax => _aMax;
|
||||
|
||||
public byte RedRange => (byte)(_rMax - _rMin);
|
||||
public byte GreenRange => (byte)(_gMax - _gMin);
|
||||
public byte BlueRange => (byte)(_bMax - _bMin);
|
||||
public byte AlphaRange => (byte)(_aMax - _aMin);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString() => $"[A: {AlphaMin}-{AlphaMax}, R: {RedMin}-{RedMax}, G: {GreenMin}-{GreenMax}, B: {BlueMin}-{BlueMax}]";
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// ReSharper disable ConvertToAutoProperty
|
||||
// ReSharper disable ConvertToAutoPropertyWhenPossible
|
||||
// ReSharper disable ReplaceWithPrimaryConstructorParameter
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
[DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public readonly partial struct SumABGR(long a, long b, long g, long r) : ISum
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly long _a = a;
|
||||
private readonly long _b = b;
|
||||
private readonly long _g = g;
|
||||
private readonly long _r = r;
|
||||
|
||||
public long R => _r;
|
||||
public long G => _g;
|
||||
public long B => _b;
|
||||
public long A => _a;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// ReSharper disable ConvertToAutoProperty
|
||||
// ReSharper disable ConvertToAutoPropertyWhenPossible
|
||||
// ReSharper disable ReplaceWithPrimaryConstructorParameter
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
[DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public readonly partial struct SumARGB(long a, long r, long g, long b) : ISum
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly long _a = a;
|
||||
private readonly long _r = r;
|
||||
private readonly long _g = g;
|
||||
private readonly long _b = b;
|
||||
|
||||
public long R => _r;
|
||||
public long G => _g;
|
||||
public long B => _b;
|
||||
public long A => _a;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// ReSharper disable ConvertToAutoProperty
|
||||
// ReSharper disable ConvertToAutoPropertyWhenPossible
|
||||
// ReSharper disable ReplaceWithPrimaryConstructorParameter
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
[DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public readonly partial struct SumBGR(long b, long g, long r, long a) : ISum
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly long _b = b;
|
||||
private readonly long _g = g;
|
||||
private readonly long _r = r;
|
||||
private readonly long _a = a;
|
||||
|
||||
public long A => _a;
|
||||
public long R => _r;
|
||||
public long G => _g;
|
||||
public long B => _b;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// ReSharper disable ConvertToAutoProperty
|
||||
// ReSharper disable ConvertToAutoPropertyWhenPossible
|
||||
// ReSharper disable ReplaceWithPrimaryConstructorParameter
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
[DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public readonly partial struct SumBGRA(long b, long g, long r, long a) : ISum
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly long _b = b;
|
||||
private readonly long _g = g;
|
||||
private readonly long _r = r;
|
||||
private readonly long _a = a;
|
||||
|
||||
public long R => _r;
|
||||
public long G => _g;
|
||||
public long B => _b;
|
||||
public long A => _a;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// ReSharper disable ConvertToAutoProperty
|
||||
// ReSharper disable ConvertToAutoPropertyWhenPossible
|
||||
// ReSharper disable ReplaceWithPrimaryConstructorParameter
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
[DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public readonly partial struct SumRGB(long r, long g, long b, long a) : ISum
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly long _r = r;
|
||||
private readonly long _g = g;
|
||||
private readonly long _b = b;
|
||||
private readonly long _a = a;
|
||||
|
||||
public long A => _a;
|
||||
public long R => _r;
|
||||
public long G => _g;
|
||||
public long B => _b;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// ReSharper disable ConvertToAutoProperty
|
||||
// ReSharper disable ConvertToAutoPropertyWhenPossible
|
||||
// ReSharper disable ReplaceWithPrimaryConstructorParameter
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
[DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public readonly partial struct SumRGBA(long r, long g, long b, long a) : ISum
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly long _r = r;
|
||||
private readonly long _g = g;
|
||||
private readonly long _b = b;
|
||||
private readonly long _a = a;
|
||||
|
||||
public long R => _r;
|
||||
public long G => _g;
|
||||
public long B => _b;
|
||||
public long A => _a;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
|
||||
<CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\HPPH.Generators\HPPH.Generators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="$(CompilerGeneratedFilesOutputPath)/**/*.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,15 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=colors/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=colors_005C24bit/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=colors_005C32bit/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=colors_005Cgeneric/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=communitytoolkit_002Ehighperformance/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=data/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=extensions/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=generatorattributes/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=generic/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=images/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=interfaces/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=minmax/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=minmax_005Cgeneric/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=quantization/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
@@ -0,0 +1,189 @@
|
||||
namespace HPPH;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a image.
|
||||
/// </summary>
|
||||
public interface IImage : IEnumerable<IColor>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the color format used in this image.
|
||||
/// </summary>
|
||||
IColorFormat ColorFormat { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the width of this image.
|
||||
/// </summary>
|
||||
int Width { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the height of this image.
|
||||
/// </summary>
|
||||
int Height { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size in bytes of this image.
|
||||
/// </summary>
|
||||
int SizeInBytes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the color at the specified location.
|
||||
/// </summary>
|
||||
/// <param name="x">The X-location to read.</param>
|
||||
/// <param name="y">The Y-location to read.</param>
|
||||
/// <returns>The color at the specified location.</returns>
|
||||
IColor this[int x, int y] { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets an image representing the specified location.
|
||||
/// </summary>
|
||||
/// <param name="x">The X-location of the image.</param>
|
||||
/// <param name="y">The Y-location of the image.</param>
|
||||
/// <param name="width">The width of the sub-image.</param>
|
||||
/// <param name="height"></param>
|
||||
/// <returns></returns>
|
||||
IImage this[int x, int y, int width, int height] { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of all rows of this image.
|
||||
/// </summary>
|
||||
IImageRows Rows { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of all columns of this image.
|
||||
/// </summary>
|
||||
IImageColumns Columns { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets an <see cref="RefImage{TColor}"/> representing this <see cref="IImage"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TColor">The color-type of the iamge.</typeparam>
|
||||
/// <returns>The <inheritdoc cref="RefImage{TColor}"/>.</returns>
|
||||
RefImage<TColor> AsRefImage<TColor>() where TColor : struct, IColor;
|
||||
|
||||
/// <summary>
|
||||
/// Copies the contents of this <see cref="IImage"/> into a destination <see cref="Span{T}"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="destination">The destination <see cref="Span{T}"/> instance.</param>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when <paramref name="destination"/> is shorter than the source <see cref="IImage"/> instance.
|
||||
/// </exception>
|
||||
void CopyTo(Span<byte> destination);
|
||||
|
||||
/// <summary>
|
||||
/// Allocates a new array and copies this <see cref="IImage"/> into it.
|
||||
/// </summary>
|
||||
/// <returns>The new array containing the data of this <see cref="IImage"/>.</returns>
|
||||
byte[] ToArray();
|
||||
|
||||
/// <summary>
|
||||
/// Represents a list of rows of an image.
|
||||
/// </summary>
|
||||
public interface IImageRows : IEnumerable<IImageRow>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the amount of rows in this list.
|
||||
/// </summary>
|
||||
int Count { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a specific <see cref="IImageRow"/>.
|
||||
/// </summary>
|
||||
/// <param name="column">The ´row to get.</param>
|
||||
/// <returns>The requested <see cref="IImageRow"/>.</returns>
|
||||
IImageRow this[int column] { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a list of columns of an image.
|
||||
/// </summary>
|
||||
public interface IImageColumns : IEnumerable<IImageColumn>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the amount of columns in this list.
|
||||
/// </summary>
|
||||
int Count { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a specific <see cref="IImageColumn"/>.
|
||||
/// </summary>
|
||||
/// <param name="column">The column to get.</param>
|
||||
/// <returns>The requested <see cref="IImageColumn"/>.</returns>
|
||||
IImageColumn this[int column] { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a single row of an image.
|
||||
/// </summary>
|
||||
public interface IImageRow : IEnumerable<IColor>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the length of the row.
|
||||
/// </summary>
|
||||
int Length { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size in bytes of this row.
|
||||
/// </summary>
|
||||
int SizeInBytes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IColor"/> at the specified location.
|
||||
/// </summary>
|
||||
/// <param name="x">The location to get the color from.</param>
|
||||
/// <returns>The <see cref="IColor"/> at the specified location.</returns>
|
||||
IColor this[int x] { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Copies the contents of this <see cref="IImageRow"/> into a destination <see cref="Span{T}"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="destination">The destination <see cref="Span{T}"/> instance.</param>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when <paramref name="destination"/> is shorter than the source <see cref="IImageRow"/> instance.
|
||||
/// </exception>
|
||||
void CopyTo(Span<byte> destination);
|
||||
|
||||
/// <summary>
|
||||
/// Allocates a new array and copies this <see cref="IImageRow"/> into it.
|
||||
/// </summary>
|
||||
/// <returns>The new array containing the data of this <see cref="IImageRow"/>.</returns>
|
||||
byte[] ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a single column of an image.
|
||||
/// </summary>
|
||||
public interface IImageColumn : IEnumerable<IColor>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the length of the column.
|
||||
/// </summary>
|
||||
int Length { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size in bytes of this column.
|
||||
/// </summary>
|
||||
int SizeInBytes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IColor"/> at the specified location.
|
||||
/// </summary>
|
||||
/// <param name="y">The location to get the color from.</param>
|
||||
/// <returns>The <see cref="IColor"/> at the specified location.</returns>
|
||||
IColor this[int y] { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Copies the contents of this <see cref="IImageColumn"/> into a destination <see cref="Span{T}"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="destination">The destination <see cref="Span{T}"/> instance.</param>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when <paramref name="destination"/> is shorter than the source <see cref="IImageColumn"/> instance.
|
||||
/// </exception>
|
||||
void CopyTo(Span<byte> destination);
|
||||
|
||||
/// <summary>
|
||||
/// Allocates a new array and copies this <see cref="IImageColumn"/> into it.
|
||||
/// </summary>
|
||||
/// <returns>The new array containing the data of this <see cref="IImageColumn"/>.</returns>
|
||||
byte[] ToArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,428 @@
|
||||
using System.Collections;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
/// <inheritdoc />
|
||||
public sealed class Image<TColor> : IImage
|
||||
where TColor : struct, IColor
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly byte[] _buffer;
|
||||
|
||||
private readonly int _x;
|
||||
private readonly int _y;
|
||||
private readonly int _stride;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IColorFormat ColorFormat
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => TColor.ColorFormat;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int Width { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public int Height { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public int SizeInBytes => Width * Height * ColorFormat.BytesPerPixel;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Indexer
|
||||
|
||||
/// <inheritdoc />
|
||||
public IColor this[int x, int y]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
if ((x < 0) || (y < 0) || (x >= Width) || (y >= Height)) throw new IndexOutOfRangeException();
|
||||
|
||||
return MemoryMarshal.Cast<byte, TColor>(_buffer)[((_y + y) * _stride) + (_x + x)];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IImage this[int x, int y, int width, int height]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
if ((x < 0) || (y < 0) || (width <= 0) || (height <= 0) || ((x + width) > Width) || ((y + height) > Height)) throw new IndexOutOfRangeException();
|
||||
|
||||
return new Image<TColor>(_buffer, _x + x, _y + y, width, height, _stride);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IImage.IImageRows Rows
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => new ImageRows(_buffer, _x, _y, Width, Height, _stride);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IImage.IImageColumns Columns
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => new ImageColumns(_buffer, _x, _y, Width, Height, _stride);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
internal Image(byte[] buffer, int x, int y, int width, int height, int stride)
|
||||
{
|
||||
this._buffer = buffer;
|
||||
this._x = x;
|
||||
this._y = y;
|
||||
this.Width = width;
|
||||
this.Height = height;
|
||||
this._stride = stride;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public void CopyTo(Span<byte> destination)
|
||||
{
|
||||
if (destination == null) throw new ArgumentNullException(nameof(destination));
|
||||
if (destination.Length < SizeInBytes) throw new ArgumentException("The destination is too small to fit this image.", nameof(destination));
|
||||
|
||||
int targetStride = Width * ColorFormat.BytesPerPixel;
|
||||
IImage.IImageRows rows = Rows;
|
||||
Span<byte> target = destination;
|
||||
foreach (IImage.IImageRow row in rows)
|
||||
{
|
||||
row.CopyTo(target);
|
||||
target = target[targetStride..];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte[] ToArray()
|
||||
{
|
||||
byte[] array = new byte[SizeInBytes];
|
||||
CopyTo(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public RefImage<T> AsRefImage<T>()
|
||||
where T : struct, IColor
|
||||
{
|
||||
if (typeof(T) != typeof(TColor)) throw new ArgumentException("The requested color format does not fit this image.", nameof(T));
|
||||
|
||||
return new RefImage<T>(MemoryMarshal.Cast<byte, T>(_buffer), _x, _y, Width, Height, _stride);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerator<IColor> GetEnumerator()
|
||||
{
|
||||
for (int y = 0; y < Height; y++)
|
||||
for (int x = 0; x < Width; x++)
|
||||
yield return this[x, y];
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Indexer-Classes
|
||||
|
||||
/// <inheritdoc />
|
||||
private sealed class ImageRows : IImage.IImageRows
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly byte[] _buffer;
|
||||
private readonly int _x;
|
||||
private readonly int _y;
|
||||
private readonly int _width;
|
||||
private readonly int _height;
|
||||
private readonly int _stride;
|
||||
|
||||
/// <inheritdoc />
|
||||
public int Count => _height;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Indexer
|
||||
|
||||
/// <inheritdoc />
|
||||
public IImage.IImageRow this[int row]
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((row < 0) || (row >= _height)) throw new IndexOutOfRangeException();
|
||||
|
||||
return new ImageRow(_buffer, (((row + _y) * _stride) + _x), _width);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
internal ImageRows(byte[] buffer, int x, int y, int width, int height, int stride)
|
||||
{
|
||||
this._buffer = buffer;
|
||||
this._x = x;
|
||||
this._y = y;
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
this._stride = stride;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerator<IImage.IImageRow> GetEnumerator()
|
||||
{
|
||||
for (int y = 0; y < _height; y++)
|
||||
yield return this[y];
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
private sealed class ImageRow : IImage.IImageRow
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly byte[] _buffer;
|
||||
private readonly int _start;
|
||||
private readonly int _length;
|
||||
|
||||
/// <inheritdoc />
|
||||
public int Length => _length;
|
||||
|
||||
/// <inheritdoc />
|
||||
public int SizeInBytes => Length * TColor.ColorFormat.BytesPerPixel;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Indexer
|
||||
|
||||
/// <inheritdoc />
|
||||
public IColor this[int x]
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((x < 0) || (x >= _length)) throw new IndexOutOfRangeException();
|
||||
|
||||
ReadOnlySpan<TColor> row = MemoryMarshal.Cast<byte, TColor>(_buffer)[_start..];
|
||||
return row[x];
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
internal ImageRow(byte[] buffer, int start, int length)
|
||||
{
|
||||
this._buffer = buffer;
|
||||
this._start = start;
|
||||
this._length = length;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public void CopyTo(Span<byte> destination)
|
||||
{
|
||||
if (destination == null) throw new ArgumentNullException(nameof(destination));
|
||||
if (destination.Length < SizeInBytes) throw new ArgumentException("The destination is too small to fit this image.", nameof(destination));
|
||||
|
||||
MemoryMarshal.Cast<byte, TColor>(_buffer).Slice(_start, _length).CopyTo(MemoryMarshal.Cast<byte, TColor>(destination));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte[] ToArray()
|
||||
{
|
||||
byte[] array = new byte[SizeInBytes];
|
||||
CopyTo(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerator<IColor> GetEnumerator()
|
||||
{
|
||||
for (int x = 0; x < _length; x++)
|
||||
yield return this[x];
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
private sealed class ImageColumns : IImage.IImageColumns
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly byte[] _buffer;
|
||||
private readonly int _x;
|
||||
private readonly int _y;
|
||||
private readonly int _width;
|
||||
private readonly int _height;
|
||||
private readonly int _stride;
|
||||
|
||||
/// <inheritdoc />
|
||||
public int Count => _width;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Indexer
|
||||
|
||||
/// <inheritdoc />
|
||||
public IImage.IImageColumn this[int column]
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((column < 0) || (column >= _width)) throw new IndexOutOfRangeException();
|
||||
|
||||
return new ImageColumn(_buffer, (_y * _stride) + _x + column, _height, _stride);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
internal ImageColumns(byte[] buffer, int x, int y, int width, int height, int stride)
|
||||
{
|
||||
this._buffer = buffer;
|
||||
this._x = x;
|
||||
this._y = y;
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
this._stride = stride;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerator<IImage.IImageColumn> GetEnumerator()
|
||||
{
|
||||
for (int y = 0; y < _height; y++)
|
||||
yield return this[y];
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
private sealed class ImageColumn : IImage.IImageColumn
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly byte[] _buffer;
|
||||
private readonly int _start;
|
||||
private readonly int _length;
|
||||
private readonly int _step;
|
||||
|
||||
/// <inheritdoc />
|
||||
public int Length => _length;
|
||||
|
||||
/// <inheritdoc />
|
||||
public int SizeInBytes => Length * TColor.ColorFormat.BytesPerPixel;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Indexer
|
||||
|
||||
/// <inheritdoc />
|
||||
public IColor this[int y]
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((y < 0) || (y >= _length)) throw new IndexOutOfRangeException();
|
||||
|
||||
ReadOnlySpan<TColor> data = MemoryMarshal.Cast<byte, TColor>(_buffer)[_start..];
|
||||
return data[y * _step];
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
internal ImageColumn(byte[] buffer, int start, int length, int step)
|
||||
{
|
||||
this._buffer = buffer;
|
||||
this._start = start;
|
||||
this._length = length;
|
||||
this._step = step;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public void CopyTo(Span<byte> destination)
|
||||
{
|
||||
if (destination == null) throw new ArgumentNullException(nameof(destination));
|
||||
if (destination.Length < SizeInBytes) throw new ArgumentException("The destination is too small to fit this image.", nameof(destination));
|
||||
|
||||
if (_step == 1)
|
||||
_buffer.AsSpan(_start, SizeInBytes).CopyTo(destination);
|
||||
else
|
||||
{
|
||||
ReadOnlySpan<TColor> data = MemoryMarshal.Cast<byte, TColor>(_buffer)[_start..];
|
||||
Span<TColor> target = MemoryMarshal.Cast<byte, TColor>(destination);
|
||||
for (int i = 0; i < Length; i++)
|
||||
target[i] = data[i * _step];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte[] ToArray()
|
||||
{
|
||||
byte[] array = new byte[SizeInBytes];
|
||||
CopyTo(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerator<IColor> GetEnumerator()
|
||||
{
|
||||
for (int y = 0; y < _length; y++)
|
||||
yield return this[y];
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,367 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public readonly ref struct RefImage<TColor>
|
||||
where TColor : struct, IColor
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly ReadOnlySpan<TColor> _pixels;
|
||||
|
||||
private readonly int _x;
|
||||
private readonly int _y;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the width of the image.
|
||||
/// </summary>
|
||||
public int Width { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the height of the image.
|
||||
/// </summary>
|
||||
public int Height { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the stride (entries per row) of the underlying buffer.
|
||||
/// Only useful if you want to work with a pinned buffer.
|
||||
/// </summary>
|
||||
public int RawStride { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Indexer
|
||||
|
||||
public ref readonly TColor this[int x, int y]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
if ((x < 0) || (y < 0) || (x >= Width) || (y >= Height)) throw new IndexOutOfRangeException();
|
||||
|
||||
ref TColor r0 = ref MemoryMarshal.GetReference(_pixels);
|
||||
nint offset = (nint)(uint)((_y + y) * RawStride) + (_x + x);
|
||||
return ref Unsafe.Add(ref r0, offset);
|
||||
}
|
||||
}
|
||||
|
||||
public RefImage<TColor> this[int x, int y, int width, int height]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
if ((x < 0) || (y < 0) || (width <= 0) || (height <= 0) || ((x + width) > Width) || ((y + height) > Height)) throw new IndexOutOfRangeException();
|
||||
|
||||
return new RefImage<TColor>(_pixels, _x + x, _y + y, width, height, RawStride);
|
||||
}
|
||||
}
|
||||
|
||||
public ImageRows Rows
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => new(_pixels, _x, _y, Width, Height, RawStride);
|
||||
}
|
||||
|
||||
public ImageColumns Columns
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => new(_pixels, _x, _y, Width, Height, RawStride);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
internal RefImage(ReadOnlySpan<TColor> pixels, int x, int y, int width, int height, int stride)
|
||||
{
|
||||
this._pixels = pixels;
|
||||
this._x = x;
|
||||
this._y = y;
|
||||
this.Width = width;
|
||||
this.Height = height;
|
||||
this.RawStride = stride;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Copies the contents of this <see cref="RefImage{TColor}"/> into a destination <see cref="Span{T}"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="destination">The destination <see cref="Span{T}"/> instance.</param>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when <paramref name="destination"/> is shorter than the source <see cref="RefImage{TColor}"/> instance.
|
||||
/// </exception>
|
||||
public void CopyTo(Span<TColor> destination)
|
||||
{
|
||||
if (destination == null) throw new ArgumentNullException(nameof(destination));
|
||||
if (destination.Length < (Width * Height)) throw new ArgumentException("The destination is too small to fit this image.", nameof(destination));
|
||||
|
||||
ImageRows rows = Rows;
|
||||
Span<TColor> target = destination;
|
||||
foreach (ReadOnlyRefEnumerable<TColor> row in rows)
|
||||
{
|
||||
row.CopyTo(target);
|
||||
target = target[Width..];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates a new array and copies this <see cref="RefImage{TColor}"/> into it.
|
||||
/// </summary>
|
||||
/// <returns>The new array containing the data of this <see cref="RefImage{TColor}"/>.</returns>
|
||||
public TColor[] ToArray()
|
||||
{
|
||||
TColor[] array = new TColor[Width * Height];
|
||||
CopyTo(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a reference to the first element of this image inside the full image buffer.
|
||||
/// </summary>
|
||||
public ref readonly TColor GetPinnableReference()
|
||||
{
|
||||
if (_pixels.Length == 0)
|
||||
return ref Unsafe.NullRef<TColor>();
|
||||
|
||||
int offset = (_y * RawStride) + _x;
|
||||
return ref MemoryMarshal.GetReference(_pixels[offset..]);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="System.Collections.IEnumerable.GetEnumerator"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ImageEnumerator GetEnumerator() => new(_pixels);
|
||||
|
||||
#endregion
|
||||
|
||||
public ref struct ImageEnumerator
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly ReadOnlySpan<TColor> _pixels;
|
||||
private int _position;
|
||||
|
||||
/// <inheritdoc cref="System.Collections.Generic.IEnumerator{T}.Current"/>
|
||||
public TColor Current
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _pixels[_position];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal ImageEnumerator(ReadOnlySpan<TColor> pixels)
|
||||
{
|
||||
this._pixels = pixels;
|
||||
|
||||
_position = -1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc cref="System.Collections.IEnumerator.MoveNext"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool MoveNext() => ++_position < _pixels.Length;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region Indexer-Structs
|
||||
|
||||
public readonly ref struct ImageRows
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly ReadOnlySpan<TColor> _pixels;
|
||||
private readonly int _x;
|
||||
private readonly int _y;
|
||||
private readonly int _width;
|
||||
private readonly int _height;
|
||||
private readonly int _stride;
|
||||
|
||||
public int Count => _height;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Indexer
|
||||
|
||||
public readonly ReadOnlyRefEnumerable<TColor> this[int row]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
if ((row < 0) || (row > _height)) throw new IndexOutOfRangeException();
|
||||
|
||||
ref TColor r0 = ref MemoryMarshal.GetReference(_pixels);
|
||||
ref TColor rr = ref Unsafe.Add(ref r0, (nint)(uint)(((row + _y) * _stride) + _x));
|
||||
|
||||
return new ReadOnlyRefEnumerable<TColor>(rr, _width, 1);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public ImageRows(ReadOnlySpan<TColor> pixels, int x, int y, int width, int height, int stride)
|
||||
{
|
||||
this._pixels = pixels;
|
||||
this._x = x;
|
||||
this._y = y;
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
this._stride = stride;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc cref="System.Collections.IEnumerable.GetEnumerator"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ImageRowsEnumerator GetEnumerator() => new(this);
|
||||
|
||||
#endregion
|
||||
|
||||
public ref struct ImageRowsEnumerator
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly ImageRows _rows;
|
||||
private int _position;
|
||||
|
||||
/// <inheritdoc cref="System.Collections.Generic.IEnumerator{T}.Current"/>
|
||||
public ReadOnlyRefEnumerable<TColor> Current
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _rows[_position];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal ImageRowsEnumerator(ImageRows rows)
|
||||
{
|
||||
this._rows = rows;
|
||||
|
||||
_position = -1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc cref="System.Collections.IEnumerator.MoveNext"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool MoveNext() => ++_position < _rows._height;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
public readonly ref struct ImageColumns
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly ReadOnlySpan<TColor> _pixels;
|
||||
private readonly int _x;
|
||||
private readonly int _y;
|
||||
private readonly int _width;
|
||||
private readonly int _height;
|
||||
private readonly int _stride;
|
||||
|
||||
public int Count => _width;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Indexer
|
||||
|
||||
public ReadOnlyRefEnumerable<TColor> this[int column]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
if ((column < 0) || (column > _width)) throw new IndexOutOfRangeException();
|
||||
|
||||
ref TColor r0 = ref MemoryMarshal.GetReference(_pixels);
|
||||
ref TColor rc = ref Unsafe.Add(ref r0, (nint)(uint)((_y * _stride) + (column + _x)));
|
||||
|
||||
return new ReadOnlyRefEnumerable<TColor>(rc, _height, _stride);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
public ImageColumns(ReadOnlySpan<TColor> pixels, int x, int y, int width, int height, int stride)
|
||||
{
|
||||
this._pixels = pixels;
|
||||
this._x = x;
|
||||
this._y = y;
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
this._stride = stride;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc cref="System.Collections.IEnumerable.GetEnumerator"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ImageColumnsEnumerator GetEnumerator() => new(this);
|
||||
|
||||
#endregion
|
||||
|
||||
public ref struct ImageColumnsEnumerator
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly ImageColumns _columns;
|
||||
private int _position;
|
||||
|
||||
/// <inheritdoc cref="System.Collections.Generic.IEnumerator{T}.Current"/>
|
||||
public ReadOnlyRefEnumerable<TColor> Current
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _columns[_position];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal ImageColumnsEnumerator(ImageColumns columns)
|
||||
{
|
||||
this._columns = columns;
|
||||
this._position = -1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc cref="System.Collections.IEnumerator.MoveNext"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool MoveNext() => ++_position < _columns._width;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
using System.Buffers;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public static partial class PixelHelper
|
||||
{
|
||||
#region Methods
|
||||
|
||||
public static IColor Average(IImage image)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(image);
|
||||
|
||||
int dataLength = image.SizeInBytes;
|
||||
byte[] array = ArrayPool<byte>.Shared.Rent(dataLength);
|
||||
Span<byte> buffer = array.AsSpan()[..dataLength];
|
||||
try
|
||||
{
|
||||
image.CopyTo(buffer);
|
||||
return image.ColorFormat.Average(buffer);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(array);
|
||||
}
|
||||
}
|
||||
|
||||
public static T Average<T>(RefImage<T> image)
|
||||
where T : struct, IColor
|
||||
{
|
||||
int dataLength = image.Width * image.Height;
|
||||
T[] array = ArrayPool<T>.Shared.Rent(dataLength);
|
||||
Span<T> buffer = array.AsSpan()[..(dataLength)];
|
||||
try
|
||||
{
|
||||
image.CopyTo(buffer);
|
||||
return Average<T>(buffer);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<T>.Shared.Return(array);
|
||||
}
|
||||
}
|
||||
|
||||
public static T Average<T>(ReadOnlySpan<T> colors)
|
||||
where T : struct, IColor
|
||||
{
|
||||
if (colors == null) throw new ArgumentNullException(nameof(colors));
|
||||
|
||||
return T.ColorFormat.BytesPerPixel switch
|
||||
{
|
||||
3 => Unsafe.BitCast<Generic3ByteData, T>(Average(MemoryMarshal.Cast<T, Generic3ByteData>(colors))),
|
||||
4 => Unsafe.BitCast<Generic4ByteData, T>(Average(MemoryMarshal.Cast<T, Generic4ByteData>(colors))),
|
||||
_ => throw new NotSupportedException("Data is not of a supported valid color-type.")
|
||||
};
|
||||
}
|
||||
|
||||
internal static Generic3ByteData Average(ReadOnlySpan<Generic3ByteData> data)
|
||||
{
|
||||
if (data == null) throw new ArgumentNullException(nameof(data));
|
||||
if (data.Length == 0) return default;
|
||||
if (data.Length == 1) return data[0];
|
||||
|
||||
Generic4LongData sum = Sum(data);
|
||||
|
||||
float count = data.Length;
|
||||
return new Generic3ByteData((sum.L1 / count).GetByteValueFromPercentage(),
|
||||
(sum.L2 / count).GetByteValueFromPercentage(),
|
||||
(sum.L3 / count).GetByteValueFromPercentage());
|
||||
}
|
||||
|
||||
internal static Generic4ByteData Average(ReadOnlySpan<Generic4ByteData> data)
|
||||
{
|
||||
if (data == null) throw new ArgumentNullException(nameof(data));
|
||||
if (data.Length == 0) return default;
|
||||
if (data.Length == 1) return data[0];
|
||||
|
||||
Generic4LongData sum = Sum(data);
|
||||
|
||||
float count = data.Length;
|
||||
return new Generic4ByteData((sum.L1 / count).GetByteValueFromPercentage(),
|
||||
(sum.L2 / count).GetByteValueFromPercentage(),
|
||||
(sum.L3 / count).GetByteValueFromPercentage(),
|
||||
(sum.L4 / count).GetByteValueFromPercentage());
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
using System.Buffers;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public static unsafe partial class PixelHelper
|
||||
{
|
||||
#region Methods
|
||||
|
||||
public static IMinMax MinMax(IImage image)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(image);
|
||||
|
||||
int dataLength = image.SizeInBytes;
|
||||
byte[] array = ArrayPool<byte>.Shared.Rent(dataLength);
|
||||
Span<byte> buffer = array.AsSpan()[..dataLength];
|
||||
try
|
||||
{
|
||||
image.CopyTo(buffer);
|
||||
return image.ColorFormat.MinMax(buffer);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(array);
|
||||
}
|
||||
}
|
||||
|
||||
public static IMinMax MinMax<T>(RefImage<T> image)
|
||||
where T : struct, IColor
|
||||
{
|
||||
int dataLength = image.Width * image.Height;
|
||||
T[] array = ArrayPool<T>.Shared.Rent(dataLength);
|
||||
Span<T> buffer = array.AsSpan()[..(dataLength)];
|
||||
try
|
||||
{
|
||||
image.CopyTo(buffer);
|
||||
return MinMax<T>(buffer);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<T>.Shared.Return(array);
|
||||
}
|
||||
}
|
||||
|
||||
public static IMinMax MinMax<T>(ReadOnlySpan<T> colors)
|
||||
where T : struct, IColor
|
||||
=> T.ColorFormat.MinMax(MemoryMarshal.AsBytes(colors));
|
||||
|
||||
internal static IMinMax MinMax<T, TMinMax>(ReadOnlySpan<T> colors)
|
||||
where T : struct, IColor
|
||||
where TMinMax : struct, IMinMax
|
||||
{
|
||||
if (colors == null) throw new ArgumentNullException(nameof(colors));
|
||||
|
||||
return T.ColorFormat.BytesPerPixel switch
|
||||
{
|
||||
3 => Unsafe.BitCast<Generic3ByteMinMax, TMinMax>(MinMax(MemoryMarshal.Cast<T, Generic3ByteData>(colors))),
|
||||
4 => Unsafe.BitCast<Generic4ByteMinMax, TMinMax>(MinMax(MemoryMarshal.Cast<T, Generic4ByteData>(colors))),
|
||||
_ => throw new NotSupportedException("Data is not of a supported valid color-type.")
|
||||
};
|
||||
}
|
||||
|
||||
internal static Generic3ByteMinMax MinMax(ReadOnlySpan<Generic3ByteData> data)
|
||||
{
|
||||
byte minB1 = byte.MaxValue, minB2 = byte.MaxValue, minB3 = byte.MaxValue;
|
||||
byte maxB1 = byte.MinValue, maxB2 = byte.MinValue, maxB3 = byte.MinValue;
|
||||
|
||||
const int BYTES_PER_COLOR = 3;
|
||||
int elementsPerVector = Vector<byte>.Count / BYTES_PER_COLOR;
|
||||
|
||||
int chunks;
|
||||
if (Vector.IsHardwareAccelerated && ((chunks = data.Length / elementsPerVector) > 1))
|
||||
{
|
||||
int bytesPerVector = elementsPerVector * BYTES_PER_COLOR;
|
||||
int missingElements = data.Length - (chunks * elementsPerVector);
|
||||
|
||||
Vector<byte> max = Vector<byte>.Zero;
|
||||
Vector<byte> min = new(byte.MaxValue);
|
||||
|
||||
ReadOnlySpan<byte> colorBytes = MemoryMarshal.AsBytes(data);
|
||||
fixed (byte* colorPtr = colorBytes)
|
||||
{
|
||||
for (int i = 0; i < chunks; i++)
|
||||
{
|
||||
Vector<byte> vector = *(Vector<byte>*)(colorPtr + (i * bytesPerVector));
|
||||
|
||||
max = Vector.Max(max, vector);
|
||||
min = Vector.Min(min, vector);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < bytesPerVector; i += BYTES_PER_COLOR)
|
||||
{
|
||||
minB1 = Math.Min(minB1, min[i]);
|
||||
minB2 = Math.Min(minB2, min[i + 1]);
|
||||
minB3 = Math.Min(minB3, min[i + 2]);
|
||||
|
||||
maxB1 = Math.Max(maxB1, max[i]);
|
||||
maxB2 = Math.Max(maxB2, max[i + 1]);
|
||||
maxB3 = Math.Max(maxB3, max[i + 2]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < missingElements; i++)
|
||||
{
|
||||
Generic3ByteData d = data[^(i + 1)];
|
||||
|
||||
minB1 = Math.Min(minB1, d.B1);
|
||||
minB2 = Math.Min(minB2, d.B2);
|
||||
minB3 = Math.Min(minB3, d.B3);
|
||||
|
||||
maxB1 = Math.Max(maxB1, d.B1);
|
||||
maxB2 = Math.Max(maxB2, d.B2);
|
||||
maxB3 = Math.Max(maxB3, d.B3);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (Generic3ByteData d in data)
|
||||
{
|
||||
minB1 = Math.Min(minB1, d.B1);
|
||||
minB2 = Math.Min(minB2, d.B2);
|
||||
minB3 = Math.Min(minB3, d.B3);
|
||||
|
||||
maxB1 = Math.Max(maxB1, d.B1);
|
||||
maxB2 = Math.Max(maxB2, d.B2);
|
||||
maxB3 = Math.Max(maxB3, d.B3);
|
||||
}
|
||||
}
|
||||
|
||||
return new Generic3ByteMinMax(minB1, maxB1, minB2, maxB2, minB3, maxB3);
|
||||
}
|
||||
|
||||
internal static Generic4ByteMinMax MinMax(ReadOnlySpan<Generic4ByteData> data)
|
||||
{
|
||||
byte minB1 = byte.MaxValue, minB2 = byte.MaxValue, minB3 = byte.MaxValue, minB4 = byte.MaxValue;
|
||||
byte maxB1 = byte.MinValue, maxB2 = byte.MinValue, maxB3 = byte.MinValue, maxB4 = byte.MinValue;
|
||||
|
||||
const int BYTES_PER_COLOR = 4;
|
||||
int elementsPerVector = Vector<byte>.Count / BYTES_PER_COLOR;
|
||||
|
||||
int chunks;
|
||||
if (Vector.IsHardwareAccelerated && ((chunks = data.Length / elementsPerVector) > 1))
|
||||
{
|
||||
int bytesPerVector = elementsPerVector * BYTES_PER_COLOR;
|
||||
int missingElements = data.Length - (chunks * elementsPerVector);
|
||||
|
||||
Vector<byte> max = Vector<byte>.Zero;
|
||||
Vector<byte> min = new(byte.MaxValue);
|
||||
|
||||
ReadOnlySpan<byte> colorBytes = MemoryMarshal.AsBytes(data);
|
||||
fixed (byte* colorPtr = colorBytes)
|
||||
{
|
||||
for (int i = 0; i < chunks; i++)
|
||||
{
|
||||
Vector<byte> vector = *(Vector<byte>*)(colorPtr + (i * bytesPerVector));
|
||||
|
||||
max = Vector.Max(max, vector);
|
||||
min = Vector.Min(min, vector);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < bytesPerVector; i += BYTES_PER_COLOR)
|
||||
{
|
||||
minB1 = Math.Min(minB1, min[i]);
|
||||
minB2 = Math.Min(minB2, min[i + 1]);
|
||||
minB3 = Math.Min(minB3, min[i + 2]);
|
||||
minB4 = Math.Min(minB4, min[i + 3]);
|
||||
|
||||
maxB1 = Math.Max(maxB1, max[i]);
|
||||
maxB2 = Math.Max(maxB2, max[i + 1]);
|
||||
maxB3 = Math.Max(maxB3, max[i + 2]);
|
||||
maxB4 = Math.Max(maxB4, max[i + 3]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < missingElements; i++)
|
||||
{
|
||||
Generic4ByteData d = data[^(i + 1)];
|
||||
|
||||
minB1 = Math.Min(minB1, d.B1);
|
||||
minB2 = Math.Min(minB2, d.B2);
|
||||
minB3 = Math.Min(minB3, d.B3);
|
||||
minB4 = Math.Min(minB4, d.B4);
|
||||
|
||||
maxB1 = Math.Max(maxB1, d.B1);
|
||||
maxB2 = Math.Max(maxB2, d.B2);
|
||||
maxB3 = Math.Max(maxB3, d.B3);
|
||||
maxB4 = Math.Max(maxB4, d.B4);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (Generic4ByteData d in data)
|
||||
{
|
||||
minB1 = Math.Min(minB1, d.B1);
|
||||
minB2 = Math.Min(minB2, d.B2);
|
||||
minB3 = Math.Min(minB3, d.B3);
|
||||
minB4 = Math.Min(minB4, d.B4);
|
||||
|
||||
maxB1 = Math.Max(maxB1, d.B1);
|
||||
maxB2 = Math.Max(maxB2, d.B2);
|
||||
maxB3 = Math.Max(maxB3, d.B3);
|
||||
maxB4 = Math.Max(maxB4, d.B4);
|
||||
}
|
||||
}
|
||||
|
||||
return new Generic4ByteMinMax(minB1, maxB1, minB2, maxB2, minB3, maxB3, minB4, maxB4);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using System.Buffers;
|
||||
using System.Numerics;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public static partial class PixelHelper
|
||||
{
|
||||
#region Methods
|
||||
|
||||
public static IColor[] CreateColorPalette(IImage image, int paletteSize)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(image);
|
||||
|
||||
int dataLength = image.SizeInBytes;
|
||||
byte[] array = ArrayPool<byte>.Shared.Rent(dataLength);
|
||||
Span<byte> buffer = array.AsSpan()[..dataLength];
|
||||
try
|
||||
{
|
||||
image.CopyTo(buffer);
|
||||
return image.ColorFormat.CreateColorPalette(buffer, paletteSize);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(array);
|
||||
}
|
||||
}
|
||||
|
||||
public static T[] CreateColorPalette<T>(RefImage<T> image, int paletteSize)
|
||||
where T : unmanaged, IColor
|
||||
{
|
||||
int dataLength = image.Width * image.Height;
|
||||
T[] array = ArrayPool<T>.Shared.Rent(dataLength);
|
||||
Span<T> buffer = array.AsSpan()[..(dataLength)];
|
||||
try
|
||||
{
|
||||
image.CopyTo(buffer);
|
||||
return CreateColorPalette(buffer, paletteSize);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<T>.Shared.Return(array);
|
||||
}
|
||||
}
|
||||
|
||||
public static T[] CreateColorPalette<T>(ReadOnlySpan<T> colors, int paletteSize)
|
||||
where T : unmanaged, IColor
|
||||
{
|
||||
T[] buffer = ArrayPool<T>.Shared.Rent(colors.Length);
|
||||
try
|
||||
{
|
||||
Span<T> colorBuffer = buffer.AsSpan()[..colors.Length];
|
||||
colors.CopyTo(colorBuffer);
|
||||
|
||||
return CreateColorPalette(colorBuffer, paletteSize);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<T>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public static T[] CreateColorPalette<T>(Span<T> colors, int paletteSize)
|
||||
where T : unmanaged, IColor
|
||||
{
|
||||
int splits = BitOperations.Log2((uint)paletteSize);
|
||||
|
||||
Span<ColorCube<T>> cubes = new ColorCube<T>[1 << splits];
|
||||
cubes[0] = new ColorCube<T>(0, colors.Length, SortTarget.None);
|
||||
|
||||
int currentIndex = 0;
|
||||
for (int i = 0; i < splits; i++)
|
||||
{
|
||||
int currentCubeCount = 1 << i;
|
||||
Span<ColorCube<T>> currentCubes = cubes[..currentCubeCount];
|
||||
for (int j = 0; j < currentCubes.Length; j++)
|
||||
{
|
||||
currentCubes[j].Split(colors, out ColorCube<T> a, out ColorCube<T> b);
|
||||
currentCubes[j] = a;
|
||||
cubes[++currentIndex] = b;
|
||||
}
|
||||
}
|
||||
|
||||
T[] result = new T[cubes.Length];
|
||||
for (int i = 0; i < cubes.Length; i++)
|
||||
result[i] = Average<T>(cubes[i].Slice(colors));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
namespace HPPH;
|
||||
|
||||
public static unsafe partial class PixelHelper
|
||||
{
|
||||
#region Methods
|
||||
|
||||
[ColorSortGenerator("T", "R")]
|
||||
public static partial void SortByRed<T>(Span<T> colors)
|
||||
where T : unmanaged, IColor;
|
||||
|
||||
[ColorSortGenerator("T", "G")]
|
||||
public static partial void SortByGreen<T>(Span<T> colors)
|
||||
where T : unmanaged, IColor;
|
||||
|
||||
[ColorSortGenerator("T", "B")]
|
||||
public static partial void SortByBlue<T>(Span<T> colors)
|
||||
where T : unmanaged, IColor;
|
||||
|
||||
[ColorSortGenerator("T", "A")]
|
||||
public static partial void SortByAlpha<T>(Span<T> colors)
|
||||
where T : unmanaged, IColor;
|
||||
|
||||
[ColorSortGenerator("Generic3ByteData", "B1")]
|
||||
internal static partial void SortB1(Span<Generic3ByteData> colors);
|
||||
|
||||
[ColorSortGenerator("Generic3ByteData", "B2")]
|
||||
internal static partial void SortB2(Span<Generic3ByteData> colors);
|
||||
|
||||
[ColorSortGenerator("Generic3ByteData", "B3")]
|
||||
internal static partial void SortB3(Span<Generic3ByteData> colors);
|
||||
|
||||
[ColorSortGenerator("Generic4ByteData", "B1")]
|
||||
internal static partial void SortB1(Span<Generic4ByteData> colors);
|
||||
|
||||
[ColorSortGenerator("Generic4ByteData", "B2")]
|
||||
internal static partial void SortB2(Span<Generic4ByteData> colors);
|
||||
|
||||
[ColorSortGenerator("Generic4ByteData", "B3")]
|
||||
internal static partial void SortB3(Span<Generic4ByteData> colors);
|
||||
|
||||
[ColorSortGenerator("Generic4ByteData", "B4")]
|
||||
internal static partial void SortB4(Span<Generic4ByteData> colors);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
using System.Runtime.Intrinsics;
|
||||
using System.Buffers;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
public static unsafe partial class PixelHelper
|
||||
{
|
||||
#region Methods
|
||||
|
||||
public static ISum Sum(IImage image)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(image);
|
||||
|
||||
int dataLength = image.SizeInBytes;
|
||||
byte[] array = ArrayPool<byte>.Shared.Rent(dataLength);
|
||||
Span<byte> buffer = array.AsSpan()[..dataLength];
|
||||
try
|
||||
{
|
||||
image.CopyTo(buffer);
|
||||
return image.ColorFormat.Sum(buffer);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(array);
|
||||
}
|
||||
}
|
||||
|
||||
public static ISum Sum<T>(RefImage<T> image)
|
||||
where T : struct, IColor
|
||||
{
|
||||
int dataLength = image.Width * image.Height;
|
||||
T[] array = ArrayPool<T>.Shared.Rent(dataLength);
|
||||
Span<T> buffer = array.AsSpan()[..(dataLength)];
|
||||
try
|
||||
{
|
||||
image.CopyTo(buffer);
|
||||
return Sum<T>(buffer);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<T>.Shared.Return(array);
|
||||
}
|
||||
}
|
||||
|
||||
public static ISum Sum<T>(ReadOnlySpan<T> colors)
|
||||
where T : struct, IColor
|
||||
=> T.ColorFormat.Sum(MemoryMarshal.AsBytes(colors));
|
||||
|
||||
internal static ISum Sum<T, TSum>(ReadOnlySpan<T> colors)
|
||||
where T : struct, IColor
|
||||
where TSum : struct, ISum
|
||||
{
|
||||
if (colors == null) throw new ArgumentNullException(nameof(colors));
|
||||
|
||||
return T.ColorFormat.BytesPerPixel switch
|
||||
{
|
||||
// DarthAffe 05.07.2024: Important: The sum of 3-byte colors are result in 4 byte data!
|
||||
3 => Unsafe.BitCast<Generic4LongData, TSum>(Sum(MemoryMarshal.Cast<T, Generic3ByteData>(colors))),
|
||||
4 => Unsafe.BitCast<Generic4LongData, TSum>(Sum(MemoryMarshal.Cast<T, Generic4ByteData>(colors))),
|
||||
_ => throw new NotSupportedException("Data is not of a supported valid color-type.")
|
||||
};
|
||||
}
|
||||
|
||||
internal static Generic4LongData Sum(ReadOnlySpan<Generic3ByteData> data)
|
||||
{
|
||||
long b1Sum = 0, b2Sum = 0, b3Sum = 0;
|
||||
|
||||
const int ELEMENTS_PER_VECTOR = 32;
|
||||
int chunks;
|
||||
if (Avx2.IsSupported && ((chunks = data.Length / ELEMENTS_PER_VECTOR) > 0))
|
||||
{
|
||||
ReadOnlySpan<byte> blendMask1 =
|
||||
[
|
||||
255, 0, 0,
|
||||
255, 0, 0,
|
||||
255, 0, 0,
|
||||
255, 0, 0,
|
||||
255, 0, 0,
|
||||
255, 0, 0,
|
||||
255, 0, 0,
|
||||
255, 0, 0,
|
||||
255, 0, 0,
|
||||
255, 0, 0,
|
||||
255, 0
|
||||
];
|
||||
|
||||
ReadOnlySpan<byte> blendMask2 =
|
||||
[
|
||||
0, 255, 0,
|
||||
0, 255, 0,
|
||||
0, 255, 0,
|
||||
0, 255, 0,
|
||||
0, 255, 0,
|
||||
0, 255, 0,
|
||||
0, 255, 0,
|
||||
0, 255, 0,
|
||||
0, 255, 0,
|
||||
0, 255, 0,
|
||||
0, 255
|
||||
];
|
||||
|
||||
ReadOnlySpan<byte> blendMask3 =
|
||||
[
|
||||
0, 0, 255,
|
||||
0, 0, 255,
|
||||
0, 0, 255,
|
||||
0, 0, 255,
|
||||
0, 0, 255,
|
||||
0, 0, 255,
|
||||
0, 0, 255,
|
||||
0, 0, 255,
|
||||
0, 0, 255,
|
||||
0, 0, 255,
|
||||
0, 0
|
||||
];
|
||||
|
||||
Vector256<byte> blend1MaskVector = Vector256.LoadUnsafe(ref MemoryMarshal.GetReference(blendMask1));
|
||||
Vector256<byte> blend2MaskVector = Vector256.LoadUnsafe(ref MemoryMarshal.GetReference(blendMask2));
|
||||
Vector256<byte> blend3MaskVector = Vector256.LoadUnsafe(ref MemoryMarshal.GetReference(blendMask3));
|
||||
|
||||
Vector256<long> b1SumVector = Vector256<long>.Zero;
|
||||
Vector256<long> b2SumVector = Vector256<long>.Zero;
|
||||
Vector256<long> b3SumVector = Vector256<long>.Zero;
|
||||
|
||||
int missingElements = data.Length - (chunks * ELEMENTS_PER_VECTOR);
|
||||
|
||||
ReadOnlySpan<byte> dataBytes = MemoryMarshal.AsBytes(data);
|
||||
fixed (byte* bytePtr = dataBytes)
|
||||
{
|
||||
for (int i = 0; i < chunks; i++)
|
||||
{
|
||||
byte* basePtr = bytePtr + (i * 96);
|
||||
Vector256<byte> data1 = *(Vector256<byte>*)(basePtr);
|
||||
Vector256<byte> data2 = *(Vector256<byte>*)(basePtr + 32);
|
||||
Vector256<byte> data3 = *(Vector256<byte>*)(basePtr + 64);
|
||||
|
||||
Vector256<byte> vectorB1Blend1 = Avx2.BlendVariable(data2, data1, blend1MaskVector);
|
||||
Vector256<byte> vectorB2Blend1 = Avx2.BlendVariable(data2, data1, blend2MaskVector);
|
||||
Vector256<byte> vectorB3Blend1 = Avx2.BlendVariable(data2, data1, blend3MaskVector);
|
||||
|
||||
Vector256<byte> vectorB1Blend2 = Avx2.BlendVariable(vectorB1Blend1, data3, blend3MaskVector);
|
||||
Vector256<byte> vectorB2Blend2 = Avx2.BlendVariable(vectorB2Blend1, data3, blend1MaskVector);
|
||||
Vector256<byte> vectorB3Blend2 = Avx2.BlendVariable(vectorB3Blend1, data3, blend2MaskVector);
|
||||
|
||||
Vector256<long> sumB1 = Avx2.SumAbsoluteDifferences(vectorB1Blend2, Vector256<byte>.Zero).AsInt64();
|
||||
Vector256<long> sumB2 = Avx2.SumAbsoluteDifferences(vectorB2Blend2, Vector256<byte>.Zero).AsInt64();
|
||||
Vector256<long> sumB3 = Avx2.SumAbsoluteDifferences(vectorB3Blend2, Vector256<byte>.Zero).AsInt64();
|
||||
|
||||
b1SumVector = Avx2.Add(b1SumVector, sumB1);
|
||||
b2SumVector = Avx2.Add(b2SumVector, sumB2);
|
||||
b3SumVector = Avx2.Add(b3SumVector, sumB3);
|
||||
}
|
||||
}
|
||||
|
||||
b1Sum = b1SumVector[0] + b1SumVector[1] + b1SumVector[2] + b1SumVector[3];
|
||||
b2Sum = b2SumVector[0] + b2SumVector[1] + b2SumVector[2] + b2SumVector[3];
|
||||
b3Sum = b3SumVector[0] + b3SumVector[1] + b3SumVector[2] + b3SumVector[3];
|
||||
|
||||
for (int i = 0; i < missingElements; i++)
|
||||
{
|
||||
Generic3ByteData d = data[^(i + 1)];
|
||||
b1Sum += d.B1;
|
||||
b2Sum += d.B2;
|
||||
b3Sum += d.B3;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (Generic3ByteData d in data)
|
||||
{
|
||||
b1Sum += d.B1;
|
||||
b2Sum += d.B2;
|
||||
b3Sum += d.B3;
|
||||
}
|
||||
}
|
||||
|
||||
return new Generic4LongData(b1Sum, b2Sum, b3Sum, data.Length * 255);
|
||||
}
|
||||
|
||||
internal static Generic4LongData Sum(ReadOnlySpan<Generic4ByteData> data)
|
||||
{
|
||||
long b1Sum, b2Sum, b3Sum, b4Sum;
|
||||
int i = 0;
|
||||
|
||||
if (Avx2.IsSupported && (data.Length >= 8))
|
||||
{
|
||||
ReadOnlySpan<byte> avx2ShuffleMask =
|
||||
[
|
||||
// Byte 1
|
||||
15, 11, 7, 3,
|
||||
// Byte 2
|
||||
14, 10, 6, 2,
|
||||
// Byte 3
|
||||
13, 9, 5, 1,
|
||||
// Byte 4
|
||||
12, 8, 4, 0
|
||||
];
|
||||
|
||||
ReadOnlySpan<int> avx2ControlData =
|
||||
[
|
||||
// Byte 1
|
||||
7, 3,
|
||||
// Byte 2
|
||||
6, 2,
|
||||
// Byte 3
|
||||
5, 1,
|
||||
// Byte 4
|
||||
4, 0
|
||||
];
|
||||
|
||||
Vector256<long> rgbaSum64 = Vector256<long>.Zero;
|
||||
|
||||
ReadOnlySpan<byte> dataBytes = MemoryMarshal.AsBytes(data);
|
||||
fixed (byte* bytePtr = dataBytes)
|
||||
fixed (int* controlPtr = avx2ControlData)
|
||||
fixed (byte* maskPtr = avx2ShuffleMask)
|
||||
{
|
||||
Vector256<byte> avx2ShuffleMaskVector = Avx2.BroadcastVector128ToVector256(maskPtr);
|
||||
|
||||
for (int j = 0; j < (data.Length / 8); j++, i += 8)
|
||||
{
|
||||
Vector256<byte> chunk = *(Vector256<byte>*)(bytePtr + (i * 4));
|
||||
Vector256<byte> deinterleaved = Avx2.Shuffle(chunk, avx2ShuffleMaskVector);
|
||||
Vector256<int> deinterleaved2 = Avx2.PermuteVar8x32(deinterleaved.AsInt32(), *(Vector256<int>*)controlPtr);
|
||||
Vector256<long> sum = Avx2.SumAbsoluteDifferences(deinterleaved2.AsByte(), Vector256<byte>.Zero).AsInt64();
|
||||
rgbaSum64 = Avx2.Add(rgbaSum64, sum);
|
||||
}
|
||||
}
|
||||
|
||||
Vector128<long> b1B2Sum = rgbaSum64.GetLower();
|
||||
Vector128<long> b3B4Sum = rgbaSum64.GetUpper();
|
||||
|
||||
b1Sum = b1B2Sum.GetLower()[0];
|
||||
b2Sum = b1B2Sum.GetUpper()[0];
|
||||
b3Sum = b3B4Sum.GetLower()[0];
|
||||
b4Sum = b3B4Sum.GetUpper()[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
b1Sum = b2Sum = b3Sum = b4Sum = 0;
|
||||
}
|
||||
|
||||
for (; i < data.Length; i++)
|
||||
{
|
||||
b1Sum += data[i].B1;
|
||||
b2Sum += data[i].B2;
|
||||
b3Sum += data[i].B3;
|
||||
b4Sum += data[i].B4;
|
||||
}
|
||||
|
||||
return new Generic4LongData(b1Sum, b2Sum, b3Sum, b4Sum);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
[SkipLocalsInit]
|
||||
public static partial class PixelHelper;
|
||||
@@ -0,0 +1,72 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
[SkipLocalsInit]
|
||||
internal struct ColorCube<T>
|
||||
where T : unmanaged, IColor
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly int _offset;
|
||||
private readonly int _length;
|
||||
private SortTarget _sortOrder;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
internal ColorCube(int offset, int length, SortTarget sortOrder)
|
||||
{
|
||||
this._offset = offset;
|
||||
this._length = length;
|
||||
this._sortOrder = sortOrder;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal Span<T> Slice(Span<T> fullColorList) => fullColorList.Slice(_offset, _length);
|
||||
|
||||
internal void Split(Span<T> fullColorList, out ColorCube<T> a, out ColorCube<T> b)
|
||||
{
|
||||
OrderColors(Slice(fullColorList), _sortOrder);
|
||||
|
||||
int median = _length / 2;
|
||||
|
||||
a = new ColorCube<T>(_offset, median, _sortOrder);
|
||||
b = new ColorCube<T>(_offset + median, _length - median, _sortOrder);
|
||||
}
|
||||
|
||||
private void OrderColors(Span<T> colors, SortTarget preOrdered)
|
||||
{
|
||||
if (colors.Length < 2) return;
|
||||
IMinMax colorRanges = PixelHelper.MinMax<T>(colors);
|
||||
|
||||
if ((colorRanges.RedRange > colorRanges.GreenRange) && (colorRanges.RedRange > colorRanges.BlueRange))
|
||||
{
|
||||
if (preOrdered != SortTarget.Red)
|
||||
PixelHelper.SortByRed(colors);
|
||||
|
||||
_sortOrder = SortTarget.Red;
|
||||
}
|
||||
else if (colorRanges.GreenRange > colorRanges.BlueRange)
|
||||
{
|
||||
if (preOrdered != SortTarget.Green)
|
||||
PixelHelper.SortByGreen(colors);
|
||||
|
||||
_sortOrder = SortTarget.Green;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (preOrdered != SortTarget.Blue)
|
||||
PixelHelper.SortByBlue(colors);
|
||||
|
||||
_sortOrder = SortTarget.Blue;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace HPPH;
|
||||
|
||||
internal enum SortTarget
|
||||
{
|
||||
None, Red, Green, Blue
|
||||
}
|
||||
Reference in new issue
Block a user