Skip to content

Commit 5cd9424

Browse files
committed
Type raw texture payload variants
1 parent cb62313 commit 5cd9424

10 files changed

Lines changed: 292 additions & 76 deletions

File tree

src/PlateauResoniteLink/Application/Importing/SceneImportContractTypes.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ private RawRgba32TexturePayload(
134134
ITextureImportSource source)
135135
: base(source)
136136
{
137-
ArgumentNullException.ThrowIfNull(binaryPayload);
137+
Rgba32RawTexturePayload.ValidateByteLength(width, height, binaryPayload);
138138
Width = width;
139139
Height = height;
140140
BinaryPayload = ImmutableArray.CreateRange(binaryPayload);

src/PlateauResoniteLink/Application/Importing/TextureImportSource.cs

Lines changed: 191 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,99 @@
88

99
namespace PlateauResoniteLink.Application.Importing;
1010

11-
public enum RawTexturePayloadFormat
11+
public abstract class RawTexturePayload
1212
{
13-
Rgba32 = 0,
14-
RgbaFloat32 = 1,
13+
private protected RawTexturePayload(
14+
int width,
15+
int height,
16+
string? colorProfile,
17+
byte[] bytes)
18+
{
19+
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(width);
20+
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(height);
21+
ArgumentNullException.ThrowIfNull(bytes);
22+
23+
Width = width;
24+
Height = height;
25+
ColorProfile = colorProfile;
26+
Bytes = bytes;
27+
}
28+
29+
public int Width { get; }
30+
31+
public int Height { get; }
32+
33+
public string? ColorProfile { get; }
34+
35+
public byte[] Bytes { get; }
36+
37+
public abstract TResult Match<TResult>(
38+
Func<Rgba32RawTexturePayload, TResult> rgba32,
39+
Func<RgbaFloat32RawTexturePayload, TResult> rgbaFloat32);
1540
}
1641

17-
public sealed record RawTexturePayload(
18-
int Width,
19-
int Height,
20-
string? ColorProfile,
21-
byte[] Bytes,
22-
RawTexturePayloadFormat Format = RawTexturePayloadFormat.Rgba32);
42+
public sealed class Rgba32RawTexturePayload : RawTexturePayload
43+
{
44+
private const int BytesPerPixel = 4;
45+
46+
public Rgba32RawTexturePayload(
47+
int width,
48+
int height,
49+
string? colorProfile,
50+
byte[] bytes)
51+
: base(width, height, colorProfile, bytes)
52+
{
53+
ValidateByteLength(width, height, bytes);
54+
}
55+
56+
internal static void ValidateByteLength(
57+
int width,
58+
int height,
59+
byte[] bytes)
60+
{
61+
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(width);
62+
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(height);
63+
ArgumentNullException.ThrowIfNull(bytes);
64+
65+
int expectedByteLength = checked(width * height * BytesPerPixel);
66+
if (bytes.Length != expectedByteLength)
67+
{
68+
throw new ArgumentException(
69+
$"RGBA32 texture payload length must be width * height * {BytesPerPixel} bytes ({expectedByteLength}), but was {bytes.Length}.",
70+
nameof(bytes));
71+
}
72+
}
73+
74+
public override TResult Match<TResult>(
75+
Func<Rgba32RawTexturePayload, TResult> rgba32,
76+
Func<RgbaFloat32RawTexturePayload, TResult> rgbaFloat32)
77+
{
78+
ArgumentNullException.ThrowIfNull(rgba32);
79+
ArgumentNullException.ThrowIfNull(rgbaFloat32);
80+
return rgba32(this);
81+
}
82+
}
83+
84+
public sealed class RgbaFloat32RawTexturePayload : RawTexturePayload
85+
{
86+
public RgbaFloat32RawTexturePayload(
87+
int width,
88+
int height,
89+
string? colorProfile,
90+
byte[] bytes)
91+
: base(width, height, colorProfile, bytes)
92+
{
93+
}
94+
95+
public override TResult Match<TResult>(
96+
Func<Rgba32RawTexturePayload, TResult> rgba32,
97+
Func<RgbaFloat32RawTexturePayload, TResult> rgbaFloat32)
98+
{
99+
ArgumentNullException.ThrowIfNull(rgba32);
100+
ArgumentNullException.ThrowIfNull(rgbaFloat32);
101+
return rgbaFloat32(this);
102+
}
103+
}
23104

24105
public interface ITextureImportSource
25106
{
@@ -32,9 +113,14 @@ public interface ITextureImportSource
32113
long? EstimatedByteLength { get; }
33114
}
34115

35-
internal interface IRawTexturePayloadSource : ITextureImportSource
116+
internal interface IRgba32RawTexturePayloadSource : ITextureImportSource
36117
{
37-
ValueTask<RawTexturePayload> MaterializeRawAsync(CancellationToken cancellationToken);
118+
ValueTask<Rgba32RawTexturePayload> MaterializeRgba32Async(CancellationToken cancellationToken);
119+
}
120+
121+
internal interface IRgbaFloat32RawTexturePayloadSource : ITextureImportSource
122+
{
123+
ValueTask<RgbaFloat32RawTexturePayload> MaterializeRgbaFloat32Async(CancellationToken cancellationToken);
38124
}
39125

40126
internal static class TextureImportSourceMaterializer
@@ -44,17 +130,50 @@ public static ValueTask<RawTexturePayload> MaterializeRawAsync(
44130
CancellationToken cancellationToken)
45131
{
46132
ArgumentNullException.ThrowIfNull(source);
47-
if (source is not IRawTexturePayloadSource rawSource)
133+
if (source is IRgba32RawTexturePayloadSource rgba32Source)
134+
{
135+
return new ValueTask<RawTexturePayload>(MaterializeRgba32AsRawAsync(rgba32Source, cancellationToken));
136+
}
137+
138+
if (source is IRgbaFloat32RawTexturePayloadSource rgbaFloat32Source)
139+
{
140+
return new ValueTask<RawTexturePayload>(MaterializeRgbaFloat32AsRawAsync(rgbaFloat32Source, cancellationToken));
141+
}
142+
143+
throw new InvalidOperationException(
144+
$"Texture import source '{source.GetType().Name}' cannot materialize a raw texture payload.");
145+
146+
static async Task<RawTexturePayload> MaterializeRgba32AsRawAsync(
147+
IRgba32RawTexturePayloadSource source,
148+
CancellationToken cancellationToken)
149+
{
150+
return await source.MaterializeRgba32Async(cancellationToken);
151+
}
152+
153+
static async Task<RawTexturePayload> MaterializeRgbaFloat32AsRawAsync(
154+
IRgbaFloat32RawTexturePayloadSource source,
155+
CancellationToken cancellationToken)
156+
{
157+
return await source.MaterializeRgbaFloat32Async(cancellationToken);
158+
}
159+
}
160+
161+
public static ValueTask<Rgba32RawTexturePayload> MaterializeRgba32Async(
162+
ITextureImportSource source,
163+
CancellationToken cancellationToken)
164+
{
165+
ArgumentNullException.ThrowIfNull(source);
166+
if (source is not IRgba32RawTexturePayloadSource rgba32Source)
48167
{
49168
throw new InvalidOperationException(
50-
$"Texture import source '{source.GetType().Name}' cannot materialize a raw texture payload.");
169+
$"Texture import source '{source.GetType().Name}' cannot materialize an RGBA32 texture payload.");
51170
}
52171

53-
return rawSource.MaterializeRawAsync(cancellationToken);
172+
return rgba32Source.MaterializeRgba32Async(cancellationToken);
54173
}
55174
}
56175

57-
internal sealed class InMemoryRawTextureImportSource : IRawTexturePayloadSource
176+
internal sealed class InMemoryRawTextureImportSource : IRgba32RawTexturePayloadSource
58177
{
59178
private readonly byte[] bytes;
60179

@@ -69,6 +188,7 @@ public InMemoryRawTextureImportSource(
69188
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(height);
70189
ArgumentNullException.ThrowIfNull(bytes);
71190
ArgumentException.ThrowIfNullOrWhiteSpace(identity);
191+
Rgba32RawTexturePayload.ValidateByteLength(width, height, bytes);
72192
Width = width;
73193
Height = height;
74194
ColorProfile = colorProfile;
@@ -88,18 +208,18 @@ public InMemoryRawTextureImportSource(
88208

89209
public long? EstimatedByteLength => bytes.Length;
90210

91-
public ValueTask<RawTexturePayload> MaterializeRawAsync(CancellationToken cancellationToken)
211+
public ValueTask<Rgba32RawTexturePayload> MaterializeRgba32Async(CancellationToken cancellationToken)
92212
{
93213
cancellationToken.ThrowIfCancellationRequested();
94-
return ValueTask.FromResult(new RawTexturePayload(
214+
return ValueTask.FromResult(new Rgba32RawTexturePayload(
95215
Width,
96216
Height,
97217
ColorProfile,
98218
(byte[])bytes.Clone()));
99219
}
100220
}
101221

102-
internal sealed class InMemoryEncodedTextureImportSource : IRawTexturePayloadSource
222+
internal sealed class InMemoryEncodedTextureImportSource : IRgba32RawTexturePayloadSource
103223
{
104224
private readonly byte[] bytes;
105225

@@ -123,7 +243,7 @@ public InMemoryEncodedTextureImportSource(
123243

124244
public long? EstimatedByteLength => bytes.Length;
125245

126-
public async ValueTask<RawTexturePayload> MaterializeRawAsync(CancellationToken cancellationToken)
246+
public async ValueTask<Rgba32RawTexturePayload> MaterializeRgba32Async(CancellationToken cancellationToken)
127247
{
128248
cancellationToken.ThrowIfCancellationRequested();
129249
using MemoryStream stream = new(bytes, writable: false);
@@ -138,7 +258,7 @@ internal sealed class DatasetTextureImportSource(
138258
IPlateauDatasetContentSource datasetSource,
139259
string relativePath,
140260
string? colorProfile,
141-
string identity) : IRawTexturePayloadSource
261+
string identity) : IRgba32RawTexturePayloadSource
142262
{
143263
public string Identity { get; } = identity;
144264

@@ -150,7 +270,7 @@ internal sealed class DatasetTextureImportSource(
150270
? lengthSource.TryGetFileLength(relativePath)
151271
: null;
152272

153-
public async ValueTask<RawTexturePayload> MaterializeRawAsync(CancellationToken cancellationToken)
273+
public async ValueTask<Rgba32RawTexturePayload> MaterializeRgba32Async(CancellationToken cancellationToken)
154274
{
155275
await using Stream stream = await datasetSource.OpenReadAsync(relativePath, cancellationToken);
156276
using Image<Rgba32> image = await Image.LoadAsync<Rgba32>(stream, cancellationToken);
@@ -161,7 +281,7 @@ public async ValueTask<RawTexturePayload> MaterializeRawAsync(CancellationToken
161281
internal sealed class FileTextureImportSource(
162282
string absolutePath,
163283
string colorProfile,
164-
string identity) : IRawTexturePayloadSource
284+
string identity) : IRgba32RawTexturePayloadSource
165285
{
166286
public string Identity { get; } = identity;
167287

@@ -188,19 +308,40 @@ public long? EstimatedByteLength
188308
}
189309
}
190310

191-
public async ValueTask<RawTexturePayload> MaterializeRawAsync(CancellationToken cancellationToken)
311+
public async ValueTask<Rgba32RawTexturePayload> MaterializeRgba32Async(CancellationToken cancellationToken)
192312
{
193313
using Image<Rgba32> image = await Image.LoadAsync<Rgba32>(absolutePath, cancellationToken);
194314
return TextureImportSourceFactory.CreateRawPayloadFromImage(image, ColorProfile);
195315
}
196316
}
197317

198-
internal sealed class GeneratedTextureImportSource(
199-
Func<CancellationToken, ValueTask<RawTexturePayload>> materializeRawAsync,
318+
internal sealed class GeneratedRgba32TextureImportSource(
319+
Func<CancellationToken, ValueTask<Rgba32RawTexturePayload>> materializeRgba32Async,
320+
string identity,
321+
string description,
322+
string? colorProfile,
323+
long? estimatedByteLength = null) : IRgba32RawTexturePayloadSource
324+
{
325+
public string Identity { get; } = identity;
326+
327+
public string Description { get; } = description;
328+
329+
public string? ColorProfile { get; } = colorProfile;
330+
331+
public long? EstimatedByteLength { get; } = estimatedByteLength;
332+
333+
public ValueTask<Rgba32RawTexturePayload> MaterializeRgba32Async(CancellationToken cancellationToken)
334+
{
335+
return materializeRgba32Async(cancellationToken);
336+
}
337+
}
338+
339+
internal sealed class GeneratedRgbaFloat32TextureImportSource(
340+
Func<CancellationToken, ValueTask<RgbaFloat32RawTexturePayload>> materializeRgbaFloat32Async,
200341
string identity,
201342
string description,
202343
string? colorProfile,
203-
long? estimatedByteLength = null) : IRawTexturePayloadSource
344+
long? estimatedByteLength = null) : IRgbaFloat32RawTexturePayloadSource
204345
{
205346
public string Identity { get; } = identity;
206347

@@ -210,9 +351,9 @@ internal sealed class GeneratedTextureImportSource(
210351

211352
public long? EstimatedByteLength { get; } = estimatedByteLength;
212353

213-
public ValueTask<RawTexturePayload> MaterializeRawAsync(CancellationToken cancellationToken)
354+
public ValueTask<RgbaFloat32RawTexturePayload> MaterializeRgbaFloat32Async(CancellationToken cancellationToken)
214355
{
215-
return materializeRawAsync(cancellationToken);
356+
return materializeRgbaFloat32Async(cancellationToken);
216357
}
217358
}
218359

@@ -266,15 +407,30 @@ public static ITextureImportSource CreateFileImage(
266407
identity ?? $"file:{Path.GetFullPath(absolutePath)}:{colorProfile}");
267408
}
268409

269-
public static ITextureImportSource CreateGeneratedImage(
270-
Func<CancellationToken, ValueTask<RawTexturePayload>> materializeRawAsync,
410+
public static ITextureImportSource CreateGeneratedRgba32Image(
411+
Func<CancellationToken, ValueTask<Rgba32RawTexturePayload>> materializeRgba32Async,
412+
string identity,
413+
string description,
414+
string? colorProfile,
415+
long? estimatedByteLength = null)
416+
{
417+
return new GeneratedRgba32TextureImportSource(
418+
materializeRgba32Async,
419+
identity,
420+
description,
421+
colorProfile,
422+
estimatedByteLength);
423+
}
424+
425+
public static ITextureImportSource CreateGeneratedRgbaFloat32Image(
426+
Func<CancellationToken, ValueTask<RgbaFloat32RawTexturePayload>> materializeRgbaFloat32Async,
271427
string identity,
272428
string description,
273429
string? colorProfile,
274430
long? estimatedByteLength = null)
275431
{
276-
return new GeneratedTextureImportSource(
277-
materializeRawAsync,
432+
return new GeneratedRgbaFloat32TextureImportSource(
433+
materializeRgbaFloat32Async,
278434
identity,
279435
description,
280436
colorProfile,
@@ -290,7 +446,7 @@ public static ITextureImportSource CreateGeneratedImageFromClone(
290446
ArgumentNullException.ThrowIfNull(image);
291447
Image<Rgba32> retainedImage = image.Clone();
292448
object gate = new();
293-
return CreateGeneratedImage(
449+
return CreateGeneratedRgba32Image(
294450
cancellationToken =>
295451
{
296452
cancellationToken.ThrowIfCancellationRequested();
@@ -305,14 +461,14 @@ public static ITextureImportSource CreateGeneratedImageFromClone(
305461
(long)image.Width * image.Height * 4);
306462
}
307463

308-
public static RawTexturePayload CreateRawPayloadFromImage(
464+
public static Rgba32RawTexturePayload CreateRawPayloadFromImage(
309465
Image<Rgba32> image,
310466
string? colorProfile)
311467
{
312468
ArgumentNullException.ThrowIfNull(image);
313469
byte[] rawBytes = new byte[image.Width * image.Height * 4];
314470
image.CopyPixelDataTo(rawBytes);
315-
return new RawTexturePayload(
471+
return new Rgba32RawTexturePayload(
316472
image.Width,
317473
image.Height,
318474
colorProfile,

0 commit comments

Comments
 (0)