diff --git a/HPPH.Benchmark/HPPH.Benchmark.csproj b/HPPH.Benchmark/HPPH.Benchmark.csproj
new file mode 100644
index 0000000..d519e0a
--- /dev/null
+++ b/HPPH.Benchmark/HPPH.Benchmark.csproj
@@ -0,0 +1,19 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/HPPH.Benchmark/Program.cs b/HPPH.Benchmark/Program.cs
new file mode 100644
index 0000000..a751bf5
--- /dev/null
+++ b/HPPH.Benchmark/Program.cs
@@ -0,0 +1 @@
+Console.WriteLine("Empty");
\ No newline at end of file
diff --git a/HPPH.DotSettings b/HPPH.DotSettings
new file mode 100644
index 0000000..8860971
--- /dev/null
+++ b/HPPH.DotSettings
@@ -0,0 +1,3 @@
+
+ BGR
+ BGRA
\ No newline at end of file
diff --git a/HPPH.Generators/ColorFormatData.cs b/HPPH.Generators/ColorFormatData.cs
new file mode 100644
index 0000000..02625b7
--- /dev/null
+++ b/HPPH.Generators/ColorFormatData.cs
@@ -0,0 +1,50 @@
+using System.Text;
+
+namespace HPPH.Generators;
+
+internal readonly struct ColorFormatData(string typeName, int bpp, char firstEntry, char secondEntry, char thirdEntry, char fourthEntry)
+{
+ public readonly string TypeName = typeName;
+ public readonly int Bpp = bpp;
+ public readonly string FirstEntry = firstEntry.ToString().ToLowerInvariant();
+ public readonly string SecondEntry = secondEntry.ToString().ToLowerInvariant();
+ public readonly string ThirdEntry = thirdEntry.ToString().ToLowerInvariant();
+ public readonly string FourthEntry = fourthEntry.ToString().ToLowerInvariant();
+
+ public string FirstEntryName => GetEntryName(FirstEntry);
+ public string SecondEntryName => GetEntryName(SecondEntry);
+ public string ThirdEntryName => GetEntryName(ThirdEntry);
+ public string FourthEntryName => GetEntryName(FourthEntry);
+
+ public string Format
+ {
+ get
+ {
+ StringBuilder sb = new();
+
+ if (!string.IsNullOrWhiteSpace(FirstEntry))
+ sb.Append(FirstEntry);
+
+ if (!string.IsNullOrWhiteSpace(SecondEntry))
+ sb.Append(SecondEntry);
+
+ if (!string.IsNullOrWhiteSpace(ThirdEntry))
+ sb.Append(ThirdEntry);
+
+ if (!string.IsNullOrWhiteSpace(FourthEntry))
+ sb.Append(FourthEntry);
+
+ return sb.ToString().ToUpper();
+ }
+ }
+
+ private static string GetEntryName(string entry)
+ => entry switch
+ {
+ "a" => "Alpha",
+ "r" => "Red",
+ "g" => "Green",
+ "b" => "Blue",
+ _ => string.Empty
+ };
+}
\ No newline at end of file
diff --git a/HPPH.Generators/ColorFormatSourceGenerator.cs b/HPPH.Generators/ColorFormatSourceGenerator.cs
new file mode 100644
index 0000000..42e7b72
--- /dev/null
+++ b/HPPH.Generators/ColorFormatSourceGenerator.cs
@@ -0,0 +1,90 @@
+using System.Collections.Immutable;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Text;
+
+namespace HPPH.Generators;
+
+[Generator(LanguageNames.CSharp)]
+public class ColorSourceGenerator : IIncrementalGenerator
+{
+ #region Properties & Fields
+
+ private static readonly Regex COLOR_FORMAT_REGEX = new("^Color(?[ARGB]{3,4})$", RegexOptions.Compiled);
+
+ private static readonly IGeneratorFeature[] FEATURES =
+ [
+ new Colors(),
+ new Average(),
+ new MinMax(),
+ new Sum(),
+ new Quantize()
+ ];
+
+ private const string COLOR_GENERATOR_ATTRIBUTE_SOURCE = """
+ namespace HPPH;
+
+ [AttributeUsage(AttributeTargets.Struct)]
+ internal class ColorGeneratorAttribute : Attribute;
+ """;
+
+ #endregion
+
+ #region Methods
+
+ public void Initialize(IncrementalGeneratorInitializationContext context)
+ {
+ // if (!Debugger.IsAttached)
+ // Debugger.Launch();
+
+ context.RegisterPostInitializationOutput(ctx => ctx.AddSource("ColorGeneratorAttribute.g.cs", SourceText.From(COLOR_GENERATOR_ATTRIBUTE_SOURCE, Encoding.UTF8)));
+
+ IncrementalValueProvider> classes = context.SyntaxProvider
+ .ForAttributeWithMetadataName("HPPH.ColorGeneratorAttribute", static (_, __) => true, Transform)
+ .Where(type => type.HasValue)
+ .Select((data, _) => data!.Value)
+ .Collect();
+ context.RegisterSourceOutput(classes, GenerateCode);
+ }
+
+ private static ColorFormatData? Transform(GeneratorAttributeSyntaxContext context, CancellationToken cancellationToken)
+ {
+ if (context.TargetNode is not StructDeclarationSyntax structDeclaration) return null;
+ if (context.SemanticModel.GetDeclaredSymbol(structDeclaration, cancellationToken) is not ITypeSymbol type) return null;
+
+ Match m = COLOR_FORMAT_REGEX.Match(type.Name);
+ if (!m.Success) return null;
+
+ string colorFormat = m.Groups["colorFormat"].Value;
+ if (colorFormat.Distinct().Count() != colorFormat.Length) return null;
+
+ return new ColorFormatData(type.Name,
+ colorFormat.Length,
+ colorFormat.Length > 0 ? colorFormat[0] : ' ',
+ colorFormat.Length > 1 ? colorFormat[1] : ' ',
+ colorFormat.Length > 2 ? colorFormat[2] : ' ',
+ colorFormat.Length > 3 ? colorFormat[3] : ' ');
+ }
+
+ private static void GenerateCode(SourceProductionContext context, ImmutableArray colorFormats)
+ {
+ if (colorFormats.IsDefaultOrEmpty) return;
+
+ foreach (ColorFormatData formatData in colorFormats)
+ {
+ foreach (IGeneratorFeature feature in FEATURES)
+ foreach ((string name, string source) in feature.GenerateFor(formatData))
+ context.AddSource($"{name}.g.cs", SourceText.From(source, Encoding.UTF8));
+ }
+
+ foreach (IGeneratorFeature feature in FEATURES)
+ foreach ((string name, string source) in feature.GenerateFor(colorFormats))
+ context.AddSource($"{name}.g.cs", SourceText.From(source, Encoding.UTF8));
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/HPPH.Generators/ColorSortData.cs b/HPPH.Generators/ColorSortData.cs
new file mode 100644
index 0000000..730c498
--- /dev/null
+++ b/HPPH.Generators/ColorSortData.cs
@@ -0,0 +1,3 @@
+namespace HPPH.Generators;
+
+internal record struct ColorSortData(string Namespace, string Class, string ClassModifiers, string Signature, string DataTypeName, string SortValueName);
\ No newline at end of file
diff --git a/HPPH.Generators/ColorSortSourceGenerator.cs b/HPPH.Generators/ColorSortSourceGenerator.cs
new file mode 100644
index 0000000..c05c75d
--- /dev/null
+++ b/HPPH.Generators/ColorSortSourceGenerator.cs
@@ -0,0 +1,135 @@
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Text;
+
+namespace HPPH.Generators;
+
+[Generator(LanguageNames.CSharp)]
+public class ColorSortSourceGenerator : IIncrementalGenerator
+{
+ #region Properties & Fields
+
+ private const string SORT_GENERATOR_ATTRIBUTE_SOURCE = """
+ namespace HPPH;
+
+ [AttributeUsage(AttributeTargets.Method)]
+ internal class ColorSortGeneratorAttribute(string dataTypeName, string sortValueName) : Attribute
+ {
+ public string DataTypeName { get; } = dataTypeName;
+ public string SortValueName { get; } = sortValueName;
+ }
+ """;
+
+ #endregion
+
+ #region Methods
+
+ public void Initialize(IncrementalGeneratorInitializationContext context)
+ {
+ //if (!Debugger.IsAttached)
+ // Debugger.Launch();
+
+ context.RegisterPostInitializationOutput(ctx => ctx.AddSource("ColorSortGeneratorAttribute.g.cs", SourceText.From(SORT_GENERATOR_ATTRIBUTE_SOURCE, Encoding.UTF8)));
+
+ IncrementalValueProvider> classes = context.SyntaxProvider
+ .ForAttributeWithMetadataName("HPPH.ColorSortGeneratorAttribute", static (_, __) => true, Transform)
+ .Where(type => type.HasValue)
+ .Select((data, _) => data!.Value)
+ .Collect();
+ context.RegisterSourceOutput(classes, GenerateCode);
+ }
+
+ private static ColorSortData? Transform(GeneratorAttributeSyntaxContext context, CancellationToken cancellationToken)
+ {
+ if (context.TargetNode is not MethodDeclarationSyntax methodDeclaration) return null;
+ if (context.SemanticModel.GetDeclaredSymbol(methodDeclaration, cancellationToken) is not IMethodSymbol method) return null;
+ if (!method.IsPartialDefinition) return null;
+
+ foreach (AttributeData attribute in method.GetAttributes())
+ {
+ if (attribute.AttributeClass?.Name != "ColorSortGeneratorAttribute")
+ continue;
+
+ if (!attribute.ConstructorArguments.IsEmpty)
+ {
+ ImmutableArray args = attribute.ConstructorArguments;
+ if (args.Any(arg => arg.Kind == TypedConstantKind.Error)) break;
+
+ return new ColorSortData(method.ContainingNamespace.Name, method.ContainingType.Name, ((ClassDeclarationSyntax)methodDeclaration.Parent!).Modifiers.ToString(), $"{methodDeclaration.Modifiers} {methodDeclaration.ReturnType} {methodDeclaration.Identifier}{methodDeclaration.TypeParameterList}{methodDeclaration.ParameterList} {methodDeclaration.ConstraintClauses}", args[0].Value!.ToString(), args[1].Value!.ToString());
+ }
+ }
+
+ return null;
+ }
+
+ private static void GenerateCode(SourceProductionContext context, ImmutableArray colorSorts)
+ {
+ if (colorSorts.IsDefaultOrEmpty) return;
+
+ Dictionary<(string, string, string), string> sourceMapping = colorSorts.Select(x => (x.Namespace, x.Class, x.ClassModifiers)).Distinct().ToDictionary(x => x, _ => string.Empty);
+
+ foreach (ColorSortData colorSort in colorSorts)
+ sourceMapping[(colorSort.Namespace, colorSort.Class, colorSort.ClassModifiers)] += GenerateSortMethodSource(colorSort);
+
+ foreach (KeyValuePair<(string @namespace, string @class, string classModifier), string> data in sourceMapping)
+ {
+ context.AddSource($"{data.Key.@class}.g.cs", SourceText.From($$"""
+ using System.Buffers;
+
+ namespace {{data.Key.@namespace}};
+
+ {{data.Key.classModifier}} class {{data.Key.@class}}
+ {
+ {{data.Value}}
+ }
+ """, Encoding.UTF8));
+ }
+ }
+
+ private static string GenerateSortMethodSource(ColorSortData colorSort)
+ => $$"""
+ {{colorSort.Signature}}
+ {
+ fixed ({{colorSort.DataTypeName}}* ptr = colors)
+ {
+ {{colorSort.DataTypeName}}* end = ptr + colors.Length;
+
+ Span histogram = stackalloc int[256];
+ histogram.Clear();
+ for ({{colorSort.DataTypeName}}* color = ptr; color < end; color++)
+ histogram[(*color).{{colorSort.SortValueName}}]++;
+
+ {{colorSort.DataTypeName}}[] bucketsArray = ArrayPool<{{colorSort.DataTypeName}}>.Shared.Rent(colors.Length);
+ try
+ {
+ Span<{{colorSort.DataTypeName}}> buckets = bucketsArray.AsSpan()[..colors.Length];
+ Span currentBucketIndex = stackalloc int[256];
+
+ int offset = 0;
+ for (int i = 0; i < histogram.Length; i++)
+ {
+ currentBucketIndex[i] = offset;
+ offset += histogram[i];
+ }
+
+ for ({{colorSort.DataTypeName}}* color = ptr; color < end; color++)
+ buckets[currentBucketIndex[(*color).{{colorSort.SortValueName}}]++] = (*color);
+
+ buckets.CopyTo(colors);
+ }
+ finally
+ {
+ ArrayPool<{{colorSort.DataTypeName}}>.Shared.Return(bucketsArray);
+ }
+ }
+ }
+
+ """;
+
+ #endregion
+}
\ No newline at end of file
diff --git a/HPPH.Generators/Features/Average.cs b/HPPH.Generators/Features/Average.cs
new file mode 100644
index 0000000..0441165
--- /dev/null
+++ b/HPPH.Generators/Features/Average.cs
@@ -0,0 +1,47 @@
+using System.Collections.Generic;
+using System.Collections.Immutable;
+
+namespace HPPH.Generators;
+
+internal class Average : IGeneratorFeature
+{
+ public IEnumerable<(string name, string source)> GenerateFor(ColorFormatData colorFormat)
+ {
+ yield return ($"ColorFormat{colorFormat.Format}.Average", GenerateColorFormatAverage(colorFormat));
+ }
+
+ public IEnumerable<(string name, string source)> GenerateFor(ImmutableArray colorFormats)
+ {
+ yield return ("IColorFormat.Average", GenerateColorFormatInterfaceAverage());
+ }
+
+ private static string GenerateColorFormatAverage(ColorFormatData colorFormat)
+ {
+ return $$"""
+ using System.Runtime.InteropServices;
+
+ namespace HPPH;
+
+ public sealed partial class ColorFormat{{colorFormat.Format}}
+ {
+ #region Methods
+
+ unsafe IColor IColorFormat.Average(ReadOnlySpan data) => PixelHelper.Average(MemoryMarshal.Cast(data));
+
+ #endregion
+ }
+ """;
+ }
+
+ private static string GenerateColorFormatInterfaceAverage()
+ {
+ return """
+ namespace HPPH;
+
+ public partial interface IColorFormat
+ {
+ internal IColor Average(ReadOnlySpan data);
+ }
+ """;
+ }
+}
\ No newline at end of file
diff --git a/HPPH.Generators/Features/Colors.cs b/HPPH.Generators/Features/Colors.cs
new file mode 100644
index 0000000..d60bc18
--- /dev/null
+++ b/HPPH.Generators/Features/Colors.cs
@@ -0,0 +1,200 @@
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Text;
+
+namespace HPPH.Generators;
+
+internal class Colors : IGeneratorFeature
+{
+ public IEnumerable<(string name, string source)> GenerateFor(ColorFormatData colorFormat)
+ {
+ string colorStructCode = GenerateColorStructCode(colorFormat);
+ if (!string.IsNullOrWhiteSpace(colorStructCode))
+ {
+ yield return (colorFormat.TypeName, colorStructCode!);
+ yield return ($"ColorFormat{colorFormat.Format}", GenerateColorFormatCode(colorFormat));
+ }
+ }
+
+ public IEnumerable<(string name, string source)> GenerateFor(ImmutableArray colorFormats)
+ {
+ yield return ("IColorFormat.Instances.cs", GenerateColorFormats(colorFormats));
+ }
+
+ private static string GenerateColorStructCode(ColorFormatData colorFormat)
+ => colorFormat.Bpp switch
+ {
+ 3 => $$"""
+ // ReSharper disable ConvertToAutoProperty
+ // ReSharper disable ConvertToAutoPropertyWhenPossible
+ // ReSharper disable ReplaceWithPrimaryConstructorParameter
+
+ using System.Diagnostics;
+ using System.Runtime.InteropServices;
+
+ namespace HPPH;
+
+ ///
+ /// Represents a color in 24 bit {{colorFormat.Format}}-format.
+ ///
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The {{colorFormat.FirstEntryName}}-component of the color.
+ /// The {{colorFormat.SecondEntryName}}-component of the color.
+ /// The {{colorFormat.ThirdEntryName}}-component of the color.
+ [DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")]
+ [StructLayout(LayoutKind.Sequential)]
+ public readonly partial struct {{colorFormat.TypeName}}(byte {{colorFormat.FirstEntry}}, byte {{colorFormat.SecondEntry}}, byte {{colorFormat.ThirdEntry}}): IColor
+ {
+ #region Properties & Fields
+
+ ///
+ public static IColorFormat ColorFormat => IColorFormat.{{colorFormat.Format}};
+
+ private readonly byte _{{colorFormat.FirstEntry}} = {{colorFormat.FirstEntry}};
+ private readonly byte _{{colorFormat.SecondEntry}} = {{colorFormat.SecondEntry}};
+ private readonly byte _{{colorFormat.ThirdEntry}} = {{colorFormat.ThirdEntry}};
+
+ ///
+ public byte R => _r;
+
+ ///
+ public byte G => _g;
+
+ ///
+ public byte B => _b;
+
+ ///
+ public byte A => byte.MaxValue;
+
+ #endregion
+
+ #region Methods
+
+ ///
+ public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
+
+ ///
+ public static IColor Create(byte r, byte g, byte b, byte a) => new {{colorFormat.TypeName}}({{colorFormat.FirstEntry}}, {{colorFormat.SecondEntry}}, {{colorFormat.ThirdEntry}});
+
+ #endregion
+ }
+
+ """,
+
+ 4 => $$"""
+ // ReSharper disable ConvertToAutoProperty
+ // ReSharper disable ConvertToAutoPropertyWhenPossible
+ // ReSharper disable ReplaceWithPrimaryConstructorParameter
+
+ using System.Diagnostics;
+ using System.Runtime.InteropServices;
+
+ namespace HPPH;
+
+ ///
+ /// Represents a color in 32 bit {{colorFormat.Format}}-format.
+ ///
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The {{colorFormat.FirstEntryName}}-component of the color.
+ /// The {{colorFormat.SecondEntryName}}-component of the color.
+ /// The {{colorFormat.ThirdEntryName}}-component of the color.
+ /// The {{colorFormat.FourthEntryName}}-component of the color.
+ [DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")]
+ [StructLayout(LayoutKind.Sequential)]
+ public readonly partial struct {{colorFormat.TypeName}}(byte {{colorFormat.FirstEntry}}, byte {{colorFormat.SecondEntry}}, byte {{colorFormat.ThirdEntry}}, byte {{colorFormat.FourthEntry}}) : IColor
+ {
+ #region Properties & Fields
+
+ ///
+ public static IColorFormat ColorFormat => IColorFormat.{{colorFormat.Format}};
+
+ private readonly byte _{{colorFormat.FirstEntry}} = {{colorFormat.FirstEntry}};
+ private readonly byte _{{colorFormat.SecondEntry}} = {{colorFormat.SecondEntry}};
+ private readonly byte _{{colorFormat.ThirdEntry}} = {{colorFormat.ThirdEntry}};
+ private readonly byte _{{colorFormat.FourthEntry}} = {{colorFormat.FourthEntry}};
+
+ ///
+ public byte R => _r;
+
+ ///
+ public byte G => _g;
+
+ ///
+ public byte B => _b;
+
+ ///
+ public byte A => _a;
+
+ #endregion
+
+ #region Methods
+
+ ///
+ public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
+
+ ///
+ public static IColor Create(byte r, byte g, byte b, byte a) => new {{colorFormat.TypeName}}({{colorFormat.FirstEntry}}, {{colorFormat.SecondEntry}}, {{colorFormat.ThirdEntry}}, {{colorFormat.FourthEntry}});
+
+ #endregion
+ }
+
+ """,
+ _ => null
+ };
+
+ private static string GenerateColorFormatCode(ColorFormatData colorFormat)
+ {
+ return $$"""
+ namespace HPPH;
+
+ public sealed partial class ColorFormat{{colorFormat.Format}} : IColorFormat
+ {
+ #region Properties & Fields
+
+ public static ColorFormat{{colorFormat.Format}} Instance { get; } = new();
+
+ public int BytesPerPixel => {{colorFormat.Bpp}};
+
+ public string Name => "{{colorFormat.Format}}";
+
+ #endregion
+
+ #region Constructors
+
+ private ColorFormat{{colorFormat.Format}}() {}
+
+ #endregion
+ }
+ """;
+ }
+
+ private static string GenerateColorFormats(ImmutableArray colorFormats)
+ {
+ StringBuilder sb = new();
+ sb.AppendLine("""
+ namespace HPPH;
+
+ public partial interface IColorFormat
+ {
+ #region Instances
+ """);
+
+ sb.AppendLine();
+
+ foreach (ColorFormatData colorFormat in colorFormats)
+ sb.AppendLine($" public static ColorFormat{colorFormat.Format} {colorFormat.Format} => ColorFormat{colorFormat.Format}.Instance;");
+
+ sb.AppendLine();
+
+ sb.AppendLine("""
+ #endregion
+ }
+ """);
+
+ return sb.ToString();
+ }
+}
\ No newline at end of file
diff --git a/HPPH.Generators/Features/IGeneratorFeature.cs b/HPPH.Generators/Features/IGeneratorFeature.cs
new file mode 100644
index 0000000..9c7a8e2
--- /dev/null
+++ b/HPPH.Generators/Features/IGeneratorFeature.cs
@@ -0,0 +1,10 @@
+using System.Collections.Generic;
+using System.Collections.Immutable;
+
+namespace HPPH.Generators;
+
+internal interface IGeneratorFeature
+{
+ public IEnumerable<(string name, string source)> GenerateFor(ColorFormatData colorFormat);
+ public IEnumerable<(string name, string source)> GenerateFor(ImmutableArray colorFormats);
+}
\ No newline at end of file
diff --git a/HPPH.Generators/Features/MinMax.cs b/HPPH.Generators/Features/MinMax.cs
new file mode 100644
index 0000000..c84aeda
--- /dev/null
+++ b/HPPH.Generators/Features/MinMax.cs
@@ -0,0 +1,162 @@
+using System.Collections.Generic;
+using System.Collections.Immutable;
+
+namespace HPPH.Generators;
+
+internal class MinMax : IGeneratorFeature
+{
+ public IEnumerable<(string name, string source)> GenerateFor(ColorFormatData colorFormat)
+ {
+ yield return ($"MinMax{colorFormat.Format}", GenerateMinMaxStruct(colorFormat));
+ yield return ($"ColorFormat{colorFormat.Format}.MinMax", GenerateColorFormatMinMax(colorFormat));
+ }
+
+ public IEnumerable<(string name, string source)> GenerateFor(ImmutableArray colorFormats)
+ {
+ yield return ("IColorFormat.MinMax", GenerateColorFormatInterfaceMinMax());
+ }
+
+ private static string GenerateMinMaxStruct(ColorFormatData colorFormat)
+ => colorFormat.Bpp switch
+ {
+ 3 => $$"""
+ // 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 MinMax{{colorFormat.Format}}(byte {{colorFormat.FirstEntry}}Min, byte {{colorFormat.FirstEntry}}Max, byte {{colorFormat.SecondEntry}}Min, byte {{colorFormat.SecondEntry}}Max, byte {{colorFormat.ThirdEntry}}Min, byte {{colorFormat.ThirdEntry}}Max) : IMinMax
+ {
+ #region Properties & Fields
+
+ private readonly byte _{{colorFormat.FirstEntry}}Min = {{colorFormat.FirstEntry}}Min;
+ private readonly byte _{{colorFormat.FirstEntry}}Max = {{colorFormat.FirstEntry}}Max;
+
+ private readonly byte _{{colorFormat.SecondEntry}}Min = {{colorFormat.SecondEntry}}Min;
+ private readonly byte _{{colorFormat.SecondEntry}}Max = {{colorFormat.SecondEntry}}Max;
+
+ private readonly byte _{{colorFormat.ThirdEntry}}Min = {{colorFormat.ThirdEntry}}Min;
+ private readonly byte _{{colorFormat.ThirdEntry}}Max = {{colorFormat.ThirdEntry}}Max;
+
+ 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
+
+ ///
+ public override string ToString() => $"[A: {AlphaMin}-{AlphaMax}, R: {RedMin}-{RedMax}, G: {GreenMin}-{GreenMax}, B: {BlueMin}-{BlueMax}]";
+
+ #endregion
+ }
+
+ """,
+
+ 4 => $$"""
+ // 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 MinMax{{colorFormat.Format}}(byte {{colorFormat.FirstEntry}}Min, byte {{colorFormat.FirstEntry}}Max, byte {{colorFormat.SecondEntry}}Min, byte {{colorFormat.SecondEntry}}Max, byte {{colorFormat.ThirdEntry}}Min, byte {{colorFormat.ThirdEntry}}Max, byte {{colorFormat.FourthEntry}}Min, byte {{colorFormat.FourthEntry}}Max) : IMinMax
+ {
+ #region Properties & Fields
+
+ private readonly byte _{{colorFormat.FirstEntry}}Min = {{colorFormat.FirstEntry}}Min;
+ private readonly byte _{{colorFormat.FirstEntry}}Max = {{colorFormat.FirstEntry}}Max;
+
+ private readonly byte _{{colorFormat.SecondEntry}}Min = {{colorFormat.SecondEntry}}Min;
+ private readonly byte _{{colorFormat.SecondEntry}}Max = {{colorFormat.SecondEntry}}Max;
+
+ private readonly byte _{{colorFormat.ThirdEntry}}Min = {{colorFormat.ThirdEntry}}Min;
+ private readonly byte _{{colorFormat.ThirdEntry}}Max = {{colorFormat.ThirdEntry}}Max;
+
+ private readonly byte _{{colorFormat.FourthEntry}}Min = {{colorFormat.FourthEntry}}Min;
+ private readonly byte _{{colorFormat.FourthEntry}}Max = {{colorFormat.FourthEntry}}Max;
+
+ 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
+
+ ///
+ public override string ToString() => $"[A: {AlphaMin}-{AlphaMax}, R: {RedMin}-{RedMax}, G: {GreenMin}-{GreenMax}, B: {BlueMin}-{BlueMax}]";
+
+ #endregion
+ }
+
+ """,
+ _ => null
+ };
+
+ private static string GenerateColorFormatMinMax(ColorFormatData colorFormat)
+ {
+ return $$"""
+ using System.Runtime.InteropServices;
+
+ namespace HPPH;
+
+ public sealed partial class ColorFormat{{colorFormat.Format}}
+ {
+ #region Methods
+
+ unsafe IMinMax IColorFormat.MinMax(ReadOnlySpan data) => PixelHelper.MinMax(MemoryMarshal.Cast(data));
+
+ #endregion
+ }
+ """;
+ }
+
+ private static string GenerateColorFormatInterfaceMinMax()
+ {
+ return """
+ namespace HPPH;
+
+ public partial interface IColorFormat
+ {
+ internal IMinMax MinMax(ReadOnlySpan data);
+ }
+ """;
+ }
+}
\ No newline at end of file
diff --git a/HPPH.Generators/Features/Quantize.cs b/HPPH.Generators/Features/Quantize.cs
new file mode 100644
index 0000000..c0b6224
--- /dev/null
+++ b/HPPH.Generators/Features/Quantize.cs
@@ -0,0 +1,56 @@
+using System.Collections.Generic;
+using System.Collections.Immutable;
+
+namespace HPPH.Generators;
+
+internal class Quantize : IGeneratorFeature
+{
+ public IEnumerable<(string name, string source)> GenerateFor(ColorFormatData colorFormat)
+ {
+ yield return ($"ColorFormat{colorFormat.Format}.Quantize", GenerateColorFormatQuantize(colorFormat));
+ }
+
+ public IEnumerable<(string name, string source)> GenerateFor(ImmutableArray colorFormats)
+ {
+ yield return ("IColorFormat.Quantize", GenerateColorFormatInterfaceQuantize());
+ }
+
+ private static string GenerateColorFormatQuantize(ColorFormatData colorFormat)
+ {
+ return $$"""
+ using System.Runtime.InteropServices;
+
+ namespace HPPH;
+
+ public sealed partial class ColorFormat{{colorFormat.Format}}
+ {
+ #region Methods
+
+ unsafe IColor[] IColorFormat.CreateColorPalette(ReadOnlySpan data, int paletteSize)
+ {
+ Color{{colorFormat.Format}}[] colors = PixelHelper.CreateColorPalette(MemoryMarshal.Cast(data), paletteSize);
+
+ IColor[] result = new IColor[colors.Length];
+ for(int i = 0; i < colors.Length; i++)
+ result[i] = colors[i];
+
+ return result;
+ }
+
+ #endregion
+ }
+ """;
+ }
+
+ private static string GenerateColorFormatInterfaceQuantize()
+ {
+ return """
+ namespace HPPH;
+
+ public partial interface IColorFormat
+ {
+ internal IColor[] CreateColorPalette(ReadOnlySpan data, int paletteSize);
+ }
+ """;
+ }
+}
\ No newline at end of file
diff --git a/HPPH.Generators/Features/Sum.cs b/HPPH.Generators/Features/Sum.cs
new file mode 100644
index 0000000..0b869bb
--- /dev/null
+++ b/HPPH.Generators/Features/Sum.cs
@@ -0,0 +1,129 @@
+using System.Collections.Generic;
+using System.Collections.Immutable;
+
+namespace HPPH.Generators;
+
+internal class Sum : IGeneratorFeature
+{
+ public IEnumerable<(string name, string source)> GenerateFor(ColorFormatData colorFormat)
+ {
+ yield return ($"Sum{colorFormat.Format}", GenerateSumStruct(colorFormat));
+ yield return ($"ColorFormat{colorFormat.Format}.Sum", GenerateColorFormatSum(colorFormat));
+ }
+
+ public IEnumerable<(string name, string source)> GenerateFor(ImmutableArray colorFormats)
+ {
+ yield return ("IColorFormat.Sum", GenerateColorFormatInterfaceSum());
+ }
+
+ private static string GenerateSumStruct(ColorFormatData colorFormat)
+ => colorFormat.Bpp switch
+ {
+ 3 => $$"""
+ // 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 Sum{{colorFormat.Format}}(long {{colorFormat.FirstEntry}}, long {{colorFormat.SecondEntry}}, long {{colorFormat.ThirdEntry}}, long a) : ISum
+ {
+ #region Properties & Fields
+
+ private readonly long _{{colorFormat.FirstEntry}} = {{colorFormat.FirstEntry}};
+ private readonly long _{{colorFormat.SecondEntry}} = {{colorFormat.SecondEntry}};
+ private readonly long _{{colorFormat.ThirdEntry}} = {{colorFormat.ThirdEntry}};
+ private readonly long _a = a;
+
+ public long A => _a;
+ public long R => _r;
+ public long G => _g;
+ public long B => _b;
+
+ #endregion
+
+ #region Methods
+
+ ///
+ public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
+
+ #endregion
+ }
+
+ """,
+
+ 4 => $$"""
+ // 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 Sum{{colorFormat.Format}}(long {{colorFormat.FirstEntry}}, long {{colorFormat.SecondEntry}}, long {{colorFormat.ThirdEntry}}, long {{colorFormat.FourthEntry}}) : ISum
+ {
+ #region Properties & Fields
+
+ private readonly long _{{colorFormat.FirstEntry}} = {{colorFormat.FirstEntry}};
+ private readonly long _{{colorFormat.SecondEntry}} = {{colorFormat.SecondEntry}};
+ private readonly long _{{colorFormat.ThirdEntry}} = {{colorFormat.ThirdEntry}};
+ private readonly long _{{colorFormat.FourthEntry}} = {{colorFormat.FourthEntry}};
+
+ public long R => _r;
+ public long G => _g;
+ public long B => _b;
+ public long A => _a;
+
+ #endregion
+
+ #region Methods
+
+ ///
+ public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
+
+ #endregion
+ }
+
+ """,
+ _ => null
+ };
+
+ private static string GenerateColorFormatSum(ColorFormatData colorFormat)
+ {
+ return $$"""
+ using System.Runtime.InteropServices;
+
+ namespace HPPH;
+
+ public sealed partial class ColorFormat{{colorFormat.Format}}
+ {
+ #region Methods
+
+ unsafe ISum IColorFormat.Sum(ReadOnlySpan data) => PixelHelper.Sum(MemoryMarshal.Cast(data));
+
+ #endregion
+ }
+ """;
+ }
+
+ private static string GenerateColorFormatInterfaceSum()
+ {
+ return """
+ namespace HPPH;
+
+ public partial interface IColorFormat
+ {
+ internal ISum Sum(ReadOnlySpan data);
+ }
+ """;
+ }
+}
\ No newline at end of file
diff --git a/HPPH.Generators/HPPH.Generators.csproj b/HPPH.Generators/HPPH.Generators.csproj
new file mode 100644
index 0000000..18611e6
--- /dev/null
+++ b/HPPH.Generators/HPPH.Generators.csproj
@@ -0,0 +1,21 @@
+
+
+
+ netstandard2.0
+ true
+ true
+ true
+ false
+ true
+ latest
+ True
+ False
+ True
+ True
+
+
+
+
+
+
+
diff --git a/HPPH.Generators/HPPH.Generators.csproj.DotSettings b/HPPH.Generators/HPPH.Generators.csproj.DotSettings
new file mode 100644
index 0000000..3a36d17
--- /dev/null
+++ b/HPPH.Generators/HPPH.Generators.csproj.DotSettings
@@ -0,0 +1,2 @@
+
+ True
\ No newline at end of file
diff --git a/HPPH.Reference/HPPH.Reference.csproj b/HPPH.Reference/HPPH.Reference.csproj
new file mode 100644
index 0000000..69fb405
--- /dev/null
+++ b/HPPH.Reference/HPPH.Reference.csproj
@@ -0,0 +1,13 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/HPPH.Reference/HPPHExtensions.cs b/HPPH.Reference/HPPHExtensions.cs
new file mode 100644
index 0000000..8212d6d
--- /dev/null
+++ b/HPPH.Reference/HPPHExtensions.cs
@@ -0,0 +1,14 @@
+namespace HPPH.Reference;
+
+internal static class HPPHExtensions
+{
+ public static byte GetByteValueFromPercentage(this float percentage)
+ {
+ if (float.IsNaN(percentage) || (percentage < 0)) return 0;
+
+ return (byte)(percentage >= 1.0f ? 255 : percentage * 256.0f);
+ }
+
+ public static float GetPercentageFromByteValue(this byte value)
+ => value == 255 ? 1.0f : (value / 256.0f);
+}
\ No newline at end of file
diff --git a/HPPH.Reference/PixelHelper.Average.cs b/HPPH.Reference/PixelHelper.Average.cs
new file mode 100644
index 0000000..5544155
--- /dev/null
+++ b/HPPH.Reference/PixelHelper.Average.cs
@@ -0,0 +1,43 @@
+namespace HPPH.Reference;
+
+public static partial class ReferencePixelHelper
+{
+ #region Methods
+
+ public static IColor Average(IImage image)
+ {
+ float count = image.Width * image.Height;
+
+ ISum sum = Sum(image);
+ return new ColorRGBA((sum.R / count).GetByteValueFromPercentage(),
+ (sum.G / count).GetByteValueFromPercentage(),
+ (sum.B / count).GetByteValueFromPercentage(),
+ (sum.A / count).GetByteValueFromPercentage());
+ }
+
+ public static T Average(RefImage image)
+ where T : struct, IColor
+ {
+ float count = image.Width * image.Height;
+
+ ISum sum = Sum(image);
+ return (T)T.Create((sum.R / count).GetByteValueFromPercentage(),
+ (sum.G / count).GetByteValueFromPercentage(),
+ (sum.B / count).GetByteValueFromPercentage(),
+ (sum.A / count).GetByteValueFromPercentage());
+ }
+
+ public static T Average(ReadOnlySpan colors)
+ where T : struct, IColor
+ {
+ float count = colors.Length;
+
+ ISum sum = Sum(colors);
+ return (T)T.Create((sum.R / count).GetByteValueFromPercentage(),
+ (sum.G / count).GetByteValueFromPercentage(),
+ (sum.B / count).GetByteValueFromPercentage(),
+ (sum.A / count).GetByteValueFromPercentage());
+ }
+
+ #endregion
+}
diff --git a/HPPH.Reference/PixelHelper.MinMax.cs b/HPPH.Reference/PixelHelper.MinMax.cs
new file mode 100644
index 0000000..80ce107
--- /dev/null
+++ b/HPPH.Reference/PixelHelper.MinMax.cs
@@ -0,0 +1,73 @@
+namespace HPPH.Reference;
+
+public static partial class ReferencePixelHelper
+{
+ #region Methods
+
+ public static IMinMax MinMax(IImage image)
+ {
+ byte minR = byte.MaxValue, minG = byte.MaxValue, minB = byte.MaxValue, minA = byte.MaxValue;
+ byte maxR = byte.MinValue, maxG = byte.MinValue, maxB = byte.MinValue, maxA = byte.MinValue;
+
+ foreach (IColor color in image)
+ {
+ minR = Math.Min(minR, color.R);
+ minG = Math.Min(minG, color.G);
+ minB = Math.Min(minB, color.B);
+ minA = Math.Min(minA, color.A);
+
+ maxR = Math.Max(maxR, color.R);
+ maxG = Math.Max(maxG, color.G);
+ maxB = Math.Max(maxB, color.B);
+ maxA = Math.Max(maxA, color.A);
+ }
+
+ return new MinMaxRGBA(minR, maxR, minG, maxG, minB, maxB, minA, maxA);
+ }
+
+ public static IMinMax MinMax(RefImage image)
+ where T : struct, IColor
+ {
+ byte minR = byte.MaxValue, minG = byte.MaxValue, minB = byte.MaxValue, minA = byte.MaxValue;
+ byte maxR = byte.MinValue, maxG = byte.MinValue, maxB = byte.MinValue, maxA = byte.MinValue;
+
+ foreach (T color in image)
+ {
+ minR = Math.Min(minR, color.R);
+ minG = Math.Min(minG, color.G);
+ minB = Math.Min(minB, color.B);
+ minA = Math.Min(minA, color.A);
+
+ maxR = Math.Max(maxR, color.R);
+ maxG = Math.Max(maxG, color.G);
+ maxB = Math.Max(maxB, color.B);
+ maxA = Math.Max(maxA, color.A);
+ }
+
+ return new MinMaxRGBA(minR, maxR, minG, maxG, minB, maxB, minA, maxA);
+ }
+
+ public static IMinMax MinMax(ReadOnlySpan colors)
+ where T : struct, IColor
+ {
+ byte minR = byte.MaxValue, minG = byte.MaxValue, minB = byte.MaxValue, minA = byte.MaxValue;
+ byte maxR = byte.MinValue, maxG = byte.MinValue, maxB = byte.MinValue, maxA = byte.MinValue;
+
+ foreach (T color in colors)
+ {
+ minR = Math.Min(minR, color.R);
+ minG = Math.Min(minG, color.G);
+ minB = Math.Min(minB, color.B);
+ minA = Math.Min(minA, color.A);
+
+ maxR = Math.Max(maxR, color.R);
+ maxG = Math.Max(maxG, color.G);
+ maxB = Math.Max(maxB, color.B);
+ maxA = Math.Max(maxA, color.A);
+ }
+
+ return new MinMaxRGBA(minR, maxR, minG, maxG, minB, maxB, minA, maxA);
+ }
+
+ #endregion
+}
diff --git a/HPPH.Reference/PixelHelper.Quantize.cs b/HPPH.Reference/PixelHelper.Quantize.cs
new file mode 100644
index 0000000..7a5e7ee
--- /dev/null
+++ b/HPPH.Reference/PixelHelper.Quantize.cs
@@ -0,0 +1,8 @@
+namespace HPPH.Reference;
+
+public static partial class ReferencePixelHelper
+{
+ #region Methods
+
+ #endregion
+}
diff --git a/HPPH.Reference/PixelHelper.Sort.cs b/HPPH.Reference/PixelHelper.Sort.cs
new file mode 100644
index 0000000..9952f3f
--- /dev/null
+++ b/HPPH.Reference/PixelHelper.Sort.cs
@@ -0,0 +1,26 @@
+namespace HPPH.Reference;
+
+public static partial class ReferencePixelHelper
+{
+ #region Methods
+
+ // DarthAffe 05.07.2024: LINQ OrderBy uses a stable sorting algorithm -> it's a good reference as the optimized sort is supposed to be stable.
+
+ public static void SortByRed(Span colors)
+ where T : unmanaged, IColor
+ => colors.ToArray().OrderBy(x => x.R).ToArray().AsSpan().CopyTo(colors);
+
+ public static void SortByGreen(Span colors)
+ where T : unmanaged, IColor
+ => colors.ToArray().OrderBy(x => x.G).ToArray().AsSpan().CopyTo(colors);
+
+ public static void SortByBlue(Span colors)
+ where T : unmanaged, IColor
+ => colors.ToArray().OrderBy(x => x.B).ToArray().AsSpan().CopyTo(colors);
+
+ public static void SortByAlpha(Span colors)
+ where T : unmanaged, IColor
+ => colors.ToArray().OrderBy(x => x.A).ToArray().AsSpan().CopyTo(colors);
+
+ #endregion
+}
diff --git a/HPPH.Reference/PixelHelper.Sum.cs b/HPPH.Reference/PixelHelper.Sum.cs
new file mode 100644
index 0000000..6ea5e38
--- /dev/null
+++ b/HPPH.Reference/PixelHelper.Sum.cs
@@ -0,0 +1,55 @@
+namespace HPPH.Reference;
+
+public static partial class ReferencePixelHelper
+{
+ #region Methods
+
+ public static ISum Sum(IImage image)
+ {
+ long sumR = 0, sumG = 0, sumB = 0, sumA = 0;
+
+ foreach (IColor color in image)
+ {
+ sumR += color.R;
+ sumG += color.G;
+ sumB += color.B;
+ sumA += color.A;
+ }
+
+ return new SumRGBA(sumR, sumG, sumB, sumA);
+ }
+
+ public static ISum Sum(RefImage image)
+ where T : struct, IColor
+ {
+ long sumR = 0, sumG = 0, sumB = 0, sumA = 0;
+
+ foreach (T color in image)
+ {
+ sumR += color.R;
+ sumG += color.G;
+ sumB += color.B;
+ sumA += color.A;
+ }
+
+ return new SumRGBA(sumR, sumG, sumB, sumA);
+ }
+
+ public static ISum Sum(ReadOnlySpan colors)
+ where T : struct, IColor
+ {
+ long sumR = 0, sumG = 0, sumB = 0, sumA = 0;
+
+ foreach (T color in colors)
+ {
+ sumR += color.R;
+ sumG += color.G;
+ sumB += color.B;
+ sumA += color.A;
+ }
+
+ return new SumRGBA(sumR, sumG, sumB, sumA);
+ }
+
+ #endregion
+}
diff --git a/HPPH.Reference/PixelHelper.cs b/HPPH.Reference/PixelHelper.cs
new file mode 100644
index 0000000..faee3b8
--- /dev/null
+++ b/HPPH.Reference/PixelHelper.cs
@@ -0,0 +1,3 @@
+namespace HPPH.Reference;
+
+public static partial class ReferencePixelHelper;
diff --git a/HPPH.Test/AverageTests.cs b/HPPH.Test/AverageTests.cs
new file mode 100644
index 0000000..fbc375a
--- /dev/null
+++ b/HPPH.Test/AverageTests.cs
@@ -0,0 +1,65 @@
+using HPPH.Reference;
+
+namespace HPPH.Test;
+
+[TestClass]
+public class AverageTests
+{
+ private static IEnumerable GetTestImages() => Directory.EnumerateFiles(@"..\..\..\..\sample_data", "*.png", SearchOption.AllDirectories);
+
+ [TestMethod]
+ public void AverageImage3Byte()
+ {
+ }
+
+ [TestMethod]
+ public void AverageRefImage3Byte()
+ {
+ }
+
+ [TestMethod]
+ public void AverageReadOnlySpan3Byte()
+ {
+ foreach (string image in GetTestImages())
+ {
+ ColorRGB[] data = ImageHelper.Get3ByteColorsFromImage(image);
+ ReadOnlySpan span = data;
+
+ ColorRGB reference = ReferencePixelHelper.Average(span);
+ ColorRGB test = PixelHelper.Average(span);
+
+ Assert.AreEqual(reference.R, test.R, "R differs");
+ Assert.AreEqual(reference.G, test.G, "G differs");
+ Assert.AreEqual(reference.B, test.B, "B differs");
+ Assert.AreEqual(reference.A, test.A, "A differs");
+ }
+ }
+
+ [TestMethod]
+ public void AverageImage4Byte()
+ {
+ }
+
+ [TestMethod]
+ public void AverageRefImage4Byte()
+ {
+ }
+
+ [TestMethod]
+ public void AverageReadOnlySpan4Byte()
+ {
+ foreach (string image in GetTestImages())
+ {
+ ColorRGBA[] data = ImageHelper.Get4ByteColorsFromImage(image);
+ ReadOnlySpan span = data;
+
+ ColorRGBA reference = ReferencePixelHelper.Average(span);
+ ColorRGBA test = PixelHelper.Average(span);
+
+ Assert.AreEqual(reference.R, test.R, "R differs");
+ Assert.AreEqual(reference.G, test.G, "G differs");
+ Assert.AreEqual(reference.B, test.B, "B differs");
+ Assert.AreEqual(reference.A, test.A, "A differs");
+ }
+ }
+}
\ No newline at end of file
diff --git a/HPPH.Test/HPPH.Test.csproj b/HPPH.Test/HPPH.Test.csproj
new file mode 100644
index 0000000..9ff21f8
--- /dev/null
+++ b/HPPH.Test/HPPH.Test.csproj
@@ -0,0 +1,29 @@
+
+
+
+ net8.0-windows
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/HPPH.Test/ImageHelper.cs b/HPPH.Test/ImageHelper.cs
new file mode 100644
index 0000000..f779dd7
--- /dev/null
+++ b/HPPH.Test/ImageHelper.cs
@@ -0,0 +1,44 @@
+using System.Drawing;
+
+namespace HPPH.Test;
+
+internal static class ImageHelper
+{
+ #region Methods
+
+ public static ColorRGB[] Get3ByteColorsFromImage(string file)
+ {
+ using FileStream stream = File.OpenRead(file);
+ using Bitmap bmp = new(stream);
+
+ ColorRGB[] colors = new ColorRGB[bmp.Width * bmp.Height];
+ int i = 0;
+ for (int x = 0; x < bmp.Width; x++)
+ for (int y = 0; y < bmp.Height; y++)
+ {
+ Color color = bmp.GetPixel(x, y);
+ colors[i++] = new ColorRGB(color.R, color.G, color.B);
+ }
+
+ return colors;
+ }
+
+ public static ColorRGBA[] Get4ByteColorsFromImage(string file)
+ {
+ using FileStream stream = File.OpenRead(file);
+ using Bitmap bmp = new(stream);
+
+ ColorRGBA[] colors = new ColorRGBA[bmp.Width * bmp.Height];
+ int i = 0;
+ for (int x = 0; x < bmp.Width; x++)
+ for (int y = 0; y < bmp.Height; y++)
+ {
+ Color color = bmp.GetPixel(x, y);
+ colors[i++] = new ColorRGBA(color.R, color.G, color.B, color.A);
+ }
+
+ return colors;
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/HPPH.Test/MinMaxTests.cs b/HPPH.Test/MinMaxTests.cs
new file mode 100644
index 0000000..5f3ecb8
--- /dev/null
+++ b/HPPH.Test/MinMaxTests.cs
@@ -0,0 +1,85 @@
+using HPPH.Reference;
+
+namespace HPPH.Test;
+
+[TestClass]
+public class MinMaxTests
+{
+ private static IEnumerable GetTestImages() => Directory.EnumerateFiles(@"..\..\..\..\sample_data", "*.png", SearchOption.AllDirectories);
+
+ [TestMethod]
+ public void MinMaxImage3Byte()
+ {
+ }
+
+ [TestMethod]
+ public void MinMaxRefImage3Byte()
+ {
+ }
+
+ [TestMethod]
+ public void MinMaxReadOnlySpan3Byte()
+ {
+ foreach (string image in GetTestImages())
+ {
+ ColorRGB[] data = ImageHelper.Get3ByteColorsFromImage(image);
+ ReadOnlySpan span = data;
+
+ IMinMax reference = ReferencePixelHelper.MinMax(span);
+ IMinMax test = PixelHelper.MinMax(span);
+
+ Assert.AreEqual(reference.RedMin, test.RedMin, "RedMin differs");
+ Assert.AreEqual(reference.GreenMin, test.GreenMin, "GreenMin differs");
+ Assert.AreEqual(reference.BlueMin, test.BlueMin, "BlueMin differs");
+ Assert.AreEqual(reference.AlphaMin, test.AlphaMin, "AlphaMin differs");
+
+ Assert.AreEqual(reference.RedMax, test.RedMax, "RedMax differs");
+ Assert.AreEqual(reference.GreenMax, test.GreenMax, "GreenMax differs");
+ Assert.AreEqual(reference.BlueMax, test.BlueMax, "BlueMax differs");
+ Assert.AreEqual(reference.AlphaMax, test.AlphaMax, "AlphaMax differs");
+
+ Assert.AreEqual(reference.RedRange, test.RedRange, "RedRange differs");
+ Assert.AreEqual(reference.GreenRange, test.GreenRange, "GreenRange differs");
+ Assert.AreEqual(reference.BlueRange, test.BlueRange, "BlueRange differs");
+ Assert.AreEqual(reference.AlphaRange, test.AlphaRange, "AlphaRange differs");
+ }
+ }
+
+ [TestMethod]
+ public void MinMaxImage4Byte()
+ {
+ }
+
+ [TestMethod]
+ public void MinMaxRefImage4Byte()
+ {
+ }
+
+ [TestMethod]
+ public void MinMaxReadOnlySpan4Byte()
+ {
+ foreach (string image in GetTestImages())
+ {
+ ColorRGBA[] data = ImageHelper.Get4ByteColorsFromImage(image);
+ ReadOnlySpan span = data;
+
+ IMinMax reference = ReferencePixelHelper.MinMax(span);
+ IMinMax test = PixelHelper.MinMax(span);
+
+ Assert.AreEqual(reference.RedMin, test.RedMin, "RedMin differs");
+ Assert.AreEqual(reference.GreenMin, test.GreenMin, "GreenMin differs");
+ Assert.AreEqual(reference.BlueMin, test.BlueMin, "BlueMin differs");
+ Assert.AreEqual(reference.AlphaMin, test.AlphaMin, "AlphaMin differs");
+
+ Assert.AreEqual(reference.RedMax, test.RedMax, "RedMax differs");
+ Assert.AreEqual(reference.GreenMax, test.GreenMax, "GreenMax differs");
+ Assert.AreEqual(reference.BlueMax, test.BlueMax, "BlueMax differs");
+ Assert.AreEqual(reference.AlphaMax, test.AlphaMax, "AlphaMax differs");
+
+ Assert.AreEqual(reference.RedRange, test.RedRange, "RedRange differs");
+ Assert.AreEqual(reference.GreenRange, test.GreenRange, "GreenRange differs");
+ Assert.AreEqual(reference.BlueRange, test.BlueRange, "BlueRange differs");
+ Assert.AreEqual(reference.AlphaRange, test.AlphaRange, "AlphaRange differs");
+ }
+ }
+}
\ No newline at end of file
diff --git a/HPPH.Test/SortTests.cs b/HPPH.Test/SortTests.cs
new file mode 100644
index 0000000..e41159a
--- /dev/null
+++ b/HPPH.Test/SortTests.cs
@@ -0,0 +1,143 @@
+using HPPH.Reference;
+
+namespace HPPH.Test;
+
+[TestClass]
+public class SortTests
+{
+ private static IEnumerable GetTestImages() => Directory.EnumerateFiles(@"..\..\..\..\sample_data", "*.png", SearchOption.AllDirectories);
+
+
+ [TestMethod]
+ public void SortByRed3Byte()
+ {
+ foreach (string image in GetTestImages())
+ {
+ ColorRGB[] referenceData = ImageHelper.Get3ByteColorsFromImage(image);
+ ColorRGB[] testData = new ColorRGB[referenceData.Length];
+ Span referenceSpan = referenceData;
+ Span testSpan = testData;
+ referenceSpan.CopyTo(testSpan);
+
+ ReferencePixelHelper.SortByRed(referenceSpan);
+ PixelHelper.SortByRed(testSpan);
+
+ for (int i = 0; i < referenceData.Length; i++)
+ Assert.AreEqual(referenceSpan[i], testSpan[i], $"Index {i} differs");
+ }
+ }
+
+ [TestMethod]
+ public void SortByGreen3Byte()
+ {
+ foreach (string image in GetTestImages())
+ {
+ ColorRGB[] referenceData = ImageHelper.Get3ByteColorsFromImage(image);
+ ColorRGB[] testData = new ColorRGB[referenceData.Length];
+ Span referenceSpan = referenceData;
+ Span testSpan = testData;
+ referenceSpan.CopyTo(testSpan);
+
+ ReferencePixelHelper.SortByGreen(referenceSpan);
+ PixelHelper.SortByGreen(testSpan);
+
+ for (int i = 0; i < referenceData.Length; i++)
+ Assert.AreEqual(referenceSpan[i], testSpan[i], $"Index {i} differs");
+ }
+ }
+
+ [TestMethod]
+ public void SortByBlue3Byte()
+ {
+ foreach (string image in GetTestImages())
+ {
+ ColorRGB[] referenceData = ImageHelper.Get3ByteColorsFromImage(image);
+ ColorRGB[] testData = new ColorRGB[referenceData.Length];
+ Span referenceSpan = referenceData;
+ Span testSpan = testData;
+ referenceSpan.CopyTo(testSpan);
+
+ ReferencePixelHelper.SortByBlue(referenceSpan);
+ PixelHelper.SortByBlue(testSpan);
+
+ for (int i = 0; i < referenceData.Length; i++)
+ Assert.AreEqual(referenceSpan[i], testSpan[i], $"Index {i} differs");
+ }
+ }
+
+ [TestMethod]
+ public void SortByRed4Byte()
+ {
+ foreach (string image in GetTestImages())
+ {
+ ColorRGBA[] referenceData = ImageHelper.Get4ByteColorsFromImage(image);
+ ColorRGBA[] testData = new ColorRGBA[referenceData.Length];
+ Span referenceSpan = referenceData;
+ Span testSpan = testData;
+ referenceSpan.CopyTo(testSpan);
+
+ ReferencePixelHelper.SortByRed(referenceSpan);
+ PixelHelper.SortByRed(testSpan);
+
+ for (int i = 0; i < referenceData.Length; i++)
+ Assert.AreEqual(referenceSpan[i], testSpan[i], $"Index {i} differs");
+ }
+ }
+
+ [TestMethod]
+ public void SortByGreen4Byte()
+ {
+ foreach (string image in GetTestImages())
+ {
+ ColorRGBA[] referenceData = ImageHelper.Get4ByteColorsFromImage(image);
+ ColorRGBA[] testData = new ColorRGBA[referenceData.Length];
+ Span referenceSpan = referenceData;
+ Span testSpan = testData;
+ referenceSpan.CopyTo(testSpan);
+
+ ReferencePixelHelper.SortByGreen(referenceSpan);
+ PixelHelper.SortByGreen(testSpan);
+
+ for (int i = 0; i < referenceData.Length; i++)
+ Assert.AreEqual(referenceSpan[i], testSpan[i], $"Index {i} differs");
+ }
+ }
+
+ [TestMethod]
+ public void SortByBlue4Byte()
+ {
+ foreach (string image in GetTestImages())
+ {
+ ColorRGBA[] referenceData = ImageHelper.Get4ByteColorsFromImage(image);
+ ColorRGBA[] testData = new ColorRGBA[referenceData.Length];
+ Span referenceSpan = referenceData;
+ Span testSpan = testData;
+ referenceSpan.CopyTo(testSpan);
+
+ ReferencePixelHelper.SortByBlue(referenceSpan);
+ PixelHelper.SortByBlue(testSpan);
+
+ for (int i = 0; i < referenceData.Length; i++)
+ Assert.AreEqual(referenceSpan[i], testSpan[i], $"Index {i} differs");
+ }
+ }
+
+ [TestMethod]
+ public void SortByAlpha4Byte()
+ {
+ foreach (string image in GetTestImages())
+ {
+ ColorRGBA[] referenceData = ImageHelper.Get4ByteColorsFromImage(image);
+ ColorRGBA[] testData = new ColorRGBA[referenceData.Length];
+ Span referenceSpan = referenceData;
+ Span testSpan = testData;
+ referenceSpan.CopyTo(testSpan);
+
+ ReferencePixelHelper.SortByAlpha(referenceSpan);
+ PixelHelper.SortByAlpha(testSpan);
+
+ for (int i = 0; i < referenceData.Length; i++)
+ Assert.AreEqual(referenceSpan[i], testSpan[i], $"Index {i} differs");
+ }
+ }
+}
\ No newline at end of file
diff --git a/HPPH.Test/SumTests.cs b/HPPH.Test/SumTests.cs
new file mode 100644
index 0000000..d227e5e
--- /dev/null
+++ b/HPPH.Test/SumTests.cs
@@ -0,0 +1,65 @@
+using HPPH.Reference;
+
+namespace HPPH.Test;
+
+[TestClass]
+public class SumTests
+{
+ private static IEnumerable GetTestImages() => Directory.EnumerateFiles(@"..\..\..\..\sample_data", "*.png", SearchOption.AllDirectories);
+
+ [TestMethod]
+ public void SumImage3Byte()
+ {
+ }
+
+ [TestMethod]
+ public void SumRefImage3Byte()
+ {
+ }
+
+ [TestMethod]
+ public void SumReadOnlySpan3Byte()
+ {
+ foreach (string image in GetTestImages())
+ {
+ ColorRGB[] data = ImageHelper.Get3ByteColorsFromImage(image);
+ ReadOnlySpan span = data;
+
+ ISum reference = ReferencePixelHelper.Sum(span);
+ ISum test = PixelHelper.Sum(span);
+
+ Assert.AreEqual(reference.R, test.R, "R differs");
+ Assert.AreEqual(reference.G, test.G, "G differs");
+ Assert.AreEqual(reference.B, test.B, "B differs");
+ Assert.AreEqual(reference.A, test.A, "A differs");
+ }
+ }
+
+ [TestMethod]
+ public void SumImage4Byte()
+ {
+ }
+
+ [TestMethod]
+ public void SumRefImage4Byte()
+ {
+ }
+
+ [TestMethod]
+ public void SumReadOnlySpan4Byte()
+ {
+ foreach (string image in GetTestImages())
+ {
+ ColorRGBA[] data = ImageHelper.Get4ByteColorsFromImage(image);
+ ReadOnlySpan span = data;
+
+ ISum reference = ReferencePixelHelper.Sum(span);
+ ISum test = PixelHelper.Sum(span);
+
+ Assert.AreEqual(reference.R, test.R, "R differs");
+ Assert.AreEqual(reference.G, test.G, "G differs");
+ Assert.AreEqual(reference.B, test.B, "B differs");
+ Assert.AreEqual(reference.A, test.A, "A differs");
+ }
+ }
+}
\ No newline at end of file
diff --git a/HPPH.sln b/HPPH.sln
new file mode 100644
index 0000000..67c72eb
--- /dev/null
+++ b/HPPH.sln
@@ -0,0 +1,49 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.9.34714.143
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HPPH", "HPPH\HPPH.csproj", "{FDC79AD8-26FF-4EA4-B1FF-7D4818F57A79}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HPPH.Test", "HPPH.Test\HPPH.Test.csproj", "{11795808-7916-4EAA-A69A-B09FEF46F71D}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HPPH.Benchmark", "HPPH.Benchmark\HPPH.Benchmark.csproj", "{233CFF7C-DDA4-4450-AB88-17C1B58EBFB5}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HPPH.Reference", "HPPH.Reference\HPPH.Reference.csproj", "{1675FE68-1F51-4202-9567-BB46215B2BBD}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HPPH.Generators", "HPPH.Generators\HPPH.Generators.csproj", "{C247512B-E6D2-4591-8AFA-F2268F1AEAB2}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {FDC79AD8-26FF-4EA4-B1FF-7D4818F57A79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FDC79AD8-26FF-4EA4-B1FF-7D4818F57A79}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FDC79AD8-26FF-4EA4-B1FF-7D4818F57A79}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FDC79AD8-26FF-4EA4-B1FF-7D4818F57A79}.Release|Any CPU.Build.0 = Release|Any CPU
+ {11795808-7916-4EAA-A69A-B09FEF46F71D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {11795808-7916-4EAA-A69A-B09FEF46F71D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {11795808-7916-4EAA-A69A-B09FEF46F71D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {11795808-7916-4EAA-A69A-B09FEF46F71D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {233CFF7C-DDA4-4450-AB88-17C1B58EBFB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {233CFF7C-DDA4-4450-AB88-17C1B58EBFB5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {233CFF7C-DDA4-4450-AB88-17C1B58EBFB5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {233CFF7C-DDA4-4450-AB88-17C1B58EBFB5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1675FE68-1F51-4202-9567-BB46215B2BBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1675FE68-1F51-4202-9567-BB46215B2BBD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1675FE68-1F51-4202-9567-BB46215B2BBD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1675FE68-1F51-4202-9567-BB46215B2BBD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C247512B-E6D2-4591-8AFA-F2268F1AEAB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C247512B-E6D2-4591-8AFA-F2268F1AEAB2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C247512B-E6D2-4591-8AFA-F2268F1AEAB2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C247512B-E6D2-4591-8AFA-F2268F1AEAB2}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {EAF6CA55-D81E-4887-9FAC-8D633BBC0096}
+ EndGlobalSection
+EndGlobal
diff --git a/HPPH.sln.DotSettings b/HPPH.sln.DotSettings
new file mode 100644
index 0000000..c50ef0d
--- /dev/null
+++ b/HPPH.sln.DotSettings
@@ -0,0 +1,2 @@
+
+ HPPH
\ No newline at end of file
diff --git a/HPPH/Colors/Colors.cs b/HPPH/Colors/Colors.cs
new file mode 100644
index 0000000..9e22c38
--- /dev/null
+++ b/HPPH/Colors/Colors.cs
@@ -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;
diff --git a/HPPH/Colors/IColor.cs b/HPPH/Colors/IColor.cs
new file mode 100644
index 0000000..82d5a29
--- /dev/null
+++ b/HPPH/Colors/IColor.cs
@@ -0,0 +1,34 @@
+namespace HPPH;
+
+///
+/// Represents a generic color made of 4 bytes (alpha, red, green and blue)
+///
+public interface IColor
+{
+ ///
+ /// Gets the red-component of this color.
+ ///
+ byte R { get; }
+
+ ///
+ /// Gets the green-component of this color.
+ ///
+ byte G { get; }
+
+ ///
+ /// Gets the blue-component of this color.
+ ///
+ byte B { get; }
+
+ ///
+ /// Gets the alpha-component of this color.
+ ///
+ byte A { get; }
+
+ ///
+ /// Gets the color-format of this color.
+ ///
+ public static virtual IColorFormat ColorFormat => throw new NotSupportedException();
+
+ public static virtual IColor Create(byte r, byte g, byte b, byte a) => throw new NotSupportedException();
+}
\ No newline at end of file
diff --git a/HPPH/Colors/IColorFormat.cs b/HPPH/Colors/IColorFormat.cs
new file mode 100644
index 0000000..7c33a71
--- /dev/null
+++ b/HPPH/Colors/IColorFormat.cs
@@ -0,0 +1,15 @@
+namespace HPPH;
+
+///
+/// Represents a color format.
+///
+// ReSharper disable once InconsistentNaming
+public partial interface IColorFormat
+{
+ ///
+ /// Gets the Bytes per pixel for this color-format.
+ ///
+ int BytesPerPixel { get; }
+
+ string Name { get; }
+}
\ No newline at end of file
diff --git a/HPPH/CommunityToolkit.HighPerformance/ReadOnlyRefEnumerable.cs b/HPPH/CommunityToolkit.HighPerformance/ReadOnlyRefEnumerable.cs
new file mode 100644
index 0000000..cbc49f2
--- /dev/null
+++ b/HPPH/CommunityToolkit.HighPerformance/ReadOnlyRefEnumerable.cs
@@ -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;
+
+///
+/// A that iterates readonly items from arbitrary memory locations.
+///
+/// The type of items to enumerate.
+public readonly ref struct ReadOnlyRefEnumerable
+{
+ #region Properties & Fields
+
+ ///
+ /// The instance pointing to the first item in the target memory area.
+ ///
+ /// The field maps to the total available length.
+ private readonly ReadOnlySpan _span;
+
+ ///
+ /// The distance between items in the sequence to enumerate.
+ ///
+ /// The distance refers to items, not byte offset.
+ private readonly int _step;
+
+ ///
+ /// Gets the total available length for the sequence.
+ ///
+ public int Length
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _span.Length;
+ }
+
+ ///
+ /// Gets the element at the specified zero-based index.
+ ///
+ /// The zero-based index of the element.
+ /// A reference to the element at the specified index.
+ ///
+ /// Thrown when is invalid.
+ ///
+ 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;
+ }
+ }
+
+ ///
+ /// Gets the element at the specified zero-based index.
+ ///
+ /// The zero-based index of the element.
+ /// A reference to the element at the specified index.
+ ///
+ /// Thrown when is invalid.
+ ///
+ public ref readonly T this[Index index]
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => ref this[index.GetOffset(Length)];
+ }
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// A reference to the first item of the sequence.
+ /// The number of items in the sequence.
+ /// The distance between items in the sequence to enumerate.
+ [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
+
+ ///
+ [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;
+ }
+
+ ///
+ /// Copies the contents of this into a destination instance.
+ ///
+ /// The destination instance.
+ ///
+ /// Thrown when is shorter than the source instance.
+ ///
+ public void CopyTo(Span 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);
+ }
+
+ ///
+ /// Attempts to copy the current instance to a destination .
+ ///
+ /// The target of the copy operation.
+ /// Whether or not the operation was successful.
+ public bool TryCopyTo(Span 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
+
+ ///
+ /// A custom enumerator type to traverse items within a instance.
+ ///
+ public ref struct Enumerator
+ {
+ #region Properties & Fields
+
+ ///
+ private readonly ReadOnlySpan _span;
+
+ ///
+ private readonly int _step;
+
+ ///
+ /// The current position in the sequence.
+ ///
+ private int _position;
+
+ ///
+ 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
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The instance with the info on the items to traverse.
+ /// The distance between items in the sequence to enumerate.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal Enumerator(ReadOnlySpan span, int step)
+ {
+ this._span = span;
+ this._step = step;
+
+ _position = -1;
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool MoveNext() => ++_position < _span.Length;
+
+ #endregion
+ }
+}
diff --git a/HPPH/Data/Generic3ByteData.cs b/HPPH/Data/Generic3ByteData.cs
new file mode 100644
index 0000000..d2d239e
--- /dev/null
+++ b/HPPH/Data/Generic3ByteData.cs
@@ -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;
+}
\ No newline at end of file
diff --git a/HPPH/Data/Generic3ByteMinMax.cs b/HPPH/Data/Generic3ByteMinMax.cs
new file mode 100644
index 0000000..ad9c9b8
--- /dev/null
+++ b/HPPH/Data/Generic3ByteMinMax.cs
@@ -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);
+}
diff --git a/HPPH/Data/Generic4ByteData.cs b/HPPH/Data/Generic4ByteData.cs
new file mode 100644
index 0000000..1163c61
--- /dev/null
+++ b/HPPH/Data/Generic4ByteData.cs
@@ -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;
+}
\ No newline at end of file
diff --git a/HPPH/Data/Generic4ByteMinMax.cs b/HPPH/Data/Generic4ByteMinMax.cs
new file mode 100644
index 0000000..b256660
--- /dev/null
+++ b/HPPH/Data/Generic4ByteMinMax.cs
@@ -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);
+}
diff --git a/HPPH/Data/Generic4LongData.cs b/HPPH/Data/Generic4LongData.cs
new file mode 100644
index 0000000..122c1fc
--- /dev/null
+++ b/HPPH/Data/Generic4LongData.cs
@@ -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;
+}
\ No newline at end of file
diff --git a/HPPH/Data/IMinMax.cs b/HPPH/Data/IMinMax.cs
new file mode 100644
index 0000000..4650cd7
--- /dev/null
+++ b/HPPH/Data/IMinMax.cs
@@ -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; }
+}
\ No newline at end of file
diff --git a/HPPH/Data/ISum.cs b/HPPH/Data/ISum.cs
new file mode 100644
index 0000000..8dcbad9
--- /dev/null
+++ b/HPPH/Data/ISum.cs
@@ -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; }
+}
\ No newline at end of file
diff --git a/HPPH/Extensions/HPPHExtensions.cs b/HPPH/Extensions/HPPHExtensions.cs
new file mode 100644
index 0000000..66dae82
--- /dev/null
+++ b/HPPH/Extensions/HPPHExtensions.cs
@@ -0,0 +1,94 @@
+using System.Numerics;
+using System.Runtime.CompilerServices;
+
+namespace HPPH;
+
+///
+/// Offers some extensions and helper-methods for the work with floats.
+///
+internal static class HPPHExtensions
+{
+ #region Constants
+
+ ///
+ /// Defines the precision RGB.NET processes floating point comparisons in.
+ ///
+ public const float TOLERANCE = 1E-7f;
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Checks if two values are equal respecting the .
+ ///
+ /// The first value to compare.
+ /// The first value to compare.
+ /// true if the difference is smaller than the ; otherwise, false.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool EqualsInTolerance(this float value1, float value2) => Math.Abs(value1 - value2) < TOLERANCE;
+
+ ///
+ /// Clamps the provided value to be bigger or equal min and smaller or equal max.
+ ///
+ /// The value to clamp.
+ /// The lower value of the range the value is clamped to.
+ /// The higher value of the range the value is clamped to.
+ /// The clamped value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static T Clamp(this T value, T min, T max)
+ where T : INumber
+ {
+ // 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
+ }
+
+ ///
+ /// Enforces the provided value to be in the specified range by wrapping it around the edges if it exceeds them.
+ ///
+ /// The value to wrap.
+ /// The lower value of the range the value is wrapped into.
+ /// The higher value of the range the value is wrapped into.
+ /// The wrapped value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static T Wrap(this T value, T min, T max)
+ where T : INumber
+ {
+ T range = max - min;
+
+ while (value >= max)
+ value -= range;
+
+ while (value < min)
+ value += range;
+
+ return value;
+ }
+
+ ///
+ /// Converts a normalized float value in the range [0..1] to a byte [0..255].
+ ///
+ /// The normalized float value to convert.
+ /// The byte value.
+ [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);
+ }
+
+ ///
+ /// Converts a byte value [0..255] to a normalized float value in the range [0..1].
+ ///
+ /// The byte value to convert.
+ /// The normalized float value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float GetPercentageFromByteValue(this byte value)
+ => value == 255 ? 1.0f : (value / 256.0f);
+
+ #endregion
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSortSourceGenerator/ColorSortGeneratorAttribute.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSortSourceGenerator/ColorSortGeneratorAttribute.g.cs
new file mode 100644
index 0000000..4ae6eb7
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSortSourceGenerator/ColorSortGeneratorAttribute.g.cs
@@ -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;
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSortSourceGenerator/PixelHelper.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSortSourceGenerator/PixelHelper.g.cs
new file mode 100644
index 0000000..da7195d
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSortSourceGenerator/PixelHelper.g.cs
@@ -0,0 +1,393 @@
+using System.Buffers;
+
+namespace HPPH;
+
+public static unsafe partial class PixelHelper
+{
+ public static partial void SortByRed(Span colors) where T : unmanaged, IColor
+ {
+ fixed (T* ptr = colors)
+ {
+ T* end = ptr + colors.Length;
+
+ Span histogram = stackalloc int[256];
+ histogram.Clear();
+ for (T* color = ptr; color < end; color++)
+ histogram[(*color).R]++;
+
+ T[] bucketsArray = ArrayPool.Shared.Rent(colors.Length);
+ try
+ {
+ Span buckets = bucketsArray.AsSpan()[..colors.Length];
+ Span 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.Shared.Return(bucketsArray);
+ }
+ }
+ }
+ public static partial void SortByGreen(Span colors) where T : unmanaged, IColor
+ {
+ fixed (T* ptr = colors)
+ {
+ T* end = ptr + colors.Length;
+
+ Span histogram = stackalloc int[256];
+ histogram.Clear();
+ for (T* color = ptr; color < end; color++)
+ histogram[(*color).G]++;
+
+ T[] bucketsArray = ArrayPool.Shared.Rent(colors.Length);
+ try
+ {
+ Span buckets = bucketsArray.AsSpan()[..colors.Length];
+ Span 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.Shared.Return(bucketsArray);
+ }
+ }
+ }
+ public static partial void SortByBlue(Span colors) where T : unmanaged, IColor
+ {
+ fixed (T* ptr = colors)
+ {
+ T* end = ptr + colors.Length;
+
+ Span histogram = stackalloc int[256];
+ histogram.Clear();
+ for (T* color = ptr; color < end; color++)
+ histogram[(*color).B]++;
+
+ T[] bucketsArray = ArrayPool.Shared.Rent(colors.Length);
+ try
+ {
+ Span buckets = bucketsArray.AsSpan()[..colors.Length];
+ Span 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.Shared.Return(bucketsArray);
+ }
+ }
+ }
+ public static partial void SortByAlpha(Span colors) where T : unmanaged, IColor
+ {
+ fixed (T* ptr = colors)
+ {
+ T* end = ptr + colors.Length;
+
+ Span histogram = stackalloc int[256];
+ histogram.Clear();
+ for (T* color = ptr; color < end; color++)
+ histogram[(*color).A]++;
+
+ T[] bucketsArray = ArrayPool.Shared.Rent(colors.Length);
+ try
+ {
+ Span buckets = bucketsArray.AsSpan()[..colors.Length];
+ Span 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.Shared.Return(bucketsArray);
+ }
+ }
+ }
+ internal static partial void SortB1(Span colors)
+ {
+ fixed (Generic3ByteData* ptr = colors)
+ {
+ Generic3ByteData* end = ptr + colors.Length;
+
+ Span histogram = stackalloc int[256];
+ histogram.Clear();
+ for (Generic3ByteData* color = ptr; color < end; color++)
+ histogram[(*color).B1]++;
+
+ Generic3ByteData[] bucketsArray = ArrayPool.Shared.Rent(colors.Length);
+ try
+ {
+ Span buckets = bucketsArray.AsSpan()[..colors.Length];
+ Span 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.Shared.Return(bucketsArray);
+ }
+ }
+ }
+ internal static partial void SortB2(Span colors)
+ {
+ fixed (Generic3ByteData* ptr = colors)
+ {
+ Generic3ByteData* end = ptr + colors.Length;
+
+ Span histogram = stackalloc int[256];
+ histogram.Clear();
+ for (Generic3ByteData* color = ptr; color < end; color++)
+ histogram[(*color).B2]++;
+
+ Generic3ByteData[] bucketsArray = ArrayPool.Shared.Rent(colors.Length);
+ try
+ {
+ Span buckets = bucketsArray.AsSpan()[..colors.Length];
+ Span 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.Shared.Return(bucketsArray);
+ }
+ }
+ }
+ internal static partial void SortB3(Span colors)
+ {
+ fixed (Generic3ByteData* ptr = colors)
+ {
+ Generic3ByteData* end = ptr + colors.Length;
+
+ Span histogram = stackalloc int[256];
+ histogram.Clear();
+ for (Generic3ByteData* color = ptr; color < end; color++)
+ histogram[(*color).B3]++;
+
+ Generic3ByteData[] bucketsArray = ArrayPool.Shared.Rent(colors.Length);
+ try
+ {
+ Span buckets = bucketsArray.AsSpan()[..colors.Length];
+ Span 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.Shared.Return(bucketsArray);
+ }
+ }
+ }
+ internal static partial void SortB1(Span colors)
+ {
+ fixed (Generic4ByteData* ptr = colors)
+ {
+ Generic4ByteData* end = ptr + colors.Length;
+
+ Span histogram = stackalloc int[256];
+ histogram.Clear();
+ for (Generic4ByteData* color = ptr; color < end; color++)
+ histogram[(*color).B1]++;
+
+ Generic4ByteData[] bucketsArray = ArrayPool.Shared.Rent(colors.Length);
+ try
+ {
+ Span buckets = bucketsArray.AsSpan()[..colors.Length];
+ Span 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.Shared.Return(bucketsArray);
+ }
+ }
+ }
+ internal static partial void SortB2(Span colors)
+ {
+ fixed (Generic4ByteData* ptr = colors)
+ {
+ Generic4ByteData* end = ptr + colors.Length;
+
+ Span histogram = stackalloc int[256];
+ histogram.Clear();
+ for (Generic4ByteData* color = ptr; color < end; color++)
+ histogram[(*color).B2]++;
+
+ Generic4ByteData[] bucketsArray = ArrayPool.Shared.Rent(colors.Length);
+ try
+ {
+ Span buckets = bucketsArray.AsSpan()[..colors.Length];
+ Span 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.Shared.Return(bucketsArray);
+ }
+ }
+ }
+ internal static partial void SortB3(Span colors)
+ {
+ fixed (Generic4ByteData* ptr = colors)
+ {
+ Generic4ByteData* end = ptr + colors.Length;
+
+ Span histogram = stackalloc int[256];
+ histogram.Clear();
+ for (Generic4ByteData* color = ptr; color < end; color++)
+ histogram[(*color).B3]++;
+
+ Generic4ByteData[] bucketsArray = ArrayPool.Shared.Rent(colors.Length);
+ try
+ {
+ Span buckets = bucketsArray.AsSpan()[..colors.Length];
+ Span 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.Shared.Return(bucketsArray);
+ }
+ }
+ }
+ internal static partial void SortB4(Span colors)
+ {
+ fixed (Generic4ByteData* ptr = colors)
+ {
+ Generic4ByteData* end = ptr + colors.Length;
+
+ Span histogram = stackalloc int[256];
+ histogram.Clear();
+ for (Generic4ByteData* color = ptr; color < end; color++)
+ histogram[(*color).B4]++;
+
+ Generic4ByteData[] bucketsArray = ArrayPool.Shared.Rent(colors.Length);
+ try
+ {
+ Span buckets = bucketsArray.AsSpan()[..colors.Length];
+ Span 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.Shared.Return(bucketsArray);
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorABGR.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorABGR.g.cs
new file mode 100644
index 0000000..5adf3e4
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorABGR.g.cs
@@ -0,0 +1,57 @@
+// ReSharper disable ConvertToAutoProperty
+// ReSharper disable ConvertToAutoPropertyWhenPossible
+// ReSharper disable ReplaceWithPrimaryConstructorParameter
+
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+///
+/// Represents a color in 32 bit ABGR-format.
+///
+///
+/// Initializes a new instance of the class.
+///
+/// The Alpha-component of the color.
+/// The Blue-component of the color.
+/// The Green-component of the color.
+/// The Red-component of the color.
+[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
+
+ ///
+ 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;
+
+ ///
+ public byte R => _r;
+
+ ///
+ public byte G => _g;
+
+ ///
+ public byte B => _b;
+
+ ///
+ public byte A => _a;
+
+ #endregion
+
+ #region Methods
+
+ ///
+ public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
+
+ ///
+ public static IColor Create(byte r, byte g, byte b, byte a) => new ColorABGR(a, b, g, r);
+
+ #endregion
+}
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorARGB.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorARGB.g.cs
new file mode 100644
index 0000000..19d95a1
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorARGB.g.cs
@@ -0,0 +1,57 @@
+// ReSharper disable ConvertToAutoProperty
+// ReSharper disable ConvertToAutoPropertyWhenPossible
+// ReSharper disable ReplaceWithPrimaryConstructorParameter
+
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+///
+/// Represents a color in 32 bit ARGB-format.
+///
+///
+/// Initializes a new instance of the class.
+///
+/// The Alpha-component of the color.
+/// The Red-component of the color.
+/// The Green-component of the color.
+/// The Blue-component of the color.
+[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
+
+ ///
+ 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;
+
+ ///
+ public byte R => _r;
+
+ ///
+ public byte G => _g;
+
+ ///
+ public byte B => _b;
+
+ ///
+ public byte A => _a;
+
+ #endregion
+
+ #region Methods
+
+ ///
+ public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
+
+ ///
+ public static IColor Create(byte r, byte g, byte b, byte a) => new ColorARGB(a, r, g, b);
+
+ #endregion
+}
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorBGR.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorBGR.g.cs
new file mode 100644
index 0000000..6133d7b
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorBGR.g.cs
@@ -0,0 +1,55 @@
+// ReSharper disable ConvertToAutoProperty
+// ReSharper disable ConvertToAutoPropertyWhenPossible
+// ReSharper disable ReplaceWithPrimaryConstructorParameter
+
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+///
+/// Represents a color in 24 bit BGR-format.
+///
+///
+/// Initializes a new instance of the class.
+///
+/// The Blue-component of the color.
+/// The Green-component of the color.
+/// The Red-component of the color.
+[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
+
+ ///
+ public static IColorFormat ColorFormat => IColorFormat.BGR;
+
+ private readonly byte _b = b;
+ private readonly byte _g = g;
+ private readonly byte _r = r;
+
+ ///
+ public byte R => _r;
+
+ ///
+ public byte G => _g;
+
+ ///
+ public byte B => _b;
+
+ ///
+ public byte A => byte.MaxValue;
+
+ #endregion
+
+ #region Methods
+
+ ///
+ public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
+
+ ///
+ public static IColor Create(byte r, byte g, byte b, byte a) => new ColorBGR(b, g, r);
+
+ #endregion
+}
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorBGRA.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorBGRA.g.cs
new file mode 100644
index 0000000..da510c4
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorBGRA.g.cs
@@ -0,0 +1,57 @@
+// ReSharper disable ConvertToAutoProperty
+// ReSharper disable ConvertToAutoPropertyWhenPossible
+// ReSharper disable ReplaceWithPrimaryConstructorParameter
+
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+///
+/// Represents a color in 32 bit BGRA-format.
+///
+///
+/// Initializes a new instance of the class.
+///
+/// The Blue-component of the color.
+/// The Green-component of the color.
+/// The Red-component of the color.
+/// The Alpha-component of the color.
+[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
+
+ ///
+ 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;
+
+ ///
+ public byte R => _r;
+
+ ///
+ public byte G => _g;
+
+ ///
+ public byte B => _b;
+
+ ///
+ public byte A => _a;
+
+ #endregion
+
+ #region Methods
+
+ ///
+ public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
+
+ ///
+ public static IColor Create(byte r, byte g, byte b, byte a) => new ColorBGRA(b, g, r, a);
+
+ #endregion
+}
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatABGR.Average.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatABGR.Average.g.cs
new file mode 100644
index 0000000..89256c8
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatABGR.Average.g.cs
@@ -0,0 +1,12 @@
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+public sealed partial class ColorFormatABGR
+{
+ #region Methods
+
+ unsafe IColor IColorFormat.Average(ReadOnlySpan data) => PixelHelper.Average(MemoryMarshal.Cast(data));
+
+ #endregion
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatABGR.MinMax.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatABGR.MinMax.g.cs
new file mode 100644
index 0000000..bfa8909
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatABGR.MinMax.g.cs
@@ -0,0 +1,12 @@
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+public sealed partial class ColorFormatABGR
+{
+ #region Methods
+
+ unsafe IMinMax IColorFormat.MinMax(ReadOnlySpan data) => PixelHelper.MinMax(MemoryMarshal.Cast(data));
+
+ #endregion
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatABGR.Quantize.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatABGR.Quantize.g.cs
new file mode 100644
index 0000000..d8c4820
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatABGR.Quantize.g.cs
@@ -0,0 +1,21 @@
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+public sealed partial class ColorFormatABGR
+{
+ #region Methods
+
+ unsafe IColor[] IColorFormat.CreateColorPalette(ReadOnlySpan data, int paletteSize)
+ {
+ ColorABGR[] colors = PixelHelper.CreateColorPalette(MemoryMarshal.Cast(data), paletteSize);
+
+ IColor[] result = new IColor[colors.Length];
+ for(int i = 0; i < colors.Length; i++)
+ result[i] = colors[i];
+
+ return result;
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatABGR.Sum.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatABGR.Sum.g.cs
new file mode 100644
index 0000000..8747e73
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatABGR.Sum.g.cs
@@ -0,0 +1,12 @@
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+public sealed partial class ColorFormatABGR
+{
+ #region Methods
+
+ unsafe ISum IColorFormat.Sum(ReadOnlySpan data) => PixelHelper.Sum(MemoryMarshal.Cast(data));
+
+ #endregion
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatABGR.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatABGR.g.cs
new file mode 100644
index 0000000..076f185
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatABGR.g.cs
@@ -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
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatARGB.Average.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatARGB.Average.g.cs
new file mode 100644
index 0000000..75a4c40
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatARGB.Average.g.cs
@@ -0,0 +1,12 @@
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+public sealed partial class ColorFormatARGB
+{
+ #region Methods
+
+ unsafe IColor IColorFormat.Average(ReadOnlySpan data) => PixelHelper.Average(MemoryMarshal.Cast(data));
+
+ #endregion
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatARGB.MinMax.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatARGB.MinMax.g.cs
new file mode 100644
index 0000000..37d06a3
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatARGB.MinMax.g.cs
@@ -0,0 +1,12 @@
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+public sealed partial class ColorFormatARGB
+{
+ #region Methods
+
+ unsafe IMinMax IColorFormat.MinMax(ReadOnlySpan data) => PixelHelper.MinMax(MemoryMarshal.Cast(data));
+
+ #endregion
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatARGB.Quantize.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatARGB.Quantize.g.cs
new file mode 100644
index 0000000..411e19c
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatARGB.Quantize.g.cs
@@ -0,0 +1,21 @@
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+public sealed partial class ColorFormatARGB
+{
+ #region Methods
+
+ unsafe IColor[] IColorFormat.CreateColorPalette(ReadOnlySpan data, int paletteSize)
+ {
+ ColorARGB[] colors = PixelHelper.CreateColorPalette(MemoryMarshal.Cast(data), paletteSize);
+
+ IColor[] result = new IColor[colors.Length];
+ for(int i = 0; i < colors.Length; i++)
+ result[i] = colors[i];
+
+ return result;
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatARGB.Sum.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatARGB.Sum.g.cs
new file mode 100644
index 0000000..0275eff
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatARGB.Sum.g.cs
@@ -0,0 +1,12 @@
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+public sealed partial class ColorFormatARGB
+{
+ #region Methods
+
+ unsafe ISum IColorFormat.Sum(ReadOnlySpan data) => PixelHelper.Sum(MemoryMarshal.Cast(data));
+
+ #endregion
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatARGB.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatARGB.g.cs
new file mode 100644
index 0000000..5dcb76d
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatARGB.g.cs
@@ -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
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGR.Average.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGR.Average.g.cs
new file mode 100644
index 0000000..932d2c7
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGR.Average.g.cs
@@ -0,0 +1,12 @@
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+public sealed partial class ColorFormatBGR
+{
+ #region Methods
+
+ unsafe IColor IColorFormat.Average(ReadOnlySpan data) => PixelHelper.Average(MemoryMarshal.Cast(data));
+
+ #endregion
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGR.MinMax.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGR.MinMax.g.cs
new file mode 100644
index 0000000..2276eb2
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGR.MinMax.g.cs
@@ -0,0 +1,12 @@
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+public sealed partial class ColorFormatBGR
+{
+ #region Methods
+
+ unsafe IMinMax IColorFormat.MinMax(ReadOnlySpan data) => PixelHelper.MinMax(MemoryMarshal.Cast(data));
+
+ #endregion
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGR.Quantize.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGR.Quantize.g.cs
new file mode 100644
index 0000000..0f82830
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGR.Quantize.g.cs
@@ -0,0 +1,21 @@
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+public sealed partial class ColorFormatBGR
+{
+ #region Methods
+
+ unsafe IColor[] IColorFormat.CreateColorPalette(ReadOnlySpan data, int paletteSize)
+ {
+ ColorBGR[] colors = PixelHelper.CreateColorPalette(MemoryMarshal.Cast(data), paletteSize);
+
+ IColor[] result = new IColor[colors.Length];
+ for(int i = 0; i < colors.Length; i++)
+ result[i] = colors[i];
+
+ return result;
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGR.Sum.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGR.Sum.g.cs
new file mode 100644
index 0000000..4d46ad0
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGR.Sum.g.cs
@@ -0,0 +1,12 @@
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+public sealed partial class ColorFormatBGR
+{
+ #region Methods
+
+ unsafe ISum IColorFormat.Sum(ReadOnlySpan data) => PixelHelper.Sum(MemoryMarshal.Cast(data));
+
+ #endregion
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGR.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGR.g.cs
new file mode 100644
index 0000000..1de747c
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGR.g.cs
@@ -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
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGRA.Average.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGRA.Average.g.cs
new file mode 100644
index 0000000..2226df5
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGRA.Average.g.cs
@@ -0,0 +1,12 @@
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+public sealed partial class ColorFormatBGRA
+{
+ #region Methods
+
+ unsafe IColor IColorFormat.Average(ReadOnlySpan data) => PixelHelper.Average(MemoryMarshal.Cast(data));
+
+ #endregion
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGRA.MinMax.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGRA.MinMax.g.cs
new file mode 100644
index 0000000..d954759
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGRA.MinMax.g.cs
@@ -0,0 +1,12 @@
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+public sealed partial class ColorFormatBGRA
+{
+ #region Methods
+
+ unsafe IMinMax IColorFormat.MinMax(ReadOnlySpan data) => PixelHelper.MinMax(MemoryMarshal.Cast(data));
+
+ #endregion
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGRA.Quantize.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGRA.Quantize.g.cs
new file mode 100644
index 0000000..9c1e1e6
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGRA.Quantize.g.cs
@@ -0,0 +1,21 @@
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+public sealed partial class ColorFormatBGRA
+{
+ #region Methods
+
+ unsafe IColor[] IColorFormat.CreateColorPalette(ReadOnlySpan data, int paletteSize)
+ {
+ ColorBGRA[] colors = PixelHelper.CreateColorPalette(MemoryMarshal.Cast(data), paletteSize);
+
+ IColor[] result = new IColor[colors.Length];
+ for(int i = 0; i < colors.Length; i++)
+ result[i] = colors[i];
+
+ return result;
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGRA.Sum.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGRA.Sum.g.cs
new file mode 100644
index 0000000..e4f3926
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGRA.Sum.g.cs
@@ -0,0 +1,12 @@
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+public sealed partial class ColorFormatBGRA
+{
+ #region Methods
+
+ unsafe ISum IColorFormat.Sum(ReadOnlySpan data) => PixelHelper.Sum(MemoryMarshal.Cast(data));
+
+ #endregion
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGRA.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGRA.g.cs
new file mode 100644
index 0000000..17feeb9
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGRA.g.cs
@@ -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
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGB.Average.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGB.Average.g.cs
new file mode 100644
index 0000000..9891fb8
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGB.Average.g.cs
@@ -0,0 +1,12 @@
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+public sealed partial class ColorFormatRGB
+{
+ #region Methods
+
+ unsafe IColor IColorFormat.Average(ReadOnlySpan data) => PixelHelper.Average(MemoryMarshal.Cast(data));
+
+ #endregion
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGB.MinMax.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGB.MinMax.g.cs
new file mode 100644
index 0000000..31c5360
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGB.MinMax.g.cs
@@ -0,0 +1,12 @@
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+public sealed partial class ColorFormatRGB
+{
+ #region Methods
+
+ unsafe IMinMax IColorFormat.MinMax(ReadOnlySpan data) => PixelHelper.MinMax(MemoryMarshal.Cast(data));
+
+ #endregion
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGB.Quantize.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGB.Quantize.g.cs
new file mode 100644
index 0000000..3a7daca
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGB.Quantize.g.cs
@@ -0,0 +1,21 @@
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+public sealed partial class ColorFormatRGB
+{
+ #region Methods
+
+ unsafe IColor[] IColorFormat.CreateColorPalette(ReadOnlySpan data, int paletteSize)
+ {
+ ColorRGB[] colors = PixelHelper.CreateColorPalette(MemoryMarshal.Cast(data), paletteSize);
+
+ IColor[] result = new IColor[colors.Length];
+ for(int i = 0; i < colors.Length; i++)
+ result[i] = colors[i];
+
+ return result;
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGB.Sum.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGB.Sum.g.cs
new file mode 100644
index 0000000..70a9b12
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGB.Sum.g.cs
@@ -0,0 +1,12 @@
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+public sealed partial class ColorFormatRGB
+{
+ #region Methods
+
+ unsafe ISum IColorFormat.Sum(ReadOnlySpan data) => PixelHelper.Sum(MemoryMarshal.Cast(data));
+
+ #endregion
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGB.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGB.g.cs
new file mode 100644
index 0000000..1208503
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGB.g.cs
@@ -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
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGBA.Average.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGBA.Average.g.cs
new file mode 100644
index 0000000..609b087
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGBA.Average.g.cs
@@ -0,0 +1,12 @@
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+public sealed partial class ColorFormatRGBA
+{
+ #region Methods
+
+ unsafe IColor IColorFormat.Average(ReadOnlySpan data) => PixelHelper.Average(MemoryMarshal.Cast(data));
+
+ #endregion
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGBA.MinMax.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGBA.MinMax.g.cs
new file mode 100644
index 0000000..381cb6b
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGBA.MinMax.g.cs
@@ -0,0 +1,12 @@
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+public sealed partial class ColorFormatRGBA
+{
+ #region Methods
+
+ unsafe IMinMax IColorFormat.MinMax(ReadOnlySpan data) => PixelHelper.MinMax(MemoryMarshal.Cast(data));
+
+ #endregion
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGBA.Quantize.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGBA.Quantize.g.cs
new file mode 100644
index 0000000..43a36dd
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGBA.Quantize.g.cs
@@ -0,0 +1,21 @@
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+public sealed partial class ColorFormatRGBA
+{
+ #region Methods
+
+ unsafe IColor[] IColorFormat.CreateColorPalette(ReadOnlySpan data, int paletteSize)
+ {
+ ColorRGBA[] colors = PixelHelper.CreateColorPalette(MemoryMarshal.Cast(data), paletteSize);
+
+ IColor[] result = new IColor[colors.Length];
+ for(int i = 0; i < colors.Length; i++)
+ result[i] = colors[i];
+
+ return result;
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGBA.Sum.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGBA.Sum.g.cs
new file mode 100644
index 0000000..1ffb1ff
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGBA.Sum.g.cs
@@ -0,0 +1,12 @@
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+public sealed partial class ColorFormatRGBA
+{
+ #region Methods
+
+ unsafe ISum IColorFormat.Sum(ReadOnlySpan data) => PixelHelper.Sum(MemoryMarshal.Cast(data));
+
+ #endregion
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGBA.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGBA.g.cs
new file mode 100644
index 0000000..1e29a15
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGBA.g.cs
@@ -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
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorGeneratorAttribute.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorGeneratorAttribute.g.cs
new file mode 100644
index 0000000..d847095
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorGeneratorAttribute.g.cs
@@ -0,0 +1,4 @@
+namespace HPPH;
+
+[AttributeUsage(AttributeTargets.Struct)]
+internal class ColorGeneratorAttribute : Attribute;
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorRGB.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorRGB.g.cs
new file mode 100644
index 0000000..34b6b9d
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorRGB.g.cs
@@ -0,0 +1,55 @@
+// ReSharper disable ConvertToAutoProperty
+// ReSharper disable ConvertToAutoPropertyWhenPossible
+// ReSharper disable ReplaceWithPrimaryConstructorParameter
+
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+///
+/// Represents a color in 24 bit RGB-format.
+///
+///
+/// Initializes a new instance of the class.
+///
+/// The Red-component of the color.
+/// The Green-component of the color.
+/// The Blue-component of the color.
+[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
+
+ ///
+ public static IColorFormat ColorFormat => IColorFormat.RGB;
+
+ private readonly byte _r = r;
+ private readonly byte _g = g;
+ private readonly byte _b = b;
+
+ ///
+ public byte R => _r;
+
+ ///
+ public byte G => _g;
+
+ ///
+ public byte B => _b;
+
+ ///
+ public byte A => byte.MaxValue;
+
+ #endregion
+
+ #region Methods
+
+ ///
+ public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
+
+ ///
+ public static IColor Create(byte r, byte g, byte b, byte a) => new ColorRGB(r, g, b);
+
+ #endregion
+}
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorRGBA.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorRGBA.g.cs
new file mode 100644
index 0000000..134a1a2
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorRGBA.g.cs
@@ -0,0 +1,57 @@
+// ReSharper disable ConvertToAutoProperty
+// ReSharper disable ConvertToAutoPropertyWhenPossible
+// ReSharper disable ReplaceWithPrimaryConstructorParameter
+
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+///
+/// Represents a color in 32 bit RGBA-format.
+///
+///
+/// Initializes a new instance of the class.
+///
+/// The Red-component of the color.
+/// The Green-component of the color.
+/// The Blue-component of the color.
+/// The Alpha-component of the color.
+[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
+
+ ///
+ 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;
+
+ ///
+ public byte R => _r;
+
+ ///
+ public byte G => _g;
+
+ ///
+ public byte B => _b;
+
+ ///
+ public byte A => _a;
+
+ #endregion
+
+ #region Methods
+
+ ///
+ public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
+
+ ///
+ public static IColor Create(byte r, byte g, byte b, byte a) => new ColorRGBA(r, g, b, a);
+
+ #endregion
+}
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/IColorFormat.Average.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/IColorFormat.Average.g.cs
new file mode 100644
index 0000000..9227055
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/IColorFormat.Average.g.cs
@@ -0,0 +1,6 @@
+namespace HPPH;
+
+public partial interface IColorFormat
+{
+ internal IColor Average(ReadOnlySpan data);
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/IColorFormat.Instances.cs.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/IColorFormat.Instances.cs.g.cs
new file mode 100644
index 0000000..49328d2
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/IColorFormat.Instances.cs.g.cs
@@ -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
+}
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/IColorFormat.MinMax.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/IColorFormat.MinMax.g.cs
new file mode 100644
index 0000000..98aa3ea
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/IColorFormat.MinMax.g.cs
@@ -0,0 +1,6 @@
+namespace HPPH;
+
+public partial interface IColorFormat
+{
+ internal IMinMax MinMax(ReadOnlySpan data);
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/IColorFormat.Quantize.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/IColorFormat.Quantize.g.cs
new file mode 100644
index 0000000..2bb8aca
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/IColorFormat.Quantize.g.cs
@@ -0,0 +1,6 @@
+namespace HPPH;
+
+public partial interface IColorFormat
+{
+ internal IColor[] CreateColorPalette(ReadOnlySpan data, int paletteSize);
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/IColorFormat.Sum.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/IColorFormat.Sum.g.cs
new file mode 100644
index 0000000..d05861e
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/IColorFormat.Sum.g.cs
@@ -0,0 +1,6 @@
+namespace HPPH;
+
+public partial interface IColorFormat
+{
+ internal ISum Sum(ReadOnlySpan data);
+}
\ No newline at end of file
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/MinMaxABGR.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/MinMaxABGR.g.cs
new file mode 100644
index 0000000..9fb0a3f
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/MinMaxABGR.g.cs
@@ -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
+
+ ///
+ public override string ToString() => $"[A: {AlphaMin}-{AlphaMax}, R: {RedMin}-{RedMax}, G: {GreenMin}-{GreenMax}, B: {BlueMin}-{BlueMax}]";
+
+ #endregion
+}
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/MinMaxARGB.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/MinMaxARGB.g.cs
new file mode 100644
index 0000000..4065e99
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/MinMaxARGB.g.cs
@@ -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
+
+ ///
+ public override string ToString() => $"[A: {AlphaMin}-{AlphaMax}, R: {RedMin}-{RedMax}, G: {GreenMin}-{GreenMax}, B: {BlueMin}-{BlueMax}]";
+
+ #endregion
+}
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/MinMaxBGR.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/MinMaxBGR.g.cs
new file mode 100644
index 0000000..ef4d4e7
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/MinMaxBGR.g.cs
@@ -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
+
+ ///
+ public override string ToString() => $"[A: {AlphaMin}-{AlphaMax}, R: {RedMin}-{RedMax}, G: {GreenMin}-{GreenMax}, B: {BlueMin}-{BlueMax}]";
+
+ #endregion
+}
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/MinMaxBGRA.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/MinMaxBGRA.g.cs
new file mode 100644
index 0000000..ab76312
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/MinMaxBGRA.g.cs
@@ -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
+
+ ///
+ public override string ToString() => $"[A: {AlphaMin}-{AlphaMax}, R: {RedMin}-{RedMax}, G: {GreenMin}-{GreenMax}, B: {BlueMin}-{BlueMax}]";
+
+ #endregion
+}
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/MinMaxRGB.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/MinMaxRGB.g.cs
new file mode 100644
index 0000000..0c5e9ce
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/MinMaxRGB.g.cs
@@ -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
+
+ ///
+ public override string ToString() => $"[A: {AlphaMin}-{AlphaMax}, R: {RedMin}-{RedMax}, G: {GreenMin}-{GreenMax}, B: {BlueMin}-{BlueMax}]";
+
+ #endregion
+}
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/MinMaxRGBA.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/MinMaxRGBA.g.cs
new file mode 100644
index 0000000..44d1c4a
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/MinMaxRGBA.g.cs
@@ -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
+
+ ///
+ public override string ToString() => $"[A: {AlphaMin}-{AlphaMax}, R: {RedMin}-{RedMax}, G: {GreenMin}-{GreenMax}, B: {BlueMin}-{BlueMax}]";
+
+ #endregion
+}
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/SumABGR.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/SumABGR.g.cs
new file mode 100644
index 0000000..5330cbf
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/SumABGR.g.cs
@@ -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
+
+ ///
+ public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
+
+ #endregion
+}
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/SumARGB.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/SumARGB.g.cs
new file mode 100644
index 0000000..351ec90
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/SumARGB.g.cs
@@ -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
+
+ ///
+ public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
+
+ #endregion
+}
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/SumBGR.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/SumBGR.g.cs
new file mode 100644
index 0000000..df489ce
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/SumBGR.g.cs
@@ -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
+
+ ///
+ public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
+
+ #endregion
+}
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/SumBGRA.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/SumBGRA.g.cs
new file mode 100644
index 0000000..2e5cf13
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/SumBGRA.g.cs
@@ -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
+
+ ///
+ public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
+
+ #endregion
+}
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/SumRGB.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/SumRGB.g.cs
new file mode 100644
index 0000000..c852c37
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/SumRGB.g.cs
@@ -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
+
+ ///
+ public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
+
+ #endregion
+}
diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/SumRGBA.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/SumRGBA.g.cs
new file mode 100644
index 0000000..7799ff2
--- /dev/null
+++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/SumRGBA.g.cs
@@ -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
+
+ ///
+ public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
+
+ #endregion
+}
diff --git a/HPPH/HPPH.csproj b/HPPH/HPPH.csproj
new file mode 100644
index 0000000..0561bbc
--- /dev/null
+++ b/HPPH/HPPH.csproj
@@ -0,0 +1,20 @@
+
+
+
+ net8.0
+ enable
+ enable
+ true
+ true
+ Generated
+
+
+
+
+
+
+
+
+
+
+
diff --git a/HPPH/HPPH.csproj.DotSettings b/HPPH/HPPH.csproj.DotSettings
new file mode 100644
index 0000000..f85fea1
--- /dev/null
+++ b/HPPH/HPPH.csproj.DotSettings
@@ -0,0 +1,15 @@
+
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
\ No newline at end of file
diff --git a/HPPH/Images/IImage.cs b/HPPH/Images/IImage.cs
new file mode 100644
index 0000000..bd5cac6
--- /dev/null
+++ b/HPPH/Images/IImage.cs
@@ -0,0 +1,189 @@
+namespace HPPH;
+
+///
+/// Represents a image.
+///
+public interface IImage : IEnumerable
+{
+ ///
+ /// Gets the color format used in this image.
+ ///
+ IColorFormat ColorFormat { get; }
+
+ ///
+ /// Gets the width of this image.
+ ///
+ int Width { get; }
+
+ ///
+ /// Gets the height of this image.
+ ///
+ int Height { get; }
+
+ ///
+ /// Gets the size in bytes of this image.
+ ///
+ int SizeInBytes { get; }
+
+ ///
+ /// Gets the color at the specified location.
+ ///
+ /// The X-location to read.
+ /// The Y-location to read.
+ /// The color at the specified location.
+ IColor this[int x, int y] { get; }
+
+ ///
+ /// Gets an image representing the specified location.
+ ///
+ /// The X-location of the image.
+ /// The Y-location of the image.
+ /// The width of the sub-image.
+ ///
+ ///
+ IImage this[int x, int y, int width, int height] { get; }
+
+ ///
+ /// Gets a list of all rows of this image.
+ ///
+ IImageRows Rows { get; }
+
+ ///
+ /// Gets a list of all columns of this image.
+ ///
+ IImageColumns Columns { get; }
+
+ ///
+ /// Gets an representing this .
+ ///
+ /// The color-type of the iamge.
+ /// The .
+ RefImage AsRefImage() where TColor : struct, IColor;
+
+ ///
+ /// Copies the contents of this into a destination instance.
+ ///
+ /// The destination instance.
+ ///
+ /// Thrown when is shorter than the source instance.
+ ///
+ void CopyTo(Span destination);
+
+ ///
+ /// Allocates a new array and copies this into it.
+ ///
+ /// The new array containing the data of this .
+ byte[] ToArray();
+
+ ///
+ /// Represents a list of rows of an image.
+ ///
+ public interface IImageRows : IEnumerable
+ {
+ ///
+ /// Gets the amount of rows in this list.
+ ///
+ int Count { get; }
+
+ ///
+ /// Gets a specific .
+ ///
+ /// The ´row to get.
+ /// The requested .
+ IImageRow this[int column] { get; }
+ }
+
+ ///
+ /// Represents a list of columns of an image.
+ ///
+ public interface IImageColumns : IEnumerable
+ {
+ ///
+ /// Gets the amount of columns in this list.
+ ///
+ int Count { get; }
+
+ ///
+ /// Gets a specific .
+ ///
+ /// The column to get.
+ /// The requested .
+ IImageColumn this[int column] { get; }
+ }
+
+ ///
+ /// Represents a single row of an image.
+ ///
+ public interface IImageRow : IEnumerable
+ {
+ ///
+ /// Gets the length of the row.
+ ///
+ int Length { get; }
+
+ ///
+ /// Gets the size in bytes of this row.
+ ///
+ int SizeInBytes { get; }
+
+ ///
+ /// Gets the at the specified location.
+ ///
+ /// The location to get the color from.
+ /// The at the specified location.
+ IColor this[int x] { get; }
+
+ ///
+ /// Copies the contents of this into a destination instance.
+ ///
+ /// The destination instance.
+ ///
+ /// Thrown when is shorter than the source instance.
+ ///
+ void CopyTo(Span destination);
+
+ ///
+ /// Allocates a new array and copies this into it.
+ ///
+ /// The new array containing the data of this .
+ byte[] ToArray();
+ }
+
+ ///
+ /// Represents a single column of an image.
+ ///
+ public interface IImageColumn : IEnumerable
+ {
+ ///
+ /// Gets the length of the column.
+ ///
+ int Length { get; }
+
+ ///
+ /// Gets the size in bytes of this column.
+ ///
+ int SizeInBytes { get; }
+
+ ///
+ /// Gets the at the specified location.
+ ///
+ /// The location to get the color from.
+ /// The at the specified location.
+ IColor this[int y] { get; }
+
+ ///
+ /// Copies the contents of this into a destination instance.
+ ///
+ /// The destination instance.
+ ///
+ /// Thrown when is shorter than the source instance.
+ ///
+ void CopyTo(Span destination);
+
+ ///
+ /// Allocates a new array and copies this into it.
+ ///
+ /// The new array containing the data of this .
+ byte[] ToArray();
+ }
+}
\ No newline at end of file
diff --git a/HPPH/Images/Image.cs b/HPPH/Images/Image.cs
new file mode 100644
index 0000000..3833d78
--- /dev/null
+++ b/HPPH/Images/Image.cs
@@ -0,0 +1,428 @@
+using System.Collections;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace HPPH;
+
+///
+public sealed class Image : IImage
+ where TColor : struct, IColor
+{
+ #region Properties & Fields
+
+ private readonly byte[] _buffer;
+
+ private readonly int _x;
+ private readonly int _y;
+ private readonly int _stride;
+
+ ///
+ public IColorFormat ColorFormat
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => TColor.ColorFormat;
+ }
+
+ ///
+ public int Width { get; }
+
+ ///
+ public int Height { get; }
+
+ ///
+ public int SizeInBytes => Width * Height * ColorFormat.BytesPerPixel;
+
+ #endregion
+
+ #region Indexer
+
+ ///
+ 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(_buffer)[((_y + y) * _stride) + (_x + x)];
+ }
+ }
+
+ ///
+ 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(_buffer, _x + x, _y + y, width, height, _stride);
+ }
+ }
+
+ ///
+ public IImage.IImageRows Rows
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => new ImageRows(_buffer, _x, _y, Width, Height, _stride);
+ }
+
+ ///
+ 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
+
+ ///
+ public void CopyTo(Span 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 target = destination;
+ foreach (IImage.IImageRow row in rows)
+ {
+ row.CopyTo(target);
+ target = target[targetStride..];
+ }
+ }
+
+ ///
+ public byte[] ToArray()
+ {
+ byte[] array = new byte[SizeInBytes];
+ CopyTo(array);
+ return array;
+ }
+
+ ///
+ public RefImage AsRefImage()
+ 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(MemoryMarshal.Cast(_buffer), _x, _y, Width, Height, _stride);
+ }
+
+ ///
+ public IEnumerator GetEnumerator()
+ {
+ for (int y = 0; y < Height; y++)
+ for (int x = 0; x < Width; x++)
+ yield return this[x, y];
+ }
+
+ ///
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
+ #endregion
+
+ #region Indexer-Classes
+
+ ///
+ 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;
+
+ ///
+ public int Count => _height;
+
+ #endregion
+
+ #region Indexer
+
+ ///
+ 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
+
+ ///
+ public IEnumerator GetEnumerator()
+ {
+ for (int y = 0; y < _height; y++)
+ yield return this[y];
+ }
+
+ ///
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
+ #endregion
+ }
+
+ ///
+ private sealed class ImageRow : IImage.IImageRow
+ {
+ #region Properties & Fields
+
+ private readonly byte[] _buffer;
+ private readonly int _start;
+ private readonly int _length;
+
+ ///
+ public int Length => _length;
+
+ ///
+ public int SizeInBytes => Length * TColor.ColorFormat.BytesPerPixel;
+
+ #endregion
+
+ #region Indexer
+
+ ///
+ public IColor this[int x]
+ {
+ get
+ {
+ if ((x < 0) || (x >= _length)) throw new IndexOutOfRangeException();
+
+ ReadOnlySpan row = MemoryMarshal.Cast(_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
+
+ ///
+ public void CopyTo(Span 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(_buffer).Slice(_start, _length).CopyTo(MemoryMarshal.Cast(destination));
+ }
+
+ ///
+ public byte[] ToArray()
+ {
+ byte[] array = new byte[SizeInBytes];
+ CopyTo(array);
+ return array;
+ }
+
+ ///
+ public IEnumerator GetEnumerator()
+ {
+ for (int x = 0; x < _length; x++)
+ yield return this[x];
+ }
+
+ ///
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
+ #endregion
+ }
+
+ ///
+ 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;
+
+ ///
+ public int Count => _width;
+
+ #endregion
+
+ #region Indexer
+
+ ///
+ 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
+
+ ///
+ public IEnumerator GetEnumerator()
+ {
+ for (int y = 0; y < _height; y++)
+ yield return this[y];
+ }
+
+ ///
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
+ #endregion
+ }
+
+ ///
+ 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;
+
+ ///
+ public int Length => _length;
+
+ ///
+ public int SizeInBytes => Length * TColor.ColorFormat.BytesPerPixel;
+
+ #endregion
+
+ #region Indexer
+
+ ///
+ public IColor this[int y]
+ {
+ get
+ {
+ if ((y < 0) || (y >= _length)) throw new IndexOutOfRangeException();
+
+ ReadOnlySpan data = MemoryMarshal.Cast(_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
+
+ ///