1use std::collections::HashMap;
6use std::sync::Arc;
7
8use embedder_traits::UntrustedNodeAddress;
9use euclid::Size2D;
10use fonts::FontContext;
11use layout_api::{
12 AnimatingImages, IFrameSizes, LayoutImageDestination, LayoutNode, PendingImage,
13 PendingImageState, PendingRasterizationImage,
14};
15use net_traits::image_cache::{
16 Image as CachedImage, ImageCache, ImageCacheResult, ImageOrMetadataAvailable, PendingImageId,
17};
18use parking_lot::{Mutex, RwLock};
19use pixels::RasterImage;
20use script::layout_dom::ServoLayoutNode;
21use servo_base::id::PainterId;
22use servo_url::{ImmutableOrigin, ServoUrl};
23use style::context::SharedStyleContext;
24use style::dom::OpaqueNode;
25use style::values::computed::image::{Gradient, Image};
26use webrender_api::units::{DeviceIntSize, DeviceSize};
27
28pub(crate) type CachedImageOrError = Result<CachedImage, ResolveImageError>;
29
30pub(crate) struct LayoutContext<'a> {
31 pub use_rayon: bool,
32
33 pub style_context: SharedStyleContext<'a>,
35
36 pub font_context: Arc<FontContext>,
38
39 pub iframe_sizes: Mutex<IFrameSizes>,
41
42 pub image_resolver: Arc<ImageResolver>,
45
46 pub painter_id: PainterId,
48}
49
50pub enum ResolvedImage<'a> {
51 Gradient(&'a Gradient),
52 Image {
55 image: CachedImage,
56 size: DeviceSize,
57 },
58}
59
60#[derive(Clone, Copy, Debug)]
61pub enum ResolveImageError {
62 LoadError,
63 ImagePending,
64 OnlyMetadata,
65 InvalidUrl,
66 MissingNode,
67 ImageMissingFromImageSet,
68 NotImplementedYet,
69 None,
70}
71
72pub(crate) enum LayoutImageCacheResult {
73 Pending,
74 DataAvailable(ImageOrMetadataAvailable),
75 LoadError,
76}
77
78pub(crate) struct ImageResolver {
79 pub origin: ImmutableOrigin,
81
82 pub image_cache: Arc<dyn ImageCache>,
84
85 pub pending_images: Mutex<Vec<PendingImage>>,
87
88 pub pending_rasterization_images: Mutex<Vec<PendingRasterizationImage>>,
91
92 pub pending_svg_elements_for_serialization: Mutex<Vec<UntrustedNodeAddress>>,
98
99 pub animating_images: Arc<RwLock<AnimatingImages>>,
103
104 pub resolved_images_cache: Arc<RwLock<HashMap<ServoUrl, CachedImageOrError>>>,
107
108 pub animation_timeline_value: f64,
110}
111
112impl Drop for ImageResolver {
113 fn drop(&mut self) {
114 if !std::thread::panicking() {
115 assert!(self.pending_images.lock().is_empty());
116 assert!(self.pending_rasterization_images.lock().is_empty());
117 assert!(
118 self.pending_svg_elements_for_serialization
119 .lock()
120 .is_empty()
121 );
122 }
123 }
124}
125
126impl ImageResolver {
127 pub(crate) fn get_or_request_image_or_meta(
128 &self,
129 node: OpaqueNode,
130 url: ServoUrl,
131 destination: LayoutImageDestination,
132 ) -> LayoutImageCacheResult {
133 let cache_result =
135 self.image_cache
136 .get_cached_image_status(url.clone(), self.origin.clone(), None);
137
138 match cache_result {
139 ImageCacheResult::Available(img_or_meta) => {
140 LayoutImageCacheResult::DataAvailable(img_or_meta)
141 },
142 ImageCacheResult::Pending(id) => {
145 let image = PendingImage {
146 state: PendingImageState::PendingResponse,
147 node: node.into(),
148 id,
149 origin: self.origin.clone(),
150 destination,
151 };
152 self.pending_images.lock().push(image);
153 LayoutImageCacheResult::Pending
154 },
155 ImageCacheResult::ReadyForRequest(id) => {
157 let image = PendingImage {
158 state: PendingImageState::Unrequested(url),
159 node: node.into(),
160 id,
161 origin: self.origin.clone(),
162 destination,
163 };
164 self.pending_images.lock().push(image);
165 LayoutImageCacheResult::Pending
166 },
167 ImageCacheResult::FailedToLoadOrDecode => LayoutImageCacheResult::LoadError,
169 }
170 }
171
172 pub(crate) fn handle_animated_image(&self, node: OpaqueNode, image: Arc<RasterImage>) {
173 let mut animating_images = self.animating_images.write();
174 if !image.should_animate() {
175 animating_images.remove(node);
176 } else {
177 animating_images.maybe_insert_or_update(node, image, self.animation_timeline_value);
178 }
179 }
180
181 pub(crate) fn get_cached_image_for_url(
182 &self,
183 node: OpaqueNode,
184 url: ServoUrl,
185 destination: LayoutImageDestination,
186 ) -> Result<CachedImage, ResolveImageError> {
187 if let Some(cached_image) = self.resolved_images_cache.read().get(&url) {
188 return cached_image.clone();
189 }
190
191 let result = self.get_or_request_image_or_meta(node, url.clone(), destination);
192 match result {
193 LayoutImageCacheResult::DataAvailable(img_or_meta) => match img_or_meta {
194 ImageOrMetadataAvailable::ImageAvailable { image, .. } => {
195 if let Some(image) = image.as_raster_image() {
196 self.handle_animated_image(node, image);
197 }
198
199 let mut resolved_images_cache = self.resolved_images_cache.write();
200 resolved_images_cache.insert(url, Ok(image.clone()));
201 Ok(image)
202 },
203 ImageOrMetadataAvailable::MetadataAvailable(..) => {
204 Result::Err(ResolveImageError::OnlyMetadata)
205 },
206 },
207 LayoutImageCacheResult::Pending => Result::Err(ResolveImageError::ImagePending),
208 LayoutImageCacheResult::LoadError => {
209 let error = Err(ResolveImageError::LoadError);
210 self.resolved_images_cache
211 .write()
212 .insert(url, error.clone());
213 error
214 },
215 }
216 }
217
218 pub(crate) fn rasterize_vector_image(
219 &self,
220 image_id: PendingImageId,
221 size: DeviceIntSize,
222 node: OpaqueNode,
223 svg_id: Option<String>,
224 ) -> Option<RasterImage> {
225 let result = self
226 .image_cache
227 .rasterize_vector_image(image_id, size, svg_id);
228 if result.is_none() {
229 self.pending_rasterization_images
230 .lock()
231 .push(PendingRasterizationImage {
232 id: image_id,
233 node: node.into(),
234 size,
235 });
236 }
237 result
238 }
239
240 pub(crate) fn queue_svg_element_for_serialization(&self, element: ServoLayoutNode<'_>) {
241 self.pending_svg_elements_for_serialization
242 .lock()
243 .push(element.opaque().into())
244 }
245
246 pub(crate) fn resolve_image<'a>(
247 &self,
248 node: Option<OpaqueNode>,
249 image: &'a Image,
250 ) -> Result<ResolvedImage<'a>, ResolveImageError> {
251 match image {
252 Image::None => Result::Err(ResolveImageError::None),
254 Image::CrossFade(_) => Result::Err(ResolveImageError::NotImplementedYet),
255 Image::PaintWorklet(_) => Result::Err(ResolveImageError::NotImplementedYet),
256 Image::Gradient(gradient) => Ok(ResolvedImage::Gradient(gradient)),
257 Image::Url(image_url) => {
258 let image_url = image_url.url().ok_or(ResolveImageError::InvalidUrl)?;
265 let node = node.ok_or(ResolveImageError::MissingNode)?;
266 let image = self.get_cached_image_for_url(
267 node,
268 image_url.clone().into(),
269 LayoutImageDestination::DisplayListBuilding,
270 )?;
271 let metadata = image.metadata();
272 let size = Size2D::new(metadata.width, metadata.height).to_f32();
273 Ok(ResolvedImage::Image { image, size })
274 },
275 Image::ImageSet(image_set) => {
276 image_set
277 .items
278 .get(image_set.selected_index)
279 .ok_or(ResolveImageError::ImageMissingFromImageSet)
280 .and_then(|image| {
281 self.resolve_image(node, &image.image)
282 .map(|info| match info {
283 ResolvedImage::Image {
284 image: cached_image,
285 ..
286 } => {
287 let image_metadata = cached_image.metadata();
294 let size = if cached_image.as_raster_image().is_some() {
295 let scale_factor = image.resolution.dppx();
296 Size2D::new(
297 image_metadata.width as f32 / scale_factor,
298 image_metadata.height as f32 / scale_factor,
299 )
300 } else {
301 Size2D::new(image_metadata.width, image_metadata.height)
302 .to_f32()
303 };
304
305 ResolvedImage::Image {
306 image: cached_image,
307 size,
308 }
309 },
310 _ => info,
311 })
312 })
313 },
314 Image::LightDark(..) => unreachable!("light-dark() should be disabled"),
315 }
316 }
317}