@@ -14,6 +14,7 @@ use egui::TextureHandle;
1414use image:: { Delay , Frame } ;
1515
1616use egui:: ColorImage ;
17+ use webp:: AnimFrame ;
1718
1819use std:: collections:: HashMap ;
1920use std:: fs:: { self , create_dir_all, File } ;
@@ -31,6 +32,7 @@ pub struct TexturesCache {
3132 pub static_image : StaticImgTexCache ,
3233 pub blurred : BlurCache ,
3334 pub animated : AnimatedImgTexCache ,
35+ pub webp : crate :: media:: webp:: WebpTexCache ,
3436}
3537
3638impl TexturesCache {
@@ -43,6 +45,9 @@ impl TexturesCache {
4345 animated : AnimatedImgTexCache :: new (
4446 base_dir. join ( MediaCache :: rel_dir ( MediaCacheType :: Gif ) ) ,
4547 ) ,
48+ webp : crate :: media:: webp:: WebpTexCache :: new (
49+ base_dir. join ( MediaCache :: rel_dir ( MediaCacheType :: Webp ) ) ,
50+ ) ,
4651 }
4752 }
4853}
@@ -53,6 +58,12 @@ pub enum TextureState<T> {
5358 Loaded ( T ) ,
5459}
5560
61+ impl < T > TextureState < T > {
62+ pub fn is_loaded ( & self ) -> bool {
63+ matches ! ( self , TextureState :: Loaded ( _) )
64+ }
65+ }
66+
5667impl < T > std:: fmt:: Debug for TextureState < T > {
5768 fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
5869 match self {
@@ -102,6 +113,7 @@ pub struct MediaCache {
102113pub enum MediaCacheType {
103114 Image ,
104115 Gif ,
116+ Webp ,
105117}
106118
107119impl MediaCache {
@@ -136,6 +148,7 @@ impl MediaCache {
136148 match cache_type {
137149 MediaCacheType :: Image => "img" ,
138150 MediaCacheType :: Gif => "gif" ,
151+ MediaCacheType :: Webp => "webp" ,
139152 }
140153 }
141154
@@ -180,6 +193,60 @@ impl MediaCache {
180193 Ok ( ( ) )
181194 }
182195
196+ pub fn write_webp ( cache_dir : & path:: Path , url : & str , data : Vec < ImageFrame > ) -> Result < ( ) > {
197+ if data. is_empty ( ) {
198+ return Err ( crate :: Error :: Generic (
199+ "No frames provided to write_webp" . to_owned ( ) ,
200+ ) ) ;
201+ }
202+
203+ let file_path = cache_dir. join ( Self :: key ( url) ) ;
204+ if let Some ( p) = file_path. parent ( ) {
205+ create_dir_all ( p) ?;
206+ }
207+
208+ // TODO: makes sense to make it static
209+ let mut config = webp:: WebPConfig :: new ( ) . or ( Err ( crate :: Error :: Generic (
210+ "Failed to configure webp encoder" . to_owned ( ) ,
211+ ) ) ) ?;
212+ config. lossless = 1 ;
213+ config. alpha_compression = 0 ;
214+
215+ let reference_frame: & ImageFrame = data. first ( ) . ok_or ( crate :: Error :: Generic (
216+ "No frames provided to write_webp" . to_owned ( ) ,
217+ ) ) ?;
218+ let mut encoder = webp:: AnimEncoder :: new (
219+ reference_frame. image . size [ 0 ] as u32 ,
220+ reference_frame. image . size [ 1 ] as u32 ,
221+ & config,
222+ ) ;
223+
224+ let _ = data. iter ( ) . fold ( 0i32 , |acc_timestamp, frame| {
225+ let [ width, height] = frame. image . size ;
226+ let delay = frame. delay . as_millis ( ) ;
227+ let frame_delay = if delay < i32:: MAX as u128 {
228+ delay as i32
229+ } else {
230+ 300i32
231+ } ;
232+
233+ let timestamp = acc_timestamp;
234+
235+ encoder. add_frame ( AnimFrame :: from_rgba (
236+ frame. image . as_raw ( ) ,
237+ width as u32 ,
238+ height as u32 ,
239+ timestamp,
240+ ) ) ;
241+
242+ acc_timestamp. saturating_add ( frame_delay)
243+ } ) ;
244+
245+ let webp = encoder. encode ( ) ;
246+
247+ Ok ( std:: fs:: write ( file_path, & * webp) ?)
248+ }
249+
183250 pub fn key ( url : & str ) -> String {
184251 let k: String = sha2:: Sha256 :: digest ( url. as_bytes ( ) ) . encode_hex ( ) ;
185252 PathBuf :: from ( & k[ 0 ..2 ] )
@@ -272,11 +339,13 @@ pub struct Images {
272339 pub base_path : path:: PathBuf ,
273340 pub static_imgs : MediaCache ,
274341 pub gifs : MediaCache ,
342+ pub webps : MediaCache ,
275343 pub textures : TexturesCache ,
276344 pub urls : UrlMimes ,
277345 /// cached imeta data
278346 pub metadata : HashMap < String , ImageMetadata > ,
279347 pub gif_states : GifStateMap ,
348+ pub webp_states : WebpStateMap ,
280349}
281350
282351impl Images {
@@ -286,16 +355,19 @@ impl Images {
286355 base_path : path. clone ( ) ,
287356 static_imgs : MediaCache :: new ( & path, MediaCacheType :: Image ) ,
288357 gifs : MediaCache :: new ( & path, MediaCacheType :: Gif ) ,
358+ webps : MediaCache :: new ( & path, MediaCacheType :: Webp ) ,
289359 urls : UrlMimes :: new ( UrlCache :: new ( path. join ( UrlCache :: rel_dir ( ) ) ) ) ,
290360 gif_states : Default :: default ( ) ,
361+ webp_states : Default :: default ( ) ,
291362 metadata : Default :: default ( ) ,
292363 textures : TexturesCache :: new ( path. clone ( ) ) ,
293364 }
294365 }
295366
296367 pub fn migrate_v0 ( & self ) -> Result < ( ) > {
297368 self . static_imgs . migrate_v0 ( ) ?;
298- self . gifs . migrate_v0 ( )
369+ self . gifs . migrate_v0 ( ) ?;
370+ self . webps . migrate_v0 ( )
299371 }
300372
301373 pub fn get_renderable_media ( & mut self , url : & str ) -> Option < RenderableMedia > {
@@ -334,7 +406,9 @@ impl Images {
334406 let mut loader = NoLoadingLatestTex :: new (
335407 & self . textures . static_image ,
336408 & self . textures . animated ,
409+ & self . textures . webp ,
337410 & mut self . gif_states ,
411+ & mut self . webp_states ,
338412 ) ;
339413 loader. latest ( jobs, ui. ctx ( ) , url, cache_type, img_type, animation_mode)
340414 }
@@ -343,13 +417,15 @@ impl Images {
343417 match cache_type {
344418 MediaCacheType :: Image => & self . static_imgs ,
345419 MediaCacheType :: Gif => & self . gifs ,
420+ MediaCacheType :: Webp => & self . webps ,
346421 }
347422 }
348423
349424 pub fn get_cache_mut ( & mut self , cache_type : MediaCacheType ) -> & mut MediaCache {
350425 match cache_type {
351426 MediaCacheType :: Image => & mut self . static_imgs ,
352427 MediaCacheType :: Gif => & mut self . gifs ,
428+ MediaCacheType :: Webp => & mut self . webps ,
353429 }
354430 }
355431
@@ -368,7 +444,9 @@ impl Images {
368444 self . urls . cache . clear ( ) ;
369445 self . static_imgs . clear ( ) ;
370446 self . gifs . clear ( ) ;
447+ self . webps . clear ( ) ;
371448 self . gif_states . clear ( ) ;
449+ self . webp_states . clear ( ) ;
372450
373451 Ok ( ( ) )
374452 }
@@ -378,7 +456,9 @@ impl Images {
378456 NoLoadingLatestTex :: new (
379457 & self . textures . static_image ,
380458 & self . textures . animated ,
459+ & self . textures . webp ,
381460 & mut self . gif_states ,
461+ & mut self . webp_states ,
382462 ) ,
383463 & self . textures . blurred ,
384464 )
@@ -392,14 +472,17 @@ impl Images {
392472 NoLoadingLatestTex :: new (
393473 & self . textures . static_image ,
394474 & self . textures . animated ,
475+ & self . textures . webp ,
395476 & mut self . gif_states ,
477+ & mut self . webp_states ,
396478 )
397479 }
398480
399481 pub fn user_trusts_img ( & self , url : & str , media_type : MediaCacheType ) -> bool {
400482 match media_type {
401483 MediaCacheType :: Image => self . textures . static_image . contains ( url) ,
402484 MediaCacheType :: Gif => self . textures . animated . contains ( url) ,
485+ MediaCacheType :: Webp => self . textures . webp . contains ( url) ,
403486 }
404487 }
405488}
@@ -413,6 +496,15 @@ pub struct GifState {
413496 pub last_frame_index : usize ,
414497}
415498
499+ pub type WebpStateMap = HashMap < String , WebpState > ;
500+
501+ pub struct WebpState {
502+ pub last_frame_rendered : Instant ,
503+ pub last_frame_duration : Duration ,
504+ pub next_frame_time : Option < SystemTime > ,
505+ pub last_frame_index : usize ,
506+ }
507+
416508pub struct LatestTexture {
417509 pub texture : TextureHandle ,
418510 pub request_next_repaint : Option < SystemTime > ,
0 commit comments