Table of Contents

Class RowReader

Namespace
Egami.Formats
Assembly
Egami.dll

One open image, as a format reader streams it: frames of rows at the stored size. Derive from RowReader<TNatural>, which names the natural pixel type.

public abstract class RowReader : IDisposable
Inheritance
RowReader
Implements
Derived
RowReader<TNatural>
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()
        {
        }
    }
}

Constructors

RowReader()

Creates the reader. Only RowReader<TNatural> can derive from it, since it names the natural pixel type.

protected RowReader()

Properties

Access

Gets whether rows are decoded as they are asked for or served from a buffer. Defaults to streamed.

public virtual RasterAccess Access { get; }

Property Value

RasterAccess

FrameCount

Gets the number of frames, or null while unknown. Defaults to 1.

public virtual int? FrameCount { get; }

Property Value

int?

Height

Gets the stored height.

public abstract int Height { get; }

Property Value

int

IsMetadataComplete

Gets whether Metadata is complete; a format can store metadata after its image data. Defaults to true.

public virtual bool IsMetadataComplete { get; }

Property Value

bool

Layout

Gets the layout of the natural pixel type.

public abstract PixelLayoutInfo Layout { get; }

Property Value

PixelLayoutInfo

Metadata

Gets the metadata read so far.

public abstract RasterMetadata Metadata { get; }

Property Value

RasterMetadata

Width

Gets the stored width.

public abstract int Width { get; }

Property Value

int

WorkingBytes

Gets the most bytes the reader holds while decoding, for WorkingBytes. Defaults to 0.

public virtual long WorkingBytes { get; }

Property Value

long

Methods

Dispose()

Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.

public void Dispose()

Dispose(bool)

Returns the reader's buffers.

protected virtual void Dispose(bool disposing)

Parameters

disposing bool

true when called from Dispose().

EndFrame()

Says no more rows of the current frame are needed, so the rest can be skipped. Does nothing by default.

public virtual void EndFrame()

NextFrame()

Moves to the next frame: the first call moves to frame 0. Returns its metadata, or null after the last.

public abstract FrameMetadata? NextFrame()

Returns

FrameMetadata

The frame's metadata, or null when there are no more frames.

ReadRow<TPixel>(int, int, Span<TPixel>)

Converts row y of the current frame, from column x, into destination. y never decreases within a frame, so rows in between can be skipped.

public abstract void ReadRow<TPixel>(int y, int x, Span<TPixel> destination) where TPixel : unmanaged, IPixelType<TPixel>

Parameters

y int

The row.

x int

The first column.

destination Span<TPixel>

Where the row goes; its length is how many columns are wanted.

Type Parameters

TPixel

The pixel type to produce.