1use crate::{
2 AnyElement, AnyEntity, App, AppContext, Asset, AssetLogger, Bounds, Element, ElementId, Entity,
3 GlobalElementId, ImageAssetLoader, ImageCacheError, IntoElement, LayoutId, ParentElement,
4 Pixels, RenderImage, Resource, Style, StyleRefinement, Styled, Window, hash,
5};
6
7use crate::asset_cache::CachedLoad;
8use refineable::Refineable;
9use smallvec::SmallVec;
10use std::{collections::HashMap, fmt, sync::Arc};
11
12pub fn image_cache(image_cache_provider: impl ImageCacheProvider) -> ImageCacheElement {
15 ImageCacheElement {
16 image_cache_provider: Box::new(image_cache_provider),
17 style: StyleRefinement::default(),
18 children: SmallVec::default(),
19 }
20}
21
22#[derive(Clone)]
24pub struct AnyImageCache {
25 image_cache: AnyEntity,
26 load_fn: fn(
27 image_cache: &AnyEntity,
28 resource: &Resource,
29 window: &mut Window,
30 cx: &mut App,
31 ) -> Option<Result<Arc<RenderImage>, ImageCacheError>>,
32}
33
34impl<I: ImageCache> From<Entity<I>> for AnyImageCache {
35 fn from(image_cache: Entity<I>) -> Self {
36 Self {
37 image_cache: image_cache.into_any(),
38 load_fn: any_image_cache::load::<I>,
39 }
40 }
41}
42
43impl AnyImageCache {
44 pub fn load(
47 &self,
48 resource: &Resource,
49 window: &mut Window,
50 cx: &mut App,
51 ) -> Option<Result<Arc<RenderImage>, ImageCacheError>> {
52 (self.load_fn)(&self.image_cache, resource, window, cx)
53 }
54}
55
56mod any_image_cache {
57 use super::*;
58
59 pub(crate) fn load<I: 'static + ImageCache>(
60 image_cache: &AnyEntity,
61 resource: &Resource,
62 window: &mut Window,
63 cx: &mut App,
64 ) -> Option<Result<Arc<RenderImage>, ImageCacheError>> {
65 let image_cache = image_cache.clone().downcast::<I>().unwrap();
66 image_cache.update(cx, |image_cache, cx| image_cache.load(resource, window, cx))
67 }
68}
69
70pub struct ImageCacheElement {
72 image_cache_provider: Box<dyn ImageCacheProvider>,
73 style: StyleRefinement,
74 children: SmallVec<[AnyElement; 2]>,
75}
76
77impl ParentElement for ImageCacheElement {
78 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
79 self.children.extend(elements)
80 }
81}
82
83impl Styled for ImageCacheElement {
84 fn style(&mut self) -> &mut StyleRefinement {
85 &mut self.style
86 }
87}
88
89impl IntoElement for ImageCacheElement {
90 type Element = Self;
91
92 fn into_element(self) -> Self::Element {
93 self
94 }
95}
96
97impl Element for ImageCacheElement {
98 type RequestLayoutState = SmallVec<[LayoutId; 4]>;
99 type PrepaintState = ();
100
101 fn id(&self) -> Option<ElementId> {
102 None
103 }
104
105 fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
106 None
107 }
108
109 fn request_layout(
110 &mut self,
111 _id: Option<&GlobalElementId>,
112 window: &mut Window,
113 cx: &mut App,
114 ) -> (LayoutId, Self::RequestLayoutState) {
115 let image_cache = self.image_cache_provider.provide(window, cx);
116 window.with_image_cache(Some(image_cache), |window| {
117 let child_layout_ids = self
118 .children
119 .iter_mut()
120 .map(|child| child.request_layout(window, cx))
121 .collect::<SmallVec<_>>();
122 let mut style = Style::default();
123 style.refine(&self.style);
124 let layout_id = window.request_layout(style, child_layout_ids.iter().copied(), cx);
125 (layout_id, child_layout_ids)
126 })
127 }
128
129 fn prepaint(
130 &mut self,
131 _id: Option<&GlobalElementId>,
132 _bounds: Bounds<Pixels>,
133 _request_layout: &mut Self::RequestLayoutState,
134 window: &mut Window,
135 cx: &mut App,
136 ) -> Self::PrepaintState {
137 for child in &mut self.children {
138 child.prepaint(window, cx);
139 }
140 }
141
142 fn paint(
143 &mut self,
144 _id: Option<&GlobalElementId>,
145 _bounds: Bounds<Pixels>,
146 _request_layout: &mut Self::RequestLayoutState,
147 _prepaint: &mut Self::PrepaintState,
148 window: &mut Window,
149 cx: &mut App,
150 ) {
151 let image_cache = self.image_cache_provider.provide(window, cx);
152 window.with_image_cache(Some(image_cache), |window| {
153 for child in &mut self.children {
154 child.paint(window, cx);
155 }
156 })
157 }
158}
159
160pub struct ImageCacheItem(CachedLoad<Result<Arc<RenderImage>, ImageCacheError>>);
165
166impl std::fmt::Debug for ImageCacheItem {
167 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168 f.debug_struct("ImageCacheItem")
169 .field("result", &self.get())
170 .finish()
171 }
172}
173
174impl ImageCacheItem {
175 pub fn new(source: &Resource, cx: &mut App) -> Self {
177 let future = AssetLogger::<ImageAssetLoader>::load(source.clone(), cx);
178 Self(CachedLoad::new(future, cx))
179 }
180
181 pub fn get(&self) -> Option<Result<Arc<RenderImage>, ImageCacheError>> {
183 self.0.get()
184 }
185
186 pub fn use_image(&self, window: &Window) -> Option<Result<Arc<RenderImage>, ImageCacheError>> {
188 self.0.use_by(window.current_view())
189 }
190}
191
192pub trait ImageCache: 'static {
195 fn load(
198 &mut self,
199 resource: &Resource,
200 window: &mut Window,
201 cx: &mut App,
202 ) -> Option<Result<Arc<RenderImage>, ImageCacheError>>;
203}
204
205pub trait ImageCacheProvider: 'static {
208 fn provide(&mut self, _window: &mut Window, _cx: &mut App) -> AnyImageCache;
210}
211
212impl<T: ImageCache> ImageCacheProvider for Entity<T> {
213 fn provide(&mut self, _window: &mut Window, _cx: &mut App) -> AnyImageCache {
214 self.clone().into()
215 }
216}
217
218pub struct RetainAllImageCache(HashMap<u64, ImageCacheItem>);
220
221impl fmt::Debug for RetainAllImageCache {
222 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223 f.debug_struct("HashMapImageCache")
224 .field("num_images", &self.0.len())
225 .finish()
226 }
227}
228
229impl RetainAllImageCache {
230 #[inline]
232 pub fn new(cx: &mut App) -> Entity<Self> {
233 let e = cx.new(|_cx| RetainAllImageCache(HashMap::new()));
234 cx.observe_release(&e, |image_cache, cx| {
235 for (_, item) in std::mem::replace(&mut image_cache.0, HashMap::new()) {
236 if let Some(Ok(image)) = item.get() {
237 cx.drop_image(image, None);
238 }
239 }
240 })
241 .detach();
242 e
243 }
244
245 pub fn load(
249 &mut self,
250 source: &Resource,
251 window: &mut Window,
252 cx: &mut App,
253 ) -> Option<Result<Arc<RenderImage>, ImageCacheError>> {
254 let hash = hash(source);
255
256 self.0
257 .entry(hash)
258 .or_insert_with(|| ImageCacheItem::new(source, cx))
259 .use_image(window)
260 }
261
262 pub fn clear(&mut self, window: &mut Window, cx: &mut App) {
264 for (_, item) in std::mem::replace(&mut self.0, HashMap::new()) {
265 if let Some(Ok(image)) = item.get() {
266 cx.drop_image(image, Some(window));
267 }
268 }
269 }
270
271 pub fn remove(&mut self, source: &Resource, window: &mut Window, cx: &mut App) {
273 let hash = hash(source);
274 if let Some(item) = self.0.remove(&hash)
275 && let Some(Ok(image)) = item.get()
276 {
277 cx.drop_image(image, Some(window));
278 }
279 }
280
281 pub fn len(&self) -> usize {
283 self.0.len()
284 }
285
286 pub fn is_empty(&self) -> bool {
288 self.0.is_empty()
289 }
290}
291
292impl ImageCache for RetainAllImageCache {
293 fn load(
294 &mut self,
295 resource: &Resource,
296 window: &mut Window,
297 cx: &mut App,
298 ) -> Option<Result<Arc<RenderImage>, ImageCacheError>> {
299 RetainAllImageCache::load(self, resource, window, cx)
300 }
301}
302
303pub fn retain_all(id: impl Into<ElementId>) -> RetainAllImageCacheProvider {
305 RetainAllImageCacheProvider { id: id.into() }
306}
307
308pub struct RetainAllImageCacheProvider {
310 id: ElementId,
311}
312
313impl ImageCacheProvider for RetainAllImageCacheProvider {
314 fn provide(&mut self, window: &mut Window, cx: &mut App) -> AnyImageCache {
315 window
316 .with_global_id(self.id.clone(), |global_id, window| {
317 window.with_element_state::<Entity<RetainAllImageCache>, _>(
318 global_id,
319 |cache, _window| {
320 let mut cache = cache.unwrap_or_else(|| RetainAllImageCache::new(cx));
321 (cache.clone(), cache)
322 },
323 )
324 })
325 .into()
326 }
327}