Initial commit (WIP)

This commit is contained in:
DarthAffe committed 2024-07-06 00:04:07 +02:00
1 parent 5474273e70
commit 696f3b955f
121 files changed
+5723 -1

No files matched your search

+50
View File
@@ -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
};
}
@@ -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(?<colorFormat>[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<ImmutableArray<ColorFormatData>> 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<ColorFormatData> 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
}
+3
View File
@@ -0,0 +1,3 @@
namespace HPPH.Generators;
internal record struct ColorSortData(string Namespace, string Class, string ClassModifiers, string Signature, string DataTypeName, string SortValueName);
+135
View File
@@ -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<ImmutableArray<ColorSortData>> 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<TypedConstant> 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<ColorSortData> 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<int> 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<int> 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
}
+47
View File
@@ -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<ColorFormatData> 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<byte> data) => PixelHelper.Average(MemoryMarshal.Cast<byte, Color{{colorFormat.Format}}>(data));
#endregion
}
""";
}
private static string GenerateColorFormatInterfaceAverage()
{
return """
namespace HPPH;
public partial interface IColorFormat
{
internal IColor Average(ReadOnlySpan<byte> data);
}
""";
}
}
+200
View File
@@ -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<ColorFormatData> 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;
/// <summary>
/// Represents a color in 24 bit {{colorFormat.Format}}-format.
/// </summary>
/// <remarks>
/// Initializes a new instance of the <see cref="{{colorFormat.TypeName}}"/> class.
/// </remarks>
/// <param name="{{colorFormat.FirstEntry}}">The {{colorFormat.FirstEntryName}}-component of the color.</param>
/// <param name="{{colorFormat.SecondEntry}}">The {{colorFormat.SecondEntryName}}-component of the color.</param>
/// <param name="{{colorFormat.ThirdEntry}}">The {{colorFormat.ThirdEntryName}}-component of the color.</param>
[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
/// <inheritdoc />
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}};
/// <inheritdoc />
public byte R => _r;
/// <inheritdoc />
public byte G => _g;
/// <inheritdoc />
public byte B => _b;
/// <inheritdoc />
public byte A => byte.MaxValue;
#endregion
#region Methods
/// <inheritdoc />
public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
/// <inheritdoc />
public static IColor Create(byte r, byte g, byte b, byte a) => new {{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;
/// <summary>
/// Represents a color in 32 bit {{colorFormat.Format}}-format.
/// </summary>
/// <remarks>
/// Initializes a new instance of the <see cref="{{colorFormat.TypeName}}"/> class.
/// </remarks>
/// <param name="{{colorFormat.FirstEntry}}">The {{colorFormat.FirstEntryName}}-component of the color.</param>
/// <param name="{{colorFormat.SecondEntry}}">The {{colorFormat.SecondEntryName}}-component of the color.</param>
/// <param name="{{colorFormat.ThirdEntry}}">The {{colorFormat.ThirdEntryName}}-component of the color.</param>
/// <param name="{{colorFormat.FourthEntry}}">The {{colorFormat.FourthEntryName}}-component of the color.</param>
[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
/// <inheritdoc />
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}};
/// <inheritdoc />
public byte R => _r;
/// <inheritdoc />
public byte G => _g;
/// <inheritdoc />
public byte B => _b;
/// <inheritdoc />
public byte A => _a;
#endregion
#region Methods
/// <inheritdoc />
public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]";
/// <inheritdoc />
public static IColor Create(byte r, byte g, byte b, byte a) => new {{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<ColorFormatData> 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();
}
}
@@ -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<ColorFormatData> colorFormats);
}
+162
View File
@@ -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<ColorFormatData> 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
/// <inheritdoc />
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
/// <inheritdoc />
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<byte> data) => PixelHelper.MinMax<Color{{colorFormat.Format}}, MinMax{{colorFormat.Format}}>(MemoryMarshal.Cast<byte, Color{{colorFormat.Format}}>(data));
#endregion
}
""";
}
private static string GenerateColorFormatInterfaceMinMax()
{
return """
namespace HPPH;
public partial interface IColorFormat
{
internal IMinMax MinMax(ReadOnlySpan<byte> data);
}
""";
}
}
+56
View File
@@ -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<ColorFormatData> 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<byte> data, int paletteSize)
{
Color{{colorFormat.Format}}[] colors = PixelHelper.CreateColorPalette<Color{{colorFormat.Format}}>(MemoryMarshal.Cast<byte, Color{{colorFormat.Format}}>(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<byte> data, int paletteSize);
}
""";
}
}
+129
View File
@@ -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<ColorFormatData> 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
/// <inheritdoc />
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
/// <inheritdoc />
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<byte> data) => PixelHelper.Sum<Color{{colorFormat.Format}}, Sum{{colorFormat.Format}}>(MemoryMarshal.Cast<byte, Color{{colorFormat.Format}}>(data));
#endregion
}
""";
}
private static string GenerateColorFormatInterfaceSum()
{
return """
namespace HPPH;
public partial interface IColorFormat
{
internal ISum Sum(ReadOnlySpan<byte> data);
}
""";
}
}
+21
View File
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<IsRoslynComponent>true</IsRoslynComponent>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<DevelopmentDependency>true</DevelopmentDependency>
<IncludeBuildOutput>false</IncludeBuildOutput>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<LangVersion>latest</LangVersion>
<SourceGenerator_EnableLogging>True</SourceGenerator_EnableLogging>
<SourceGenerator_EnableDebug>False</SourceGenerator_EnableDebug>
<SourceGenerator_DetailedLog>True</SourceGenerator_DetailedLog>
<SourceGenerator_IntellisenseFix>True</SourceGenerator_IntellisenseFix>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" />
</ItemGroup>
</Project>
@@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=features/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>