Table of Contents

Class Picture

Namespace
Egami.Drawing
Assembly
Egami.Drawing.dll

Recorded canvas calls, from a PictureRecorder or Load(Stream, EgamiContext?): replay them on any canvas with DrawPicture(Picture, Matrix3x2?, Paint?, bool) or Playback(Canvas), fill shapes with them through a PicturePaint, render them with ToRaster<TPixel>(float, CanvasOptions?, EgamiContext?), or keep them in an .egdr file with Save(Stream). Replay is always vector, so a picture stays sharp at any scale. Immutable, and safe to replay on several threads and canvases at once.

public sealed class Picture
Inheritance
Picture
Inherited Members

Examples

// Record any picture code once...
PictureRecorder recorder = new(new RectF(0, 0, 40, 40));
DrawLogo(recorder);
Picture logo = recorder.Finish();

using (RasterCanvas<Rgba8> canvas = raster.CreateCanvas())
{
    // ...and replay it anywhere, at any scale, still sharp: replay is vector.
    canvas.DrawPicture(logo, Matrix3x2.CreateTranslation(10, 10));
    canvas.DrawPicture(logo, Matrix3x2.CreateScale(4) * Matrix3x2.CreateTranslation(60, 10));

    // A paint with an opacity merges the whole picture as one layer.
    canvas.DrawPicture(logo, Matrix3x2.CreateTranslation(240, 10), new SolidPaint(Rgba8.White) { Opacity = 0.4f });

    // Drawn many times at one scale, a cached image is cheaper than replaying.
    for (int i = 0; i < 6; i++)
    {
        canvas.DrawPicture(logo, Matrix3x2.CreateTranslation(230 + (i * 25), 150), cacheAsImage: true);
    }
}

using Raster<Rgba8> icon = logo.ToRaster<Rgba8>(scale: 2);

Properties

Bounds

Gets the picture's bounds in its own coordinates, as the recorder was given them.

public RectF Bounds { get; }

Property Value

RectF

CallCount

Gets the number of recorded calls, saves and transforms included; nested pictures count as one.

public int CallCount { get; }

Property Value

int

Methods

Load(Stream, EgamiContext?)

Reads an Egami picture file. The file is untrusted input: its images go through the PNG reader with the context's SafetyLimits, what it decodes counts toward MaxTotalBytes, and pictures may nest 64 deep and replay at most 50 million calls with nested pictures expanded.

public static Picture Load(Stream stream, EgamiContext? context = null)

Parameters

stream Stream

The stream to read from, to its end.

context EgamiContext

The context whose limits apply, or null for the default.

Returns

Picture

The picture.

Examples

// Keep a picture in Egami's own documented format, and load it later; a loaded file is untrusted, so the context's
// safety limits apply.
using (FileStream file = File.Create("logo.egdr"))
{
    logo.Save(file);
}

Picture loaded;
using (FileStream file = File.OpenRead("logo.egdr"))
{
    loaded = Picture.Load(file);
}

Exceptions

UnsupportedFormatException

The data is not an .egdr file, or is a major version this reader does not know.

CorruptDataException

The file is damaged.

LimitExceededException

The file is larger, or nests deeper, than the limits allow.

Playback(Canvas)

Replays the calls on a canvas through its public calls, between a Save() and a Restore(); recorded transforms apply after the canvas's transform at the start.

public void Playback(Canvas canvas)

Parameters

canvas Canvas

The canvas.

Examples

// Record any picture code once...
PictureRecorder recorder = new(new RectF(0, 0, 40, 40));
DrawLogo(recorder);
Picture logo = recorder.Finish();

using (RasterCanvas<Rgba8> canvas = raster.CreateCanvas())
{
    // ...and replay it anywhere, at any scale, still sharp: replay is vector.
    canvas.DrawPicture(logo, Matrix3x2.CreateTranslation(10, 10));
    canvas.DrawPicture(logo, Matrix3x2.CreateScale(4) * Matrix3x2.CreateTranslation(60, 10));

    // A paint with an opacity merges the whole picture as one layer.
    canvas.DrawPicture(logo, Matrix3x2.CreateTranslation(240, 10), new SolidPaint(Rgba8.White) { Opacity = 0.4f });

    // Drawn many times at one scale, a cached image is cheaper than replaying.
    for (int i = 0; i < 6; i++)
    {
        canvas.DrawPicture(logo, Matrix3x2.CreateTranslation(230 + (i * 25), 150), cacheAsImage: true);
    }
}

using Raster<Rgba8> icon = logo.ToRaster<Rgba8>(scale: 2);

Save(Stream)

Writes the picture as an Egami picture file (.egdr), Egami's own documented binary format: a signature and version, then chunks that a reader skips when it does not know them. Images are kept as PNG (so a float image is kept at 16 bits), paths and numbers as 32-bit floats, nested pictures once however often they are used, and text as its outlines, since a file holds no fonts. A picture that loads back replays the same calls and draws the same pixels, except that text comes back as outline fills, which the raster backend draws as paths rather than from its glyph cache.

public void Save(Stream stream)

Parameters

stream Stream

The stream to write to.

Examples

// Keep a picture in Egami's own documented format, and load it later; a loaded file is untrusted, so the context's
// safety limits apply.
using (FileStream file = File.Create("logo.egdr"))
{
    logo.Save(file);
}

Picture loaded;
using (FileStream file = File.OpenRead("logo.egdr"))
{
    loaded = Picture.Load(file);
}

Exceptions

NotSupportedException

A call holds C# code a file cannot: a CustomPaint, or a layer's filter, backdrop or soft mask. The message gives the call's index. Nothing is written in that case.

ToRaster<TPixel>(float, CanvasOptions?, EgamiContext?)

Renders the picture's Bounds into a new raster, over transparency.

public Raster<TPixel> ToRaster<TPixel>(float scale = 1, CanvasOptions? options = null, EgamiContext? context = null) where TPixel : unmanaged, IPixelType<TPixel>

Parameters

scale float

Raster pixels per picture unit.

options CanvasOptions

The canvas settings, or null for the defaults.

context EgamiContext

The context the raster is made in, or null for the default.

Returns

Raster<TPixel>

A raster of the bounds' size times the scale, rounded up.

Type Parameters

TPixel

The raster's pixel type.

Examples

// Record any picture code once...
PictureRecorder recorder = new(new RectF(0, 0, 40, 40));
DrawLogo(recorder);
Picture logo = recorder.Finish();

using (RasterCanvas<Rgba8> canvas = raster.CreateCanvas())
{
    // ...and replay it anywhere, at any scale, still sharp: replay is vector.
    canvas.DrawPicture(logo, Matrix3x2.CreateTranslation(10, 10));
    canvas.DrawPicture(logo, Matrix3x2.CreateScale(4) * Matrix3x2.CreateTranslation(60, 10));

    // A paint with an opacity merges the whole picture as one layer.
    canvas.DrawPicture(logo, Matrix3x2.CreateTranslation(240, 10), new SolidPaint(Rgba8.White) { Opacity = 0.4f });

    // Drawn many times at one scale, a cached image is cheaper than replaying.
    for (int i = 0; i < 6; i++)
    {
        canvas.DrawPicture(logo, Matrix3x2.CreateTranslation(230 + (i * 25), 150), cacheAsImage: true);
    }
}

using Raster<Rgba8> icon = logo.ToRaster<Rgba8>(scale: 2);

Exceptions

ArgumentOutOfRangeException

scale is not positive and finite, or the raster would be too large.