//! `cargo run --example clip_path` //! //! Demonstrates [`Canvas::set_clip_path`] — real per-path clipping. Each cell //! fills its whole rect with a background colour, then installs a path clip and //! fills again with a foreground colour: only the pixels inside the path are //! repainted, so the shape's silhouette appears (not its bounding box). //! //! Both backends clip with smooth, anti-aliased edges: the software backend //! with a tiny-skia coverage mask, the GLES backend by compositing an offscreen //! layer through an equivalent coverage mask. The circle and rounded corners //! look the same on both. Esc exits. use ltk::{ column, row, text, App, Color, Element, External, Keysym, PathCmd, Rect }; #[ derive( Clone, Debug ) ] enum Msg {} struct Demo; const CELL: f32 = 220.0; const MARGIN: f32 = 24.0; fn bg() -> Color { Color::rgba( 0.16, 0.18, 0.22, 1.0 ) } fn fg() -> Color { Color::rgba( 0.30, 0.68, 0.90, 1.0 ) } /// Rounded rectangle inset by `MARGIN`, corners of `radius`, built from lines /// and quadratic corners. fn rounded_rect_path( r: Rect, radius: f32 ) -> Vec { let l = r.x + MARGIN; let t = r.y + MARGIN; let right = r.x + r.width - MARGIN; let b = r.y + r.height - MARGIN; vec![ PathCmd::MoveTo( l + radius, t ), PathCmd::LineTo( right - radius, t ), PathCmd::QuadTo( right, t, right, t + radius ), PathCmd::LineTo( right, b - radius ), PathCmd::QuadTo( right, b, right - radius, b ), PathCmd::LineTo( l + radius, b ), PathCmd::QuadTo( l, b, l, b - radius ), PathCmd::LineTo( l, t + radius ), PathCmd::QuadTo( l, t, l + radius, t ), PathCmd::Close, ] } /// Circle centred in the cell, drawn as four cubic-Bézier quadrants. fn circle_path( r: Rect ) -> Vec { let cx = r.x + r.width / 2.0; let cy = r.y + r.height / 2.0; let rad = ( r.width.min( r.height ) ) / 2.0 - MARGIN; let k = rad * 0.552_284_75; // 4/3 * tan(pi/8): cubic circle approximation vec![ PathCmd::MoveTo( cx + rad, cy ), PathCmd::CubicTo( cx + rad, cy + k, cx + k, cy + rad, cx, cy + rad ), PathCmd::CubicTo( cx - k, cy + rad, cx - rad, cy + k, cx - rad, cy ), PathCmd::CubicTo( cx - rad, cy - k, cx - k, cy - rad, cx, cy - rad ), PathCmd::CubicTo( cx + k, cy - rad, cx + rad, cy - k, cx + rad, cy ), PathCmd::Close, ] } /// Downward triangle inset by `MARGIN` — a polygon clip. fn triangle_path( r: Rect ) -> Vec { vec![ PathCmd::MoveTo( r.x + r.width / 2.0, r.y + MARGIN ), PathCmd::LineTo( r.x + r.width - MARGIN, r.y + r.height - MARGIN ), PathCmd::LineTo( r.x + MARGIN, r.y + r.height - MARGIN ), PathCmd::Close, ] } /// A cell that paints `bg()` everywhere, then `fg()` clipped to `shape`. The /// previous clip is snapshotted and restored so the clip does not leak to the /// rest of the frame. fn clipped_cell( shape: fn( Rect ) -> Vec ) -> Element { External::cpu( CELL, CELL, move |canvas, rect| { canvas.fill_rect( rect, bg(), 0.0 ); let saved = canvas.clip_bounds(); canvas.set_clip_path( &shape( rect ) ); canvas.fill_rect( rect, fg(), 0.0 ); canvas.set_clip_rects( &saved ); } ).into() } impl App for Demo { type Message = Msg; fn view( &self ) -> Element { column() .spacing( 16.0 ) .padding( 24.0 ) .push( text( "Canvas::set_clip_path — rounded rect, circle, triangle" ) ) .push( row() .spacing( 16.0 ) .push( clipped_cell( |r| rounded_rect_path( r, 32.0 ) ) ) .push( clipped_cell( circle_path ) ) .push( clipped_cell( triangle_path ) ) ) .into() } fn update( &mut self, _msg: Msg ) {} fn on_key( &mut self, keysym: Keysym ) -> Option { if keysym == Keysym::Escape { std::process::exit( 0 ); } None } fn background_color( &self ) -> Color { ltk::theme_palette().bg } } fn main() { ltk::run( Demo ); }