Skip to content

Commit 1957784

Browse files
committed
Support specifying channels
1 parent 1bf5f70 commit 1957784

14 files changed

Lines changed: 1069 additions & 17 deletions

File tree

AssetRipper.TextureDecoder.ColorGenerator/Program.cs

Lines changed: 189 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ namespace AssetRipper.TextureDecoder.ColorGenerator;
77

88
internal static partial class Program
99
{
10-
private const string AttributeNamespace = "AssetRipper.TextureDecoder.Attributes";
11-
private const string OutputNamespace = "AssetRipper.TextureDecoder.Rgb.Formats";
12-
private const string OutputFolder = "../../../../AssetRipper.TextureDecoder/Rgb/Formats/";
10+
private const string RgbNamespace = "AssetRipper.TextureDecoder.Rgb";
11+
private const string RgbFolder = "../../../../AssetRipper.TextureDecoder/Rgb/";
12+
private const string FormatsNamespace = "AssetRipper.TextureDecoder.Rgb.Formats";
13+
private const string FormatsFolder = "../../../../AssetRipper.TextureDecoder/Rgb/Formats/";
1314

1415
/// <summary>
1516
/// Name, Type, Red, Blue, Green, Alpha, Fully Utilized
@@ -46,6 +47,10 @@ static void Main()
4647
{
4748
WriteGenericColor(genericColor);
4849
}
50+
for (int i = 1; i <= 4; i++)
51+
{
52+
WriteSuperGenericColor(i);
53+
}
4954
NumericConversionGenerator.Run();
5055
Console.WriteLine("Done!");
5156
}
@@ -54,24 +59,32 @@ private static void WriteCustomColor((string, Type, bool, bool, bool, bool, bool
5459
{
5560
(string name, Type type, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha, bool fullyUtilized) = details;
5661
Console.WriteLine(name);
57-
using IndentedTextWriter writer = IndentedTextWriterFactory.Create(OutputFolder, name);
62+
using IndentedTextWriter writer = IndentedTextWriterFactory.Create(FormatsFolder, name);
5863
WriteCustomColor(writer, name, type, hasRed, hasGreen, hasBlue, hasAlpha, fullyUtilized);
5964
}
6065

6166
private static void WriteGenericColor((string, bool, bool, bool, bool) details)
6267
{
6368
(string name, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha) = details;
6469
Console.WriteLine(name);
65-
using IndentedTextWriter writer = IndentedTextWriterFactory.Create(OutputFolder, name);
70+
using IndentedTextWriter writer = IndentedTextWriterFactory.Create(FormatsFolder, name);
6671
WriteGenericColor(writer, name, hasRed, hasGreen, hasBlue, hasAlpha);
6772
}
6873

74+
private static void WriteSuperGenericColor(int channelCount)
75+
{
76+
string name = $"Color`{channelCount}";
77+
Console.WriteLine(name);
78+
using IndentedTextWriter writer = IndentedTextWriterFactory.Create(RgbFolder, name);
79+
WriteSuperGenericColor(writer, channelCount);
80+
}
81+
6982
private static void WriteCustomColor(IndentedTextWriter writer, string name, Type type, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha, bool fullyUtilized)
7083
{
7184
string typeName = CSharpPrimitives.Dictionary[type].LangName;
7285
writer.WriteGeneratedCodeWarning();
7386
writer.WriteLine();
74-
using (new Namespace(writer, OutputNamespace))
87+
using (new Namespace(writer, FormatsNamespace))
7588
{
7689
writer.WriteLine($"public partial struct {name} : IColor<{typeName}>");
7790
using (new CurlyBrackets(writer))
@@ -129,11 +142,16 @@ private static void WriteToString(IndentedTextWriter writer, bool hasRed, bool h
129142

130143
private static void WriteNonGenericStaticProperties(IndentedTextWriter writer, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha, bool fullyUtilized, string typeName)
131144
{
132-
writer.WriteLine($"static bool IColor.HasRedChannel => {hasRed.ToLowerString()};");
133-
writer.WriteLine($"static bool IColor.HasGreenChannel => {hasGreen.ToLowerString()};");
134-
writer.WriteLine($"static bool IColor.HasBlueChannel => {hasBlue.ToLowerString()};");
135-
writer.WriteLine($"static bool IColor.HasAlphaChannel => {hasAlpha.ToLowerString()};");
136-
writer.WriteLine($"static bool IColor.ChannelsAreFullyUtilized => {fullyUtilized.ToLowerString()};");
145+
WriteNonGenericStaticProperties(writer, hasRed.ToLowerString(), hasGreen.ToLowerString(), hasBlue.ToLowerString(), hasAlpha.ToLowerString(), fullyUtilized.ToLowerString(), typeName);
146+
}
147+
148+
private static void WriteNonGenericStaticProperties(IndentedTextWriter writer, string hasRed, string hasGreen, string hasBlue, string hasAlpha, string fullyUtilized, string typeName)
149+
{
150+
writer.WriteLine($"static bool IColor.HasRedChannel => {hasRed};");
151+
writer.WriteLine($"static bool IColor.HasGreenChannel => {hasGreen};");
152+
writer.WriteLine($"static bool IColor.HasBlueChannel => {hasBlue};");
153+
writer.WriteLine($"static bool IColor.HasAlphaChannel => {hasAlpha};");
154+
writer.WriteLine($"static bool IColor.ChannelsAreFullyUtilized => {fullyUtilized};");
137155
writer.WriteLine($"static Type IColor.ChannelType => typeof({typeName});");
138156
}
139157

@@ -205,7 +223,7 @@ private static void WriteGenericColor(IndentedTextWriter writer, string name, bo
205223
writer.WriteGeneratedCodeWarning();
206224
writer.WriteLineNoTabs();
207225

208-
writer.WriteFileScopedNamespace(OutputNamespace);
226+
writer.WriteFileScopedNamespace(FormatsNamespace);
209227
writer.WriteLineNoTabs();
210228

211229
writer.WriteLine($"public partial struct {name}<T> : IColor<{name}<T>, T> where T : unmanaged, INumberBase<T>, IMinMaxValue<T>");
@@ -234,6 +252,165 @@ private static void WriteGenericColor(IndentedTextWriter writer, string name, bo
234252
}
235253
}
236254

255+
private static void WriteSuperGenericColor(IndentedTextWriter writer, int channelCount)
256+
{
257+
const string typeName = "TChannelValue";
258+
const string minValue = $"NumericConversion.GetMinimumValueSafe<{typeName}>()";
259+
const string maxValue = $"NumericConversion.GetMaximumValueSafe<{typeName}>()";
260+
261+
string[] channelTypeParameters = channelCount switch
262+
{
263+
1 => ["TChannel"],
264+
2 => ["TChannel1", "TChannel2"],
265+
3 => ["TChannel1", "TChannel2", "TChannel3"],
266+
4 => ["TChannel1", "TChannel2", "TChannel3", "TChannel4"],
267+
_ => throw new(),
268+
};
269+
270+
string[] fieldNames = channelCount switch
271+
{
272+
1 => ["value"],
273+
2 => ["value1", "value2"],
274+
3 => ["value1", "value2", "value3"],
275+
4 => ["value1", "value2", "value3", "value4"],
276+
_ => throw new(),
277+
};
278+
279+
280+
281+
writer.WriteGeneratedCodeWarning();
282+
writer.WriteLineNoTabs();
283+
284+
writer.WriteUsing("AssetRipper.TextureDecoder.Rgb.Channels");
285+
writer.WriteLineNoTabs();
286+
287+
writer.WriteFileScopedNamespace("AssetRipper.TextureDecoder.Rgb");
288+
writer.WriteLineNoTabs();
289+
290+
string structName;
291+
{
292+
StringBuilder sb = new();
293+
sb.Append("Color<");
294+
sb.Append(typeName);
295+
for (int i = 0; i < channelTypeParameters.Length; i++)
296+
{
297+
sb.Append(", ");
298+
sb.Append(channelTypeParameters[i]);
299+
}
300+
sb.Append('>');
301+
structName = sb.ToString();
302+
}
303+
304+
writer.WriteLine($"public partial struct {structName} : IColor<{structName}, {typeName}>");
305+
using (new Indented(writer))
306+
{
307+
writer.WriteLine($"where {typeName} : unmanaged, INumberBase<{typeName}>, IMinMaxValue<{typeName}>");
308+
foreach (string channel in channelTypeParameters)
309+
{
310+
writer.WriteLine($"where {channel} : IChannel");
311+
}
312+
}
313+
314+
using (new CurlyBrackets(writer))
315+
{
316+
foreach (string fieldName in fieldNames)
317+
{
318+
writer.WriteLine($"private {typeName} {fieldName};");
319+
}
320+
writer.WriteLineNoTabs();
321+
322+
WriteProperty(writer, 'R', "Red", channelTypeParameters, fieldNames, minValue, typeName);
323+
writer.WriteLineNoTabs();
324+
WriteProperty(writer, 'G', "Green", channelTypeParameters, fieldNames, minValue, typeName);
325+
writer.WriteLineNoTabs();
326+
WriteProperty(writer, 'B', "Blue", channelTypeParameters, fieldNames, minValue, typeName);
327+
writer.WriteLineNoTabs();
328+
WriteProperty(writer, 'A', "Alpha", channelTypeParameters, fieldNames, maxValue, typeName);
329+
writer.WriteLineNoTabs();
330+
331+
writer.Write("public Color(");
332+
for (int i = 0; i < fieldNames.Length; i++)
333+
{
334+
if (i != 0)
335+
{
336+
writer.Write(", ");
337+
}
338+
339+
writer.Write(typeName);
340+
writer.Write(' ');
341+
writer.Write(fieldNames[i]);
342+
}
343+
writer.WriteLine(')');
344+
using (new CurlyBrackets(writer))
345+
{
346+
foreach (string fieldName in fieldNames)
347+
{
348+
writer.WriteLine($"this.{fieldName} = {fieldName};");
349+
}
350+
}
351+
writer.WriteLineNoTabs();
352+
WriteGetChannels(writer, typeName);
353+
writer.WriteLineNoTabs();
354+
WriteSetChannels(writer, typeName, true, true, true, true);
355+
writer.WriteLineNoTabs();
356+
WriteNonGenericStaticProperties(
357+
writer,
358+
string.Join(" || ", channelTypeParameters.Select(c => $"{c}.IsRed")),
359+
string.Join(" || ", channelTypeParameters.Select(c => $"{c}.IsGreen")),
360+
string.Join(" || ", channelTypeParameters.Select(c => $"{c}.IsBlue")),
361+
string.Join(" || ", channelTypeParameters.Select(c => $"{c}.IsAlpha")),
362+
string.Join(" && ", channelTypeParameters.Select(c => $"{c}.FullyUtilized")),
363+
typeName);
364+
writer.WriteLineNoTabs();
365+
366+
writer.WriteLine($"public static {structName} Black");
367+
using (new CurlyBrackets(writer))
368+
{
369+
writer.WriteLine($"get => new({string.Join(", ", channelTypeParameters.Select(c => $"{c}.GetBlack<{typeName}>()"))});");
370+
}
371+
writer.WriteLineNoTabs();
372+
373+
writer.WriteLine($"public static {structName} White");
374+
using (new CurlyBrackets(writer))
375+
{
376+
writer.WriteLine($"get => new({string.Join(", ", channelTypeParameters.Select(c => $"{c}.GetWhite<{typeName}>()"))});");
377+
}
378+
writer.WriteLineNoTabs();
379+
380+
WriteToString(writer, true, true, true, true);
381+
}
382+
383+
static void WriteProperty(IndentedTextWriter writer, char property, string channel, string[] channels, string[] fields, string defaultValue, string typeName)
384+
{
385+
writer.WriteLine($"public {typeName} {property}");
386+
using (new CurlyBrackets(writer))
387+
{
388+
writer.WriteLine("readonly get");
389+
using (new CurlyBrackets(writer))
390+
{
391+
If.Write(
392+
writer,
393+
Enumerable.Range(0, channels.Length).Select(i => new ValueTuple<string, Action<IndentedTextWriter>?>($"{channels[i]}.Is{channel}", (writer) =>
394+
{
395+
writer.WriteLine($"return {channels[i]}.Get{channel}({fields[i]});");
396+
})),
397+
(writer) =>
398+
{
399+
writer.WriteLine($"return {defaultValue};");
400+
});
401+
}
402+
writer.WriteLine("set");
403+
using (new CurlyBrackets(writer))
404+
{
405+
for (int i = 0; i < channels.Length; i++)
406+
{
407+
writer.WriteLine($"Channel.SetIf{channel}<{channels[i]}, {typeName}>(ref {fields[i]}, value);");
408+
}
409+
}
410+
}
411+
}
412+
}
413+
237414
private static void WriteConstructor(IndentedTextWriter writer, string declaringTypeName, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha, string channelTypeName)
238415
{
239416
writer.Write($"public {declaringTypeName}(");

AssetRipper.TextureDecoder.ConsoleApp/DirectBitmap.cs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using AssetRipper.TextureDecoder.Rgb;
2+
using AssetRipper.TextureDecoder.Rgb.Channels;
23
using AssetRipper.TextureDecoder.Rgb.Formats;
34
using StbImageWriteSharp;
45
using System.Runtime.CompilerServices;
@@ -96,24 +97,34 @@ public void SaveAsTga(string path)
9697

9798
private void GetDataAndComponentsForSaving(out byte[] data, out ColorComponents components)
9899
{
99-
if (typeof(TColor) == typeof(ColorRGBA<byte>))
100+
if (typeof(TColor) == typeof(Color<byte, R, G, B, A>) || typeof(TColor) == typeof(ColorRGBA<byte>))
100101
{
101102
data = Data;
102103
components = ColorComponents.RedGreenBlueAlpha;
103104
}
104-
else if (typeof(TColor) == typeof(ColorRGB<byte>))
105+
else if (typeof(TColor) == typeof(Color<byte, R, G, B>) || typeof(TColor) == typeof(ColorRGB<byte>))
105106
{
106107
data = Data;
107108
components = ColorComponents.RedGreenBlue;
108109
}
110+
else if (typeof(TColor) == typeof(Color<byte, Gray, A>))
111+
{
112+
data = Data;
113+
components = ColorComponents.GreyAlpha;
114+
}
115+
else if (typeof(TColor) == typeof(Color<byte, Gray>))
116+
{
117+
data = Data;
118+
components = ColorComponents.Grey;
119+
}
109120
else if (TColor.HasAlphaChannel)
110121
{
111-
RgbConverter.Convert<TColor, TColorArg, ColorRGBA<byte>, byte>(Bits, Width, Height, out data);
122+
RgbConverter.Convert<TColor, TColorArg, Color<byte, R, G, B, A>, byte>(Bits, Width, Height, out data);
112123
components = ColorComponents.RedGreenBlueAlpha;
113124
}
114125
else
115126
{
116-
RgbConverter.Convert<TColor, TColorArg, ColorRGB<byte>, byte>(Bits, Width, Height, out data);
127+
RgbConverter.Convert<TColor, TColorArg, Color<byte, R, G, B>, byte>(Bits, Width, Height, out data);
117128
components = ColorComponents.RedGreenBlue;
118129
}
119130
}

AssetRipper.TextureDecoder.TestGenerator/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ private static void GenerateRgbConsoleMethod()
6262
writer.WriteLine($"case {i}:");
6363
using (new Indented(writer))
6464
{
65-
writer.WriteLine($"RgbConverter.Convert<{colorName}, {channelName}, ColorBGRA32, byte>(input, width, height, output);");
65+
writer.WriteLine($"RgbConverter.Convert<{colorName}, {channelName}, Color<byte, B, G, R, A>, byte>(input, width, height, output);");
6666
writer.WriteLine("break;");
6767
}
6868
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace AssetRipper.TextureDecoder.Rgb.Channels;
2+
3+
public readonly struct A : IChannel
4+
{
5+
static bool IChannel.IsAlpha => true;
6+
static T IChannel.GetBlack<T>() => NumericConversion.GetMaximumValueSafe<T>();
7+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace AssetRipper.TextureDecoder.Rgb.Channels;
2+
3+
public readonly struct B : IChannel
4+
{
5+
static bool IChannel.IsBlue => true;
6+
}

0 commit comments

Comments
 (0)