mirror of
https://github.com/AssetRipper/AssetRipper.TextureDecoder.git
synced 2026-11-12 18:07:28 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2772dcb83 | ||
|
|
1e363e355e | ||
|
|
8e82e702a7 | ||
|
|
1957784de4 | ||
|
|
1bf5f70ead | ||
|
|
2bb946b26f | ||
|
|
eed528cc68 | ||
|
|
c6d3236770 | ||
|
|
34d8310764 |
No files matched your search
@@ -5,7 +5,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.11" />
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.12" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -7,9 +7,10 @@ namespace AssetRipper.TextureDecoder.ColorGenerator;
|
||||
|
||||
internal static partial class Program
|
||||
{
|
||||
private const string AttributeNamespace = "AssetRipper.TextureDecoder.Attributes";
|
||||
private const string OutputNamespace = "AssetRipper.TextureDecoder.Rgb.Formats";
|
||||
private const string OutputFolder = "../../../../AssetRipper.TextureDecoder/Rgb/Formats/";
|
||||
private const string RgbNamespace = "AssetRipper.TextureDecoder.Rgb";
|
||||
private const string RgbFolder = "../../../../AssetRipper.TextureDecoder/Rgb/";
|
||||
private const string FormatsNamespace = "AssetRipper.TextureDecoder.Rgb.Formats";
|
||||
private const string FormatsFolder = "../../../../AssetRipper.TextureDecoder/Rgb/Formats/";
|
||||
|
||||
/// <summary>
|
||||
/// Name, Type, Red, Blue, Green, Alpha, Fully Utilized
|
||||
@@ -46,6 +47,10 @@ internal static partial class Program
|
||||
{
|
||||
WriteGenericColor(genericColor);
|
||||
}
|
||||
for (int i = 1; i <= 4; i++)
|
||||
{
|
||||
WriteSuperGenericColor(i);
|
||||
}
|
||||
NumericConversionGenerator.Run();
|
||||
Console.WriteLine("Done!");
|
||||
}
|
||||
@@ -54,7 +59,7 @@ internal static partial class Program
|
||||
{
|
||||
(string name, Type type, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha, bool fullyUtilized) = details;
|
||||
Console.WriteLine(name);
|
||||
using IndentedTextWriter writer = IndentedTextWriterFactory.Create(OutputFolder, name);
|
||||
using IndentedTextWriter writer = IndentedTextWriterFactory.Create(FormatsFolder, name);
|
||||
WriteCustomColor(writer, name, type, hasRed, hasGreen, hasBlue, hasAlpha, fullyUtilized);
|
||||
}
|
||||
|
||||
@@ -62,16 +67,24 @@ internal static partial class Program
|
||||
{
|
||||
(string name, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha) = details;
|
||||
Console.WriteLine(name);
|
||||
using IndentedTextWriter writer = IndentedTextWriterFactory.Create(OutputFolder, name);
|
||||
using IndentedTextWriter writer = IndentedTextWriterFactory.Create(FormatsFolder, name);
|
||||
WriteGenericColor(writer, name, hasRed, hasGreen, hasBlue, hasAlpha);
|
||||
}
|
||||
|
||||
private static void WriteSuperGenericColor(int channelCount)
|
||||
{
|
||||
string name = $"Color`{channelCount}";
|
||||
Console.WriteLine(name);
|
||||
using IndentedTextWriter writer = IndentedTextWriterFactory.Create(RgbFolder, name);
|
||||
WriteSuperGenericColor(writer, channelCount);
|
||||
}
|
||||
|
||||
private static void WriteCustomColor(IndentedTextWriter writer, string name, Type type, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha, bool fullyUtilized)
|
||||
{
|
||||
string typeName = CSharpPrimitives.Dictionary[type].LangName;
|
||||
writer.WriteGeneratedCodeWarning();
|
||||
writer.WriteLine();
|
||||
using (new Namespace(writer, OutputNamespace))
|
||||
using (new Namespace(writer, FormatsNamespace))
|
||||
{
|
||||
writer.WriteLine($"public partial struct {name} : IColor<{typeName}>");
|
||||
using (new CurlyBrackets(writer))
|
||||
@@ -129,11 +142,16 @@ internal static partial class Program
|
||||
|
||||
private static void WriteNonGenericStaticProperties(IndentedTextWriter writer, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha, bool fullyUtilized, string typeName)
|
||||
{
|
||||
writer.WriteLine($"static bool IColor.HasRedChannel => {hasRed.ToLowerString()};");
|
||||
writer.WriteLine($"static bool IColor.HasGreenChannel => {hasGreen.ToLowerString()};");
|
||||
writer.WriteLine($"static bool IColor.HasBlueChannel => {hasBlue.ToLowerString()};");
|
||||
writer.WriteLine($"static bool IColor.HasAlphaChannel => {hasAlpha.ToLowerString()};");
|
||||
writer.WriteLine($"static bool IColor.ChannelsAreFullyUtilized => {fullyUtilized.ToLowerString()};");
|
||||
WriteNonGenericStaticProperties(writer, hasRed.ToLowerString(), hasGreen.ToLowerString(), hasBlue.ToLowerString(), hasAlpha.ToLowerString(), fullyUtilized.ToLowerString(), typeName);
|
||||
}
|
||||
|
||||
private static void WriteNonGenericStaticProperties(IndentedTextWriter writer, string hasRed, string hasGreen, string hasBlue, string hasAlpha, string fullyUtilized, string typeName)
|
||||
{
|
||||
writer.WriteLine($"static bool IColor.HasRedChannel => {hasRed};");
|
||||
writer.WriteLine($"static bool IColor.HasGreenChannel => {hasGreen};");
|
||||
writer.WriteLine($"static bool IColor.HasBlueChannel => {hasBlue};");
|
||||
writer.WriteLine($"static bool IColor.HasAlphaChannel => {hasAlpha};");
|
||||
writer.WriteLine($"static bool IColor.ChannelsAreFullyUtilized => {fullyUtilized};");
|
||||
writer.WriteLine($"static Type IColor.ChannelType => typeof({typeName});");
|
||||
}
|
||||
|
||||
@@ -205,7 +223,7 @@ internal static partial class Program
|
||||
writer.WriteGeneratedCodeWarning();
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
writer.WriteFileScopedNamespace(OutputNamespace);
|
||||
writer.WriteFileScopedNamespace(FormatsNamespace);
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
writer.WriteLine($"public partial struct {name}<T> : IColor<{name}<T>, T> where T : unmanaged, INumberBase<T>, IMinMaxValue<T>");
|
||||
@@ -234,6 +252,165 @@ internal static partial class Program
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteSuperGenericColor(IndentedTextWriter writer, int channelCount)
|
||||
{
|
||||
const string typeName = "TChannelValue";
|
||||
const string minValue = $"NumericConversion.GetMinimumValueSafe<{typeName}>()";
|
||||
const string maxValue = $"NumericConversion.GetMaximumValueSafe<{typeName}>()";
|
||||
|
||||
string[] channelTypeParameters = channelCount switch
|
||||
{
|
||||
1 => ["TChannel"],
|
||||
2 => ["TChannel1", "TChannel2"],
|
||||
3 => ["TChannel1", "TChannel2", "TChannel3"],
|
||||
4 => ["TChannel1", "TChannel2", "TChannel3", "TChannel4"],
|
||||
_ => throw new(),
|
||||
};
|
||||
|
||||
string[] fieldNames = channelCount switch
|
||||
{
|
||||
1 => ["value"],
|
||||
2 => ["value1", "value2"],
|
||||
3 => ["value1", "value2", "value3"],
|
||||
4 => ["value1", "value2", "value3", "value4"],
|
||||
_ => throw new(),
|
||||
};
|
||||
|
||||
|
||||
|
||||
writer.WriteGeneratedCodeWarning();
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
writer.WriteUsing("AssetRipper.TextureDecoder.Rgb.Channels");
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
writer.WriteFileScopedNamespace("AssetRipper.TextureDecoder.Rgb");
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
string structName;
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
sb.Append("Color<");
|
||||
sb.Append(typeName);
|
||||
for (int i = 0; i < channelTypeParameters.Length; i++)
|
||||
{
|
||||
sb.Append(", ");
|
||||
sb.Append(channelTypeParameters[i]);
|
||||
}
|
||||
sb.Append('>');
|
||||
structName = sb.ToString();
|
||||
}
|
||||
|
||||
writer.WriteLine($"public partial struct {structName} : IColor<{structName}, {typeName}>");
|
||||
using (new Indented(writer))
|
||||
{
|
||||
writer.WriteLine($"where {typeName} : unmanaged, INumberBase<{typeName}>, IMinMaxValue<{typeName}>");
|
||||
foreach (string channel in channelTypeParameters)
|
||||
{
|
||||
writer.WriteLine($"where {channel} : IChannel");
|
||||
}
|
||||
}
|
||||
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
foreach (string fieldName in fieldNames)
|
||||
{
|
||||
writer.WriteLine($"private {typeName} {fieldName};");
|
||||
}
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
WriteProperty(writer, 'R', "Red", channelTypeParameters, fieldNames, minValue, typeName);
|
||||
writer.WriteLineNoTabs();
|
||||
WriteProperty(writer, 'G', "Green", channelTypeParameters, fieldNames, minValue, typeName);
|
||||
writer.WriteLineNoTabs();
|
||||
WriteProperty(writer, 'B', "Blue", channelTypeParameters, fieldNames, minValue, typeName);
|
||||
writer.WriteLineNoTabs();
|
||||
WriteProperty(writer, 'A', "Alpha", channelTypeParameters, fieldNames, maxValue, typeName);
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
writer.Write("public Color(");
|
||||
for (int i = 0; i < fieldNames.Length; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
writer.Write(", ");
|
||||
}
|
||||
|
||||
writer.Write(typeName);
|
||||
writer.Write(' ');
|
||||
writer.Write(fieldNames[i]);
|
||||
}
|
||||
writer.WriteLine(')');
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
foreach (string fieldName in fieldNames)
|
||||
{
|
||||
writer.WriteLine($"this.{fieldName} = {fieldName};");
|
||||
}
|
||||
}
|
||||
writer.WriteLineNoTabs();
|
||||
WriteGetChannels(writer, typeName);
|
||||
writer.WriteLineNoTabs();
|
||||
WriteSetChannels(writer, typeName, true, true, true, true);
|
||||
writer.WriteLineNoTabs();
|
||||
WriteNonGenericStaticProperties(
|
||||
writer,
|
||||
string.Join(" || ", channelTypeParameters.Select(c => $"{c}.IsRed")),
|
||||
string.Join(" || ", channelTypeParameters.Select(c => $"{c}.IsGreen")),
|
||||
string.Join(" || ", channelTypeParameters.Select(c => $"{c}.IsBlue")),
|
||||
string.Join(" || ", channelTypeParameters.Select(c => $"{c}.IsAlpha")),
|
||||
string.Join(" && ", channelTypeParameters.Select(c => $"{c}.FullyUtilized")),
|
||||
typeName);
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
writer.WriteLine($"public static {structName} Black");
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
writer.WriteLine($"get => new({string.Join(", ", channelTypeParameters.Select(c => $"{c}.GetBlack<{typeName}>()"))});");
|
||||
}
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
writer.WriteLine($"public static {structName} White");
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
writer.WriteLine($"get => new({string.Join(", ", channelTypeParameters.Select(c => $"{c}.GetWhite<{typeName}>()"))});");
|
||||
}
|
||||
writer.WriteLineNoTabs();
|
||||
|
||||
WriteToString(writer, true, true, true, true);
|
||||
}
|
||||
|
||||
static void WriteProperty(IndentedTextWriter writer, char property, string channel, string[] channels, string[] fields, string defaultValue, string typeName)
|
||||
{
|
||||
writer.WriteLine($"public {typeName} {property}");
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
writer.WriteLine("readonly get");
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
If.Write(
|
||||
writer,
|
||||
Enumerable.Range(0, channels.Length).Select(i => new ValueTuple<string, Action<IndentedTextWriter>?>($"{channels[i]}.Is{channel}", (writer) =>
|
||||
{
|
||||
writer.WriteLine($"return {channels[i]}.Get{channel}({fields[i]});");
|
||||
})),
|
||||
(writer) =>
|
||||
{
|
||||
writer.WriteLine($"return {defaultValue};");
|
||||
});
|
||||
}
|
||||
writer.WriteLine("set");
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
for (int i = 0; i < channels.Length; i++)
|
||||
{
|
||||
writer.WriteLine($"Channel.SetIf{channel}<{channels[i]}, {typeName}>(ref {fields[i]}, value);");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteConstructor(IndentedTextWriter writer, string declaringTypeName, bool hasRed, bool hasGreen, bool hasBlue, bool hasAlpha, string channelTypeName)
|
||||
{
|
||||
writer.Write($"public {declaringTypeName}(");
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using AssetRipper.TextureDecoder.Exr;
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
using AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
using AssetRipper.TextureDecoder.Rgb.Formats;
|
||||
using StbImageWriteSharp;
|
||||
using System.Runtime.CompilerServices;
|
||||
@@ -54,6 +56,12 @@ public readonly struct DirectBitmap<TColor, TColorArg>
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveAsExr(string path)
|
||||
{
|
||||
using Stream stream = File.OpenWrite(path);
|
||||
ExrWriter.Write<TColor, TColorArg>(stream, Width, Height, Pixels);
|
||||
}
|
||||
|
||||
public void SaveAsHdr(string path)
|
||||
{
|
||||
GetDataAndComponentsForSaving(out byte[] data, out ColorComponents components);
|
||||
@@ -96,24 +104,34 @@ public readonly struct DirectBitmap<TColor, TColorArg>
|
||||
|
||||
private void GetDataAndComponentsForSaving(out byte[] data, out ColorComponents components)
|
||||
{
|
||||
if (typeof(TColor) == typeof(ColorRGBA<byte>))
|
||||
if (typeof(TColor) == typeof(Color<byte, R, G, B, A>) || typeof(TColor) == typeof(ColorRGBA<byte>))
|
||||
{
|
||||
data = Data;
|
||||
components = ColorComponents.RedGreenBlueAlpha;
|
||||
}
|
||||
else if (typeof(TColor) == typeof(ColorRGB<byte>))
|
||||
else if (typeof(TColor) == typeof(Color<byte, R, G, B>) || typeof(TColor) == typeof(ColorRGB<byte>))
|
||||
{
|
||||
data = Data;
|
||||
components = ColorComponents.RedGreenBlue;
|
||||
}
|
||||
else if (typeof(TColor) == typeof(Color<byte, Gray, A>))
|
||||
{
|
||||
data = Data;
|
||||
components = ColorComponents.GreyAlpha;
|
||||
}
|
||||
else if (typeof(TColor) == typeof(Color<byte, Gray>))
|
||||
{
|
||||
data = Data;
|
||||
components = ColorComponents.Grey;
|
||||
}
|
||||
else if (TColor.HasAlphaChannel)
|
||||
{
|
||||
RgbConverter.Convert<TColor, TColorArg, ColorRGBA<byte>, byte>(Bits, Width, Height, out data);
|
||||
RgbConverter.Convert<TColor, TColorArg, Color<byte, R, G, B, A>, byte>(Bits, Width, Height, out data);
|
||||
components = ColorComponents.RedGreenBlueAlpha;
|
||||
}
|
||||
else
|
||||
{
|
||||
RgbConverter.Convert<TColor, TColorArg, ColorRGB<byte>, byte>(Bits, Width, Height, out data);
|
||||
RgbConverter.Convert<TColor, TColorArg, Color<byte, R, G, B>, byte>(Bits, Width, Height, out data);
|
||||
components = ColorComponents.RedGreenBlue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,18 +63,32 @@ internal static partial class Program
|
||||
|
||||
switch (outputType)
|
||||
{
|
||||
case "png":
|
||||
{
|
||||
string newPath = Path.Combine(dirPath, name + ".png");
|
||||
bitmap.FlipY();
|
||||
bitmap.SaveAsPng(newPath);
|
||||
}
|
||||
break;
|
||||
case "bgra":
|
||||
{
|
||||
string newPath = Path.Combine(dirPath, name + ".bgra");
|
||||
WriteAllBytes(newPath, bitmap.Bits);
|
||||
}
|
||||
WriteAllBytes(Path.Combine(dirPath, name + ".bgra"), bitmap.Bits);
|
||||
break;
|
||||
case "bmp":
|
||||
bitmap.FlipY();
|
||||
bitmap.SaveAsBmp(Path.Combine(dirPath, name + ".bmp"));
|
||||
break;
|
||||
case "exr":
|
||||
bitmap.FlipY();
|
||||
bitmap.SaveAsExr(Path.Combine(dirPath, name + ".exr"));
|
||||
break;
|
||||
case "hdr":
|
||||
bitmap.FlipY();
|
||||
bitmap.SaveAsHdr(Path.Combine(dirPath, name + ".hdr"));
|
||||
break;
|
||||
case "jpg":
|
||||
bitmap.FlipY();
|
||||
bitmap.SaveAsJpg(Path.Combine(dirPath, name + ".jpg"));
|
||||
break;
|
||||
case "png":
|
||||
bitmap.FlipY();
|
||||
bitmap.SaveAsPng(Path.Combine(dirPath, name + ".png"));
|
||||
break;
|
||||
case "tga":
|
||||
bitmap.FlipY();
|
||||
bitmap.SaveAsTga(Path.Combine(dirPath, name + ".tga"));
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException(outputType);
|
||||
@@ -239,4 +253,4 @@ internal static partial class Program
|
||||
using Microsoft.Win32.SafeHandles.SafeFileHandle sfh = File.OpenHandle(path, FileMode.Create, FileAccess.Write, FileShare.Read);
|
||||
RandomAccess.Write(sfh, bytes, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,7 @@ namespace AssetRipper.TextureDecoder.TestGenerator
|
||||
writer.WriteLine($"case {i}:");
|
||||
using (new Indented(writer))
|
||||
{
|
||||
writer.WriteLine($"RgbConverter.Convert<{colorName}, {channelName}, ColorBGRA32, byte>(input, width, height, output);");
|
||||
writer.WriteLine($"RgbConverter.Convert<{colorName}, {channelName}, Color<byte, B, G, R, A>, byte>(input, width, height, output);");
|
||||
writer.WriteLine("break;");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ internal static class TestClassGenerator
|
||||
writer.WriteLine($"[TestFixture(TypeArgs = new Type[] {{ typeof({colorName}<{channeName}>), typeof({channeName}) }})]");
|
||||
}
|
||||
}
|
||||
writer.WriteLine("partial class GenericColorTests<TColor, TChannel>");
|
||||
writer.WriteLine("partial class GenericColorTests<TColor, TChannelValue>");
|
||||
using (new CurlyBrackets(writer))
|
||||
{
|
||||
}
|
||||
@@ -354,4 +354,4 @@ internal static class TestClassGenerator
|
||||
textWriter.WriteLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,14 +5,17 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageReference Include="NUnit" Version="4.0.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
||||
<PackageReference Include="NUnit" Version="4.1.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||
<PackageReference Include="NUnit.Analyzers" Version="3.10.0">
|
||||
<PackageReference Include="NUnit.Analyzers" Version="4.1.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -74,4 +74,10 @@ public sealed class AstcTests
|
||||
}
|
||||
ByteArrayDeviation.AssertMinimalDeviation(decompressedData, AndroidTextures.Logo_00.Data, maxMeanDeviation, maxStandardDeviation);
|
||||
}
|
||||
|
||||
[TestCase(new byte[16] { 83, 1, 147, 104, 198, 173, 55, 116, 182, 66, 105, 11, 102, 43, 148, 125 }, 8, 8, TestName = "GetBits Should not throw in DecodeIntseq")]
|
||||
public void DecodesBlockWithoutThrowing(byte[] block, int blockWidth, int blockHeight)
|
||||
{
|
||||
Assert.DoesNotThrow(() => AstcDecoder.DecodeASTC(block, blockWidth, blockHeight, blockWidth, blockHeight, out _));
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
internal static class ColorRandom<TColor, TChannel> where TColor : unmanaged, IColor<TChannel> where TChannel : unmanaged
|
||||
internal static class ColorRandom<TColor, TChannelValue> where TColor : unmanaged, IColor<TChannelValue> where TChannelValue : unmanaged
|
||||
{
|
||||
public static TChannel MakeRandomValue()
|
||||
public static TChannelValue MakeRandomValue()
|
||||
{
|
||||
ulong value = TestContext.CurrentContext.Random.NextULong();
|
||||
return NumericConversion.Convert<ulong, TChannel>(value);
|
||||
return NumericConversion.Convert<ulong, TChannelValue>(value);
|
||||
}
|
||||
|
||||
public static TColor MakeRandomColor()
|
||||
|
||||
@@ -3,19 +3,19 @@ using System.Runtime.CompilerServices;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats.Generic;
|
||||
|
||||
internal partial class GenericColorTests<TColor, TChannel> where TColor : unmanaged, IColor<TChannel> where TChannel : unmanaged
|
||||
internal partial class GenericColorTests<TColor, TChannelValue> where TColor : unmanaged, IColor<TChannelValue> where TChannelValue : unmanaged
|
||||
{
|
||||
[Test]
|
||||
public void CorrectSizeTest()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<TColor>(), Is.EqualTo(Color.GetChannelCount<TColor>() * Unsafe.SizeOf<TChannel>()));
|
||||
Assert.That(Unsafe.SizeOf<TColor>(), Is.EqualTo(Color.GetChannelCount<TColor>() * Unsafe.SizeOf<TChannelValue>()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertyIsSymmetric_R()
|
||||
{
|
||||
TColor color = MakeRandomColor();
|
||||
TChannel r = color.R;
|
||||
TChannelValue r = color.R;
|
||||
color.R = r;
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
}
|
||||
@@ -24,7 +24,7 @@ internal partial class GenericColorTests<TColor, TChannel> where TColor : unmana
|
||||
public void PropertyIsSymmetric_G()
|
||||
{
|
||||
TColor color = MakeRandomColor();
|
||||
TChannel g = color.G;
|
||||
TChannelValue g = color.G;
|
||||
color.G = g;
|
||||
Assert.That(color.G, Is.EqualTo(g));
|
||||
}
|
||||
@@ -33,7 +33,7 @@ internal partial class GenericColorTests<TColor, TChannel> where TColor : unmana
|
||||
public void PropertyIsSymmetric_B()
|
||||
{
|
||||
TColor color = MakeRandomColor();
|
||||
TChannel b = color.B;
|
||||
TChannelValue b = color.B;
|
||||
color.B = b;
|
||||
Assert.That(color.B, Is.EqualTo(b));
|
||||
}
|
||||
@@ -42,7 +42,7 @@ internal partial class GenericColorTests<TColor, TChannel> where TColor : unmana
|
||||
public void PropertyIsSymmetric_A()
|
||||
{
|
||||
TColor color = MakeRandomColor();
|
||||
TChannel a = color.A;
|
||||
TChannelValue a = color.A;
|
||||
color.A = a;
|
||||
Assert.That(color.A, Is.EqualTo(a));
|
||||
}
|
||||
@@ -51,9 +51,9 @@ internal partial class GenericColorTests<TColor, TChannel> where TColor : unmana
|
||||
public void ChannelsAreIndependent_R()
|
||||
{
|
||||
TColor color = MakeRandomColor();
|
||||
TChannel g = color.G;
|
||||
TChannel b = color.B;
|
||||
TChannel a = color.A;
|
||||
TChannelValue g = color.G;
|
||||
TChannelValue b = color.B;
|
||||
TChannelValue a = color.A;
|
||||
color.R = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
@@ -67,9 +67,9 @@ internal partial class GenericColorTests<TColor, TChannel> where TColor : unmana
|
||||
public void ChannelsAreIndependent_G()
|
||||
{
|
||||
TColor color = MakeRandomColor();
|
||||
TChannel r = color.R;
|
||||
TChannel b = color.B;
|
||||
TChannel a = color.A;
|
||||
TChannelValue r = color.R;
|
||||
TChannelValue b = color.B;
|
||||
TChannelValue a = color.A;
|
||||
color.G = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
@@ -83,9 +83,9 @@ internal partial class GenericColorTests<TColor, TChannel> where TColor : unmana
|
||||
public void ChannelsAreIndependent_B()
|
||||
{
|
||||
TColor color = MakeRandomColor();
|
||||
TChannel r = color.R;
|
||||
TChannel g = color.G;
|
||||
TChannel a = color.A;
|
||||
TChannelValue r = color.R;
|
||||
TChannelValue g = color.G;
|
||||
TChannelValue a = color.A;
|
||||
color.B = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
@@ -99,9 +99,9 @@ internal partial class GenericColorTests<TColor, TChannel> where TColor : unmana
|
||||
public void ChannelsAreIndependent_A()
|
||||
{
|
||||
TColor color = MakeRandomColor();
|
||||
TChannel r = color.R;
|
||||
TChannel g = color.G;
|
||||
TChannel b = color.B;
|
||||
TChannelValue r = color.R;
|
||||
TChannelValue g = color.G;
|
||||
TChannelValue b = color.B;
|
||||
color.A = MakeRandomValue();
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
@@ -115,7 +115,7 @@ internal partial class GenericColorTests<TColor, TChannel> where TColor : unmana
|
||||
public void GetMethodMatchesProperties()
|
||||
{
|
||||
TColor color = MakeRandomColor();
|
||||
color.GetChannels(out TChannel r, out TChannel g, out TChannel b, out TChannel a);
|
||||
color.GetChannels(out TChannelValue r, out TChannelValue g, out TChannelValue b, out TChannelValue a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(color.R, Is.EqualTo(r));
|
||||
@@ -129,7 +129,7 @@ internal partial class GenericColorTests<TColor, TChannel> where TColor : unmana
|
||||
public void MethodsAreSymmetric()
|
||||
{
|
||||
TColor color = MakeRandomColor();
|
||||
color.GetChannels(out TChannel r, out TChannel g, out TChannel b, out TChannel a);
|
||||
color.GetChannels(out TChannelValue r, out TChannelValue g, out TChannelValue b, out TChannelValue a);
|
||||
color.SetChannels(r, g, b, a);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
@@ -140,7 +140,7 @@ internal partial class GenericColorTests<TColor, TChannel> where TColor : unmana
|
||||
});
|
||||
}
|
||||
|
||||
private static TChannel MakeRandomValue() => ColorRandom<TColor, TChannel>.MakeRandomValue();
|
||||
private static TChannelValue MakeRandomValue() => ColorRandom<TColor, TChannelValue>.MakeRandomValue();
|
||||
|
||||
private static TColor MakeRandomColor() => ColorRandom<TColor, TChannel>.MakeRandomColor();
|
||||
private static TColor MakeRandomColor() => ColorRandom<TColor, TChannelValue>.MakeRandomColor();
|
||||
}
|
||||
@@ -88,6 +88,6 @@ namespace AssetRipper.TextureDecoder.Tests.Formats.Generic;
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGB<decimal>), typeof(decimal) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorRGBA<decimal>), typeof(decimal) })]
|
||||
[TestFixture(TypeArgs = new Type[] { typeof(ColorA<decimal>), typeof(decimal) })]
|
||||
partial class GenericColorTests<TColor, TChannel>
|
||||
partial class GenericColorTests<TColor, TChannelValue>
|
||||
{
|
||||
}
|
||||
@@ -3,15 +3,15 @@
|
||||
namespace AssetRipper.TextureDecoder.Tests.Formats;
|
||||
internal static class LosslessConversion
|
||||
{
|
||||
internal static void Assert<TColor1, TChannel1, TColor2, TChannel2>()
|
||||
where TChannel1 : unmanaged
|
||||
where TChannel2 : unmanaged
|
||||
where TColor1 : unmanaged, IColor<TChannel1>
|
||||
where TColor2 : unmanaged, IColor<TChannel2>
|
||||
internal static void Assert<TColor1, TChannelValue1, TColor2, TChannelValue2>()
|
||||
where TChannelValue1 : unmanaged
|
||||
where TChannelValue2 : unmanaged
|
||||
where TColor1 : unmanaged, IColor<TChannelValue1>
|
||||
where TColor2 : unmanaged, IColor<TChannelValue2>
|
||||
{
|
||||
TColor1 original = ColorRandom<TColor1, TChannel1>.MakeRandomColor();
|
||||
TColor2 converted = original.Convert<TColor1, TChannel1, TColor2, TChannel2>();
|
||||
TColor1 convertedBack = converted.Convert<TColor2, TChannel2, TColor1, TChannel1>();
|
||||
TColor1 original = ColorRandom<TColor1, TChannelValue1>.MakeRandomColor();
|
||||
TColor2 converted = original.Convert<TColor1, TChannelValue1, TColor2, TChannelValue2>();
|
||||
TColor1 convertedBack = converted.Convert<TColor2, TChannelValue2, TColor1, TChannelValue1>();
|
||||
NUnit.Framework.Assert.That(convertedBack, Is.EqualTo(original));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<IsTrimmable>true</IsTrimmable>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
||||
<PackageId>AssetRipper.TextureDecoder</PackageId>
|
||||
<PackageTags>C# Texture</PackageTags>
|
||||
<RepositoryUrl>https://github.com/AssetRipper/TextureDecoder</RepositoryUrl>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageProjectUrl>https://github.com/AssetRipper/TextureDecoder</PackageProjectUrl>
|
||||
<Copyright>Copyright (c) 2022 - 2023 ds5678</Copyright>
|
||||
<Description>Managed library for decoding textures in multiple formats.</Description>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<DocumentationFile>bin\TextureDecoder.xml</DocumentationFile>
|
||||
<NoWarn>1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<IsTrimmable>true</IsTrimmable>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
||||
<PackageId>AssetRipper.TextureDecoder</PackageId>
|
||||
<PackageTags>C# Texture</PackageTags>
|
||||
<RepositoryUrl>https://github.com/AssetRipper/TextureDecoder</RepositoryUrl>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageProjectUrl>https://github.com/AssetRipper/TextureDecoder</PackageProjectUrl>
|
||||
<Copyright>Copyright (c) 2022 - 2024 ds5678</Copyright>
|
||||
<Description>Managed library for decoding textures in multiple formats.</Description>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<DocumentationFile>bin\TextureDecoder.xml</DocumentationFile>
|
||||
<NoWarn>1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -883,7 +883,19 @@ namespace AssetRipper.TextureDecoder.Astc
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static int GetBits(ReadOnlySpan<byte> input, int bit, int len)
|
||||
{
|
||||
return (BinaryPrimitives.ReadInt32LittleEndian(input[(bit / 8)..]) >> (bit % 8)) & ((1 << len) - 1);
|
||||
ReadOnlySpan<byte> slice = input[(bit / 8)..];
|
||||
int bitBuffer;
|
||||
if (slice.Length >= sizeof(int))
|
||||
{
|
||||
bitBuffer = BinaryPrimitives.ReadInt32LittleEndian(slice);
|
||||
}
|
||||
else
|
||||
{
|
||||
Span<byte> temp = stackalloc byte[sizeof(int)];
|
||||
slice.CopyTo(temp);
|
||||
bitBuffer = BinaryPrimitives.ReadInt32LittleEndian(temp);
|
||||
}
|
||||
return (bitBuffer >> (bit % 8)) & ((1 << len) - 1);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
|
||||
@@ -36,50 +36,50 @@ namespace AssetRipper.TextureDecoder.Dxt
|
||||
/// Decompress a DXT1 image
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
|
||||
/// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <typeparam name="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT1<TOutputColor, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannel : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannel>
|
||||
public static int DecompressDXT1<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||
return DecompressDXT1<TOutputColor, TOutputChannel>(input, width, height, output);
|
||||
return DecompressDXT1<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT1 image
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
|
||||
/// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <typeparam name="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT1<TOutputColor, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannel : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannel>
|
||||
public static int DecompressDXT1<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
return DecompressDXT1<TOutputColor, TOutputChannel>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
return DecompressDXT1<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT1 image
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
|
||||
/// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <typeparam name="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT1<TOutputColor, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannel : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannel>
|
||||
public static int DecompressDXT1<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
|
||||
@@ -97,17 +97,17 @@ namespace AssetRipper.TextureDecoder.Dxt
|
||||
int q1 = input[offset + 2] | input[offset + 3] << 8;
|
||||
Rgb565(q0, out byte r0, out byte g0, out byte b0);
|
||||
Rgb565(q1, out byte r1, out byte g1, out byte b1);
|
||||
colors[0].SetConvertedChannels<TOutputColor, TOutputChannel, byte>(r0, g0, b0);
|
||||
colors[1].SetConvertedChannels<TOutputColor, TOutputChannel, byte>(r1, g1, b1);
|
||||
colors[0].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>(r0, g0, b0);
|
||||
colors[1].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>(r1, g1, b1);
|
||||
if (q0 > q1)
|
||||
{
|
||||
colors[2].SetConvertedChannels<TOutputColor, TOutputChannel, byte>((byte)((r0 * 2 + r1) / 3), (byte)((g0 * 2 + g1) / 3), (byte)((b0 * 2 + b1) / 3));
|
||||
colors[3].SetConvertedChannels<TOutputColor, TOutputChannel, byte>((byte)((r0 + r1 * 2) / 3), (byte)((g0 + g1 * 2) / 3), (byte)((b0 + b1 * 2) / 3));
|
||||
colors[2].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>((byte)((r0 * 2 + r1) / 3), (byte)((g0 * 2 + g1) / 3), (byte)((b0 * 2 + b1) / 3));
|
||||
colors[3].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>((byte)((r0 + r1 * 2) / 3), (byte)((g0 + g1 * 2) / 3), (byte)((b0 + b1 * 2) / 3));
|
||||
}
|
||||
else
|
||||
{
|
||||
colors[2].SetConvertedChannels<TOutputColor, TOutputChannel, byte>((byte)((r0 + r1) / 2), (byte)((g0 + g1) / 2), (byte)((b0 + b1) / 2));
|
||||
colors[3].SetBlack<TOutputColor, TOutputChannel>();
|
||||
colors[2].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>((byte)((r0 + r1) / 2), (byte)((g0 + g1) / 2), (byte)((b0 + b1) / 2));
|
||||
colors[3].SetBlack<TOutputColor, TOutputChannelValue>();
|
||||
}
|
||||
|
||||
uint d = ToUInt32(input, offset + 4);
|
||||
@@ -157,50 +157,50 @@ namespace AssetRipper.TextureDecoder.Dxt
|
||||
/// Decompress a DXT3 image
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
|
||||
/// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <typeparam name="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT3<TOutputColor, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannel : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannel>
|
||||
public static int DecompressDXT3<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||
return DecompressDXT3<TOutputColor, TOutputChannel>(input, width, height, output);
|
||||
return DecompressDXT3<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT1 image
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
|
||||
/// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <typeparam name="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT3<TOutputColor, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannel : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannel>
|
||||
public static int DecompressDXT3<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
return DecompressDXT3<TOutputColor, TOutputChannel>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
return DecompressDXT3<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT3 image
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
|
||||
/// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <typeparam name="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT3<TOutputColor, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannel : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannel>
|
||||
public static int DecompressDXT3<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
|
||||
@@ -244,7 +244,7 @@ namespace AssetRipper.TextureDecoder.Dxt
|
||||
uint d = ToUInt32(input, offset + 12);
|
||||
for (int i = 0; i < 16; i++, d >>= 2)
|
||||
{
|
||||
buffer[i].SetConvertedChannels<TOutputColor, TOutputChannel, byte>(colors[unchecked((int)(d & 3))], alphas[i]);
|
||||
buffer[i].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>(colors[unchecked((int)(d & 3))], alphas[i]);
|
||||
}
|
||||
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
@@ -288,50 +288,50 @@ namespace AssetRipper.TextureDecoder.Dxt
|
||||
/// Decompress a DXT5 image
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
|
||||
/// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <typeparam name="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT5<TOutputColor, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannel : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannel>
|
||||
public static int DecompressDXT5<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||
return DecompressDXT5<TOutputColor, TOutputChannel>(input, width, height, output);
|
||||
return DecompressDXT5<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT1 image
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
|
||||
/// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <typeparam name="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT5<TOutputColor, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannel : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannel>
|
||||
public static int DecompressDXT5<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
return DecompressDXT5<TOutputColor, TOutputChannel>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
return DecompressDXT5<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a DXT5 image
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
|
||||
/// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <typeparam name="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressDXT5<TOutputColor, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannel : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannel>
|
||||
public static int DecompressDXT5<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
|
||||
@@ -390,7 +390,7 @@ namespace AssetRipper.TextureDecoder.Dxt
|
||||
uint dc = ToUInt32(input, offset + 12);
|
||||
for (int i = 0; i < 16; i++, da >>= 3, dc >>= 2)
|
||||
{
|
||||
buffer[i].SetConvertedChannels<TOutputColor, TOutputChannel, byte>(colors[unchecked((int)(dc & 3))], alphas[unchecked((int)(da & 7))]);
|
||||
buffer[i].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>(colors[unchecked((int)(dc & 3))], alphas[unchecked((int)(da & 7))]);
|
||||
}
|
||||
|
||||
int clen = s < bcw - 1 ? 4 : clen_last;
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
using System.Buffers;
|
||||
using System.Text;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
internal static class BinaryWriterExtensions
|
||||
{
|
||||
public static void WriteLengthPrefixedString(this BinaryWriter writer, string value)
|
||||
{
|
||||
int length = Encoding.UTF8.GetByteCount(value);
|
||||
writer.Write(length);
|
||||
|
||||
byte[]? rentedArray;
|
||||
scoped Span<byte> buffer;
|
||||
if (length <= 1024)
|
||||
{
|
||||
rentedArray = null;
|
||||
buffer = stackalloc byte[length];
|
||||
}
|
||||
else
|
||||
{
|
||||
rentedArray = ArrayPool<byte>.Shared.Rent(length);
|
||||
buffer = rentedArray.AsSpan(0, length);
|
||||
}
|
||||
|
||||
Encoding.UTF8.GetBytes(value, buffer);
|
||||
writer.Write(buffer);
|
||||
|
||||
if (rentedArray is not null)
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(rentedArray);
|
||||
}
|
||||
}
|
||||
|
||||
public static void WriteNullTerminatedString(this BinaryWriter writer, string value)
|
||||
{
|
||||
int length = Encoding.UTF8.GetByteCount(value);
|
||||
|
||||
byte[]? rentedArray;
|
||||
scoped Span<byte> buffer;
|
||||
if (length <= 1024)
|
||||
{
|
||||
rentedArray = null;
|
||||
buffer = stackalloc byte[length];
|
||||
}
|
||||
else
|
||||
{
|
||||
rentedArray = ArrayPool<byte>.Shared.Rent(length);
|
||||
buffer = rentedArray.AsSpan(0, length);
|
||||
}
|
||||
|
||||
Encoding.UTF8.GetBytes(value, buffer);
|
||||
writer.Write(buffer);
|
||||
|
||||
if (rentedArray is not null)
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(rentedArray);
|
||||
}
|
||||
|
||||
writer.WriteNullByte();
|
||||
}
|
||||
|
||||
public static void WriteNullTerminatedString(this BinaryWriter writer, ReadOnlySpan<byte> utf8String)
|
||||
{
|
||||
writer.Write(utf8String);
|
||||
writer.WriteNullByte();
|
||||
}
|
||||
|
||||
public static void WriteNullByte(this BinaryWriter writer)
|
||||
{
|
||||
writer.Write(default(byte));
|
||||
}
|
||||
|
||||
public static void Write<T>(this BinaryWriter writer, T value) where T : unmanaged, Enum
|
||||
{
|
||||
switch (Unsafe.SizeOf<T>())
|
||||
{
|
||||
case sizeof(byte):
|
||||
writer.Write(Unsafe.As<T, byte>(ref value));
|
||||
break;
|
||||
case sizeof(ushort):
|
||||
writer.Write(Unsafe.As<T, ushort>(ref value));
|
||||
break;
|
||||
case sizeof(uint):
|
||||
writer.Write(Unsafe.As<T, uint>(ref value));
|
||||
break;
|
||||
case sizeof(ulong):
|
||||
writer.Write(Unsafe.As<T, ulong>(ref value));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void WriteAttribute<T>(this BinaryWriter writer, ReadOnlySpan<byte> attributeName, T attributeValue) where T : IExrDataType
|
||||
{
|
||||
writer.Write(attributeName);
|
||||
writer.WriteNullByte();
|
||||
writer.Write(T.TypeName);
|
||||
writer.WriteNullByte();
|
||||
writer.Write(attributeValue.Size);
|
||||
attributeValue.Write(writer);
|
||||
}
|
||||
|
||||
public static void WriteAttribute<T>(this BinaryWriter writer, ReadOnlySpan<byte> attributeName, ReadOnlySpan<byte> attributeType, T attributeValue) where T : unmanaged, Enum
|
||||
{
|
||||
writer.Write(attributeName);
|
||||
writer.WriteNullByte();
|
||||
writer.Write(attributeType);
|
||||
writer.WriteNullByte();
|
||||
writer.Write(Unsafe.SizeOf<T>());
|
||||
writer.Write(attributeValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public static class ExrAttributes
|
||||
{
|
||||
public static ReadOnlySpan<byte> Channels => "channels"u8;
|
||||
|
||||
public static ReadOnlySpan<byte> Compression => "compression"u8;
|
||||
|
||||
public static ReadOnlySpan<byte> DataWindow => "dataWindow"u8;
|
||||
|
||||
public static ReadOnlySpan<byte> DisplayWindow => "displayWindow"u8;
|
||||
|
||||
public static ReadOnlySpan<byte> LineOrder => "lineOrder"u8;
|
||||
|
||||
public static ReadOnlySpan<byte> PixelAspectRatio => "pixelAspectRatio"u8;
|
||||
|
||||
public static ReadOnlySpan<byte> ScreenWindowCenter => "screenWindowCenter"u8;
|
||||
|
||||
public static ReadOnlySpan<byte> ScreenWindowWidth => "screenWindowWidth"u8;
|
||||
|
||||
public static ReadOnlySpan<byte> Tiles => "tiles"u8;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct ExrBox2F(float XMin, float YMin, float XMax, float YMax) : IExrDataType
|
||||
{
|
||||
public static ReadOnlySpan<byte> TypeName => "box2f"u8;
|
||||
|
||||
public int Size => sizeof(float) + sizeof(float) + sizeof(float) + sizeof(float);
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(XMin);
|
||||
writer.Write(YMin);
|
||||
writer.Write(XMax);
|
||||
writer.Write(YMax);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct ExrBox2I(int XMin, int YMin, int XMax, int YMax) : IExrDataType
|
||||
{
|
||||
public static ReadOnlySpan<byte> TypeName => "box2i"u8;
|
||||
|
||||
public int Size => sizeof(int) + sizeof(int) + sizeof(int) + sizeof(int);
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(XMin);
|
||||
writer.Write(YMin);
|
||||
writer.Write(XMax);
|
||||
writer.Write(YMax);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System.Text;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct ExrChannel(string Name, ExrPixelType PixelType, bool PLinear, int XSampling, int YSampling)
|
||||
{
|
||||
public int Size
|
||||
{
|
||||
get
|
||||
{
|
||||
return Encoding.UTF8.GetByteCount(Name)
|
||||
+ sizeof(byte)//Null terminator
|
||||
+ sizeof(ExrPixelType)
|
||||
+ sizeof(bool)
|
||||
+ 3 * sizeof(byte)//Reserved bytes
|
||||
+ sizeof(int)
|
||||
+ sizeof(int);
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.WriteNullTerminatedString(Name);
|
||||
writer.Write(PixelType);
|
||||
writer.Write(PLinear);
|
||||
writer.WriteNullByte();
|
||||
writer.WriteNullByte();
|
||||
writer.WriteNullByte();
|
||||
writer.Write(XSampling);
|
||||
writer.Write(YSampling);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct ExrChannelList(ExrChannel[] Channels) : IExrDataType
|
||||
{
|
||||
public static ReadOnlySpan<byte> TypeName => "chlist"u8;
|
||||
|
||||
public int Size => Channels.Sum(c => c.Size);
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
foreach (ExrChannel channel in Channels)
|
||||
{
|
||||
channel.Write(writer);
|
||||
}
|
||||
writer.WriteNullByte();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public enum ExrCompression : byte
|
||||
{
|
||||
None = 0,
|
||||
RLE = 1,
|
||||
ZIPS = 2,
|
||||
ZIP = 3,
|
||||
PIZ = 4,
|
||||
PXR24 = 5,
|
||||
B44 = 6,
|
||||
B44A = 7,
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct ExrDouble(double Value) : IExrDataType
|
||||
{
|
||||
public static ReadOnlySpan<byte> TypeName => "double"u8;
|
||||
|
||||
public int Size => sizeof(double);
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(Value);
|
||||
}
|
||||
|
||||
public static implicit operator double(ExrDouble value) => value.Value;
|
||||
|
||||
public static implicit operator ExrDouble(double value) => new(value);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
public enum ExrImageFormat
|
||||
{
|
||||
ScanLines,
|
||||
Tiles,
|
||||
}
|
||||
public static class ExrImageFormatExtensions
|
||||
{
|
||||
public static byte ToVersionByte(this ExrImageFormat format) => format switch
|
||||
{
|
||||
ExrImageFormat.Tiles => 0x02,
|
||||
_ => 0x00,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct ExrInt32(int Value) : IExrDataType
|
||||
{
|
||||
public static ReadOnlySpan<byte> TypeName => "int"u8;
|
||||
|
||||
public int Size => sizeof(int);
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(Value);
|
||||
}
|
||||
|
||||
public static implicit operator int(ExrInt32 value) => value.Value;
|
||||
|
||||
public static implicit operator ExrInt32(int value) => new(value);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
[Flags]
|
||||
public enum ExrLevelMode : byte
|
||||
{
|
||||
OneLevel = 0,
|
||||
MipMapLevels = 1,
|
||||
RipMapLevels = 2,
|
||||
RoundDown = 0,
|
||||
RoundUp = 16,
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public enum ExrLineOrder : byte
|
||||
{
|
||||
IncreasingY = 0,
|
||||
DecreasingY = 1,
|
||||
RandomY = 2,
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public enum ExrPixelType : int
|
||||
{
|
||||
UInt = 0,
|
||||
Half = 1,
|
||||
Single = 2,
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct ExrSingle(float Value) : IExrDataType
|
||||
{
|
||||
public static ReadOnlySpan<byte> TypeName => "float"u8;
|
||||
|
||||
public int Size => sizeof(float);
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(Value);
|
||||
}
|
||||
|
||||
public static implicit operator float(ExrSingle value) => value.Value;
|
||||
|
||||
public static implicit operator ExrSingle(float value) => new(value);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System.Text;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct ExrString(string Value) : IExrDataType
|
||||
{
|
||||
public static ReadOnlySpan<byte> TypeName => "string"u8;
|
||||
|
||||
public int Size => sizeof(int) + Encoding.UTF8.GetByteCount(Value);
|
||||
|
||||
public void Write(BinaryWriter writer) => writer.WriteLengthPrefixedString(Value);
|
||||
|
||||
public static implicit operator string(ExrString value) => value.Value;
|
||||
|
||||
public static implicit operator ExrString(string value) => new(value);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct ExrVector2(Vector2 Value) : IExrDataType
|
||||
{
|
||||
public ExrVector2(float x, float y) : this(new Vector2(x, y))
|
||||
{
|
||||
}
|
||||
|
||||
public int Size => sizeof(float) + sizeof(float);
|
||||
|
||||
public float X => Value.X;
|
||||
|
||||
public float Y => Value.Y;
|
||||
|
||||
public static ReadOnlySpan<byte> TypeName => "v2f"u8;
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(X);
|
||||
writer.Write(Y);
|
||||
}
|
||||
|
||||
public static implicit operator Vector2(ExrVector2 value) => value.Value;
|
||||
|
||||
public static implicit operator ExrVector2(Vector2 value) => new(value);
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
using AssetRipper.TextureDecoder.Rgb;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public static class ExrWriter
|
||||
{
|
||||
public static ReadOnlySpan<byte> Magic => [0x76, 0x2f, 0x31, 0x01];
|
||||
|
||||
public static void Write<TColor, TChannelValue>(Stream stream, int width, int height, ReadOnlySpan<TColor> pixels)
|
||||
where TChannelValue : unmanaged
|
||||
where TColor : unmanaged, IColor<TChannelValue>
|
||||
{
|
||||
int ChannelCount = Color.GetChannelCount<TColor>();
|
||||
ExrPixelType PixelType = Unsafe.SizeOf<TChannelValue>() <= sizeof(ushort) ? ExrPixelType.Half : ExrPixelType.Single;
|
||||
int ChannelSize = PixelType is ExrPixelType.Half ? Unsafe.SizeOf<Half>() : sizeof(float);
|
||||
|
||||
using BinaryWriter writer = new BinaryWriter(stream);
|
||||
|
||||
//Magic bytes
|
||||
writer.Write(Magic);
|
||||
|
||||
//Version bytes
|
||||
writer.Write((byte)2);//OpenEXR version 2
|
||||
writer.Write(ExrImageFormat.ScanLines.ToVersionByte());
|
||||
writer.WriteNullByte();
|
||||
writer.WriteNullByte();
|
||||
|
||||
ExrChannel[] channels;
|
||||
{
|
||||
Span<ExrChannel> buffer = [default, default, default, default];
|
||||
//Stackalloc doesn't work for managed types.
|
||||
//This can be reworked when ref structs can be used in generics.
|
||||
|
||||
int i = 0;
|
||||
|
||||
//The order doesn't really matter here because the format sorts channels alphabetically
|
||||
//when reading reading scan lines, but this makes that more clear.
|
||||
if (TColor.HasAlphaChannel)
|
||||
{
|
||||
buffer[i] = new ExrChannel("A", PixelType, false, 1, 1);
|
||||
i++;
|
||||
}
|
||||
if (TColor.HasBlueChannel)
|
||||
{
|
||||
buffer[i] = new ExrChannel("B", PixelType, false, 1, 1);
|
||||
i++;
|
||||
}
|
||||
if (TColor.HasGreenChannel)
|
||||
{
|
||||
buffer[i] = new ExrChannel("G", PixelType, false, 1, 1);
|
||||
i++;
|
||||
}
|
||||
if (TColor.HasRedChannel)
|
||||
{
|
||||
buffer[i] = new ExrChannel("R", PixelType, false, 1, 1);
|
||||
i++;
|
||||
}
|
||||
|
||||
channels = buffer[..i].ToArray();
|
||||
}
|
||||
|
||||
//Attributes
|
||||
writer.WriteAttribute(ExrAttributes.Channels, new ExrChannelList(channels));
|
||||
writer.WriteAttribute(ExrAttributes.Compression, "compression"u8, ExrCompression.None);
|
||||
writer.WriteAttribute(ExrAttributes.DataWindow, new ExrBox2I(0, 0, width - 1, height - 1));
|
||||
writer.WriteAttribute(ExrAttributes.DisplayWindow, new ExrBox2I(0, 0, width - 1, height - 1));
|
||||
writer.WriteAttribute(ExrAttributes.LineOrder, "lineOrder"u8, ExrLineOrder.IncreasingY);
|
||||
writer.WriteAttribute(ExrAttributes.PixelAspectRatio, new ExrSingle(1f));
|
||||
writer.WriteAttribute(ExrAttributes.ScreenWindowCenter, new ExrVector2(0f, 0f));
|
||||
writer.WriteAttribute(ExrAttributes.ScreenWindowWidth, new ExrSingle(1f));
|
||||
writer.WriteNullByte();//End of header
|
||||
|
||||
//Scan line offset table
|
||||
long offset = writer.BaseStream.Position + height * sizeof(ulong);
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
writer.Write(offset);
|
||||
offset += sizeof(int) + ChannelCount * ChannelSize * width;
|
||||
}
|
||||
|
||||
//Scan lines
|
||||
if (PixelType is ExrPixelType.Half)
|
||||
{
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
writer.Write(i);
|
||||
writer.Write(ChannelCount * ChannelSize * width);
|
||||
if (TColor.HasAlphaChannel)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
Half alpha = NumericConversion.Convert<TChannelValue, Half>(pixels[i * width + j].A);
|
||||
writer.Write(alpha);
|
||||
}
|
||||
}
|
||||
if (TColor.HasBlueChannel)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
float blue = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].B);
|
||||
if (TColor.HasAlphaChannel)
|
||||
{
|
||||
float alpha = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].A);
|
||||
writer.Write((Half)ConvertSRGB(blue * alpha));
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write((Half)ConvertSRGB(blue));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (TColor.HasGreenChannel)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
float green = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].G);
|
||||
if (TColor.HasAlphaChannel)
|
||||
{
|
||||
float alpha = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].A);
|
||||
writer.Write((Half)ConvertSRGB(green * alpha));
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write((Half)ConvertSRGB(green));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (TColor.HasRedChannel)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
float red = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].R);
|
||||
if (TColor.HasAlphaChannel)
|
||||
{
|
||||
float alpha = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].A);
|
||||
writer.Write((Half)ConvertSRGB(red * alpha));
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write((Half)ConvertSRGB(red));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
writer.Write(i);
|
||||
writer.Write(ChannelCount * ChannelSize * width);
|
||||
if (TColor.HasAlphaChannel)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
float alpha = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].A);
|
||||
writer.Write(alpha);
|
||||
}
|
||||
}
|
||||
if (TColor.HasBlueChannel)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
float blue = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].B);
|
||||
if (TColor.HasAlphaChannel)
|
||||
{
|
||||
float alpha = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].A);
|
||||
writer.Write(ConvertSRGB(blue * alpha));
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write(ConvertSRGB(blue));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (TColor.HasGreenChannel)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
float green = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].G);
|
||||
if (TColor.HasAlphaChannel)
|
||||
{
|
||||
float alpha = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].A);
|
||||
writer.Write(ConvertSRGB(green * alpha));
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write(ConvertSRGB(green));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (TColor.HasRedChannel)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
float red = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].R);
|
||||
if (TColor.HasAlphaChannel)
|
||||
{
|
||||
float alpha = NumericConversion.Convert<TChannelValue, float>(pixels[i * width + j].A);
|
||||
writer.Write(ConvertSRGB(red * alpha));
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write(ConvertSRGB(red));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static float ConvertSRGB(float value)
|
||||
{
|
||||
//https://en.wikipedia.org/wiki/SRGB#From_sRGB_to_CIE_XYZ
|
||||
//https://en.wikipedia.org/wiki/Gamma_correction#Computer_displays
|
||||
if (value < 0.04045)
|
||||
{
|
||||
return value / 12.92f;
|
||||
}
|
||||
else
|
||||
{
|
||||
return float.Pow((value + 0.055f) / 1.055f, 2.4f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public interface IExrDataType
|
||||
{
|
||||
static abstract ReadOnlySpan<byte> TypeName { get; }
|
||||
|
||||
int Size { get; }
|
||||
|
||||
void Write(BinaryWriter writer);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace AssetRipper.TextureDecoder.Exr;
|
||||
|
||||
public readonly record struct TileDescription(int XSize, int YSize, ExrLevelMode Mode) : IExrDataType
|
||||
{
|
||||
public static ReadOnlySpan<byte> TypeName => "tileDesc"u8;
|
||||
|
||||
public int Size => sizeof(int) + sizeof(int) + sizeof(ExrLevelMode);
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(XSize);
|
||||
writer.Write(YSize);
|
||||
writer.Write(Mode);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
|
||||
public readonly struct A : IChannel
|
||||
{
|
||||
static bool IChannel.IsAlpha => true;
|
||||
static T IChannel.GetBlack<T>() => NumericConversion.GetMaximumValueSafe<T>();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
|
||||
public readonly struct B : IChannel
|
||||
{
|
||||
static bool IChannel.IsBlue => true;
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
|
||||
public static class Channel
|
||||
{
|
||||
public static bool TryGetRed<TChannel, TValue>(in TValue field, out TValue value)
|
||||
where TChannel : IChannel
|
||||
where TValue : unmanaged, INumberBase<TValue>, IMinMaxValue<TValue>
|
||||
{
|
||||
if (TChannel.IsRed)
|
||||
{
|
||||
value = TChannel.GetRed(field);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryGetGreen<TChannel, TValue>(in TValue field, out TValue value)
|
||||
where TChannel : IChannel
|
||||
where TValue : unmanaged, INumberBase<TValue>, IMinMaxValue<TValue>
|
||||
{
|
||||
if (TChannel.IsGreen)
|
||||
{
|
||||
value = TChannel.GetGreen(field);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryGetBlue<TChannel, TValue>(in TValue field, out TValue value)
|
||||
where TChannel : IChannel
|
||||
where TValue : unmanaged, INumberBase<TValue>, IMinMaxValue<TValue>
|
||||
{
|
||||
if (TChannel.IsBlue)
|
||||
{
|
||||
value = TChannel.GetBlue(field);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryGetAlpha<TChannel, TValue>(in TValue field, out TValue value)
|
||||
where TChannel : IChannel
|
||||
where TValue : unmanaged, INumberBase<TValue>, IMinMaxValue<TValue>
|
||||
{
|
||||
if (TChannel.IsAlpha)
|
||||
{
|
||||
value = TChannel.GetAlpha(field);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetIfRed<TChannel, TValue>(ref TValue field, TValue value)
|
||||
where TChannel : IChannel
|
||||
where TValue : unmanaged, INumberBase<TValue>, IMinMaxValue<TValue>
|
||||
{
|
||||
if (TChannel.IsRed)
|
||||
{
|
||||
TChannel.SetRed(ref field, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetIfGreen<TChannel, TValue>(ref TValue field, TValue value)
|
||||
where TChannel : IChannel
|
||||
where TValue : unmanaged, INumberBase<TValue>, IMinMaxValue<TValue>
|
||||
{
|
||||
if (TChannel.IsGreen)
|
||||
{
|
||||
TChannel.SetGreen(ref field, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetIfBlue<TChannel, TValue>(ref TValue field, TValue value)
|
||||
where TChannel : IChannel
|
||||
where TValue : unmanaged, INumberBase<TValue>, IMinMaxValue<TValue>
|
||||
{
|
||||
if (TChannel.IsBlue)
|
||||
{
|
||||
TChannel.SetBlue(ref field, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetIfAlpha<TChannel, TValue>(ref TValue field, TValue value)
|
||||
where TChannel : IChannel
|
||||
where TValue : unmanaged, INumberBase<TValue>, IMinMaxValue<TValue>
|
||||
{
|
||||
if (TChannel.IsAlpha)
|
||||
{
|
||||
TChannel.SetAlpha(ref field, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
|
||||
public readonly struct G : IChannel
|
||||
{
|
||||
static bool IChannel.IsGreen => true;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
|
||||
public readonly struct Gray : IChannel
|
||||
{
|
||||
static bool IChannel.IsRed => true;
|
||||
static bool IChannel.IsGreen => true;
|
||||
static bool IChannel.IsBlue => true;
|
||||
static bool IChannel.FullyUtilized => false;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
|
||||
public interface IChannel
|
||||
{
|
||||
static virtual bool IsRed => false;
|
||||
static virtual bool IsGreen => false;
|
||||
static virtual bool IsBlue => false;
|
||||
static virtual bool IsAlpha => false;
|
||||
static virtual bool FullyUtilized => true;
|
||||
|
||||
static virtual T GetBlack<T>() where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
return NumericConversion.GetMinimumValueSafe<T>();
|
||||
}
|
||||
|
||||
static virtual T GetWhite<T>() where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
return NumericConversion.GetMaximumValueSafe<T>();
|
||||
}
|
||||
|
||||
static virtual T GetRed<T>(T field) where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
return field;
|
||||
}
|
||||
|
||||
static virtual T GetGreen<T>(T field) where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
return field;
|
||||
}
|
||||
|
||||
static virtual T GetBlue<T>(T field) where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
return field;
|
||||
}
|
||||
|
||||
static virtual T GetAlpha<T>(T field) where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
return field;
|
||||
}
|
||||
|
||||
static virtual void SetRed<T>(ref T field, T value) where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
field = value;
|
||||
}
|
||||
|
||||
static virtual void SetGreen<T>(ref T field, T value) where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
field = value;
|
||||
}
|
||||
|
||||
static virtual void SetBlue<T>(ref T field, T value) where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
field = value;
|
||||
}
|
||||
|
||||
static virtual void SetAlpha<T>(ref T field, T value) where T : unmanaged, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
field = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
|
||||
public readonly struct R : IChannel
|
||||
{
|
||||
static bool IChannel.IsRed => true;
|
||||
}
|
||||
@@ -74,11 +74,11 @@ public static class Color
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static TTarget Convert<TThis, TThisChannel, TTarget, TTargetChannel>(this TThis color)
|
||||
where TThisChannel : unmanaged
|
||||
where TTargetChannel : unmanaged
|
||||
where TThis : unmanaged, IColor<TThisChannel>
|
||||
where TTarget : unmanaged, IColor<TTargetChannel>
|
||||
public static TTarget Convert<TThis, TThisChannelValue, TTarget, TTargetChannelValue>(this TThis color)
|
||||
where TThisChannelValue : unmanaged
|
||||
where TTargetChannelValue : unmanaged
|
||||
where TThis : unmanaged, IColor<TThisChannelValue>
|
||||
where TTarget : unmanaged, IColor<TTargetChannelValue>
|
||||
{
|
||||
if (typeof(TThis) == typeof(TTarget))
|
||||
{
|
||||
@@ -87,8 +87,8 @@ public static class Color
|
||||
else
|
||||
{
|
||||
TTarget destination = default;
|
||||
color.GetChannels(out TThisChannel r, out TThisChannel g, out TThisChannel b, out TThisChannel a);
|
||||
destination.SetConvertedChannels<TTarget, TTargetChannel, TThisChannel>(r, g, b, a);
|
||||
color.GetChannels(out TThisChannelValue r, out TThisChannelValue g, out TThisChannelValue b, out TThisChannelValue a);
|
||||
destination.SetConvertedChannels<TTarget, TTargetChannelValue, TThisChannelValue>(r, g, b, a);
|
||||
return destination;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
// Auto-generated code. Do not modify manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Rgb;
|
||||
|
||||
public partial struct Color<TChannelValue, TChannel> : IColor<Color<TChannelValue, TChannel>, TChannelValue>
|
||||
where TChannelValue : unmanaged, INumberBase<TChannelValue>, IMinMaxValue<TChannelValue>
|
||||
where TChannel : IChannel
|
||||
{
|
||||
private TChannelValue value;
|
||||
|
||||
public TChannelValue R
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel.IsRed)
|
||||
{
|
||||
return TChannel.GetRed(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMinimumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfRed<TChannel, TChannelValue>(ref value, value);
|
||||
}
|
||||
}
|
||||
|
||||
public TChannelValue G
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel.IsGreen)
|
||||
{
|
||||
return TChannel.GetGreen(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMinimumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfGreen<TChannel, TChannelValue>(ref value, value);
|
||||
}
|
||||
}
|
||||
|
||||
public TChannelValue B
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel.IsBlue)
|
||||
{
|
||||
return TChannel.GetBlue(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMinimumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfBlue<TChannel, TChannelValue>(ref value, value);
|
||||
}
|
||||
}
|
||||
|
||||
public TChannelValue A
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel.IsAlpha)
|
||||
{
|
||||
return TChannel.GetAlpha(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMaximumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfAlpha<TChannel, TChannelValue>(ref value, value);
|
||||
}
|
||||
}
|
||||
|
||||
public Color(TChannelValue value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public readonly void GetChannels(out TChannelValue r, out TChannelValue g, out TChannelValue b, out TChannelValue a)
|
||||
{
|
||||
r = R;
|
||||
g = G;
|
||||
b = B;
|
||||
a = A;
|
||||
}
|
||||
|
||||
public void SetChannels(TChannelValue r, TChannelValue g, TChannelValue b, TChannelValue a)
|
||||
{
|
||||
R = r;
|
||||
G = g;
|
||||
B = b;
|
||||
A = a;
|
||||
}
|
||||
|
||||
static bool IColor.HasRedChannel => TChannel.IsRed;
|
||||
static bool IColor.HasGreenChannel => TChannel.IsGreen;
|
||||
static bool IColor.HasBlueChannel => TChannel.IsBlue;
|
||||
static bool IColor.HasAlphaChannel => TChannel.IsAlpha;
|
||||
static bool IColor.ChannelsAreFullyUtilized => TChannel.FullyUtilized;
|
||||
static Type IColor.ChannelType => typeof(TChannelValue);
|
||||
|
||||
public static Color<TChannelValue, TChannel> Black
|
||||
{
|
||||
get => new(TChannel.GetBlack<TChannelValue>());
|
||||
}
|
||||
|
||||
public static Color<TChannelValue, TChannel> White
|
||||
{
|
||||
get => new(TChannel.GetWhite<TChannelValue>());
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{{ R: {R}, G: {G}, B: {B}, A: {A} }}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
// Auto-generated code. Do not modify manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Rgb;
|
||||
|
||||
public partial struct Color<TChannelValue, TChannel1, TChannel2> : IColor<Color<TChannelValue, TChannel1, TChannel2>, TChannelValue>
|
||||
where TChannelValue : unmanaged, INumberBase<TChannelValue>, IMinMaxValue<TChannelValue>
|
||||
where TChannel1 : IChannel
|
||||
where TChannel2 : IChannel
|
||||
{
|
||||
private TChannelValue value1;
|
||||
private TChannelValue value2;
|
||||
|
||||
public TChannelValue R
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel1.IsRed)
|
||||
{
|
||||
return TChannel1.GetRed(value1);
|
||||
}
|
||||
else if (TChannel2.IsRed)
|
||||
{
|
||||
return TChannel2.GetRed(value2);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMinimumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfRed<TChannel1, TChannelValue>(ref value1, value);
|
||||
Channel.SetIfRed<TChannel2, TChannelValue>(ref value2, value);
|
||||
}
|
||||
}
|
||||
|
||||
public TChannelValue G
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel1.IsGreen)
|
||||
{
|
||||
return TChannel1.GetGreen(value1);
|
||||
}
|
||||
else if (TChannel2.IsGreen)
|
||||
{
|
||||
return TChannel2.GetGreen(value2);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMinimumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfGreen<TChannel1, TChannelValue>(ref value1, value);
|
||||
Channel.SetIfGreen<TChannel2, TChannelValue>(ref value2, value);
|
||||
}
|
||||
}
|
||||
|
||||
public TChannelValue B
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel1.IsBlue)
|
||||
{
|
||||
return TChannel1.GetBlue(value1);
|
||||
}
|
||||
else if (TChannel2.IsBlue)
|
||||
{
|
||||
return TChannel2.GetBlue(value2);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMinimumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfBlue<TChannel1, TChannelValue>(ref value1, value);
|
||||
Channel.SetIfBlue<TChannel2, TChannelValue>(ref value2, value);
|
||||
}
|
||||
}
|
||||
|
||||
public TChannelValue A
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel1.IsAlpha)
|
||||
{
|
||||
return TChannel1.GetAlpha(value1);
|
||||
}
|
||||
else if (TChannel2.IsAlpha)
|
||||
{
|
||||
return TChannel2.GetAlpha(value2);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMaximumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfAlpha<TChannel1, TChannelValue>(ref value1, value);
|
||||
Channel.SetIfAlpha<TChannel2, TChannelValue>(ref value2, value);
|
||||
}
|
||||
}
|
||||
|
||||
public Color(TChannelValue value1, TChannelValue value2)
|
||||
{
|
||||
this.value1 = value1;
|
||||
this.value2 = value2;
|
||||
}
|
||||
|
||||
public readonly void GetChannels(out TChannelValue r, out TChannelValue g, out TChannelValue b, out TChannelValue a)
|
||||
{
|
||||
r = R;
|
||||
g = G;
|
||||
b = B;
|
||||
a = A;
|
||||
}
|
||||
|
||||
public void SetChannels(TChannelValue r, TChannelValue g, TChannelValue b, TChannelValue a)
|
||||
{
|
||||
R = r;
|
||||
G = g;
|
||||
B = b;
|
||||
A = a;
|
||||
}
|
||||
|
||||
static bool IColor.HasRedChannel => TChannel1.IsRed || TChannel2.IsRed;
|
||||
static bool IColor.HasGreenChannel => TChannel1.IsGreen || TChannel2.IsGreen;
|
||||
static bool IColor.HasBlueChannel => TChannel1.IsBlue || TChannel2.IsBlue;
|
||||
static bool IColor.HasAlphaChannel => TChannel1.IsAlpha || TChannel2.IsAlpha;
|
||||
static bool IColor.ChannelsAreFullyUtilized => TChannel1.FullyUtilized && TChannel2.FullyUtilized;
|
||||
static Type IColor.ChannelType => typeof(TChannelValue);
|
||||
|
||||
public static Color<TChannelValue, TChannel1, TChannel2> Black
|
||||
{
|
||||
get => new(TChannel1.GetBlack<TChannelValue>(), TChannel2.GetBlack<TChannelValue>());
|
||||
}
|
||||
|
||||
public static Color<TChannelValue, TChannel1, TChannel2> White
|
||||
{
|
||||
get => new(TChannel1.GetWhite<TChannelValue>(), TChannel2.GetWhite<TChannelValue>());
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{{ R: {R}, G: {G}, B: {B}, A: {A} }}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
// Auto-generated code. Do not modify manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Rgb;
|
||||
|
||||
public partial struct Color<TChannelValue, TChannel1, TChannel2, TChannel3> : IColor<Color<TChannelValue, TChannel1, TChannel2, TChannel3>, TChannelValue>
|
||||
where TChannelValue : unmanaged, INumberBase<TChannelValue>, IMinMaxValue<TChannelValue>
|
||||
where TChannel1 : IChannel
|
||||
where TChannel2 : IChannel
|
||||
where TChannel3 : IChannel
|
||||
{
|
||||
private TChannelValue value1;
|
||||
private TChannelValue value2;
|
||||
private TChannelValue value3;
|
||||
|
||||
public TChannelValue R
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel1.IsRed)
|
||||
{
|
||||
return TChannel1.GetRed(value1);
|
||||
}
|
||||
else if (TChannel2.IsRed)
|
||||
{
|
||||
return TChannel2.GetRed(value2);
|
||||
}
|
||||
else if (TChannel3.IsRed)
|
||||
{
|
||||
return TChannel3.GetRed(value3);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMinimumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfRed<TChannel1, TChannelValue>(ref value1, value);
|
||||
Channel.SetIfRed<TChannel2, TChannelValue>(ref value2, value);
|
||||
Channel.SetIfRed<TChannel3, TChannelValue>(ref value3, value);
|
||||
}
|
||||
}
|
||||
|
||||
public TChannelValue G
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel1.IsGreen)
|
||||
{
|
||||
return TChannel1.GetGreen(value1);
|
||||
}
|
||||
else if (TChannel2.IsGreen)
|
||||
{
|
||||
return TChannel2.GetGreen(value2);
|
||||
}
|
||||
else if (TChannel3.IsGreen)
|
||||
{
|
||||
return TChannel3.GetGreen(value3);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMinimumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfGreen<TChannel1, TChannelValue>(ref value1, value);
|
||||
Channel.SetIfGreen<TChannel2, TChannelValue>(ref value2, value);
|
||||
Channel.SetIfGreen<TChannel3, TChannelValue>(ref value3, value);
|
||||
}
|
||||
}
|
||||
|
||||
public TChannelValue B
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel1.IsBlue)
|
||||
{
|
||||
return TChannel1.GetBlue(value1);
|
||||
}
|
||||
else if (TChannel2.IsBlue)
|
||||
{
|
||||
return TChannel2.GetBlue(value2);
|
||||
}
|
||||
else if (TChannel3.IsBlue)
|
||||
{
|
||||
return TChannel3.GetBlue(value3);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMinimumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfBlue<TChannel1, TChannelValue>(ref value1, value);
|
||||
Channel.SetIfBlue<TChannel2, TChannelValue>(ref value2, value);
|
||||
Channel.SetIfBlue<TChannel3, TChannelValue>(ref value3, value);
|
||||
}
|
||||
}
|
||||
|
||||
public TChannelValue A
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel1.IsAlpha)
|
||||
{
|
||||
return TChannel1.GetAlpha(value1);
|
||||
}
|
||||
else if (TChannel2.IsAlpha)
|
||||
{
|
||||
return TChannel2.GetAlpha(value2);
|
||||
}
|
||||
else if (TChannel3.IsAlpha)
|
||||
{
|
||||
return TChannel3.GetAlpha(value3);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMaximumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfAlpha<TChannel1, TChannelValue>(ref value1, value);
|
||||
Channel.SetIfAlpha<TChannel2, TChannelValue>(ref value2, value);
|
||||
Channel.SetIfAlpha<TChannel3, TChannelValue>(ref value3, value);
|
||||
}
|
||||
}
|
||||
|
||||
public Color(TChannelValue value1, TChannelValue value2, TChannelValue value3)
|
||||
{
|
||||
this.value1 = value1;
|
||||
this.value2 = value2;
|
||||
this.value3 = value3;
|
||||
}
|
||||
|
||||
public readonly void GetChannels(out TChannelValue r, out TChannelValue g, out TChannelValue b, out TChannelValue a)
|
||||
{
|
||||
r = R;
|
||||
g = G;
|
||||
b = B;
|
||||
a = A;
|
||||
}
|
||||
|
||||
public void SetChannels(TChannelValue r, TChannelValue g, TChannelValue b, TChannelValue a)
|
||||
{
|
||||
R = r;
|
||||
G = g;
|
||||
B = b;
|
||||
A = a;
|
||||
}
|
||||
|
||||
static bool IColor.HasRedChannel => TChannel1.IsRed || TChannel2.IsRed || TChannel3.IsRed;
|
||||
static bool IColor.HasGreenChannel => TChannel1.IsGreen || TChannel2.IsGreen || TChannel3.IsGreen;
|
||||
static bool IColor.HasBlueChannel => TChannel1.IsBlue || TChannel2.IsBlue || TChannel3.IsBlue;
|
||||
static bool IColor.HasAlphaChannel => TChannel1.IsAlpha || TChannel2.IsAlpha || TChannel3.IsAlpha;
|
||||
static bool IColor.ChannelsAreFullyUtilized => TChannel1.FullyUtilized && TChannel2.FullyUtilized && TChannel3.FullyUtilized;
|
||||
static Type IColor.ChannelType => typeof(TChannelValue);
|
||||
|
||||
public static Color<TChannelValue, TChannel1, TChannel2, TChannel3> Black
|
||||
{
|
||||
get => new(TChannel1.GetBlack<TChannelValue>(), TChannel2.GetBlack<TChannelValue>(), TChannel3.GetBlack<TChannelValue>());
|
||||
}
|
||||
|
||||
public static Color<TChannelValue, TChannel1, TChannel2, TChannel3> White
|
||||
{
|
||||
get => new(TChannel1.GetWhite<TChannelValue>(), TChannel2.GetWhite<TChannelValue>(), TChannel3.GetWhite<TChannelValue>());
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{{ R: {R}, G: {G}, B: {B}, A: {A} }}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
// Auto-generated code. Do not modify manually.
|
||||
|
||||
using AssetRipper.TextureDecoder.Rgb.Channels;
|
||||
|
||||
namespace AssetRipper.TextureDecoder.Rgb;
|
||||
|
||||
public partial struct Color<TChannelValue, TChannel1, TChannel2, TChannel3, TChannel4> : IColor<Color<TChannelValue, TChannel1, TChannel2, TChannel3, TChannel4>, TChannelValue>
|
||||
where TChannelValue : unmanaged, INumberBase<TChannelValue>, IMinMaxValue<TChannelValue>
|
||||
where TChannel1 : IChannel
|
||||
where TChannel2 : IChannel
|
||||
where TChannel3 : IChannel
|
||||
where TChannel4 : IChannel
|
||||
{
|
||||
private TChannelValue value1;
|
||||
private TChannelValue value2;
|
||||
private TChannelValue value3;
|
||||
private TChannelValue value4;
|
||||
|
||||
public TChannelValue R
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel1.IsRed)
|
||||
{
|
||||
return TChannel1.GetRed(value1);
|
||||
}
|
||||
else if (TChannel2.IsRed)
|
||||
{
|
||||
return TChannel2.GetRed(value2);
|
||||
}
|
||||
else if (TChannel3.IsRed)
|
||||
{
|
||||
return TChannel3.GetRed(value3);
|
||||
}
|
||||
else if (TChannel4.IsRed)
|
||||
{
|
||||
return TChannel4.GetRed(value4);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMinimumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfRed<TChannel1, TChannelValue>(ref value1, value);
|
||||
Channel.SetIfRed<TChannel2, TChannelValue>(ref value2, value);
|
||||
Channel.SetIfRed<TChannel3, TChannelValue>(ref value3, value);
|
||||
Channel.SetIfRed<TChannel4, TChannelValue>(ref value4, value);
|
||||
}
|
||||
}
|
||||
|
||||
public TChannelValue G
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel1.IsGreen)
|
||||
{
|
||||
return TChannel1.GetGreen(value1);
|
||||
}
|
||||
else if (TChannel2.IsGreen)
|
||||
{
|
||||
return TChannel2.GetGreen(value2);
|
||||
}
|
||||
else if (TChannel3.IsGreen)
|
||||
{
|
||||
return TChannel3.GetGreen(value3);
|
||||
}
|
||||
else if (TChannel4.IsGreen)
|
||||
{
|
||||
return TChannel4.GetGreen(value4);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMinimumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfGreen<TChannel1, TChannelValue>(ref value1, value);
|
||||
Channel.SetIfGreen<TChannel2, TChannelValue>(ref value2, value);
|
||||
Channel.SetIfGreen<TChannel3, TChannelValue>(ref value3, value);
|
||||
Channel.SetIfGreen<TChannel4, TChannelValue>(ref value4, value);
|
||||
}
|
||||
}
|
||||
|
||||
public TChannelValue B
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel1.IsBlue)
|
||||
{
|
||||
return TChannel1.GetBlue(value1);
|
||||
}
|
||||
else if (TChannel2.IsBlue)
|
||||
{
|
||||
return TChannel2.GetBlue(value2);
|
||||
}
|
||||
else if (TChannel3.IsBlue)
|
||||
{
|
||||
return TChannel3.GetBlue(value3);
|
||||
}
|
||||
else if (TChannel4.IsBlue)
|
||||
{
|
||||
return TChannel4.GetBlue(value4);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMinimumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfBlue<TChannel1, TChannelValue>(ref value1, value);
|
||||
Channel.SetIfBlue<TChannel2, TChannelValue>(ref value2, value);
|
||||
Channel.SetIfBlue<TChannel3, TChannelValue>(ref value3, value);
|
||||
Channel.SetIfBlue<TChannel4, TChannelValue>(ref value4, value);
|
||||
}
|
||||
}
|
||||
|
||||
public TChannelValue A
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (TChannel1.IsAlpha)
|
||||
{
|
||||
return TChannel1.GetAlpha(value1);
|
||||
}
|
||||
else if (TChannel2.IsAlpha)
|
||||
{
|
||||
return TChannel2.GetAlpha(value2);
|
||||
}
|
||||
else if (TChannel3.IsAlpha)
|
||||
{
|
||||
return TChannel3.GetAlpha(value3);
|
||||
}
|
||||
else if (TChannel4.IsAlpha)
|
||||
{
|
||||
return TChannel4.GetAlpha(value4);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NumericConversion.GetMaximumValueSafe<TChannelValue>();
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
Channel.SetIfAlpha<TChannel1, TChannelValue>(ref value1, value);
|
||||
Channel.SetIfAlpha<TChannel2, TChannelValue>(ref value2, value);
|
||||
Channel.SetIfAlpha<TChannel3, TChannelValue>(ref value3, value);
|
||||
Channel.SetIfAlpha<TChannel4, TChannelValue>(ref value4, value);
|
||||
}
|
||||
}
|
||||
|
||||
public Color(TChannelValue value1, TChannelValue value2, TChannelValue value3, TChannelValue value4)
|
||||
{
|
||||
this.value1 = value1;
|
||||
this.value2 = value2;
|
||||
this.value3 = value3;
|
||||
this.value4 = value4;
|
||||
}
|
||||
|
||||
public readonly void GetChannels(out TChannelValue r, out TChannelValue g, out TChannelValue b, out TChannelValue a)
|
||||
{
|
||||
r = R;
|
||||
g = G;
|
||||
b = B;
|
||||
a = A;
|
||||
}
|
||||
|
||||
public void SetChannels(TChannelValue r, TChannelValue g, TChannelValue b, TChannelValue a)
|
||||
{
|
||||
R = r;
|
||||
G = g;
|
||||
B = b;
|
||||
A = a;
|
||||
}
|
||||
|
||||
static bool IColor.HasRedChannel => TChannel1.IsRed || TChannel2.IsRed || TChannel3.IsRed || TChannel4.IsRed;
|
||||
static bool IColor.HasGreenChannel => TChannel1.IsGreen || TChannel2.IsGreen || TChannel3.IsGreen || TChannel4.IsGreen;
|
||||
static bool IColor.HasBlueChannel => TChannel1.IsBlue || TChannel2.IsBlue || TChannel3.IsBlue || TChannel4.IsBlue;
|
||||
static bool IColor.HasAlphaChannel => TChannel1.IsAlpha || TChannel2.IsAlpha || TChannel3.IsAlpha || TChannel4.IsAlpha;
|
||||
static bool IColor.ChannelsAreFullyUtilized => TChannel1.FullyUtilized && TChannel2.FullyUtilized && TChannel3.FullyUtilized && TChannel4.FullyUtilized;
|
||||
static Type IColor.ChannelType => typeof(TChannelValue);
|
||||
|
||||
public static Color<TChannelValue, TChannel1, TChannel2, TChannel3, TChannel4> Black
|
||||
{
|
||||
get => new(TChannel1.GetBlack<TChannelValue>(), TChannel2.GetBlack<TChannelValue>(), TChannel3.GetBlack<TChannelValue>(), TChannel4.GetBlack<TChannelValue>());
|
||||
}
|
||||
|
||||
public static Color<TChannelValue, TChannel1, TChannel2, TChannel3, TChannel4> White
|
||||
{
|
||||
get => new(TChannel1.GetWhite<TChannelValue>(), TChannel2.GetWhite<TChannelValue>(), TChannel3.GetWhite<TChannelValue>(), TChannel4.GetWhite<TChannelValue>());
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{{ R: {R}, G: {G}, B: {B}, A: {A} }}";
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
/// <typeparam name="TSelf">
|
||||
/// The implementing type.
|
||||
/// </typeparam>
|
||||
/// <typeparam name="TChannel">
|
||||
/// <typeparam name="TChannelValue">
|
||||
/// Supported types are:
|
||||
/// <list type="bullet">
|
||||
/// <item><see cref="sbyte"/></item>
|
||||
@@ -32,9 +32,9 @@
|
||||
/// <item><see cref="decimal"/></item>
|
||||
/// </list>
|
||||
/// </typeparam>
|
||||
public interface IColor<TSelf, TChannel> : IColor<TChannel>
|
||||
where TSelf : unmanaged, IColor<TSelf, TChannel>
|
||||
where TChannel : unmanaged
|
||||
public interface IColor<TSelf, TChannelValue> : IColor<TChannelValue>
|
||||
where TSelf : unmanaged, IColor<TSelf, TChannelValue>
|
||||
where TChannelValue : unmanaged
|
||||
{
|
||||
/// <summary>
|
||||
/// A black pixel.
|
||||
|
||||
@@ -35,50 +35,50 @@ namespace AssetRipper.TextureDecoder.Yuy2
|
||||
/// Decompress a YUY2 image
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
|
||||
/// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <typeparam name="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressYUY2<TOutputColor, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannel : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannel>
|
||||
public static int DecompressYUY2<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
output = new byte[width * height * Unsafe.SizeOf<TOutputColor>()];
|
||||
return DecompressYUY2<TOutputColor, TOutputChannel>(input, width, height, output);
|
||||
return DecompressYUY2<TOutputColor, TOutputChannelValue>(input, width, height, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a YUY2 image
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
|
||||
/// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <typeparam name="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressYUY2<TOutputColor, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannel : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannel>
|
||||
public static int DecompressYUY2<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
return DecompressYUY2<TOutputColor, TOutputChannel>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
return DecompressYUY2<TOutputColor, TOutputChannelValue>(input, width, height, MemoryMarshal.Cast<byte, TOutputColor>(output));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompress a YUY2 image
|
||||
/// </summary>
|
||||
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
|
||||
/// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <typeparam name="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
|
||||
/// <param name="input">Input buffer containing the compressed image.</param>
|
||||
/// <param name="width">Pixel width of the image.</param>
|
||||
/// <param name="height">Pixel height of the image.</param>
|
||||
/// <param name="output">An output buffer. Must be at least width * height.</param>
|
||||
/// <returns>Number of bytes read from <paramref name="input"/></returns>
|
||||
public static int DecompressYUY2<TOutputColor, TOutputChannel>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannel : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannel>
|
||||
public static int DecompressYUY2<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int width, int height, Span<TOutputColor> output)
|
||||
where TOutputChannelValue : unmanaged
|
||||
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
|
||||
{
|
||||
ThrowHelper.ThrowIfNotEnoughSpace(output.Length, width * height);
|
||||
|
||||
@@ -99,12 +99,12 @@ namespace AssetRipper.TextureDecoder.Yuy2
|
||||
byte b0 = ClampByte((298 * c + 516 * d + 128) >> 8); // blue
|
||||
byte g0 = ClampByte((298 * c - 100 * d - 208 * e + 128) >> 8); // green
|
||||
byte r0 = ClampByte((298 * c + 409 * e + 128) >> 8); // red
|
||||
output[o++].SetConvertedChannels<TOutputColor, TOutputChannel, byte>(r0, g0, b0);
|
||||
output[o++].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>(r0, g0, b0);
|
||||
c = y1 - 16;
|
||||
byte b1 = ClampByte((298 * c + 516 * d + 128) >> 8); // blue
|
||||
byte g1 = ClampByte((298 * c - 100 * d - 208 * e + 128) >> 8); // green
|
||||
byte r1 = ClampByte((298 * c + 409 * e + 128) >> 8); // red
|
||||
output[o++].SetConvertedChannels<TOutputColor, TOutputChannel, byte>(r1, g1, b1);
|
||||
output[o++].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>(r1, g1, b1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<IsAotCompatible>true</IsAotCompatible>
|
||||
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
|
||||
|
||||
<Version>2.1.0</Version>
|
||||
<Version>2.2.0</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
<Copyright>Copyright © ds5678</Copyright>
|
||||
<Authors>ds5678</Authors>
|
||||
|
||||
Reference in new issue
Block a user