Skip to main content

devpage_design/render/
drawing.rs

1//! A drawing: a scene laid out and made into its display list in one
2//! theme, kept so later scenes update it; its area and the images it paints.
3
4use std::collections::{BTreeMap, BTreeSet, HashMap};
5
6use kurbo::{Rect as KRect, Vec2};
7use peniko::ImageData;
8
9use crate::display::{
10    Blend, Builder, Chunk, Clip, Effect, Id, List, Order, Placed, RunId, Space, Transform,
11};
12use crate::resolve::{self, ImageId, Node, Origin, Scene, Theme};
13use crate::tree::{NodeId as SceneId, OrderKey};
14
15use super::*;
16
17/// An image a drawing paints, and what it shows: an image file, known by
18/// its id, or a shadow's pixels.
19#[derive(Clone)]
20pub(super) struct Painted {
21    pub(super) data: ImageData,
22    pub(super) file: Option<ImageId>,
23}
24
25impl Painted {
26    /// Whether the two show the same pixels.
27    pub(super) fn same(&self, other: &Painted) -> bool {
28        match (self.file, other.file) {
29            (Some(a), Some(b)) => a == b,
30            (None, None) => {
31                (self.data.width, self.data.height) == (other.data.width, other.data.height)
32                    && self.data.data.data() == other.data.data.data()
33            }
34            _ => false,
35        }
36    }
37}
38
39/// A scene laid out, in one theme, and made into its display list: kept, so
40/// a later scene updates it by what changed. Its layout tree, every node's
41/// box by its id, the property-tree nodes each node made, the list, and the
42/// images the list paints.
43pub struct Drawing {
44    /// The layout: the drawing's own tree and each run's.
45    pub(super) laid: Laid,
46    pub(super) theme: Theme,
47    /// Every node's box, in its space's coordinates.
48    pub(super) frames: BTreeMap<Order, Frame>,
49    pub(super) recs: BTreeMap<Order, Rec>,
50    /// Each run's place in the list, by its layout's slot.
51    pub(super) shown: HashMap<u32, RunId>,
52    /// For each run, the least start along it, in the design's coordinates,
53    /// of a child an update took out: where what follows moved from.
54    pub(super) shifts: HashMap<RunId, f64>,
55    /// Each run as the last walk through it found it.
56    pub(super) contexts: HashMap<RunId, RunContext>,
57    /// Each node's order, by its id: where a patch's nodes are in it.
58    pub(super) orders: HashMap<SceneId, Order>,
59    /// The display list: chunks under property trees, and their index.
60    pub list: List,
61    /// The blob each image file is painted from in this drawing.
62    pub(super) blobs: HashMap<ImageId, ImageData>,
63    /// Every image the list paints, each under a blob of its own, in the
64    /// order the list first paints it: what the GPU's atlas holds for this
65    /// drawing, placed as [`crate::atlas::place`] places them.
66    pub images: Vec<ImageData>,
67    /// What each of `images` shows.
68    pub(super) shows: Vec<Painted>,
69    /// The nodes whose own commands paint an image, in paint order.
70    pub(super) painters: BTreeSet<Order>,
71    /// Whether `images` fit the GPU's atlas together.
72    pub(super) fits: Result<(), Undrawable>,
73    /// How many nodes the last update visited after laying out.
74    pub(super) visited: usize,
75}
76
77/// What an update to a drawing touched, in the design's coordinates: each
78/// chunk it changed, where it was and where it is — and the strips the
79/// drawing's area gained or lost. `all` when the area's top-left moved, which
80/// moves every tile.
81#[derive(Debug, Clone, Default, PartialEq)]
82pub struct Damage {
83    pub rects: Vec<KRect>,
84    pub all: bool,
85}
86
87/// The property-tree nodes a scene node made — its transform, its opacity's
88/// effect and its clip — and the images its own commands paint, in order.
89#[derive(Clone)]
90pub(super) struct Rec {
91    pub(super) node: SceneId,
92    /// The space its box and chunk are in.
93    pub(super) space: Space,
94    pub(super) transform: Id,
95    pub(super) effect: Option<Id>,
96    pub(super) clip: Option<Id>,
97    pub(super) painted: Vec<(Painted, Origin)>,
98}
99
100impl Drawing {
101    /// The design's drawing: its root's box plus anything painted outside
102    /// it, cut by any clipping frame, in whole pixels.
103    pub fn area(&self) -> Frame {
104        self.list.area
105    }
106
107    /// Every node's box, in tree order, in the design's coordinates.
108    pub fn frames(&self) -> Vec<Frame> {
109        self.frames
110            .iter()
111            .map(|(o, f)| {
112                let at = self.list.space_at(&self.recs[o].space);
113                Frame {
114                    x: f.x + at.x as f32,
115                    y: f.y + at.y as f32,
116                    ..*f
117                }
118            })
119            .collect()
120    }
121
122    pub fn theme(&self) -> Theme {
123        self.theme
124    }
125
126    /// The tiles `view` needs at its own zoom's level, nearest its middle
127    /// first: what a compositor asks to be painted.
128    pub fn view_tiles(&self, view: &crate::tiles::View) -> Vec<(u32, u32)> {
129        let a = self.area();
130        let area = KRect::new(
131            a.x as f64,
132            a.y as f64,
133            (a.x + a.width) as f64,
134            (a.y + a.height) as f64,
135        );
136        view.tiles(area, crate::tiles::Level::of(view.zoom))
137    }
138
139    /// The interaction of the topmost node at `(x, y)` — in the design's
140    /// coordinates — that has one, where its clipping frames show it.
141    pub fn action_at(&self, x: f32, y: f32) -> Option<&str> {
142        self.list.action_at(x as f64, y as f64)
143    }
144
145    /// Updates the drawing to `new` by the patch that made it: `applied`,
146    /// what [`crate::wire::apply`] did to the scene the drawing showed — the
147    /// subtrees removed and placed and the nodes changed, by id. Only those
148    /// are laid out and painted again, and nodes that only moved keep their
149    /// chunks — the work is the patch's, never the scene's. The damage it
150    /// returns is what changed; or why `new` can't be painted — its images
151    /// too many for the GPU's atlas — in which case the drawing is `new`'s
152    /// all the same.
153    pub fn update(
154        &mut self,
155        renderer: &mut Renderer,
156        new: &Scene,
157        applied: &crate::wire::Applied,
158    ) -> Result<Damage, Undrawable> {
159        renderer.update(self, new, applied)
160    }
161
162    /// Rounds anew, before `area` — in the design's coordinates — is painted,
163    /// hit or read, the children of runs there that an update moved by a
164    /// fraction of a pixel: an update moves them by the sums alone and leaves
165    /// their boxes and chunks as they were, so an edit costs what it changed
166    /// and the rounding costs what is read. `scene` is the scene the drawing
167    /// shows. After it, the drawing there is a fresh one's, box for box.
168    pub fn prepare(&mut self, renderer: &mut Renderer, scene: &Scene, area: KRect) {
169        let mut rounded = false;
170        loop {
171            let stale = self.list.unrounded(area);
172            if stale.is_empty() {
173                break;
174            }
175            for (drid, key) in stale {
176                renderer.reround(self, scene, drid, &key);
177            }
178            self.list.flush();
179            rounded = true;
180        }
181        if rounded {
182            renderer.settle_area(self);
183        }
184    }
185
186    /// [`Drawing::prepare`] over the whole drawing.
187    pub fn prepare_all(&mut self, renderer: &mut Renderer, scene: &Scene) {
188        let everything = KRect::new(-1e12, -1e12, 1e12, 1e12);
189        self.prepare(renderer, scene, everything);
190    }
191
192    /// [`Drawing::update`] from `old`, the scene the drawing shows, to the
193    /// content of `new`, by the patch between them — `old` patched to it,
194    /// keeping its places, so it stays the scene the drawing shows: for a
195    /// caller holding both.
196    pub fn update_between(
197        &mut self,
198        renderer: &mut Renderer,
199        old: &mut Scene,
200        new: &Scene,
201    ) -> Result<Damage, Undrawable> {
202        let mut uses = crate::wire::Uses::of(&old.root);
203        let applied = crate::wire::bring(old, &mut uses, new)
204            .expect("the patch between two scenes applies to the first");
205        renderer.update(self, old, &applied)
206    }
207}
208
209impl Renderer {
210    /// The scene laid out once and made into its display list in `theme` —
211    /// every color its value there — or why it can't be drawn: images and
212    /// shadows that don't fit the GPU's atlas together. What the PNG, the
213    /// canvas and `devpage check` all start from.
214    ///
215    /// Each image is painted under a blob of its own, made in the order the
216    /// drawing first paints it — except that a drawing made `after` the one
217    /// a canvas last showed, painting the same images in the same order,
218    /// paints that drawing's: the GPU then keeps the atlas it has, which
219    /// holds exactly them, placed as a fresh one would.
220    pub fn drawing(
221        &mut self,
222        scene: &Scene,
223        theme: Theme,
224        after: Option<&Drawing>,
225    ) -> Result<Drawing, Undrawable> {
226        self.register(&scene.fonts);
227        let mut laid = Laid::build(self, scene);
228        let hugs = std::mem::take(&mut laid.hugs);
229        laid.settle_top(self, &hugs);
230        let root = laid.root;
231        let mut d = Drawing {
232            laid,
233            theme,
234            frames: BTreeMap::new(),
235            recs: BTreeMap::new(),
236            shown: HashMap::new(),
237            shifts: HashMap::new(),
238            contexts: HashMap::new(),
239            orders: HashMap::new(),
240            list: List::new(),
241            blobs: HashMap::new(),
242            images: Vec::new(),
243            shows: Vec::new(),
244            painters: BTreeSet::new(),
245            fits: Ok(()),
246            visited: 0,
247        };
248        self.blobs.clear();
249        let mut made = Vec::new();
250        let (t, c) = (d.list.root_transform, d.list.root_clip);
251        let mut walk = Walk {
252            scene,
253            dirty: &|_| true,
254            reflow: None,
255            path: None,
256            damage: None,
257            fresh: Some(&mut made),
258            images: true,
259        };
260        self.sync(
261            &mut d,
262            &mut walk,
263            &scene.root,
264            Vec::new(),
265            Owner::Top,
266            root,
267            t,
268            Place::default(),
269            (&Space::Root, Vec2::ZERO),
270            c,
271            None,
272            None,
273            (false, true),
274            None,
275        );
276        d.list.load(made);
277        d.blobs = std::mem::take(&mut self.blobs);
278        self.settle_area(&mut d);
279        self.settle_images(&mut d)?;
280        if let Some(last) = after
281            && last.shows.len() == d.shows.len()
282            && last.shows.iter().zip(&d.shows).all(|(a, b)| a.same(b))
283        {
284            let last_of: HashMap<u64, ImageData> = d
285                .shows
286                .iter()
287                .zip(&last.shows)
288                .map(|(new, old)| (new.data.data.id(), old.data.clone()))
289                .collect();
290            d.list.swap_images(&last_of);
291            for rec in d.recs.values_mut() {
292                for (p, _) in &mut rec.painted {
293                    if let Some(old) = last_of.get(&p.data.data.id()) {
294                        p.data = old.clone();
295                    }
296                }
297            }
298            for data in d.blobs.values_mut() {
299                if let Some(old) = last_of.get(&data.data.id()) {
300                    *data = old.clone();
301                }
302            }
303            d.shows = last.shows.clone();
304            d.images = d.shows.iter().map(|p| p.data.clone()).collect();
305        }
306        Ok(d)
307    }
308
309    /// The drawing's area from its root's box and what its chunks cover,
310    /// rounded out to whole pixels, ignoring the slivers floating point
311    /// leaves on a curve's bounds.
312    pub(super) fn settle_area(&mut self, d: &mut Drawing) {
313        let root = d.frames[&Vec::new()];
314        let mut area = frame_rect(root);
315        if let Some(covered) = d.list.covered() {
316            area = area.union(covered);
317        }
318        const SLIVER: f64 = 0.01;
319        let area = KRect::new(
320            (area.x0 + SLIVER).floor(),
321            (area.y0 + SLIVER).floor(),
322            (area.x1 - SLIVER).ceil(),
323            (area.y1 - SLIVER).ceil(),
324        );
325        d.list.set_area(Frame {
326            x: area.x0 as f32,
327            y: area.y0 as f32,
328            width: area.width() as f32,
329            height: area.height() as f32,
330        });
331    }
332
333    /// The images the drawing paints, in the order it first paints them —
334    /// each image file once — and whether they fit the GPU's atlas together.
335    pub(super) fn settle_images(&mut self, d: &mut Drawing) -> Result<(), Undrawable> {
336        d.fits = self.fit_images(d);
337        d.fits.clone()
338    }
339
340    pub(super) fn fit_images(&mut self, d: &mut Drawing) -> Result<(), Undrawable> {
341        let mut files = BTreeSet::new();
342        let mut painted: Vec<(Painted, Origin)> = Vec::new();
343        for order in &d.painters {
344            for (p, at) in &d.recs[order].painted {
345                if p.file.is_none_or(|id| files.insert(id)) {
346                    painted.push((p.clone(), at.clone()));
347                }
348            }
349        }
350        d.blobs.retain(|id, _| files.contains(id));
351        d.shows = painted.iter().map(|(p, _)| p.clone()).collect();
352        d.images = d.shows.iter().map(|p| p.data.clone()).collect();
353        let sizes: Vec<(u32, u32)> = painted
354            .iter()
355            .map(|(p, _)| (p.data.width, p.data.height))
356            .collect();
357        if let Err(n) = crate::atlas::place(&sizes) {
358            let (Painted { data, .. }, at) = &painted[n];
359            let what = if at.file.is_empty() {
360                "an image"
361            } else {
362                "this"
363            };
364            return Err(Undrawable {
365                message: format!(
366                    "{what} doesn't fit: the images a drawing paints — its image files, \
367                     at most {side} pixels a side, and its shadows — share the GPU's one \
368                     {atlas}×{atlas} atlas, and the {n} before this {w}×{h} fill it",
369                    side = resolve::MAX_IMAGE_SIDE,
370                    atlas = crate::atlas::SIDE,
371                    w = data.width,
372                    h = data.height,
373                ),
374                at: Some(at.clone()),
375            });
376        }
377        Ok(())
378    }
379
380    /// Makes `node`'s chunk again when it is dirty, new or another size, or
381    /// moves it when only its place changed; then its children's. `remade`
382    /// says its parent's clip or effect was made anew, which its own
383    /// commands are under; `all` that every node under it is to be visited.
384    /// `space` is the space it is in and where that space's origin is in the
385    /// design's coordinates; `item`, the run and key when it is a run's
386    /// child, whose place the run's sums keep.
387    #[allow(clippy::too_many_arguments)]
388    pub(super) fn sync(
389        &mut self,
390        d: &mut Drawing,
391        w: &mut Walk,
392        node: &Node,
393        order: Order,
394        owner: Owner,
395        id: NodeId,
396        parent_t: Id,
397        parent: Place,
398        (space, space_at): (&Space, Vec2),
399        clip: Id,
400        cut: Option<KRect>,
401        effect: Option<Id>,
402        (remade, all_below): (bool, bool),
403        item: Option<(RunId, OrderKey)>,
404    ) -> bool {
405        let (abs, place) = placed(d.laid.tree(owner), id, parent, order.is_empty());
406        let f = Frame {
407            x: abs.x - space_at.x as f32,
408            y: abs.y - space_at.y as f32,
409            ..abs
410        };
411        let parent_origin = Vec2::new(
412            parent.at.0 as f64 - space_at.x,
413            parent.at.1 as f64 - space_at.y,
414        );
415        let origin = Vec2::new(f.x as f64, f.y as f64);
416        d.visited += 1;
417        let old = d.recs.remove(&order);
418        let is_new = old.is_none();
419        let was = d.frames.insert(order.clone(), f);
420        let resized = was.is_none_or(|was| (was.width, was.height) != (f.width, f.height));
421        let moved = was.is_none_or(|was| (was.x, was.y) != (f.x, f.y));
422        let cut_changed = d.list.chunk(&order).is_none_or(|c| c.cut != cut);
423        let dirty = (w.dirty)(&order);
424        let remake = old.is_none() || remade || dirty || resized;
425        let trees = &mut d.list.trees;
426        let offset = if item.is_some() {
427            Vec2::ZERO
428        } else {
429            origin - parent_origin
430        };
431        // Its transform is kept, and moved: its children's are under it.
432        let transform = match &old {
433            Some(rec) => {
434                let t = trees.transforms.get_mut(rec.transform);
435                t.offset = offset;
436                t.item = item.clone();
437                rec.transform
438            }
439            None => trees.transforms.add(Transform {
440                parent: Some(parent_t),
441                offset,
442                item: item.clone(),
443            }),
444        };
445        let mut children_remade = remade;
446        let (own_effect, own_clip) = if remake {
447            // Its opacity and its clip are changed in place where they stay,
448            // so what its children paint under them still names them.
449            let opacity = (node.opacity < 1.0).then_some(node.opacity);
450            let had = old.as_ref().and_then(|r| r.effect);
451            let new_effect = match (had, opacity) {
452                (Some(e), Some(o)) if !remade => {
453                    trees.effects.get_mut(e).blend = Blend::Opacity(o);
454                    Some(e)
455                }
456                (had, o) => {
457                    if let Some(e) = had {
458                        trees.effects.remove(e);
459                    }
460                    children_remade |= had.is_some() || o.is_some();
461                    o.map(|o| {
462                        trees.effects.add(Effect {
463                            parent: effect,
464                            clip,
465                            transform,
466                            blend: Blend::Opacity(o),
467                            bounds: None,
468                        })
469                    })
470                }
471            };
472            let shape = node.clip.then(|| {
473                node_shape(
474                    node,
475                    KRect::new(0.0, 0.0, f.width as f64, f.height as f64),
476                    0.0,
477                )
478            });
479            let had = old.as_ref().and_then(|r| r.clip);
480            let new_clip = match (had, shape) {
481                (Some(c), Some(path)) if !remade => {
482                    trees.clips.get_mut(c).path = path;
483                    Some(c)
484                }
485                (had, shape) => {
486                    if let Some(c) = had {
487                        trees.clips.remove(c);
488                    }
489                    children_remade |= had.is_some() || shape.is_some();
490                    shape.map(|path| {
491                        trees.clips.add(Clip {
492                            parent: Some(clip),
493                            transform,
494                            path,
495                        })
496                    })
497                }
498            };
499            (new_effect, new_clip)
500        } else {
501            let rec = old.as_ref().expect("kept");
502            (rec.effect, rec.clip)
503        };
504        let under = own_effect.or(effect);
505        let was = d.list.take(&order);
506        let (chunk, painted) = match was {
507            Some(c) if !remake => {
508                let moved = c.origin != origin || c.cut != cut;
509                let at = d.list.space_at(space);
510                if moved && let Some(damage) = w.damage.as_deref_mut() {
511                    damage.rects.extend(c.bounds.map(|b| b + at));
512                }
513                let c = c.moved(origin, cut);
514                if moved && let Some(damage) = w.damage.as_deref_mut() {
515                    damage.rects.extend(c.bounds.map(|b| b + space_at));
516                }
517                (c, old.expect("kept").painted)
518            }
519            was => {
520                if let Some(c) = &was {
521                    if let Some(damage) = w.damage.as_deref_mut() {
522                        let at = d.list.space_at(&c.space);
523                        damage.rects.extend(c.bounds.map(|b| b + at));
524                    }
525                    d.list.free(&c.made);
526                }
527                let local = Frame {
528                    x: 0.0,
529                    y: 0.0,
530                    width: f.width,
531                    height: f.height,
532                };
533                self.painted.clear();
534                let mut covers = Vec::new();
535                let mut b = Builder::new(&mut d.list.trees, transform, clip, under);
536                self.own(w.scene, node, local, d.theme, &mut b, &mut covers);
537                let mut built = b.finish();
538                let mut painted = std::mem::take(&mut self.painted);
539                // A shadow that paints the very pixels it painted before
540                // keeps its blobs, so the GPU keeps its atlas.
541                if let Some(rec) = &old
542                    && rec.painted.len() == painted.len()
543                    && rec
544                        .painted
545                        .iter()
546                        .zip(&painted)
547                        .all(|(a, b)| a.0.same(&b.0))
548                {
549                    let last_of: HashMap<u64, ImageData> = painted
550                        .iter()
551                        .zip(&rec.painted)
552                        .map(|(new, old)| (new.0.data.data.id(), old.0.data.clone()))
553                        .collect();
554                    swap(&mut built.0, &last_of);
555                    painted = rec.painted.clone();
556                }
557                let action = node
558                    .on_click
559                    .clone()
560                    .map(|a| (a, KRect::new(0.0, 0.0, f.width as f64, f.height as f64)));
561                let c = Chunk::new(transform, space.clone(), origin, built, covers, action, cut);
562                if let Some(damage) = w.damage.as_deref_mut() {
563                    damage.rects.extend(c.bounds.map(|b| b + space_at));
564                }
565                if !painted.is_empty() || old.as_ref().is_some_and(|r| !r.painted.is_empty()) {
566                    w.images = true;
567                }
568                (c, painted)
569            }
570        };
571        if painted.is_empty() {
572            d.painters.remove(&order);
573        } else {
574            d.painters.insert(order.clone());
575        }
576        d.orders.insert(node.id.clone(), order.clone());
577        d.recs.insert(
578            order.clone(),
579            Rec {
580                node: node.id.clone(),
581                space: space.clone(),
582                transform,
583                effect: own_effect,
584                clip: own_clip,
585                painted,
586            },
587        );
588        match w.fresh.as_deref_mut() {
589            Some(fresh) if *space == Space::Root => fresh.push((order.clone(), chunk)),
590            _ => d.list.put(order.clone(), chunk),
591        }
592        let rect = frame_rect(f);
593        let (inner, inner_cut) = match own_clip {
594            Some(c) => (c, Some(cut.map_or(rect, |k| rect.intersect(k)))),
595            None => (clip, cut),
596        };
597        // Its children: all of them when what they are placed by or drawn
598        // under changed; otherwise only those on a changed node's path, and
599        // the rest too when one of those changed size, which can move them.
600        let all = w.path.is_none()
601            || all_below
602            || is_new
603            || dirty
604            || resized
605            || moved
606            || cut_changed
607            || children_remade
608            || w.reflow.is_some_and(|r| r.contains(&order));
609        if leaf_of(node, &w.scene.images).is_some() {
610            return resized;
611        }
612        let on_path = match (all, w.path) {
613            (false, Some(path)) => path.get(&order).cloned().unwrap_or_default(),
614            _ => BTreeSet::new(),
615        };
616        let down = Down {
617            transform,
618            place,
619            space,
620            space_at,
621            clip: inner,
622            cut: inner_cut,
623            effect: under,
624            remade: children_remade,
625        };
626        if let Some(&rid) = d.laid.run_of.get(&order) {
627            // A run's children are placed by its sums: its own move or change
628            // of size moves them without a visit — what does need one, a
629            // move by a fraction of a pixel, a new breadth or a new padding,
630            // the run finds itself.
631            let every = w.path.is_none() || all_below || is_new || dirty || children_remade;
632            let on_path = match w.path {
633                Some(path) if !every => path.get(&order).cloned().unwrap_or_default(),
634                _ => BTreeSet::new(),
635            };
636            self.sync_run(
637                d, w, node, &order, owner, id, rid, &down, every, &on_path, is_new,
638            );
639            return resized;
640        }
641        let visit = |r: &mut Renderer, d: &mut Drawing, w: &mut Walk, k: &OrderKey, all: bool| {
642            let Some(child) = node.children.get(k) else {
643                return false;
644            };
645            let mut o = order.clone();
646            o.push(k.clone());
647            let (owner, id) = d.laid.nodes[&o];
648            r.sync(
649                d,
650                w,
651                child,
652                o,
653                owner,
654                id,
655                down.transform,
656                down.place,
657                (down.space, down.space_at),
658                down.clip,
659                down.cut,
660                down.effect,
661                (down.remade, all),
662                None,
663            )
664        };
665        // Only in auto layout does one child's size move its siblings.
666        let flows = node.layout != resolve::Layout::None;
667        if all {
668            for k in node.children.entries().map(|(k, _)| k) {
669                visit(self, d, w, k, all_below);
670            }
671        } else {
672            let mut moves = false;
673            for k in &on_path {
674                moves |= visit(self, d, w, k, false);
675            }
676            if moves && flows {
677                for k in node.children.entries().map(|(k, _)| k) {
678                    if !on_path.contains(k) {
679                        visit(self, d, w, k, false);
680                    }
681                }
682            }
683        }
684        resized
685    }
686
687    /// A run's children: each placed by the run's sums, those `all` or on
688    /// the path visited, and — when one of them changed size by a fraction
689    /// of a pixel, which moves where every child after it rounds — every
690    /// child after it too. A child that changed size moves the ones after it
691    /// by the sums alone; their damage is one rectangle, from where the
692    /// first change starts to where the run ends.
693    #[allow(clippy::too_many_arguments)]
694    fn sync_run(
695        &mut self,
696        d: &mut Drawing,
697        w: &mut Walk,
698        node: &Node,
699        order: &Order,
700        owner: Owner,
701        id: NodeId,
702        rid: u32,
703        down: &Down,
704        all: bool,
705        on_path: &BTreeSet<OrderKey>,
706        is_new: bool,
707    ) {
708        let size = d.laid.tree(owner).unrounded_layout(id).size;
709        let Some((run, asked, lead)) = d.laid.run_places(rid, size) else {
710            return;
711        };
712        let (pc0, _) = run.padding_across();
713        let (row, gap, line) = (run.row, run.gap as f64, asked.line);
714        let drid = match d.shown.get(&rid) {
715            Some(&r) => r,
716            None => {
717                let r = d.list.add_run(down.space.clone());
718                d.shown.insert(rid, r);
719                r
720            }
721        };
722        let raw = Vec2::new(
723            down.place.raw.0 as f64 - down.space_at.x,
724            down.place.raw.1 as f64 - down.space_at.y,
725        );
726        let at = Vec2::new(
727            down.place.at.0 as f64 - down.space_at.x,
728            down.place.at.1 as f64 - down.space_at.y,
729        );
730        let old_entry = d.list.run(drid).entry().map(|e| e + down.space_at);
731        let changed = {
732            let dr = d.list.run(drid);
733            // What every child's own layout or record holds: its direction,
734            // the gap each carries, its line's breadth. A move of the run, by
735            // any amount, or of where its first child starts, is only where
736            // the sums place them — rounded again lazily, when read.
737            is_new || dr.row != row || dr.gap != gap || dr.line != line as f64
738        };
739        {
740            let dr = d.list.run_mut(drid);
741            dr.raw = raw;
742            dr.at = at;
743            dr.origin = down.space_at;
744            dr.row = row;
745            dr.start = lead as f64;
746            dr.across = pc0 as f64;
747            dr.gap = gap;
748            dr.line = line as f64;
749            dr.cut = down.cut;
750        }
751        d.list.resum(drid);
752        let every = all || changed;
753        let local = |v: (f32, f32)| (v.0 - down.space_at.x as f32, v.1 - down.space_at.y as f32);
754        d.contexts.insert(
755            drid,
756            RunContext {
757                order: order.clone(),
758                owner,
759                id,
760                rid,
761                transform: down.transform,
762                place: Place {
763                    at: local(down.place.at),
764                    raw: local(down.place.raw),
765                },
766                space: down.space.clone(),
767                clip: down.clip,
768                effect: down.effect,
769            },
770        );
771        let mut shift: Option<f64> = d.shifts.remove(&drid);
772        let keys: Vec<OrderKey> = if every {
773            node.children.entries().map(|(k, _)| k.clone()).collect()
774        } else {
775            on_path.iter().cloned().collect()
776        };
777        let mut queue: std::collections::VecDeque<OrderKey> = keys.into();
778        let mut seen = BTreeSet::new();
779        while let Some(k) = queue.pop_front() {
780            if !seen.insert(k.clone()) {
781                continue;
782            }
783            let Some(child) = node.children.get(&k) else {
784                continue;
785            };
786            let (run, asked, _) = d.laid.run_places(rid, size).expect("placed");
787            let Some(s) = asked.sizes.get(&k).copied() else {
788                continue;
789            };
790            let stretch = run.children[&k].stretch;
791            let offset = run.across(line, &s, stretch) as f64;
792            let child_node = run.children[&k].node;
793            self.relay(&mut d.laid.runs, rid, &k, line);
794            let (before, old_size) = {
795                let dr = d.list.run(drid);
796                let before = dr.prefix(&k);
797                (before, dr.child(&k).map(|(_, p)| p.size))
798            };
799            let item = child_place(down.place, row, lead as f64 + before, pc0 as f64 + offset);
800            let item_at = Vec2::new(item.at.0 as f64, item.at.1 as f64);
801            let mut o = order.clone();
802            o.push(k.clone());
803            self.sync(
804                d,
805                w,
806                child,
807                o,
808                Owner::Run(rid),
809                child_node,
810                down.transform,
811                item,
812                (&Space::Item(drid, k.clone()), item_at),
813                down.clip,
814                None,
815                down.effect,
816                (down.remade, every),
817                Some((drid, k.clone())),
818            );
819            let delta = {
820                let (raw, at) = d.list.run(drid).place(before, offset);
821                at - raw
822            };
823            d.list.place_child(
824                drid,
825                k.clone(),
826                Some(Placed::new(s.along as f64, gap, offset, delta)),
827            );
828            let grew = old_size.map_or(s.along as f64 + gap, |o| s.along as f64 - o);
829            if grew != 0.0 {
830                let start = d.list.space_at(down.space) + d.list.run(drid).place(before, offset).1;
831                let start = if row { start.x } else { start.y };
832                // The children after it move by the sums; any it moves by a
833                // fraction of a pixel round anew when they are next read.
834                shift = Some(shift.map_or(start, |s: f64| s.min(start)));
835            }
836        }
837        d.list.flush();
838        if let Some(start) = shift
839            && let Some(damage) = w.damage.as_deref_mut()
840        {
841            let new_entry = d.list.run(drid).entry().map(|e| e + down.space_at);
842            if let Some(both) = match (old_entry, new_entry) {
843                (Some(a), Some(b)) => Some(a.union(b)),
844                (a, b) => a.or(b),
845            } {
846                let r = if row {
847                    KRect::new(start.max(both.x0), both.y0, both.x1, both.y1)
848                } else {
849                    KRect::new(both.x0, start.max(both.y0), both.x1, both.y1)
850                };
851                if r.width() > 0.0 && r.height() > 0.0 {
852                    damage.rects.push(r);
853                }
854            }
855        }
856    }
857}
858
859impl Renderer {
860    /// Run `drid`'s child `key` rounded where the run's sums now place it:
861    /// its subtree's boxes and chunks made again as a walk through its run
862    /// would make them.
863    pub(super) fn reround(&mut self, d: &mut Drawing, scene: &Scene, drid: RunId, key: &OrderKey) {
864        let Some(ctx) = d.contexts.get(&drid).cloned() else {
865            return;
866        };
867        let space_at = d.list.space_at(&ctx.space);
868        let abs = |v: (f32, f32)| (v.0 + space_at.x as f32, v.1 + space_at.y as f32);
869        let place = Place {
870            at: abs(ctx.place.at),
871            raw: abs(ctx.place.raw),
872        };
873        let size = d.laid.tree(ctx.owner).unrounded_layout(ctx.id).size;
874        let Some((run, asked, lead)) = d.laid.run_places(ctx.rid, size) else {
875            return;
876        };
877        let line = asked.line;
878        let Some(s) = asked.sizes.get(key).copied() else {
879            return;
880        };
881        let offset = run.across(line, &s, run.children[key].stretch) as f64;
882        let (pc0, _) = run.padding_across();
883        let (row, gap, child_node) = (run.row, run.gap as f64, run.children[key].node);
884        self.relay(&mut d.laid.runs, ctx.rid, key, line);
885        let before = d.list.run(drid).prefix(key);
886        let item = child_place(place, row, lead as f64 + before, pc0 as f64 + offset);
887        let item_at = Vec2::new(item.at.0 as f64, item.at.1 as f64);
888        let mut order = ctx.order.clone();
889        order.push(key.clone());
890        let Some(node) = scene.at(&order) else {
891            return;
892        };
893        let mut walk = Walk {
894            scene,
895            dirty: &|_| false,
896            reflow: None,
897            path: None,
898            damage: None,
899            fresh: None,
900            images: false,
901        };
902        self.blobs = std::mem::take(&mut d.blobs);
903        self.sync(
904            d,
905            &mut walk,
906            node,
907            order,
908            Owner::Run(ctx.rid),
909            child_node,
910            ctx.transform,
911            item,
912            (&Space::Item(drid, key.clone()), item_at),
913            ctx.clip,
914            None,
915            ctx.effect,
916            (false, true),
917            Some((drid, key.clone())),
918        );
919        d.blobs = std::mem::take(&mut self.blobs);
920        let delta = {
921            let (raw, at) = d.list.run(drid).place(before, offset);
922            at - raw
923        };
924        d.list.place_child(
925            drid,
926            key.clone(),
927            Some(Placed::new(s.along as f64, gap, offset, delta)),
928        );
929        if walk.images {
930            let _ = self.settle_images(d);
931        }
932    }
933}
934
935/// Where a run is and what its children are drawn under, as the last walk
936/// through it found: what rounding a child of it again, lazily, starts from.
937/// Its places are in its space's coordinates.
938#[derive(Clone)]
939pub(super) struct RunContext {
940    pub(super) order: Order,
941    pub(super) owner: Owner,
942    pub(super) id: NodeId,
943    pub(super) rid: u32,
944    pub(super) transform: Id,
945    pub(super) place: Place,
946    pub(super) space: Space,
947    pub(super) clip: Id,
948    pub(super) effect: Option<Id>,
949}
950
951/// What a node's children are visited under.
952pub(super) struct Down<'a> {
953    pub(super) transform: Id,
954    pub(super) place: Place,
955    pub(super) space: &'a Space,
956    pub(super) space_at: Vec2,
957    pub(super) clip: Id,
958    pub(super) cut: Option<KRect>,
959    pub(super) effect: Option<Id>,
960    pub(super) remade: bool,
961}
962
963/// What a [`Renderer::sync`] walk reads and adds to.
964pub(super) struct Walk<'a> {
965    pub(super) scene: &'a Scene,
966    /// Whether a node's own attributes changed.
967    pub(super) dirty: &'a dyn Fn(&Order) -> bool,
968    /// The nodes whose children were added or removed: all their children
969    /// may have moved.
970    pub(super) reflow: Option<&'a BTreeSet<Order>>,
971    /// For an update, the children of each node on a changed node's path
972    /// that lead to one; `None` walks every node.
973    pub(super) path: Option<&'a HashMap<Order, BTreeSet<OrderKey>>>,
974    pub(super) damage: Option<&'a mut Damage>,
975    /// For a new drawing, the chunks made, to bulk-load the index with.
976    pub(super) fresh: Option<&'a mut Vec<(Order, Chunk)>>,
977    /// Whether a chunk made again paints an image, or painted one.
978    pub(super) images: bool,
979}
980
981/// The commands' images swapped for the blobs `last_of` names.
982pub(super) fn swap(commands: &mut [crate::display::Command], last_of: &HashMap<u64, ImageData>) {
983    for c in commands {
984        if let Draw::Fill(_, Paint::Image(data, _)) = &mut c.draw
985            && let Some(old) = last_of.get(&data.data.id())
986        {
987            *data = old.clone();
988        }
989    }
990}
991
992#[cfg(test)]
993mod tests;