1use crate::{
2 makepad_derive_widget::*,
3 image_cache::*,
4 makepad_draw::*,
5 widget::*
6};
7use std::sync::Arc;
8use std::path::{Path, PathBuf};
9
10live_design!{
11 link widgets;
12 use link::shaders::*;
13
14 DrawImage= {{DrawImage}} {
15 texture image: texture2d
16 opacity: 1.0
17 image_scale: vec2(1.0, 1.0)
18 image_pan: vec2(0.0, 0.0)
19
20 fn get_color_scale_pan(self, scale: vec2, pan: vec2) -> vec4 {
21 return sample2d(self.image, self.pos * scale + pan).xyzw;
22 }
23
24 fn get_color(self) -> vec4 {
25 return self.get_color_scale_pan(self.image_scale, self.image_pan)
26 }
27
28 fn pixel(self) -> vec4 {
29 let color = mix(self.get_color(), #3, self.async_load);
30 return Pal::premul(vec4(color.xyz, color.w * self.opacity))
31 }
32
33 }
34
35 pub ImageBase = {{Image}} {}
36
37 pub Image = <ImageBase> {
38 animator: {
39 async_load = {
40 default: off,
41 off = {
42 from: {all: Forward {duration: 0.1}}
43 apply: {
44 draw_bg: {async_load: 0.0}
45 }
46 }
47 on = {
48 from: {
49 all: Forward {duration: 0.1}
50 }
51 apply: {
52 draw_bg: {async_load: 1.0}
53 }
54 }
55 }
56 }
57
58 width: 100
59 height: 100
60 }
61
62}
63
64#[derive(Live, LiveHook, LiveRegister)]
65#[repr(C)]
66pub struct DrawImage {
67 #[deref] draw_super: DrawQuad,
68 #[live] pub opacity: f32,
69 #[live] image_scale: Vec2,
70 #[live] image_pan: Vec2,
71 #[live] async_load: f32
72}
73
74
75#[derive(Copy, Clone, Debug, Live, LiveHook)]
76#[live_ignore]
77pub enum ImageAnimation {
78 Stop,
79 Once,
80 #[pick] Loop,
81 Bounce,
82 #[live(0.0)] Frame(f64),
83 #[live(0.0)] Factor(f64),
84 #[live(60.0)] OnceFps(f64),
85 #[live(60.0)] LoopFps(f64),
86 #[live(60.0)] BounceFps(f64),
87}
88
89#[derive(Live, Widget)]
90pub struct Image {
91 #[walk] walk: Walk,
92 #[animator] animator: Animator,
93 #[redraw] #[live] pub draw_bg: DrawImage,
94 #[live] min_width: i64,
95 #[live] min_height: i64,
96 #[live(1.0)] width_scale: f64,
97 #[live(ImageAnimation::BounceFps(25.0))] animation: ImageAnimation,
98 #[rust] last_time: Option<f64>,
99 #[rust] animation_frame: f64,
100 #[visible] #[live(true)] visible: bool,
101 #[rust] next_frame: NextFrame,
102 #[live] fit: ImageFit,
103 #[live] source: LiveDependency,
104 #[rust] async_image_path: Option<PathBuf>,
105 #[rust] async_image_size: Option<(usize, usize)>,
106 #[rust] texture: Option<Texture>,
107}
108
109impl ImageCacheImpl for Image {
110 fn get_texture(&self, _id:usize) -> &Option<Texture> {
111 &self.texture
112 }
113
114 fn set_texture(&mut self, texture: Option<Texture>, _id:usize) {
115 self.texture = texture;
116 }
117}
118
119impl LiveHook for Image{
120 fn after_apply(&mut self, cx: &mut Cx, apply: &mut Apply, _index: usize, _nodes: &[LiveNode]) {
121 match apply.from{
122 ApplyFrom::NewFromDoc{..}|ApplyFrom:: UpdateFromDoc{..}|
124 ApplyFrom::Over{..}=>{
125 self.lazy_create_image_cache(cx);
126 let source = self.source.clone();
127 if source.as_str().len()>0 {
128 let _ = self.load_image_dep_by_path(cx, source.as_str(), 0);
129 }
130 }
131 _=>()
132 }
133 }
134}
135
136impl Widget for Image {
137 fn handle_event(&mut self, cx:&mut Cx, event:&Event, _scope:&mut Scope){
138 if self.animator_handle_event(cx, event).must_redraw() {
139 self.draw_bg.redraw(cx);
140 }
141 if let Event::Actions(actions) = &event{
143 for action in actions{
144 if let Some(AsyncImageLoad{image_path, result}) = &action.downcast_ref(){
145 if let Some(result) = result.borrow_mut().take(){
146 self.process_async_image_load(cx, image_path, result);
148 }
149 if self.async_image_size.is_some() && self.async_image_path.clone() == Some(image_path.to_path_buf()){ self.load_image_from_cache(cx, image_path, 0);
151 self.async_image_size = None;
152 self.animator_play(cx, id!(async_load.off));
153 self.redraw(cx);
154 }
155 }
156 }
157 }
158 if let Some(nf) = self.next_frame.is_event(event) {
159 if let Some(image_texture) = &self.texture {
161 let (texture_width, texture_height) = image_texture.get_format(cx).vec_width_height().unwrap_or((self.min_width as usize, self.min_height as usize));
162 if let Some(animation) = image_texture.animation(cx).clone(){
163 let delta = if let Some(last_time) = &self.last_time{
164 nf.time - last_time
165 }
166 else{
167 0.0
168 };
169 self.last_time = Some(nf.time);
170 let num_frames = animation.num_frames as f64;
171 match self.animation{
172 ImageAnimation::Stop=>{
173
174 }
175 ImageAnimation::Frame(frame)=>{
176 self.animation_frame = frame;
177 }
178 ImageAnimation::Factor(pos)=>{
179 self.animation_frame = pos * (num_frames - 1.0);
180 }
181 ImageAnimation::Once=>{
182 self.animation_frame += 1.0;
183 if self.animation_frame >= num_frames{
184 self.animation_frame = num_frames - 1.0;
185 }
186 else{
187 self.next_frame = cx.new_next_frame();
188 }
189 }
190 ImageAnimation::Loop=>{
191 self.animation_frame += 1.0;
192 if self.animation_frame >= num_frames{
193 self.animation_frame = 0.0;
194 }
195 self.next_frame = cx.new_next_frame();
196 }
197 ImageAnimation::Bounce=>{
198 self.animation_frame += 1.0;
199 if self.animation_frame >= num_frames * 2.0{
200 self.animation_frame = 0.0;
201 }
202 self.next_frame = cx.new_next_frame();
203 }
204 ImageAnimation::OnceFps(fps)=>{
205 self.animation_frame += delta * fps;
206 if self.animation_frame >= num_frames{
207 self.animation_frame = num_frames - 1.0;
208 }
209 else{
210 self.next_frame = cx.new_next_frame();
211 }
212 }
213 ImageAnimation::LoopFps(fps)=>{
214 self.animation_frame += delta * fps;
215 if self.animation_frame >= num_frames{
216 self.animation_frame = 0.0;
217 }
218 self.next_frame = cx.new_next_frame();
219 }
220 ImageAnimation::BounceFps(fps)=>{
221 self.animation_frame += delta * fps;
222 if self.animation_frame >= num_frames * 2.0{
223 self.animation_frame = 0.0;
224 }
225 self.next_frame = cx.new_next_frame();
226 }
227 }
228 let last_pan = self.draw_bg.image_pan;
230
231 let frame = if self.animation_frame >= num_frames{
232 num_frames * 2.0 - 1.0 - self.animation_frame
233 }
234 else{
235 self.animation_frame
236 } as usize;
237
238 let horizontal_frames = texture_width / animation.width;
239 let xpos = ((frame % horizontal_frames) * animation.width) as f32 / texture_width as f32;
240 let ypos = ((frame / horizontal_frames) * animation.height) as f32 / texture_height as f32;
241 self.draw_bg.image_pan = vec2(xpos, ypos);
242 if self.draw_bg.image_pan != last_pan{
243 self.draw_bg.update_instance_area_value(cx, id!(image_pan))
245 }
246 }
247 }
248 }
249 }
250
251 fn draw_walk(&mut self, cx: &mut Cx2d, _scope: &mut Scope, walk: Walk) -> DrawStep {
252 self.draw_walk(cx, walk)
253 }
254}
255
256impl Image {
257 pub fn size_in_pixels(&self, cx: &mut Cx) -> Option<(usize, usize)> {
261 self.texture.as_ref()
262 .and_then(|t| t.get_format(cx).vec_width_height())
263 }
264
265 pub fn has_texture(&self) -> bool {
267 self.texture.is_some()
268 }
269
270 pub fn draw_walk(&mut self, cx: &mut Cx2d, mut walk: Walk) -> DrawStep {
271 if !self.visible{
272 return DrawStep::done()
273 }
274 let rect = cx.peek_walk_turtle(walk);
277 let dpi = cx.current_dpi_factor();
278
279 let (width, height) = if let Some((w,h)) = &self.async_image_size{
280 (*w as f64,*h as f64)
283 }else if let Some(image_texture) = &self.texture {
284 self.draw_bg.draw_vars.set_texture(0, image_texture);
285 let (width,height) = image_texture.get_format(cx).vec_width_height().unwrap_or((self.min_width as usize, self.min_height as usize));
286 if let Some(animation) = image_texture.animation(cx){
287 let (w,h) = (animation.width as f64, animation.height as f64);
288 self.next_frame = cx.new_next_frame();
289 let scale_x = w as f32 / width as f32;
291 let scale_y = h as f32 / height as f32;
292 self.draw_bg.image_scale = vec2(scale_x, scale_y);
293 (w,h)
294 }
295 else{
296 self.draw_bg.image_scale = vec2(1.0,1.0);
297 self.draw_bg.image_pan = vec2(0.0,0.0);
298 (width as f64 * self.width_scale, height as f64)
299 }
300 }
301 else {
302 self.draw_bg.draw_vars.empty_texture(0);
303 (self.min_width as f64 / dpi, self.min_height as f64 / dpi)
304 };
305
306 let aspect = width / height;
307 match self.fit {
308 ImageFit::Size => {
309 walk.width = Size::Fixed(width);
310 walk.height = Size::Fixed(height);
311 }
312 ImageFit::Stretch => {
313 }
314 ImageFit::Horizontal => {
315 walk.height = Size::Fixed(rect.size.x / aspect);
316 }
317 ImageFit::Vertical => {
318 walk.width = Size::Fixed(rect.size.y * aspect);
319 }
320 ImageFit::Smallest => {
321 let walk_height = rect.size.x / aspect;
322 if walk_height > rect.size.y {
323 walk.width = Size::Fixed(rect.size.y * aspect);
324 }
325 else {
326 walk.height = Size::Fixed(walk_height);
327 }
328 }
329 ImageFit::Biggest => {
330 let walk_height = rect.size.x / aspect;
331 if walk_height < rect.size.y {
332 walk.width = Size::Fixed(rect.size.y * aspect);
333 }
334 else {
335 walk.height = Size::Fixed(walk_height);
336 }
337 }
338 }
339
340
341 self.draw_bg.draw_walk(cx, walk);
342
343 DrawStep::done()
344 }
345
346 pub fn load_image_file_by_path_async(&mut self, cx: &mut Cx, image_path: &Path) -> Result<(), ImageError> {
348 if let Ok(result) = self.load_image_file_by_path_async_impl(cx, image_path, 0){
349 match result{
350 AsyncLoadResult::Loading(w,h)=>{
351 self.async_image_size = Some((w,h));
352 self.async_image_path = Some(image_path.into());
353 self.animator_play(cx, id!(async_load.on));
354 self.redraw(cx);
355 }
356 AsyncLoadResult::Loaded=>{
357 self.redraw(cx);
358 }
359 }
360 }
362 Ok(())
363 }
364 pub fn load_image_from_data_async(&mut self, cx: &mut Cx, image_path: &Path, data: Arc<Vec<u8>>) -> Result<(), ImageError> {
365 if let Ok(result) = self.load_image_from_data_async_impl(cx, image_path, data, 0){
366 match result{
367 AsyncLoadResult::Loading(w,h)=>{
368 self.async_image_size = Some((w,h));
369 self.async_image_path = Some(image_path.into());
370 self.animator_play(cx, id!(async_load.on));
371 self.redraw(cx);
372 }
373 AsyncLoadResult::Loaded=>{
374 self.redraw(cx);
375 }
376 }
377 }
379 Ok(())
380 }
381}
382
383pub enum AsyncLoad{
384 Yes,
385 No
386}
387
388impl ImageRef {
389 pub fn load_image_dep_by_path(&self, cx: &mut Cx, image_path: &str) -> Result<(), ImageError> {
391 if let Some(mut inner) = self.borrow_mut() {
392 inner.load_image_dep_by_path(cx, image_path, 0)
393 } else {
394 Ok(()) }
396 }
397
398 pub fn load_image_file_by_path(&self, cx: &mut Cx, image_path: &Path) -> Result<(), ImageError> {
400 if let Some(mut inner) = self.borrow_mut() {
401 inner.load_image_file_by_path(cx, image_path, 0)
402 } else {
403 Ok(()) }
405 }
406
407 pub fn load_image_file_by_path_async(&self, cx: &mut Cx, image_path: &Path) -> Result<(), ImageError> {
409 if let Some(mut inner) = self.borrow_mut() {
410 return inner.load_image_file_by_path_async(cx, image_path)
411 }
412 Ok(())
413 }
414
415 pub fn load_image_from_data_async(&self, cx: &mut Cx, image_path: &Path, data:Arc<Vec<u8>>) -> Result<(), ImageError> {
417 if let Some(mut inner) = self.borrow_mut() {
418 return inner.load_image_from_data_async(cx, image_path, data)
419 }
420 Ok(())
421 }
422
423 pub fn load_jpg_from_data(&self, cx: &mut Cx, data: &[u8]) -> Result<(), ImageError> {
425 if let Some(mut inner) = self.borrow_mut() {
426 inner.load_jpg_from_data(cx, data, 0)
427 } else {
428 Ok(()) }
430 }
431
432 pub fn load_png_from_data(&self, cx: &mut Cx, data: &[u8]) -> Result<(), ImageError> {
434 if let Some(mut inner) = self.borrow_mut() {
435 inner.load_png_from_data(cx, data, 0)
436 } else {
437 Ok(()) }
439 }
440
441 pub fn set_texture(&self, cx:&mut Cx, texture: Option<Texture>) {
442 if let Some(mut inner) = self.borrow_mut() {
443 inner.texture = texture;
444 if cx.in_draw_event(){
445 inner.redraw(cx);
446 }
447 }
448 }
449
450 pub fn set_uniform(&self, cx: &Cx, uniform: &[LiveId], value: &[f32]) {
451 if let Some(mut inner) = self.borrow_mut() {
452 inner.draw_bg.set_uniform(cx, uniform, value);
453 }
454 }
455
456 pub fn size_in_pixels(&self, cx: &mut Cx) -> Option<(usize, usize)> {
458 if let Some(inner) = self.borrow() {
459 inner.size_in_pixels(cx)
460 } else {
461 None
462 }
463 }
464
465 pub fn has_texture(&self) -> bool {
467 if let Some(inner) = self.borrow() {
468 inner.has_texture()
469 } else {
470 false
471 }
472 }
473}
474