long_screen/kernels/reveal/mod.rs
1//! Overlay-aware source selection (docs/ARCHITECTURE.md §七).
2//!
3//! The compositor writes the first good observation of every canvas pixel. Screen-fixed or screen-moving
4//! overlays (a composer bar, a floating button, a scrollbar thumb, a cursor, a fade veil) are then burned into
5//! the canvas wherever they happened to sit when a region was first seen. This module repairs that with three
6//! frame-major passes over the decoded video, each a fixed amount of simple per-pixel work:
7//!
8//! 1. [`stats`]: per moving layer, per 2×2 screen cell, how the observation compares with the canvas at the same
9//! world position. Overlays show up as screen cells that keep disagreeing (opaque) or that only pass an
10//! attenuated copy of the page (veils), whatever page content scrolls beneath them.
11//! 2. [`model`]: those cells become overlay objects with an appearance template. [`locate`] finds each object
12//! again in every frame (it may move, bounce or fade) and turns the result into a per-cell cleanliness tier.
13//! 3. Selection ([`blocks`]): every canvas pixel keeps the value the cleanest observations agree on (a tolerant majority, so
14//! one clean sighting beats any number of covered ones and a passing cursor is outvoted). A second pass then
15//! takes whole 16×16 blocks from the latest fully clean frame that agrees, and gives each dynamic component
16//! (content that really changes over time) a single frame, so neither codec phase nor time is mixed.
17//!
18//! Pixel values are always copied from one decoded observation; nothing is averaged into the output.
19
20pub mod blocks;
21pub mod locate;
22pub mod model;
23pub mod run;
24pub mod stats;
25
26/// Side of one statistics cell in native pixels.
27pub const CELL: usize = 2;
28/// Side of one coherence block in native pixels (the compositor's quality block).
29pub const BLOCK: usize = 16;
30
31/// Tiers of one observed pixel, best last. An observation only competes with observations of its own tier.
32pub const TIER_OPAQUE: u8 = 0;
33pub const TIER_FRINGE: u8 = 1;
34pub const TIER_VEIL: u8 = 2;
35pub const TIER_CLEAN: u8 = 3;
36
37/// Per-channel difference treated as disagreement, from the source's decode noise (10 for decoded video,
38/// 0 for lossless synthetic sources).
39#[inline]
40pub fn tolerance(noise: f64) -> i32 {
41 ((noise * 2.4).round() as i32).max(2)
42}
43
44/// Integer luma, 0..=255.
45#[inline]
46pub fn luma(r: u8, g: u8, b: u8) -> u32 {
47 (r as u32 * 77 + g as u32 * 150 + b as u32 * 29) >> 8
48}
49
50/// One decoded RGBA frame.
51pub struct Frame<'a> {
52 pub rgba: &'a [u8],
53 pub width: usize,
54 pub height: usize,
55}
56
57/// Screen pixels that belong to one layer in one frame: inside the layer's rectangle, carrying its atlas label,
58/// and outside this frame's occlusion rectangles, precomputed as a byte mask over the rectangle.
59pub struct Footprint<'a> {
60 /// Layer rectangle in native screen pixels.
61 pub x: i64,
62 pub y: i64,
63 pub width: usize,
64 pub height: usize,
65 /// One byte per pixel of the rectangle, row-major; non-zero = in the footprint.
66 pub mask: &'a [u8],
67}
68
69impl Footprint<'_> {
70 #[inline]
71 pub fn contains(&self, sx: i64, sy: i64) -> bool {
72 let (lx, ly) = (sx - self.x, sy - self.y);
73 lx >= 0
74 && ly >= 0
75 && (lx as usize) < self.width
76 && (ly as usize) < self.height
77 && self.mask[ly as usize * self.width + lx as usize] != 0
78 }
79
80 /// `label_mask` with this frame's occlusion rectangles `(x, y, width, height)` cleared.
81 pub fn occlude(
82 label_mask: &[u8],
83 x: i64,
84 y: i64,
85 width: usize,
86 height: usize,
87 occlusions: &[(i64, i64, i64, i64)],
88 ) -> Vec<u8> {
89 let mut m = label_mask.to_vec();
90 for &(ox, oy, ow, oh) in occlusions {
91 let (x0, y0) = ((ox - x).max(0) as usize, (oy - y).max(0) as usize);
92 let (x1, y1) = (
93 ((ox + ow - x).max(0) as usize).min(width),
94 ((oy + oh - y).max(0) as usize).min(height),
95 );
96 for ly in y0..y1.max(y0) {
97 if x1 > x0 {
98 m[ly * width + x0..ly * width + x1].fill(0);
99 }
100 }
101 }
102 m
103 }
104}
105
106/// A canvas region resident in memory: RGBA pixels with coverage, origin in world pixels.
107pub struct CanvasView<'a> {
108 pub x: i64,
109 pub y: i64,
110 pub width: usize,
111 pub height: usize,
112 pub rgba: &'a [u8],
113 /// Coverage bits, one per pixel, row-major (the compositor's layout).
114 pub coverage: &'a [u8],
115}
116
117impl CanvasView<'_> {
118 #[inline]
119 pub fn covered(&self, wx: i64, wy: i64) -> Option<usize> {
120 let (lx, ly) = (wx - self.x, wy - self.y);
121 if lx < 0 || ly < 0 || lx >= self.width as i64 || ly >= self.height as i64 {
122 return None;
123 }
124 let i = ly as usize * self.width + lx as usize;
125 (self.coverage[i >> 3] & (1 << (i & 7)) != 0).then_some(i)
126 }
127}