@@ -28,7 +28,7 @@ fn main() -> iced::Result {
28
28
29
29
struct Gallery {
30
30
images : Vec < Image > ,
31
- thumbnails : HashMap < Id , Thumbnail > ,
31
+ previews : HashMap < Id , Preview > ,
32
32
viewer : Viewer ,
33
33
now : Instant ,
34
34
}
@@ -40,6 +40,7 @@ enum Message {
40
40
ImageDownloaded ( Result < Rgba , Error > ) ,
41
41
ThumbnailDownloaded ( Id , Result < Rgba , Error > ) ,
42
42
ThumbnailHovered ( Id , bool ) ,
43
+ BlurhashDecoded ( Id , Result < Rgba , Error > ) ,
43
44
Open ( Id ) ,
44
45
Close ,
45
46
Animate ( Instant ) ,
@@ -50,7 +51,7 @@ impl Gallery {
50
51
(
51
52
Self {
52
53
images : Vec :: new ( ) ,
53
- thumbnails : HashMap :: new ( ) ,
54
+ previews : HashMap :: new ( ) ,
54
55
viewer : Viewer :: new ( ) ,
55
56
now : Instant :: now ( ) ,
56
57
} ,
@@ -64,7 +65,7 @@ impl Gallery {
64
65
65
66
pub fn subscription ( & self ) -> Subscription < Message > {
66
67
let is_animating = self
67
- . thumbnails
68
+ . previews
68
69
. values ( )
69
70
. any ( |thumbnail| thumbnail. is_animating ( self . now ) )
70
71
|| self . viewer . is_animating ( self . now ) ;
@@ -93,28 +94,53 @@ impl Gallery {
93
94
return Task :: none ( ) ;
94
95
} ;
95
96
96
- Task :: perform ( image. download ( Size :: Thumbnail ) , move |result| {
97
- Message :: ThumbnailDownloaded ( id, result)
98
- } )
97
+ Task :: batch ( vec ! [
98
+ Task :: perform(
99
+ image. clone( ) . blurhash(
100
+ Preview :: WIDTH as u32 ,
101
+ Preview :: HEIGHT as u32 ,
102
+ ) ,
103
+ move |result| Message :: BlurhashDecoded ( id, result) ,
104
+ ) ,
105
+ Task :: perform(
106
+ image. download( Size :: Thumbnail {
107
+ width: Preview :: WIDTH ,
108
+ } ) ,
109
+ move |result| Message :: ThumbnailDownloaded ( id, result) ,
110
+ ) ,
111
+ ] )
99
112
}
100
113
Message :: ImageDownloaded ( Ok ( rgba) ) => {
101
114
self . viewer . show ( rgba) ;
102
115
103
116
Task :: none ( )
104
117
}
105
118
Message :: ThumbnailDownloaded ( id, Ok ( rgba) ) => {
106
- let thumbnail = Thumbnail :: new ( rgba) ;
107
- let _ = self . thumbnails . insert ( id, thumbnail) ;
119
+ let thumbnail = match self . previews . remove ( & id) {
120
+ Some ( Preview :: Blurhash ( blurhash) ) => {
121
+ blurhash. to_thumbnail ( rgba)
122
+ }
123
+ _ => Preview :: thumbnail ( rgba) ,
124
+ } ;
125
+
126
+ let _ = self . previews . insert ( id, thumbnail) ;
108
127
109
128
Task :: none ( )
110
129
}
111
130
Message :: ThumbnailHovered ( id, is_hovered) => {
112
- if let Some ( thumbnail) = self . thumbnails . get_mut ( & id) {
113
- thumbnail. zoom . go_mut ( is_hovered) ;
131
+ if let Some ( Preview :: Thumbnail { zoom, .. } ) =
132
+ self . previews . get_mut ( & id)
133
+ {
134
+ zoom. go_mut ( is_hovered) ;
114
135
}
115
136
116
137
Task :: none ( )
117
138
}
139
+ Message :: BlurhashDecoded ( id, Ok ( rgba) ) => {
140
+ let _ = self . previews . insert ( id, Preview :: blurhash ( rgba) ) ;
141
+
142
+ Task :: none ( )
143
+ }
118
144
Message :: Open ( id) => {
119
145
let Some ( image) = self
120
146
. images
@@ -144,7 +170,8 @@ impl Gallery {
144
170
}
145
171
Message :: ImagesListed ( Err ( error) )
146
172
| Message :: ImageDownloaded ( Err ( error) )
147
- | Message :: ThumbnailDownloaded ( _, Err ( error) ) => {
173
+ | Message :: ThumbnailDownloaded ( _, Err ( error) )
174
+ | Message :: BlurhashDecoded ( _, Err ( error) ) => {
148
175
dbg ! ( error) ;
149
176
150
177
Task :: none ( )
@@ -157,7 +184,7 @@ impl Gallery {
157
184
row ( ( 0 ..=Image :: LIMIT ) . map ( |_| placeholder ( ) ) )
158
185
} else {
159
186
row ( self . images . iter ( ) . map ( |image| {
160
- card ( image, self . thumbnails . get ( & image. id ) , self . now )
187
+ card ( image, self . previews . get ( & image. id ) , self . now )
161
188
} ) )
162
189
}
163
190
. spacing ( 10 )
@@ -174,31 +201,59 @@ impl Gallery {
174
201
175
202
fn card < ' a > (
176
203
metadata : & ' a Image ,
177
- thumbnail : Option < & ' a Thumbnail > ,
204
+ preview : Option < & ' a Preview > ,
178
205
now : Instant ,
179
206
) -> Element < ' a , Message > {
180
- let image: Element < ' _ , _ > = if let Some ( thumbnail ) = thumbnail {
181
- image ( & thumbnail . handle )
207
+ let image: Element < ' _ , _ > = match preview {
208
+ Some ( Preview :: Blurhash ( Blurhash { handle , fade_in } ) ) => image ( handle)
182
209
. width ( Fill )
183
210
. height ( Fill )
184
211
. content_fit ( ContentFit :: Cover )
185
- . opacity ( thumbnail. fade_in . interpolate ( 0.0 , 1.0 , now) )
186
- . scale ( thumbnail. zoom . interpolate ( 1.0 , 1.1 , now) )
187
- . into ( )
188
- } else {
189
- horizontal_space ( ) . into ( )
212
+ . opacity ( fade_in. interpolate ( 0.0 , 1.0 , now) )
213
+ . into ( ) ,
214
+ Some ( Preview :: Thumbnail {
215
+ blurhash,
216
+ thumbnail,
217
+ fade_in,
218
+ zoom,
219
+ } ) => stack ! [ ]
220
+ . push_maybe (
221
+ blurhash. as_ref ( ) . filter ( |_| fade_in. is_animating ( now) ) . map (
222
+ |( handle, max_opacity) | {
223
+ image ( handle)
224
+ . width ( Fill )
225
+ . height ( Fill )
226
+ . content_fit ( ContentFit :: Cover )
227
+ . opacity ( fade_in. interpolate (
228
+ * max_opacity,
229
+ 0.0 ,
230
+ now,
231
+ ) )
232
+ } ,
233
+ ) ,
234
+ )
235
+ . push (
236
+ image ( thumbnail)
237
+ . width ( Fill )
238
+ . height ( Fill )
239
+ . content_fit ( ContentFit :: Cover )
240
+ . opacity ( fade_in. interpolate ( 0.0 , 1.0 , now) )
241
+ . scale ( zoom. interpolate ( 1.0 , 1.1 , now) ) ,
242
+ )
243
+ . into ( ) ,
244
+ None => horizontal_space ( ) . into ( ) ,
190
245
} ;
191
246
192
247
let card = mouse_area (
193
248
container ( image)
194
- . width ( Thumbnail :: WIDTH )
195
- . height ( Thumbnail :: HEIGHT )
249
+ . width ( Preview :: WIDTH )
250
+ . height ( Preview :: HEIGHT )
196
251
. style ( container:: dark) ,
197
252
)
198
253
. on_enter ( Message :: ThumbnailHovered ( metadata. id , true ) )
199
254
. on_exit ( Message :: ThumbnailHovered ( metadata. id , false ) ) ;
200
255
201
- if thumbnail . is_some ( ) {
256
+ if preview . is_some ( ) {
202
257
button ( card)
203
258
. on_press ( Message :: Open ( metadata. id ) )
204
259
. padding ( 0 )
@@ -213,29 +268,71 @@ fn card<'a>(
213
268
214
269
fn placeholder < ' a > ( ) -> Element < ' a , Message > {
215
270
container ( horizontal_space ( ) )
216
- . width ( Thumbnail :: WIDTH )
217
- . height ( Thumbnail :: HEIGHT )
271
+ . width ( Preview :: WIDTH )
272
+ . height ( Preview :: HEIGHT )
218
273
. style ( container:: dark)
219
274
. into ( )
220
275
}
221
276
222
- struct Thumbnail {
223
- handle : image:: Handle ,
277
+ struct Blurhash {
224
278
fade_in : Animation < bool > ,
225
- zoom : Animation < bool > ,
279
+ handle : image :: Handle ,
226
280
}
227
281
228
- impl Thumbnail {
282
+ impl Blurhash {
283
+ fn to_thumbnail ( self , rgba : Rgba ) -> Preview {
284
+ Preview :: Thumbnail {
285
+ blurhash : Some ( (
286
+ self . handle ,
287
+ // Fade out starting at this value for opacity
288
+ self . fade_in . interpolate ( 0.0 , 1.0 , Instant :: now ( ) ) ,
289
+ ) ) ,
290
+ thumbnail : image:: Handle :: from_rgba (
291
+ rgba. width ,
292
+ rgba. height ,
293
+ rgba. pixels ,
294
+ ) ,
295
+ fade_in : Animation :: new ( false ) . very_slow ( ) . go ( true ) ,
296
+ zoom : Animation :: new ( false )
297
+ . quick ( )
298
+ . easing ( animation:: Easing :: EaseInOut ) ,
299
+ }
300
+ }
301
+ }
302
+
303
+ enum Preview {
304
+ Blurhash ( Blurhash ) ,
305
+ Thumbnail {
306
+ blurhash : Option < ( image:: Handle , f32 ) > ,
307
+ thumbnail : image:: Handle ,
308
+ fade_in : Animation < bool > ,
309
+ zoom : Animation < bool > ,
310
+ } ,
311
+ }
312
+
313
+ impl Preview {
229
314
const WIDTH : u16 = 320 ;
230
315
const HEIGHT : u16 = 410 ;
231
316
232
- fn new ( rgba : Rgba ) -> Self {
233
- Self {
317
+ fn blurhash ( rgba : Rgba ) -> Self {
318
+ Self :: Blurhash ( Blurhash {
319
+ fade_in : Animation :: new ( false ) . slow ( ) . go ( true ) ,
234
320
handle : image:: Handle :: from_rgba (
235
321
rgba. width ,
236
322
rgba. height ,
237
323
rgba. pixels ,
238
324
) ,
325
+ } )
326
+ }
327
+
328
+ fn thumbnail ( rgba : Rgba ) -> Self {
329
+ Self :: Thumbnail {
330
+ blurhash : None ,
331
+ thumbnail : image:: Handle :: from_rgba (
332
+ rgba. width ,
333
+ rgba. height ,
334
+ rgba. pixels ,
335
+ ) ,
239
336
fade_in : Animation :: new ( false ) . slow ( ) . go ( true ) ,
240
337
zoom : Animation :: new ( false )
241
338
. quick ( )
@@ -244,7 +341,14 @@ impl Thumbnail {
244
341
}
245
342
246
343
fn is_animating ( & self , now : Instant ) -> bool {
247
- self . fade_in . is_animating ( now) || self . zoom . is_animating ( now)
344
+ match self {
345
+ Preview :: Blurhash ( Blurhash { fade_in, .. } ) => {
346
+ fade_in. is_animating ( now)
347
+ }
348
+ Preview :: Thumbnail { fade_in, zoom, .. } => {
349
+ fade_in. is_animating ( now) || zoom. is_animating ( now)
350
+ }
351
+ }
248
352
}
249
353
}
250
354
0 commit comments