Interface IMultiImageWriter
An optional interface for a format's writer whose files hold several images (TIFF pages, icon entries, DDS texture arrays and cube maps), for SaveAll(IEnumerable<Raster>, Stream, FormatInfo, WriterOptions?). Without it, SaveAll takes one raster and writes it as Save would.
public interface IMultiImageWriter
Examples
/// <summary>A format holding several RAW8 images: "RAW8P", the image count as one byte, then each image as RAW8 stores it.</summary>
public static class Raw8PagesFormat
{
public static FormatInfo Info { get; } = new()
{
Name = "RAW8P",
Extensions = ["raw8p"],
SignatureLength = 5,
Matches = header => header.StartsWith("RAW8P"u8),
Reader = new Reader(),
};
/// <summary>Names the image a Load reads; LoadAll sets it for each.</summary>
public sealed record PageOptions : ReaderOptions
{
public int Page { get; init; }
}
private sealed class Reader : IFormatReader, IMultiImageReader
{
public IReadOnlyList<ReaderOptions> Images(EgamiInput input, ReaderOptions options)
{
input.Skip(5);
int count = input.ReadByte();
return [.. Enumerable.Range(0, count).Select(page => new PageOptions { Recovery = options.Recovery, Page = page })];
}
public RasterInfo ReadInfo(EgamiInput input, ReaderOptions options)
{
(int width, int height, int count) = Seek(input, options);
return new RasterInfo { Format = Info, Width = width, Height = height, Layout = Rgba8.Layout, FrameCount = 1, ImageCount = count, EstimatedBytes = 4L * width * height };
}
public Raster<TPixel> Read<TPixel>(EgamiInput input, ReaderOptions options)
where TPixel : unmanaged, IPixelType<TPixel>
{
(int width, int height, _) = Seek(input, options);
Raster<TPixel> raster = input.CreateRaster<TPixel>(width, height);
byte[] row = new byte[width * 4];
for (int y = 0; y < height; y++)
{
input.ReadExactly(row);
PixelConvert.Convert<Rgba8, TPixel>(MemoryMarshal.Cast<byte, Rgba8>(row.AsSpan()), raster.Row(y));
}
return raster;
}
public Raster Read(EgamiInput input, ReaderOptions options) => Read<Rgba8>(input, options);
/// <summary>Skips to the page the options name and reads its size.</summary>
private static (int Width, int Height, int Count) Seek(EgamiInput input, ReaderOptions options)
{
input.Skip(5);
int count = input.ReadByte();
int page = (options as PageOptions)?.Page ?? 0;
for (int i = 0; ; i++)
{
input.Skip(4);
int width = (int)input.ReadUInt32BigEndian();
int height = (int)input.ReadUInt32BigEndian();
if (i == page)
{
return (width, height, count);
}
input.Skip(4L * width * height);
}
}
}
}
Methods
WriteAll(IReadOnlyList<Raster>, Stream, WriterOptions)
Writes every raster, in order, to one file.
void WriteAll(IReadOnlyList<Raster> rasters, Stream output, WriterOptions options)
Parameters
rastersIReadOnlyList<Raster>The images, at least one.
outputStreamWhere to write. The writer leaves it open.
optionsWriterOptionsHow to write.
Examples
/// <summary>A format holding several RAW8 images: "RAW8P", the image count as one byte, then each image as RAW8 stores it.</summary>
public static class Raw8PagesFormat
{
public static FormatInfo Info { get; } = new()
{
Name = "RAW8P",
Extensions = ["raw8p"],
SignatureLength = 5,
Matches = header => header.StartsWith("RAW8P"u8),
Reader = new Reader(),
};
/// <summary>Names the image a Load reads; LoadAll sets it for each.</summary>
public sealed record PageOptions : ReaderOptions
{
public int Page { get; init; }
}
private sealed class Reader : IFormatReader, IMultiImageReader
{
public IReadOnlyList<ReaderOptions> Images(EgamiInput input, ReaderOptions options)
{
input.Skip(5);
int count = input.ReadByte();
return [.. Enumerable.Range(0, count).Select(page => new PageOptions { Recovery = options.Recovery, Page = page })];
}
public RasterInfo ReadInfo(EgamiInput input, ReaderOptions options)
{
(int width, int height, int count) = Seek(input, options);
return new RasterInfo { Format = Info, Width = width, Height = height, Layout = Rgba8.Layout, FrameCount = 1, ImageCount = count, EstimatedBytes = 4L * width * height };
}
public Raster<TPixel> Read<TPixel>(EgamiInput input, ReaderOptions options)
where TPixel : unmanaged, IPixelType<TPixel>
{
(int width, int height, _) = Seek(input, options);
Raster<TPixel> raster = input.CreateRaster<TPixel>(width, height);
byte[] row = new byte[width * 4];
for (int y = 0; y < height; y++)
{
input.ReadExactly(row);
PixelConvert.Convert<Rgba8, TPixel>(MemoryMarshal.Cast<byte, Rgba8>(row.AsSpan()), raster.Row(y));
}
return raster;
}
public Raster Read(EgamiInput input, ReaderOptions options) => Read<Rgba8>(input, options);
/// <summary>Skips to the page the options name and reads its size.</summary>
private static (int Width, int Height, int Count) Seek(EgamiInput input, ReaderOptions options)
{
input.Skip(5);
int count = input.ReadByte();
int page = (options as PageOptions)?.Page ?? 0;
for (int i = 0; ; i++)
{
input.Skip(4);
int width = (int)input.ReadUInt32BigEndian();
int height = (int)input.ReadUInt32BigEndian();
if (i == page)
{
return (width, height, count);
}
input.Skip(4L * width * height);
}
}
}
}