Skip to main content

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;
21mod controls;
22pub mod locate;
23pub mod model;
24pub mod run;
25pub mod stats;
26
27/// Side of one statistics cell in native pixels.
28pub const CELL: usize = 2;
29/// Side of one coherence block in native pixels (the compositor's quality block).
30pub const BLOCK: usize = 16;
31
32/// Tiers of one observed pixel, best last. An observation only competes with observations of its own tier.
33pub const TIER_OPAQUE: u8 = 0;
34pub const TIER_FRINGE: u8 = 1;
35pub const TIER_VEIL: u8 = 2;
36pub const TIER_CLEAN: u8 = 3;
37
38/// Per-channel difference treated as disagreement, from the source's decode noise (10 for decoded video,
39/// 0 for lossless synthetic sources).
40#[inline]
41pub fn tolerance(noise: f64) -> i32 {
42    ((noise * 2.4).round() as i32).max(2)
43}
44
45/// Integer luma, 0..=255.
46#[inline]
47pub fn luma(r: u8, g: u8, b: u8) -> u32 {
48    (r as u32 * 77 + g as u32 * 150 + b as u32 * 29) >> 8
49}
50
51/// One decoded RGBA frame.
52pub struct Frame<'a> {
53    pub rgba: &'a [u8],
54    pub width: usize,
55    pub height: usize,
56}
57
58/// Screen pixels that belong to one layer in one frame: inside the layer's rectangle, carrying its atlas label,
59/// and outside this frame's occlusion rectangles, precomputed as a byte mask over the rectangle.
60pub struct Footprint<'a> {
61    /// Layer rectangle in native screen pixels.
62    pub x: i64,
63    pub y: i64,
64    pub width: usize,
65    pub height: usize,
66    /// One byte per pixel of the rectangle, row-major; non-zero = in the footprint.
67    pub mask: &'a [u8],
68}
69
70impl Footprint<'_> {
71    #[inline]
72    pub fn contains(&self, sx: i64, sy: i64) -> bool {
73        let (lx, ly) = (sx - self.x, sy - self.y);
74        lx >= 0
75            && ly >= 0
76            && (lx as usize) < self.width
77            && (ly as usize) < self.height
78            && self.mask[ly as usize * self.width + lx as usize] != 0
79    }
80
81    /// `label_mask` with this frame's occlusion rectangles `(x, y, width, height)` cleared.
82    pub fn occlude(
83        label_mask: &[u8],
84        x: i64,
85        y: i64,
86        width: usize,
87        height: usize,
88        occlusions: &[(i64, i64, i64, i64)],
89    ) -> Vec<u8> {
90        let mut m = label_mask.to_vec();
91        for &(ox, oy, ow, oh) in occlusions {
92            let (x0, y0) = ((ox - x).max(0) as usize, (oy - y).max(0) as usize);
93            let (x1, y1) = (
94                ((ox + ow - x).max(0) as usize).min(width),
95                ((oy + oh - y).max(0) as usize).min(height),
96            );
97            for ly in y0..y1.max(y0) {
98                if x1 > x0 {
99                    m[ly * width + x0..ly * width + x1].fill(0);
100                }
101            }
102        }
103        m
104    }
105}
106
107/// A canvas region resident in memory: RGBA pixels with coverage, origin in world pixels.
108pub struct CanvasView<'a> {
109    pub x: i64,
110    pub y: i64,
111    pub width: usize,
112    pub height: usize,
113    pub rgba: &'a [u8],
114    /// Coverage bits, one per pixel, row-major (the compositor's layout).
115    pub coverage: &'a [u8],
116}
117
118impl CanvasView<'_> {
119    #[inline]
120    pub fn covered(&self, wx: i64, wy: i64) -> Option<usize> {
121        let (lx, ly) = (wx - self.x, wy - self.y);
122        if lx < 0 || ly < 0 || lx >= self.width as i64 || ly >= self.height as i64 {
123            return None;
124        }
125        let i = ly as usize * self.width + lx as usize;
126        (self.coverage[i >> 3] & (1 << (i & 7)) != 0).then_some(i)
127    }
128}