oxygengine_integration_ui_cr/
lib.rs

1use oxygengine_composite_renderer::{
2    component::{CompositeCamera, CompositeRenderable, CompositeTransform},
3    composite_renderer::{
4        Command, CompositeRenderer, Image, Mask, PathElement, Rectangle, Renderable, Text,
5        TextAlign,
6    },
7    jpg_image_asset_protocol::JpgImageAsset,
8    math::{Color, Mat2d, Rect, Vec2},
9    png_image_asset_protocol::PngImageAsset,
10    sprite_sheet_asset_protocol::SpriteSheetAsset,
11    svg_image_asset_protocol::SvgImageAsset,
12};
13use oxygengine_core::{
14    app::AppBuilder,
15    assets::{asset::AssetId, database::AssetsDatabase},
16    ecs::{
17        components::Name,
18        pipeline::{PipelineBuilder, PipelineBuilderError},
19        Comp, Universe, WorldRef,
20    },
21    prefab::{Prefab, PrefabComponent, PrefabManager},
22    Ignite, Scalar,
23};
24use oxygengine_user_interface::{
25    component::UserInterfaceView,
26    raui::core::{
27        layout::{CoordsMapping, CoordsMappingScaling, Layout},
28        renderer::Renderer,
29        widget::{
30            context::WidgetContext,
31            node::WidgetNode,
32            unit::{
33                area::{AreaBoxNode, AreaBoxRendererEffect},
34                image::{ImageBoxFrame, ImageBoxImageScaling, ImageBoxMaterial},
35                text::{TextBoxFont, TextBoxHorizontalAlign},
36                WidgetUnit,
37            },
38            utils::{lerp, Color as RauiColor, Rect as RauiRect, Transform, Vec2 as RauiVec2},
39        },
40    },
41    resource::UserInterface,
42    PropsData,
43};
44use serde::{Deserialize, Serialize};
45use std::collections::HashMap;
46
47pub mod prelude {
48    pub use crate::*;
49}
50
51#[derive(Ignite, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
52pub enum UserInterfaceApproxRectValues {
53    Exact,
54    Round,
55    RoundDown,
56    RoundUp,
57    RoundInside,
58    RoundOutside,
59}
60
61impl Default for UserInterfaceApproxRectValues {
62    fn default() -> Self {
63        Self::Exact
64    }
65}
66
67impl UserInterfaceApproxRectValues {
68    pub fn calculate(self, rect: Rect) -> Rect {
69        match self {
70            Self::Exact => rect,
71            Self::Round => Rect {
72                x: rect.x.round(),
73                y: rect.y.round(),
74                w: rect.w.round(),
75                h: rect.h.round(),
76            },
77            Self::RoundDown => Rect {
78                x: rect.x.floor(),
79                y: rect.y.floor(),
80                w: rect.w.floor(),
81                h: rect.h.floor(),
82            },
83            Self::RoundUp => Rect {
84                x: rect.x.ceil(),
85                y: rect.y.ceil(),
86                w: rect.w.ceil(),
87                h: rect.h.ceil(),
88            },
89            Self::RoundInside => Rect {
90                x: rect.x.ceil(),
91                y: rect.y.ceil(),
92                w: rect.w.floor(),
93                h: rect.h.floor(),
94            },
95            Self::RoundOutside => Rect {
96                x: rect.x.floor(),
97                y: rect.y.floor(),
98                w: rect.w.ceil(),
99                h: rect.h.ceil(),
100            },
101        }
102    }
103}
104
105#[derive(Ignite, Debug, Clone, Serialize, Deserialize)]
106pub struct UserInterfaceViewSyncCompositeRenderable {
107    #[serde(default)]
108    pub camera_name: String,
109    #[serde(default)]
110    pub viewport: RauiRect,
111    #[serde(default)]
112    pub mapping_scaling: CoordsMappingScaling,
113    #[serde(default)]
114    pub approx_rect_values: UserInterfaceApproxRectValues,
115    #[serde(default = "UserInterfaceViewSyncCompositeRenderable::default_image_smoothing")]
116    pub image_smoothing: bool,
117}
118
119impl Default for UserInterfaceViewSyncCompositeRenderable {
120    fn default() -> Self {
121        Self {
122            camera_name: Default::default(),
123            viewport: Default::default(),
124            mapping_scaling: Default::default(),
125            approx_rect_values: Default::default(),
126            image_smoothing: Self::default_image_smoothing(),
127        }
128    }
129}
130
131impl UserInterfaceViewSyncCompositeRenderable {
132    fn default_image_smoothing() -> bool {
133        true
134    }
135}
136
137impl Prefab for UserInterfaceViewSyncCompositeRenderable {}
138impl PrefabComponent for UserInterfaceViewSyncCompositeRenderable {}
139
140#[derive(Debug, Default)]
141pub struct ApplyUserInterfaceToCompositeRendererSystemCache {
142    images_cache: HashMap<String, String>,
143    atlas_table: HashMap<AssetId, String>,
144    frames_cache: HashMap<String, HashMap<String, Rect>>,
145    images_sizes_cache: HashMap<String, Vec2>,
146    images_sizes_table: HashMap<AssetId, String>,
147}
148
149pub type ApplyUserInterfaceToCompositeRendererSystemResources<'a, CR> = (
150    WorldRef,
151    &'a CR,
152    &'a AssetsDatabase,
153    &'a mut UserInterface,
154    &'a mut ApplyUserInterfaceToCompositeRendererSystemCache,
155    Comp<&'a mut CompositeRenderable>,
156    Comp<&'a UserInterfaceView>,
157    Comp<&'a UserInterfaceViewSyncCompositeRenderable>,
158    Comp<&'a CompositeCamera>,
159    Comp<&'a CompositeTransform>,
160    Comp<&'a Name>,
161);
162
163pub fn apply_user_interface_to_composite_renderer_system<CR>(universe: &mut Universe)
164where
165    CR: CompositeRenderer + 'static,
166{
167    let (world, renderer, assets, mut ui, mut cache, ..) =
168        universe.query_resources::<ApplyUserInterfaceToCompositeRendererSystemResources<CR>>();
169
170    for id in assets.lately_loaded_protocol("atlas") {
171        let id = *id;
172        let asset = assets
173            .asset_by_id(id)
174            .expect("trying to use not loaded atlas asset");
175        let path = asset.path().to_owned();
176        let asset = asset
177            .get::<SpriteSheetAsset>()
178            .expect("trying to use non-atlas asset");
179        let image = asset.info().meta.image_name();
180        let frames = asset
181            .info()
182            .frames
183            .iter()
184            .map(|(k, v)| (k.to_owned(), v.frame))
185            .collect();
186        cache.images_cache.insert(path.clone(), image);
187        cache.atlas_table.insert(id, path.clone());
188        cache.frames_cache.insert(path, frames);
189    }
190    for id in assets.lately_unloaded_protocol("atlas") {
191        if let Some(path) = cache.atlas_table.remove(id) {
192            cache.images_cache.remove(&path);
193            cache.frames_cache.remove(&path);
194        }
195    }
196    for id in assets.lately_loaded_protocol("png") {
197        let id = *id;
198        let asset = assets
199            .asset_by_id(id)
200            .expect("trying to use not loaded png asset");
201        let path = asset.path().to_owned();
202        let asset = asset
203            .get::<PngImageAsset>()
204            .expect("trying to use non-png asset");
205        let width = asset.width() as Scalar;
206        let height = asset.height() as Scalar;
207        cache
208            .images_sizes_cache
209            .insert(path.clone(), Vec2::new(width, height));
210        cache.images_sizes_table.insert(id, path);
211    }
212    for id in assets.lately_unloaded_protocol("png") {
213        if let Some(path) = cache.images_sizes_table.remove(id) {
214            cache.images_sizes_cache.remove(&path);
215        }
216    }
217    for id in assets.lately_loaded_protocol("jpg") {
218        let id = *id;
219        let asset = assets
220            .asset_by_id(id)
221            .expect("trying to use not loaded jpg asset");
222        let path = asset.path().to_owned();
223        let asset = asset
224            .get::<JpgImageAsset>()
225            .expect("trying to use non-jpg asset");
226        let width = asset.width() as Scalar;
227        let height = asset.height() as Scalar;
228        cache
229            .images_sizes_cache
230            .insert(path.clone(), Vec2::new(width, height));
231        cache.images_sizes_table.insert(id, path);
232    }
233    for id in assets.lately_unloaded_protocol("jpg") {
234        if let Some(path) = cache.images_sizes_table.remove(id) {
235            cache.images_sizes_cache.remove(&path);
236        }
237    }
238    for id in assets.lately_loaded_protocol("svg") {
239        let id = *id;
240        let asset = assets
241            .asset_by_id(id)
242            .expect("trying to use not loaded svg asset");
243        let path = asset.path().to_owned();
244        let asset = asset
245            .get::<SvgImageAsset>()
246            .expect("trying to use non-svg asset");
247        let width = asset.width() as Scalar;
248        let height = asset.height() as Scalar;
249        cache
250            .images_sizes_cache
251            .insert(path.clone(), Vec2::new(width, height));
252        cache.images_sizes_table.insert(id, path);
253    }
254    for id in assets.lately_unloaded_protocol("svg") {
255        if let Some(path) = cache.images_sizes_table.remove(id) {
256            cache.images_sizes_cache.remove(&path);
257        }
258    }
259
260    let view_size = renderer.view_size();
261
262    for (_, (renderable, view, sync)) in world
263        .query::<(
264            &mut CompositeRenderable,
265            &UserInterfaceView,
266            &UserInterfaceViewSyncCompositeRenderable,
267        )>()
268        .iter()
269    {
270        let mapping = world
271            .query::<(&CompositeCamera, &Name, &CompositeTransform)>()
272            .iter()
273            .find_map(|(_, (c, n, t))| {
274                if sync.camera_name == n.0 {
275                    if let Some(inv_mat) = !c.view_matrix(t, view_size) {
276                        let size = view_size * inv_mat;
277                        let rect = RauiRect {
278                            left: lerp(0.0, size.x, sync.viewport.left),
279                            right: lerp(size.x, 0.0, sync.viewport.right),
280                            top: lerp(0.0, size.y, sync.viewport.top),
281                            bottom: lerp(size.y, 0.0, sync.viewport.bottom),
282                        };
283                        Some(CoordsMapping::new_scaling(rect, sync.mapping_scaling))
284                    } else {
285                        None
286                    }
287                } else {
288                    None
289                }
290            });
291        if let (Some(mapping), Some(data)) = (mapping, ui.get_mut(view.app_id())) {
292            data.coords_mapping = mapping;
293            let mut raui_renderer = RauiRenderer::new(
294                &cache.images_cache,
295                &cache.frames_cache,
296                &cache.images_sizes_cache,
297                sync.approx_rect_values,
298                sync.image_smoothing,
299                32,
300            );
301            if let Ok(commands) = data
302                .application
303                .render(&data.coords_mapping, &mut raui_renderer)
304            {
305                renderable.0 = Renderable::Commands(commands);
306            }
307        }
308    }
309}
310
311fn raui_to_vec2(v: RauiVec2) -> Vec2 {
312    Vec2::new(v.x, v.y)
313}
314
315fn raui_to_rect(v: RauiRect) -> Rect {
316    Rect::new(Vec2::new(v.left, v.top), Vec2::new(v.width(), v.height()))
317}
318
319fn raui_to_color(v: RauiColor) -> Color {
320    Color::rgba(
321        (v.r * 255.0) as u8,
322        (v.g * 255.0) as u8,
323        (v.b * 255.0) as u8,
324        (v.a * 255.0) as u8,
325    )
326}
327
328#[derive(Debug, Copy, Clone)]
329enum ImageFrame {
330    None,
331    TopLeft,
332    TopCenter,
333    TopRight,
334    MiddleLeft,
335    MiddleCenter,
336    MiddleRight,
337    BottomLeft,
338    BottomCenter,
339    BottomRight,
340}
341
342impl ImageFrame {
343    fn source(self, rect: Rect, image_frame: Option<&ImageBoxFrame>) -> Rect {
344        if let Some(image_frame) = image_frame {
345            let result = match self {
346                ImageFrame::None => rect,
347                ImageFrame::TopLeft => Rect::new(
348                    Vec2::new(rect.x, rect.y),
349                    Vec2::new(image_frame.source.left, image_frame.source.top),
350                ),
351                ImageFrame::TopCenter => Rect::new(
352                    Vec2::new(rect.x + image_frame.source.left, rect.y),
353                    Vec2::new(
354                        rect.w - image_frame.source.left - image_frame.source.right,
355                        image_frame.source.top,
356                    ),
357                ),
358                ImageFrame::TopRight => Rect::new(
359                    Vec2::new(rect.x + rect.w - image_frame.source.right, rect.y),
360                    Vec2::new(image_frame.source.right, image_frame.source.top),
361                ),
362                ImageFrame::MiddleLeft => Rect::new(
363                    Vec2::new(rect.x, rect.y + image_frame.source.top),
364                    Vec2::new(
365                        image_frame.source.left,
366                        rect.h - image_frame.source.top - image_frame.source.bottom,
367                    ),
368                ),
369                ImageFrame::MiddleCenter => Rect::new(
370                    Vec2::new(
371                        rect.x + image_frame.source.left,
372                        rect.y + image_frame.source.top,
373                    ),
374                    Vec2::new(
375                        rect.w - image_frame.source.left - image_frame.source.right,
376                        rect.h - image_frame.source.top - image_frame.source.bottom,
377                    ),
378                ),
379                ImageFrame::MiddleRight => Rect::new(
380                    Vec2::new(
381                        rect.x + rect.w - image_frame.source.right,
382                        rect.y + image_frame.source.top,
383                    ),
384                    Vec2::new(
385                        image_frame.source.right,
386                        rect.h - image_frame.source.top - image_frame.source.bottom,
387                    ),
388                ),
389                ImageFrame::BottomLeft => Rect::new(
390                    Vec2::new(rect.x, rect.y + rect.h - image_frame.source.bottom),
391                    Vec2::new(image_frame.source.left, image_frame.source.bottom),
392                ),
393                ImageFrame::BottomCenter => Rect::new(
394                    Vec2::new(
395                        rect.x + image_frame.source.left,
396                        rect.y + rect.h - image_frame.source.bottom,
397                    ),
398                    Vec2::new(
399                        rect.w - image_frame.source.left - image_frame.source.right,
400                        image_frame.source.bottom,
401                    ),
402                ),
403                ImageFrame::BottomRight => Rect::new(
404                    Vec2::new(
405                        rect.x + rect.w - image_frame.source.right,
406                        rect.y + rect.h - image_frame.source.bottom,
407                    ),
408                    Vec2::new(image_frame.source.right, image_frame.source.bottom),
409                ),
410            };
411            if result.w >= 0.0 && result.h >= 0.0 {
412                result
413            } else {
414                Rect::default()
415            }
416        } else {
417            match self {
418                ImageFrame::None | ImageFrame::MiddleCenter => rect,
419                _ => Rect::default(),
420            }
421        }
422    }
423
424    fn destination(
425        self,
426        rect: RauiRect,
427        image_frame: Option<&ImageBoxFrame>,
428        source_size: Option<Vec2>,
429    ) -> Rect {
430        if let Some(image_frame) = image_frame {
431            let mut d = image_frame.destination;
432            if image_frame.frame_keep_aspect_ratio {
433                if let Some(source_size) = source_size {
434                    d.left = (image_frame.source.left * rect.height()) / source_size.y;
435                    d.right = (image_frame.source.right * rect.height()) / source_size.y;
436                    d.top = (image_frame.source.top * rect.width()) / source_size.x;
437                    d.bottom = (image_frame.source.bottom * rect.width()) / source_size.x;
438                }
439            }
440            if d.left + d.right > rect.width() {
441                let m = d.left + d.right;
442                d.left = rect.width() * d.left / m;
443                d.right = rect.width() * d.right / m;
444            }
445            if d.top + d.bottom > rect.height() {
446                let m = d.top + d.bottom;
447                d.top = rect.height() * d.top / m;
448                d.bottom = rect.height() * d.bottom / m;
449            }
450            let result = match self {
451                ImageFrame::None => raui_to_rect(rect),
452                ImageFrame::TopLeft => {
453                    Rect::new(Vec2::new(rect.left, rect.top), Vec2::new(d.left, d.top))
454                }
455                ImageFrame::TopCenter => Rect::new(
456                    Vec2::new(rect.left + d.left, rect.top),
457                    Vec2::new(rect.width() - d.left - d.right, d.top),
458                ),
459                ImageFrame::TopRight => Rect::new(
460                    Vec2::new(rect.right - d.right, rect.top),
461                    Vec2::new(d.right, d.top),
462                ),
463                ImageFrame::MiddleLeft => Rect::new(
464                    Vec2::new(rect.left, rect.top + d.top),
465                    Vec2::new(d.left, rect.height() - d.top - d.bottom),
466                ),
467                ImageFrame::MiddleCenter => Rect::new(
468                    Vec2::new(rect.left + d.left, rect.top + d.top),
469                    Vec2::new(
470                        rect.width() - d.left - d.right,
471                        rect.height() - d.top - d.bottom,
472                    ),
473                ),
474                ImageFrame::MiddleRight => Rect::new(
475                    Vec2::new(rect.right - d.right, rect.top + d.top),
476                    Vec2::new(d.right, rect.height() - d.top - d.bottom),
477                ),
478                ImageFrame::BottomLeft => Rect::new(
479                    Vec2::new(rect.left, rect.bottom - d.bottom),
480                    Vec2::new(d.left, d.bottom),
481                ),
482                ImageFrame::BottomCenter => Rect::new(
483                    Vec2::new(rect.left + d.left, rect.bottom - d.bottom),
484                    Vec2::new(rect.width() - d.left - d.right, d.bottom),
485                ),
486                ImageFrame::BottomRight => Rect::new(
487                    Vec2::new(rect.right - d.right, rect.bottom - d.bottom),
488                    Vec2::new(d.right, d.bottom),
489                ),
490            };
491            if result.w >= 0.0 && result.h >= 0.0 {
492                result
493            } else {
494                Rect::default()
495            }
496        } else {
497            match self {
498                ImageFrame::None | ImageFrame::MiddleCenter => raui_to_rect(rect),
499                _ => Rect::default(),
500            }
501        }
502    }
503}
504
505enum Filter {
506    None,
507    Reset,
508    Replace([Scalar; 8]),
509    Combine([Scalar; 8]),
510}
511
512struct RauiRenderer<'a> {
513    images: &'a HashMap<String, String>,
514    frames: &'a HashMap<String, HashMap<String, Rect>>,
515    images_sizes: &'a HashMap<String, Vec2>,
516    approx_rect_values: UserInterfaceApproxRectValues,
517    image_smoothing: bool,
518    filter_stack: Vec<[Scalar; 8]>,
519}
520
521impl<'a> RauiRenderer<'a> {
522    pub fn new(
523        images: &'a HashMap<String, String>,
524        frames: &'a HashMap<String, HashMap<String, Rect>>,
525        images_sizes: &'a HashMap<String, Vec2>,
526        approx_rect_values: UserInterfaceApproxRectValues,
527        image_smoothing: bool,
528        filter_stack_capacity: usize,
529    ) -> Self {
530        let mut filter_stack = Vec::with_capacity(filter_stack_capacity);
531        filter_stack.push([0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0]);
532        Self {
533            images,
534            frames,
535            images_sizes,
536            approx_rect_values,
537            image_smoothing,
538            filter_stack,
539        }
540    }
541
542    #[allow(clippy::many_single_char_names)]
543    fn make_transform_command(transform: &Transform, rect: RauiRect) -> Command<'static> {
544        let size = rect.size();
545        let offset = Vec2::new(rect.left, rect.top);
546        let offset = Mat2d::translation(offset);
547        let pivot = Vec2::new(
548            lerp(0.0, size.x, transform.pivot.x),
549            lerp(0.0, size.y, transform.pivot.y),
550        );
551        let pivot = Mat2d::translation(pivot);
552        let inv_pivot = pivot.inverse().unwrap_or_default();
553        let align = Vec2::new(
554            lerp(0.0, size.x, transform.align.x),
555            lerp(0.0, size.y, transform.align.y),
556        );
557        let align = Mat2d::translation(align);
558        let translate = Mat2d::translation(raui_to_vec2(transform.translation));
559        let rotate = Mat2d::rotation(transform.rotation);
560        let scale = Mat2d::scale(raui_to_vec2(transform.scale));
561        let skew = Mat2d::skew(raui_to_vec2(transform.skew));
562        let matrix = pivot * translate * rotate * scale * skew * inv_pivot * align * offset;
563        let [a, b, c, d, e, f] = matrix.0;
564        Command::Transform(a, b, c, d, e, f)
565    }
566
567    #[allow(clippy::many_single_char_names)]
568    fn make_simple_transform_command(rect: RauiRect) -> Command<'static> {
569        let offset = Vec2::new(rect.left, rect.top);
570        let offset = Mat2d::translation(offset);
571        let [a, b, c, d, e, f] = offset.0;
572        Command::Transform(a, b, c, d, e, f)
573    }
574
575    fn make_area_effect(effect: &Option<AreaBoxRendererEffect>) -> Filter {
576        if let Some(effect) = effect {
577            match effect.id.as_str() {
578                "filter-reset" => Filter::Reset,
579                "filter-replace" => Filter::Replace(effect.params),
580                "filter-combine" => Filter::Combine(effect.params),
581                _ => Filter::None,
582            }
583        } else {
584            Filter::None
585        }
586    }
587
588    fn make_rect_renderable(
589        &self,
590        color: RauiColor,
591        rect: RauiRect,
592        image_frame: Option<&ImageBoxFrame>,
593        subframe: ImageFrame,
594    ) -> Rectangle {
595        Rectangle {
596            color: raui_to_color(color),
597            rect: self
598                .approx_rect_values
599                .calculate(subframe.destination(rect, image_frame, None)),
600        }
601    }
602
603    fn make_image_renderable(
604        &self,
605        image: &str,
606        image_source: Option<&RauiRect>,
607        rect: RauiRect,
608        image_frame: Option<&ImageBoxFrame>,
609        subframe: ImageFrame,
610    ) -> Image<'static> {
611        let mut it = image.split('@');
612        if let Some(sheet) = it.next() {
613            if let Some(frame) = it.next() {
614                if let Some(name) = self.images.get(sheet) {
615                    if let Some(frames) = self.frames.get(sheet) {
616                        let srect = match image_source {
617                            Some(rect) => raui_to_rect(*rect),
618                            None => frames.get(frame).copied().unwrap_or_default(),
619                        };
620                        let destination = self.approx_rect_values.calculate(subframe.destination(
621                            rect,
622                            image_frame,
623                            Some(srect.size()),
624                        ));
625                        return Image {
626                            image: name.to_owned().into(),
627                            source: Some(subframe.source(srect, image_frame)),
628                            destination: Some(destination),
629                            alignment: Vec2::zero(),
630                        };
631                    }
632                }
633            }
634        }
635        let frame = match image_source {
636            Some(rect) => raui_to_rect(*rect),
637            None => self
638                .images_sizes
639                .get(image)
640                .copied()
641                .map(Rect::with_size)
642                .unwrap_or_default(),
643        };
644        let destination = self.approx_rect_values.calculate(subframe.destination(
645            rect,
646            image_frame,
647            Some(frame.size()),
648        ));
649        Image {
650            image: image.to_owned().into(),
651            source: Some(subframe.source(frame, image_frame)),
652            destination: Some(destination),
653            alignment: Vec2::zero(),
654        }
655    }
656
657    fn make_text_renderable(
658        text: &str,
659        font: &TextBoxFont,
660        rect: RauiRect,
661        horizontal_align: TextBoxHorizontalAlign,
662        color: RauiColor,
663    ) -> Command<'static> {
664        let (align, position) = match horizontal_align {
665            TextBoxHorizontalAlign::Left => (TextAlign::Left, Vec2::new(rect.left, rect.top)),
666            TextBoxHorizontalAlign::Center => (
667                TextAlign::Center,
668                Vec2::new(rect.left + rect.width() * 0.5, rect.top),
669            ),
670            TextBoxHorizontalAlign::Right => (TextAlign::Right, Vec2::new(rect.right, rect.top)),
671        };
672        Command::Draw(Renderable::Text(Text {
673            color: raui_to_color(color),
674            font: font.name.to_owned().into(),
675            align,
676            text: text.to_owned().into(),
677            position,
678            size: font.size,
679            max_width: Some(rect.width()),
680            ..Default::default()
681        }))
682    }
683
684    fn image_size(&self, image: &str) -> Vec2 {
685        let mut it = image.split('@');
686        if let Some(sheet) = it.next() {
687            if let Some(frame) = it.next() {
688                if let Some(frames) = self.frames.get(sheet) {
689                    if let Some(rect) = frames.get(frame) {
690                        return Vec2::new(rect.w, rect.h);
691                    }
692                }
693            }
694        }
695        if let Some(rect) = self.images_sizes.get(image).copied().map(Rect::with_size) {
696            return Vec2::new(rect.w, rect.h);
697        }
698        Vec2::zero()
699    }
700
701    // TODO: refactor this shit!
702    fn render_node(
703        &mut self,
704        unit: &WidgetUnit,
705        mapping: &CoordsMapping,
706        layout: &Layout,
707        local: bool,
708    ) -> Vec<Command<'static>> {
709        match unit {
710            WidgetUnit::None | WidgetUnit::PortalBox(_) => vec![],
711            WidgetUnit::AreaBox(unit) => {
712                if let Some(item) = layout.items.get(&unit.id) {
713                    let local_space = mapping.virtual_to_real_rect(item.local_space, local);
714                    let transform = Self::make_simple_transform_command(local_space);
715                    let area_effect = match Self::make_area_effect(&unit.renderer_effect) {
716                        Filter::None => self.filter_stack.last().copied().unwrap(),
717                        Filter::Reset => [0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0],
718                        Filter::Replace(data) => data,
719                        Filter::Combine(data) => {
720                            let old = self.filter_stack.last().copied().unwrap();
721                            [
722                                old[0] + data[0],
723                                old[1] + data[1],
724                                old[2] + data[2],
725                                old[3] + data[3],
726                                old[4] + data[4],
727                                old[5] + data[5],
728                                old[6] + data[6],
729                                old[7] + data[7],
730                            ]
731                        }
732                    };
733                    self.filter_stack.push(area_effect);
734                    let area_effect = format!(
735                        "blur({}px) brightness({}%) contrast({}%) grayscale({}%) invert({}%) saturate({}%) sepia({}%) hue-rotate({}deg)",
736                        area_effect[0],
737                        area_effect[1] * 100.0,
738                        area_effect[2] * 100.0,
739                        area_effect[3] * 100.0,
740                        area_effect[4] * 100.0,
741                        area_effect[5] * 100.0,
742                        area_effect[6] * 100.0,
743                        area_effect[7],
744                    );
745                    let children = self.render_node(&unit.slot, mapping, layout, true);
746                    self.filter_stack.pop();
747                    std::iter::once(Command::Store)
748                        .chain(std::iter::once(transform))
749                        .chain(std::iter::once(Command::Filter(area_effect.into())))
750                        .chain(children)
751                        .chain(std::iter::once(Command::Restore))
752                        .collect::<Vec<_>>()
753                } else {
754                    vec![]
755                }
756            }
757            WidgetUnit::ContentBox(unit) => {
758                if let Some(item) = layout.items.get(&unit.id) {
759                    let mut items = unit
760                        .items
761                        .iter()
762                        .map(|item| (item.layout.depth, item))
763                        .collect::<Vec<_>>();
764                    items.sort_by(|(a, _), (b, _)| a.partial_cmp(b).unwrap());
765                    let local_space = mapping.virtual_to_real_rect(item.local_space, local);
766                    let transform = Self::make_transform_command(&unit.transform, local_space);
767                    let mask = if unit.clipping {
768                        Command::Draw(Renderable::Mask(Mask {
769                            elements: vec![PathElement::Rectangle(Rect::with_size(Vec2::new(
770                                local_space.width(),
771                                local_space.height(),
772                            )))],
773                        }))
774                    } else {
775                        Command::None
776                    };
777                    std::iter::once(Command::Store)
778                        .chain(std::iter::once(transform))
779                        .chain(std::iter::once(mask))
780                        .chain(items.into_iter().flat_map(|(_, item)| {
781                            self.render_node(&item.slot, mapping, layout, true)
782                        }))
783                        .chain(std::iter::once(Command::Restore))
784                        .collect::<Vec<_>>()
785                } else {
786                    vec![]
787                }
788            }
789            WidgetUnit::FlexBox(unit) => {
790                if let Some(item) = layout.items.get(&unit.id) {
791                    let local_space = mapping.virtual_to_real_rect(item.local_space, local);
792                    let transform = Self::make_transform_command(&unit.transform, local_space);
793                    std::iter::once(Command::Store)
794                        .chain(std::iter::once(transform))
795                        .chain(
796                            unit.items.iter().flat_map(|item| {
797                                self.render_node(&item.slot, mapping, layout, true)
798                            }),
799                        )
800                        .chain(std::iter::once(Command::Restore))
801                        .collect::<Vec<_>>()
802                } else {
803                    vec![]
804                }
805            }
806            WidgetUnit::GridBox(unit) => {
807                if let Some(item) = layout.items.get(&unit.id) {
808                    let local_space = mapping.virtual_to_real_rect(item.local_space, local);
809                    let transform = Self::make_transform_command(&unit.transform, local_space);
810                    std::iter::once(Command::Store)
811                        .chain(std::iter::once(transform))
812                        .chain(
813                            unit.items.iter().flat_map(|item| {
814                                self.render_node(&item.slot, mapping, layout, true)
815                            }),
816                        )
817                        .chain(std::iter::once(Command::Restore))
818                        .collect::<Vec<_>>()
819                } else {
820                    vec![]
821                }
822            }
823            WidgetUnit::SizeBox(unit) => {
824                if let Some(item) = layout.items.get(&unit.id) {
825                    let local_space = mapping.virtual_to_real_rect(item.local_space, local);
826                    let transform = Self::make_transform_command(&unit.transform, local_space);
827                    std::iter::once(Command::Store)
828                        .chain(std::iter::once(transform))
829                        .chain(self.render_node(&unit.slot, mapping, layout, true))
830                        .chain(std::iter::once(Command::Restore))
831                        .collect::<Vec<_>>()
832                } else {
833                    vec![]
834                }
835            }
836            WidgetUnit::ImageBox(unit) => match &unit.material {
837                ImageBoxMaterial::Color(image) => {
838                    if let Some(item) = layout.items.get(&unit.id) {
839                        let local_space = mapping.virtual_to_real_rect(item.local_space, local);
840                        let transform = Self::make_transform_command(&unit.transform, local_space);
841                        let rect = RauiRect {
842                            left: 0.0,
843                            right: local_space.width(),
844                            top: 0.0,
845                            bottom: local_space.height(),
846                        };
847                        match &image.scaling {
848                            ImageBoxImageScaling::Stretch => {
849                                let renderable = self.make_rect_renderable(
850                                    image.color,
851                                    rect,
852                                    None,
853                                    ImageFrame::None,
854                                );
855                                vec![
856                                    Command::Store,
857                                    Command::Smoothing(self.image_smoothing),
858                                    transform,
859                                    Command::Draw(Renderable::Rectangle(renderable)),
860                                    Command::Restore,
861                                ]
862                            }
863                            ImageBoxImageScaling::Frame(frame) => {
864                                let renderable_top_left = self.make_rect_renderable(
865                                    image.color,
866                                    rect,
867                                    Some(frame),
868                                    ImageFrame::TopLeft,
869                                );
870                                let renderable_top_center = self.make_rect_renderable(
871                                    image.color,
872                                    rect,
873                                    Some(frame),
874                                    ImageFrame::TopCenter,
875                                );
876                                let renderable_top_right = self.make_rect_renderable(
877                                    image.color,
878                                    rect,
879                                    Some(frame),
880                                    ImageFrame::TopRight,
881                                );
882                                let renderable_middle_left = self.make_rect_renderable(
883                                    image.color,
884                                    rect,
885                                    Some(frame),
886                                    ImageFrame::MiddleLeft,
887                                );
888                                let renderable_middle_right = self.make_rect_renderable(
889                                    image.color,
890                                    rect,
891                                    Some(frame),
892                                    ImageFrame::MiddleRight,
893                                );
894                                let renderable_bottom_left = self.make_rect_renderable(
895                                    image.color,
896                                    rect,
897                                    Some(frame),
898                                    ImageFrame::BottomLeft,
899                                );
900                                let renderable_bottom_center = self.make_rect_renderable(
901                                    image.color,
902                                    rect,
903                                    Some(frame),
904                                    ImageFrame::BottomCenter,
905                                );
906                                let renderable_bottom_right = self.make_rect_renderable(
907                                    image.color,
908                                    rect,
909                                    Some(frame),
910                                    ImageFrame::BottomRight,
911                                );
912                                if frame.frame_only {
913                                    vec![
914                                        Command::Store,
915                                        Command::Smoothing(self.image_smoothing),
916                                        transform,
917                                        Command::Draw(Renderable::Rectangle(renderable_top_left)),
918                                        Command::Draw(Renderable::Rectangle(renderable_top_center)),
919                                        Command::Draw(Renderable::Rectangle(renderable_top_right)),
920                                        Command::Draw(Renderable::Rectangle(
921                                            renderable_middle_left,
922                                        )),
923                                        Command::Draw(Renderable::Rectangle(
924                                            renderable_middle_right,
925                                        )),
926                                        Command::Draw(Renderable::Rectangle(
927                                            renderable_bottom_left,
928                                        )),
929                                        Command::Draw(Renderable::Rectangle(
930                                            renderable_bottom_center,
931                                        )),
932                                        Command::Draw(Renderable::Rectangle(
933                                            renderable_bottom_right,
934                                        )),
935                                        Command::Restore,
936                                    ]
937                                } else {
938                                    let renderable_middle_center = self.make_rect_renderable(
939                                        image.color,
940                                        rect,
941                                        Some(frame),
942                                        ImageFrame::MiddleCenter,
943                                    );
944                                    vec![
945                                        Command::Store,
946                                        Command::Smoothing(self.image_smoothing),
947                                        transform,
948                                        Command::Draw(Renderable::Rectangle(renderable_top_left)),
949                                        Command::Draw(Renderable::Rectangle(renderable_top_center)),
950                                        Command::Draw(Renderable::Rectangle(renderable_top_right)),
951                                        Command::Draw(Renderable::Rectangle(
952                                            renderable_middle_left,
953                                        )),
954                                        Command::Draw(Renderable::Rectangle(
955                                            renderable_middle_center,
956                                        )),
957                                        Command::Draw(Renderable::Rectangle(
958                                            renderable_middle_right,
959                                        )),
960                                        Command::Draw(Renderable::Rectangle(
961                                            renderable_bottom_left,
962                                        )),
963                                        Command::Draw(Renderable::Rectangle(
964                                            renderable_bottom_center,
965                                        )),
966                                        Command::Draw(Renderable::Rectangle(
967                                            renderable_bottom_right,
968                                        )),
969                                        Command::Restore,
970                                    ]
971                                }
972                            }
973                        }
974                    } else {
975                        vec![]
976                    }
977                }
978                ImageBoxMaterial::Image(image) => {
979                    if let Some(item) = layout.items.get(&unit.id) {
980                        let local_space = mapping.virtual_to_real_rect(item.local_space, local);
981                        let transform = Self::make_transform_command(&unit.transform, local_space);
982                        let rect = RauiRect {
983                            left: 0.0,
984                            right: local_space.width(),
985                            top: 0.0,
986                            bottom: local_space.height(),
987                        };
988                        let alpha = Command::Alpha(image.tint.a);
989                        let rect = if let Some(aspect) = unit.content_keep_aspect_ratio {
990                            let size = self.image_size(&image.id);
991                            let ox = rect.left;
992                            let oy = rect.top;
993                            let iw = rect.width();
994                            let ih = rect.height();
995                            let ra = size.x / size.y;
996                            let ia = iw / ih;
997                            let scale = if (ra >= ia) != aspect.outside {
998                                iw / size.x
999                            } else {
1000                                ih / size.y
1001                            };
1002                            let w = size.x * scale;
1003                            let h = size.y * scale;
1004                            let ow = lerp(0.0, iw - w, aspect.horizontal_alignment);
1005                            let oh = lerp(0.0, ih - h, aspect.vertical_alignment);
1006                            RauiRect {
1007                                left: ox + ow,
1008                                right: ox + ow + w,
1009                                top: oy + oh,
1010                                bottom: oy + oh + h,
1011                            }
1012                        } else {
1013                            rect
1014                        };
1015                        match &image.scaling {
1016                            ImageBoxImageScaling::Stretch => {
1017                                let renderable = self.make_image_renderable(
1018                                    &image.id,
1019                                    image.source_rect.as_ref(),
1020                                    rect,
1021                                    None,
1022                                    ImageFrame::None,
1023                                );
1024                                vec![
1025                                    Command::Store,
1026                                    Command::Smoothing(self.image_smoothing),
1027                                    transform,
1028                                    alpha,
1029                                    Command::Draw(Renderable::Image(renderable)),
1030                                    Command::Restore,
1031                                ]
1032                            }
1033                            ImageBoxImageScaling::Frame(frame) => {
1034                                let renderable_top_left = self.make_image_renderable(
1035                                    &image.id,
1036                                    image.source_rect.as_ref(),
1037                                    rect,
1038                                    Some(frame),
1039                                    ImageFrame::TopLeft,
1040                                );
1041                                let renderable_top_center = self.make_image_renderable(
1042                                    &image.id,
1043                                    image.source_rect.as_ref(),
1044                                    rect,
1045                                    Some(frame),
1046                                    ImageFrame::TopCenter,
1047                                );
1048                                let renderable_top_right = self.make_image_renderable(
1049                                    &image.id,
1050                                    image.source_rect.as_ref(),
1051                                    rect,
1052                                    Some(frame),
1053                                    ImageFrame::TopRight,
1054                                );
1055                                let renderable_middle_left = self.make_image_renderable(
1056                                    &image.id,
1057                                    image.source_rect.as_ref(),
1058                                    rect,
1059                                    Some(frame),
1060                                    ImageFrame::MiddleLeft,
1061                                );
1062                                let renderable_middle_right = self.make_image_renderable(
1063                                    &image.id,
1064                                    image.source_rect.as_ref(),
1065                                    rect,
1066                                    Some(frame),
1067                                    ImageFrame::MiddleRight,
1068                                );
1069                                let renderable_bottom_left = self.make_image_renderable(
1070                                    &image.id,
1071                                    image.source_rect.as_ref(),
1072                                    rect,
1073                                    Some(frame),
1074                                    ImageFrame::BottomLeft,
1075                                );
1076                                let renderable_bottom_center = self.make_image_renderable(
1077                                    &image.id,
1078                                    image.source_rect.as_ref(),
1079                                    rect,
1080                                    Some(frame),
1081                                    ImageFrame::BottomCenter,
1082                                );
1083                                let renderable_bottom_right = self.make_image_renderable(
1084                                    &image.id,
1085                                    image.source_rect.as_ref(),
1086                                    rect,
1087                                    Some(frame),
1088                                    ImageFrame::BottomRight,
1089                                );
1090                                if frame.frame_only {
1091                                    vec![
1092                                        Command::Store,
1093                                        Command::Smoothing(self.image_smoothing),
1094                                        transform,
1095                                        alpha,
1096                                        Command::Draw(Renderable::Image(renderable_top_left)),
1097                                        Command::Draw(Renderable::Image(renderable_top_center)),
1098                                        Command::Draw(Renderable::Image(renderable_top_right)),
1099                                        Command::Draw(Renderable::Image(renderable_middle_left)),
1100                                        Command::Draw(Renderable::Image(renderable_middle_right)),
1101                                        Command::Draw(Renderable::Image(renderable_bottom_left)),
1102                                        Command::Draw(Renderable::Image(renderable_bottom_center)),
1103                                        Command::Draw(Renderable::Image(renderable_bottom_right)),
1104                                        Command::Restore,
1105                                    ]
1106                                } else {
1107                                    let renderable_middle_center = self.make_image_renderable(
1108                                        &image.id,
1109                                        image.source_rect.as_ref(),
1110                                        rect,
1111                                        Some(frame),
1112                                        ImageFrame::MiddleCenter,
1113                                    );
1114                                    vec![
1115                                        Command::Store,
1116                                        Command::Smoothing(self.image_smoothing),
1117                                        transform,
1118                                        alpha,
1119                                        Command::Draw(Renderable::Image(renderable_top_left)),
1120                                        Command::Draw(Renderable::Image(renderable_top_center)),
1121                                        Command::Draw(Renderable::Image(renderable_top_right)),
1122                                        Command::Draw(Renderable::Image(renderable_middle_left)),
1123                                        Command::Draw(Renderable::Image(renderable_middle_center)),
1124                                        Command::Draw(Renderable::Image(renderable_middle_right)),
1125                                        Command::Draw(Renderable::Image(renderable_bottom_left)),
1126                                        Command::Draw(Renderable::Image(renderable_bottom_center)),
1127                                        Command::Draw(Renderable::Image(renderable_bottom_right)),
1128                                        Command::Restore,
1129                                    ]
1130                                }
1131                            }
1132                        }
1133                    } else {
1134                        vec![]
1135                    }
1136                }
1137                ImageBoxMaterial::Procedural(_) => vec![],
1138            },
1139            WidgetUnit::TextBox(unit) => {
1140                if let Some(item) = layout.items.get(&unit.id) {
1141                    let local_space = mapping.virtual_to_real_rect(item.local_space, local);
1142                    let transform = Self::make_transform_command(&unit.transform, local_space);
1143                    let rect = RauiRect {
1144                        left: 0.0,
1145                        right: local_space.width(),
1146                        top: 0.0,
1147                        bottom: local_space.height(),
1148                    };
1149                    let mut font = unit.font.clone();
1150                    font.size *= mapping.scale().x.max(mapping.scale().y);
1151                    let renderable = Self::make_text_renderable(
1152                        &unit.text,
1153                        &font,
1154                        rect,
1155                        unit.horizontal_align,
1156                        unit.color,
1157                    );
1158                    vec![Command::Store, transform, renderable, Command::Restore]
1159                } else {
1160                    vec![]
1161                }
1162            }
1163        }
1164    }
1165}
1166
1167impl<'a> Renderer<Vec<Command<'static>>, ()> for RauiRenderer<'a> {
1168    fn render(
1169        &mut self,
1170        tree: &WidgetUnit,
1171        mapping: &CoordsMapping,
1172        layout: &Layout,
1173    ) -> Result<Vec<Command<'static>>, ()> {
1174        Ok(self.render_node(tree, mapping, layout, false))
1175    }
1176}
1177
1178#[derive(PropsData, Debug, Clone, Serialize, Deserialize)]
1179pub enum FilterBoxProps {
1180    None,
1181    Reset,
1182    Replace(FilterBoxValues),
1183    Combine(FilterBoxValues),
1184}
1185
1186impl Default for FilterBoxProps {
1187    fn default() -> Self {
1188        Self::None
1189    }
1190}
1191
1192impl FilterBoxProps {
1193    pub fn effect(&self) -> Option<AreaBoxRendererEffect> {
1194        match self {
1195            Self::None => None,
1196            Self::Reset => Some(AreaBoxRendererEffect {
1197                id: "filter-reset".to_owned(),
1198                ..Default::default()
1199            }),
1200            Self::Replace(values) => Some(AreaBoxRendererEffect {
1201                id: "filter-replace".to_owned(),
1202                params: values.params(),
1203            }),
1204            Self::Combine(values) => Some(AreaBoxRendererEffect {
1205                id: "filter-combine".to_owned(),
1206                params: values.params(),
1207            }),
1208        }
1209    }
1210}
1211
1212#[derive(Debug, Default, Clone, Serialize, Deserialize)]
1213pub struct FilterBoxValues {
1214    #[serde(default)]
1215    pub blur: Scalar,
1216    #[serde(default)]
1217    pub brightness: Scalar,
1218    #[serde(default)]
1219    pub contrast: Scalar,
1220    #[serde(default)]
1221    pub grayscale: Scalar,
1222    #[serde(default)]
1223    pub invert: Scalar,
1224    #[serde(default)]
1225    pub saturate: Scalar,
1226    #[serde(default)]
1227    pub sepia: Scalar,
1228    #[serde(default)]
1229    pub hue_rotate: Scalar,
1230}
1231
1232impl FilterBoxValues {
1233    pub fn params(&self) -> [Scalar; 8] {
1234        [
1235            self.blur,
1236            self.brightness,
1237            self.contrast,
1238            self.grayscale,
1239            self.invert,
1240            self.saturate,
1241            self.sepia,
1242            self.hue_rotate,
1243        ]
1244    }
1245}
1246
1247pub fn filter_box(mut context: WidgetContext) -> WidgetNode {
1248    let slot = context.named_slots.remove("content").unwrap();
1249    let renderer_effect = context
1250        .props
1251        .read_cloned_or_default::<FilterBoxProps>()
1252        .effect();
1253
1254    AreaBoxNode {
1255        id: context.id.to_owned(),
1256        slot: Box::new(slot),
1257        renderer_effect,
1258    }
1259    .into()
1260}
1261
1262pub fn bundle_installer<PB, CR>(
1263    builder: &mut AppBuilder<PB>,
1264    _: (),
1265) -> Result<(), PipelineBuilderError>
1266where
1267    PB: PipelineBuilder,
1268    CR: CompositeRenderer + 'static,
1269{
1270    builder.install_resource(ApplyUserInterfaceToCompositeRendererSystemCache::default());
1271    builder.install_system::<ApplyUserInterfaceToCompositeRendererSystemResources<CR>>(
1272        "apply-user-interface-to-composite-renderer",
1273        apply_user_interface_to_composite_renderer_system::<CR>,
1274        &[],
1275    )?;
1276    Ok(())
1277}
1278
1279pub fn prefabs_installer(prefabs: &mut PrefabManager) {
1280    prefabs.register_component_factory::<UserInterfaceViewSyncCompositeRenderable>(
1281        "UserInterfaceViewSyncCompositeRenderable",
1282    );
1283}