pub struct Canvas<'a> { /* private fields */ }Expand description
A clipped, writable pixel target.
Every operation is clipped to Canvas::clip, which starts as the whole frame
and only ever shrinks. Clipping is rectangular: that covers scrolling regions,
damage-restricted repaint and nested panels, which is all a UI actually needs.
Arbitrary clip shapes are not planned.
Coordinates are physical pixels relative to the frame origin, never relative to the clip.
Implementations§
Source§impl<'a> Canvas<'a>
impl<'a> Canvas<'a>
Sourcepub fn new(frame: &'a mut Frame<'_>) -> Self
pub fn new(frame: &'a mut Frame<'_>) -> Self
Borrows a frame for drawing, clipped to the whole frame.
Sourcepub fn from_pixels(
pixels: &'a mut [u32],
size: Size,
stride: u32,
format: PixelFormat,
) -> Option<Self>
pub fn from_pixels( pixels: &'a mut [u32], size: Size, stride: u32, format: PixelFormat, ) -> Option<Self>
Wraps a raw pixel slice. Returns None if it is too small for the geometry.
Prefer Canvas::new; this exists for offscreen buffers and benchmarks that
have no Frame to hand.
Sourcepub const fn format(&self) -> PixelFormat
pub const fn format(&self) -> PixelFormat
Word layout of the target.
Sourcepub fn with_clip(&mut self, rect: Rect) -> Canvas<'_>
pub fn with_clip(&mut self, rect: Rect) -> Canvas<'_>
A canvas over the same pixels with a tighter clip.
The borrow ends when the returned canvas is dropped, so this is how a parent hands a child a region to draw in without either being able to escape it.
Sourcepub const fn is_clipped_out(&self) -> bool
pub const fn is_clipped_out(&self) -> bool
Returns true if the clip admits no pixels, so drawing can be skipped.
Sourcepub fn clear(&mut self, color: Color)
pub fn clear(&mut self, color: Color)
Fills the entire clip with an opaque colour.
This is the full-frame clear when the clip is untouched, and the damage-restricted clear when it is not.
Sourcepub fn copy_from(&mut self, src: &PixelView<'_>, regions: &[Rect])
pub fn copy_from(&mut self, src: &PixelView<'_>, regions: &[Rect])
Copies matching regions out of another buffer.
Source and destination coordinates are the same, so this is the damage-driven “publish what changed” blit a double-buffered backend needs — not a general blitter. Regions are clipped to both buffers.
Source§impl Canvas<'_>
impl Canvas<'_>
Source§impl Canvas<'_>
impl Canvas<'_>
Sourcepub fn fill_circle(
&mut self,
centre: Point,
radius: i32,
color: impl Into<Paint>,
)
pub fn fill_circle( &mut self, centre: Point, radius: i32, color: impl Into<Paint>, )
Fills a circle of radius around centre, anti-aliased.
Exactly Canvas::fill_rounded_rect on the bounding square — this name
exists so the intent is readable and the radius impossible to get wrong.
The painted diameter is 2 * radius pixels.
Sourcepub fn stroke_circle(
&mut self,
centre: Point,
radius: i32,
thickness: i32,
color: impl Into<Paint>,
)
pub fn stroke_circle( &mut self, centre: Point, radius: i32, thickness: i32, color: impl Into<Paint>, )
Draws a ring of thickness pixels just inside the circle of radius
around centre, anti-aliased.
Sourcepub fn stroke_arc(
&mut self,
centre: Point,
radius: i32,
thickness: i32,
start: i32,
sweep: i32,
color: impl Into<Paint>,
)
pub fn stroke_arc( &mut self, centre: Point, radius: i32, thickness: i32, start: i32, sweep: i32, color: impl Into<Paint>, )
Draws part of a ring: from start, sweeping sweep, both in units of
TURN. Angle 0 is twelve o’clock and positive sweeps go clockwise;
a negative sweep goes the other way. The ends are cut flat along the
radius — butt caps.
A sweep of at least a full TURN is exactly Canvas::stroke_circle,
which is what lets a progress ring pass done * TURN / total without
special-casing 100%. A thickness of at least radius fills to the
centre, which makes a pie slice.
The cost is proportional to the pixels the arc actually covers, not to its bounding square — a thin spinner touches a thin ring of pixels. What to damage for an animated arc is the caller’s business, but the same property means a conservative bounding rectangle only costs rasterising the ring inside it, not the square.
Source§impl Canvas<'_>
impl Canvas<'_>
Sourcepub fn blit(&mut self, src: &PixelView<'_>, at: Point)
pub fn blit(&mut self, src: &PixelView<'_>, at: Point)
Draws src with its top-left corner at at, one source pixel per
destination pixel, composited with source-over alpha.
Source pixels are premultiplied 0xAARRGGBB — see the
module docs. Clipped to the canvas clip like every other
operation; any part of the image outside it is simply not drawn.
Sourcepub fn blit_scaled(&mut self, src: &PixelView<'_>, dest: Rect)
pub fn blit_scaled(&mut self, src: &PixelView<'_>, dest: Rect)
Draws all of src into dest, nearest-neighbour resampled, composited
with source-over alpha.
Source pixels are premultiplied 0xAARRGGBB — see the
module docs. When dest is exactly the source size this is
Canvas::blit. Sampling is at pixel centres, so an integer upscale
turns each source pixel into an even block, and a downscale picks
representative pixels rather than always the top-left ones.
Sourcepub fn blit_rounded(
&mut self,
src: &PixelView<'_>,
dest: Rect,
shape: Rect,
radius: i32,
)
pub fn blit_rounded( &mut self, src: &PixelView<'_>, dest: Rect, shape: Rect, radius: i32, )
Draws all of src into dest, masked to shape with rounded corners
of radius, anti-aliased.
shape is the rectangle whose corners are rounded and outside which
nothing is drawn; sampling is still mapped from the whole of dest.
They are separate arguments because they genuinely differ in the Cover
case — an image scaled past its box so the box is filled edge to edge —
where dest overflows and shape is the box. When the picture and the
mask are the same rectangle, pass it twice. radius is clamped to half
of shape’s shorter side, so a full radius on a square shape is a
circle — the avatar crop. Zero draws exactly Canvas::blit_scaled
restricted to shape.
The mask must not come from the clip: the clip is damage, and a damage-restricted repaint of half an image has to round the image’s corners, never the damage rectangle’s.
Source§impl Canvas<'_>
impl Canvas<'_>
Sourcepub fn draw_line(&mut self, a: Point, b: Point, color: impl Into<Paint>)
pub fn draw_line(&mut self, a: Point, b: Point, color: impl Into<Paint>)
Draws a one-pixel line between two pixel centres, anti-aliased.
Axis-aligned lines take an exact integer path and get no anti-aliasing, because a horizontal rule that has been softened to 97% grey looks like a rendering bug, not like quality. Everything else is a Wu-style two-pixel blend along the minor axis.
Thickness is not a parameter. Borders come from Canvas::stroke_rect and
Canvas::stroke_rounded_rect; thick arbitrary-angle lines are not
something a UI toolkit needs before it can draw a chart, and that is not
this milestone.
Source§impl Canvas<'_>
impl Canvas<'_>
Sourcepub fn fill_star(
&mut self,
centre: Point,
outer_radius: i32,
inner_radius: i32,
points: u32,
rotation: i32,
color: impl Into<Paint>,
)
pub fn fill_star( &mut self, centre: Point, outer_radius: i32, inner_radius: i32, points: u32, rotation: i32, color: impl Into<Paint>, )
Fills a star, anti-aliased.
points is the number of spikes — five for the familiar one. Vertices
alternate between outer_radius at the tips and inner_radius at the
valleys, so the ratio between them is how pointed the star looks: about
0.38 of the outer radius is the classic pentagram, and an inner radius
approaching the outer one is a polygon with 2 × points sides.
rotation is in the same binary turns as the arcs — see TURN — and
zero puts a tip at twelve o’clock.
Vertices are computed to sub-pixel precision even though the centre is whole pixels, which is what keeps a small star from looking chewed.
§A five-pointed star is not exactly five-fold symmetric
TURN is a power of two, so it divides exactly by two, four and eight
and not by five. A five-pointed star’s vertex angles are therefore each
rounded to the nearest unit — at most half a unit in 65536, which is
under a hundredth of a pixel at any radius a screen can show, and
invisible. But it does mean a star rotated by TURN / 5 is not the
bit-identical picture, only the same one. Anything that needs exactness
should compare against a tolerance rather than against pixels.
Source§impl Canvas<'_>
impl Canvas<'_>
Sourcepub fn fill_rect(&mut self, rect: Rect, color: impl Into<Paint>)
pub fn fill_rect(&mut self, rect: Rect, color: impl Into<Paint>)
Fills rect, compositing with the destination if the paint has alpha.
Sourcepub fn fill_rects(&mut self, rects: &[Rect], color: impl Into<Paint>)
pub fn fill_rects(&mut self, rects: &[Rect], color: impl Into<Paint>)
Fills every rectangle. Overlapping rectangles composite twice.
Sourcepub fn stroke_rect(
&mut self,
rect: Rect,
thickness: i32,
color: impl Into<Paint>,
)
pub fn stroke_rect( &mut self, rect: Rect, thickness: i32, color: impl Into<Paint>, )
Draws a border of thickness pixels inside rect.
The four bands are cut so they do not overlap. Drawing them as four full-length rectangles would composite the corners twice, which is invisible at full alpha and obvious at anything less.
Source§impl Canvas<'_>
impl Canvas<'_>
Sourcepub fn fill_rounded_rect(
&mut self,
rect: Rect,
radius: i32,
color: impl Into<Paint>,
)
pub fn fill_rounded_rect( &mut self, rect: Rect, radius: i32, color: impl Into<Paint>, )
Fills a rectangle with rounded corners, anti-aliased.
radius is clamped to half the shorter side; a radius of zero is exactly
Canvas::fill_rect.