1#![allow(clippy::collapsible_if)]
5
6mod color;
7mod debug_overlay;
8mod filters;
9mod gradient;
10mod kurbo_css;
11mod layers;
12mod render;
13
14pub use layers::{LayerSite, SceneLayerCounts, latest_scene_layers};
15mod sizing;
16mod text;
17
18use std::collections::HashMap;
19
20use anyrender::{PaintScene, Scene};
21use blitz_dom::{BaseDocument, NodeId, util::Color};
22use render::BlitzDomPainter;
23
24const FONT_EMBOLDEN_ENABLED: bool = cfg!(any(
25 feature = "font-embolden",
26 all(feature = "apple-font-embolden", target_os = "macos"),
27 all(feature = "apple-font-embolden", target_os = "ios"),
28));
29
30const SELECTION_COLOR: Color = Color::from_rgb8(180, 213, 255);
32
33type CustomWidgetSceneMap = HashMap<(usize, NodeId), Scene>;
35
36pub fn paint_scene(
46 scene: &mut impl PaintScene,
47 doc: &mut BaseDocument,
48 scale: f64,
49 width: u32,
50 height: u32,
51 x_offset: u32,
52 y_offset: u32,
53) {
54 paint_scene_at_time(scene, doc, scale, width, height, x_offset, y_offset, 0.0)
55}
56
57#[allow(clippy::too_many_arguments)]
64pub fn paint_scene_at_time(
65 scene: &mut impl PaintScene,
66 doc: &mut BaseDocument,
67 scale: f64,
68 width: u32,
69 height: u32,
70 x_offset: u32,
71 y_offset: u32,
72 animation_time: f64,
73) {
74 #[allow(unused_mut)]
79 let mut custom_widget_scenes: CustomWidgetSceneMap = HashMap::new();
80 #[cfg(feature = "custom-widget")]
81 build_custom_widget_scenes(&mut custom_widget_scenes, doc, scene, scale);
82
83 let generator = BlitzDomPainter::new(
84 doc,
85 scale,
86 width,
87 height,
88 x_offset as f64,
89 y_offset as f64,
90 &custom_widget_scenes,
91 animation_time,
92 );
93 generator.paint_scene(scene);
94
95 }
102
103#[cfg(feature = "custom-widget")]
104fn build_custom_widget_scenes(
105 custom_widget_scenes: &mut CustomWidgetSceneMap,
106 doc: &mut BaseDocument,
107 render_ctx: &mut impl anyrender::RenderContext,
108 scale: f64,
109) {
110 let doc_id = doc.id();
111
112 let custom_widget_node_ids = doc.custom_widget_node_ids();
114 for node_id in custom_widget_node_ids.into_iter() {
115 if let Some(scene) = process_custom_widget_node(doc, render_ctx, node_id, scale) {
116 custom_widget_scenes.insert((doc_id, node_id), scene);
117 }
118 }
119
120 let sub_document_node_ids = doc.sub_document_node_ids();
122 for node_id in sub_document_node_ids.into_iter() {
123 if let Some(sub_doc) = doc.get_node_mut(node_id).and_then(|node| node.subdoc_mut()) {
124 let mut inner = sub_doc.inner_mut();
125 build_custom_widget_scenes(custom_widget_scenes, &mut inner, render_ctx, scale);
126 }
127 }
128}
129
130#[cfg(feature = "custom-widget")]
131fn process_custom_widget_node(
132 doc: &mut BaseDocument,
133 render_ctx: &mut impl anyrender::RenderContext,
134 node_id: NodeId,
135 scale: f64,
136) -> Option<Scene> {
137 use blitz_dom::node::{CustomWidgetStatus, ProxyRenderContext};
138
139 let node = doc.get_node_mut(node_id)?;
140 let width = (node.final_layout().size.width as f64 * scale) as u32;
141 let height = (node.final_layout().size.height as f64 * scale) as u32;
142
143 if width == 0 || height == 0 {
144 return None;
145 }
146
147 let style = (*node.stylo_element_data().primary_styles()?).clone();
148 let element = node.data.downcast_element_mut()?;
149 let widget_data = element.custom_widget_data_mut()?;
150
151 let mut render_ctx = ProxyRenderContext {
152 inner: render_ctx,
153 resource_ids: &mut widget_data.active_resource_ids,
154 };
155
156 if widget_data.status == CustomWidgetStatus::Suspended {
157 widget_data.widget.can_create_surfaces(&mut render_ctx);
158 widget_data.status = CustomWidgetStatus::Active;
159 }
160
161 let widget_scene = widget_data
162 .widget
163 .paint(&mut render_ctx, &style, width, height, scale);
164
165 Some(widget_scene)
166}