1use std::sync::Arc;
6
7use embedder_traits::UntrustedNodeAddress;
8use euclid::Size2D;
9use fnv::FnvHashMap;
10use fonts::FontContext;
11use layout_api::wrapper_traits::ThreadSafeLayoutNode;
12use layout_api::{
13 IFrameSizes, ImageAnimationState, PendingImage, PendingImageState, PendingRasterizationImage,
14};
15use net_traits::image_cache::{
16 Image as CachedImage, ImageCache, ImageCacheResult, ImageOrMetadataAvailable, PendingImageId,
17 UsePlaceholder,
18};
19use parking_lot::{Mutex, RwLock};
20use pixels::RasterImage;
21use rustc_hash::FxHashMap;
22use script::layout_dom::ServoThreadSafeLayoutNode;
23use servo_url::{ImmutableOrigin, ServoUrl};
24use style::context::SharedStyleContext;
25use style::dom::OpaqueNode;
26use style::values::computed::image::{Gradient, Image};
27use webrender_api::units::{DeviceIntSize, DeviceSize};
28
29pub(crate) type CachedImageOrError = Result<CachedImage, ResolveImageError>;
30
31pub(crate) struct LayoutContext<'a> {
32 pub use_rayon: bool,
33
34 pub style_context: SharedStyleContext<'a>,
36
37 pub font_context: Arc<FontContext>,
39
40 pub iframe_sizes: Mutex<IFrameSizes>,
42
43 pub image_resolver: Arc<ImageResolver>,
46}
47
48pub enum ResolvedImage<'a> {
49 Gradient(&'a Gradient),
50 Image {
53 image: CachedImage,
54 size: DeviceSize,
55 },
56}
57
58#[derive(Clone, Copy, Debug)]
59pub enum ResolveImageError {
60 LoadError,
61 ImagePending,
62 OnlyMetadata,
63 InvalidUrl,
64 MissingNode,
65 ImageMissingFromImageSet,
66 NotImplementedYet,
67 None,
68}
69
70pub(crate) enum LayoutImageCacheResult {
71 Pending,
72 DataAvailable(ImageOrMetadataAvailable),
73 LoadError,
74}
75
76pub(crate) struct ImageResolver {
77 pub origin: ImmutableOrigin,
79
80 pub image_cache: Arc<dyn ImageCache>,
82
83 pub pending_images: Mutex<Vec<PendingImage>>,
85
86 pub pending_rasterization_images: Mutex<Vec<PendingRasterizationImage>>,
89
90 pub pending_svg_elements_for_serialization: Mutex<Vec<UntrustedNodeAddress>>,
96
97 pub node_to_animating_image_map: Arc<RwLock<FxHashMap<OpaqueNode, ImageAnimationState>>>,
101
102 pub resolved_images_cache:
105 Arc<RwLock<FnvHashMap<(ServoUrl, UsePlaceholder), CachedImageOrError>>>,
106
107 pub animation_timeline_value: f64,
109}
110
111impl Drop for ImageResolver {
112 fn drop(&mut self) {
113 if !std::thread::panicking() {
114 assert!(self.pending_images.lock().is_empty());
115 assert!(self.pending_rasterization_images.lock().is_empty());
116 assert!(
117 self.pending_svg_elements_for_serialization
118 .lock()
119 .is_empty()
120 );
121 }
122 }
123}
124
125impl ImageResolver {
126 pub(crate) fn get_or_request_image_or_meta(
127 &self,
128 node: OpaqueNode,
129 url: ServoUrl,
130 use_placeholder: UsePlaceholder,
131 ) -> LayoutImageCacheResult {
132 let cache_result = self.image_cache.get_cached_image_status(
134 url.clone(),
135 self.origin.clone(),
136 None,
137 use_placeholder,
138 );
139
140 match cache_result {
141 ImageCacheResult::Available(img_or_meta) => {
142 LayoutImageCacheResult::DataAvailable(img_or_meta)
143 },
144 ImageCacheResult::Pending(id) => {
147 let image = PendingImage {
148 state: PendingImageState::PendingResponse,
149 node: node.into(),
150 id,
151 origin: self.origin.clone(),
152 };
153 self.pending_images.lock().push(image);
154 LayoutImageCacheResult::Pending
155 },
156 ImageCacheResult::ReadyForRequest(id) => {
158 let image = PendingImage {
159 state: PendingImageState::Unrequested(url),
160 node: node.into(),
161 id,
162 origin: self.origin.clone(),
163 };
164 self.pending_images.lock().push(image);
165 LayoutImageCacheResult::Pending
166 },
167 ImageCacheResult::LoadError => LayoutImageCacheResult::LoadError,
169 }
170 }
171
172 pub(crate) fn handle_animated_image(&self, node: OpaqueNode, image: Arc<RasterImage>) {
173 let mut map = self.node_to_animating_image_map.write();
174 if !image.should_animate() {
175 map.remove(&node);
176 return;
177 }
178 let new_image_animation_state =
179 || ImageAnimationState::new(image.clone(), self.animation_timeline_value);
180
181 let entry = map.entry(node).or_insert_with(new_image_animation_state);
182
183 if entry.image.id != image.id {
186 *entry = new_image_animation_state();
187 }
188 }
189
190 pub(crate) fn get_cached_image_for_url(
191 &self,
192 node: OpaqueNode,
193 url: ServoUrl,
194 use_placeholder: UsePlaceholder,
195 ) -> Result<CachedImage, ResolveImageError> {
196 if let Some(cached_image) = self
197 .resolved_images_cache
198 .read()
199 .get(&(url.clone(), use_placeholder))
200 {
201 return cached_image.clone();
202 }
203
204 let result = self.get_or_request_image_or_meta(node, url.clone(), use_placeholder);
205 match result {
206 LayoutImageCacheResult::DataAvailable(img_or_meta) => match img_or_meta {
207 ImageOrMetadataAvailable::ImageAvailable { image, .. } => {
208 if let Some(image) = image.as_raster_image() {
209 self.handle_animated_image(node, image.clone());
210 }
211
212 let mut resolved_images_cache = self.resolved_images_cache.write();
213 resolved_images_cache.insert((url, use_placeholder), Ok(image.clone()));
214 Ok(image)
215 },
216 ImageOrMetadataAvailable::MetadataAvailable(..) => {
217 Result::Err(ResolveImageError::OnlyMetadata)
218 },
219 },
220 LayoutImageCacheResult::Pending => Result::Err(ResolveImageError::ImagePending),
221 LayoutImageCacheResult::LoadError => {
222 let error = Err(ResolveImageError::LoadError);
223 self.resolved_images_cache
224 .write()
225 .insert((url, use_placeholder), error.clone());
226 error
227 },
228 }
229 }
230
231 pub(crate) fn rasterize_vector_image(
232 &self,
233 image_id: PendingImageId,
234 size: DeviceIntSize,
235 node: OpaqueNode,
236 ) -> Option<RasterImage> {
237 let result = self.image_cache.rasterize_vector_image(image_id, size);
238 if result.is_none() {
239 self.pending_rasterization_images
240 .lock()
241 .push(PendingRasterizationImage {
242 id: image_id,
243 node: node.into(),
244 size,
245 });
246 }
247 result
248 }
249
250 pub(crate) fn queue_svg_element_for_serialization(
251 &self,
252 element: ServoThreadSafeLayoutNode<'_>,
253 ) {
254 self.pending_svg_elements_for_serialization
255 .lock()
256 .push(element.opaque().into())
257 }
258
259 pub(crate) fn resolve_image<'a>(
260 &self,
261 node: Option<OpaqueNode>,
262 image: &'a Image,
263 ) -> Result<ResolvedImage<'a>, ResolveImageError> {
264 match image {
265 Image::None => Result::Err(ResolveImageError::None),
267 Image::CrossFade(_) => Result::Err(ResolveImageError::NotImplementedYet),
268 Image::PaintWorklet(_) => Result::Err(ResolveImageError::NotImplementedYet),
269 Image::Gradient(gradient) => Ok(ResolvedImage::Gradient(gradient)),
270 Image::Url(image_url) => {
271 let image_url = image_url.url().ok_or(ResolveImageError::InvalidUrl)?;
278 let node = node.ok_or(ResolveImageError::MissingNode)?;
279 let image = self.get_cached_image_for_url(
280 node,
281 image_url.clone().into(),
282 UsePlaceholder::No,
283 )?;
284 let metadata = image.metadata();
285 let size = Size2D::new(metadata.width, metadata.height).to_f32();
286 Ok(ResolvedImage::Image { image, size })
287 },
288 Image::ImageSet(image_set) => {
289 image_set
290 .items
291 .get(image_set.selected_index)
292 .ok_or(ResolveImageError::ImageMissingFromImageSet)
293 .and_then(|image| {
294 self.resolve_image(node, &image.image)
295 .map(|info| match info {
296 ResolvedImage::Image {
297 image: cached_image,
298 ..
299 } => {
300 let image_metadata = cached_image.metadata();
307 let size = if cached_image.as_raster_image().is_some() {
308 let scale_factor = image.resolution.dppx();
309 Size2D::new(
310 image_metadata.width as f32 / scale_factor,
311 image_metadata.height as f32 / scale_factor,
312 )
313 } else {
314 Size2D::new(image_metadata.width, image_metadata.height)
315 .to_f32()
316 };
317
318 ResolvedImage::Image {
319 image: cached_image,
320 size,
321 }
322 },
323 _ => info,
324 })
325 })
326 },
327 Image::LightDark(..) => unreachable!("light-dark() should be disabled"),
328 }
329 }
330}