1use api::{
6 AlphaType, ColorDepth, ColorF, ColorRange, ColorU, ExternalImageData, ExternalImageType, ImageBufferKind, ImageKey as ApiImageKey, ImageRendering, PremultipliedColorF, RasterSpace, Shadow, YuvColorSpace, YuvFormat
7};
8use api::units::*;
9use euclid::point2;
10use crate::clip::{ClipChainInstance, ClipIntern};
11use crate::command_buffer::CommandBufferIndex;
12use crate::gpu_types::{ImageBrushPrimitiveData, YuvPrimitive};
13use crate::pattern::image::ImagePattern;
14use crate::quad::QuadTransformState;
15use crate::renderer::{GpuBufferAddress, GpuBufferBuilderF, GpuBufferWriterF};
16use crate::scene_building::{CreateShadow, IsVisible};
17use crate::frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext};
18use crate::intern::{DataStore, Handle as InternHandle, InternDebug, Internable};
19use crate::internal_types::LayoutPrimitiveInfo;
20use crate::prim_store::{
21 EdgeMask, InternablePrimitive, PrimKey, PrimTemplate, PrimTemplateCommonData, PrimitiveInstanceIndex, PrimitiveKind, PrimitiveOpacity, PrimitiveScratchBuffer, PrimitiveStore, SizeKey
22};
23use crate::prim_store::storage;
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::visibility::compute_conservative_visible_rect;
32use crate::spatial_tree::SpatialNodeIndex;
33use crate::{image_tiling, quad};
34
35#[derive(Debug)]
36#[cfg_attr(feature = "capture", derive(Serialize))]
37#[cfg_attr(feature = "replay", derive(Deserialize))]
38pub struct VisibleImageTile {
39 pub src_color: RenderTaskId,
40 pub edge_flags: EdgeMask,
41 pub local_rect: LayoutRect,
42 pub local_clip_rect: LayoutRect,
43}
44
45#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
48#[cfg_attr(feature = "capture", derive(Serialize))]
49#[cfg_attr(feature = "replay", derive(Deserialize))]
50pub struct ImageCacheKey {
51 pub request: ImageRequest,
52 pub texel_rect: Option<DeviceIntRect>,
53}
54
55#[derive(Debug)]
61#[cfg_attr(feature = "capture", derive(Serialize))]
62pub struct ImageScratch {
63 pub visible_tiles: storage::Range<VisibleImageTile>,
66 pub src_color: Option<RenderTaskId>,
68 pub normalized_uvs: bool,
71 pub adjustment: AdjustedImageSource,
74 pub tight_local_clip_rect: LayoutRect,
80 pub may_need_repetition: bool,
86 pub gpu_address: GpuBufferAddress,
91}
92
93impl ImageScratch {
94 pub fn empty() -> Self {
95 ImageScratch {
96 visible_tiles: storage::Range::empty(),
97 src_color: None,
98 normalized_uvs: false,
99 adjustment: AdjustedImageSource::new(),
100 tight_local_clip_rect: LayoutRect::zero(),
101 may_need_repetition: true,
102 gpu_address: GpuBufferAddress::INVALID,
103 }
104 }
105}
106
107#[cfg_attr(feature = "capture", derive(Serialize))]
115#[cfg_attr(feature = "replay", derive(Deserialize))]
116#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, MallocSizeOf)]
117pub struct StretchSizeKey {
118 pub size: SizeKey,
119 pub fills_width: bool,
120 pub fills_height: bool,
121}
122
123impl StretchSizeKey {
124 pub fn fills_prim() -> Self {
127 StretchSizeKey {
128 size: LayoutSize::zero().into(),
129 fills_width: true,
130 fills_height: true,
131 }
132 }
133}
134
135#[cfg_attr(feature = "capture", derive(Serialize))]
136#[cfg_attr(feature = "replay", derive(Deserialize))]
137#[derive(Debug, Clone, Copy, MallocSizeOf)]
138pub struct StretchSize {
139 pub size: LayoutSize,
140 pub fills_width: bool,
141 pub fills_height: bool,
142}
143
144impl From<StretchSizeKey> for StretchSize {
145 fn from(k: StretchSizeKey) -> Self {
146 StretchSize {
147 size: k.size.into(),
148 fills_width: k.fills_width,
149 fills_height: k.fills_height,
150 }
151 }
152}
153
154impl StretchSize {
155 pub fn resolve(self, prim_rect: &LayoutRect) -> LayoutSize {
159 let prim_size = prim_rect.size();
160 LayoutSize::new(
161 if self.fills_width { prim_size.width } else { self.size.width },
162 if self.fills_height { prim_size.height } else { self.size.height },
163 )
164 }
165}
166
167#[cfg_attr(feature = "capture", derive(Serialize))]
168#[cfg_attr(feature = "replay", derive(Deserialize))]
169#[derive(Debug, Clone, Eq, PartialEq, MallocSizeOf, Hash)]
170pub struct Image {
171 pub key: ApiImageKey,
172 pub stretch_size: StretchSizeKey,
173 pub tile_spacing: SizeKey,
174 pub color: ColorU,
175 pub image_rendering: ImageRendering,
176 pub alpha_type: AlphaType,
177}
178
179pub type ImageKey = PrimKey<Image>;
180
181impl ImageKey {
182 pub fn new(
183 info: &LayoutPrimitiveInfo,
184 image: Image,
185 ) -> Self {
186 ImageKey {
187 common: info.into(),
188 kind: image,
189 }
190 }
191}
192
193impl InternDebug for ImageKey {}
194
195#[cfg_attr(feature = "capture", derive(Serialize))]
196#[cfg_attr(feature = "replay", derive(Deserialize))]
197#[derive(Debug, MallocSizeOf)]
198pub struct ImageData {
199 pub key: ApiImageKey,
200 pub stretch_size: StretchSize,
201 pub tile_spacing: LayoutSize,
202 pub color: ColorF,
203 pub image_rendering: ImageRendering,
204 pub alpha_type: AlphaType,
205}
206
207impl From<Image> for ImageData {
208 fn from(image: Image) -> Self {
209 ImageData {
210 key: image.key,
211 color: image.color.into(),
212 stretch_size: image.stretch_size.into(),
213 tile_spacing: image.tile_spacing.into(),
214 image_rendering: image.image_rendering,
215 alpha_type: image.alpha_type,
216 }
217 }
218}
219
220impl ImageData {
221 pub fn update(
226 &mut self,
227 common: &mut PrimTemplateCommonData,
228 prim_instance_index: PrimitiveInstanceIndex,
229 prim_spatial_node_index: SpatialNodeIndex,
230 frame_state: &mut FrameBuildingState,
231 frame_context: &FrameBuildingContext,
232 prim_rect: LayoutRect,
233 scratch: &mut PrimitiveScratchBuffer,
234 ) -> storage::Index<ImageScratch> {
235
236 let image_properties = frame_state
237 .resource_cache
238 .get_image_properties(self.key);
239
240 common.opacity = match &image_properties {
241 Some(properties) => {
242 if properties.descriptor.is_opaque() {
243 PrimitiveOpacity::from_alpha(self.color.a)
244 } else {
245 PrimitiveOpacity::translucent()
246 }
247 }
248 None => PrimitiveOpacity::opaque(),
249 };
250
251 let request = ImageRequest {
252 key: self.key,
253 rendering: self.image_rendering,
254 tile: None,
255 };
256
257 let tight_clip_rect = scratch.frame.draws[prim_instance_index.0 as usize]
264 .clip_chain
265 .local_clip_rect
266 .intersection(&prim_rect).unwrap();
267
268 let effective_stretch_size = self.stretch_size.resolve(&prim_rect);
269
270 let mut image_scratch = ImageScratch::empty();
271 image_scratch.tight_local_clip_rect = tight_clip_rect;
272 if effective_stretch_size.width >= prim_rect.size().width
273 && effective_stretch_size.height >= prim_rect.size().height
274 {
275 image_scratch.may_need_repetition = false;
276 }
277
278 match image_properties {
279 Some(ImageProperties { tiling: None, ref descriptor, ref external_image, adjustment, .. }) => {
281 image_scratch.adjustment = adjustment;
282
283 let mut size = frame_state.resource_cache.request_image(
284 request,
285 &mut frame_state.frame_gpu_data.f32,
286 );
287
288 let mut task_id = frame_state.rg_builder.add().init(
289 RenderTask::new_image(size, request, false)
290 );
291
292 if let Some(external_image) = external_image {
293 let requires_copy = frame_context.fb_config.external_images_require_copy &&
296 external_image.image_type ==
297 ExternalImageType::TextureHandle(ImageBufferKind::TextureExternal);
298
299 if requires_copy {
300 let target_kind = if descriptor.format.bytes_per_pixel() == 1 {
301 RenderTargetKind::Alpha
302 } else {
303 RenderTargetKind::Color
304 };
305
306 task_id = RenderTask::new_scaling(
307 task_id,
308 frame_state.rg_builder,
309 target_kind,
310 size
311 );
312
313 frame_state.surface_builder.add_child_render_task(
314 task_id,
315 frame_state.rg_builder,
316 );
317 }
318
319 if !requires_copy {
323 image_scratch.normalized_uvs = external_image.normalized_uvs;
324 }
325 }
326
327 if self.tile_spacing == LayoutSize::zero() {
332 image_scratch.src_color = Some(task_id);
334 } else {
335 let padding = DeviceIntSideOffsets::new(
336 0,
337 (self.tile_spacing.width * size.width as f32 / effective_stretch_size.width) as i32,
338 (self.tile_spacing.height * size.height as f32 / effective_stretch_size.height) as i32,
339 0,
340 );
341
342 size.width += padding.horizontal();
343 size.height += padding.vertical();
344
345 if padding != DeviceIntSideOffsets::zero() {
346 common.opacity = PrimitiveOpacity::translucent();
347 }
348
349 let image_cache_key = ImageCacheKey {
350 request,
351 texel_rect: None,
352 };
353 let target_kind = if descriptor.format.bytes_per_pixel() == 1 {
354 RenderTargetKind::Alpha
355 } else {
356 RenderTargetKind::Color
357 };
358
359 let cached_task_handle = frame_state.resource_cache.request_render_task(
361 Some(RenderTaskCacheKey {
362 origin: DeviceIntPoint::zero(),
363 size,
364 kind: RenderTaskCacheKeyKind::Image(image_cache_key),
365 }),
366 descriptor.is_opaque(),
367 RenderTaskParent::Surface,
368 &mut frame_state.frame_gpu_data.f32,
369 frame_state.rg_builder,
370 &mut frame_state.surface_builder,
371 &mut |rg_builder, _| {
372 let cache_to_target_task_id = RenderTask::new_scaling_with_padding(
376 task_id,
377 rg_builder,
378 target_kind,
379 size,
380 padding,
381 );
382
383 RenderTask::new_blit(
387 size,
388 cache_to_target_task_id,
389 size.into(),
390 rg_builder,
391 )
392 }
393 );
394
395 image_scratch.src_color = Some(cached_task_handle);
396 }
397 }
398 Some(ImageProperties { tiling: Some(tile_size), visible_rect, .. }) => {
400 image_scratch.src_color = None;
402
403 let active_rect = visible_rect;
407
408 let visible_rect = compute_conservative_visible_rect(
409 &scratch.frame.draws[prim_instance_index.0 as usize].clip_chain,
410 frame_state.current_dirty_region().combined,
411 frame_state.current_dirty_region().visibility_spatial_node,
412 prim_spatial_node_index,
413 frame_context.spatial_tree,
414 );
415
416 let base_edge_flags = edge_flags_for_tile_spacing(&self.tile_spacing);
417
418 let stride = effective_stretch_size + self.tile_spacing;
419
420 image_scratch.may_need_repetition = false;
423
424 let repetitions = image_tiling::repetitions(
425 &prim_rect,
426 &visible_rect,
427 stride,
428 );
429
430 let tiles_open = scratch.frame.visible_image_tiles.open_range();
431 for image_tiling::Repetition { origin, edge_flags } in repetitions {
432 let edge_flags = base_edge_flags | edge_flags;
433
434 let layout_image_rect = LayoutRect::from_origin_and_size(
435 origin,
436 effective_stretch_size,
437 );
438
439 let tiles = image_tiling::tiles(
440 &layout_image_rect,
441 &visible_rect,
442 &active_rect,
443 tile_size as i32,
444 );
445
446 for tile in tiles {
447 let request = request.with_tile(tile.offset);
448 let size = frame_state.resource_cache.request_image(
449 request,
450 &mut frame_state.frame_gpu_data.f32,
451 );
452
453 let task_id = frame_state.rg_builder.add().init(
454 RenderTask::new_image(size, request, false)
455 );
456
457 scratch.frame.visible_image_tiles.push(VisibleImageTile {
458 src_color: task_id,
459 edge_flags: tile.edge_flags & edge_flags,
460 local_rect: tile.rect,
461 local_clip_rect: tight_clip_rect,
462 });
463 }
464 }
465 image_scratch.visible_tiles = scratch.frame.visible_image_tiles.close_range(tiles_open);
466
467 if image_scratch.visible_tiles.is_empty() {
468 scratch.frame.draws[prim_instance_index.0 as usize].reset();
470 }
471 }
472 None => {
473 image_scratch.src_color = None;
474 }
475 }
476
477 if let Some(task_id) = frame_state.image_dependencies.get(&self.key) {
478 frame_state.surface_builder.add_child_render_task(
479 *task_id,
480 frame_state.rg_builder
481 );
482 }
483
484 let mut writer = frame_state.frame_gpu_data.f32.write_blocks(3);
485 self.write_prim_gpu_blocks(&image_scratch.adjustment, effective_stretch_size, &mut writer);
486 image_scratch.gpu_address = writer.finish();
487
488 scratch.frame.images.push(image_scratch)
489 }
490
491 pub fn write_prim_gpu_blocks(
492 &self,
493 adjustment: &AdjustedImageSource,
494 stretch_size: LayoutSize,
495 writer: &mut GpuBufferWriterF,
496 ) {
497 let stretch_size = adjustment.map_stretch_size(stretch_size)
498 + self.tile_spacing;
499
500 writer.push(&ImageBrushPrimitiveData {
501 color: self.color.premultiplied(),
502 background_color: PremultipliedColorF::WHITE,
503 stretch_size,
504 });
505 }
506}
507
508pub fn prepare_image_quads(
509 prim_rect: &LayoutRect,
510 common_data: &PrimTemplateCommonData,
511 image_data: &ImageData,
512 clip_chain: &ClipChainInstance,
513 prim_instance_index: PrimitiveInstanceIndex,
514 quad_transform: &mut QuadTransformState,
515 frame_context: &FrameBuildingContext,
516 pic_context: &PictureContext,
517 targets: &[CommandBufferIndex],
518 interned_clips: &DataStore<ClipIntern>,
519 frame_state: &mut FrameBuildingState,
520 scratch: &mut PrimitiveScratchBuffer,
521) {
522 let image_properties = frame_state
523 .resource_cache
524 .get_image_properties(image_data.key);
525
526 let Some(image_properties) = image_properties else {
527 return;
528 };
529
530 let src_is_opaque = image_properties.descriptor.is_opaque()
531 && common_data.opacity.is_opaque
532 && image_data.color.a >= 0.9999;
533
534 let premultiplied = image_data.alpha_type == AlphaType::PremultipliedAlpha;
535
536 let tight_clip_rect = clip_chain
543 .local_clip_rect
544 .intersection(&prim_rect)
545 .unwrap();
546
547 let request = ImageRequest {
548 key: image_data.key,
549 rendering: image_data.image_rendering,
550 tile: None,
551 };
552
553 let mut sampler_kind = ImageBufferKind::Texture2D;
554 if let Some(ExternalImageData { image_type: ExternalImageType::TextureHandle(kind), .. }) = image_properties.external_image {
555 sampler_kind = kind;
556 }
557
558
559 match image_properties.tiling {
560 None => {
562 let size = frame_state.resource_cache.request_image(
563 request,
564 &mut frame_state.frame_gpu_data.f32,
565 );
566
567 let effective_stretch_size = image_data.stretch_size.resolve(prim_rect);
568 let prim_rect = image_properties.adjustment.map_local_rect(&prim_rect);
569 let stretch_size = image_properties.adjustment.map_stretch_size(effective_stretch_size);
570
571 let mut src_task_id = frame_state.rg_builder.add().init(
572 RenderTask::new_image(size, request, false)
573 );
574
575 if let Some(external_image) = image_properties.external_image {
576 let requires_copy = frame_context.fb_config.external_images_require_copy
579 && external_image.image_type
580 == ExternalImageType::TextureHandle(ImageBufferKind::TextureExternal);
581
582 if requires_copy {
583 let target_kind = if image_properties.descriptor.format.bytes_per_pixel() == 1 {
584 RenderTargetKind::Alpha
585 } else {
586 RenderTargetKind::Color
587 };
588
589 src_task_id = RenderTask::new_scaling(
590 src_task_id,
591 frame_state.rg_builder,
592 target_kind,
593 size,
594 );
595
596 frame_state.surface_builder.add_child_render_task(
597 src_task_id,
598 frame_state.rg_builder,
599 );
600
601 sampler_kind = ImageBufferKind::Texture2D;
602 }
603 }
604
605 let image_pattern = ImagePattern {
606 src_task_id,
607 src_is_opaque,
608 premultiplied,
609 sampler_kind,
610 color: image_data.color,
611 };
612
613 quad::prepare_repeatable_quad(
614 &image_pattern,
615 &prim_rect,
616 &tight_clip_rect,
617 stretch_size,
618 image_data.tile_spacing,
619 common_data.aligned_aa_edges,
620 common_data.transformed_aa_edges,
621 prim_instance_index,
622 &None,
623 clip_chain,
624 quad_transform,
625 frame_context,
626 pic_context,
627 targets,
628 interned_clips,
629 frame_state,
630 scratch,
631 );
632 }
633 Some(tile_size) => {
634 let active_rect = image_properties.visible_rect;
638 let visible_rect = compute_conservative_visible_rect(
639 &scratch.frame.draws[prim_instance_index.0 as usize].clip_chain,
640 frame_state.current_dirty_region().combined,
641 frame_state.current_dirty_region().visibility_spatial_node,
642 quad_transform.prim_spatial_node_index(),
643 frame_context.spatial_tree,
644 );
645
646 let effective_stretch_size = image_data.stretch_size.resolve(prim_rect);
647 let stride = effective_stretch_size + image_data.tile_spacing;
648
649 let repetitions = image_tiling::repetitions(
650 prim_rect,
651 &visible_rect,
652 stride,
653 );
654
655 let base_edge_flags = edge_flags_for_tile_spacing(&image_data.tile_spacing);
656
657 for image_tiling::Repetition { origin, edge_flags } in repetitions {
658 let rep_edge_flags = base_edge_flags & edge_flags;
659
660 let layout_image_rect = LayoutRect::from_origin_and_size(
661 origin,
662 effective_stretch_size,
663 );
664
665 let tiles = image_tiling::tiles(
666 &layout_image_rect,
667 &visible_rect,
668 &active_rect,
669 tile_size as i32,
670 );
671
672 for tile in tiles {
673 let request = request.with_tile(tile.offset);
674 let size = frame_state.resource_cache.request_image(
675 request,
676 &mut frame_state.frame_gpu_data.f32,
677 );
678
679 let tile_edge_flags = rep_edge_flags & tile.edge_flags;
680 let aligned_aa_edges = tile_edge_flags & common_data.aligned_aa_edges;
681 let transformed_aa_edges = tile_edge_flags & common_data.transformed_aa_edges;
682
683 let src_task_id = frame_state.rg_builder.add().init(
684 RenderTask::new_image(size, request, false)
685 );
686
687 let image_pattern = ImagePattern {
688 src_task_id,
689 src_is_opaque,
690 premultiplied,
691 sampler_kind,
692 color: image_data.color,
693 };
694
695 quad::prepare_quad(
696 &image_pattern,
697 &tile.rect,
698 &tight_clip_rect,
699 aligned_aa_edges,
700 transformed_aa_edges,
701 prim_instance_index,
702 &None,
703 clip_chain,
704 quad_transform,
705 frame_context,
706 pic_context,
707 targets,
708 interned_clips,
709 frame_state,
710 scratch,
711 );
712 }
713 }
714 }
715 }
716}
717
718fn edge_flags_for_tile_spacing(tile_spacing: &LayoutSize) -> EdgeMask {
719 let mut flags = EdgeMask::empty();
720
721 if tile_spacing.width > 0.0 {
722 flags |= EdgeMask::LEFT | EdgeMask::RIGHT;
723 }
724 if tile_spacing.height > 0.0 {
725 flags |= EdgeMask::TOP | EdgeMask::BOTTOM;
726 }
727
728 flags
729}
730
731pub type ImageTemplate = PrimTemplate<ImageData>;
732
733impl From<ImageKey> for ImageTemplate {
734 fn from(image: ImageKey) -> Self {
735 let common = PrimTemplateCommonData::with_key_common(image.common);
736
737 ImageTemplate {
738 common,
739 kind: image.kind.into(),
740 }
741 }
742}
743
744pub type ImageDataHandle = InternHandle<Image>;
745
746impl Internable for Image {
747 type Key = ImageKey;
748 type StoreData = ImageTemplate;
749 type InternData = ();
750 const PROFILE_COUNTER: usize = crate::profiler::INTERNED_IMAGES;
751}
752
753impl InternablePrimitive for Image {
754 fn into_key(
755 self,
756 info: &LayoutPrimitiveInfo,
757 ) -> ImageKey {
758 ImageKey::new(info, self)
759 }
760
761 fn make_instance_kind(
762 _key: ImageKey,
763 data_handle: ImageDataHandle,
764 _prim_store: &mut PrimitiveStore,
765 ) -> PrimitiveKind {
766 PrimitiveKind::Image {
767 data_handle,
768 }
769 }
770}
771
772impl CreateShadow for Image {
773 fn create_shadow(
774 &self,
775 shadow: &Shadow,
776 _: bool,
777 _: RasterSpace,
778 ) -> Self {
779 Image {
780 tile_spacing: self.tile_spacing,
781 stretch_size: self.stretch_size,
782 key: self.key,
783 image_rendering: self.image_rendering,
784 alpha_type: self.alpha_type,
785 color: shadow.color.into(),
786 }
787 }
788}
789
790impl IsVisible for Image {
791 fn is_visible(&self) -> bool {
792 true
793 }
794}
795
796#[derive(Copy, Clone, Debug)]
814#[cfg_attr(feature = "capture", derive(Serialize))]
815#[cfg_attr(feature = "replay", derive(Deserialize))]
816pub struct AdjustedImageSource {
817 x0: f32,
818 y0: f32,
819 x1: f32,
820 y1: f32,
821}
822
823impl AdjustedImageSource {
824 pub fn new() -> Self {
826 AdjustedImageSource {
827 x0: 0.0,
828 y0: 0.0,
829 x1: 0.0,
830 y1: 0.0,
831 }
832 }
833
834 pub fn from_rects(reference: &LayoutRect, actual: &LayoutRect) -> Self {
837 let ref_size = reference.size();
838 let min_offset = reference.min.to_vector();
839 let max_offset = reference.max.to_vector();
840 AdjustedImageSource {
841 x0: (actual.min.x - min_offset.x) / ref_size.width,
842 y0: (actual.min.y - min_offset.y) / ref_size.height,
843 x1: (actual.max.x - max_offset.x) / ref_size.width,
844 y1: (actual.max.y - max_offset.y) / ref_size.height,
845 }
846 }
847
848 pub fn map_local_rect(&self, rect: &LayoutRect) -> LayoutRect {
850 let w = rect.width();
851 let h = rect.height();
852 LayoutRect {
853 min: point2(
854 rect.min.x + w * self.x0,
855 rect.min.y + h * self.y0,
856 ),
857 max: point2(
858 rect.max.x + w * self.x1,
859 rect.max.y + h * self.y1,
860 ),
861 }
862 }
863
864 pub fn map_stretch_size(&self, size: LayoutSize) -> LayoutSize {
878 LayoutSize::new(
879 size.width * (1.0 + self.x1 - self.x0),
880 size.height * (1.0 + self.y1 - self.y0),
881 )
882 }
883}
884
885#[cfg_attr(feature = "capture", derive(Serialize))]
888#[cfg_attr(feature = "replay", derive(Deserialize))]
889#[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
890pub struct YuvImage {
891 pub color_depth: ColorDepth,
892 pub yuv_key: [ApiImageKey; 3],
893 pub format: YuvFormat,
894 pub color_space: YuvColorSpace,
895 pub color_range: ColorRange,
896 pub image_rendering: ImageRendering,
897}
898
899pub type YuvImageKey = PrimKey<YuvImage>;
900
901impl YuvImageKey {
902 pub fn new(
903 info: &LayoutPrimitiveInfo,
904 yuv_image: YuvImage,
905 ) -> Self {
906 YuvImageKey {
907 common: info.into(),
908 kind: yuv_image,
909 }
910 }
911}
912
913impl InternDebug for YuvImageKey {}
914
915#[cfg_attr(feature = "capture", derive(Serialize))]
916#[cfg_attr(feature = "replay", derive(Deserialize))]
917#[derive(MallocSizeOf)]
918pub struct YuvImageData {
919 pub color_depth: ColorDepth,
920 pub yuv_key: [ApiImageKey; 3],
921 pub src_yuv: [Option<RenderTaskId>; 3],
922 pub format: YuvFormat,
923 pub color_space: YuvColorSpace,
924 pub color_range: ColorRange,
925 pub image_rendering: ImageRendering,
926}
927
928impl From<YuvImage> for YuvImageData {
929 fn from(image: YuvImage) -> Self {
930 YuvImageData {
931 color_depth: image.color_depth,
932 yuv_key: image.yuv_key,
933 src_yuv: [None, None, None],
934 format: image.format,
935 color_space: image.color_space,
936 color_range: image.color_range,
937 image_rendering: image.image_rendering,
938 }
939 }
940}
941
942impl YuvImageData {
943 pub fn update(
948 &mut self,
949 common: &mut PrimTemplateCommonData,
950 is_composited: bool,
951 frame_state: &mut FrameBuildingState,
952 ) {
953
954 self.src_yuv = [ None, None, None ];
955
956 let channel_num = self.format.get_plane_num();
957 debug_assert!(channel_num <= 3);
958 for channel in 0 .. channel_num {
959 let request = ImageRequest {
960 key: self.yuv_key[channel],
961 rendering: self.image_rendering,
962 tile: None,
963 };
964
965 let size = frame_state.resource_cache.request_image(
966 request,
967 &mut frame_state.frame_gpu_data.f32,
968 );
969
970 let task_id = frame_state.rg_builder.add().init(
971 RenderTask::new_image(
972 size,
973 request,
974 is_composited,
975 )
976 );
977
978 self.src_yuv[channel] = Some(task_id);
979 }
980
981 let mut writer = frame_state.frame_gpu_data.f32.write_blocks(1);
982 self.write_prim_gpu_blocks(&mut writer);
983 common.gpu_buffer_address = writer.finish();
984
985 common.opacity = PrimitiveOpacity::opaque();
987 }
988
989 pub fn request_resources(
990 &mut self,
991 resource_cache: &mut ResourceCache,
992 gpu_buffer: &mut GpuBufferBuilderF,
993 ) {
994 let channel_num = self.format.get_plane_num();
995 debug_assert!(channel_num <= 3);
996 for channel in 0 .. channel_num {
997 resource_cache.request_image(
998 ImageRequest {
999 key: self.yuv_key[channel],
1000 rendering: self.image_rendering,
1001 tile: None,
1002 },
1003 gpu_buffer,
1004 );
1005 }
1006 }
1007
1008 pub fn write_prim_gpu_blocks(&self, writer: &mut GpuBufferWriterF) {
1009 writer.push(&YuvPrimitive {
1010 channel_bit_depth: self.color_depth.bit_depth(),
1011 color_space: self.color_space.with_range(self.color_range),
1012 yuv_format: self.format,
1013 });
1014 }
1015}
1016
1017pub type YuvImageTemplate = PrimTemplate<YuvImageData>;
1018
1019impl From<YuvImageKey> for YuvImageTemplate {
1020 fn from(image: YuvImageKey) -> Self {
1021 let common = PrimTemplateCommonData::with_key_common(image.common);
1022
1023 YuvImageTemplate {
1024 common,
1025 kind: image.kind.into(),
1026 }
1027 }
1028}
1029
1030pub type YuvImageDataHandle = InternHandle<YuvImage>;
1031
1032impl Internable for YuvImage {
1033 type Key = YuvImageKey;
1034 type StoreData = YuvImageTemplate;
1035 type InternData = ();
1036 const PROFILE_COUNTER: usize = crate::profiler::INTERNED_YUV_IMAGES;
1037}
1038
1039impl InternablePrimitive for YuvImage {
1040 fn into_key(
1041 self,
1042 info: &LayoutPrimitiveInfo,
1043 ) -> YuvImageKey {
1044 YuvImageKey::new(info, self)
1045 }
1046
1047 fn make_instance_kind(
1048 _key: YuvImageKey,
1049 data_handle: YuvImageDataHandle,
1050 _prim_store: &mut PrimitiveStore,
1051 ) -> PrimitiveKind {
1052 PrimitiveKind::YuvImage {
1053 data_handle,
1054 }
1055 }
1056}
1057
1058impl IsVisible for YuvImage {
1059 fn is_visible(&self) -> bool {
1060 true
1061 }
1062}
1063
1064#[test]
1065#[cfg(target_pointer_width = "64")]
1066fn test_struct_sizes() {
1067 use std::mem;
1068 assert_eq!(mem::size_of::<Image>(), 36, "Image size changed");
1075 assert_eq!(mem::size_of::<ImageTemplate>(), 56, "ImageTemplate size changed");
1076 assert_eq!(mem::size_of::<ImageKey>(), 40, "ImageKey size changed");
1077 assert_eq!(mem::size_of::<YuvImage>(), 32, "YuvImage size changed");
1078 assert_eq!(mem::size_of::<YuvImageTemplate>(), 76, "YuvImageTemplate size changed");
1079 assert_eq!(mem::size_of::<YuvImageKey>(), 36, "YuvImageKey size changed");
1080}