1use api::{
6 AlphaType, ColorDepth, ColorF, ColorU, ExternalImageType,
7 ImageKey as ApiImageKey, ImageBufferKind, ImageRendering, PremultipliedColorF,
8 RasterSpace, Shadow, YuvColorSpace, ColorRange, YuvFormat,
9};
10use api::units::*;
11use euclid::point2;
12use crate::composite::CompositorSurfaceKind;
13use crate::scene_building::{CreateShadow, IsVisible};
14use crate::frame_builder::{FrameBuildingContext, FrameBuildingState};
15use crate::gpu_cache::{GpuCache, GpuDataRequest};
16use crate::intern::{Internable, InternDebug, Handle as InternHandle};
17use crate::internal_types::LayoutPrimitiveInfo;
18use crate::prim_store::{
19 EdgeAaSegmentMask, PrimitiveInstanceKind,
20 PrimitiveOpacity, PrimKey,
21 PrimTemplate, PrimTemplateCommonData, PrimitiveStore, SegmentInstanceIndex,
22 SizeKey, InternablePrimitive,
23};
24use crate::render_target::RenderTargetKind;
25use crate::render_task_graph::RenderTaskId;
26use crate::render_task::RenderTask;
27use crate::render_task_cache::{
28 RenderTaskCacheKey, RenderTaskCacheKeyKind, RenderTaskParent
29};
30use crate::resource_cache::{ImageRequest, ImageProperties, ResourceCache};
31use crate::util::pack_as_float;
32use crate::visibility::{PrimitiveVisibility, compute_conservative_visible_rect};
33use crate::spatial_tree::SpatialNodeIndex;
34use crate::image_tiling;
35
36#[derive(Debug)]
37#[cfg_attr(feature = "capture", derive(Serialize))]
38#[cfg_attr(feature = "replay", derive(Deserialize))]
39pub struct VisibleImageTile {
40 pub src_color: RenderTaskId,
41 pub edge_flags: EdgeAaSegmentMask,
42 pub local_rect: LayoutRect,
43 pub local_clip_rect: LayoutRect,
44}
45
46#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
49#[cfg_attr(feature = "capture", derive(Serialize))]
50#[cfg_attr(feature = "replay", derive(Deserialize))]
51pub struct ImageCacheKey {
52 pub request: ImageRequest,
53 pub texel_rect: Option<DeviceIntRect>,
54}
55
56#[derive(Debug)]
69#[cfg_attr(feature = "capture", derive(Serialize))]
70pub struct ImageInstance {
71 pub segment_instance_index: SegmentInstanceIndex,
72 pub tight_local_clip_rect: LayoutRect,
73 pub visible_tiles: Vec<VisibleImageTile>,
74 pub src_color: Option<RenderTaskId>,
75 pub normalized_uvs: bool,
76 pub adjustment: AdjustedImageSource,
77}
78
79#[cfg_attr(feature = "capture", derive(Serialize))]
80#[cfg_attr(feature = "replay", derive(Deserialize))]
81#[derive(Debug, Clone, Eq, PartialEq, MallocSizeOf, Hash)]
82pub struct Image {
83 pub key: ApiImageKey,
84 pub stretch_size: SizeKey,
85 pub tile_spacing: SizeKey,
86 pub color: ColorU,
87 pub image_rendering: ImageRendering,
88 pub alpha_type: AlphaType,
89}
90
91pub type ImageKey = PrimKey<Image>;
92
93impl ImageKey {
94 pub fn new(
95 info: &LayoutPrimitiveInfo,
96 image: Image,
97 ) -> Self {
98 ImageKey {
99 common: info.into(),
100 kind: image,
101 }
102 }
103}
104
105impl InternDebug for ImageKey {}
106
107#[cfg_attr(feature = "capture", derive(Serialize))]
108#[cfg_attr(feature = "replay", derive(Deserialize))]
109#[derive(Debug, MallocSizeOf)]
110pub struct ImageData {
111 pub key: ApiImageKey,
112 pub stretch_size: LayoutSize,
113 pub tile_spacing: LayoutSize,
114 pub color: ColorF,
115 pub image_rendering: ImageRendering,
116 pub alpha_type: AlphaType,
117}
118
119impl From<Image> for ImageData {
120 fn from(image: Image) -> Self {
121 ImageData {
122 key: image.key,
123 color: image.color.into(),
124 stretch_size: image.stretch_size.into(),
125 tile_spacing: image.tile_spacing.into(),
126 image_rendering: image.image_rendering,
127 alpha_type: image.alpha_type,
128 }
129 }
130}
131
132impl ImageData {
133 pub fn update(
138 &mut self,
139 common: &mut PrimTemplateCommonData,
140 image_instance: &mut ImageInstance,
141 prim_spatial_node_index: SpatialNodeIndex,
142 frame_state: &mut FrameBuildingState,
143 frame_context: &FrameBuildingContext,
144 visibility: &mut PrimitiveVisibility,
145 ) {
146
147 let image_properties = frame_state
148 .resource_cache
149 .get_image_properties(self.key);
150
151 common.opacity = match &image_properties {
152 Some(properties) => {
153 if properties.descriptor.is_opaque() {
154 PrimitiveOpacity::from_alpha(self.color.a)
155 } else {
156 PrimitiveOpacity::translucent()
157 }
158 }
159 None => PrimitiveOpacity::opaque(),
160 };
161
162 if self.stretch_size.width >= common.prim_rect.width() &&
163 self.stretch_size.height >= common.prim_rect.height() {
164
165 common.may_need_repetition = false;
166 }
167
168 let request = ImageRequest {
169 key: self.key,
170 rendering: self.image_rendering,
171 tile: None,
172 };
173
174 let tight_clip_rect = visibility
181 .clip_chain
182 .local_clip_rect
183 .intersection(&common.prim_rect).unwrap();
184 image_instance.tight_local_clip_rect = tight_clip_rect;
185
186 image_instance.adjustment = AdjustedImageSource::new();
187
188 match image_properties {
189 Some(ImageProperties { tiling: None, ref descriptor, ref external_image, adjustment, .. }) => {
191 image_instance.adjustment = adjustment;
192
193 let mut size = frame_state.resource_cache.request_image(
194 request,
195 frame_state.gpu_cache,
196 );
197
198 let mut task_id = frame_state.rg_builder.add().init(
199 RenderTask::new_image(size, request)
200 );
201
202 if let Some(external_image) = external_image {
203 let requires_copy = frame_context.fb_config.external_images_require_copy &&
206 external_image.image_type ==
207 ExternalImageType::TextureHandle(ImageBufferKind::TextureExternal);
208
209 if requires_copy {
210 let target_kind = if descriptor.format.bytes_per_pixel() == 1 {
211 RenderTargetKind::Alpha
212 } else {
213 RenderTargetKind::Color
214 };
215
216 task_id = RenderTask::new_scaling(
217 task_id,
218 frame_state.rg_builder,
219 target_kind,
220 size
221 );
222
223 frame_state.surface_builder.add_child_render_task(
224 task_id,
225 frame_state.rg_builder,
226 );
227 }
228
229 if !requires_copy {
233 image_instance.normalized_uvs = external_image.normalized_uvs;
234 }
235 }
236
237 if self.tile_spacing == LayoutSize::zero() {
242 image_instance.src_color = Some(task_id);
244 } else {
245 let padding = DeviceIntSideOffsets::new(
246 0,
247 (self.tile_spacing.width * size.width as f32 / self.stretch_size.width) as i32,
248 (self.tile_spacing.height * size.height as f32 / self.stretch_size.height) as i32,
249 0,
250 );
251
252 size.width += padding.horizontal();
253 size.height += padding.vertical();
254
255 if padding != DeviceIntSideOffsets::zero() {
256 common.opacity = PrimitiveOpacity::translucent();
257 }
258
259 let image_cache_key = ImageCacheKey {
260 request,
261 texel_rect: None,
262 };
263 let target_kind = if descriptor.format.bytes_per_pixel() == 1 {
264 RenderTargetKind::Alpha
265 } else {
266 RenderTargetKind::Color
267 };
268
269 let cached_task_handle = frame_state.resource_cache.request_render_task(
271 Some(RenderTaskCacheKey {
272 size,
273 kind: RenderTaskCacheKeyKind::Image(image_cache_key),
274 }),
275 descriptor.is_opaque(),
276 RenderTaskParent::Surface,
277 frame_state.gpu_cache,
278 &mut frame_state.frame_gpu_data.f32,
279 frame_state.rg_builder,
280 &mut frame_state.surface_builder,
281 &mut |rg_builder, _, _| {
282 let cache_to_target_task_id = RenderTask::new_scaling_with_padding(
286 task_id,
287 rg_builder,
288 target_kind,
289 size,
290 padding,
291 );
292
293 RenderTask::new_blit(
297 size,
298 cache_to_target_task_id,
299 size.into(),
300 rg_builder,
301 )
302 }
303 );
304
305 image_instance.src_color = Some(cached_task_handle);
306 }
307 }
308 Some(ImageProperties { tiling: Some(tile_size), visible_rect, .. }) => {
310 image_instance.src_color = None;
312
313 image_instance.visible_tiles.clear();
314 let active_rect = visible_rect;
318
319 let visible_rect = compute_conservative_visible_rect(
320 &visibility.clip_chain,
321 frame_state.current_dirty_region().combined,
322 frame_state.current_dirty_region().visibility_spatial_node,
323 prim_spatial_node_index,
324 frame_context.spatial_tree,
325 );
326
327 let base_edge_flags = edge_flags_for_tile_spacing(&self.tile_spacing);
328
329 let stride = self.stretch_size + self.tile_spacing;
330
331 common.may_need_repetition = false;
334
335 let repetitions = image_tiling::repetitions(
336 &common.prim_rect,
337 &visible_rect,
338 stride,
339 );
340
341 for image_tiling::Repetition { origin, edge_flags } in repetitions {
342 let edge_flags = base_edge_flags | edge_flags;
343
344 let layout_image_rect = LayoutRect::from_origin_and_size(
345 origin,
346 self.stretch_size,
347 );
348
349 let tiles = image_tiling::tiles(
350 &layout_image_rect,
351 &visible_rect,
352 &active_rect,
353 tile_size as i32,
354 );
355
356 for tile in tiles {
357 let request = request.with_tile(tile.offset);
358 let size = frame_state.resource_cache.request_image(
359 request,
360 frame_state.gpu_cache,
361 );
362
363 let task_id = frame_state.rg_builder.add().init(
364 RenderTask::new_image(size, request)
365 );
366
367 image_instance.visible_tiles.push(VisibleImageTile {
368 src_color: task_id,
369 edge_flags: tile.edge_flags & edge_flags,
370 local_rect: tile.rect,
371 local_clip_rect: tight_clip_rect,
372 });
373 }
374 }
375
376 if image_instance.visible_tiles.is_empty() {
377 visibility.reset();
379 }
380 }
381 None => {
382 image_instance.src_color = None;
383 }
384 }
385
386 if let Some(task_id) = frame_state.image_dependencies.get(&self.key) {
387 frame_state.surface_builder.add_child_render_task(
388 *task_id,
389 frame_state.rg_builder
390 );
391 }
392
393 if let Some(mut request) = frame_state.gpu_cache.request(&mut common.gpu_cache_handle) {
394 self.write_prim_gpu_blocks(&image_instance.adjustment, &mut request);
395 }
396 }
397
398 pub fn write_prim_gpu_blocks(&self, adjustment: &AdjustedImageSource, request: &mut GpuDataRequest) {
399 let stretch_size = adjustment.map_stretch_size(self.stretch_size);
400 request.push(self.color.premultiplied());
404 request.push(PremultipliedColorF::WHITE);
405 request.push([
406 stretch_size.width + self.tile_spacing.width,
407 stretch_size.height + self.tile_spacing.height,
408 0.0,
409 0.0,
410 ]);
411 }
412}
413
414fn edge_flags_for_tile_spacing(tile_spacing: &LayoutSize) -> EdgeAaSegmentMask {
415 let mut flags = EdgeAaSegmentMask::empty();
416
417 if tile_spacing.width > 0.0 {
418 flags |= EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::RIGHT;
419 }
420 if tile_spacing.height > 0.0 {
421 flags |= EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::BOTTOM;
422 }
423
424 flags
425}
426
427pub type ImageTemplate = PrimTemplate<ImageData>;
428
429impl From<ImageKey> for ImageTemplate {
430 fn from(image: ImageKey) -> Self {
431 let common = PrimTemplateCommonData::with_key_common(image.common);
432
433 ImageTemplate {
434 common,
435 kind: image.kind.into(),
436 }
437 }
438}
439
440pub type ImageDataHandle = InternHandle<Image>;
441
442impl Internable for Image {
443 type Key = ImageKey;
444 type StoreData = ImageTemplate;
445 type InternData = ();
446 const PROFILE_COUNTER: usize = crate::profiler::INTERNED_IMAGES;
447}
448
449impl InternablePrimitive for Image {
450 fn into_key(
451 self,
452 info: &LayoutPrimitiveInfo,
453 ) -> ImageKey {
454 ImageKey::new(info, self)
455 }
456
457 fn make_instance_kind(
458 _key: ImageKey,
459 data_handle: ImageDataHandle,
460 prim_store: &mut PrimitiveStore,
461 ) -> PrimitiveInstanceKind {
462 let image_instance_index = prim_store.images.push(ImageInstance {
465 segment_instance_index: SegmentInstanceIndex::INVALID,
466 tight_local_clip_rect: LayoutRect::zero(),
467 visible_tiles: Vec::new(),
468 src_color: None,
469 normalized_uvs: false,
470 adjustment: AdjustedImageSource::new(),
471 });
472
473 PrimitiveInstanceKind::Image {
474 data_handle,
475 image_instance_index,
476 compositor_surface_kind: CompositorSurfaceKind::Blit,
477 }
478 }
479}
480
481impl CreateShadow for Image {
482 fn create_shadow(
483 &self,
484 shadow: &Shadow,
485 _: bool,
486 _: RasterSpace,
487 ) -> Self {
488 Image {
489 tile_spacing: self.tile_spacing,
490 stretch_size: self.stretch_size,
491 key: self.key,
492 image_rendering: self.image_rendering,
493 alpha_type: self.alpha_type,
494 color: shadow.color.into(),
495 }
496 }
497}
498
499impl IsVisible for Image {
500 fn is_visible(&self) -> bool {
501 true
502 }
503}
504
505#[derive(Copy, Clone, Debug)]
523#[cfg_attr(feature = "capture", derive(Serialize))]
524#[cfg_attr(feature = "replay", derive(Deserialize))]
525pub struct AdjustedImageSource {
526 x0: f32,
527 y0: f32,
528 x1: f32,
529 y1: f32,
530}
531
532impl AdjustedImageSource {
533 pub fn new() -> Self {
535 AdjustedImageSource {
536 x0: 0.0,
537 y0: 0.0,
538 x1: 0.0,
539 y1: 0.0,
540 }
541 }
542
543 pub fn from_rects(reference: &LayoutRect, actual: &LayoutRect) -> Self {
546 let ref_size = reference.size();
547 let min_offset = reference.min.to_vector();
548 let max_offset = reference.max.to_vector();
549 AdjustedImageSource {
550 x0: (actual.min.x - min_offset.x) / ref_size.width,
551 y0: (actual.min.y - min_offset.y) / ref_size.height,
552 x1: (actual.max.x - max_offset.x) / ref_size.width,
553 y1: (actual.max.y - max_offset.y) / ref_size.height,
554 }
555 }
556
557 pub fn map_local_rect(&self, rect: &LayoutRect) -> LayoutRect {
559 let w = rect.width();
560 let h = rect.height();
561 LayoutRect {
562 min: point2(
563 rect.min.x + w * self.x0,
564 rect.min.y + h * self.y0,
565 ),
566 max: point2(
567 rect.max.x + w * self.x1,
568 rect.max.y + h * self.y1,
569 ),
570 }
571 }
572
573 pub fn map_stretch_size(&self, size: LayoutSize) -> LayoutSize {
587 LayoutSize::new(
588 size.width * (1.0 + self.x1 - self.x0),
589 size.height * (1.0 + self.y1 - self.y0),
590 )
591 }
592}
593
594#[cfg_attr(feature = "capture", derive(Serialize))]
597#[cfg_attr(feature = "replay", derive(Deserialize))]
598#[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
599pub struct YuvImage {
600 pub color_depth: ColorDepth,
601 pub yuv_key: [ApiImageKey; 3],
602 pub format: YuvFormat,
603 pub color_space: YuvColorSpace,
604 pub color_range: ColorRange,
605 pub image_rendering: ImageRendering,
606}
607
608pub type YuvImageKey = PrimKey<YuvImage>;
609
610impl YuvImageKey {
611 pub fn new(
612 info: &LayoutPrimitiveInfo,
613 yuv_image: YuvImage,
614 ) -> Self {
615 YuvImageKey {
616 common: info.into(),
617 kind: yuv_image,
618 }
619 }
620}
621
622impl InternDebug for YuvImageKey {}
623
624#[cfg_attr(feature = "capture", derive(Serialize))]
625#[cfg_attr(feature = "replay", derive(Deserialize))]
626#[derive(MallocSizeOf)]
627pub struct YuvImageData {
628 pub color_depth: ColorDepth,
629 pub yuv_key: [ApiImageKey; 3],
630 pub src_yuv: [Option<RenderTaskId>; 3],
631 pub format: YuvFormat,
632 pub color_space: YuvColorSpace,
633 pub color_range: ColorRange,
634 pub image_rendering: ImageRendering,
635}
636
637impl From<YuvImage> for YuvImageData {
638 fn from(image: YuvImage) -> Self {
639 YuvImageData {
640 color_depth: image.color_depth,
641 yuv_key: image.yuv_key,
642 src_yuv: [None, None, None],
643 format: image.format,
644 color_space: image.color_space,
645 color_range: image.color_range,
646 image_rendering: image.image_rendering,
647 }
648 }
649}
650
651impl YuvImageData {
652 pub fn update(
657 &mut self,
658 common: &mut PrimTemplateCommonData,
659 frame_state: &mut FrameBuildingState,
660 ) {
661
662 self.src_yuv = [ None, None, None ];
663
664 let channel_num = self.format.get_plane_num();
665 debug_assert!(channel_num <= 3);
666 for channel in 0 .. channel_num {
667 let request = ImageRequest {
668 key: self.yuv_key[channel],
669 rendering: self.image_rendering,
670 tile: None,
671 };
672
673 let size = frame_state.resource_cache.request_image(
674 request,
675 frame_state.gpu_cache,
676 );
677
678 let task_id = frame_state.rg_builder.add().init(
679 RenderTask::new_image(size, request)
680 );
681
682 self.src_yuv[channel] = Some(task_id);
683 }
684
685 if let Some(mut request) = frame_state.gpu_cache.request(&mut common.gpu_cache_handle) {
686 self.write_prim_gpu_blocks(&mut request);
687 };
688
689 common.opacity = PrimitiveOpacity::opaque();
691 }
692
693 pub fn request_resources(
694 &mut self,
695 resource_cache: &mut ResourceCache,
696 gpu_cache: &mut GpuCache,
697 ) {
698 let channel_num = self.format.get_plane_num();
699 debug_assert!(channel_num <= 3);
700 for channel in 0 .. channel_num {
701 resource_cache.request_image(
702 ImageRequest {
703 key: self.yuv_key[channel],
704 rendering: self.image_rendering,
705 tile: None,
706 },
707 gpu_cache,
708 );
709 }
710 }
711
712 pub fn write_prim_gpu_blocks(&self, request: &mut GpuDataRequest) {
713 let ranged_color_space = self.color_space.with_range(self.color_range);
714 request.push([
715 pack_as_float(self.color_depth.bit_depth()),
716 pack_as_float(ranged_color_space as u32),
717 pack_as_float(self.format as u32),
718 0.0
719 ]);
720 }
721}
722
723pub type YuvImageTemplate = PrimTemplate<YuvImageData>;
724
725impl From<YuvImageKey> for YuvImageTemplate {
726 fn from(image: YuvImageKey) -> Self {
727 let common = PrimTemplateCommonData::with_key_common(image.common);
728
729 YuvImageTemplate {
730 common,
731 kind: image.kind.into(),
732 }
733 }
734}
735
736pub type YuvImageDataHandle = InternHandle<YuvImage>;
737
738impl Internable for YuvImage {
739 type Key = YuvImageKey;
740 type StoreData = YuvImageTemplate;
741 type InternData = ();
742 const PROFILE_COUNTER: usize = crate::profiler::INTERNED_YUV_IMAGES;
743}
744
745impl InternablePrimitive for YuvImage {
746 fn into_key(
747 self,
748 info: &LayoutPrimitiveInfo,
749 ) -> YuvImageKey {
750 YuvImageKey::new(info, self)
751 }
752
753 fn make_instance_kind(
754 _key: YuvImageKey,
755 data_handle: YuvImageDataHandle,
756 _prim_store: &mut PrimitiveStore,
757 ) -> PrimitiveInstanceKind {
758 PrimitiveInstanceKind::YuvImage {
759 data_handle,
760 segment_instance_index: SegmentInstanceIndex::INVALID,
761 compositor_surface_kind: CompositorSurfaceKind::Blit,
762 }
763 }
764}
765
766impl IsVisible for YuvImage {
767 fn is_visible(&self) -> bool {
768 true
769 }
770}
771
772#[test]
773#[cfg(target_pointer_width = "64")]
774fn test_struct_sizes() {
775 use std::mem;
776 assert_eq!(mem::size_of::<Image>(), 32, "Image size changed");
783 assert_eq!(mem::size_of::<ImageTemplate>(), 72, "ImageTemplate size changed");
784 assert_eq!(mem::size_of::<ImageKey>(), 52, "ImageKey size changed");
785 assert_eq!(mem::size_of::<YuvImage>(), 32, "YuvImage size changed");
786 assert_eq!(mem::size_of::<YuvImageTemplate>(), 84, "YuvImageTemplate size changed");
787 assert_eq!(mem::size_of::<YuvImageKey>(), 52, "YuvImageKey size changed");
788}