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