makepad_draw/shader/
draw_icon.rs

1use {
2    std::rc::Rc,
3    crate::{
4        makepad_platform::*,
5        draw_list_2d::ManyInstances,
6        geometry::GeometryQuad2D,
7        icon_atlas::{CxIconAtlas, CxIconArgs},
8        cx_2d::Cx2d,
9        turtle::{Walk, Size}
10    },
11};
12
13
14live_design!{
15    
16    DrawIcon = {{DrawIcon}} {
17        color: #fff
18        
19        uniform u_brightness: float
20        uniform u_curve: float
21        
22        texture tex: texture2d
23        varying pos: vec2,
24        varying tex_coord1: vec2
25        varying clipped: vec2
26        
27        fn clip_and_transform_vertex(self, rect_pos: vec2, rect_size: vec2) -> vec4 {
28            let clipped: vec2 = clamp(
29                self.geom_pos * rect_size + rect_pos,
30                self.draw_clip.xy,
31                self.draw_clip.zw
32            )
33            self.pos = (clipped - rect_pos) / rect_size
34            
35            self.tex_coord1 = mix(
36                self.icon_t1.xy,
37                self.icon_t2.xy,
38                self.pos.xy
39            )
40            
41            // only pass the clipped position forward
42            return self.camera_projection * (self.camera_view * (self.view_transform * vec4(
43                clipped.x,
44                clipped.y,
45                self.draw_depth + self.draw_zbias,
46                1.
47            )))
48        }
49        
50        fn vertex(self) -> vec4 {
51            return self.clip_and_transform_vertex(self.rect_pos, self.rect_size)
52        }
53        
54        fn get_color(self) -> vec4 {
55            return self.color;
56        }
57        
58        fn pixel(self) -> vec4 {
59            let dx = dFdx(vec2(self.tex_coord1.x * 2048.0, 0.)).x;
60            let dp = 1.0 / 2048.0;
61            
62            // basic hardcoded mipmapping so it stops 'swimming' in VR
63            // mipmaps are stored in red/green/blue channel
64            let s = sample2d_rt(self.tex, self.tex_coord1.xy).x;
65            s = pow(s, self.u_curve);
66            let col = self.get_color(); //color!(white);//get_color();
67            return vec4(s * col.rgb * self.u_brightness * col.a, s * col.a);
68        }
69    }
70}
71
72#[derive(Live)]
73#[repr(C)]
74pub struct DrawIcon {
75    #[live(1.0)] pub brightness: f32,
76    #[live(0.6)] pub curve: f32,
77    #[live(0.5)] pub linearize: f32,
78    
79    #[live] pub svg_file: LiveDependency,
80    #[live] pub svg_path: Rc<String>,
81    #[live] pub translate: DVec2,
82    #[live(1.0)] pub scale: f64,
83    
84    #[rust] pub many_instances: Option<ManyInstances>,
85    #[live] pub geometry: GeometryQuad2D,
86    #[deref] pub draw_vars: DrawVars,
87    #[calc] pub rect_pos: Vec2,
88    #[calc] pub rect_size: Vec2,
89    #[calc] pub draw_clip: Vec4,
90    #[live(1.0)] pub draw_depth: f32,
91    
92    #[live] pub color: Vec4,
93    #[calc] pub icon_t1: Vec2,
94    #[calc] pub icon_t2: Vec2,
95}
96
97impl LiveHook for DrawIcon{
98    fn before_apply(&mut self, cx: &mut Cx, apply_from: ApplyFrom, index: usize, nodes: &[LiveNode]){
99        self.draw_vars.before_apply_init_shader(cx, apply_from, index, nodes, &self.geometry);
100    }
101    fn after_apply(&mut self, cx: &mut Cx, apply_from: ApplyFrom, index: usize, nodes: &[LiveNode]) {
102        self.draw_vars.after_apply_update_self(cx, apply_from, index, nodes, &self.geometry);
103    }
104}
105
106impl DrawIcon {
107    
108    pub fn new_draw_call(&self, cx: &mut Cx2d) {
109        cx.new_draw_call(&self.draw_vars);
110    }
111    
112    pub fn append_to_draw_call(&self, cx: &mut Cx2d) {
113        cx.new_draw_call(&self.draw_vars);
114    }
115    
116    pub fn begin_many_instances(&mut self, cx: &mut Cx2d) {
117        let icon_atlas_rc = cx.icon_atlas_rc.clone();
118        let icon_atlas = icon_atlas_rc.0.borrow();
119        let mi = cx.begin_many_aligned_instances(&self.draw_vars);
120        self.update_draw_call_vars(&*icon_atlas);
121        self.many_instances = mi;
122    }
123    
124    pub fn end_many_instances(&mut self, cx: &mut Cx2d) {
125        if let Some(mi) = self.many_instances.take() {
126            let new_area = cx.end_many_instances(mi);
127            self.draw_vars.area = cx.update_area_refs(self.draw_vars.area, new_area);
128        }
129    }
130    
131    pub fn draw_walk(&mut self, cx: &mut Cx2d, mut walk: Walk) {
132        let icon_atlas_rc = cx.icon_atlas_rc.clone();
133        let mut icon_atlas = icon_atlas_rc.0.borrow_mut();
134        let icon_atlas = &mut*icon_atlas;
135        if let Some((path_hash, bounds)) = icon_atlas.get_icon_bounds(cx, &self.svg_path, self.svg_file.as_ref()) {
136            let width_is_fit = walk.width.is_fit();
137            let height_is_fit = walk.height.is_fit();
138            let peek_rect = cx.peek_walk_turtle(walk);
139            let mut scale = 1.0;
140
141            if width_is_fit {
142                if !height_is_fit {
143                    scale = peek_rect.size.y / bounds.size.y
144                };
145                walk.width = Size::Fixed(bounds.size.x * self.scale * scale);
146            }
147            if height_is_fit {
148                if !width_is_fit {
149                    scale = peek_rect.size.x / bounds.size.x
150                };
151                walk.height = Size::Fixed(bounds.size.y * self.scale * scale);
152            }
153            if !width_is_fit && !height_is_fit {
154                scale = (peek_rect.size.y / bounds.size.y).min(peek_rect.size.x / bounds.size.x);
155            }
156            let rect = cx.walk_turtle(walk);
157            if rect.is_nan(){
158                return
159            }
160
161            let dpi_factor = cx.current_dpi_factor();
162            
163            // we should snap the subpixel to 8x8 steps
164            let dpi_pos = rect.pos * dpi_factor;
165            let snapped_pos = dpi_pos.floor();
166            let snapped_size = (rect.size * dpi_factor).ceil() + dvec2(1.0, 1.0);
167            let subpixel = dvec2(
168                ((dpi_pos.x - snapped_pos.x) * 8.0).floor() / 8.0,
169                ((dpi_pos.y - snapped_pos.y) * 8.0).floor() / 8.0
170            );
171            
172            // ok now we need to snap our rect to real pixels
173            let slot = icon_atlas.get_icon_slot(CxIconArgs {
174                linearize: self.linearize as f64,
175                size: snapped_size,
176                scale: self.scale * scale * dpi_factor,
177                translate: self.translate - bounds.pos,
178                subpixel: subpixel 
179            }, path_hash);
180            
181            // lets snap the pos/size to actual pixels
182            self.rect_pos = (snapped_pos / dpi_factor).into();
183            self.rect_size = (snapped_size / dpi_factor).into();
184            
185            self.icon_t1 = slot.t1;
186            self.icon_t2 = slot.t2;
187            
188            if let Some(mi) = &mut self.many_instances {
189                mi.instances.extend_from_slice(self.draw_vars.as_slice());
190            }
191            else if self.draw_vars.can_instance() {
192                self.update_draw_call_vars(icon_atlas);
193                let new_area = cx.add_aligned_instance(&self.draw_vars);
194                self.draw_vars.area = cx.update_area_refs(self.draw_vars.area, new_area);
195            }
196        }
197    }
198    
199    pub fn update_draw_call_vars(&mut self, atlas: &CxIconAtlas) {
200        self.draw_vars.texture_slots[0] = Some(atlas.texture_id);
201        self.draw_vars.user_uniforms[0] = self.brightness;
202        self.draw_vars.user_uniforms[1] = self.curve;
203    }
204    
205}