Skip to content

Commit 8e82e70

Browse files
committed
OpenEXR writing
1 parent 1957784 commit 8e82e70

21 files changed

+644
-11
lines changed

AssetRipper.TextureDecoder.ConsoleApp/DirectBitmap.cs

+7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using AssetRipper.TextureDecoder.Exr;
12
using AssetRipper.TextureDecoder.Rgb;
23
using AssetRipper.TextureDecoder.Rgb.Channels;
34
using AssetRipper.TextureDecoder.Rgb.Formats;
@@ -55,6 +56,12 @@ public void SaveAsBmp(string path)
5556
}
5657
}
5758

59+
public void SaveAsExr(string path)
60+
{
61+
using Stream stream = File.OpenWrite(path);
62+
ExrWriter.Write<TColor, TColorArg>(stream, Width, Height, Pixels);
63+
}
64+
5865
public void SaveAsHdr(string path)
5966
{
6067
GetDataAndComponentsForSaving(out byte[] data, out ColorComponents components);

AssetRipper.TextureDecoder.ConsoleApp/Program.cs

+25-11
Original file line numberDiff line numberDiff line change
@@ -63,18 +63,32 @@ static void Main(string[] args)
6363

6464
switch (outputType)
6565
{
66+
case "bgra":
67+
WriteAllBytes(Path.Combine(dirPath, name + ".bgra"), bitmap.Bits);
68+
break;
69+
case "bmp":
70+
bitmap.FlipY();
71+
bitmap.SaveAsBmp(Path.Combine(dirPath, name + ".bmp"));
72+
break;
73+
case "exr":
74+
bitmap.FlipY();
75+
bitmap.SaveAsExr(Path.Combine(dirPath, name + ".exr"));
76+
break;
77+
case "hdr":
78+
bitmap.FlipY();
79+
bitmap.SaveAsHdr(Path.Combine(dirPath, name + ".hdr"));
80+
break;
81+
case "jpg":
82+
bitmap.FlipY();
83+
bitmap.SaveAsJpg(Path.Combine(dirPath, name + ".jpg"));
84+
break;
6685
case "png":
67-
{
68-
string newPath = Path.Combine(dirPath, name + ".png");
69-
bitmap.FlipY();
70-
bitmap.SaveAsPng(newPath);
71-
}
86+
bitmap.FlipY();
87+
bitmap.SaveAsPng(Path.Combine(dirPath, name + ".png"));
7288
break;
73-
case "bgra":
74-
{
75-
string newPath = Path.Combine(dirPath, name + ".bgra");
76-
WriteAllBytes(newPath, bitmap.Bits);
77-
}
89+
case "tga":
90+
bitmap.FlipY();
91+
bitmap.SaveAsTga(Path.Combine(dirPath, name + ".tga"));
7892
break;
7993
default:
8094
throw new NotSupportedException(outputType);
@@ -239,4 +253,4 @@ public static void WriteAllBytes(string path, ReadOnlySpan<byte> bytes)
239253
using Microsoft.Win32.SafeHandles.SafeFileHandle sfh = File.OpenHandle(path, FileMode.Create, FileAccess.Write, FileShare.Read);
240254
RandomAccess.Write(sfh, bytes, 0);
241255
}
242-
}
256+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
using System.Buffers;
2+
using System.Text;
3+
4+
namespace AssetRipper.TextureDecoder.Exr;
5+
6+
internal static class BinaryWriterExtensions
7+
{
8+
public static void WriteLengthPrefixedString(this BinaryWriter writer, string value)
9+
{
10+
int length = Encoding.UTF8.GetByteCount(value);
11+
writer.Write(length);
12+
13+
byte[]? rentedArray;
14+
scoped Span<byte> buffer;
15+
if (length <= 1024)
16+
{
17+
rentedArray = null;
18+
buffer = stackalloc byte[length];
19+
}
20+
else
21+
{
22+
rentedArray = ArrayPool<byte>.Shared.Rent(length);
23+
buffer = rentedArray.AsSpan(0, length);
24+
}
25+
26+
Encoding.UTF8.GetBytes(value, buffer);
27+
writer.Write(buffer);
28+
29+
if (rentedArray is not null)
30+
{
31+
ArrayPool<byte>.Shared.Return(rentedArray);
32+
}
33+
}
34+
35+
public static void WriteNullTerminatedString(this BinaryWriter writer, string value)
36+
{
37+
int length = Encoding.UTF8.GetByteCount(value);
38+
39+
byte[]? rentedArray;
40+
scoped Span<byte> buffer;
41+
if (length <= 1024)
42+
{
43+
rentedArray = null;
44+
buffer = stackalloc byte[length];
45+
}
46+
else
47+
{
48+
rentedArray = ArrayPool<byte>.Shared.Rent(length);
49+
buffer = rentedArray.AsSpan(0, length);
50+
}
51+
52+
Encoding.UTF8.GetBytes(value, buffer);
53+
writer.Write(buffer);
54+
55+
if (rentedArray is not null)
56+
{
57+
ArrayPool<byte>.Shared.Return(rentedArray);
58+
}
59+
60+
writer.WriteNullByte();
61+
}
62+
63+
public static void WriteNullTerminatedString(this BinaryWriter writer, ReadOnlySpan<byte> utf8String)
64+
{
65+
writer.Write(utf8String);
66+
writer.WriteNullByte();
67+
}
68+
69+
public static void WriteNullByte(this BinaryWriter writer)
70+
{
71+
writer.Write(default(byte));
72+
}
73+
74+
public static void Write<T>(this BinaryWriter writer, T value) where T : unmanaged, Enum
75+
{
76+
switch (Unsafe.SizeOf<T>())
77+
{
78+
case sizeof(byte):
79+
writer.Write(Unsafe.As<T, byte>(ref value));
80+
break;
81+
case sizeof(ushort):
82+
writer.Write(Unsafe.As<T, ushort>(ref value));
83+
break;
84+
case sizeof(uint):
85+
writer.Write(Unsafe.As<T, uint>(ref value));
86+
break;
87+
case sizeof(ulong):
88+
writer.Write(Unsafe.As<T, ulong>(ref value));
89+
break;
90+
}
91+
}
92+
93+
public static void WriteAttribute<T>(this BinaryWriter writer, ReadOnlySpan<byte> attributeName, T attributeValue) where T : IExrDataType
94+
{
95+
writer.Write(attributeName);
96+
writer.WriteNullByte();
97+
writer.Write(T.TypeName);
98+
writer.WriteNullByte();
99+
writer.Write(attributeValue.Size);
100+
attributeValue.Write(writer);
101+
}
102+
103+
public static void WriteAttribute<T>(this BinaryWriter writer, ReadOnlySpan<byte> attributeName, ReadOnlySpan<byte> attributeType, T attributeValue) where T : unmanaged, Enum
104+
{
105+
writer.Write(attributeName);
106+
writer.WriteNullByte();
107+
writer.Write(attributeType);
108+
writer.WriteNullByte();
109+
writer.Write(Unsafe.SizeOf<T>());
110+
writer.Write(attributeValue);
111+
}
112+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
namespace AssetRipper.TextureDecoder.Exr;
2+
3+
public static class ExrAttributes
4+
{
5+
public static ReadOnlySpan<byte> Channels => "channels"u8;
6+
7+
public static ReadOnlySpan<byte> Compression => "compression"u8;
8+
9+
public static ReadOnlySpan<byte> DataWindow => "dataWindow"u8;
10+
11+
public static ReadOnlySpan<byte> DisplayWindow => "displayWindow"u8;
12+
13+
public static ReadOnlySpan<byte> LineOrder => "lineOrder"u8;
14+
15+
public static ReadOnlySpan<byte> PixelAspectRatio => "pixelAspectRatio"u8;
16+
17+
public static ReadOnlySpan<byte> ScreenWindowCenter => "screenWindowCenter"u8;
18+
19+
public static ReadOnlySpan<byte> ScreenWindowWidth => "screenWindowWidth"u8;
20+
21+
public static ReadOnlySpan<byte> Tiles => "tiles"u8;
22+
}
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
namespace AssetRipper.TextureDecoder.Exr;
2+
3+
public readonly record struct ExrBox2F(float XMin, float YMin, float XMax, float YMax) : IExrDataType
4+
{
5+
public static ReadOnlySpan<byte> TypeName => "box2f"u8;
6+
7+
public int Size => sizeof(float) + sizeof(float) + sizeof(float) + sizeof(float);
8+
9+
public void Write(BinaryWriter writer)
10+
{
11+
writer.Write(XMin);
12+
writer.Write(YMin);
13+
writer.Write(XMax);
14+
writer.Write(YMax);
15+
}
16+
}
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
namespace AssetRipper.TextureDecoder.Exr;
2+
3+
public readonly record struct ExrBox2I(int XMin, int YMin, int XMax, int YMax) : IExrDataType
4+
{
5+
public static ReadOnlySpan<byte> TypeName => "box2i"u8;
6+
7+
public int Size => sizeof(int) + sizeof(int) + sizeof(int) + sizeof(int);
8+
9+
public void Write(BinaryWriter writer)
10+
{
11+
writer.Write(XMin);
12+
writer.Write(YMin);
13+
writer.Write(XMax);
14+
writer.Write(YMax);
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System.Text;
2+
3+
namespace AssetRipper.TextureDecoder.Exr;
4+
5+
public readonly record struct ExrChannel(string Name, ExrPixelType PixelType, bool PLinear, int XSampling, int YSampling)
6+
{
7+
public int Size
8+
{
9+
get
10+
{
11+
return Encoding.UTF8.GetByteCount(Name)
12+
+ sizeof(byte)//Null terminator
13+
+ sizeof(ExrPixelType)
14+
+ sizeof(bool)
15+
+ 3 * sizeof(byte)//Reserved bytes
16+
+ sizeof(int)
17+
+ sizeof(int);
18+
}
19+
}
20+
21+
public void Write(BinaryWriter writer)
22+
{
23+
writer.WriteNullTerminatedString(Name);
24+
writer.Write(PixelType);
25+
writer.Write(PLinear);
26+
writer.WriteNullByte();
27+
writer.WriteNullByte();
28+
writer.WriteNullByte();
29+
writer.Write(XSampling);
30+
writer.Write(YSampling);
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace AssetRipper.TextureDecoder.Exr;
2+
3+
public readonly record struct ExrChannelList(ExrChannel[] Channels) : IExrDataType
4+
{
5+
public static ReadOnlySpan<byte> TypeName => "chlist"u8;
6+
7+
public int Size => Channels.Sum(c => c.Size);
8+
9+
public void Write(BinaryWriter writer)
10+
{
11+
foreach (ExrChannel channel in Channels)
12+
{
13+
channel.Write(writer);
14+
}
15+
writer.WriteNullByte();
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
namespace AssetRipper.TextureDecoder.Exr;
2+
3+
public enum ExrCompression : byte
4+
{
5+
None = 0,
6+
RLE = 1,
7+
ZIPS = 2,
8+
ZIP = 3,
9+
PIZ = 4,
10+
PXR24 = 5,
11+
B44 = 6,
12+
B44A = 7,
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace AssetRipper.TextureDecoder.Exr;
2+
3+
public readonly record struct ExrDouble(double Value) : IExrDataType
4+
{
5+
public static ReadOnlySpan<byte> TypeName => "double"u8;
6+
7+
public int Size => sizeof(double);
8+
9+
public void Write(BinaryWriter writer)
10+
{
11+
writer.Write(Value);
12+
}
13+
14+
public static implicit operator double(ExrDouble value) => value.Value;
15+
16+
public static implicit operator ExrDouble(double value) => new(value);
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace AssetRipper.TextureDecoder.Exr;
2+
public enum ExrImageFormat
3+
{
4+
ScanLines,
5+
Tiles,
6+
}
7+
public static class ExrImageFormatExtensions
8+
{
9+
public static byte ToVersionByte(this ExrImageFormat format) => format switch
10+
{
11+
ExrImageFormat.Tiles => 0x02,
12+
_ => 0x00,
13+
};
14+
}
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace AssetRipper.TextureDecoder.Exr;
2+
3+
public readonly record struct ExrInt32(int Value) : IExrDataType
4+
{
5+
public static ReadOnlySpan<byte> TypeName => "int"u8;
6+
7+
public int Size => sizeof(int);
8+
9+
public void Write(BinaryWriter writer)
10+
{
11+
writer.Write(Value);
12+
}
13+
14+
public static implicit operator int(ExrInt32 value) => value.Value;
15+
16+
public static implicit operator ExrInt32(int value) => new(value);
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace AssetRipper.TextureDecoder.Exr;
2+
3+
[Flags]
4+
public enum ExrLevelMode : byte
5+
{
6+
OneLevel = 0,
7+
MipMapLevels = 1,
8+
RipMapLevels = 2,
9+
RoundDown = 0,
10+
RoundUp = 16,
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace AssetRipper.TextureDecoder.Exr;
2+
3+
public enum ExrLineOrder : byte
4+
{
5+
IncreasingY = 0,
6+
DecreasingY = 1,
7+
RandomY = 2,
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace AssetRipper.TextureDecoder.Exr;
2+
3+
public enum ExrPixelType : int
4+
{
5+
UInt = 0,
6+
Half = 1,
7+
Single = 2,
8+
}

0 commit comments

Comments
 (0)