diff --git a/AssetRipper.TextureDecoder.ColorGenerator/Program.cs b/AssetRipper.TextureDecoder.ColorGenerator/Program.cs index ed39e3d..88f6790 100644 --- a/AssetRipper.TextureDecoder.ColorGenerator/Program.cs +++ b/AssetRipper.TextureDecoder.ColorGenerator/Program.cs @@ -137,11 +137,70 @@ internal static partial class Program writer.WriteLine($"static Type IColor.ChannelType => typeof({typeName});"); } + private static void WriteBlackWhiteStaticProperties(IndentedTextWriter writer, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha, string colorTypeName, string channelTypeName) + { + string minValue = $"NumericConversion.GetMinimumValueSafe<{channelTypeName}>()"; + string maxValue = $"NumericConversion.GetMaximumValueSafe<{channelTypeName}>()"; + + //Black + { + writer.Write($"public static {colorTypeName}<{channelTypeName}> Black => new("); + int rgbCount = CountRGB(hasRed, hasGreen, hasBlue); + for (int i = 0; i < rgbCount; i++) + { + if (i != 0) + { + writer.Write(", "); + } + writer.Write(minValue); + } + if (hasAlpha) + { + if (rgbCount != 0) + { + writer.Write(", "); + } + writer.Write(maxValue); + } + writer.WriteLine(");"); + } + + //White + if (hasRed || hasGreen || hasBlue) + { + writer.Write($"public static {colorTypeName}<{channelTypeName}> White => new("); + int rgbaCount = CountRGBA(hasRed, hasGreen, hasBlue, hasAlpha); + for (int i = 0; i < rgbaCount; i++) + { + if (i != 0) + { + writer.Write(", "); + } + writer.Write(maxValue); + } + writer.WriteLine(");"); + } + else + { + writer.WriteLine($"static {colorTypeName}<{channelTypeName}> IColor<{colorTypeName}<{channelTypeName}>, {channelTypeName}>.White => throw new NotSupportedException();"); + } + + static int CountRGB(bool hasRed, bool hasGreen, bool hasBlue) + { + return (hasRed ? 1 : 0) + (hasGreen ? 1 : 0) + (hasBlue ? 1 : 0); + } + + static int CountRGBA(bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha) + { + return CountRGB(hasRed, hasGreen, hasBlue) + (hasAlpha ? 1 : 0); + } + } + private static void WriteGenericColor(IndentedTextWriter writer, string name, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha) { const string typeName = "T"; - const string minValue = $"NumericConversion.GetMinimumValueSafe()"; - const string maxValue = $"NumericConversion.GetMaximumValueSafe()"; + const string minValue = "NumericConversion.GetMinimumValueSafe()"; + const string maxValue = "NumericConversion.GetMaximumValueSafe()"; writer.WriteGeneratedCodeWarning(); writer.WriteLineNoTabs(); @@ -149,10 +208,7 @@ internal static partial class Program writer.WriteFileScopedNamespace(OutputNamespace); writer.WriteLineNoTabs(); - string constraints = hasRed && hasGreen && hasBlue && hasAlpha - ? "unmanaged" - : "unmanaged, INumberBase, IMinMaxValue"; - writer.WriteLine($"public partial struct {name} : IColor<{typeName}> where T : {constraints}"); + writer.WriteLine($"public partial struct {name} : IColor<{name}, T> where T : unmanaged, INumberBase, IMinMaxValue"); using (new CurlyBrackets(writer)) { WriteProperty(writer, hasRed, typeName, 'R', minValue); @@ -172,6 +228,8 @@ internal static partial class Program writer.WriteLineNoTabs(); WriteNonGenericStaticProperties(writer, hasRed, hasGreen, hasBlue, hasAlpha, true, typeName); writer.WriteLineNoTabs(); + WriteBlackWhiteStaticProperties(writer, hasRed, hasGreen, hasBlue, hasAlpha, name, typeName); + writer.WriteLineNoTabs(); WriteToString(writer, hasRed, hasGreen, hasBlue, hasAlpha); } } diff --git a/AssetRipper.TextureDecoder/Rgb/Formats/ColorA.g.cs b/AssetRipper.TextureDecoder/Rgb/Formats/ColorA.g.cs index a86b442..3957165 100644 --- a/AssetRipper.TextureDecoder/Rgb/Formats/ColorA.g.cs +++ b/AssetRipper.TextureDecoder/Rgb/Formats/ColorA.g.cs @@ -2,7 +2,7 @@ namespace AssetRipper.TextureDecoder.Rgb.Formats; -public partial struct ColorA : IColor where T : unmanaged, INumberBase, IMinMaxValue +public partial struct ColorA : IColor, T> where T : unmanaged, INumberBase, IMinMaxValue { public readonly T R { @@ -49,6 +49,9 @@ public partial struct ColorA : IColor where T : unmanaged, INumberBase, static bool IColor.ChannelsAreFullyUtilized => true; static Type IColor.ChannelType => typeof(T); + public static ColorA Black => new(NumericConversion.GetMaximumValueSafe()); + static ColorA IColor, T>.White => throw new NotSupportedException(); + public override string ToString() { return $"{{ A: {A} }}"; diff --git a/AssetRipper.TextureDecoder/Rgb/Formats/ColorR.g.cs b/AssetRipper.TextureDecoder/Rgb/Formats/ColorR.g.cs index adbeacc..704d17c 100644 --- a/AssetRipper.TextureDecoder/Rgb/Formats/ColorR.g.cs +++ b/AssetRipper.TextureDecoder/Rgb/Formats/ColorR.g.cs @@ -2,7 +2,7 @@ namespace AssetRipper.TextureDecoder.Rgb.Formats; -public partial struct ColorR : IColor where T : unmanaged, INumberBase, IMinMaxValue +public partial struct ColorR : IColor, T> where T : unmanaged, INumberBase, IMinMaxValue { public T R { get; set; } @@ -49,6 +49,9 @@ public partial struct ColorR : IColor where T : unmanaged, INumberBase, static bool IColor.ChannelsAreFullyUtilized => true; static Type IColor.ChannelType => typeof(T); + public static ColorR Black => new(NumericConversion.GetMinimumValueSafe()); + public static ColorR White => new(NumericConversion.GetMaximumValueSafe()); + public override string ToString() { return $"{{ R: {R} }}"; diff --git a/AssetRipper.TextureDecoder/Rgb/Formats/ColorRG.g.cs b/AssetRipper.TextureDecoder/Rgb/Formats/ColorRG.g.cs index 44882a8..921a6c4 100644 --- a/AssetRipper.TextureDecoder/Rgb/Formats/ColorRG.g.cs +++ b/AssetRipper.TextureDecoder/Rgb/Formats/ColorRG.g.cs @@ -2,7 +2,7 @@ namespace AssetRipper.TextureDecoder.Rgb.Formats; -public partial struct ColorRG : IColor where T : unmanaged, INumberBase, IMinMaxValue +public partial struct ColorRG : IColor, T> where T : unmanaged, INumberBase, IMinMaxValue { public T R { get; set; } @@ -47,6 +47,9 @@ public partial struct ColorRG : IColor where T : unmanaged, INumberBase static bool IColor.ChannelsAreFullyUtilized => true; static Type IColor.ChannelType => typeof(T); + public static ColorRG Black => new(NumericConversion.GetMinimumValueSafe(), NumericConversion.GetMinimumValueSafe()); + public static ColorRG White => new(NumericConversion.GetMaximumValueSafe(), NumericConversion.GetMaximumValueSafe()); + public override string ToString() { return $"{{ R: {R}, G: {G} }}"; diff --git a/AssetRipper.TextureDecoder/Rgb/Formats/ColorRGB.g.cs b/AssetRipper.TextureDecoder/Rgb/Formats/ColorRGB.g.cs index 01ee331..c40809e 100644 --- a/AssetRipper.TextureDecoder/Rgb/Formats/ColorRGB.g.cs +++ b/AssetRipper.TextureDecoder/Rgb/Formats/ColorRGB.g.cs @@ -2,7 +2,7 @@ namespace AssetRipper.TextureDecoder.Rgb.Formats; -public partial struct ColorRGB : IColor where T : unmanaged, INumberBase, IMinMaxValue +public partial struct ColorRGB : IColor, T> where T : unmanaged, INumberBase, IMinMaxValue { public T R { get; set; } @@ -45,6 +45,9 @@ public partial struct ColorRGB : IColor where T : unmanaged, INumberBase true; static Type IColor.ChannelType => typeof(T); + public static ColorRGB Black => new(NumericConversion.GetMinimumValueSafe(), NumericConversion.GetMinimumValueSafe(), NumericConversion.GetMinimumValueSafe()); + public static ColorRGB White => new(NumericConversion.GetMaximumValueSafe(), NumericConversion.GetMaximumValueSafe(), NumericConversion.GetMaximumValueSafe()); + public override string ToString() { return $"{{ R: {R}, G: {G}, B: {B} }}"; diff --git a/AssetRipper.TextureDecoder/Rgb/Formats/ColorRGBA.g.cs b/AssetRipper.TextureDecoder/Rgb/Formats/ColorRGBA.g.cs index 440d06e..f2c1e8c 100644 --- a/AssetRipper.TextureDecoder/Rgb/Formats/ColorRGBA.g.cs +++ b/AssetRipper.TextureDecoder/Rgb/Formats/ColorRGBA.g.cs @@ -2,7 +2,7 @@ namespace AssetRipper.TextureDecoder.Rgb.Formats; -public partial struct ColorRGBA : IColor where T : unmanaged +public partial struct ColorRGBA : IColor, T> where T : unmanaged, INumberBase, IMinMaxValue { public T R { get; set; } @@ -43,6 +43,9 @@ public partial struct ColorRGBA : IColor where T : unmanaged static bool IColor.ChannelsAreFullyUtilized => true; static Type IColor.ChannelType => typeof(T); + public static ColorRGBA Black => new(NumericConversion.GetMinimumValueSafe(), NumericConversion.GetMinimumValueSafe(), NumericConversion.GetMinimumValueSafe(), NumericConversion.GetMaximumValueSafe()); + public static ColorRGBA White => new(NumericConversion.GetMaximumValueSafe(), NumericConversion.GetMaximumValueSafe(), NumericConversion.GetMaximumValueSafe(), NumericConversion.GetMaximumValueSafe()); + public override string ToString() { return $"{{ R: {R}, G: {G}, B: {B}, A: {A} }}"; diff --git a/AssetRipper.TextureDecoder/Rgb/IColor`2.cs b/AssetRipper.TextureDecoder/Rgb/IColor`2.cs new file mode 100644 index 0000000..3180940 --- /dev/null +++ b/AssetRipper.TextureDecoder/Rgb/IColor`2.cs @@ -0,0 +1,47 @@ +namespace AssetRipper.TextureDecoder.Rgb; + +/// +/// An for handling color formats with up to 4 channels. +/// +/// +/// When used as a generic type constraint, the methods and properties get devirtualized by the JIT compiler. +/// This prevents boxing when the implementing type is a . +/// +/// +/// The implementing type. +/// +/// +/// Supported types are: +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +public interface IColor : IColor + where TSelf : unmanaged, IColor + where TChannel : unmanaged +{ + /// + /// A black pixel. + /// + static abstract TSelf Black { get; } + /// + /// A white pixel. + /// + static abstract TSelf White { get; } +}