@@ -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,59 @@ 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+ for ( index, frame) in data. iter ( ) . enumerate ( ) {
225+ let [ width, height] = frame. image . size ;
226+ let timestamp = index as i32 * {
227+ let delay = frame. delay . as_millis ( ) ;
228+ if delay < i32:: MAX as u128 {
229+ delay as i32
230+ } else {
231+ // If delay is to big defaulting to 300 ms
232+ 300i32
233+ }
234+ } ;
235+
236+ encoder. add_frame ( AnimFrame :: from_rgba (
237+ frame. image . as_raw ( ) ,
238+ width as u32 ,
239+ height as u32 ,
240+ timestamp,
241+ ) ) ;
242+ }
243+
244+ let webp = encoder. encode ( ) ;
245+
246+ Ok ( std:: fs:: write ( file_path, & * webp) ?)
247+ }
248+
183249 pub fn key ( url : & str ) -> String {
184250 let k: String = sha2:: Sha256 :: digest ( url. as_bytes ( ) ) . encode_hex ( ) ;
185251 PathBuf :: from ( & k[ 0 ..2 ] )
@@ -272,11 +338,13 @@ pub struct Images {
272338 pub base_path : path:: PathBuf ,
273339 pub static_imgs : MediaCache ,
274340 pub gifs : MediaCache ,
341+ pub webps : MediaCache ,
275342 pub textures : TexturesCache ,
276343 pub urls : UrlMimes ,
277344 /// cached imeta data
278345 pub metadata : HashMap < String , ImageMetadata > ,
279346 pub gif_states : GifStateMap ,
347+ pub webp_states : WebpStateMap ,
280348}
281349
282350impl Images {
@@ -286,16 +354,19 @@ impl Images {
286354 base_path : path. clone ( ) ,
287355 static_imgs : MediaCache :: new ( & path, MediaCacheType :: Image ) ,
288356 gifs : MediaCache :: new ( & path, MediaCacheType :: Gif ) ,
357+ webps : MediaCache :: new ( & path, MediaCacheType :: Webp ) ,
289358 urls : UrlMimes :: new ( UrlCache :: new ( path. join ( UrlCache :: rel_dir ( ) ) ) ) ,
290359 gif_states : Default :: default ( ) ,
360+ webp_states : Default :: default ( ) ,
291361 metadata : Default :: default ( ) ,
292362 textures : TexturesCache :: new ( path. clone ( ) ) ,
293363 }
294364 }
295365
296366 pub fn migrate_v0 ( & self ) -> Result < ( ) > {
297367 self . static_imgs . migrate_v0 ( ) ?;
298- self . gifs . migrate_v0 ( )
368+ self . gifs . migrate_v0 ( ) ?;
369+ self . webps . migrate_v0 ( )
299370 }
300371
301372 pub fn get_renderable_media ( & mut self , url : & str ) -> Option < RenderableMedia > {
@@ -334,7 +405,9 @@ impl Images {
334405 let mut loader = NoLoadingLatestTex :: new (
335406 & self . textures . static_image ,
336407 & self . textures . animated ,
408+ & self . textures . webp ,
337409 & mut self . gif_states ,
410+ & mut self . webp_states ,
338411 ) ;
339412 loader. latest ( jobs, ui. ctx ( ) , url, cache_type, img_type, animation_mode)
340413 }
@@ -343,13 +416,15 @@ impl Images {
343416 match cache_type {
344417 MediaCacheType :: Image => & self . static_imgs ,
345418 MediaCacheType :: Gif => & self . gifs ,
419+ MediaCacheType :: Webp => & self . webps ,
346420 }
347421 }
348422
349423 pub fn get_cache_mut ( & mut self , cache_type : MediaCacheType ) -> & mut MediaCache {
350424 match cache_type {
351425 MediaCacheType :: Image => & mut self . static_imgs ,
352426 MediaCacheType :: Gif => & mut self . gifs ,
427+ MediaCacheType :: Webp => & mut self . webps ,
353428 }
354429 }
355430
@@ -368,7 +443,9 @@ impl Images {
368443 self . urls . cache . clear ( ) ;
369444 self . static_imgs . clear ( ) ;
370445 self . gifs . clear ( ) ;
446+ self . webps . clear ( ) ;
371447 self . gif_states . clear ( ) ;
448+ self . webp_states . clear ( ) ;
372449
373450 Ok ( ( ) )
374451 }
@@ -378,7 +455,9 @@ impl Images {
378455 NoLoadingLatestTex :: new (
379456 & self . textures . static_image ,
380457 & self . textures . animated ,
458+ & self . textures . webp ,
381459 & mut self . gif_states ,
460+ & mut self . webp_states ,
382461 ) ,
383462 & self . textures . blurred ,
384463 )
@@ -392,14 +471,17 @@ impl Images {
392471 NoLoadingLatestTex :: new (
393472 & self . textures . static_image ,
394473 & self . textures . animated ,
474+ & self . textures . webp ,
395475 & mut self . gif_states ,
476+ & mut self . webp_states ,
396477 )
397478 }
398479
399480 pub fn user_trusts_img ( & self , url : & str , media_type : MediaCacheType ) -> bool {
400481 match media_type {
401482 MediaCacheType :: Image => self . textures . static_image . contains ( url) ,
402483 MediaCacheType :: Gif => self . textures . animated . contains ( url) ,
484+ MediaCacheType :: Webp => self . textures . webp . contains ( url) ,
403485 }
404486 }
405487}
@@ -413,6 +495,15 @@ pub struct GifState {
413495 pub last_frame_index : usize ,
414496}
415497
498+ pub type WebpStateMap = HashMap < String , WebpState > ;
499+
500+ pub struct WebpState {
501+ pub last_frame_rendered : Instant ,
502+ pub last_frame_duration : Duration ,
503+ pub next_frame_time : Option < SystemTime > ,
504+ pub last_frame_index : usize ,
505+ }
506+
416507pub struct LatestTexture {
417508 pub texture : TextureHandle ,
418509 pub request_next_repaint : Option < SystemTime > ,
0 commit comments