Table of Contents

Class CanvasOptions

Namespace
Egami.Drawing
Assembly
Egami.Drawing.dll

Settings for a raster canvas.

public sealed record CanvasOptions : IEquatable<CanvasOptions>
Inheritance
CanvasOptions
Implements
Inherited Members

Examples

// A canvas draws onto a raster. Calls draw when they are made; dispose the canvas when done.
using Raster<Rgba8> raster = Raster.Create(400, 300, Rgba8.White);
using (RasterCanvas<Rgba8> canvas = raster.CreateCanvas())
{
    canvas.FillRect(new RectF(20, 20, 160, 100), new Rgba8(30, 90, 200, 255));

    // Save and Restore bracket transform and clip changes.
    canvas.Save();
    canvas.Translate(260, 150);
    canvas.Rotate(30);
    canvas.FillRect(new RectF(-50, -30, 100, 60), new Rgba8(200, 60, 30, 200));
    canvas.Restore();

    canvas.FillCircle(new Vector2(100, 220), 50, Rgba8.Black);
}

// Blend edges in linear light, which keeps thin lines and translucent colors from darkening.
using RasterCanvas<Rgba8> linear = raster.CreateCanvas(new CanvasOptions { ColorSpace = ColorSpace.Linear });

Properties

Antialiasing

Gets how shapes are antialiased. Defaults to Exact; GdiPlus samples as GDI+ does and draws its thin pens. Text and images are not affected, nor are paints with Antialias off.

public Antialiasing Antialiasing { get; init; }

Property Value

Antialiasing

Examples

// Shapes as System.Drawing draws them at its defaults (SmoothingMode.AntiAlias, PixelOffsetMode.None): whole coordinates on
// pixel centres, GDI+'s 8 by 4 samples a pixel, and pens up to 1.5 px drawn as its one-pixel thin lines.
// CanvasOptions.GdiPlus sets both too.
var options = new CanvasOptions { PixelOffset = PixelOffset.None, Antialiasing = Antialiasing.GdiPlus };
using (RasterCanvas<Rgb8> canvas = raster.CreateCanvas(options))
{
    canvas.FillOval(new RectF(4, 4, 20, 20), new SolidPaint(new Rgba8(0, 90, 200, 255)));
    canvas.DrawLine(new Vector2(30, 6), new Vector2(60, 26), new Stroke(0.5f), new SolidPaint(Rgba8.Black));
}

Exceptions

ArgumentOutOfRangeException

The value is not defined.

ClearTypeLevel

Gets how much of the LCD mask Lcd text uses, from 0 (the grayscale coverage of the same samples, no colour fringes) to 1 (the default, the filtered subpixel coverage), as Windows' ClearType level. Values between mix the two, trading fringes for sharpness.

public float ClearTypeLevel { get; init; }

Property Value

float

Examples

// GDI+ ClearType's look: its filter (an even spread over three subpixels), no smoothing along the stripes, and the text
// gamma that then matched GDI+'s weight for a white Trebuchet MS label at 16 pixels.
var gdiPlus = new CanvasOptions
{
    PixelGeometry = PixelGeometry.RgbHorizontal,
    LcdFilter = LcdFilter.Gdi,
    LcdSampling = LcdSampling.Gdi,
    TextGamma = 1.25f,
};
var label = new Font(face, 16) { Hinting = FontHinting.Light, Edging = FontEdging.Lcd, Subpixel = false };
using (RasterCanvas<Rgb8> canvas = button.CreateCanvas(gdiPlus))
{
    canvas.DrawText("Start the course", new Vector2(40, 26), label, Rgba8.White);
}

// Softer fringes: a balanced filter and half the colour of full ClearType.
var soft = new CanvasOptions { PixelGeometry = PixelGeometry.RgbHorizontal, LcdFilter = LcdFilter.Balanced, ClearTypeLevel = 0.5f };

// Or weights of your own, normalised to add up to 256.
var custom = new CanvasOptions { PixelGeometry = PixelGeometry.RgbHorizontal, LcdFilter = LcdFilter.Custom(1, 3, 4, 3, 1) };

Exceptions

ArgumentOutOfRangeException

The value is not between 0 and 1.

ColorGlyphRenderer

Gets a renderer for color glyphs of formats Egami.Drawing does not draw itself (OpenType SVG with Egami.Svg's SvgGlyphRenderer.Instance), or null (the default) to draw those glyphs as outlines.

public IColorGlyphRenderer? ColorGlyphRenderer { get; init; }

Property Value

IColorGlyphRenderer

Examples

// Emoji and other glyphs from an OpenType SVG table draw in color on a canvas given the renderer.
using (RasterCanvas<Rgba8> canvas = raster.CreateCanvas(new CanvasOptions { ColorGlyphRenderer = SvgGlyphRenderer.Instance }))
{
    canvas.DrawText("Hello", new System.Numerics.Vector2(10, 35), font, new SolidPaint(new RgbaF32(0, 0, 0, 1)));
}

ColorSpace

Gets where edges and paints blend. Encoded (the default) blends the stored values, as Egami's other operations and SkiaSharp do; Linear blends in linear light, which keeps antialiased edges and translucent colors from darkening.

public ColorSpace ColorSpace { get; init; }

Property Value

ColorSpace

Exceptions

ArgumentOutOfRangeException

The value is not defined.

Default

Gets the options every canvas starts with.

public static CanvasOptions Default { get; }

Property Value

CanvasOptions

GdiPlus

Gets System.Drawing's canvas settings, as measured on Windows in 2.7.5 and 2.7.6. For ClearTypeGridFit text: an RGB stripe, Gdi, Gdi (coverage in sixths, blended through GDI+'s 8-bit gamma tables) and a TextGamma of 1.4 (GDI+'s 1 + Graphics.TextContrast / 10 at its default contrast of 4). For shapes and images: GDI+'s default None and its GdiPlus. Use it with fonts from WithGdiPlusLook(); the resample filter is each draw call's, such as ResampleFilter.GdiPlusBilinear.

public static CanvasOptions GdiPlus { get; }

Property Value

CanvasOptions

Examples

// Text that looks like System.Drawing's Graphics.DrawString with TextRenderingHint.ClearTypeGridFit: the font's own hints
// and grid-fitted advances, GDI+'s ClearType filter, sampling and gamma.
Font label = new Font(face, 16).WithGdiPlusLook();
using (RasterCanvas<Rgb8> canvas = button.CreateCanvas(CanvasOptions.GdiPlus))
{
    float width = label.MeasureText("Start the course").Advance;
    canvas.DrawText("Start the course", new Vector2(MathF.Round((200 - width) / 2), 26), label, Rgba8.White);
}

// A different Graphics.TextContrast (0 to 12) is a different text gamma: 1 + contrast / 10.
CanvasOptions contrast8 = CanvasOptions.GdiPlus with { TextGamma = 1 + (8 / 10f) };

LcdFilter

Gets the filter Lcd text spreads subpixel coverage with. Defaults to Default; Gdi is the one measured from GDI and GDI+.

public LcdFilter LcdFilter { get; init; }

Property Value

LcdFilter

Examples

// GDI+ ClearType's look: its filter (an even spread over three subpixels), no smoothing along the stripes, and the text
// gamma that then matched GDI+'s weight for a white Trebuchet MS label at 16 pixels.
var gdiPlus = new CanvasOptions
{
    PixelGeometry = PixelGeometry.RgbHorizontal,
    LcdFilter = LcdFilter.Gdi,
    LcdSampling = LcdSampling.Gdi,
    TextGamma = 1.25f,
};
var label = new Font(face, 16) { Hinting = FontHinting.Light, Edging = FontEdging.Lcd, Subpixel = false };
using (RasterCanvas<Rgb8> canvas = button.CreateCanvas(gdiPlus))
{
    canvas.DrawText("Start the course", new Vector2(40, 26), label, Rgba8.White);
}

// Softer fringes: a balanced filter and half the colour of full ClearType.
var soft = new CanvasOptions { PixelGeometry = PixelGeometry.RgbHorizontal, LcdFilter = LcdFilter.Balanced, ClearTypeLevel = 0.5f };

// Or weights of your own, normalised to add up to 256.
var custom = new CanvasOptions { PixelGeometry = PixelGeometry.RgbHorizontal, LcdFilter = LcdFilter.Custom(1, 3, 4, 3, 1) };

Exceptions

ArgumentNullException

The value is null.

LcdSampling

Gets how Lcd text samples glyphs. Defaults to Smooth; Gdi leaves edges along the stripes unsmoothed, as GDI+ ClearType does.

public LcdSampling LcdSampling { get; init; }

Property Value

LcdSampling

Examples

// GDI+ ClearType's look: its filter (an even spread over three subpixels), no smoothing along the stripes, and the text
// gamma that then matched GDI+'s weight for a white Trebuchet MS label at 16 pixels.
var gdiPlus = new CanvasOptions
{
    PixelGeometry = PixelGeometry.RgbHorizontal,
    LcdFilter = LcdFilter.Gdi,
    LcdSampling = LcdSampling.Gdi,
    TextGamma = 1.25f,
};
var label = new Font(face, 16) { Hinting = FontHinting.Light, Edging = FontEdging.Lcd, Subpixel = false };
using (RasterCanvas<Rgb8> canvas = button.CreateCanvas(gdiPlus))
{
    canvas.DrawText("Start the course", new Vector2(40, 26), label, Rgba8.White);
}

// Softer fringes: a balanced filter and half the colour of full ClearType.
var soft = new CanvasOptions { PixelGeometry = PixelGeometry.RgbHorizontal, LcdFilter = LcdFilter.Balanced, ClearTypeLevel = 0.5f };

// Or weights of your own, normalised to add up to 256.
var custom = new CanvasOptions { PixelGeometry = PixelGeometry.RgbHorizontal, LcdFilter = LcdFilter.Custom(1, 3, 4, 3, 1) };

Exceptions

ArgumentOutOfRangeException

The value is not defined.

PixelGeometry

Gets the subpixel order of the display the image is for, which Lcd text needs. Defaults to Unknown, which draws LCD-edged text in grayscale.

public PixelGeometry PixelGeometry { get; init; }

Property Value

PixelGeometry

Exceptions

ArgumentOutOfRangeException

The value is not defined.

PixelOffset

Gets where pixel centres are. Defaults to Half; None is GDI+'s default, which moves paths and unhinted text half a pixel.

public PixelOffset PixelOffset { get; init; }

Property Value

PixelOffset

Examples

// Shapes as System.Drawing draws them at its defaults (SmoothingMode.AntiAlias, PixelOffsetMode.None): whole coordinates on
// pixel centres, GDI+'s 8 by 4 samples a pixel, and pens up to 1.5 px drawn as its one-pixel thin lines.
// CanvasOptions.GdiPlus sets both too.
var options = new CanvasOptions { PixelOffset = PixelOffset.None, Antialiasing = Antialiasing.GdiPlus };
using (RasterCanvas<Rgb8> canvas = raster.CreateCanvas(options))
{
    canvas.FillOval(new RectF(4, 4, 20, 20), new SolidPaint(new Rgba8(0, 90, 200, 255)));
    canvas.DrawLine(new Vector2(30, 6), new Vector2(60, 26), new Stroke(0.5f), new SolidPaint(Rgba8.Black));
}

Exceptions

ArgumentOutOfRangeException

The value is not defined.

TextContrast

Gets how much thin glyph coverage is boosted before it blends, from 0 to 1. Defaults to 0, none. Coverage a becomes a + (1 − a)·c·a, the contrast boost of Skia and DirectWrite: it thickens antialiased edges and leaves full coverage alone. It applies to all cached glyphs, whatever the paint, canvas or ColorSpace.

public float TextContrast { get; init; }

Property Value

float

Examples

// A white label on a blue button, weighted like GDI+'s ClearTypeGridFit text: a text gamma of 1.15 matched the brightness
// GDI+ adds to within 1% for Trebuchet MS at 16 pixels. Shapes and images on this canvas blend as before.
var options = new CanvasOptions { PixelGeometry = PixelGeometry.RgbHorizontal, TextGamma = 1.15f };
var label = new Font(face, 16) { Hinting = FontHinting.Light, Edging = FontEdging.Lcd, Subpixel = false };
using (RasterCanvas<Rgb8> canvas = button.CreateCanvas(options))
{
    canvas.DrawText("Start the course", new Vector2(40, 26), label, Rgba8.White);
}

// Thin dark text on a light page can take a contrast boost instead, as Skia and DirectWrite do.
var boosted = new CanvasOptions { TextContrast = 0.25f };

Exceptions

ArgumentOutOfRangeException

The value is not between 0 and 1.

TextGamma

Gets the gamma of the blend that draws glyph coverage, from 0.5 to 3. Defaults to 1, today's blend. Each channel blends as (a·src^γ + (1 − a)·dst^γ)^(1/γ) on the stored values, as GDI ClearType does (its default is 1.4); above 1 light text on a dark ground gets heavier and dark text on a light ground lighter. Shapes and images never pass through it.

public float TextGamma { get; init; }

Property Value

float

Examples

// A white label on a blue button, weighted like GDI+'s ClearTypeGridFit text: a text gamma of 1.15 matched the brightness
// GDI+ adds to within 1% for Trebuchet MS at 16 pixels. Shapes and images on this canvas blend as before.
var options = new CanvasOptions { PixelGeometry = PixelGeometry.RgbHorizontal, TextGamma = 1.15f };
var label = new Font(face, 16) { Hinting = FontHinting.Light, Edging = FontEdging.Lcd, Subpixel = false };
using (RasterCanvas<Rgb8> canvas = button.CreateCanvas(options))
{
    canvas.DrawText("Start the course", new Vector2(40, 26), label, Rgba8.White);
}

// Thin dark text on a light page can take a contrast boost instead, as Skia and DirectWrite do.
var boosted = new CanvasOptions { TextContrast = 0.25f };

Remarks

The gamma applies to solid source-over text on an 8-bit RGB canvas outside a layer, the text LCD edging can draw; other text takes TextContrast only. With Linear the gamma is ignored: that blend is already a gamma-correct one.

Exceptions

ArgumentOutOfRangeException

The value is not between 0.5 and 3.

Tolerance

Gets how far, in device pixels, a curve's straight-line approximation may stray from it. Defaults to 0.1; smaller values cost more lines.

public float Tolerance { get; init; }

Property Value

float

Exceptions

ArgumentOutOfRangeException

The value is not between 0.001 and 10.