Skip to main content

Layer

Struct Layer 

Source
pub struct Layer {
    pub blur: Vec2,
    pub alpha: f32,
    pub blend: BlendMode,
    pub matrix: Option<Transform2D>,
    pub backdrop_blur: f32,
    pub backdrop_id: Option<i64>,
    pub morphology: Option<Morphology>,
    pub color_filter: ColorFilter,
}
Expand description

How a layer is composited back onto what was underneath it.

A separate type from Paint rather than a reuse of it, because only two of a paint’s parts mean anything here — there is no shape to fill and no geometry to stroke — and a caller handed a Paint would reasonably expect its shader to matter.

Fields§

§blur: Vec2

Blur the group before compositing it, with this standard deviation per axis in device pixels.

Zero on both for no blur, which is the default and costs nothing: the extra passes are only recorded where a caller asked for one, and an axis whose deviation is zero costs no pass of its own.

Two deviations rather than one because dart:ui’s ImageFilter.blur states sigmaX and sigmaY separately, and because the blur is two separable passes here regardless – so the second is a field rather than a mechanism. Self::with_blur sets both.

On the group rather than on a paint, because a blur is a function of a finished image and a paint describes one shape. Blurring each shape and compositing the results is a different picture from blurring the composite, and the second is what a shadow or a frosted panel means.

§alpha: f32

Scales the whole layer on the way back. This is what makes group opacity differ from per-shape opacity: two overlapping half-transparent shapes in a layer show one blended edge, where the same shapes drawn directly show where they cross.

§blend: BlendMode

How the finished layer meets what was underneath it.

§matrix: Option<Transform2D>

Transform the finished layer on the way back, in device pixels.

Distinct from transforming what goes into it, which the canvas’s own transform already does. This resamples the finished image: magnifying through it gives the layer’s own pixels enlarged, where drawing the same shapes under a larger transform would give them redrawn at the larger size. That difference is the whole of what a matrix image filter means – a caller wanting the sharp one already has the transform stack.

It resamples what the layer captured, and a layer captures no more than its target holds. So a matrix moves an image and cannot recover what fell outside the layer to begin with: a shape drawn past the edge of the frame is gone before this runs, where upstream sizes the layer through the filter and brings it back. docs/non-parity.md §12 has the case and what fixing it would cost.

None composites the layer where it was drawn, which is what every layer did before this existed.

§backdrop_blur: f32

Blur what is already on the target before the layer draws over it.

This is the other blur, and the difference is which image is filtered. Self::blur softens the layer’s own content, which is what a shadow is. This softens what is behind it and hands the result to the layer as its starting content, which is what frosted glass is: a panel that obscures rather than one that is itself indistinct.

Zero for none, and it costs nothing when zero. When it is not zero it costs a copy of the target and two blur passes over it, because reading what a pass is writing is not something this renderer can do – see the architecture note on cutting a pass.

§Bounds stop being an optimization

For every other layer, Canvas::save_layer_bounds only says where the content is, and the picture is the same either way. For this one the bounds are the region that gets filtered: a frosted panel is a bounded layer, and the same layer without bounds blurs the whole frame and composites the whole frame back. Both are meaningful and they are different pictures, so state the bounds when you mean a panel.

§backdrop_id: Option<i64>

Which captured backdrop this layer filters, if it shares one.

dart:ui’s pushBackdropFilter takes a backdropId and says what it means: “when the first backdrop filter with a given id is processed during rasterization, the state of the backdrop is recorded and cached. All subsequent backdrop filters with the same identifier will apply their filter to the cached backdrop.” So it is a different picture, not only a faster one – overlapping panels sharing an id each filter what was behind all of them, rather than each filtering the one before it.

That is the case it exists for. A list of frosted rows over one background wants each row to show the background, and without the id the second row shows the first row’s blur blurred again. With it, the capture happens once and each row filters it: the picture the caller meant, and one pass cut instead of one per row.

An id keys a captured image and nothing more. A layer naming one from inside another layer still filters what the id captured rather than its own target’s content, which is upstream’s reading and is the point of the case it tests with each panel wrapped in a save layer of its own.

None captures afresh, which is what every layer did before this existed and is right whenever the backdrops genuinely differ.

§morphology: Option<Morphology>

Spread or shrink the group before compositing it. See Morphology.

On the group for the same reason Self::blur is: a maximum over a window is a function of a finished image. Dilating each shape and then compositing is a different picture from dilating the composite wherever two shapes overlap.

None for no morphology, which is the default and costs nothing.

§color_filter: ColorFilter

Recolor the finished group on its way back.

What dart:ui means by the colorFilter on the paint saveLayer takes, and the same distinction group opacity has: filtering each shape and compositing the results differs from filtering the composite wherever two of them overlap, because a filter is not linear in general.

It is also the only way to filter what a caller’s own fragment program drew. A runtime effect is a whole pipeline rather than a material this renderer’s shader evaluates, so there is nowhere in it to apply a matrix – the filter has to act on the image the program produced.

Implementations§

Source§

impl Layer

Source

pub fn with_matrix(self, matrix: impl Into<Transform2D>) -> Layer

Transform the finished layer on the way back. See Self::matrix.

Takes an affine or a projective transform, the same as concat. A layer moved by one of these is a finished image being placed, so perspective here gives the image seen at an angle rather than the content redrawn at one – which is the whole distinction this filter exists to make, and it survives the widening unchanged.

Source

pub fn opacity(alpha: f32) -> Layer

Source

pub fn with_blend(self, blend: BlendMode) -> Layer

Source

pub fn with_color_filter(self, filter: ColorFilter) -> Layer

Recolor the finished group on its way back. See Self::color_filter.

Source

pub fn with_morphology(self, morphology: Morphology) -> Layer

Spread or shrink the finished group. See Morphology.

A morphology that would move nothing becomes None, on the same reasoning as a blur of zero: a caller animating a radius down to nothing should get the unfiltered thing at the end, not two passes that copy the image twice to say so.

Source

pub fn with_backdrop_blur(self, sigma: f32) -> Layer

Blur the finished group, with sigma in device pixels.

Anything at or below zero, or not a number, means no blur rather than an error: a caller animating a shadow’s softness to nothing should get the sharp thing, not a refusal at the end of the animation. Blur the backdrop this layer is drawn over.

Ignored unless finite and positive, on the same terms as Self::with_blur: a sigma that is not a length describes no blur.

Source

pub fn with_backdrop_id(self, id: i64) -> Layer

Share one captured backdrop with every layer naming the same id.

See Self::backdrop_id for what is shared and why it changes the picture rather than only the cost. The id is the caller’s, and means nothing beyond identity: any two values that differ keep their backdrops apart.

The id outlives the target it captured. A layer naming one from inside another layer is seeded with the captured image placed where that layer’s target sits within it, which is the same arithmetic a bounded layer is already seeded by.

Source

pub fn with_blur(self, sigma: f32) -> Layer

Source

pub fn with_blur_xy(self, sigma_x: f32, sigma_y: f32) -> Layer

Blur the finished group by a deviation per axis, in device pixels.

Two, because dart:ui’s ImageFilter.blur states sigmaX and sigmaY separately. A blur here is two separable passes already, one along each axis, so each simply uses its own – and a pass whose deviation is zero is skipped rather than run, which is what keeps a blur along one axis from resampling the other for nothing.

Trait Implementations§

Source§

impl Clone for Layer

Source§

fn clone(&self) -> Layer

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Copy for Layer

Source§

impl Debug for Layer

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for Layer

Source§

fn default() -> Layer

Returns the “default value” for a type. Read more
Source§

impl PartialEq for Layer

Source§

fn eq(&self, other: &Layer) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for Layer

Auto Trait Implementations§

§

impl Freeze for Layer

§

impl RefUnwindSafe for Layer

§

impl Send for Layer

§

impl Sync for Layer

§

impl Unpin for Layer

§

impl UnsafeUnpin for Layer

§

impl UnwindSafe for Layer

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> Downcast<T> for T

Source§

fn downcast(&self) -> &T

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> Upcast<T> for T

Source§

fn upcast(&self) -> Option<&T>