Class RowEncoder<TPixel>
One encode in progress: a still image's rows top-down with WriteRow(ReadOnlySpan<TPixel>), or whole frames with WriteFrame(Frame<TPixel>), then Complete().
public abstract class RowEncoder<TPixel> : IDisposable where TPixel : unmanaged, IPixelType<TPixel>
Type Parameters
TPixelThe pixel type of the rows and frames.
- Inheritance
-
RowEncoder<TPixel>
- Implements
- Inherited Members
Examples
/// <summary>The same RAW8 format, streaming: rows go in and out one at a time, so RasterDecoder, RasterEncoder and Transcode stream it.</summary>
public static class StreamingRaw8Format
{
public static FormatInfo Info { get; } = Raw8Format.Info with { Reader = new Reader(), Writer = new Writer() };
private sealed class Reader : IFormatReader, IRowFormatReader
{
private static readonly IFormatReader Whole = Raw8Format.Info.Reader!;
public RasterInfo ReadInfo(EgamiInput input, ReaderOptions options) => Whole.ReadInfo(input, options) with { Access = RasterAccess.Streamed };
public Raster<TPixel> Read<TPixel>(EgamiInput input, ReaderOptions options)
where TPixel : unmanaged, IPixelType<TPixel> => Whole.Read<TPixel>(input, options);
public Raster Read(EgamiInput input, ReaderOptions options) => Whole.Read(input, options);
// The open image reads each row when it is asked for; the decoder crops, reduces and converts after it.
public RowReader Open(EgamiInput input, ReaderOptions options)
{
input.Skip(4);
int width = (int)input.ReadUInt32BigEndian();
int height = (int)input.ReadUInt32BigEndian();
// No raster checks the size here, so check it before the row buffer is allocated.
input.CheckFrameSize(width, height);
return new Rows(input, width, height);
}
}
private sealed class Rows(EgamiInput input, int width, int height) : RowReader<Rgba8>
{
private readonly Rgba8[] _row = new Rgba8[width];
private int _next;
private bool _started;
public override int Width => width;
public override int Height => height;
public override Metadata.RasterMetadata Metadata { get; } = new();
public override Metadata.FrameMetadata? NextFrame()
{
if (_started)
{
return null;
}
_started = true;
return new Metadata.FrameMetadata();
}
public override void ReadRow<TPixel>(int y, int x, Span<TPixel> destination)
{
for (; _next <= y; _next++)
{
input.ReadExactly(MemoryMarshal.AsBytes(_row.AsSpan()));
}
PixelConvert.Convert<Rgba8, TPixel>(_row.AsSpan(x, destination.Length), destination);
}
}
private sealed class Writer : IFormatWriter, IRowFormatWriter
{
public void Write<TPixel>(Raster<TPixel> raster, Stream output, WriterOptions options)
where TPixel : unmanaged, IPixelType<TPixel> => Raw8Format.Info.Writer!.Write(raster, output, options);
// Save, SaveAsync, RasterEncoder and Transcode all write through the encoder.
public RowEncoder<TPixel> Start<TPixel>(Stream output, EncodeStart<TPixel> start)
where TPixel : unmanaged, IPixelType<TPixel>
{
// RAW8 keeps 8-bit RGBA and nothing else: deeper samples or metadata are reported as lost.
start.ReportLosses("RAW8", SaveKeeps.Alpha);
return new Encoder<TPixel>(output, start.Width, start.Height);
}
}
private sealed class Encoder<TPixel> : RowEncoder<TPixel>
where TPixel : unmanaged, IPixelType<TPixel>
{
private readonly Stream _output;
private readonly Rgba8[] _row;
public Encoder(Stream output, int width, int height)
{
_output = output;
_row = new Rgba8[width];
Span<byte> header = stackalloc byte[12];
"RAW8"u8.CopyTo(header);
BinaryPrimitives.WriteUInt32BigEndian(header[4..], (uint)width);
BinaryPrimitives.WriteUInt32BigEndian(header[8..], (uint)height);
output.Write(header);
}
public override void WriteRow(ReadOnlySpan<TPixel> row)
{
PixelConvert.Convert<TPixel, Rgba8>(row, _row);
_output.Write(MemoryMarshal.AsBytes(_row.AsSpan()));
}
public override void Complete()
{
}
}
}
Properties
Animates
Gets whether the format holds more than one frame, so WriteFrame(Frame<TPixel>) takes them all. Otherwise one frame is written as the still image, through WriteRow(ReadOnlySpan<TPixel>).
public virtual bool Animates { get; }
Property Value
Methods
Complete()
Writes what follows the image data. Every row or frame has been written.
public abstract void Complete()
Dispose()
Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
public void Dispose()
Dispose(bool)
Returns the encoder's buffers.
protected virtual void Dispose(bool disposing)
Parameters
WriteFrame(Frame<TPixel>)
Writes the next frame of an animation. Only called when Animates.
public virtual void WriteFrame(Frame<TPixel> frame)
Parameters
frameFrame<TPixel>The frame and its metadata; the encoder may not keep it after returning.
Exceptions
- NotSupportedException
The format does not animate.
WriteRow(ReadOnlySpan<TPixel>)
Writes the next row of the still image, top-down.
public abstract void WriteRow(ReadOnlySpan<TPixel> row)
Parameters
rowReadOnlySpan<TPixel>The row, Width wide.
WriteWhole(Raster<TPixel>)
Writes the whole image Save was given, in steps: SaveAsync writes out the bytes so far at each one. By default the rows of the first frame, top-down, 64 at a time; a format with bottom-up rows or frames overrides it.
public virtual IEnumerable<int> WriteWhole(Raster<TPixel> raster)
Parameters
Returns
- IEnumerable<int>
A step after each band or frame; the values only count steps.