Table of Contents

Class RasterOps

Namespace
Egami.Operations
Assembly
Egami.dll

Animation steps: they work on the frames of the whole animation rather than on each frame, so they run in Apply, Derive and Transcode (which then decodes the whole animation first) but not inside Within. Frames in memory are already full pictures of what is on screen, so there is no Coalesce, and the writers find each frame's changed area themselves, so there is no Optimize.

public sealed class RasterOps
Inheritance
RasterOps
Inherited Members
Extension Methods

Examples

raster.Apply(ops => ops.Add(new MyOperation()));

Methods

AdaptiveThreshold(float, int?, Rgba8?, Rgba8?)

Thresholds each pixel against the mean of the window around it (Bradley and Roth, 2007): a pixel is dark when its luminance is more than limit below its window's mean, so uneven lighting on a scanned page does not swallow text. Alpha is kept.

public RasterOps AdaptiveThreshold(float limit = 0.15, int? window = null, Rgba8? below = null, Rgba8? above = null)

Parameters

limit float

How far below the local mean a pixel must be to count as dark, from 0 to 1. Defaults to 0.15.

window int?

The window's side in pixels, at least 1, or null (the default) for an eighth of the width.

below Rgba8?

The dark color, or null for black.

above Rgba8?

The light color, or null for white.

Returns

RasterOps

This builder.

Examples

// Threshold a scanned page for OCR: Otsu's level for an evenly lit page, or Bradley's local threshold for uneven light.
float level = page.FindThreshold(ThresholdMethod.Otsu);
using Raster<Gray8> even = page.Derive(o => o.AutoThreshold(ThresholdMethod.Otsu), Ct);
using Raster<Gray8> uneven = page.Derive(o => o.AdaptiveThreshold(limit: 0.15f), Ct);

Remarks

A luminance plane, 4 bytes a pixel, counts against MaxTotalBytes. Inside a rectangle or mask, the scope is its image.

Exceptions

ArgumentOutOfRangeException

An argument is out of range.

Add(IRasterOperation)

Adds a step, such as a custom IRasterOperation.

public RasterOps Add(IRasterOperation operation)

Parameters

operation IRasterOperation

The operation.

Returns

RasterOps

This builder.

Exceptions

ArgumentNullException

operation is null.

AddNoise(NoiseKind, float, bool, ulong)

Adds noise to red, green and blue (or a CMYK raster's inks), as ImageMagick's +noise with -attenuate does: film grain, sensor noise, or salt and pepper for testing filters. Alpha is never touched. The same seed gives the same pixels on any machine and at any thread count, and inside a scope an area gets the noise the whole image would get there.

public RasterOps AddNoise(NoiseKind kind, float amount = 1, bool monochrome = false, ulong seed = 0)

Parameters

kind NoiseKind

The kind of noise.

amount float

The strength, as ImageMagick's -attenuate, from 0 (changes nothing) to 10; 1 gives ImageMagick's default.

monochrome bool

Whether red, green and blue get the same noise, so it has no color.

seed ulong

The seed: change it for different noise.

Returns

RasterOps

This builder.

Examples

// Film grain: gray Gaussian noise at a third of ImageMagick's default strength; the same seed gives the same grain.
photo.Apply(ops => ops.AddNoise(NoiseKind.Gaussian, 0.3f, monochrome: true, seed: 42), Ct);

Exceptions

ArgumentOutOfRangeException

kind is not defined, or amount is outside 0..10.

AlphaToGray()

Makes the image a gray picture of its alpha: red, green and blue become the alpha value and alpha becomes opaque, as ImageMagick's -alpha extract does. A color matrix, so it fuses with neighbouring color steps.

public RasterOps AlphaToGray()

Returns

RasterOps

This builder.

Examples

using Raster alpha = photo.Derive(ops => ops.AlphaToGray(), Ct);                     // see the transparency as gray
using Raster cutout = photo.Derive(ops => ops.CopyAlpha(new Mask(matte)), Ct);       // a gray image becomes the alpha
using Raster onWhite = cutout.Derive(ops => ops.Flatten(Rgba8.White), Ct);           // composite onto white, opaque
using Raster luma = photo.Derive(ops => ops.LuminanceToAlpha(), Ct);                 // brightness becomes alpha

ApplyFilter(ColorFilter)

Runs a ColorFilter on every pixel. Adjacent filters, tone steps and color matrices run as one pass over the image, clamping between steps as running them one by one would.

public RasterOps ApplyFilter(ColorFilter filter)

Parameters

filter ColorFilter

The filter.

Returns

RasterOps

This builder.

Examples

// A look as a value: build it once, mix it in at 70%, apply it to any image or to your own pixels.
ColorFilter look = ColorFilter.Level(0.03f, 0.97f).Then(ColorFilter.Saturation(1.2f)).Then(ColorFilter.Tint(new Rgba8(255, 190, 140, 255), 0.2f));
ColorFilter subtle = ColorFilter.Lerp(ColorFilter.Identity, look, 0.7f);
raster.Apply(o => o.ApplyFilter(subtle), Ct);

Span<Rgba8> swatch = [new Rgba8(200, 120, 80, 255), new Rgba8(40, 60, 90, 255)];
subtle.Apply(swatch);

Exceptions

ArgumentNullException

filter is null.

Arithmetic(PixelMath, float, ColorChannels)

Applies op with value to every value of channels, on values scaled 0 to 1 and clamped back to 0..1, as ImageMagick's -evaluate defines each operator. 8-bit and 16-bit pixels are evaluated as 16-bit samples and rounded as ImageMagick's Q16 build rounds them, through a table per call, so results are identical to -evaluate.

public RasterOps Arithmetic(PixelMath op, float value, ColorChannels channels = ColorChannels.Color)

Parameters

op PixelMath

The operator.

value float

The constant, on the same 0 to 1 scale for Add, Subtract, Min, Max and Set.

channels ColorChannels

The channels to change. Defaults to Color; alpha changes only when included.

Returns

RasterOps

This builder.

Examples

// Lift the shadows with a log curve, then cap the highlights; values are on a 0 to 1 scale, as ImageMagick's -evaluate.
photo.Apply(ops => ops
    .Arithmetic(PixelMath.Log, 3)
    .Arithmetic(PixelMath.Min, 0.95f), Ct);

Exceptions

ArgumentOutOfRangeException

op is not defined, value is not finite, or channels holds values that are not channels.

AutoGamma(bool, bool)

Applies the Gamma(float) that moves the image's mean to 0.5, as ImageMagick's -auto-gamma; an image whose mean is 0 or 1 is left alone. Measured like AutoLevel(bool, bool).

public RasterOps AutoGamma(bool linked = true, bool perFrame = false)

Parameters

linked bool

Whether the channels share one gamma from their joint mean (the default) or each gets its own.

perFrame bool

Whether each frame is measured alone.

Returns

RasterOps

This builder.

Examples

// Fix a dull scan: stretch it to the full range, clipping the darkest 2% and lightest 1%, then set the midtones.
raster.Apply(o => o.Normalize().AutoGamma(), Ct);

// Or stretch each channel on its own, which also removes a cast.
raster.Apply(o => o.AutoLevel(linked: false), Ct);

// Measured steps measure only their scope: here the sky alone is stretched.
raster.Apply(o => o.Within(new Rect(0, 0, raster.Width, raster.Height / 3), sky => sky.AutoLevel()), Ct);

AutoLevel(bool, bool)

Stretches the darkest value to 0 and the lightest to 1, as ImageMagick's -auto-level. The chain runs up to this step, the result is measured, and the step becomes a Level(float, float, float, float, float); inside Within(Rect, Action<RasterOps>), Within(Mask, Action<RasterOps>) or OnChannels(ColorChannels, Action<RasterOps>) only that scope is measured. Transparent pixels do not count.

public RasterOps AutoLevel(bool linked = true, bool perFrame = false)

Parameters

linked bool

Whether red, green and blue share one black and one white point (the default, which keeps colors), or each gets its own, which also removes a cast.

perFrame bool

Whether each frame is measured alone, as ImageMagick does, instead of all frames together so an animation does not flicker.

Returns

RasterOps

This builder.

Examples

// Fix a dull scan: stretch it to the full range, clipping the darkest 2% and lightest 1%, then set the midtones.
raster.Apply(o => o.Normalize().AutoGamma(), Ct);

// Or stretch each channel on its own, which also removes a cast.
raster.Apply(o => o.AutoLevel(linked: false), Ct);

// Measured steps measure only their scope: here the sky alone is stretched.
raster.Apply(o => o.Within(new Rect(0, 0, raster.Width, raster.Height / 3), sky => sky.AutoLevel()), Ct);

AutoOrient()

Turns the image the way its EXIF orientation says, so it looks as the camera meant, and sets the orientation to Normal. Each orientation is one exact pass (a flip, a quarter turn or a transpose); an image without EXIF, or already upright, is left alone. Followed by Resize(int, int, ResampleFilter, ColorSpace), Fit(int, int, ResampleFilter, ColorSpace, bool) or Cover(int, int, Anchor, ResampleFilter, ColorSpace), the resize runs first on the stored image and the small result is turned, which matches the other order to within rounding.

public RasterOps AutoOrient()

Returns

RasterOps

This builder.

AutoThreshold(ThresholdMethod, ThresholdMode, Rgba8?, Rgba8?, bool)

Thresholds at the level method finds in the histogram, as ImageMagick's -auto-threshold; see FindThreshold(ThresholdMethod, ThresholdMode). Measured like AutoLevel(bool, bool).

public RasterOps AutoThreshold(ThresholdMethod method = ThresholdMethod.Otsu, ThresholdMode mode = ThresholdMode.Luminance, Rgba8? below = null, Rgba8? above = null, bool perFrame = false)

Parameters

method ThresholdMethod

How the level is found. Defaults to Otsu.

mode ThresholdMode

What is measured. Defaults to Luminance.

below Rgba8?

The lower color, or null for black.

above Rgba8?

The upper color, or null for white.

perFrame bool

Whether each frame is measured alone.

Returns

RasterOps

This builder.

Examples

// Threshold a scanned page for OCR: Otsu's level for an evenly lit page, or Bradley's local threshold for uneven light.
float level = page.FindThreshold(ThresholdMethod.Otsu);
using Raster<Gray8> even = page.Derive(o => o.AutoThreshold(ThresholdMethod.Otsu), Ct);
using Raster<Gray8> uneven = page.Derive(o => o.AdaptiveThreshold(limit: 0.15f), Ct);

Exceptions

ArgumentOutOfRangeException

An enum value is not defined.

AutoWhiteBalance(WhiteBalanceMethod, bool)

Removes a color cast it measures, by method. Measured like AutoLevel(bool, bool).

public RasterOps AutoWhiteBalance(WhiteBalanceMethod method = WhiteBalanceMethod.GrayWorld, bool perFrame = false)

Parameters

method WhiteBalanceMethod

How the cast is found. Defaults to GrayWorld, ImageMagick's -white-balance.

perFrame bool

Whether each frame is measured alone.

Returns

RasterOps

This builder.

Examples

// Correct a tungsten-lit photo, as a raw editor's temperature slider does.
raster.Apply(o => o.WhiteBalance(temperature: 3200), Ct);

// Or pick a gray card's color and make it neutral.
raster.Apply(o => o.WhiteBalance(new Rgba8(182, 170, 151, 255)), Ct);

// Or let the image tell: ImageMagick's gray world, or the brightest pixels as white.
raster.Apply(o => o.AutoWhiteBalance(WhiteBalanceMethod.GrayWorld), Ct);
raster.Apply(o => o.AutoWhiteBalance(WhiteBalanceMethod.WhitePatch), Ct);

Exceptions

ArgumentOutOfRangeException

method is not defined.

BilateralBlur(float, float, int?)

Smooths while keeping edges (Tomasi and Manduchi's bilateral filter): each neighbour counts by its distance, through a Gaussian of spatialSigma, and by how much its color differs, through a Gaussian of rangeSigma, so noise flattens and edges stay. The color difference is the root-mean-square difference of red, green and blue on 0..1, so on a gray image the sigmas mean what ImageMagick's -bilateral-blur WxW+(255·rangeSigma)+spatialSigma means. Alpha is averaged with the same weights; edges repeat the edge pixel.

public RasterOps BilateralBlur(float spatialSigma, float rangeSigma, int? radius = null)

Parameters

spatialSigma float

The reach in pixels, above 0 and at most 50.

rangeSigma float

How different a color may be and still count, above 0, on the 0..1 scale: 0.1 keeps most edges.

radius int?

The window's radius, or null for ⌈2 · spatialSigma⌉ as ImageMagick's width; at most 100. Above 8 the taps are taken every ⌈radius / 8⌉ pixels, so the cost stays bounded.

Returns

RasterOps

This builder.

Examples

// Smooth skin or sky while keeping edges: neighbours count less the more their color differs (0.1 on the 0..1 scale).
using Raster smooth = photo.Derive(ops => ops.BilateralBlur(spatialSigma: 4, rangeSigma: 0.1f), Ct);

Exceptions

ArgumentOutOfRangeException

A sigma is not positive and finite or above its limit, or radius is negative or above 100.

BlendColor(Rgba8, BlendMode, float, ColorSpace)

Blends one color over every pixel by mode: Multiply tints, Screen washes out, Color colorizes. The color's own alpha and opacity set how strongly.

public RasterOps BlendColor(Rgba8 color, BlendMode mode, float opacity = 1, ColorSpace colorSpace = ColorSpace.Encoded)

Parameters

color Rgba8

The color, with straight alpha.

mode BlendMode

How the color combines with each pixel.

opacity float

Multiplies the color's alpha before the mode: 1 uses it as it is.

colorSpace ColorSpace

Where pixels are blended. Defaults to Encoded.

Returns

RasterOps

This builder.

Examples

// Warm the photo with an orange tint, and give it a gray-blue color grade.
photo.Apply(ops => ops
    .BlendColor(new Rgba8(255, 200, 150, 255), BlendMode.Multiply)
    .BlendColor(new Rgba8(60, 80, 110, 255), BlendMode.Color, opacity: 0.3f), Ct);

Exceptions

ArgumentOutOfRangeException

opacity is outside 0..1, or an enum value is not defined.

Bloom(float, float, float)

Makes bright areas glow, as a camera's bloom: the parts of each channel above threshold, stretched to 0..1 and scaled by intensity, blurred in linear light with Fast and screened onto the image in linear light. It is exactly the chain written by hand: a layer made with one ColorMatrix(ColorMatrix) (red, green and blue times k = intensity / (1 − threshold), minus threshold × k, clamped) and Blur(float, ColorSpace, BlurMethod) in linear light, then drawn with DrawImage, Screen, in linear light.

public RasterOps Bloom(float threshold = 0.8, float sigma = 10, float intensity = 1)

Parameters

threshold float

The stored value above which a channel blooms, from 0 to under 1.

sigma float

The bloom's blur in pixels, from 0 to 1000.

intensity float

How strongly the bloom is added, from 0 (nothing) to 100; values are clamped to 1 before the blur.

Returns

RasterOps

This builder.

Examples

// Highlights above 80% bleed light into their surroundings, added in linear light as a lens does.
photo.Apply(ops => ops.Bloom(threshold: 0.8f, sigma: 12, intensity: 1.5f), Ct);

Exceptions

ArgumentOutOfRangeException

threshold is outside 0 to under 1, sigma is outside 0..1000, or intensity is outside 0..100.

Blur(float, ColorSpace)

Blurs with a Gaussian of standard deviation sigma pixels (radius ceil(3σ), weights normalized). Edges repeat the edge pixel. Above σ = 100, where a true Gaussian needs over 600 taps a pass, it runs as three box blurs of matched width, whose variance is within 3% of the Gaussian's.

public RasterOps Blur(float sigma, ColorSpace colorSpace = ColorSpace.Encoded)

Parameters

sigma float

The standard deviation in pixels; 0 changes nothing.

colorSpace ColorSpace

Where pixels are blended. Defaults to Encoded.

Returns

RasterOps

This builder.

Exceptions

ArgumentOutOfRangeException

sigma is negative or not finite.

Blur(float, ColorSpace, BlurMethod)

Blurs with a Gaussian of standard deviation sigma pixels, computed by method. Exact is Blur(float, ColorSpace); Fast costs the same at any σ, for shadows, glows and other large blurs. Edges repeat the edge pixel.

public RasterOps Blur(float sigma, ColorSpace colorSpace, BlurMethod method)

Parameters

sigma float

The standard deviation in pixels; 0 changes nothing.

colorSpace ColorSpace

Where pixels are blended.

method BlurMethod

How the blur is computed.

Returns

RasterOps

This builder.

Examples

// A large, soft background blur: Fast costs the same at any sigma and is within 50 dB of the exact Gaussian from sigma 3.
using Raster soft = photo.Derive(ops => ops.Blur(40, ColorSpace.Linear, BlurMethod.Fast), Ct);

Exceptions

ArgumentOutOfRangeException

sigma is negative, not finite or above 1000, or an enum value is not defined.

BoxBlur(int)

Replaces each pixel with the mean of the (2r + 1) square around it. Edges repeat the edge pixel.

public RasterOps BoxBlur(int radius)

Parameters

radius int

The radius in pixels; 0 changes nothing.

Returns

RasterOps

This builder.

Exceptions

ArgumentOutOfRangeException

radius is negative or above 1000.

Brightness(float)

Multiplies red, green and blue by amount: 1 changes nothing, 0 gives black. See Brightness(float).

public RasterOps Brightness(float amount)

Parameters

amount float

The factor, 0 or more.

Returns

RasterOps

This builder.

Exceptions

ArgumentOutOfRangeException

amount is negative or not finite.

CannyEdges(float, float, float)

Finds thin edges with Canny's detector, as ImageMagick's -canny 0x{sigma}+{low × 100}%+{high × 100}% does: the luma is smoothed by a Gaussian, Canny's first differences give the gradient, pixels that are not the strongest across their edge are dropped, and hysteresis keeps pixels whose magnitude reaches high times the largest, plus those 8-connected to them that reach low times it. Writes white edges on black, one pixel wide, with alpha kept: a gray result works as a Mask as it is, a color one with Luminance. Reads the whole image, so inside a scope it sees all of it.

public RasterOps CannyEdges(float sigma = 1, float low = 0.1, float high = 0.3)

Parameters

sigma float

The Gaussian's standard deviation in pixels, 0 (no smoothing) to 100.

low float

The low threshold, as a fraction 0 to 1 of the largest gradient; at most high.

high float

The high threshold, as a fraction 0 to 1 of the largest gradient.

Returns

RasterOps

This builder.

Examples

// Thin white edges on black. A gray raster works as a Mask as it is; an RGBA one needs MaskChannel.Luminance,
// because a Mask of a raster with alpha reads its alpha.
using Raster<Gray8> edges = Raster.Load<Gray8>("duck.png");
edges.Apply(o => o.CannyEdges(sigma: 1.4f).Dilate(1), Ct);                 // thin edges, thickened

photo.Apply(o => o.Within(new Mask(edges), s => s.FillColor(Rgba8.Red)), Ct);  // paint over them

Exceptions

ArgumentOutOfRangeException

sigma is negative, not finite or above 100; a threshold is outside 0 to 1, or low is above high.

ChangeSpeed(double)

Plays the animation factor times as fast: every delay is divided by it, rounded to whole milliseconds.

public RasterOps ChangeSpeed(double factor)

Parameters

factor double

The speed-up, more than 0: 2 plays twice as fast, 0.5 at half speed.

Returns

RasterOps

This builder.

Examples

// Twice as fast, then backwards: a boomerang clip is the clip followed by its reverse.
using Raster fast = clip.Derive(ops => ops.ChangeSpeed(2), Ct);
using Raster backwards = fast.Derive(ops => ops.Reverse(), Ct);

Exceptions

ArgumentOutOfRangeException

factor is not finite or not positive.

ChromaKey(Rgba8, float, float)

Makes pixels whose chroma lies within similarity of color's transparent, keeping their color: a chroma key for green and blue screens. The distance is taken between Rec. 709 Cb and Cr, leaving luma out, so lighter and darker patches of an unevenly lit screen stay closer to the key than they do by the RGB distance MakeTransparent(Rgba8, float, float) uses. blend ramps alpha back up over the next band of distance, for hair and soft edges. Follow it with Despill(DespillColor, float, float) to remove the screen's cast from the edges that stay.

public RasterOps ChromaKey(Rgba8 color, float similarity = 0.1, float blend = 0.05)

Parameters

color Rgba8

The screen color, such as #00b140 for a green screen; its alpha is ignored.

similarity float

The chroma distance that becomes fully transparent, from 0 (the key's exact chroma) to 1 (every color), as a share of the largest distance in RGB, the one between green and magenta.

blend float

The band of distance beyond similarity over which alpha returns to full; 0 for a hard edge.

Returns

RasterOps

This builder.

Examples

// Key out a green screen by chroma, so its lighter and darker patches go too, soften the edge, and take the green cast off
// what is left; then put the result over a new background.
photo.Apply(ops => ops
    .ChromaKey(new Rgba8(0, 177, 64, 255), similarity: 0.12f, blend: 0.06f)
    .Despill(DespillColor.Green, mix: 0.5f), Ct);
backdrop.Apply(ops => ops.DrawImage(photo, new Point(0, 0)), Ct);

Exceptions

ArgumentOutOfRangeException

similarity is outside 0..1, or blend is negative or not finite.

Clahe(int, int, float, int)

Contrast-limited adaptive histogram equalization (Zuiderveld, 1994) on L*, as ImageMagick's -clahe: each of tilesX × tilesY tiles gets its own clipped, equalized curve, blended smoothly between tile centers, so local detail comes up without the noise of plain equalization. a* and b* are kept. Each frame is its own.

public RasterOps Clahe(int tilesX = 8, int tilesY = 8, float clipLimit = 2, int bins = 256)

Parameters

tilesX int

Tiles across, from 1 to 256. Defaults to 8.

tilesY int

Tiles down, from 1 to 256. Defaults to 8.

clipLimit float

How far a tile's histogram may rise, as a multiple of its mean bin (2 is mild, 4 strong); 0 does not clip.

bins int

The histogram bins, from 2 to 4,096. Defaults to 256.

Returns

RasterOps

This builder.

Examples

// Bring out detail in a flat, foggy landscape: CLAHE on 8 × 8 tiles, then ImageMagick's local contrast.
raster.Apply(o => o.Clahe(tilesX: 8, tilesY: 8, clipLimit: 2).LocalContrast(radius: 0.1f, strength: 0.15f), Ct);

Remarks

The lightness plane, 4 bytes a pixel, counts against MaxTotalBytes. Inside a rectangle or mask, the scope is its image.

Exceptions

ArgumentOutOfRangeException

An argument is out of range.

ColorMatrix(ColorMatrix)

Applies a color matrix to straight RGBA and clamps the result. Adjacent color steps are multiplied into one matrix before anything runs, so the chain clamps once, at its end.

public RasterOps ColorMatrix(ColorMatrix matrix)

Parameters

matrix ColorMatrix

The matrix.

Returns

RasterOps

This builder.

Colorize(Rgba8, float)

Blends red, green and blue toward color, as ImageMagick's -colorize. See Colorize(Rgba8, float).

public RasterOps Colorize(Rgba8 color, float amount)

Parameters

color Rgba8

The color.

amount float

The blend, from 0 to 1.

Returns

RasterOps

This builder.

Examples

// A two-color poster, then a check of how it reads to someone with deuteranopia.
raster.Apply(o => o.Duotone(Rgba8.FromHex("#1b2a49"), Rgba8.FromHex("#f2c14e")).Posterize(4, DitherMode.Ordered), Ct);
using Raster<Rgba8> preview = raster.Derive(o => o.SimulateColorBlindness(ColorVisionDeficiency.Deuteranopia), Ct);

// High contrast for low vision: gray, lightness inverted so dark becomes light, and more contrast.
using Raster<Rgba8> readable = raster.Derive(o => o.HighContrast(grayscale: true, ContrastInvert.Lightness, contrast: 0.3f), Ct);

Exceptions

ArgumentOutOfRangeException

amount is outside 0..1.

Contrast(float)

Scales red, green and blue around mid-gray: 1 changes nothing, 0 gives flat gray. See Contrast(float).

public RasterOps Contrast(float amount)

Parameters

amount float

The factor, 0 or more.

Returns

RasterOps

This builder.

Exceptions

ArgumentOutOfRangeException

amount is negative or not finite.

ContrastStretch(float, float, bool, bool)

Clips the darkest black and lightest white fractions of the pixels, then stretches the rest to 0..1, as ImageMagick's -contrast-stretch. Linked, the points come from the luminance histogram, as ImageMagick's synced channels. Measured like AutoLevel(bool, bool).

public RasterOps ContrastStretch(float black, float white, bool linked = true, bool perFrame = false)

Parameters

black float

The fraction of pixels that becomes black, from 0 to 1.

white float

The fraction that becomes white, from 0 to 1; with black less than 1.

linked bool

Whether the channels share one pair of points (the default) or each gets its own.

perFrame bool

Whether each frame is measured alone.

Returns

RasterOps

This builder.

Examples

// Fix a dull scan: stretch it to the full range, clipping the darkest 2% and lightest 1%, then set the midtones.
raster.Apply(o => o.Normalize().AutoGamma(), Ct);

// Or stretch each channel on its own, which also removes a cast.
raster.Apply(o => o.AutoLevel(linked: false), Ct);

// Measured steps measure only their scope: here the sky alone is stretched.
raster.Apply(o => o.Within(new Rect(0, 0, raster.Width, raster.Height / 3), sky => sky.AutoLevel()), Ct);

Exceptions

ArgumentOutOfRangeException

A fraction is outside 0..1, or the two add up to 1 or more.

ConvertColor(ColorProfile, ColorTransformOptions?)

Converts the pixels from the encoding the raster is in (GetColorProfile(), or the target of an earlier ConvertColor in the chain) to target, in place, and records the new encoding in the metadata: no profile for sRGB, the target's ICC profile otherwise. The pixel type stays the same, so the target must have the pixels' model (an RGB target for gray pixels uses its curve); use ConvertColor<TTo>(ColorProfile, ColorTransformOptions?) to change model, as from CMYK to RGB.

public RasterOps ConvertColor(ColorProfile target, ColorTransformOptions? options = null)

Parameters

target ColorProfile

The encoding to convert to.

options ColorTransformOptions

The intent and black point compensation, or null for the defaults.

Returns

RasterOps

This builder.

Examples

// What the pixels mean, from the file's ICC profile, tags and gamma; sRGB when it said nothing.
ColorProfile stored = raster.Metadata.GetColorProfile();

// Convert in place to sRGB for the web: the metadata then says sRGB, so no profile is written.
raster.Apply(ops => ops.ConvertColor(ColorProfile.Srgb));

Exceptions

ArgumentNullException

target is null.

Convolve(ConvolutionKernel, EdgeMode, bool)

Convolves with a kernel, applied as laid out. Color and alpha are filtered together in premultiplied form unless preserveAlpha is set, which filters only the color and keeps each pixel's alpha, as edge-detect kernels need.

public RasterOps Convolve(ConvolutionKernel kernel, EdgeMode edge = EdgeMode.Clamp, bool preserveAlpha = false)

Parameters

kernel ConvolutionKernel

The kernel. Separable kernels run as two 1D passes.

edge EdgeMode

How taps past the border are read. Defaults to Clamp.

preserveAlpha bool

Whether to keep the source alpha and filter only red, green and blue.

Returns

RasterOps

This builder.

Exceptions

ArgumentOutOfRangeException

edge is not defined.

ArgumentNullException

kernel is null.

CopyAlpha(Mask)

Sets alpha to mask's coverage and keeps the color: a gray image from elsewhere becomes the transparency. Outside the mask raster coverage is 0, so pixels there become transparent, unless the mask is inverted.

public RasterOps CopyAlpha(Mask mask)

Parameters

mask Mask

The coverage: a raster of any size and type, placed at its offset, read by its channel.

Returns

RasterOps

This builder.

Examples

using Raster alpha = photo.Derive(ops => ops.AlphaToGray(), Ct);                     // see the transparency as gray
using Raster cutout = photo.Derive(ops => ops.CopyAlpha(new Mask(matte)), Ct);       // a gray image becomes the alpha
using Raster onWhite = cutout.Derive(ops => ops.Flatten(Rgba8.White), Ct);           // composite onto white, opaque
using Raster luma = photo.Derive(ops => ops.LuminanceToAlpha(), Ct);                 // brightness becomes alpha

Remarks

The mask raster must stay undisposed until the chain has run. Copying a raster's own coverage reads from a copy.

Exceptions

ArgumentException

The mask has no raster.

ArgumentOutOfRangeException

The mask's channel is not defined.

Cover(int, int, Anchor, ResampleFilter, ColorSpace)

Scales the image to cover width × height with its aspect ratio kept, keeping the part at anchor. Only the kept part is read; nothing is cropped first.

public RasterOps Cover(int width, int height, Anchor anchor = Anchor.Center, ResampleFilter filter = ResampleFilter.Bicubic, ColorSpace colorSpace = ColorSpace.Encoded)

Parameters

width int

The output width, at least 1.

height int

The output height, at least 1.

anchor Anchor

Which part of the image to keep. Defaults to the center.

filter ResampleFilter

The resampling kernel. Defaults to Bicubic.

colorSpace ColorSpace

Where pixels are blended. Defaults to Encoded.

Returns

RasterOps

This builder.

Exceptions

ArgumentOutOfRangeException

A size is less than 1, or an enum value is not defined.

Crop(Rect)

Keeps one rectangle of the image. The pixels are copied exactly.

public RasterOps Crop(Rect rect)

Parameters

rect Rect

The rectangle to keep. It must lie inside the image, which is checked before any pixel changes.

Returns

RasterOps

This builder.

Exceptions

ArgumentOutOfRangeException

The rectangle is empty, or leaves the image when the chain runs.

Crop(int, int, int, int)

Keeps one rectangle of the image. The pixels are copied exactly.

public RasterOps Crop(int x, int y, int width, int height)

Parameters

x int

The left edge of the rectangle.

y int

The top edge of the rectangle.

width int

The width of the rectangle, at least 1.

height int

The height of the rectangle, at least 1.

Returns

RasterOps

This builder.

Exceptions

ArgumentOutOfRangeException

The rectangle is empty, or leaves the image when the chain runs.

Deskew(float, Rgba8?, bool, ResampleFilter)

Straightens a scanned or photographed page: detects the tilt of its text lines (see DetectSkew(float)) and turns the image back by it. Every frame is turned by the first frame's angle; an image with no clear lines is left as it is.

public RasterOps Deskew(float maxAngle = 15, Rgba8? background = null, bool keepSize = true, ResampleFilter filter = ResampleFilter.Bicubic)

Parameters

maxAngle float

The largest tilt looked for, in degrees, more than 0 and at most 45.

background Rgba8?

The color of the uncovered corners, or null (the default) for the top-left pixel's, the border color Trim would use.

keepSize bool

Whether the page keeps its size (the default, as a scanner user expects), or the canvas grows to the turned bounds as Rotate does.

filter ResampleFilter

The resampling kernel for the turn.

Returns

RasterOps

This builder.

Examples

// Measure the tilt first, if you want to show it or decide yourself...
float tilt = photo.DetectSkew(maxAngle: 10);

// ...or straighten in one step, keeping the page size with white corners.
using Raster straight = photo.Derive(ops => ops.Deskew(background: Rgba8.White), Ct);

Remarks

The angle depends on the pixels, so the chain runs up to the deskew and measures; it cannot run inside Within.

Exceptions

ArgumentOutOfRangeException

maxAngle is out of range, or filter is not defined.

Despeckle()

Removes specks while keeping edges, by Crimmins' complementary hulling exactly as ImageMagick's -despeckle runs it: each color channel is nudged, a level at a time, toward neighbours that differ by two levels or more, in 16 passes along four directions. Alpha is kept. The image's border is compared with black, as ImageMagick does.

public RasterOps Despeckle()

Returns

RasterOps

This builder.

Examples

// Clean a scan: specks shrink, edges and text stay. Median(1) is the stronger alternative.
scan.Apply(ops => ops.Despeckle(), Ct);

Despill(DespillColor, float, float)

Removes the colored cast a green or blue screen leaves on edges, hair and see-through pixels: the screen's channel is held down to a limit taken from the other two channels. Run it after ChromaKey(Rgba8, float, float) or MakeTransparent(Rgba8, float, float), or on its own on a photo shot in front of a screen.

public RasterOps Despill(DespillColor screen = DespillColor.Green, float mix = 0.5, float expand = 0)

Parameters

screen DespillColor

The screen color whose spill is removed.

mix float

How the limit is taken from the other two channels, from 0 (their average, the strongest removal) to 1 (the larger of the two, the gentlest, which leaves neutral colors alone).

expand float

How much further the limit is lowered, from 0 to 1, for heavy spill; this darkens the screen's channel more.

Returns

RasterOps

This builder.

Examples

// Key out a green screen by chroma, so its lighter and darker patches go too, soften the edge, and take the green cast off
// what is left; then put the result over a new background.
photo.Apply(ops => ops
    .ChromaKey(new Rgba8(0, 177, 64, 255), similarity: 0.12f, blend: 0.06f)
    .Despill(DespillColor.Green, mix: 0.5f), Ct);
backdrop.Apply(ops => ops.DrawImage(photo, new Point(0, 0)), Ct);

Exceptions

ArgumentOutOfRangeException

screen is not defined, or mix or expand is outside 0..1.

Dilate(int)

The maximum over the (2r + 1) square around each pixel: light areas grow by radius pixels.

public RasterOps Dilate(int radius)

Parameters

radius int

The radius in pixels, 0 (changes nothing) to 1000.

Returns

RasterOps

This builder.

Examples

using Raster grown = photo.Derive(o => o.Dilate(3), Ct);       // light areas grow 3 px: a 7 × 7 square
using Raster shrunk = photo.Derive(o => o.Erode(3), Ct);       // light areas shrink 3 px
using Raster wide = photo.Derive(o => o.Dilate(6, 1), Ct);     // 13 × 3, as Skia's dilate radii

Exceptions

ArgumentOutOfRangeException

radius is negative or above 1000.

Dilate(int, int)

The maximum over the (2rx + 1) × (2ry + 1) rectangle around each pixel, as Skia's dilate filter with those radii.

public RasterOps Dilate(int radiusX, int radiusY)

Parameters

radiusX int

The horizontal radius in pixels, 0 to 1000.

radiusY int

The vertical radius in pixels, 0 to 1000.

Returns

RasterOps

This builder.

Examples

using Raster grown = photo.Derive(o => o.Dilate(3), Ct);       // light areas grow 3 px: a 7 × 7 square
using Raster shrunk = photo.Derive(o => o.Erode(3), Ct);       // light areas shrink 3 px
using Raster wide = photo.Derive(o => o.Dilate(6, 1), Ct);     // 13 × 3, as Skia's dilate radii

Exceptions

ArgumentOutOfRangeException

A radius is negative or above 1000.

Displace(Raster, float, float, ColorChannel, ColorChannel, SamplingOptions?)

Moves each pixel by a displacement map, as Skia's DisplacementMap and ImageMagick's -displace do: pixel (x, y) shows the source at (x + scaleX × (mx − 0.5), y + scaleY × (my − 0.5)), with mx and my the map's chosen channels from 0 to 1, so mid-gray moves nothing. The map's first frame serves every frame.

public RasterOps Displace(Raster map, float scaleX, float scaleY, ColorChannel xChannel = ColorChannel.Red, ColorChannel yChannel = ColorChannel.Green, SamplingOptions? options = null)

Parameters

map Raster

The displacement map, the image's size (Resize it first if it is not).

scaleX float

The horizontal move, in pixels, for a channel going from 0 to 1.

scaleY float

The vertical move, in pixels, for a channel going from 0 to 1.

xChannel ColorChannel

The map channel that moves pixels horizontally.

yChannel ColorChannel

The map channel that moves pixels vertically.

options SamplingOptions

The filter, color space, edges and background, or null for the defaults.

Returns

RasterOps

This builder.

Examples

// A displacement map the image's size: mid-gray moves nothing, red moves pixels sideways and green up and down.
using Raster<Rgba8> map = Raster.Create<Rgba8>(photo.Width, photo.Height);
map.ForEachRow((row, y) =>
{
    for (int x = 0; x < row.Length; x++)
    {
        byte wave = (byte)(128 + (100 * Math.Sin((x + y) / 12.0)));
        row[x] = new Rgba8(wave, 128, 128, 255);
    }
});
using Raster<Rgba8> rippled = photo.Derive(ops => ops.Displace(map, 16, 0), Ct);

Exceptions

ArgumentNullException

map is null.

ArgumentOutOfRangeException

A scale is not finite or a channel is not defined.

ArgumentException

The map's size differs from the image's, when the chain is planned.

DrawAnimation(Raster, Point, AnimationOverlayOptions?)

Draws an animation (or a still) onto this one at position. By time (the default) both timelines are cut at every frame boundary of either, so an overlay at another speed keeps its speed; each piece shows the base frame and the overlay frame of that moment, drawn as DrawImage does with the blend mode and opacity. The overlay loops for as long as the base runs unless Repeat says otherwise; a still base lasts as long as the overlay plays and takes its loop count. The overlay is converted to this raster's pixel type. The output frame count is checked against MaxFrames and MaxTotalBytes before anything is drawn.

public RasterOps DrawAnimation(Raster overlay, Point position, AnimationOverlayOptions? options = null)

Parameters

overlay Raster

The animation or still to draw.

position Point

Where its top-left corner goes; it may lie partly or wholly outside.

options AnimationOverlayOptions

The blending, repeat count, start and timing, or null for Default.

Returns

RasterOps

This builder.

Examples

// A looping sticker on an animated background: the sticker keeps its own speed and loops for the background's whole length.
background.Apply(ops => ops.DrawAnimation(sticker, new Point(10, 10), new AnimationOverlayOptions { Repeat = 0, Opacity = 0.9f }), Ct);
background.Save("with-sticker.gif");

Exceptions

ArgumentNullException

overlay is null.

LimitExceededException

The result would break MaxFrames or MaxTotalBytes.

DrawArithmetic(Raster, Point, float, float, float, float, bool)

Draws image with the arithmetic composite (SVG's feComposite arithmetic operator, Skia's arithmetic blender): each channel of the premultiplied result is k1·S·D + k2·S + k3·D + k4, with S the image and D this raster, in 0..1. Only the overlap changes. k2 = k3 = 0.5 averages the two; k1 = 1 multiplies them.

public RasterOps DrawArithmetic(Raster image, Point position, float k1, float k2, float k3, float k4, bool clamp = true)

Parameters

image Raster

The image to draw. It must stay undisposed until the chain has run; its first frame is drawn onto every frame.

position Point

Where its top-left corner goes; may be negative.

k1 float

The weight of the product S·D.

k2 float

The weight of S.

k3 float

The weight of D.

k4 float

The constant added.

clamp bool

Whether each result is kept a valid premultiplied pixel: every channel in 0..1 and color no higher than alpha. Without it, only the pixel type's own range applies.

Returns

RasterOps

This builder.

Examples

// Average two images: each channel becomes 0.5·S + 0.5·D.
photo.Apply(ops => ops.DrawArithmetic(other, new Point(0, 0), k1: 0, k2: 0.5f, k3: 0.5f, k4: 0), Ct);

Exceptions

ArgumentOutOfRangeException

A weight is not finite.

ArgumentNullException

image is null.

DrawImage(Raster, Point, BlendMode, float, ColorSpace)

Draws image onto this raster with its top-left corner at position, combining each pixel with the one under it by mode. Only the overlap of the image and this raster changes, as when drawing a layer: SourceIn and DestinationIn do not clear what lies outside the image. SourceOver on stored values gives exactly what DrawImage(Raster, Point, float) gives.

public RasterOps DrawImage(Raster image, Point position, BlendMode mode, float opacity = 1, ColorSpace colorSpace = ColorSpace.Encoded)

Parameters

image Raster

The image to draw, of any pixel type. It must stay undisposed until the chain has run; its first frame is drawn onto every frame.

position Point

Where its top-left corner goes; may be negative.

mode BlendMode

How each pixel combines with the one under it.

opacity float

Multiplies the image's alpha before the mode: 1 draws it as it is.

colorSpace ColorSpace

Where pixels are blended. Defaults to Encoded, as browsers and editors blend.

Returns

RasterOps

This builder.

Examples

// Print the photo onto paper: Multiply keeps the paper's texture under every color.
paper.Apply(ops => ops.DrawImage(photo, new Point(0, 0), BlendMode.Multiply), Ct);

// Or lighten it in linear light at half strength; only where the photo overlaps changes.
paper.Apply(ops => ops.DrawImage(photo, new Point(10, 10), BlendMode.Screen, 0.5f, ColorSpace.Linear), Ct);

Exceptions

ArgumentOutOfRangeException

opacity is outside 0..1, or an enum value is not defined.

ArgumentNullException

image is null.

DrawImage(Raster, Point, float)

Draws image onto this raster with its top-left corner at position, using source-over blending (the normal way a layer covers what is under it). The image may sit partly or wholly outside; only the overlap changes. Its first frame is drawn onto every frame, and it may be any pixel type.

public RasterOps DrawImage(Raster image, Point position, float opacity = 1)

Parameters

image Raster

The image to draw. It must stay undisposed until the chain has run.

position Point

Where its top-left corner goes; may be negative.

opacity float

Multiplies the image's alpha: 1 draws it as it is, 0 draws nothing.

Returns

RasterOps

This builder.

Remarks

Drawing a raster onto itself reads from a copy of it, made when the step runs.

Exceptions

ArgumentOutOfRangeException

opacity is outside 0..1.

ArgumentNullException

image is null.

DrawImage(Raster, Rect, Point, BlendMode, float, ColorSpace)

Draws the part of image inside sourceArea onto this raster with that part's top-left corner at position, combining pixels by mode as DrawImage(Raster, Point, BlendMode, float, ColorSpace) does for the whole image.

public RasterOps DrawImage(Raster image, Rect sourceArea, Point position, BlendMode mode, float opacity = 1, ColorSpace colorSpace = ColorSpace.Encoded)

Parameters

image Raster

The image to draw from. It must stay undisposed until the chain has run.

sourceArea Rect

The part of the image to draw, clipped to the image.

position Point

Where the area's top-left corner goes; may be negative.

mode BlendMode

How each pixel combines with the one under it.

opacity float

Multiplies the image's alpha before the mode: 1 draws it as it is.

colorSpace ColorSpace

Where pixels are blended. Defaults to Encoded.

Returns

RasterOps

This builder.

Examples

// Print the photo onto paper: Multiply keeps the paper's texture under every color.
paper.Apply(ops => ops.DrawImage(photo, new Point(0, 0), BlendMode.Multiply), Ct);

// Or lighten it in linear light at half strength; only where the photo overlaps changes.
paper.Apply(ops => ops.DrawImage(photo, new Point(10, 10), BlendMode.Screen, 0.5f, ColorSpace.Linear), Ct);

Exceptions

ArgumentOutOfRangeException

sourceArea does not overlap the image, opacity is outside 0..1, or an enum value is not defined.

ArgumentNullException

image is null.

DrawImage(Raster, Rect, Point, float)

Draws the part of image inside sourceArea onto this raster with that part's top-left corner at position, as DrawImage(Raster, Point, float) does for the whole image. Use it for sprite sheets, tiles and partial updates: no copy of the area is made.

public RasterOps DrawImage(Raster image, Rect sourceArea, Point position, float opacity = 1)

Parameters

image Raster

The image to draw from. It must stay undisposed until the chain has run.

sourceArea Rect

The part of the image to draw, clipped to the image.

position Point

Where the area's top-left corner goes; may be negative.

opacity float

Multiplies the image's alpha: 1 draws it as it is, 0 draws nothing.

Returns

RasterOps

This builder.

Exceptions

ArgumentOutOfRangeException

sourceArea does not overlap the image, or opacity is outside 0..1.

ArgumentNullException

image is null.

DropShadow(float, float, float, Rgba8, DropShadowOptions?)

Adds a drop shadow, as Skia's DropShadow image filter: the image's alpha, moved by (dx, dy), blurred by a Gaussian of sigma and filled with color, with the image drawn over it source-over. By default the canvas grows by 3σ plus the offset on the sides the shadow reaches past the image, so it is never cut off, and the step then cannot run inside Within(Rect, Action<RasterOps>); set Expand to false to keep the size. The shadow fades as if the image went on, transparent, past its edges.

public RasterOps DropShadow(float dx, float dy, float sigma, Rgba8 color, DropShadowOptions? options = null)

Parameters

dx float

How far the shadow moves right, in pixels (negative moves it left), at most 10000 either way.

dy float

How far the shadow moves down, in pixels, at most 10000 either way.

sigma float

The shadow's blur in pixels, from 0 (a hard shadow) to 1000.

color Rgba8

The shadow's color, with its alpha as the shadow's opacity.

options DropShadowOptions

A separate vertical blur, whether the canvas grows, whether only the shadow is kept, and the blur method; the defaults when null.

Returns

RasterOps

This builder.

Examples

// Skia's DropShadow: 6 px right and down, blurred with sigma 8, black at 55%; the canvas grows to hold the shadow.
using Raster shadowed = logo.Derive(ops => ops.DropShadow(6, 6, 8, new Rgba8(0, 0, 0, 140)), Ct);

// Keep the size, or keep only the shadow (as Skia's DropShadowOnly and ImageMagick's -shadow).
using Raster same = logo.Derive(ops => ops.DropShadow(4, 4, 3, Rgba8.Black, new DropShadowOptions { Expand = false }), Ct);
using Raster shadow = logo.Derive(ops => ops.DropShadow(0, 10, 12, Rgba8.Black, new DropShadowOptions { ShadowOnly = true, SigmaY = 6 }), Ct);

Exceptions

ArgumentOutOfRangeException

An offset is not finite or beyond 10000 either way, or sigma is outside 0..1000.

DropZeroDelay()

Removes frames with no delay, which no viewer shows, as ImageMagick's -layers RemoveZero; when every frame has none, nothing is removed.

public RasterOps DropZeroDelay()

Returns

RasterOps

This builder.

Examples

// Shrink a screen recording: merge frames that barely change, drop frames no viewer shows, then resample to 25 fps.
recording.Apply(ops => ops.MergeDuplicates(tolerance: 0.01f).DropZeroDelay().Retime(25), Ct);
recording.Save("recording.gif");

Duotone(Rgba8, Rgba8, LumaWeights)

Maps luminance onto the line from shadows to highlights. See Duotone(Rgba8, Rgba8, LumaWeights).

public RasterOps Duotone(Rgba8 shadows, Rgba8 highlights, LumaWeights weights = LumaWeights.Rec709)

Parameters

shadows Rgba8

The color for black.

highlights Rgba8

The color for white.

weights LumaWeights

The luma weights. Defaults to Rec709.

Returns

RasterOps

This builder.

Examples

// A two-color poster, then a check of how it reads to someone with deuteranopia.
raster.Apply(o => o.Duotone(Rgba8.FromHex("#1b2a49"), Rgba8.FromHex("#f2c14e")).Posterize(4, DitherMode.Ordered), Ct);
using Raster<Rgba8> preview = raster.Derive(o => o.SimulateColorBlindness(ColorVisionDeficiency.Deuteranopia), Ct);

// High contrast for low vision: gray, lightness inverted so dark becomes light, and more contrast.
using Raster<Rgba8> readable = raster.Derive(o => o.HighContrast(grayscale: true, ContrastInvert.Lightness, contrast: 0.3f), Ct);

Exceptions

ArgumentOutOfRangeException

weights is not defined.

Edges(EdgeOperator, bool)

Replaces each pixel with the strength of the edge through it: the gradient magnitude of op's kernels, scaled so a full black-to-white step gives 1, clamped to 0 to 1. Computed on premultiplied values with edges repeating the edge pixel; alpha is kept. In gray mode the pixels are first reduced to Rec. 709 luma and the magnitude is written as gray; otherwise each color channel gets its own.

public RasterOps Edges(EdgeOperator op = EdgeOperator.Sobel, bool grayscale = true)

Parameters

op EdgeOperator

The kernels; Sobel by default.

grayscale bool

Whether to find edges in the luma (gray result) rather than per channel.

Returns

RasterOps

This builder.

Examples

using Raster edges = photo.Derive(o => o.Edges(), Ct);                                  // Sobel, gray, 0 to 1
using Raster crisp = photo.Derive(o => o.Edges(EdgeOperator.Scharr), Ct);              // rotation-steady
using Raster colored = photo.Derive(o => o.Edges(EdgeOperator.Sobel, grayscale: false), Ct);  // per channel
using Raster lines = photo.Derive(o => o.Blur(1).Edges(EdgeOperator.Laplacian3x3), Ct); // second derivative

Exceptions

ArgumentOutOfRangeException

op is not defined.

Emboss(float, float, float, bool)

Embosses: blurs with a Gaussian of sigma, takes the slope toward the light at angle with a 3 × 3 Sobel stencil, scales it by depth and adds mid gray, so flat areas become 0.5 and slopes facing the light are lighter. This is the classic directional emboss; ImageMagick's -emboss is a different filter (a diagonal sharpening kernel followed by histogram equalization). Works on stored values; alpha is kept; edges repeat the edge pixel.

public RasterOps Emboss(float angle = 135, float depth = 1, float sigma = 1, bool grayscale = true)

Parameters

angle float

Where the light comes from, in degrees counterclockwise from the right with y up, as -shade's azimuth; 135 is the top left.

depth float

The strength of the relief, from 0 (flat gray) to 100; 1 maps a slope of one level per pixel to one level.

sigma float

The blur before the slope is taken, in pixels, from 0 (none) to 100.

grayscale bool

Whether the height is the Rec. 709 luminance and the result gray; otherwise each color channel is embossed on its own.

Returns

RasterOps

This builder.

Examples

// Gray relief: flat paper becomes mid gray, strokes rise toward the light at the top left.
using Raster embossed = page.Derive(ops => ops.Emboss(angle: 135, depth: 3, sigma: 1), Ct);

// Each color channel on its own, softer and deeper.
using Raster colored = page.Derive(ops => ops.Emboss(angle: 45, depth: 6, sigma: 2, grayscale: false), Ct);

Exceptions

ArgumentOutOfRangeException

angle is not finite, or depth or sigma is out of range.

EntropyCrop(float)

Crops to the busiest part of the image: the bounding box of the 16 × 16 tiles whose luminance entropy (the Shannon entropy of the tile's 256-level histogram, over 8 bits) reaches threshold. Frames are cropped to the union of their boxes; an image with no busy tile is left as it is.

public RasterOps EntropyCrop(float threshold = 0.5)

Parameters

threshold float

The entropy a tile needs, from 0 (every tile) to 1 (only uniformly random noise).

Returns

RasterOps

This builder.

Examples

// Keep the part with detail in it: flat tiles (a plain backdrop) fall away.
Rect busy = padded.EntropyBounds();
using Raster<Rgba8> cropped = padded.Derive(ops => ops.EntropyCrop(), Ct);

Remarks

Like Trim(float, Rgba8?), the size depends on the pixels, so it cannot run inside Within.

Exceptions

ArgumentOutOfRangeException

threshold is outside 0..1.

Equalize(EqualizeMode, bool)

Spreads the values evenly by histogram equalization: of L* by default, keeping a* and b* so colors hold, or of each channel as ImageMagick's -equalize. Measured like AutoLevel(bool, bool); a flat image is left alone.

public RasterOps Equalize(EqualizeMode mode = EqualizeMode.Luminance, bool perFrame = false)

Parameters

mode EqualizeMode

What is equalized. Defaults to Luminance.

perFrame bool

Whether each frame is measured alone.

Returns

RasterOps

This builder.

Examples

// Spread the lightness evenly without shifting colors, or each channel on its own as ImageMagick's -equalize.
using Raster<Rgba8> even = raster.Derive(o => o.Equalize(EqualizeMode.Luminance), Ct);
using Raster<Rgba8> strong = raster.Derive(o => o.Equalize(EqualizeMode.Channels), Ct);

Exceptions

ArgumentOutOfRangeException

mode is not defined.

Erode(int)

The minimum over the (2r + 1) square around each pixel: light areas shrink by radius pixels.

public RasterOps Erode(int radius)

Parameters

radius int

The radius in pixels, 0 (changes nothing) to 1000.

Returns

RasterOps

This builder.

Examples

using Raster grown = photo.Derive(o => o.Dilate(3), Ct);       // light areas grow 3 px: a 7 × 7 square
using Raster shrunk = photo.Derive(o => o.Erode(3), Ct);       // light areas shrink 3 px
using Raster wide = photo.Derive(o => o.Dilate(6, 1), Ct);     // 13 × 3, as Skia's dilate radii

Exceptions

ArgumentOutOfRangeException

radius is negative or above 1000.

Erode(int, int)

The minimum over the (2rx + 1) × (2ry + 1) rectangle around each pixel, as Skia's erode filter with those radii.

public RasterOps Erode(int radiusX, int radiusY)

Parameters

radiusX int

The horizontal radius in pixels, 0 to 1000.

radiusY int

The vertical radius in pixels, 0 to 1000.

Returns

RasterOps

This builder.

Examples

using Raster grown = photo.Derive(o => o.Dilate(3), Ct);       // light areas grow 3 px: a 7 × 7 square
using Raster shrunk = photo.Derive(o => o.Erode(3), Ct);       // light areas shrink 3 px
using Raster wide = photo.Derive(o => o.Dilate(6, 1), Ct);     // 13 × 3, as Skia's dilate radii

Exceptions

ArgumentOutOfRangeException

A radius is negative or above 1000.

FillColor(Rgba8, Rect?)

Sets every pixel in rect, or the whole image, to one color. Alpha is replaced, not blended.

public RasterOps FillColor(Rgba8 color, Rect? rect = null)

Parameters

color Rgba8

The color.

rect Rect?

The area to fill, which must lie inside the image, or null for the whole image.

Returns

RasterOps

This builder.

Exceptions

ArgumentOutOfRangeException

The rectangle is empty, or leaves the image when the chain runs.

Fit(int, int, ResampleFilter, ColorSpace, bool)

Scales the image to fit inside width × height with its aspect ratio kept, so one side matches the box and the other is no larger.

public RasterOps Fit(int width, int height, ResampleFilter filter = ResampleFilter.Bicubic, ColorSpace colorSpace = ColorSpace.Encoded, bool enlarge = true)

Parameters

width int

The width of the box, at least 1.

height int

The height of the box, at least 1.

filter ResampleFilter

The resampling kernel. Defaults to Bicubic.

colorSpace ColorSpace

Where pixels are blended. Defaults to Encoded.

enlarge bool

Whether an image smaller than the box is enlarged. Pass false for thumbnails.

Returns

RasterOps

This builder.

Exceptions

ArgumentOutOfRangeException

A size is less than 1.

Flatten(Rgba8, ColorSpace)

Composites the image over background and makes it opaque, as ImageMagick's -alpha remove does. The background's own alpha is ignored. Saving a JPEG with a Background option uses the same arithmetic.

public RasterOps Flatten(Rgba8 background, ColorSpace colorSpace = ColorSpace.Encoded)

Parameters

background Rgba8

The color to show through transparent pixels.

colorSpace ColorSpace

Where pixels are blended. Defaults to Encoded.

Returns

RasterOps

This builder.

Examples

using Raster alpha = photo.Derive(ops => ops.AlphaToGray(), Ct);                     // see the transparency as gray
using Raster cutout = photo.Derive(ops => ops.CopyAlpha(new Mask(matte)), Ct);       // a gray image becomes the alpha
using Raster onWhite = cutout.Derive(ops => ops.Flatten(Rgba8.White), Ct);           // composite onto white, opaque
using Raster luma = photo.Derive(ops => ops.LuminanceToAlpha(), Ct);                 // brightness becomes alpha

Exceptions

ArgumentOutOfRangeException

colorSpace is not defined.

FlipHorizontal()

Mirrors the image left to right. The pixels are moved exactly.

public RasterOps FlipHorizontal()

Returns

RasterOps

This builder.

FlipVertical()

Mirrors the image top to bottom. The pixels are moved exactly. Both flips together equal rotating by 180°.

public RasterOps FlipVertical()

Returns

RasterOps

This builder.

FloodFill(Point, Rgba8, float, Connectivity)

Fills the region connected to seed whose colors lie within tolerance of the seed's own with fill, as ImageMagick's -fuzz with -floodfill does. Each frame is filled from the same seed.

public RasterOps FloodFill(Point seed, Rgba8 fill, float tolerance = 0, Connectivity connectivity = Connectivity.Four)

Parameters

seed Point

Where the fill starts. It must lie inside the image, which is checked before any pixel changes.

fill Rgba8

The color to fill with.

tolerance float

The color distance, from 0 (the seed's exact color) to 1 (every color).

connectivity Connectivity

Whether the fill spreads to the four side neighbours (the default) or also the diagonal ones.

Returns

RasterOps

This builder.

Examples

// Fill the region around (10, 10) whose colors are close to its own, as a paint bucket does.
photo.Apply(ops => ops.FloodFill(new Point(10, 10), Rgba8.White, tolerance: 0.1f), Ct);

Remarks

The fill keeps a visited set of one bit a pixel, which counts against MaxTotalBytes.

Exceptions

ArgumentOutOfRangeException

tolerance is outside 0..1, connectivity is not defined, or the seed lies outside the image when the chain runs.

Gamma(float)

Raises red, green and blue to 1/gamma, as ImageMagick's -gamma: 2.2 brightens. See Gamma(float).

public RasterOps Gamma(float gamma)

Parameters

gamma float

The gamma, above 0.

Returns

RasterOps

This builder.

Examples

// A basic tone panel: levels with a midtone lift, a gentle S-curve, and a warm tint in the midtones. The three steps run as
// one pass over the pixels.
raster.Apply(o => o
    .Level(black: 0.04f, white: 0.96f, gamma: 1.1f)
    .SigmoidalContrast(4, midpoint: 0.5f)
    .Tint(new Rgba8(255, 200, 150, 255), 0.15f), Ct);
raster.Save("duck-toned.jpg");

Exceptions

ArgumentOutOfRangeException

gamma is not a positive finite number.

Glow(float, Rgba8, float, bool)

Adds a glow around the image's alpha shape, as an editor's outer glow layer style: the alpha, blurred with Fast, spread, and filled with color, drawn behind the image. With inner the glow comes in from the shape's edges instead and is drawn on the image, inside the shape only. It is exactly the chain written by hand: a layer made with a ColorMatrix(ColorMatrix) (the color, and alpha or 1 − alpha), Blur(float, ColorSpace, BlurMethod), Arithmetic(PixelMath, float, ColorChannels) multiplying alpha by 1 / (1 − spread) and Opacity(float) for the color's alpha, then drawn with DrawImage (the image over the layer, or the layer over the image with SourceAtop). An opaque image has no edge to glow from.

public RasterOps Glow(float sigma, Rgba8 color, float spread = 0, bool inner = false)

Parameters

sigma float

The glow's blur in pixels, from 0 to 1000.

color Rgba8

The glow's color, with its alpha as the glow's opacity.

spread float

How much the glow is hardened before it fades, from 0 (a plain blur) to just under 1 (a hard edge): the blurred alpha is divided by 1 − spread.

inner bool

Whether the glow is inside the shape instead of around it.

Returns

RasterOps

This builder.

Examples

// A soft yellow glow around the badge, behind it; spread hardens it before it fades.
using Raster outer = logo.Derive(ops => ops.Glow(8, new Rgba8(255, 210, 0, 230), spread: 0.3f), Ct);

// A white glow coming in from the badge's edges, inside it only.
using Raster inner = logo.Derive(ops => ops.Glow(5, Rgba8.White, inner: true), Ct);

Exceptions

ArgumentOutOfRangeException

sigma is outside 0..1000, or spread is outside 0 to under 1.

Grayscale(LumaWeights, float, ColorSpace)

Converts toward gray. By default the luma is taken from the stored, gamma-encoded values, as CSS filters do; pass Linear to weigh the colors in linear light, which keeps perceived brightness better.

public RasterOps Grayscale(LumaWeights mode = LumaWeights.Rec709, float amount = 1, ColorSpace colorSpace = ColorSpace.Encoded)

Parameters

mode LumaWeights

The luma weights. Defaults to Rec709.

amount float

0 changes nothing, 1 gives full gray.

colorSpace ColorSpace

Where the luma is taken. Defaults to Encoded.

Returns

RasterOps

This builder.

Exceptions

ArgumentOutOfRangeException

An argument is out of range.

HighContrast(bool, ContrastInvert, float)

Skia's high-contrast accessibility filter. See HighContrast(bool, ContrastInvert, float).

public RasterOps HighContrast(bool grayscale = false, ContrastInvert invert = ContrastInvert.None, float contrast = 0)

Parameters

grayscale bool

Whether colors become gray first.

invert ContrastInvert

How to invert. Defaults to None.

contrast float

The contrast, from −1 to 1.

Returns

RasterOps

This builder.

Examples

// A two-color poster, then a check of how it reads to someone with deuteranopia.
raster.Apply(o => o.Duotone(Rgba8.FromHex("#1b2a49"), Rgba8.FromHex("#f2c14e")).Posterize(4, DitherMode.Ordered), Ct);
using Raster<Rgba8> preview = raster.Derive(o => o.SimulateColorBlindness(ColorVisionDeficiency.Deuteranopia), Ct);

// High contrast for low vision: gray, lightness inverted so dark becomes light, and more contrast.
using Raster<Rgba8> readable = raster.Derive(o => o.HighContrast(grayscale: true, ContrastInvert.Lightness, contrast: 0.3f), Ct);

Exceptions

ArgumentOutOfRangeException

An argument is out of range.

HueRotate(float)

Rotates hue around the luma axis. See HueRotate(float).

public RasterOps HueRotate(float degrees)

Parameters

degrees float

The angle; 0 changes nothing.

Returns

RasterOps

This builder.

Exceptions

ArgumentOutOfRangeException

degrees is not finite.

InnerShadow(float, float, float, Rgba8)

Adds a shadow inside the image's alpha shape, as an editor's inner shadow layer style: everything outside the shape, moved by (dx, dy) rounded to whole pixels, blurred with Fast and filled with color, drawn on the image inside the shape only. Past the image's edges counts as outside, so a shape that touches an edge is shadowed from there too. It is exactly the chain written by hand: a layer made with a ColorMatrix(ColorMatrix) (the color, and 1 − alpha), Offset(int, int, EdgeMode, Rgba8) with the uncovered strips filled with the opaque color, Blur(float, ColorSpace, BlurMethod) and Opacity(float) for the color's alpha, then drawn over the image with SourceAtop.

public RasterOps InnerShadow(float dx, float dy, float sigma, Rgba8 color)

Parameters

dx float

How far the shadow moves right, in pixels (negative moves it left), at most 10000 either way.

dy float

How far the shadow moves down, in pixels, at most 10000 either way.

sigma float

The shadow's blur in pixels, from 0 (a hard shadow) to 1000.

color Rgba8

The shadow's color, with its alpha as the shadow's opacity.

Returns

RasterOps

This builder.

Examples

// A shadow inside the badge, cast from up and left, as a pressed button looks.
using Raster pressed = logo.Derive(ops => ops.InnerShadow(2, 3, 3, new Rgba8(0, 0, 0, 140)), Ct);

Exceptions

ArgumentOutOfRangeException

An offset is not finite or beyond 10000 either way, or sigma is outside 0..1000.

Invert(float)

Replaces red, green and blue with 1 − value, blended by amount. See Invert(float).

public RasterOps Invert(float amount = 1)

Parameters

amount float

0 changes nothing, 1 inverts fully.

Returns

RasterOps

This builder.

Exceptions

ArgumentOutOfRangeException

amount is outside 0..1.

Kuwahara(int, float)

Smooths while keeping edges, with the Kuwahara filter as ImageMagick's -kuwahara defines it: of the four (r + 1) squares that have the pixel as a corner, the one whose luma varies least gives the pixel its value, sampled at that square's middle. With sigma above 0 the image is blurred first with ImageMagick's kernel for radius x sigma (a Gaussian of σ out to the radius). The variances come from summed-area tables, so the cost does not grow with the radius.

public RasterOps Kuwahara(int radius = 3, float sigma = 0)

Parameters

radius int

The radius in pixels, from 1 to 100.

sigma float

The blur before, in pixels, from 0 (none) to 100.

Returns

RasterOps

This builder.

Examples

// Flattens texture into patches while keeping edges; a small blur first, as ImageMagick's -kuwahara 4x1.
using Raster smooth = photo.Derive(ops => ops.Kuwahara(4, sigma: 1), Ct);

Exceptions

ArgumentOutOfRangeException

radius is outside 1..100, or sigma is outside 0..100.

LensBlur(float, LensBlurOptions?)

Blurs as an out-of-focus lens does: each point spreads into the aperture's shape, a disc or a polygon of 5 to 12 blades, so small lights become discs, unlike a Gaussian's soft spots. Works in linear light by default, since a lens adds light; HighlightGain makes bright lights bloom. The cost does not grow with the radius: the blur runs by FFT on tiles. Edges repeat the edge pixel.

public RasterOps LensBlur(float radius, LensBlurOptions? options = null)

Parameters

radius float

The aperture's radius in pixels, from 0 (changes nothing) to 128.

options LensBlurOptions

The aperture and highlights, or null for a round aperture in linear light.

Returns

RasterOps

This builder.

Examples

// Blur the background like a wide-open lens with a six-blade aperture: bright points become hexagons.
LensBlurOptions lens = new() { Blades = 6, Rotation = 15, HighlightGain = 4, HighlightThreshold = 0.8f };
photo.Apply(ops => ops.Within(new Rect(0, 0, photo.Width, photo.Height / 3), o => o.LensBlur(12, lens)), Ct);

Exceptions

ArgumentOutOfRangeException

radius is negative, not finite or above 128, or an option is out of range: blades other than 0 or 5 to 12, a rotation that is not finite, a gain below 1 or not finite, a threshold outside 0..1, or an undefined color space.

Level(float, float, float, float, float)

Stretches black..white to outputBlack..outputWhite with a midtone gamma, as ImageMagick's -level and +level. See Level(float, float, float, float, float).

public RasterOps Level(float black = 0, float white = 1, float gamma = 1, float outputBlack = 0, float outputWhite = 1)

Parameters

black float

The input that becomes outputBlack, from 0 to 1.

white float

The input that becomes outputWhite, from 0 to 1.

gamma float

The midtone gamma, above 0: above 1 brightens.

outputBlack float

The output for black, from 0 to 1.

outputWhite float

The output for white, from 0 to 1.

Returns

RasterOps

This builder.

Examples

// A basic tone panel: levels with a midtone lift, a gentle S-curve, and a warm tint in the midtones. The three steps run as
// one pass over the pixels.
raster.Apply(o => o
    .Level(black: 0.04f, white: 0.96f, gamma: 1.1f)
    .SigmoidalContrast(4, midpoint: 0.5f)
    .Tint(new Rgba8(255, 200, 150, 255), 0.15f), Ct);
raster.Save("duck-toned.jpg");

Exceptions

ArgumentOutOfRangeException

A value is out of range, or black equals white.

Lighting(Light, LightingModel, LightingOptions?)

Lights the image as a height field with the SVG 1.1 lighting filters (feDiffuseLighting and feSpecularLighting), with Skia's parameters: the height channel (alpha by default) times SurfaceScale is a surface, its normal comes from SVG's Sobel stencils (with SVG's own stencils on the edges and corners), and the light's color is scaled by N·L (Diffuse(float)) or (N·H)^shininess (Specular(float, float)). The result is the light map (opaque for diffuse; for specular, alpha is the largest color channel), or, with Blend, the light map blended onto the image, which keeps its alpha. Light is computed on stored values, as Skia does in its working space; SVG's default of linear light is not applied.

public RasterOps Lighting(Light light, LightingModel model, LightingOptions? options = null)

Parameters

light Light

The light: DistantLight, PointLight or SpotLight, in pixel coordinates.

model LightingModel

How the surface reflects: Diffuse(float) or Specular(float, float); the default value is a diffuse model with kd 0, which is black.

options LightingOptions

The surface scale, height channel and blend; null for the defaults.

Returns

RasterOps

This builder.

Examples

// A photo's luminance as the height: light areas stand up. SurfaceScale is the height of white, in pixels.
LightingOptions relief = new() { Height = HeightSource.Luminance, SurfaceScale = 6 };
using Raster lit = photo.Derive(ops => ops.Lighting(new DistantLight(300, 50), LightingModel.Diffuse(), relief), Ct);

// The same light blended onto the photo instead of replacing it.
using Raster shaded = photo.Derive(ops => ops.Lighting(new DistantLight(300, 50), LightingModel.Diffuse(1.3f), relief with { Blend = BlendMode.Overlay }), Ct);

Exceptions

ArgumentNullException

light is null.

LocalContrast(float, float)

Brings out local detail, as ImageMagick's -local-contrast: L* is pushed away from a wide blur of itself by strength, so large-scale light changes less than detail. a* and b* are kept.

public RasterOps LocalContrast(float radius = 0.1, float strength = 0.125)

Parameters

radius float

The blur radius as a fraction of 20% of the longer side, above 0 and at most 1; the default 0.1 blurs over 2%.

strength float

How strongly detail is raised, 0 or more; the default 0.125 is ImageMagick's 12.5%.

Returns

RasterOps

This builder.

Examples

// Bring out detail in a flat, foggy landscape: CLAHE on 8 × 8 tiles, then ImageMagick's local contrast.
raster.Apply(o => o.Clahe(tilesX: 8, tilesY: 8, clipLimit: 2).LocalContrast(radius: 0.1f, strength: 0.15f), Ct);

Remarks

Two lightness planes, 4 bytes a pixel each, count against MaxTotalBytes. Inside a rectangle or mask, the scope is its image.

Exceptions

ArgumentOutOfRangeException

An argument is out of range.

LuminanceToAlpha()

Sets alpha to the luminance of the stored values, 0.212656 R + 0.715158 G + 0.072186 B (the Rec. 709 weights as ImageMagick derives them from the sRGB primaries), and keeps the color, exactly as ImageMagick's -alpha copy does; on a gray raster this turns the gray into alpha. A color matrix, so it fuses with neighbouring color steps. To also black out the color, as Skia's luma filter does, follow it with OnChannels(ColorChannels.Color, o => o.FillColor(Rgba8.Black)).

public RasterOps LuminanceToAlpha()

Returns

RasterOps

This builder.

Examples

using Raster alpha = photo.Derive(ops => ops.AlphaToGray(), Ct);                     // see the transparency as gray
using Raster cutout = photo.Derive(ops => ops.CopyAlpha(new Mask(matte)), Ct);       // a gray image becomes the alpha
using Raster onWhite = cutout.Derive(ops => ops.Flatten(Rgba8.White), Ct);           // composite onto white, opaque
using Raster luma = photo.Derive(ops => ops.LuminanceToAlpha(), Ct);                 // brightness becomes alpha

MakeTransparent(Rgba8, float, float)

Makes pixels within tolerance of color transparent, keeping their color, by ImageMagick's -fuzz distance, so with no softness the result equals ImageMagick's -fuzz with -transparent. softness ramps alpha up over the next band of distance, for antialiased edges and green screens.

public RasterOps MakeTransparent(Rgba8 color, float tolerance = 0, float softness = 0)

Parameters

color Rgba8

The color to remove, with its alpha (opaque for most keys).

tolerance float

The color distance that becomes fully transparent, from 0 (the exact color) to 1 (every color).

softness float

The band of distance beyond the tolerance over which alpha returns to full; 0 for a hard edge.

Returns

RasterOps

This builder.

Examples

// Remove a green screen: within 0.15 of pure green becomes transparent, and alpha ramps back over the next 0.1 for soft edges.
photo.Apply(ops => ops.MakeTransparent(new Rgba8(0, 177, 64, 255), tolerance: 0.15f, softness: 0.1f), Ct);

Exceptions

ArgumentOutOfRangeException

tolerance is outside 0..1, or softness is negative or not finite.

Median(int, bool)

Replaces each channel with its median over the (2r + 1) square around the pixel, as ImageMagick's -statistic Median does: removes salt-and-pepper noise while keeping edges. Works on stored values, so 8 and 16-bit results are exact; edges repeat the edge pixel. The cost per pixel does not grow with the radius on byte types.

public RasterOps Median(int radius, bool includeAlpha = false)

Parameters

radius int

The radius in pixels, from 0 (changes nothing) to 100.

includeAlpha bool

Whether alpha gets its own median too; by default it is kept.

Returns

RasterOps

This builder.

Examples

// Salt-and-pepper noise disappears and edges stay sharp; radius 2 is a 5 × 5 window.
photo.Apply(ops => ops.Median(2), Ct);

Exceptions

ArgumentOutOfRangeException

radius is negative or above 100.

MergeDuplicates(float)

Removes each frame equal to the one before it and adds its delay to the frame that stays, as ImageMagick's -layers RemoveDups: a screen recording that sat still for seconds becomes one long frame. What is on screen at every moment is unchanged. Removed frames take their metadata with them.

public RasterOps MergeDuplicates(float tolerance = 0)

Parameters

tolerance float

How far a pixel may differ and still count as equal, by ImageMagick's -fuzz distance, from 0 (the default, exact) to 1.

Returns

RasterOps

This builder.

Examples

// Shrink a screen recording: merge frames that barely change, drop frames no viewer shows, then resample to 25 fps.
recording.Apply(ops => ops.MergeDuplicates(tolerance: 0.01f).DropZeroDelay().Retime(25), Ct);
recording.Save("recording.gif");

Exceptions

ArgumentOutOfRangeException

tolerance is outside 0 to 1.

Morphology(MorphologyOp, StructuringElement, int)

Flat morphology: each channel's maximum (dilate) or minimum (erode) over shape around each pixel, or one of their combinations, as ImageMagick's -morphology. Works on stored values, alpha included, so 8 and 16-bit results are exact; Gradient, TopHat and BottomHat keep alpha. Edges repeat the edge pixel. Rectangles cost the same per pixel at any size; other shapes grow with their height.

public RasterOps Morphology(MorphologyOp op, StructuringElement shape, int iterations = 1)

Parameters

op MorphologyOp

The operation.

shape StructuringElement

The structuring element.

iterations int

How many times each dilation and erosion runs, 1 to 100: Open with 2 erodes twice, then dilates twice.

Returns

RasterOps

This builder.

Examples

// Close removes dark specks smaller than the shape (dust and toner dots on the paper) and keeps strokes wider than it;
// Open does the opposite, for light pinholes in dark areas.
using Raster<Gray8> scan = Raster.Load<Gray8>("scan.tif");
scan.Apply(o => o.Morphology(MorphologyOp.Close, StructuringElement.Disk(1)), Ct);

// The outlines of shapes, and the light details smaller than a disk.
using Raster outlines = scan.Derive(o => o.Morphology(MorphologyOp.Gradient, StructuringElement.Square(1)), Ct);
using Raster details = scan.Derive(o => o.Morphology(MorphologyOp.TopHat, StructuringElement.Disk(5)), Ct);

Exceptions

ArgumentOutOfRangeException

op is not defined, or iterations is outside 1 to 100.

MotionBlur(float, float, float, bool)

Streaks the image along a direction, as a camera moving during the exposure, with ImageMagick's -motion-blur meaning: each pixel averages 2⌈radius⌉ + 1 samples one pixel apart, from itself toward angle, weighted by a Gaussian of sigma from the pixel. Samples between pixels are read bilinearly; edges repeat the edge pixel.

public RasterOps MotionBlur(float radius, float sigma, float angle, bool centered = false)

Parameters

radius float

Half the streak's length in pixels, from 0 (changes nothing) to 127.

sigma float

How fast the weights fall along the streak, above 0.

angle float

The direction in degrees, clockwise from the x axis (y points down).

centered bool

Whether the streak runs both ways from the pixel, instead of only toward angle.

Returns

RasterOps

This builder.

Examples

using Raster moving = photo.Derive(ops => ops.MotionBlur(radius: 12, sigma: 4, angle: 15), Ct);  // a streak toward 15°
using Raster spinning = photo.Derive(ops => ops.RotationalBlur(8), Ct);                          // 8° around the center
using Raster zooming = photo.Derive(ops => ops.ZoomBlur(0.2f, new System.Numerics.Vector2(320, 200)), Ct);

Exceptions

ArgumentOutOfRangeException

An argument is not finite or out of range.

Normalize(bool, bool)

ContrastStretch(float, float, bool, bool) at ImageMagick's -normalize points: the darkest 2% of the pixels become black and the lightest 1% white.

public RasterOps Normalize(bool linked = true, bool perFrame = false)

Parameters

linked bool

Whether the channels share one pair of points (the default) or each gets its own.

perFrame bool

Whether each frame is measured alone.

Returns

RasterOps

This builder.

Examples

// Fix a dull scan: stretch it to the full range, clipping the darkest 2% and lightest 1%, then set the midtones.
raster.Apply(o => o.Normalize().AutoGamma(), Ct);

// Or stretch each channel on its own, which also removes a cast.
raster.Apply(o => o.AutoLevel(linked: false), Ct);

// Measured steps measure only their scope: here the sky alone is stretched.
raster.Apply(o => o.Within(new Rect(0, 0, raster.Width, raster.Height / 3), sky => sky.AutoLevel()), Ct);

Offset(int, int, EdgeMode, Rgba8)

Moves the image by whole pixels, right by dx and down by dy, with no resampling. What leaves one side comes in at the other with Wrap (the default, ImageMagick's -roll); with Transparent the uncovered strip is background, as Skia's Offset filter leaves it.

public RasterOps Offset(int dx, int dy, EdgeMode edges = EdgeMode.Wrap, Rgba8 background = default)

Parameters

dx int

The horizontal move, in pixels.

dy int

The vertical move, in pixels.

edges EdgeMode

What fills the uncovered strips.

background Rgba8

The color of uncovered strips under Transparent.

Returns

RasterOps

This builder.

Examples

// Roll the image half way round, so its edges meet in the middle, which shows whether it tiles seamlessly.
using Raster rolled = photo.Derive(ops => ops.Offset(photo.Width / 2, photo.Height / 2), Ct);

// Fill a 1920 × 1080 wallpaper with copies of the duck's head.
using Raster wallpaper = photo.Derive(ops => ops.Tile(1920, 1080, source: new Rect(380, 60, 160, 160)), Ct);

Exceptions

ArgumentOutOfRangeException

edges is not defined.

OilPaint(int, int)

Makes the image look painted, as ImageMagick's -paint does: around each pixel, in the (2r + 1) square, pixels are sorted into levels bins by intensity, and the pixel becomes a pixel of the most common bin, unchanged. With 256 levels on 8-bit images the result is ImageMagick's exactly; fewer levels make larger, flatter strokes. Works on stored values; the histogram slides along each row, so a larger radius costs little more per pixel than the window's size.

public RasterOps OilPaint(int radius = 3, int levels = 20)

Parameters

radius int

The brush radius in pixels, from 1 to 50.

levels int

The number of intensity bins, from 2 to 256.

Returns

RasterOps

This builder.

Examples

using Raster painted = photo.Derive(ops => ops.OilPaint(radius: 4, levels: 20), Ct);
using Raster magick = photo.Derive(ops => ops.OilPaint(3, levels: 256), Ct);   // ImageMagick's -paint 3, pixel for pixel

Exceptions

ArgumentOutOfRangeException

radius is outside 1..50, or levels is outside 2..256.

OnChannels(ColorChannels, Action<RasterOps>)

Runs the operations build adds and keeps their result only in channels, as ImageMagick's -channel does: OnChannels(ColorChannels.Alpha, o => o.Blur(2)) softens only the alpha. Channels are the working RGBA, with a gray raster's red, green and blue its gray value. Color steps that fuse into one matrix (Brightness, ShuffleChannels, a color matrix) become that matrix with the other channels left as they are, so they cost nothing extra; other chains run once and the other channels are restored. Combines with Within in either order.

public RasterOps OnChannels(ColorChannels channels, Action<RasterOps> build)

Parameters

channels ColorChannels

The channels the result is kept in.

build Action<RasterOps>

Adds the operations. Each must keep the size, as in Within(Rect, Action<RasterOps>).

Returns

RasterOps

This builder.

Examples

// Soften only the alpha; invert only the red.
photo.Apply(ops => ops
    .OnChannels(ColorChannels.Alpha, o => o.Blur(2))
    .OnChannels(ColorChannels.Red, o => o.Invert()), Ct);

Exceptions

ArgumentOutOfRangeException

channels holds values that are not channels.

ArgumentNullException

build is null.

ArgumentException

An operation inside would change the size or the color encoding; the message names it.

Opacity(float)

Multiplies alpha by amount. Has no effect on pixel types without alpha.

public RasterOps Opacity(float amount)

Parameters

amount float

0 makes the image transparent, 1 changes nothing.

Returns

RasterOps

This builder.

Exceptions

ArgumentOutOfRangeException

amount is outside 0..1.

Pad(int, int, Rgba8, Anchor)

Enlarges the canvas to width × height, placing the image at anchor and filling the new area with background.

public RasterOps Pad(int width, int height, Rgba8 background = default, Anchor anchor = Anchor.Center)

Parameters

width int

The new width, at least the image's width.

height int

The new height, at least the image's height.

background Rgba8

The color of the new area. Defaults to transparent black.

anchor Anchor

Where the image sits on the new canvas. Defaults to the center, rounding toward the top left.

Returns

RasterOps

This builder.

Exceptions

ArgumentOutOfRangeException

A size is less than 1, or smaller than the image when the chain runs (use Crop(Rect) to shrink), or anchor is not defined.

Pixelate(int, Point)

Pixelates: the image is cut into square cells, and every pixel of a cell becomes the cell's average, taken on premultiplied values so transparent pixels do not darken it. Cells start at origin and repeat every cellSize pixels both ways; cells cut by the image's edges average the pixels they hold. Inside Within(Rect, Action<RasterOps>) cells stay on the image's grid and average whole cells, reading past the area.

public RasterOps Pixelate(int cellSize, Point origin = default)

Parameters

cellSize int

The side of a cell in pixels, from 1 (changes nothing) to 4096.

origin Point

Where a cell's top-left corner falls, in the raster's coordinates; the grid repeats from there both ways.

Returns

RasterOps

This builder.

Examples

// 12-pixel cells, each the average of what it covers.
using Raster blocks = photo.Derive(ops => ops.Pixelate(12), Ct);

Exceptions

ArgumentOutOfRangeException

cellSize is outside 1..4096.

Posterize(int, DitherMode)

Rounds red, green and blue to levels evenly spaced values each, as ImageMagick's -posterize, optionally dithered: ordered dithering adds a fixed 8 × 8 pattern before rounding, Floyd–Steinberg spreads each value's rounding error to its neighbours. Without dithering it is Posterize(int).

public RasterOps Posterize(int levels, DitherMode dither = DitherMode.None)

Parameters

levels int

Values per channel, from 2 to 256.

dither DitherMode

The dithering. Defaults to None.

Returns

RasterOps

This builder.

Examples

// A two-color poster, then a check of how it reads to someone with deuteranopia.
raster.Apply(o => o.Duotone(Rgba8.FromHex("#1b2a49"), Rgba8.FromHex("#f2c14e")).Posterize(4, DitherMode.Ordered), Ct);
using Raster<Rgba8> preview = raster.Derive(o => o.SimulateColorBlindness(ColorVisionDeficiency.Deuteranopia), Ct);

// High contrast for low vision: gray, lightness inverted so dark becomes light, and more contrast.
using Raster<Rgba8> readable = raster.Derive(o => o.HighContrast(grayscale: true, ContrastInvert.Lightness, contrast: 0.3f), Ct);

Exceptions

ArgumentOutOfRangeException

levels is outside 2 to 256, or dither is not defined.

QuadDistort(Vector2, Vector2, Vector2, Vector2, TransformOptions?)

Moves the image's four corners to four points, bending it in perspective: ImageMagick's Perspective distort. Corners are pixel edges, so the top-right corner of a 100-pixel-wide image is (100, 0).

public RasterOps QuadDistort(Vector2 topLeft, Vector2 topRight, Vector2 bottomRight, Vector2 bottomLeft, TransformOptions? options = null)

Parameters

topLeft Vector2

Where the top-left corner goes.

topRight Vector2

Where the top-right corner goes.

bottomRight Vector2

Where the bottom-right corner goes.

bottomLeft Vector2

Where the bottom-left corner goes.

options TransformOptions

The filter, edges, background and viewport, or null for the defaults.

Returns

RasterOps

This builder.

Examples

// Pin the image onto a trapezoid, as if it lay on a table: its corners go to these four points (pixel edges, so the
// image's own corners are (0, 0) and (640, 480)).
using Raster onTable = photo.Derive(ops => ops.QuadDistort(new(200, 0), new(440, 0), new(640, 300), new(0, 300)), Ct);

Exceptions

ArgumentException

A point is not finite, three of them lie on one line, or the viewport is empty.

Quantize(PaletteOptions?)

Reduces each frame to at most MaxColors colors, as a palette format would, but keeps the pixel type: an Rgba16 image stays Rgba16 with at most that many distinct values. Each frame gets its own palette (its own colors when they fit, Wu's quantizer otherwise) unless FixedPalette fixes one, and is dithered as the options say. Use ToIndexed(PaletteOptions?, int) for the indices themselves.

public RasterOps Quantize(PaletteOptions? options = null)

Parameters

options PaletteOptions

The color limit, dithering, refinement and matching, or null for Default.

Returns

RasterOps

This builder.

Examples

// A 16-color poster with Atkinson's dither; the pixel type stays as it was.
photo.Apply(ops => ops.Quantize(new PaletteOptions { MaxColors = 16, Dither = DitherMode.Atkinson }), Ct);
photo.Save("poster.png");

Reach(Action<RasterOps>)

Returns how far a chain reads past each output pixel: the sum of its steps' reaches. Pixel-by-pixel steps (color matrices, arithmetic, color filters, blending a color, keying) reach nothing; blurs, convolutions, morphology, glows and drop shadows reach as far as their kernels and offsets. A step whose reach is unbounded or unknown (a resize, a geometric transform, a histogram step, a custom IRasterOperation) makes the whole chain's reach unknown.

public static OperationReach? Reach(Action<RasterOps> build)

Parameters

build Action<RasterOps>

Records the chain, as for Apply(Action<RasterOps>, CancellationToken); it is recorded, not run.

Returns

OperationReach?

The reach, or null when some step's reach is unknown.

Examples

// How far a chain reads around each pixel: run it on a part of a picture padded by this much, and the part comes out as it
// would in the whole picture.
OperationReach? reach = RasterOps.Reach(ops => ops.Blur(3).Glow(4, Rgba8.White));
if (reach is OperationReach r)
{
    Console.WriteLine($"pad by {r.X} × {r.Y}");
}

// A resize reads the whole image: its reach is unknown.
Assert.Null(RasterOps.Reach(ops => ops.Resize(100, 100)));

Exceptions

ArgumentNullException

build is null.

Remap(Palette, DitherMode, float)

Maps every pixel onto palette, as ImageMagick's -remap: a brand palette, a display's inks, or the palette of another image. The pixel type is kept, and so is each pixel's alpha when the palette is opaque.

public RasterOps Remap(Palette palette, DitherMode dither = DitherMode.FloydSteinberg, float strength = 1)

Parameters

palette Palette

The palette.

dither DitherMode

How the error is spread. Defaults to FloydSteinberg.

strength float

How strongly, from 0 (the nearest color) to 1 (the default).

Returns

RasterOps

This builder.

Examples

// Map onto a fixed palette: here an e-ink display's six inks.
Palette inks = Palette.FromColors([Rgba8.Black, Rgba8.White, new(255, 0, 0, 255), new(255, 255, 0, 255), new(0, 0, 255, 255), new(0, 255, 0, 255)]);
photo.Apply(ops => ops.Remap(inks, DitherMode.FloydSteinberg), Ct);

Exceptions

ArgumentNullException

palette is null.

ArgumentOutOfRangeException

dither is not defined, or strength is outside 0 to 1.

ReplaceColor(Rgba8, Rgba8, float)

Replaces every pixel within tolerance of from with to, by ImageMagick's -fuzz distance, as ImageMagick's -fill with -opaque does.

public RasterOps ReplaceColor(Rgba8 from, Rgba8 to, float tolerance = 0)

Parameters

from Rgba8

The color to replace, with its alpha.

to Rgba8

The color to put in its place.

tolerance float

The color distance, from 0 (the exact color) to 1 (every color).

Returns

RasterOps

This builder.

Examples

photo.Apply(ops => ops.ReplaceColor(Rgba8.White, new Rgba8(250, 245, 230, 255), tolerance: 0.05f), Ct);

Exceptions

ArgumentOutOfRangeException

tolerance is outside 0..1.

Resize(int, int, ResampleFilter, ColorSpace)

Resizes the image to width × height. Pass 0 for one dimension to keep the aspect ratio. Resizing to the current size changes nothing. A Crop(Rect) just before a resize is merged into it, so only the crop rectangle is read.

public RasterOps Resize(int width, int height, ResampleFilter filter = ResampleFilter.Bicubic, ColorSpace colorSpace = ColorSpace.Encoded)

Parameters

width int

The new width, or 0 to derive it from height.

height int

The new height, or 0 to derive it from width.

filter ResampleFilter

The resampling kernel. Defaults to Bicubic.

colorSpace ColorSpace

Where pixels are blended. Defaults to Encoded.

Returns

RasterOps

This builder.

Exceptions

ArgumentOutOfRangeException

A size is negative, or both are 0.

Retime(double)

Resamples the animation to framesPerSecond, as ffmpeg's fps filter: fixed ticks, each showing the frame visible when it starts, with neighbours that show the same frame merged. No frame is ever added and the total duration is kept; frames shorter than a tick may be dropped. A rate at or above the animation's own changes nothing. Browsers show GIF delays under 20 ms as 100 ms, so 50 is the useful ceiling for GIF.

public RasterOps Retime(double framesPerSecond)

Parameters

framesPerSecond double

The rate, more than 0 and at most 1,000.

Returns

RasterOps

This builder.

Examples

// Shrink a screen recording: merge frames that barely change, drop frames no viewer shows, then resample to 25 fps.
recording.Apply(ops => ops.MergeDuplicates(tolerance: 0.01f).DropZeroDelay().Retime(25), Ct);
recording.Save("recording.gif");

Exceptions

ArgumentOutOfRangeException

framesPerSecond is out of range or not finite.

Reverse()

Plays the animation backwards; each frame keeps its delay and metadata.

public RasterOps Reverse()

Returns

RasterOps

This builder.

Examples

// Twice as fast, then backwards: a boomerang clip is the clip followed by its reverse.
using Raster fast = clip.Derive(ops => ops.ChangeSpeed(2), Ct);
using Raster backwards = fast.Derive(ops => ops.Reverse(), Ct);

Rotate(float, ResampleFilter, Rgba8)

Rotates the image clockwise. Multiples of 90° move pixels exactly; other angles resample onto a canvas the size of the rotated bounding box, with the corners filled with background.

public RasterOps Rotate(float degrees, ResampleFilter filter = ResampleFilter.Bicubic, Rgba8 background = default)

Parameters

degrees float

The angle, clockwise. Negative angles turn counter-clockwise, so −90 equals 270.

filter ResampleFilter

The resampling kernel for angles that are not multiples of 90°. Defaults to Bicubic.

background Rgba8

The color of the canvas outside the rotated image. Defaults to transparent black.

Returns

RasterOps

This builder.

Exceptions

ArgumentOutOfRangeException

degrees is not finite, or filter is not defined.

RotationalBlur(float, Vector2?)

Spins the image around center, as ImageMagick's -rotational-blur: each pixel averages samples along an arc of degrees centered on it, about one per pixel of arc (at most 256), read bilinearly. Pixels far from the center blur most; the center does not move.

public RasterOps RotationalBlur(float degrees, Vector2? center = null)

Parameters

degrees float

The arc in degrees, from 0 (changes nothing) to 360.

center Vector2?

The center in pixels, pixel centers at +0.5, or null for the image's center.

Returns

RasterOps

This builder.

Examples

using Raster moving = photo.Derive(ops => ops.MotionBlur(radius: 12, sigma: 4, angle: 15), Ct);  // a streak toward 15°
using Raster spinning = photo.Derive(ops => ops.RotationalBlur(8), Ct);                          // 8° around the center
using Raster zooming = photo.Derive(ops => ops.ZoomBlur(0.2f, new System.Numerics.Vector2(320, 200)), Ct);

Exceptions

ArgumentOutOfRangeException

degrees is outside 0..360, or the center is not finite.

Saturation(float)

Mixes each color with its luma: 1 changes nothing, 0 gives gray, above 1 oversaturates. See Saturation(float).

public RasterOps Saturation(float amount)

Parameters

amount float

The factor, 0 or more.

Returns

RasterOps

This builder.

Exceptions

ArgumentOutOfRangeException

amount is negative or not finite.

Sepia(float)

Tints toward sepia. See Sepia(float).

public RasterOps Sepia(float amount = 1)

Parameters

amount float

0 changes nothing, 1 gives full sepia.

Returns

RasterOps

This builder.

Exceptions

ArgumentOutOfRangeException

amount is outside 0..1.

Shade(float, float, bool)

Shades the image as ImageMagick's -shade does: the luminance is a height field, and each pixel becomes the Lambertian shading of its surface normal by a distant light at azimuth and elevation, a gray relief; with colored the shading multiplies the image's own colors instead, as +shade does. Identical to ImageMagick at 8 bits; alpha is kept; edges repeat the edge pixel.

public RasterOps Shade(float azimuth = 30, float elevation = 30, bool colored = false)

Parameters

azimuth float

The light's direction in the image plane, in degrees counterclockwise from the right with y up, as ImageMagick's.

elevation float

The light's angle above the image plane, in degrees, from 0 to 90; flat areas get sin(elevation).

colored bool

Whether the shading multiplies the colors (+shade) rather than replacing them with gray (-shade).

Returns

RasterOps

This builder.

Examples

// ImageMagick's -shade 30x30 (identical at 8 bits) and +shade 120x45.
using Raster gray = photo.Derive(ops => ops.Shade(azimuth: 30, elevation: 30), Ct);
using Raster colored = photo.Derive(ops => ops.Shade(azimuth: 120, elevation: 45, colored: true), Ct);

Exceptions

ArgumentOutOfRangeException

azimuth is not finite, or elevation is not from 0 to 90.

Sharpen(float, float)

Sharpens with an unsharp mask: out = src + amount × (src − Blur(sigma)).

public RasterOps Sharpen(float amount = 1, float sigma = 1)

Parameters

amount float

How strongly to sharpen; 0 changes nothing. Defaults to 1.

sigma float

The blur radius that sets which details are sharpened, in pixels. Defaults to 1.

Returns

RasterOps

This builder.

Exceptions

ArgumentOutOfRangeException

An argument is negative or not finite, or sigma is 0 or above 100.

ShuffleChannels(ChannelSource, ChannelSource, ChannelSource, ChannelSource)

Rearranges the working RGBA channels: each output channel takes the input channel, or the constant, its argument names. Swapping red and blue, or copying green into alpha, is one color matrix, so it fuses with neighbouring color steps into one pass.

public RasterOps ShuffleChannels(ChannelSource red, ChannelSource green, ChannelSource blue, ChannelSource alpha)

Parameters

red ChannelSource

Where the output's red comes from.

green ChannelSource

Where the output's green comes from.

blue ChannelSource

Where the output's blue comes from.

alpha ChannelSource

Where the output's alpha comes from. Has no effect on pixel types without alpha.

Returns

RasterOps

This builder.

Examples

// Swap red and blue: one color matrix, fused with the other color steps into one pass.
photo.Apply(ops => ops
    .ShuffleChannels(ChannelSource.Blue, ChannelSource.Green, ChannelSource.Red, ChannelSource.Alpha)
    .Brightness(1.05f), Ct);

Exceptions

ArgumentOutOfRangeException

A source is not defined.

SigmoidalContrast(float, float, bool)

Adds (or removes) contrast with ImageMagick's scaled logistic curve, which never clips. See SigmoidalContrast(float, float, bool).

public RasterOps SigmoidalContrast(float contrast, float midpoint = 0.5, bool increase = true)

Parameters

contrast float

How steep the curve is, 0 or more: 3 is gentle, 10 strong.

midpoint float

Where the curve is steepest, from 0 to 1. Defaults to 0.5.

increase bool

Whether contrast increases (the default) or decreases.

Returns

RasterOps

This builder.

Examples

// A basic tone panel: levels with a midtone lift, a gentle S-curve, and a warm tint in the midtones. The three steps run as
// one pass over the pixels.
raster.Apply(o => o
    .Level(black: 0.04f, white: 0.96f, gamma: 1.1f)
    .SigmoidalContrast(4, midpoint: 0.5f)
    .Tint(new Rgba8(255, 200, 150, 255), 0.15f), Ct);
raster.Save("duck-toned.jpg");

Exceptions

ArgumentOutOfRangeException

An argument is out of range.

SimulateColorBlindness(ColorVisionDeficiency, float)

Shows how the image looks with a color vision deficiency. See SimulateColorBlindness(ColorVisionDeficiency, float).

public RasterOps SimulateColorBlindness(ColorVisionDeficiency kind, float severity = 1)

Parameters

kind ColorVisionDeficiency

The deficiency.

severity float

From 0 (normal vision) to 1. Defaults to 1.

Returns

RasterOps

This builder.

Examples

// A two-color poster, then a check of how it reads to someone with deuteranopia.
raster.Apply(o => o.Duotone(Rgba8.FromHex("#1b2a49"), Rgba8.FromHex("#f2c14e")).Posterize(4, DitherMode.Ordered), Ct);
using Raster<Rgba8> preview = raster.Derive(o => o.SimulateColorBlindness(ColorVisionDeficiency.Deuteranopia), Ct);

// High contrast for low vision: gray, lightness inverted so dark becomes light, and more contrast.
using Raster<Rgba8> readable = raster.Derive(o => o.HighContrast(grayscale: true, ContrastInvert.Lightness, contrast: 0.3f), Ct);

Exceptions

ArgumentOutOfRangeException

An argument is out of range.

Skew(float, float, TransformOptions?)

Shears the image about its center: vertical lines lean by degreesX (x moves by y × tan) and horizontal lines by degreesY. The canvas grows to the sheared bounds unless a viewport is given.

public RasterOps Skew(float degreesX, float degreesY, TransformOptions? options = null)

Parameters

degreesX float

The horizontal shear angle, strictly between −90 and 90.

degreesY float

The vertical shear angle, strictly between −90 and 90.

options TransformOptions

The filter, edges, background and viewport, or null for the defaults.

Returns

RasterOps

This builder.

Examples

// Lean a banner 12° to the right, filling the uncovered corners with white; the canvas grows to fit.
using Raster banner = photo.Derive(ops => ops.Skew(12, 0, new TransformOptions { Background = Rgba8.White }), Ct);

// Skew a region in place: a viewport the size of the area keeps it the same size inside Within.
using Raster region = photo.Derive(ops => ops.Within(new Rect(100, 100, 200, 120), s => s.Skew(0, 8, new TransformOptions { Viewport = new Rect(0, 0, 200, 120) })), Ct);

Exceptions

ArgumentOutOfRangeException

An angle is not finite or not strictly between −90 and 90.

ArgumentException

The two shears together collapse the image (tan x × tan y = 1), or the viewport is empty.

Solarize(float)

Inverts red, green and blue values above threshold, as ImageMagick's -solarize. See Solarize(float).

public RasterOps Solarize(float threshold = 0.5)

Parameters

threshold float

The level, from 0 to 1. Defaults to 0.5.

Returns

RasterOps

This builder.

Examples

// A two-color poster, then a check of how it reads to someone with deuteranopia.
raster.Apply(o => o.Duotone(Rgba8.FromHex("#1b2a49"), Rgba8.FromHex("#f2c14e")).Posterize(4, DitherMode.Ordered), Ct);
using Raster<Rgba8> preview = raster.Derive(o => o.SimulateColorBlindness(ColorVisionDeficiency.Deuteranopia), Ct);

// High contrast for low vision: gray, lightness inverted so dark becomes light, and more contrast.
using Raster<Rgba8> readable = raster.Derive(o => o.HighContrast(grayscale: true, ContrastInvert.Lightness, contrast: 0.3f), Ct);

Exceptions

ArgumentOutOfRangeException

threshold is outside 0..1.

Spread(float, ulong)

Scatters pixels, as ImageMagick's -spread does: each pixel takes the value of a random pixel at most radius away (a whole-pixel offset in the disc; edges repeat the edge pixel). The offsets come from a hash of the seed and the pixel's position, so the same seed gives the same pixels on any machine and any number of cores.

public RasterOps Spread(float radius, ulong seed = 0)

Parameters

radius float

The farthest a value moves, in pixels, from 0 to 1000; below 1 nothing moves.

seed ulong

Picks the pattern; the same seed gives the same result.

Returns

RasterOps

This builder.

Examples

// Frosted glass: every pixel takes a neighbour's value up to 3 pixels away; the same seed gives the same result.
using Raster frosted = photo.Derive(ops => ops.Spread(3, seed: 42), Ct);

Exceptions

ArgumentOutOfRangeException

radius is outside 0..1000.

Threshold(float, ThresholdMode, Rgba8?, Rgba8?)

Makes a two-color image: pixels above level take above, the others below, with alpha kept. See Threshold(float, ThresholdMode, Rgba8?, Rgba8?).

public RasterOps Threshold(float level = 0.5, ThresholdMode mode = ThresholdMode.Luminance, Rgba8? below = null, Rgba8? above = null)

Parameters

level float

The level, from 0 to 1. Defaults to 0.5.

mode ThresholdMode

What is measured. Defaults to Luminance.

below Rgba8?

The lower color, or null for black.

above Rgba8?

The upper color, or null for white.

Returns

RasterOps

This builder.

Examples

// Two colors: navy below half luminance, cream above; alpha is kept.
using Raster<Rgba8> stamp = raster.Derive(o => o.Threshold(0.5f, ThresholdMode.Luminance, Rgba8.Navy, new Rgba8(250, 240, 220, 255)), Ct);

Exceptions

ArgumentOutOfRangeException

level is outside 0..1, or mode is not defined.

Tile(int, int, Point, Rect?)

Makes a new width × height canvas filled by repeating the image, or the source rectangle of it, with one copy's top-left corner at origin, as Skia's Tile filter and ImageMagick's tile: fill do. Pixels are copied exactly.

public RasterOps Tile(int width, int height, Point origin = default, Rect? source = null)

Parameters

width int

The canvas width, at least 1.

height int

The canvas height, at least 1.

origin Point

Where one copy's top-left corner goes; copies repeat from there in every direction.

source Rect?

The rectangle to repeat, inside the image, or null (the default) for the whole image.

Returns

RasterOps

This builder.

Examples

// Roll the image half way round, so its edges meet in the middle, which shows whether it tiles seamlessly.
using Raster rolled = photo.Derive(ops => ops.Offset(photo.Width / 2, photo.Height / 2), Ct);

// Fill a 1920 × 1080 wallpaper with copies of the duck's head.
using Raster wallpaper = photo.Derive(ops => ops.Tile(1920, 1080, source: new Rect(380, 60, 160, 160)), Ct);

Exceptions

ArgumentOutOfRangeException

A size is less than 1, the rectangle is empty, or it leaves the image when the chain runs.

Tint(Rgba8, float)

Pushes midtones toward color and keeps black and white, as ImageMagick's -tint. See Tint(Rgba8, float).

public RasterOps Tint(Rgba8 color, float amount = 1)

Parameters

color Rgba8

The tint.

amount float

The strength, from 0 to 1. Defaults to 1.

Returns

RasterOps

This builder.

Examples

// A basic tone panel: levels with a midtone lift, a gentle S-curve, and a warm tint in the midtones. The three steps run as
// one pass over the pixels.
raster.Apply(o => o
    .Level(black: 0.04f, white: 0.96f, gamma: 1.1f)
    .SigmoidalContrast(4, midpoint: 0.5f)
    .Tint(new Rgba8(255, 200, 150, 255), 0.15f), Ct);
raster.Save("duck-toned.jpg");

Exceptions

ArgumentOutOfRangeException

amount is outside 0..1.

ToneMap(ToneMapping, double?)

Maps HDR linear light into 0..1: pixel values are read as linear light with reference white at 1.0 (as RgbaF32, RgbaF16 and RgbF32 rasters hold PQ and HLG images converted to a linear encoding), and the tone curve brings the brightest down to 1 on max(R, G, B), which keeps hue. Gray pixels map their value; CMYK pixels are left alone. The metadata's Hdr is cleared, as the pixels are no longer HDR.

public RasterOps ToneMap(ToneMapping method = ToneMapping.Bt2390, double? sourcePeakNits = null)

Parameters

method ToneMapping

The curve. Defaults to Bt2390.

sourcePeakNits double?

The brightest light in the image, in cd/m² (reference white is 203), or null to take it from the metadata's Hdr (MaxCLL, then the mastering peak), else 1,000.

Returns

RasterOps

This builder.

Examples

// HDR to SDR tone maps by default (BT.2390, source peak from the file's mDCV/cLLI, else 1,000 cd/m²).
using Raster<Rgb8> web = hdr.ConvertColor<Rgb8>(ColorProfile.Srgb);

// Or choose the curve and peak; Clip turns tone mapping off.
using Raster<Rgb8> soft = hdr.ConvertColor<Rgb8>(ColorProfile.Srgb, new ColorTransformOptions { ToneMapping = ToneMapping.Reinhard, SourcePeakNits = 4000 });

// Linear HDR pixels (1.0 = reference white, 203 cd/m²) can be tone mapped in place.
using Raster<RgbaF32> linear = hdr.ConvertColor<RgbaF32>(ColorProfile.LinearSrgb, new ColorTransformOptions { ToneMapping = ToneMapping.Clip });
linear.Apply(ops => ops.ToneMap(ToneMapping.Bt2390, sourcePeakNits: 1000));

Exceptions

ArgumentOutOfRangeException

sourcePeakNits is not a positive finite number.

Transform(Homography, TransformOptions?)

Transforms the image by a perspective (or affine) matrix that maps source positions to destination positions. Where the image shrinks or tilts away, each output pixel averages the ellipse its footprint covers in the source, so fine detail fades instead of aliasing.

public RasterOps Transform(Homography matrix, TransformOptions? options = null)

Parameters

matrix Homography

The forward matrix.

options TransformOptions

The filter, edges, background and viewport, or null for the defaults.

Returns

RasterOps

This builder.

Examples

// Turn by 20° and shrink to 40% in one pass. Matrices map source positions to destination positions and compose with *,
// left first; shrinking averages each pixel's footprint, so fine detail fades instead of aliasing.
Matrix3x2 matrix = Matrix3x2.CreateRotation(MathF.PI / 9) * Matrix3x2.CreateScale(0.4f);
using Raster turned = photo.Derive(ops => ops.Transform(matrix), Ct);

// A 3D tilt, as CSS draws a flat element: rotate about the vertical axis and look at it from 800 pixels away.
Matrix4x4 tilt = Matrix4x4.CreateTranslation(-320, -240, 0) * Matrix4x4.CreateRotationY(0.5f) * new Matrix4x4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, -1f / 800, 0, 0, 0, 1);
using Raster tilted = photo.Derive(ops => ops.Transform(tilt), Ct);

Exceptions

ArgumentException

The matrix is singular or not finite, or the viewport is empty. Without a viewport, the chain also throws when it is planned if the perspective sends a corner of the image to infinity or behind the viewer, naming the corner.

Transform(Matrix3x2, TransformOptions?)

Transforms the image by an affine matrix that maps source positions to destination positions, as Matrix3x2's factories build them (rotation is clockwise on screen). Pixels have the origin at the image's top-left corner and their centers at +0.5. The result is the transformed image's bounding box unless Viewport picks a rectangle; a pure scale equals Resize(int, int, ResampleFilter, ColorSpace) and a pure rotation equals Rotate(float, ResampleFilter, Rgba8).

public RasterOps Transform(Matrix3x2 matrix, TransformOptions? options = null)

Parameters

matrix Matrix3x2

The forward matrix.

options TransformOptions

The filter, edges, background and viewport, or null for the defaults.

Returns

RasterOps

This builder.

Examples

// Turn by 20° and shrink to 40% in one pass. Matrices map source positions to destination positions and compose with *,
// left first; shrinking averages each pixel's footprint, so fine detail fades instead of aliasing.
Matrix3x2 matrix = Matrix3x2.CreateRotation(MathF.PI / 9) * Matrix3x2.CreateScale(0.4f);
using Raster turned = photo.Derive(ops => ops.Transform(matrix), Ct);

// A 3D tilt, as CSS draws a flat element: rotate about the vertical axis and look at it from 800 pixels away.
Matrix4x4 tilt = Matrix4x4.CreateTranslation(-320, -240, 0) * Matrix4x4.CreateRotationY(0.5f) * new Matrix4x4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, -1f / 800, 0, 0, 0, 1);
using Raster tilted = photo.Derive(ops => ops.Transform(tilt), Ct);

Exceptions

ArgumentException

The matrix is singular or not finite, or the viewport is empty.

Transform(Matrix4x4, TransformOptions?)

Transforms the image by a 3D matrix applied to the image's plane at z = 0 and projected, as CSS matrix3d and Skia's SkM44 draw a flat image; see FromMatrix4x4(Matrix4x4).

public RasterOps Transform(Matrix4x4 matrix, TransformOptions? options = null)

Parameters

matrix Matrix4x4

The 3D matrix, in Matrix4x4's row-vector convention.

options TransformOptions

The filter, edges, background and viewport, or null for the defaults.

Returns

RasterOps

This builder.

Examples

// Turn by 20° and shrink to 40% in one pass. Matrices map source positions to destination positions and compose with *,
// left first; shrinking averages each pixel's footprint, so fine detail fades instead of aliasing.
Matrix3x2 matrix = Matrix3x2.CreateRotation(MathF.PI / 9) * Matrix3x2.CreateScale(0.4f);
using Raster turned = photo.Derive(ops => ops.Transform(matrix), Ct);

// A 3D tilt, as CSS draws a flat element: rotate about the vertical axis and look at it from 800 pixels away.
Matrix4x4 tilt = Matrix4x4.CreateTranslation(-320, -240, 0) * Matrix4x4.CreateRotationY(0.5f) * new Matrix4x4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, -1f / 800, 0, 0, 0, 1);
using Raster tilted = photo.Derive(ops => ops.Transform(tilt), Ct);

Exceptions

ArgumentException

The projected matrix is singular or not finite, or the viewport is empty.

Trim(float, Rgba8?)

Removes the border: the rows and columns at the edges whose pixels all lie within tolerance of the border color, by ImageMagick's -fuzz distance, so the rectangle equals ImageMagick's -trim. Every frame of an animation is cropped to the union of the frames' rectangles, so they keep one size; an image that is all border is left as it is.

public RasterOps Trim(float tolerance = 0, Rgba8? color = null)

Parameters

tolerance float

The color distance still counted as border, from 0 (the exact color) to 1 (every color).

color Rgba8?

The border color, or null (the default) for the top-left pixel's.

Returns

RasterOps

This builder.

Examples

// Cut away the white margin, counting near-white (within 10% by ImageMagick's -fuzz distance) as margin too.
Rect content = scan.TrimBounds(tolerance: 0.1f);
scan.Apply(ops => ops.Trim(tolerance: 0.1f).Fit(400, 400), Ct);

Remarks

The size depends on the pixels, so the chain runs up to the trim, measures, and plans the rest from the real size; a trim cannot run inside Within. TrimBounds(float, Rgba8?) returns the rectangle without changing anything.

Exceptions

ArgumentOutOfRangeException

tolerance is outside 0..1.

Vignette(float, float, Rgba8?, Vector2?)

Darkens (or tints) the image toward its corners, outside an ellipse fitted to the image, with a soft edge: the shape of ImageMagick's -vignette, which blurs a drawn ellipse. The mask is computed per pixel, so its cost does not depend on the softness. Pixels are mixed with the color source-over, on premultiplied stored values.

public RasterOps Vignette(float radius = 1, float softness = 0.5, Rgba8? color = null, Vector2? center = null)

Parameters

radius float

The ellipse's size as a fraction of the half-diagonal: at 1 the ellipse passes through the corners, at 0.7 it is about as wide as the image. The ellipse has the image's proportions.

softness float

The share of the radius over which the edge fades, from 0 (hard) to 1: the fade is a Gaussian-blurred edge whose ±2σ spans softness × radius.

color Rgba8?

The color toward the corners, with its alpha as its strength; black and opaque when null.

center Vector2?

The ellipse's center in the raster's pixel coordinates (0, 0 is the top-left corner of the image); the image's middle when null.

Returns

RasterOps

This builder.

Examples

// Darken the corners: the edge of the ellipse passes through the corners and fades over half the radius.
using Raster dark = photo.Derive(ops => ops.Vignette(), Ct);

// A tighter, softer, warm-white vignette, off-center toward the subject.
using Raster light = photo.Derive(
    ops => ops.Vignette(0.6f, softness: 0.8f, color: new Rgba8(255, 245, 230, 255), center: new System.Numerics.Vector2(photo.Width * 0.4f, photo.Height * 0.5f)),
    Ct);

Exceptions

ArgumentOutOfRangeException

radius is outside 0..10, softness is outside 0..1, or center is not finite.

Warp(IWarp, SamplingOptions?)

Runs a warp: each output pixel shows the source point the warp maps it to, sampled as Transform(Homography, TransformOptions?) samples, so where the warp shrinks the image its fine detail fades instead of aliasing. Warps holds the built-in ones; FromFunction(Func<Vector2, Vector2>, Size?) or an own IWarp make others.

public RasterOps Warp(IWarp warp, SamplingOptions? options = null)

Parameters

warp IWarp

The warp.

options SamplingOptions

The filter, color space, edges and background, or null for the defaults.

Returns

RasterOps

This builder.

Examples

// Correct a wide-angle lens with the PanoTools coefficients a lens database gives (ImageMagick's Barrel), then swirl a
// square region in place; size-keeping warps run inside Within.
using Raster corrected = photo.Derive(ops => ops
    .Warp(Warps.Barrel(0.0, -0.018, 0.0))
    .Within(new Rect(300, 200, 200, 200), s => s.Warp(Warps.Swirl(90))), Ct);

// Size-changing ones: wrap the image into a circle, or bend it into an arc.
using Raster disc = photo.Derive(ops => ops.Warp(Warps.Polar(), new SamplingOptions { Background = Rgba8.White }), Ct);
using Raster arc = photo.Derive(ops => ops.Warp(Warps.Arc(120)), Ct);

// A magnifying glass over the duck's head, blending into the photo over 12 pixels.
using Raster loupe = photo.Derive(ops => ops.Warp(Warps.Magnifier(new Rect(380, 60, 160, 160), 2.5f, 12)), Ct);

Remarks

Warps that keep the size run inside Within; those that change it (Polar, DePolar, Arc, Wave) throw there.

Exceptions

ArgumentNullException

warp is null.

WhiteBalance(Rgba8)

Applies WhiteBalance(Rgba8): makes neutral a neutral gray of its own luminance.

public RasterOps WhiteBalance(Rgba8 neutral)

Parameters

neutral Rgba8

A color that should be gray, such as a gray card's pixels.

Returns

RasterOps

This builder.

Examples

// Correct a tungsten-lit photo, as a raw editor's temperature slider does.
raster.Apply(o => o.WhiteBalance(temperature: 3200), Ct);

// Or pick a gray card's color and make it neutral.
raster.Apply(o => o.WhiteBalance(new Rgba8(182, 170, 151, 255)), Ct);

// Or let the image tell: ImageMagick's gray world, or the brightest pixels as white.
raster.Apply(o => o.AutoWhiteBalance(WhiteBalanceMethod.GrayWorld), Ct);
raster.Apply(o => o.AutoWhiteBalance(WhiteBalanceMethod.WhitePatch), Ct);

Exceptions

ArgumentOutOfRangeException

A channel of neutral is 0, so no gain can make it gray.

WhiteBalance(float, float)

Applies WhiteBalance(float, float): corrects for a light of temperature kelvin.

public RasterOps WhiteBalance(float temperature, float tint = 0)

Parameters

temperature float

The light's correlated color temperature, from 1,500 to 15,000 kelvin; 6504 changes almost nothing.

tint float

The light's offset from a black body's color, from −1 to 1; positive for a greener light.

Returns

RasterOps

This builder.

Examples

// Correct a tungsten-lit photo, as a raw editor's temperature slider does.
raster.Apply(o => o.WhiteBalance(temperature: 3200), Ct);

// Or pick a gray card's color and make it neutral.
raster.Apply(o => o.WhiteBalance(new Rgba8(182, 170, 151, 255)), Ct);

// Or let the image tell: ImageMagick's gray world, or the brightest pixels as white.
raster.Apply(o => o.AutoWhiteBalance(WhiteBalanceMethod.GrayWorld), Ct);
raster.Apply(o => o.AutoWhiteBalance(WhiteBalanceMethod.WhitePatch), Ct);

Exceptions

ArgumentOutOfRangeException

An argument is out of range.

Within(Mask, Action<RasterOps>)

Runs the operations build adds, then mixes each pixel with the original by the mask's coverage: none keeps the original, full keeps the result, and values between blend the two on premultiplied pixels, so a soft mask edge gives a soft transition. Only the box around the mask's non-zero coverage is touched. Masks nest by multiplying their coverage, and mix freely with rectangles.

public RasterOps Within(Mask mask, Action<RasterOps> build)

Parameters

mask Mask

The coverage: a raster of any size and type, placed at its offset, read by its channel.

build Action<RasterOps>

Adds the operations. Each must keep the size, as in Within(Rect, Action<RasterOps>).

Returns

RasterOps

This builder.

Examples

// Edit only the sky: a gray mask, white where the edit applies fully, black where it does not, soft in between.
photo.Apply(ops => ops
    .Within(new Mask(sky), o => o.Saturation(1.3f).Contrast(1.1f))

    // Or take coverage from a logo's alpha, placed on the photo, and invert the area under it.
    .Within(new Mask(logo, new Point(20, 20), MaskChannel.Alpha), o => o.Invert()), Ct);

Remarks

The mask raster must stay undisposed until the chain has run. Masking a raster with itself reads from a copy, made when the step runs.

Exceptions

ArgumentNullException

build is null.

ArgumentException

The mask has no raster, or an operation inside would change the size or the color encoding.

ArgumentOutOfRangeException

The mask's channel is not defined.

Within(Rect, Action<RasterOps>)

Runs the operations build adds so that only pixels inside area change, as if the image were edited through a rectangular hole. Filters read the real pixels around the area, so a blurred rectangle has no seam and equals the whole-image blur there; flips and a 180° turn mirror the area itself. Positions inside (DrawImage, FillColor) are the raster's, as outside. Scopes nest: a rectangle inside a rectangle is their intersection.

public RasterOps Within(Rect area, Action<RasterOps> build)

Parameters

area Rect

The rectangle, in the raster's coordinates. It is clipped to the raster; one that misses it changes nothing.

build Action<RasterOps>

Adds the operations. Each must keep the size: a resize, crop, pad, 90° or free turn, AutoOrient, ConvertColor or ToneMap throws.

Returns

RasterOps

This builder.

Examples

// Blur a face: only pixels inside the rectangle change, and the blur reads the real pixels around it, so there is no seam.
photo.Apply(ops => ops
    .Within(new Rect(200, 120, 160, 120), o => o.Blur(6))
    .Within(new Rect(0, 0, photo.Width, 60), o => o.Brightness(0.8f).Saturation(0.5f)), Ct);   // a darker band on top

Exceptions

ArgumentNullException

build is null.

ArgumentOutOfRangeException

area has a negative width or height.

ArgumentException

An operation inside would change the size or the color encoding; the message names it.

ZoomBlur(float, Vector2?)

Streaks the image along rays from center, as a zoom during the exposure (Photoshop's radial zoom blur): each pixel averages samples on the ray through it, spanning amount of its distance from the center, half inward and half outward, about one per pixel (at most 256). The center does not move, and a flat image stays flat.

public RasterOps ZoomBlur(float amount, Vector2? center = null)

Parameters

amount float

The streak's length as a fraction of the distance from the center, from 0 (changes nothing) to 1.

center Vector2?

The center in pixels, pixel centers at +0.5, or null for the image's center.

Returns

RasterOps

This builder.

Examples

using Raster moving = photo.Derive(ops => ops.MotionBlur(radius: 12, sigma: 4, angle: 15), Ct);  // a streak toward 15°
using Raster spinning = photo.Derive(ops => ops.RotationalBlur(8), Ct);                          // 8° around the center
using Raster zooming = photo.Derive(ops => ops.ZoomBlur(0.2f, new System.Numerics.Vector2(320, 200)), Ct);

Exceptions

ArgumentOutOfRangeException

amount is outside 0..1, or the center is not finite.