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