Table of Contents

Class InkCanvas

Namespace
Egami.Drawing
Assembly
Egami.Drawing.dll

A canvas that draws into an InkRaster's plates: printing inks instead of RGB. It shares RasterCanvas<TPixel>'s rasterizer, clips and layers, so shapes, strokes and text cover exactly the same pixels; where the RGB canvas blends colours, this one sets each plate. A paint's Ink prints its inks by name (inks the raster lacks through their appearance into the process inks); RGB colours, gradients and images convert into the process inks through RgbSource and the ink set's profile.

public sealed class InkCanvas : Canvas, IDisposable
Inheritance
InkCanvas
Implements
Inherited Members
Extension Methods

Examples

// Plates for CMYK and a spot; an RGB photo converts through FOGRA39, the logo prints on its own plate.
Egami.Colors.InkSet inks = Egami.Colors.InkSet.FromProfile(Egami.Colors.ColorProfile.Fogra39)
    .WithSpot("PANTONE 186 C", Egami.Colors.InkAppearance.Cmyk(0, 1, 0.81f, 0.04f));
using InkRaster page = new(inks, 300, 200);
using (InkCanvas canvas = new(page))
{
    canvas.Clear(Egami.Colors.InkColor.None);                                   // paper
    canvas.FillRect(new RectF(0, 0, 300, 120), new Rgba8(80, 140, 200, 255));   // RGB: converted to CMYK
    canvas.FillCircle(new Vector2(150, 100), 40, new SolidPaint(Egami.Colors.InkColor.Spot(inks[4], 1)));
}

Remarks

Every blend mode works as ISO 32000-2 has it for inks: on complements per plate, the non-separable ones on Cyan, Magenta and Yellow with Black from the backdrop, spots Normal where PDF allows only separable modes. Overprint picks the plates a paint prints. Layers are isolated or not, knockout or not, take soft masks, and with ColorSpace blend their process part in RGB or gray, converted into the inks as they merge. Layer filters run on RGB pixels and are not taken here.

Constructors

InkCanvas(InkRaster, InkCanvasOptions?)

Initializes a new instance of the InkCanvas class over an ink raster.

public InkCanvas(InkRaster raster, InkCanvasOptions? options = null)

Parameters

raster InkRaster

The plates to draw on.

options InkCanvasOptions

How other colours become inks, or null for Default.

Examples

// Plates for CMYK and a spot; an RGB photo converts through FOGRA39, the logo prints on its own plate.
Egami.Colors.InkSet inks = Egami.Colors.InkSet.FromProfile(Egami.Colors.ColorProfile.Fogra39)
    .WithSpot("PANTONE 186 C", Egami.Colors.InkAppearance.Cmyk(0, 1, 0.81f, 0.04f));
using InkRaster page = new(inks, 300, 200);
using (InkCanvas canvas = new(page))
{
    canvas.Clear(Egami.Colors.InkColor.None);                                   // paper
    canvas.FillRect(new RectF(0, 0, 300, 120), new Rgba8(80, 140, 200, 255));   // RGB: converted to CMYK
    canvas.FillCircle(new Vector2(150, 100), 40, new SolidPaint(Egami.Colors.InkColor.Spot(inks[4], 1)));
}

Exceptions

ArgumentNullException

raster is null.

Properties

Options

Gets the options.

public InkCanvasOptions Options { get; }

Property Value

InkCanvasOptions

Raster

Gets the plates drawn on.

public InkRaster Raster { get; }

Property Value

InkRaster

Methods

Clear(InkColor)

Fills the clip with an ink colour, replacing what is there: None leaves blank paper.

public void Clear(InkColor ink)

Parameters

ink InkColor

The colour.

Exceptions

ArgumentNullException

ink is null.

Dispose(bool)

Releases the backend's resources; called once by Dispose() after every state is restored.

protected override void Dispose(bool disposing)

Parameters

disposing bool

Always true: the canvas has no finalizer.

DrawImage(InkRaster, RectF, RectF, SamplingOptions?, Paint?)

Draws part of another ink raster's plates into a rectangle; see DrawImage(InkRaster, RectF, SamplingOptions?, Paint?).

public void DrawImage(InkRaster image, RectF source, RectF destination, SamplingOptions? sampling = null, Paint? paint = null)

Parameters

image InkRaster

The plates to draw.

source RectF

The part of the image, in its pixels.

destination RectF

Where it goes, in local coordinates.

sampling SamplingOptions

How the plates are sampled, or null for bilinear.

paint Paint

The blend mode, opacity, overprint and antialiasing, or null.

Examples

// Plates for CMYK and a spot; an RGB photo converts through FOGRA39, the logo prints on its own plate.
Egami.Colors.InkSet inks = Egami.Colors.InkSet.FromProfile(Egami.Colors.ColorProfile.Fogra39)
    .WithSpot("PANTONE 186 C", Egami.Colors.InkAppearance.Cmyk(0, 1, 0.81f, 0.04f));
using InkRaster page = new(inks, 300, 200);
using (InkCanvas canvas = new(page))
{
    canvas.Clear(Egami.Colors.InkColor.None);                                   // paper
    canvas.FillRect(new RectF(0, 0, 300, 120), new Rgba8(80, 140, 200, 255));   // RGB: converted to CMYK
    canvas.FillCircle(new Vector2(150, 100), 40, new SolidPaint(Egami.Colors.InkColor.Spot(inks[4], 1)));
}

Exceptions

ArgumentNullException

image is null.

DrawImage(InkRaster, RectF, SamplingOptions?, Paint?)

Draws another ink raster's plates into a rectangle, mapped by name: a plate this canvas also has prints on it, any other through its colorant's appearance into the process inks. Sampled nearest or bilinear (any other filter samples bilinear), with the image's edges clamped; the paint gives the blend mode, opacity, overprint and antialiasing.

public void DrawImage(InkRaster image, RectF destination, SamplingOptions? sampling = null, Paint? paint = null)

Parameters

image InkRaster

The plates to draw.

destination RectF

Where they go, in local coordinates.

sampling SamplingOptions

How the plates are sampled, or null for bilinear.

paint Paint

The blend mode, opacity, overprint and antialiasing, or null.

Examples

// Plates for CMYK and a spot; an RGB photo converts through FOGRA39, the logo prints on its own plate.
Egami.Colors.InkSet inks = Egami.Colors.InkSet.FromProfile(Egami.Colors.ColorProfile.Fogra39)
    .WithSpot("PANTONE 186 C", Egami.Colors.InkAppearance.Cmyk(0, 1, 0.81f, 0.04f));
using InkRaster page = new(inks, 300, 200);
using (InkCanvas canvas = new(page))
{
    canvas.Clear(Egami.Colors.InkColor.None);                                   // paper
    canvas.FillRect(new RectF(0, 0, 300, 120), new Rgba8(80, 140, 200, 255));   // RGB: converted to CMYK
    canvas.FillCircle(new Vector2(150, 100), 40, new SolidPaint(Egami.Colors.InkColor.Spot(inks[4], 1)));
}

Exceptions

ArgumentNullException

image is null.

OnClear(RgbaF32)

Replaces everything inside the clip with a color. A PDF page can write it as a filled rectangle over a white or transparent page; by default it is OnDrawPaint(Paint) with a solid paint in Source.

protected override void OnClear(RgbaF32 color)

Parameters

color RgbaF32

The color.

OnClipPath(Path, ClipOp, bool)

Clips by a path's fill.

protected override void OnClipPath(Path path, ClipOp op, bool antialias)

Parameters

path Path

The path in local coordinates.

op ClipOp

Whether to keep the inside or the outside.

antialias bool

Whether the edges are antialiased.

OnClipRect(RectF, ClipOp, bool)

Clips by a rectangle. By default it goes to OnClipPath(Path, ClipOp, bool).

protected override void OnClipRect(RectF rect, ClipOp op, bool antialias)

Parameters

rect RectF

The rectangle in local coordinates, under Matrix.

op ClipOp

Whether to keep the inside or the outside.

antialias bool

Whether the edges are antialiased.

OnClipRoundRect(RoundRect, ClipOp, bool)

Clips by a round rectangle. By default it goes to OnClipPath(Path, ClipOp, bool).

protected override void OnClipRoundRect(RoundRect roundRect, ClipOp op, bool antialias)

Parameters

roundRect RoundRect

The round rectangle in local coordinates.

op ClipOp

Whether to keep the inside or the outside.

antialias bool

Whether the edges are antialiased.

OnDrawAtlas(Raster, ReadOnlySpan<SpriteTransform>, ReadOnlySpan<RectF>, ReadOnlySpan<RgbaF32>, BlendMode, SamplingOptions, Paint?)

Draws sprites from one image; the lists have equal lengths and colors is empty or as long. By default a sprite without a color is a DrawImage(Raster, RectF, RectF, SamplingOptions?, Paint?) under its transform, and a tinted one a two-triangle textured OnDrawMesh(Mesh, Paint, BlendMode, ReadOnlySpan<GradientStop>) call, one sprite at a time.

protected override void OnDrawAtlas(Raster image, ReadOnlySpan<SpriteTransform> transforms, ReadOnlySpan<RectF> sources, ReadOnlySpan<RgbaF32> colors, BlendMode colorBlend, SamplingOptions sampling, Paint? paint)

Parameters

image Raster

The sprite sheet.

transforms ReadOnlySpan<SpriteTransform>

Where each sprite goes.

sources ReadOnlySpan<RectF>

Each sprite's part of the image.

colors ReadOnlySpan<RgbaF32>

One color per sprite, or empty.

colorBlend BlendMode

How a sprite combines with its color.

sampling SamplingOptions

How the image is sampled.

paint Paint

The blend mode, opacity and antialiasing, or null.

Examples

// Pack the sprites once, then draw thousands of them in one call: each with its own place, turn, scale and tint.
using SpriteSheet sheet = SpriteSheet.Pack([spark, star], new SpritePackOptions { Padding = 1 });
Random random = new(7);
SpriteTransform[] transforms = new SpriteTransform[2000];
RectF[] sources = new RectF[transforms.Length];
RgbaF32[] tints = new RgbaF32[transforms.Length];
for (int i = 0; i < transforms.Length; i++)
{
    Rect frame = sheet.Frames[i % 2];
    sources[i] = frame;
    transforms[i] = SpriteTransform.FromRotationScale(
        scale: 0.3f + random.NextSingle(),
        degrees: random.NextSingle() * 360,
        position: new Vector2(random.NextSingle() * 320, random.NextSingle() * 200),
        anchor: new Vector2(frame.Width / 2f, frame.Height / 2f));
    tints[i] = new RgbaF32(1, 0.5f + (random.NextSingle() / 2), random.NextSingle(), 0.8f);
}

using (RasterCanvas<Rgba8> canvas = raster.CreateCanvas())
{
    // Plus adds the sprites up, as light does.
    canvas.DrawAtlas(sheet.Image, transforms, sources, tints, BlendMode.Modulate, paint: new SolidPaint(Rgba8.White) { BlendMode = BlendMode.Plus });
}

OnDrawGlyphRun(GlyphRun, Vector2, Paint)

Draws a glyph run. The default turns the glyphs into one path, with the font's synthetic bold and italic, and calls OnDrawPath(Path, Stroke?, Paint), aliased for Alias, so every backend draws text; a backend with text of its own, such as a PDF page writing text operators, overrides it and can use Text and Clusters to keep the text searchable.

protected override void OnDrawGlyphRun(GlyphRun run, Vector2 origin, Paint paint)

Parameters

run GlyphRun

The glyphs, never empty.

origin Vector2

Where the run's origin goes, in local coordinates.

paint Paint

The paint.

OnDrawImage(Raster, RectF, RectF, SamplingOptions, Paint?)

Draws part of an image into a local rectangle.

protected override void OnDrawImage(Raster image, RectF source, RectF destination, SamplingOptions sampling, Paint? paint)

Parameters

image Raster

The image.

source RectF

The part to draw, inside the image.

destination RectF

Where it goes, in local coordinates.

sampling SamplingOptions

How it is sampled.

paint Paint

The blend mode, opacity and antialiasing, or null for source-over.

Examples

// A backend overrides hooks; the base class has already checked the arguments and tracks the transform and clip bounds.
// This one writes SVG elements, keeping rectangles as rectangles and strokes as stroke settings.
public sealed class SvgCanvas(float width, float height) : Canvas(width, height)
{
    private readonly System.Text.StringBuilder _body = new();

    public string Document => $"<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{Width}\" height=\"{Height}\">{_body}</svg>";

    protected override void OnDrawRect(RectF rect, Stroke? stroke, Paint paint)
        => _body.Append(FormattableString.Invariant($"<rect x=\"{rect.X}\" y=\"{rect.Y}\" width=\"{rect.Width}\" height=\"{rect.Height}\" {Style(stroke, paint)}/>"));

    protected override void OnDrawPath(Path path, Stroke? stroke, Paint paint)
    {
        System.Text.StringBuilder d = new();
        int p = 0;
        foreach (PathVerb verb in path.Verbs)
        {
            int count = verb switch { PathVerb.Quad => 2, PathVerb.Cubic => 3, PathVerb.Close => 0, _ => 1 };
            d.Append(verb switch { PathVerb.Move => "M", PathVerb.Line => "L", PathVerb.Quad => "Q", PathVerb.Cubic => "C", _ => "Z" });
            foreach (Vector2 point in path.Points.Slice(p, count))
            {
                d.Append(FormattableString.Invariant($"{point.X},{point.Y} "));
            }

            p += count;
        }

        _body.Append(System.Globalization.CultureInfo.InvariantCulture, $"<path d=\"{d}\" {Style(stroke, paint)}/>");
    }

    protected override void OnClipPath(Path path, ClipOp op, bool antialias)
    {
        // Clips would become <clipPath> elements; this sketch leaves them out.
    }

    protected override void OnDrawImage(Raster image, RectF source, RectF destination, SamplingOptions sampling, Paint? paint)
    {
        // An image would be embedded as a PNG data URI.
    }

    private string Style(Stroke? stroke, Paint paint)
    {
        Rgba8 c = paint is SolidPaint solid ? solid.Color.ToRgba8() : Rgba8.Black;
        string color = $"#{c.R:x2}{c.G:x2}{c.B:x2}";
        string transform = Matrix.IsIdentity ? "" : FormattableString.Invariant($" transform=\"matrix({Matrix.M11} {Matrix.M12} {Matrix.M21} {Matrix.M22} {Matrix.M31} {Matrix.M32})\"");
        return stroke is null
            ? $"fill=\"{color}\"{transform}"
            : FormattableString.Invariant($"fill=\"none\" stroke=\"{color}\" stroke-width=\"{stroke.Width}\" stroke-linecap=\"{stroke.Cap.ToString().ToLowerInvariant()}\"{transform}");
    }
}

OnDrawImageLattice(Raster, Lattice, RectF, SamplingOptions, Paint?)

Draws an image through a lattice; the lattice fits the image. By default each cell is an OnDrawImage(Raster, RectF, RectF, SamplingOptions, Paint?) call of its part of the image, sampled only inside that part, and each fixed-color cell an OnDrawRect(RectF, Stroke?, Paint) call; a PDF backend gets one image per cell this way. The raster backend draws the cells as one mesh instead, so they meet without seams.

protected override void OnDrawImageLattice(Raster image, Lattice lattice, RectF destination, SamplingOptions sampling, Paint? paint)

Parameters

image Raster

The image.

lattice Lattice

The grid.

destination RectF

Where the image goes, in local coordinates; not empty.

sampling SamplingOptions

How the image is sampled.

paint Paint

The blend mode, opacity and antialiasing, or null.

Examples

using (RasterCanvas<Rgba8> canvas = raster.CreateCanvas())
{
    // An Android .9.png: its one-pixel border marks what stretches. The corners stay sharp at any size.
    (Raster inner, Lattice lattice, Rect? padding) = NinePatch.FromAndroid(file);
    canvas.DrawImageLattice(inner, lattice, new RectF(10, 10, 180, 48));
    inner.Dispose();

    // A nine-patch from a plain image: the 8-pixel corners keep their size, the edges and center stretch.
    canvas.DrawImageNinePatch(frame, new Rect(8, 8, 8, 8), new RectF(10, 70, 120, 40));

    // A lattice can leave cells out or fill them with a color: here the center becomes a flat panel.
    LatticeCellKind[] kinds = [.. Enumerable.Repeat(LatticeCellKind.Default, 9)];
    kinds[4] = LatticeCellKind.FixedColor;
    RgbaF32[] colors = new RgbaF32[9];
    colors[4] = new RgbaF32(0.95f, 0.95f, 0.9f, 1);
    canvas.DrawImageLattice(frame, new Lattice([8, 16], [8, 16]) { CellKinds = kinds, CellColors = colors }, new RectF(150, 70, 140, 40));
}

OnDrawImageMask(Raster, RectF, Paint, SamplingOptions)

Paints through an image mask. A PDF backend writes it as a stencil mask. By default the paint fills the rectangle in a layer and the mask's coverage, as an image drawn with DestinationIn, cuts it out.

protected override void OnDrawImageMask(Raster mask, RectF destination, Paint paint, SamplingOptions sampling)

Parameters

mask Raster

The mask image.

destination RectF

Where the mask goes, in local coordinates; not empty.

paint Paint

What fills the covered pixels.

sampling SamplingOptions

How the mask is sampled.

Examples

// A 1-bit scan as a stencil: its white pixels paint in ink, its black ones leave the page as it is.
using Raster<Gray8> scan = Raster.Create(32, 16, new Gray8(255));
canvas.DrawImageMask(scan, new RectF(160, 20, 64, 32), new Rgba8(20, 40, 120, 255), new SamplingOptions { Filter = ResampleFilter.Nearest });

// A knockout group: each shape composites with what the group started as, so overlapping translucent shapes do not darken.
canvas.SaveLayer(new LayerOptions { Knockout = true });
canvas.FillCircle(new Vector2(180, 100), 20, new RgbaF32(1, 0, 0, 0.5f));
canvas.FillCircle(new Vector2(200, 100), 20, new RgbaF32(0, 0, 1, 0.5f));
canvas.Restore();

OnDrawMesh(Mesh, Paint, BlendMode, ReadOnlySpan<GradientStop>)

Draws a mesh with at least one triangle; a mesh with values comes with stops. A PDF backend can write it as a free-form or lattice-form shading (types 4 and 5). By default it is drawn through RasterizeFallback(Action<Canvas>, float) at one raster pixel per device unit, over the mesh's bounds, and placed with OnDrawImage(Raster, RectF, RectF, SamplingOptions, Paint?).

protected override void OnDrawMesh(Mesh mesh, Paint paint, BlendMode colorBlend, ReadOnlySpan<GradientStop> valueStops)

Parameters

mesh Mesh

The mesh, in local coordinates.

paint Paint

The paint.

colorBlend BlendMode

How the vertex colors combine with the paint.

valueStops ReadOnlySpan<GradientStop>

The stops vertex values map through, or empty.

OnDrawOval(RectF, Stroke?, Paint)

Fills or strokes an ellipse. By default it goes to OnDrawPath(Path, Stroke?, Paint).

protected override void OnDrawOval(RectF oval, Stroke? stroke, Paint paint)

Parameters

oval RectF

The rectangle the ellipse fills.

stroke Stroke

The pen, or null to fill.

paint Paint

The paint.

OnDrawPaint(Paint)

Fills the clip. By default it fills the device clip bounds mapped back to local coordinates.

protected override void OnDrawPaint(Paint paint)

Parameters

paint Paint

The paint.

OnDrawPatch(Patch, Paint, BlendMode, ReadOnlySpan<GradientStop>)

Draws a patch. A PDF backend can write it as a Coons or tensor-product shading (types 6 and 7). By default it is cut into triangles under the current transform and drawn with OnDrawMesh(Mesh, Paint, BlendMode, ReadOnlySpan<GradientStop>).

protected override void OnDrawPatch(Patch patch, Paint paint, BlendMode colorBlend, ReadOnlySpan<GradientStop> valueStops)

Parameters

patch Patch

The patch, in local coordinates.

paint Paint

The paint.

colorBlend BlendMode

How the corner colors combine with the paint.

valueStops ReadOnlySpan<GradientStop>

The stops corner values map through, or empty.

OnDrawPath(Path, Stroke?, Paint)

Fills a path by its fill rule, or strokes it.

protected override void OnDrawPath(Path path, Stroke? stroke, Paint paint)

Parameters

path Path

The path in local coordinates.

stroke Stroke

The pen, or null to fill.

paint Paint

The paint.

Examples

// A backend overrides hooks; the base class has already checked the arguments and tracks the transform and clip bounds.
// This one writes SVG elements, keeping rectangles as rectangles and strokes as stroke settings.
public sealed class SvgCanvas(float width, float height) : Canvas(width, height)
{
    private readonly System.Text.StringBuilder _body = new();

    public string Document => $"<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{Width}\" height=\"{Height}\">{_body}</svg>";

    protected override void OnDrawRect(RectF rect, Stroke? stroke, Paint paint)
        => _body.Append(FormattableString.Invariant($"<rect x=\"{rect.X}\" y=\"{rect.Y}\" width=\"{rect.Width}\" height=\"{rect.Height}\" {Style(stroke, paint)}/>"));

    protected override void OnDrawPath(Path path, Stroke? stroke, Paint paint)
    {
        System.Text.StringBuilder d = new();
        int p = 0;
        foreach (PathVerb verb in path.Verbs)
        {
            int count = verb switch { PathVerb.Quad => 2, PathVerb.Cubic => 3, PathVerb.Close => 0, _ => 1 };
            d.Append(verb switch { PathVerb.Move => "M", PathVerb.Line => "L", PathVerb.Quad => "Q", PathVerb.Cubic => "C", _ => "Z" });
            foreach (Vector2 point in path.Points.Slice(p, count))
            {
                d.Append(FormattableString.Invariant($"{point.X},{point.Y} "));
            }

            p += count;
        }

        _body.Append(System.Globalization.CultureInfo.InvariantCulture, $"<path d=\"{d}\" {Style(stroke, paint)}/>");
    }

    protected override void OnClipPath(Path path, ClipOp op, bool antialias)
    {
        // Clips would become <clipPath> elements; this sketch leaves them out.
    }

    protected override void OnDrawImage(Raster image, RectF source, RectF destination, SamplingOptions sampling, Paint? paint)
    {
        // An image would be embedded as a PNG data URI.
    }

    private string Style(Stroke? stroke, Paint paint)
    {
        Rgba8 c = paint is SolidPaint solid ? solid.Color.ToRgba8() : Rgba8.Black;
        string color = $"#{c.R:x2}{c.G:x2}{c.B:x2}";
        string transform = Matrix.IsIdentity ? "" : FormattableString.Invariant($" transform=\"matrix({Matrix.M11} {Matrix.M12} {Matrix.M21} {Matrix.M22} {Matrix.M31} {Matrix.M32})\"");
        return stroke is null
            ? $"fill=\"{color}\"{transform}"
            : FormattableString.Invariant($"fill=\"none\" stroke=\"{color}\" stroke-width=\"{stroke.Width}\" stroke-linecap=\"{stroke.Cap.ToString().ToLowerInvariant()}\"{transform}");
    }
}

OnDrawPoints(PointMode, ReadOnlySpan<Vector2>, Stroke, Paint)

Draws points, lines or a polyline. By default each dot or line goes to OnDrawPath(Path, Stroke?, Paint), the polyline as one path.

protected override void OnDrawPoints(PointMode mode, ReadOnlySpan<Vector2> points, Stroke stroke, Paint paint)

Parameters

mode PointMode

How the points are read.

points ReadOnlySpan<Vector2>

The points.

stroke Stroke

The pen.

paint Paint

The paint.

OnDrawRect(RectF, Stroke?, Paint)

Fills or strokes a rectangle. By default it goes to OnDrawPath(Path, Stroke?, Paint).

protected override void OnDrawRect(RectF rect, Stroke? stroke, Paint paint)

Parameters

rect RectF

The rectangle.

stroke Stroke

The pen, or null to fill.

paint Paint

The paint.

OnDrawRoundRect(RoundRect, Stroke?, Paint)

Fills or strokes a round rectangle. By default it goes to OnDrawPath(Path, Stroke?, Paint).

protected override void OnDrawRoundRect(RoundRect roundRect, Stroke? stroke, Paint paint)

Parameters

roundRect RoundRect

The round rectangle.

stroke Stroke

The pen, or null to fill.

paint Paint

The paint.

OnRestore()

Called by Restore() after the state is popped; Matrix and DeviceClipBounds are already the restored ones.

protected override void OnRestore()

OnRestoreLayer()

Called by Restore() for a state saved by SaveLayer(LayerOptions?), after it is popped, so Matrix and DeviceClipBounds are those the layer was started with: the raster backend merges the layer through that clip. By default it calls OnRestore().

protected override void OnRestoreLayer()

OnSave()

Called by Save() after the state is pushed.

protected override void OnSave()

OnSaveLayer(LayerOptions)

Called by SaveLayer(LayerOptions?) after the state is pushed; DeviceClipBounds is already limited by the options' bounds. The raster backend allocates the layer; a PDF backend would begin a transparency group. By default it calls OnSave(), so a backend without layers draws their contents directly.

protected override void OnSaveLayer(LayerOptions options)

Parameters

options LayerOptions

How the layer is merged.

Examples

using (RasterCanvas<Rgba8> canvas = raster.CreateCanvas())
{
    // Group opacity: the overlapping circles fade as one shape, with no darker overlap.
    canvas.SaveLayer(0.5f);
    canvas.FillCircle(new Vector2(80, 100), 60, new Rgba8(200, 30, 30, 255));
    canvas.FillCircle(new Vector2(140, 100), 60, new Rgba8(200, 30, 30, 255));
    canvas.Restore();

    // A group blended with Multiply and blurred as a whole; the layer grows by the blur's reach, so nothing is cut.
    canvas.SaveLayer(new LayerOptions { BlendMode = BlendMode.Multiply, Filter = ops => ops.Blur(4) });
    canvas.FillRect(new RectF(180, 40, 90, 120), new Rgba8(40, 120, 220, 255));
    canvas.FillCircle(new Vector2(225, 100), 30, Rgba8.White);
    canvas.Restore();
}

OnSetMatrix(Matrix3x2)

Called whenever the transform changes, with the new transform (also Matrix).

protected override void OnSetMatrix(Matrix3x2 matrix)

Parameters

matrix Matrix3x2

The transform from local to device coordinates.