Table of Contents

Class RasterEncoder<TPixel>

Namespace
Egami
Assembly
Egami.dll

An image being written a band of rows or a frame at a time, so a huge image never has to exist whole. The bytes are the ones Save(Stream, FormatInfo, WriterOptions?) writes for the same pixels and options, except where a format must write differently without seeking: top-down BMP and TGA rows, and run-length TGA, which is always top-down. Options that need every pixel before the first byte (automatic palettes, PNG's Reduce, automatic alpha) throw at Create instead of quietly writing something else.

public sealed class RasterEncoder<TPixel> : IDisposable where TPixel : unmanaged, IPixelType<TPixel>

Type Parameters

TPixel

The pixel type of the rows and frames.

Inheritance
RasterEncoder<TPixel>
Implements
Inherited Members

Examples

// A huge image written a band at a time: only the band is ever in memory.
const int Width = 8_000, Height = 6_000;
using Raster<Rgb8> band = Raster.Create<Rgb8>(Width, 64);
using RasterEncoder<Rgb8> encoder = RasterEncoder.Create<Rgb8>("big.png", Width, Height);
for (int top = 0; top < Height; top += band.Height)
{
    int rows = Math.Min(band.Height, Height - top);
    for (int y = 0; y < rows; y++)
    {
        band.Row(y).Fill(new Rgb8((byte)(top + y), 128, 200));
    }

    encoder.WriteRows(band, rows);
}

encoder.Complete(); // writes IEND; without it the file is deleted on dispose

Remarks

Write a still image with WriteRows(Raster<TPixel>, int), top to bottom, or an animation with WriteFrame(Raster<TPixel>, FrameMetadata?); then call Complete(), which writes what follows the image data. An encoder is one caller's object and is not thread-safe. Formats from plugins that do not stream gather the image and write it at Complete.

Properties

Format

Gets the format being written.

public FormatInfo Format { get; }

Property Value

FormatInfo

FramesWritten

Gets how many frames have been written.

public int FramesWritten { get; }

Property Value

int

Height

Gets the image height.

public int Height { get; }

Property Value

int

RowsWritten

Gets how many rows of the still image have been written.

public int RowsWritten { get; }

Property Value

int

Width

Gets the image width.

public int Width { get; }

Property Value

int

Methods

Complete()

Writes what follows the image data, such as PNG's IEND or a TGA footer, and the bytes still held. Without it the output is truncated.

public void Complete()

Exceptions

InvalidOperationException

Fewer rows than Height, or no frame, were written.

ObjectDisposedException

The encoder has been disposed.

CompleteAsync(CancellationToken)

Completes the image as Complete() does, writing the last bytes asynchronously.

public ValueTask CompleteAsync(CancellationToken cancellationToken = default)

Parameters

cancellationToken CancellationToken

Cancels the write.

Returns

ValueTask

A task that completes when every byte is written.

Exceptions

InvalidOperationException

Fewer rows than Height, or no frame, were written.

ObjectDisposedException

The encoder has been disposed.

OperationCanceledException

cancellationToken was canceled.

Dispose()

Releases the encoder. Disposing before Complete() leaves a truncated stream; a file the encoder created is deleted.

public void Dispose()

WriteFrame(Raster<TPixel>, FrameMetadata?)

Writes the next frame of an animation (GIF, APNG), with its delay from metadata. A format that holds one image takes one frame, written as the still image.

public void WriteFrame(Raster<TPixel> frame, FrameMetadata? metadata = null)

Parameters

frame Raster<TPixel>

The frame, Width × Height; its first frame is written.

metadata FrameMetadata

The frame's delay, or null for the frame's own metadata.

Examples

// An animation written a frame at a time, each frame from one reused canvas.
using Raster<Rgba8> canvas = Raster.Create<Rgba8>(120, 80);
using FileStream file = File.Create("spinner.gif");
using RasterEncoder<Rgba8> gif = RasterEncoder.Create<Rgba8>(file, GifFormat.Info, 120, 80);
for (int i = 0; i < 12; i++)
{
    for (int y = 0; y < canvas.Height; y++)
    {
        canvas.Row(y).Fill(new Rgba8(255, 255, 255, 255));
    }

    canvas.Row(40)[(i * 10)..((i * 10) + 10)].Fill(new Rgba8(200, 0, 0, 255));
    gif.WriteFrame(canvas, new Metadata.FrameMetadata { Delay = TimeSpan.FromMilliseconds(80) });
}

gif.Complete();

Exceptions

ArgumentNullException

frame is null.

ArgumentException

frame is not Width × Height.

InvalidOperationException

Rows were written, the format holds one image and it was written, an APNG to a stream that cannot seek has no FrameCount, or the encoder is complete.

ObjectDisposedException

The encoder has been disposed.

WriteFrameAsync(Raster<TPixel>, FrameMetadata?, CancellationToken)

Writes the next frame as WriteFrame(Raster<TPixel>, FrameMetadata?) does, then writes the encoded bytes asynchronously.

public ValueTask WriteFrameAsync(Raster<TPixel> frame, FrameMetadata? metadata = null, CancellationToken cancellationToken = default)

Parameters

frame Raster<TPixel>

The frame, Width × Height.

metadata FrameMetadata

The frame's delay, or null for the frame's own metadata.

cancellationToken CancellationToken

Cancels the write.

Returns

ValueTask

A task that completes when the bytes are written.

Exceptions

ArgumentNullException

frame is null.

ArgumentException

frame is not Width × Height.

InvalidOperationException

As for WriteFrame(Raster<TPixel>, FrameMetadata?).

ObjectDisposedException

The encoder has been disposed.

OperationCanceledException

cancellationToken was canceled.

WriteRows(Raster<TPixel>)

Writes every row of band's first frame as the next rows of the still image.

public void WriteRows(Raster<TPixel> band)

Parameters

band Raster<TPixel>

The rows, Width wide.

Exceptions

ArgumentNullException

band is null.

ArgumentException

band is not Width wide.

ArgumentOutOfRangeException

The band holds more rows than are left.

InvalidOperationException

Frames were written, or the encoder is complete.

ObjectDisposedException

The encoder has been disposed.

WriteRows(Raster<TPixel>, int)

Writes the next rows rows of the still image, from the top of band's first frame. Rows go top to bottom; together the calls write exactly Height rows.

public void WriteRows(Raster<TPixel> band, int rows)

Parameters

band Raster<TPixel>

The rows, Width wide.

rows int

How many of the band's rows to write, from its top.

Examples

// A huge image written a band at a time: only the band is ever in memory.
const int Width = 8_000, Height = 6_000;
using Raster<Rgb8> band = Raster.Create<Rgb8>(Width, 64);
using RasterEncoder<Rgb8> encoder = RasterEncoder.Create<Rgb8>("big.png", Width, Height);
for (int top = 0; top < Height; top += band.Height)
{
    int rows = Math.Min(band.Height, Height - top);
    for (int y = 0; y < rows; y++)
    {
        band.Row(y).Fill(new Rgb8((byte)(top + y), 128, 200));
    }

    encoder.WriteRows(band, rows);
}

encoder.Complete(); // writes IEND; without it the file is deleted on dispose

Exceptions

ArgumentNullException

band is null.

ArgumentException

band is not Width wide.

ArgumentOutOfRangeException

rows is negative, more than the band holds, or more than are left.

InvalidOperationException

Frames were written, or the encoder is complete.

ObjectDisposedException

The encoder has been disposed.

WriteRowsAsync(Raster<TPixel>, int, CancellationToken)

Writes the next rows as WriteRows(Raster<TPixel>, int) does, then writes the encoded bytes to the stream asynchronously. Encoding itself is synchronous, since it is CPU-bound.

public ValueTask WriteRowsAsync(Raster<TPixel> band, int rows, CancellationToken cancellationToken = default)

Parameters

band Raster<TPixel>

The rows, Width wide.

rows int

How many of the band's rows to write, from its top.

cancellationToken CancellationToken

Cancels the write.

Returns

ValueTask

A task that completes when the bytes are written.

Examples

// On a server: send each band as it is encoded, without holding the file.
using RasterDecoder source = RasterDecoder.Open("egret.png");
using Raster<Rgb8> band = Raster.Create<Rgb8>(source.Info.Width, 32);
using RasterEncoder<Rgb8> encoder = RasterEncoder.Create<Rgb8>(response, JpegFormat.Info, source.Info.Width, source.Info.Height);
int rows;
while ((rows = source.ReadRows(band)) > 0)
{
    await encoder.WriteRowsAsync(band, rows, TestContext.Current.CancellationToken);
}

await encoder.CompleteAsync(TestContext.Current.CancellationToken);

Exceptions

ArgumentNullException

band is null.

ArgumentException

band is not Width wide.

ArgumentOutOfRangeException

rows is negative, more than the band holds, or more than are left.

InvalidOperationException

Frames were written, or the encoder is complete.

ObjectDisposedException

The encoder has been disposed.

OperationCanceledException

cancellationToken was canceled.