Skip to main content

crabshell_render/
lib.rs

1//! Draws Crabshell windows on the CPU: backgrounds, rounded corners,
2//! borders and opacity with tiny-skia, glyphs from cosmic-text, and icons
3//! from SVG, PNG or raw pixels. Only what changed is repainted.
4//!
5//! The platform keeps one [`Renderer`], a [`DamageHistory`] per window and
6//! a [`BufferAge`] per buffer. Each frame it hands the renderer the window's
7//! view and damage and a [`Canvas`] over the buffer's memory, and reports
8//! the returned rectangles to the compositor:
9//!
10//! ```
11//! use crabshell_reactive::Runtime;
12//! use crabshell_render::{BufferAge, Canvas, DamageHistory, PixelFormat, Renderer};
13//! use crabshell_ui::prelude::*;
14//!
15//! let runtime = Runtime::current();
16//! let cx = Cx::new(runtime.new_owner());
17//! let bar = PanelWindow::new(&Output::new("DP-1"))
18//!     .style(Style::new().background(Color::rgb(0x1e, 0x1e, 0x2e)))
19//!     .child(Text::new("hello"));
20//! let window = Window::new(&cx, bar);
21//! let mut text = TextSystem::new();
22//! window.set_size(Size::new(200.0, 30.0));
23//! runtime.flush();
24//! window.layout(&mut text);
25//!
26//! let mut renderer = Renderer::new();
27//! let mut history = DamageHistory::new();
28//! let mut age = BufferAge::default();
29//! let scale = 1.5;
30//! let (width, height) = (300, 45); // 200 x 30 logical pixels at 1.5
31//! let mut buffer = vec![0; width * height * 4];
32//! let damage = window.take_damage();
33//! let report = renderer.draw(
34//!     &window.view(),
35//!     &damage,
36//!     &mut text,
37//!     &mut history,
38//!     Canvas {
39//!         pixels: &mut buffer,
40//!         width: width as u32,
41//!         height: height as u32,
42//!         scale,
43//!         format: PixelFormat::Argb8888,
44//!         age: &mut age,
45//!     },
46//! )?;
47//! assert_eq!(report.len(), 1); // a new buffer is drawn whole
48//! # Ok::<(), crabshell_render::CanvasError>(())
49//! ```
50//!
51//! # How it works
52//!
53//! - Damage arrives in logical pixels and becomes buffer pixels, rounded
54//!   outward. Overlapping areas are merged, a buffer that skipped frames
55//!   also repaints what changed in them, and when more than half of the
56//!   buffer is dirty, all of it is repainted.
57//! - The areas are drawn from scratch in 64-pixel tiles of a fixed grid,
58//!   each into an RGBA pixmap of its own that clips the drawing, and copied
59//!   into the buffer in the buffer's byte order. A pixel is always drawn
60//!   in the same tile, so a repaint gives the same pixels as drawing
61//!   everything, even where a tile's edge cuts a curve.
62//! - Rectangles snap to whole pixels, so edges stay sharp at fractional
63//!   scales. A border is drawn inside the edge. Opacity applies to a widget
64//!   and everything inside it as one layer.
65//! - Text is drawn from the cosmic-text buffer layout measured, as
66//!   grayscale masks, with the baseline on a whole pixel. It isn't clipped:
67//!   the window's damage covers text that overflows its widget.
68//! - Icons are rasterized at their size in pixels and cached by source and
69//!   that size.
70//! - A screencopy view's frame is scaled to its box and kept, one per
71//!   view, until a new frame or box replaces it. [`capture_frame`] turns
72//!   a compositor's buffer into such a frame, scaled down to the view.
73//! - [`quantize_colors`] decodes an image file as `Image` does and finds
74//!   its main colors by median cut, as for a theme taken from a wallpaper.
75
76#![forbid(unsafe_code)]
77#![warn(missing_docs)]
78
79mod capture;
80mod colors;
81mod damage;
82mod draw;
83mod icons;
84mod images;
85mod theme;
86
87#[cfg(test)]
88mod tests;
89
90pub use capture::{Turn, capture_frame, frame_size};
91pub use colors::quantize_colors;
92pub use damage::{BufferAge, DamageHistory, PixelRect};
93pub use draw::{Canvas, CanvasError, PixelFormat, Renderer};
94pub use icons::DEFAULT_THEME as DEFAULT_ICON_THEME;