1use crate::{animation::Interpolator, cache::CachedData, prelude::*};
2use morphorm::Node;
3use skia_safe::{
4 canvas::SaveLayerRec, ClipOp, ImageFilter, Matrix, Paint, Rect, SamplingOptions, Surface,
5};
6use std::cmp::Ordering;
7use std::collections::BinaryHeap;
8use vizia_storage::{DrawChildIterator, LayoutTreeIterator};
9use vizia_style::BlendMode;
10
11pub(crate) fn transform_system(cx: &mut Context) {
12 let iter = LayoutTreeIterator::full(&cx.tree);
13
14 for entity in iter {
15 let bounds = cx.cache.bounds.get(entity).copied().unwrap();
16 if let Some(parent) = cx.tree.get_layout_parent(entity) {
17 let parent_transform = cx.cache.transform.get(parent).copied().unwrap();
18 if let Some(tx) = cx.cache.transform.get_mut(entity) {
19 let scale_factor = cx.style.scale_factor();
20
21 let mut origin = cx
23 .style
24 .transform_origin
25 .get(entity)
26 .map(|transform_origin| {
27 let mut origin = skia_safe::Matrix::translate(bounds.top_left());
28 let offset = transform_origin.as_transform(bounds, scale_factor);
29 origin = offset * origin;
30 origin
31 })
32 .unwrap_or(skia_safe::Matrix::translate(bounds.center()));
33 let mut transform = origin;
35 origin = origin.invert().unwrap();
36
37 if let Some(translate) = cx.style.translate.get(entity) {
39 transform = transform * translate.as_transform(bounds, scale_factor);
40 }
41
42 if let Some(rotate) = cx.style.rotate.get(entity) {
44 transform = transform * rotate.as_transform(bounds, scale_factor);
45 }
46
47 if let Some(scale) = cx.style.scale.get(entity) {
49 transform = transform * scale.as_transform(bounds, scale_factor);
50 }
51
52 if let Some(transforms) = cx.style.transform.get(entity) {
54 if let Some(animation_state) = cx.style.transform.get_active_animation(entity) {
58 if let Some(start) = animation_state.keyframes.first() {
59 if let Some(end) = animation_state.keyframes.last() {
60 let start_transform =
61 start.value.as_transform(bounds, scale_factor);
62 let end_transform = end.value.as_transform(bounds, scale_factor);
63 let t = animation_state.t;
64 let animated_transform = skia_safe::Matrix::interpolate(
65 &start_transform,
66 &end_transform,
67 t,
68 );
69 transform = transform * animated_transform;
70 }
71 }
72 } else {
73 transform = transform * transforms.as_transform(bounds, scale_factor);
74 }
75 }
76
77 transform = transform * origin;
78
79 *tx = parent_transform * transform;
80 }
81
82 let overflowx = cx.style.overflowx.get(entity).copied().unwrap_or_default();
83 let overflowy = cx.style.overflowy.get(entity).copied().unwrap_or_default();
84
85 let scale = cx.style.scale_factor();
86
87 let clip_bounds = cx
88 .style
89 .clip_path
90 .get(entity)
91 .map(|clip| match clip {
92 ClipPath::Auto => bounds,
93 ClipPath::Shape(rect) => bounds.shrink_sides(
94 rect.3.to_pixels(bounds.w, scale),
95 rect.0.to_pixels(bounds.h, scale),
96 rect.1.to_pixels(bounds.w, scale),
97 rect.2.to_pixels(bounds.h, scale),
98 ),
99 })
100 .unwrap_or(bounds);
101
102 let root_bounds = BoundingBox::from_min_max(
103 -f32::MAX / 2.0,
104 -f32::MAX / 2.0,
105 f32::MAX / 2.0,
106 f32::MAX / 2.0,
107 );
108
109 let clip_bounds = match (overflowx, overflowy) {
110 (Overflow::Visible, Overflow::Visible) => root_bounds,
111 (Overflow::Hidden, Overflow::Visible) => {
112 let left = clip_bounds.left();
113 let right = clip_bounds.right();
114 let top = root_bounds.top();
115 let bottom = root_bounds.bottom();
116 BoundingBox::from_min_max(left, top, right, bottom)
117 }
118 (Overflow::Visible, Overflow::Hidden) => {
119 let left = root_bounds.left();
120 let right = root_bounds.right();
121 let top = clip_bounds.top();
122 let bottom = clip_bounds.bottom();
123 BoundingBox::from_min_max(left, top, right, bottom)
124 }
125 (Overflow::Hidden, Overflow::Hidden) => clip_bounds,
126 };
127
128 let transform =
129 cx.cache.transform.get(entity).copied().unwrap_or(Matrix::new_identity());
130
131 let rect: skia_safe::Rect = clip_bounds.into();
132 let clip_bounds: BoundingBox = transform.map_rect(rect).0.into();
133
134 let parent_clip_bounds = cx.cache.clip_path.get(parent).copied().unwrap_or(root_bounds);
135
136 if let Some(clip_path) = cx.cache.clip_path.get_mut(entity) {
137 *clip_path = clip_bounds.intersection(&parent_clip_bounds);
138 } else {
139 cx.cache.clip_path.insert(entity, clip_bounds.intersection(&parent_clip_bounds));
140 }
141 }
142 }
143}
144
145pub(crate) fn draw_system(
146 cx: &mut Context,
147 window_entity: Entity,
148 surface: &mut Surface,
149 dirty_surface: &mut Surface,
150) -> bool {
151 if cx.windows.is_empty() {
152 return false;
153 }
154
155 if !cx.entity_manager.is_alive(window_entity) {
156 return false;
157 }
158
159 transform_system(cx);
160
161 let window = cx.windows.get_mut(&window_entity).unwrap();
162
163 let mut dirty_rect = std::mem::take(&mut window.dirty_rect);
164 let redraw_list = std::mem::take(&mut window.redraw_list);
165
166 for &entity in &redraw_list {
171 if cx.tree.is_ignored(entity) {
173 continue;
174 }
175
176 if cx.tree.is_window(entity) && entity != window_entity {
177 continue;
178 }
179
180 if entity.visible(&cx.style) {
181 let draw_bounds = draw_bounds(&cx.style, &cx.cache, &cx.tree, entity);
182
183 let mut dirty_bounds = draw_bounds;
184
185 if let Some(previous_draw_bounds) = cx.cache.draw_bounds.get(entity) {
186 dirty_bounds = dirty_bounds.union(previous_draw_bounds);
187 }
188
189 if dirty_bounds.w != 0.0 && dirty_bounds.h != 0.0 {
190 if let Some(dr) = &mut dirty_rect {
191 *dr = dr.union(&dirty_bounds);
192 } else {
193 dirty_rect = Some(dirty_bounds);
194 }
195 }
196
197 if let Some(dr) = cx.cache.draw_bounds.get_mut(entity) {
198 *dr = draw_bounds;
199 } else {
200 cx.cache.draw_bounds.insert(entity, draw_bounds);
201 }
202 }
203 }
204
205 let canvas = dirty_surface.canvas();
210
211 canvas.save();
212
213 if let Some(rect) = dirty_rect.map(Rect::from) {
214 canvas.clip_rect(rect, ClipOp::Intersect, false);
215 canvas.clear(Color::transparent());
216 }
217
218 cx.resource_manager.mark_images_unused();
219
220 let mut queue = BinaryHeap::new();
221 queue.push(ZEntity { index: 0, entity: window_entity, visible: true });
222
223 while let Some(zentity) = queue.pop() {
224 canvas.save();
225 draw_entity(
226 &mut DrawContext {
227 current: zentity.entity,
228 style: &cx.style,
229 cache: &mut cx.cache,
230 tree: &cx.tree,
231 models: &cx.models,
232 views: &mut cx.views,
233 resource_manager: &cx.resource_manager,
234 text_context: &mut cx.text_context,
235 modifiers: &cx.modifiers,
236 mouse: &cx.mouse,
237 windows: &mut cx.windows,
238 },
239 &dirty_rect,
240 canvas,
241 zentity.index,
242 &mut queue,
243 zentity.visible,
244 );
245 canvas.restore();
246 }
247
248 canvas.restore();
249
250 surface.canvas().clear(Color::transparent());
251 dirty_surface.draw(surface.canvas(), (0, 0), SamplingOptions::default(), None);
252
253 true
263}
264
265fn draw_entity(
266 cx: &mut DrawContext,
267 dirty_rect: &Option<BoundingBox>,
268 canvas: &Canvas,
269 current_z: i32,
270 queue: &mut BinaryHeap<ZEntity>,
271 visible: bool,
272) {
273 let current = cx.current;
274
275 if cx.display() == Display::None {
277 return;
278 }
279
280 let z_index = cx.z_index();
281
282 if z_index > current_z {
283 queue.push(ZEntity { index: z_index, entity: current, visible });
284 return;
285 }
286
287 let backdrop_filter = cx.backdrop_filter();
288 let blend_mode = cx.style.blend_mode.get(current).copied().unwrap_or_default();
289
290 canvas.save();
291 let layer_count =
292 if cx.opacity() != 1.0 || backdrop_filter.is_some() || blend_mode != BlendMode::Normal {
293 let mut paint = Paint::default();
294 paint.set_alpha_f(cx.opacity());
295 paint.set_blend_mode(blend_mode.into());
296
297 let rect: Rect = cx.bounds().into();
298 let mut filter = ImageFilter::crop(rect, None, None).unwrap();
299
300 let slr = if let Some(backdrop_filter) = backdrop_filter {
301 match backdrop_filter {
302 Filter::Blur(radius) => {
303 let sigma = radius.to_px().unwrap() * cx.scale_factor() / 2.0;
304 filter = filter.blur(None, (sigma, sigma), None).unwrap();
305 SaveLayerRec::default().paint(&paint).backdrop(&filter)
306 }
307 }
308 } else {
309 SaveLayerRec::default().paint(&paint)
310 };
311
312 Some(canvas.save_layer(&slr))
313 } else {
314 None
315 };
316
317 if let Some(transform) = cx.cache.transform.get(current) {
318 canvas.set_matrix(&(transform.into()));
319 }
320
321 if let Some(clip_path) = cx.clip_path() {
322 canvas.clip_path(&clip_path, ClipOp::Intersect, true);
323 }
324
325 let is_visible = match (visible, cx.visibility()) {
326 (v, None) => v,
327 (_, Some(Visibility::Hidden)) => false,
328 (_, Some(Visibility::Visible)) => true,
329 };
330
331 if is_visible {
333 if let Some(dirty_rect) = dirty_rect {
334 let bounds = draw_bounds(cx.style, cx.cache, cx.tree, current);
335 if bounds.intersects(dirty_rect) {
336 if let Some(view) = cx.views.remove(¤t) {
337 view.draw(cx, canvas);
338 cx.views.insert(current, view);
339 }
340 }
341 }
342 }
343
344 let child_iter = DrawChildIterator::new(cx.tree, cx.current);
345
346 for child in child_iter {
348 cx.current = child;
349 draw_entity(cx, dirty_rect, canvas, current_z, queue, is_visible);
351 }
352
353 if let Some(count) = layer_count {
354 canvas.restore_to_count(count);
355 }
356 canvas.restore();
357 cx.current = current;
358}
359
360pub(crate) fn draw_bounds(
362 style: &Style,
363 cache: &CachedData,
364 tree: &Tree<Entity>,
365 entity: Entity,
366) -> BoundingBox {
367 let mut layout_bounds = cache.bounds.get(entity).copied().unwrap();
368
369 if let Some(shadows) = style.shadow.get(entity) {
370 for shadow in shadows.iter().filter(|shadow| !shadow.inset) {
371 let mut shadow_bounds = layout_bounds;
372
373 let x = shadow.x_offset.to_px().unwrap() * style.scale_factor();
374 let y = shadow.y_offset.to_px().unwrap() * style.scale_factor();
375
376 shadow_bounds = shadow_bounds.offset(x, y);
377
378 let scale_factor = style.scale_factor();
379
380 if let Some(blur_radius) =
381 shadow.blur_radius.as_ref().map(|br| br.clone().to_px().unwrap() * scale_factor)
382 {
383 shadow_bounds = shadow_bounds.expand(blur_radius);
384 }
385
386 if let Some(spread_radius) =
387 shadow.spread_radius.as_ref().map(|sr| sr.clone().to_px().unwrap() * scale_factor)
388 {
389 shadow_bounds = shadow_bounds.expand(spread_radius * style.scale_factor());
390 }
391
392 layout_bounds = layout_bounds.union(&shadow_bounds);
393 }
394 }
395
396 let mut outline_bounds = layout_bounds;
397
398 if let Some(outline_width) = style.outline_width.get(entity) {
399 outline_bounds = outline_bounds
400 .expand(outline_width.to_pixels(layout_bounds.diagonal(), style.scale_factor()));
401 }
402
403 if let Some(outline_offset) = style.outline_offset.get(entity) {
404 outline_bounds = outline_bounds
405 .expand(outline_offset.to_pixels(layout_bounds.diagonal(), style.scale_factor()));
406 }
407
408 layout_bounds = layout_bounds.union(&outline_bounds);
409
410 let matrix = cache.transform.get(entity).copied().unwrap_or_default();
411
412 let rect: Rect = layout_bounds.into();
413 let tr = matrix.map_rect(rect).0;
414
415 let mut dirty_bounds: BoundingBox = tr.into();
416
417 dirty_bounds = BoundingBox::from_min_max(
418 dirty_bounds.left().floor(),
419 dirty_bounds.top().floor(),
420 dirty_bounds.right().ceil(),
421 dirty_bounds.bottom().ceil(),
422 );
423
424 if style.overflowx.get(entity).copied().unwrap_or_default() == Overflow::Visible
426 || style.overflowy.get(entity).copied().unwrap_or_default() == Overflow::Visible
427 {
428 let child_iter = DrawChildIterator::new(tree, entity);
429 for child in child_iter {
430 dirty_bounds = dirty_bounds.union(&draw_bounds(style, cache, tree, child));
431 }
432 }
433
434 let z_index = style.z_index.get(entity).copied().unwrap_or_default();
435
436 let parent = tree
437 .get_layout_parent(entity)
438 .unwrap_or(tree.get_parent_window(entity).unwrap_or(Entity::root()));
439 if let Some(clip_bounds) = cache.clip_path.get(parent) {
440 let clip_bounds = &BoundingBox::from_min_max(
441 clip_bounds.left().floor(),
442 clip_bounds.top().floor(),
443 clip_bounds.right().ceil(),
444 clip_bounds.bottom().ceil(),
445 );
446
447 if z_index != 0 {
448 dirty_bounds
449 } else {
450 dirty_bounds.intersection(clip_bounds)
451 }
452 } else {
453 dirty_bounds
454 }
455}
456
457struct ZEntity {
458 pub index: i32,
459 pub entity: Entity,
460 pub visible: bool,
461}
462
463impl Ord for ZEntity {
464 fn cmp(&self, other: &Self) -> Ordering {
465 other.index.cmp(&self.index)
466 }
467}
468impl PartialOrd for ZEntity {
469 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
470 Some(self.cmp(other))
471 }
472}
473impl PartialEq for ZEntity {
474 fn eq(&self, other: &Self) -> bool {
475 self.index == other.index
476 }
477}
478
479impl Eq for ZEntity {}