tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

picture_composite_mode.rs (51035B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 use api::{ColorF, ColorU, PremultipliedColorF, PropertyBinding, PropertyBindingId, SnapshotInfo};
      6 use api::units::*;
      7 use crate::prim_store::image::AdjustedImageSource;
      8 use crate::{render_task_graph::RenderTaskGraphBuilder, renderer::GpuBufferBuilderF};
      9 use crate::box_shadow::BLUR_SAMPLE_SCALE;
     10 use crate::frame_builder::{FrameBuildingContext, FrameBuildingState};
     11 use crate::gpu_types::{BlurEdgeMode, BrushSegmentGpuData, ImageBrushPrimitiveData, UvRectKind};
     12 use crate::intern::ItemUid;
     13 use crate::render_backend::DataStores;
     14 use crate::render_task_graph::RenderTaskId;
     15 use crate::render_target::RenderTargetKind;
     16 use crate::render_task::{BlurTask, RenderTask, BlurTaskCache};
     17 use crate::render_task::RenderTaskKind;
     18 use crate::renderer::{BlendMode, GpuBufferAddress, GpuBufferBuilder};
     19 use crate::space::SpaceMapper;
     20 use crate::spatial_tree::SpatialTree;
     21 use crate::surface::{SurfaceDescriptor, SurfaceInfo, calculate_screen_uv};
     22 use crate::surface::SurfaceIndex;
     23 use crate::svg_filter::{get_coverage_source_svgfe, FilterGraphNodeKey, FilterGraphOpKey};
     24 use crate::util::MaxRect;
     25 use smallvec::SmallVec;
     26 use crate::internal_types::Filter;
     27 use crate::profiler;
     28 use core::time::Duration;
     29 use euclid::Scale;
     30 use api::MixBlendMode;
     31 use crate::filterdata::FilterDataHandle;
     32 use crate::tile_cache::SliceId;
     33 use crate::svg_filter::{FilterGraphNode, FilterGraphOp, get_coverage_target_svgfe};
     34 use crate::picture::BlitReason;
     35 use crate::prim_store::VectorKey;
     36 #[cfg(feature = "capture")]
     37 use serde::Serialize;
     38 
     39 /// Specifies how this Picture should be composited
     40 /// onto the target it belongs to.
     41 #[allow(dead_code)]
     42 #[derive(Debug, Clone)]
     43 #[cfg_attr(feature = "capture", derive(Serialize))]
     44 pub enum PictureCompositeMode {
     45    /// Apply CSS mix-blend-mode effect.
     46    MixBlend(MixBlendMode),
     47    /// Apply a CSS filter (except component transfer).
     48    Filter(Filter),
     49    /// Apply a component transfer filter.
     50    ComponentTransferFilter(FilterDataHandle),
     51    /// Draw to intermediate surface, copy straight across. This
     52    /// is used for CSS isolation, and plane splitting.
     53    Blit(BlitReason),
     54    /// Used to cache a picture as a series of tiles.
     55    TileCache {
     56        slice_id: SliceId,
     57    },
     58    /// Apply an SVG filter graph
     59    SVGFEGraph(Vec<(FilterGraphNode, FilterGraphOp)>),
     60    /// A surface that is used as an input to another primitive
     61    IntermediateSurface,
     62 }
     63 
     64 impl PictureCompositeMode {
     65    pub fn get_rect(
     66        &self,
     67        surface: &SurfaceInfo,
     68        sub_rect: Option<LayoutRect>,
     69    ) -> LayoutRect {
     70        let surface_rect = match sub_rect {
     71            Some(sub_rect) => sub_rect,
     72            None => surface.clipped_local_rect.cast_unit(),
     73        };
     74 
     75        match self {
     76            PictureCompositeMode::Filter(Filter::Blur { width, height, should_inflate, .. }) => {
     77                if *should_inflate {
     78                    let (width_factor, height_factor) = surface.clamp_blur_radius(*width, *height);
     79 
     80                    surface_rect.inflate(
     81                        width_factor.ceil() * BLUR_SAMPLE_SCALE,
     82                        height_factor.ceil() * BLUR_SAMPLE_SCALE,
     83                    )
     84                } else {
     85                    surface_rect
     86                }
     87            }
     88            PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
     89                let mut max_blur_radius = 0.0;
     90                for shadow in shadows {
     91                    max_blur_radius = f32::max(max_blur_radius, shadow.blur_radius);
     92                }
     93 
     94                let (max_blur_radius_x, max_blur_radius_y) = surface.clamp_blur_radius(
     95                    max_blur_radius,
     96                    max_blur_radius,
     97                );
     98                let blur_inflation_x = max_blur_radius_x * BLUR_SAMPLE_SCALE;
     99                let blur_inflation_y = max_blur_radius_y * BLUR_SAMPLE_SCALE;
    100 
    101                surface_rect.inflate(blur_inflation_x, blur_inflation_y)
    102            }
    103            PictureCompositeMode::SVGFEGraph(ref filters) => {
    104                // Return prim_subregion for use in get_local_prim_rect, which
    105                // is the polygon size.
    106                // This must match surface_rects.unclipped_local.
    107                get_coverage_target_svgfe(filters, surface_rect.cast_unit())
    108            }
    109            _ => {
    110                surface_rect
    111            }
    112        }
    113    }
    114 
    115    pub fn get_coverage(
    116        &self,
    117        surface: &SurfaceInfo,
    118        sub_rect: Option<LayoutRect>,
    119    ) -> LayoutRect {
    120        let surface_rect = match sub_rect {
    121            Some(sub_rect) => sub_rect,
    122            None => surface.clipped_local_rect.cast_unit(),
    123        };
    124 
    125        match self {
    126            PictureCompositeMode::Filter(Filter::Blur { width, height, should_inflate, .. }) => {
    127                if *should_inflate {
    128                    let (width_factor, height_factor) = surface.clamp_blur_radius(*width, *height);
    129 
    130                    surface_rect.inflate(
    131                        width_factor.ceil() * BLUR_SAMPLE_SCALE,
    132                        height_factor.ceil() * BLUR_SAMPLE_SCALE,
    133                    )
    134                } else {
    135                    surface_rect
    136                }
    137            }
    138            PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
    139                let mut rect = surface_rect;
    140 
    141                for shadow in shadows {
    142                    let (blur_radius_x, blur_radius_y) = surface.clamp_blur_radius(
    143                        shadow.blur_radius,
    144                        shadow.blur_radius,
    145                    );
    146                    let blur_inflation_x = blur_radius_x * BLUR_SAMPLE_SCALE;
    147                    let blur_inflation_y = blur_radius_y * BLUR_SAMPLE_SCALE;
    148 
    149                    let shadow_rect = surface_rect
    150                        .translate(shadow.offset)
    151                        .inflate(blur_inflation_x, blur_inflation_y);
    152                    rect = rect.union(&shadow_rect);
    153                }
    154 
    155                rect
    156            }
    157            PictureCompositeMode::SVGFEGraph(ref filters) => {
    158                // surface_rect may be for source or target, so invalidate based
    159                // on both interpretations
    160                let target_subregion = get_coverage_source_svgfe(filters, surface_rect.cast());
    161                let source_subregion = get_coverage_target_svgfe(filters, surface_rect.cast());
    162                target_subregion.union(&source_subregion)
    163            }
    164            _ => {
    165                surface_rect
    166            }
    167        }
    168    }
    169 
    170    pub fn write_gpu_blocks(
    171        &self,
    172        surface: &SurfaceInfo,
    173        gpu_buffers: &mut GpuBufferBuilder,
    174        data_stores: &mut DataStores,
    175        extra_gpu_data: &mut SmallVec<[GpuBufferAddress; 1]>,
    176    ) {
    177        // TODO(gw): Almost all of the composite modes below use extra_gpu_data
    178        //           to store the same type of data. The exception is the filter
    179        //           with a ColorMatrix, which stores the color matrix here. It's
    180        //           probably worth tidying this code up to be a bit more consistent.
    181        //           Perhaps store the color matrix after the common data, even though
    182        //           it's not used by that shader.
    183 
    184        match *self {
    185            PictureCompositeMode::TileCache { .. } => {}
    186            PictureCompositeMode::Filter(Filter::Blur { .. }) => {}
    187            PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
    188                extra_gpu_data.resize(shadows.len(), GpuBufferAddress::INVALID);
    189                for (shadow, extra_handle) in shadows.iter().zip(extra_gpu_data.iter_mut()) {
    190                    let mut writer = gpu_buffers.f32.write_blocks(5);
    191                    let prim_rect = surface.clipped_local_rect.cast_unit();
    192 
    193                    // Basic brush primitive header is (see end of prepare_prim_for_render_inner in prim_store.rs)
    194                    //  [brush specific data]
    195                    //  [segment_rect, segment data]
    196                    let (blur_inflation_x, blur_inflation_y) = surface.clamp_blur_radius(
    197                        shadow.blur_radius,
    198                        shadow.blur_radius,
    199                    );
    200 
    201                    let shadow_rect = prim_rect.inflate(
    202                        blur_inflation_x * BLUR_SAMPLE_SCALE,
    203                        blur_inflation_y * BLUR_SAMPLE_SCALE,
    204                    ).translate(shadow.offset);
    205 
    206                    // ImageBrush colors
    207                    writer.push(&ImageBrushPrimitiveData {
    208                        color: shadow.color.premultiplied(),
    209                        background_color: PremultipliedColorF::WHITE,
    210                        stretch_size: shadow_rect.size(),
    211                    });
    212 
    213                    writer.push(&BrushSegmentGpuData {
    214                        local_rect: shadow_rect,
    215                        extra_data: [0.0; 4],
    216                    });
    217 
    218                    *extra_handle = writer.finish();
    219                }
    220            }
    221            PictureCompositeMode::Filter(ref filter) => {
    222                match *filter {
    223                    Filter::ColorMatrix(ref m) => {
    224                        if extra_gpu_data.is_empty() {
    225                            extra_gpu_data.push(GpuBufferAddress::INVALID);
    226                        }
    227                        let mut writer = gpu_buffers.f32.write_blocks(5);
    228                        for i in 0..5 {
    229                            writer.push_one([m[i*4], m[i*4+1], m[i*4+2], m[i*4+3]]);
    230                        }
    231                        extra_gpu_data[0] = writer.finish();
    232                    }
    233                    Filter::Flood(ref color) => {
    234                        if extra_gpu_data.is_empty() {
    235                            extra_gpu_data.push(GpuBufferAddress::INVALID);
    236                        }
    237                        let mut writer = gpu_buffers.f32.write_blocks(1);
    238                        writer.push_one(color.to_array());
    239                        extra_gpu_data[0] = writer.finish();
    240                    }
    241                    _ => {}
    242                }
    243            }
    244            PictureCompositeMode::ComponentTransferFilter(handle) => {
    245                let filter_data = &mut data_stores.filter_data[handle];
    246                filter_data.write_gpu_blocks(&mut gpu_buffers.f32);
    247            }
    248            PictureCompositeMode::MixBlend(..) |
    249            PictureCompositeMode::Blit(_) |
    250            PictureCompositeMode::IntermediateSurface => {}
    251            PictureCompositeMode::SVGFEGraph(ref filters) => {
    252                // Update interned filter data
    253                for (_node, op) in filters {
    254                    match op {
    255                        FilterGraphOp::SVGFEComponentTransferInterned { handle, creates_pixels: _ } => {
    256                            let filter_data = &mut data_stores.filter_data[*handle];
    257                            filter_data.write_gpu_blocks(&mut gpu_buffers.f32);
    258                        }
    259                        _ => {}
    260                    }
    261                }
    262            }
    263        }
    264    }
    265 
    266    /// Returns a static str describing the type of PictureCompositeMode (and
    267    /// filter type if applicable)
    268    pub fn kind(&self) -> &'static str {
    269        match *self {
    270            PictureCompositeMode::Blit(..) => "Blit",
    271            PictureCompositeMode::ComponentTransferFilter(..) => "ComponentTransferFilter",
    272            PictureCompositeMode::IntermediateSurface => "IntermediateSurface",
    273            PictureCompositeMode::MixBlend(..) => "MixBlend",
    274            PictureCompositeMode::SVGFEGraph(..) => "SVGFEGraph",
    275            PictureCompositeMode::TileCache{..} => "TileCache",
    276            PictureCompositeMode::Filter(Filter::Blur{..}) => "Filter::Blur",
    277            PictureCompositeMode::Filter(Filter::Brightness(..)) => "Filter::Brightness",
    278            PictureCompositeMode::Filter(Filter::ColorMatrix(..)) => "Filter::ColorMatrix",
    279            PictureCompositeMode::Filter(Filter::ComponentTransfer) => "Filter::ComponentTransfer",
    280            PictureCompositeMode::Filter(Filter::Contrast(..)) => "Filter::Contrast",
    281            PictureCompositeMode::Filter(Filter::DropShadows(..)) => "Filter::DropShadows",
    282            PictureCompositeMode::Filter(Filter::Flood(..)) => "Filter::Flood",
    283            PictureCompositeMode::Filter(Filter::Grayscale(..)) => "Filter::Grayscale",
    284            PictureCompositeMode::Filter(Filter::HueRotate(..)) => "Filter::HueRotate",
    285            PictureCompositeMode::Filter(Filter::Identity) => "Filter::Identity",
    286            PictureCompositeMode::Filter(Filter::Invert(..)) => "Filter::Invert",
    287            PictureCompositeMode::Filter(Filter::LinearToSrgb) => "Filter::LinearToSrgb",
    288            PictureCompositeMode::Filter(Filter::Opacity(..)) => "Filter::Opacity",
    289            PictureCompositeMode::Filter(Filter::Saturate(..)) => "Filter::Saturate",
    290            PictureCompositeMode::Filter(Filter::Sepia(..)) => "Filter::Sepia",
    291            PictureCompositeMode::Filter(Filter::SrgbToLinear) => "Filter::SrgbToLinear",
    292            PictureCompositeMode::Filter(Filter::SVGGraphNode(..)) => "Filter::SVGGraphNode",
    293        }
    294    }
    295 }
    296 
    297 pub fn prepare_composite_mode(
    298    composite_mode: &PictureCompositeMode,
    299    surface_index: SurfaceIndex,
    300    parent_surface_index: SurfaceIndex,
    301    surface_rects: &SurfaceAllocInfo,
    302    snapshot: &Option<SnapshotInfo>,
    303    can_use_shared_surface: bool,
    304    frame_context: &FrameBuildingContext,
    305    frame_state: &mut FrameBuildingState,
    306    data_stores: &mut DataStores,
    307    extra_gpu_data: &mut SmallVec<[GpuBufferAddress; 1]>,
    308 ) -> (SurfaceDescriptor, [Option<RenderTaskId>; 2]) {
    309    let surface = &frame_state.surfaces[surface_index.0];
    310    let surface_spatial_node_index = surface.surface_spatial_node_index;
    311    let raster_spatial_node_index = surface.raster_spatial_node_index;
    312    let device_pixel_scale = surface.device_pixel_scale;
    313 
    314    let primary_render_task_id;
    315    let surface_descriptor;
    316    let mut secondary_render_task_id = None;
    317    match *composite_mode {
    318        PictureCompositeMode::TileCache { .. } => {
    319            unreachable!("handled above");
    320        }
    321        PictureCompositeMode::Filter(Filter::Blur { width, height, edge_mode, .. }) => {
    322            let (width, height) = surface.clamp_blur_radius(width, height);
    323 
    324            let width_std_deviation = width * surface.local_scale.0 * device_pixel_scale.0;
    325            let height_std_deviation = height * surface.local_scale.1 * device_pixel_scale.0;
    326            let blur_std_deviation = DeviceSize::new(
    327                width_std_deviation,
    328                height_std_deviation,
    329            );
    330 
    331            let original_size = surface_rects.clipped.size();
    332 
    333            let adjusted_size = BlurTask::adjusted_blur_source_size(
    334                original_size,
    335                blur_std_deviation,
    336            );
    337 
    338            let clear_color = if adjusted_size == original_size {
    339                None
    340            } else {
    341                Some(ColorF::TRANSPARENT)
    342            };
    343 
    344            let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
    345            let adjusted_size = adjusted_size.to_i32();
    346 
    347            let uv_rect_kind = calculate_uv_rect_kind(
    348                DeviceRect::from_origin_and_size(surface_rects.clipped.min, adjusted_size.to_f32()),
    349                surface_rects.unclipped,
    350            );
    351 
    352            let picture_task_id = frame_state.rg_builder.add().init(
    353                RenderTask::new_dynamic(
    354                    adjusted_size,
    355                    RenderTaskKind::new_picture(
    356                        adjusted_size,
    357                        surface_rects.needs_scissor_rect,
    358                        surface_rects.clipped.min,
    359                        surface_spatial_node_index,
    360                        raster_spatial_node_index,
    361                        device_pixel_scale,
    362                        None,
    363                        None,
    364                        clear_color,
    365                        cmd_buffer_index,
    366                        can_use_shared_surface,
    367                        Some(original_size.round().to_i32()),
    368                    )
    369                ).with_uv_rect_kind(uv_rect_kind)
    370            );
    371 
    372 
    373            let blur_render_task_id = request_render_task(
    374                frame_state,
    375                snapshot,
    376                &surface_rects,
    377                false,
    378                &mut|rg_builder, _| {
    379                    RenderTask::new_blur(
    380                        blur_std_deviation,
    381                        picture_task_id,
    382                        rg_builder,
    383                        RenderTargetKind::Color,
    384                        None,
    385                        original_size.to_i32(),
    386                        edge_mode,
    387                    )
    388                }
    389            );
    390            primary_render_task_id = blur_render_task_id;
    391 
    392            surface_descriptor = SurfaceDescriptor::new_chained(
    393                picture_task_id,
    394                blur_render_task_id,
    395                surface_rects.clipped_local,
    396            );
    397        }
    398        PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
    399            let surface = &frame_state.surfaces[surface_index.0];
    400 
    401            let device_rect = surface_rects.clipped;
    402 
    403            let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
    404 
    405            let picture_task_id = frame_state.rg_builder.add().init(
    406                RenderTask::new_dynamic(
    407                    surface_rects.task_size,
    408                    RenderTaskKind::new_picture(
    409                        surface_rects.task_size,
    410                        surface_rects.needs_scissor_rect,
    411                        device_rect.min,
    412                        surface_spatial_node_index,
    413                        raster_spatial_node_index,
    414                        device_pixel_scale,
    415                        None,
    416                        None,
    417                        None,
    418                        cmd_buffer_index,
    419                        can_use_shared_surface,
    420                        None,
    421                    ),
    422                ).with_uv_rect_kind(surface_rects.uv_rect_kind)
    423            );
    424 
    425            let mut blur_tasks = BlurTaskCache::default();
    426 
    427            extra_gpu_data.resize(shadows.len(), GpuBufferAddress::INVALID);
    428 
    429            let mut blur_render_task_id = picture_task_id;
    430            for shadow in shadows {
    431                let (blur_radius_x, blur_radius_y) = surface.clamp_blur_radius(
    432                    shadow.blur_radius,
    433                    shadow.blur_radius,
    434                );
    435 
    436                blur_render_task_id = RenderTask::new_blur(
    437                    DeviceSize::new(
    438                        blur_radius_x * surface.local_scale.0 * device_pixel_scale.0,
    439                        blur_radius_y * surface.local_scale.1 * device_pixel_scale.0,
    440                    ),
    441                    picture_task_id,
    442                    frame_state.rg_builder,
    443                    RenderTargetKind::Color,
    444                    Some(&mut blur_tasks),
    445                    device_rect.size().to_i32(),
    446                    BlurEdgeMode::Duplicate,
    447                );
    448            }
    449 
    450            frame_state.surface_builder.add_picture_render_task(picture_task_id);
    451 
    452            primary_render_task_id = blur_render_task_id;
    453            secondary_render_task_id = Some(picture_task_id);
    454 
    455            surface_descriptor = SurfaceDescriptor::new_chained(
    456                picture_task_id,
    457                blur_render_task_id,
    458                surface_rects.clipped_local,
    459            );
    460        }
    461        PictureCompositeMode::MixBlend(mode) if BlendMode::from_mix_blend_mode(
    462            mode,
    463            frame_context.fb_config.gpu_supports_advanced_blend,
    464            frame_context.fb_config.advanced_blend_is_coherent,
    465            frame_context.fb_config.dual_source_blending_is_supported,
    466        ).is_none() => {
    467            let parent_surface = &frame_state.surfaces[parent_surface_index.0];
    468 
    469            let map_pic_to_parent = SpaceMapper::new_with_target(
    470                parent_surface.surface_spatial_node_index,
    471                surface_spatial_node_index,
    472                parent_surface.clipping_rect,
    473                frame_context.spatial_tree,
    474            );
    475            let pic_rect = surface.clipped_local_rect;
    476            let pic_in_raster_space = map_pic_to_parent
    477                .map(&pic_rect)
    478                .expect("bug: unable to map mix-blend content into parent");
    479 
    480            let backdrop_rect = pic_in_raster_space;
    481            let parent_surface_rect = parent_surface.clipping_rect;
    482 
    483            let readback_task_id = match backdrop_rect.intersection(&parent_surface_rect) {
    484                Some(available_rect) => {
    485                    let backdrop_rect = parent_surface.map_to_device_rect(
    486                        &backdrop_rect,
    487                        frame_context.spatial_tree,
    488                    );
    489 
    490                    let available_rect = parent_surface.map_to_device_rect(
    491                        &available_rect,
    492                        frame_context.spatial_tree,
    493                    ).round_out();
    494 
    495                    let backdrop_uv = calculate_uv_rect_kind(
    496                        available_rect,
    497                        backdrop_rect,
    498                    );
    499 
    500                    frame_state.rg_builder.add().init(
    501                        RenderTask::new_dynamic(
    502                            available_rect.size().to_i32(),
    503                            RenderTaskKind::new_readback(Some(available_rect.min)),
    504                        ).with_uv_rect_kind(backdrop_uv)
    505                    )
    506                }
    507                None => {
    508                    frame_state.rg_builder.add().init(
    509                        RenderTask::new_dynamic(
    510                            DeviceIntSize::new(16, 16),
    511                            RenderTaskKind::new_readback(None),
    512                        )
    513                    )
    514                }
    515            };
    516 
    517            frame_state.surface_builder.add_child_render_task(
    518                readback_task_id,
    519                frame_state.rg_builder,
    520            );
    521 
    522            secondary_render_task_id = Some(readback_task_id);
    523 
    524            let task_size = surface_rects.clipped.size().to_i32();
    525 
    526            let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
    527 
    528            let is_opaque = false;
    529            let render_task_id = request_render_task(
    530                frame_state,
    531                &snapshot,
    532                &surface_rects,
    533                is_opaque,
    534                &mut|rg_builder, _| {
    535                    rg_builder.add().init(
    536                        RenderTask::new_dynamic(
    537                            task_size,
    538                            RenderTaskKind::new_picture(
    539                                task_size,
    540                                surface_rects.needs_scissor_rect,
    541                                surface_rects.clipped.min,
    542                                surface_spatial_node_index,
    543                                raster_spatial_node_index,
    544                                device_pixel_scale,
    545                                None,
    546                                None,
    547                                None,
    548                                cmd_buffer_index,
    549                                can_use_shared_surface,
    550                                None,
    551                            )
    552                        ).with_uv_rect_kind(surface_rects.uv_rect_kind)
    553                    )
    554                }
    555            );
    556 
    557            primary_render_task_id = render_task_id;
    558 
    559            surface_descriptor = SurfaceDescriptor::new_simple(
    560                render_task_id,
    561                surface_rects.clipped_local,
    562            );
    563        }
    564        PictureCompositeMode::Filter(..) => {
    565            let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
    566 
    567            let is_opaque = false;
    568            let render_task_id = request_render_task(
    569                frame_state,
    570                snapshot,
    571                &surface_rects,
    572                is_opaque,
    573                &mut|rg_builder, _| {
    574                    rg_builder.add().init(
    575                        RenderTask::new_dynamic(
    576                            surface_rects.task_size,
    577                            RenderTaskKind::new_picture(
    578                                surface_rects.task_size,
    579                                surface_rects.needs_scissor_rect,
    580                                surface_rects.clipped.min,
    581                                surface_spatial_node_index,
    582                                raster_spatial_node_index,
    583                                device_pixel_scale,
    584                                None,
    585                                None,
    586                                None,
    587                                cmd_buffer_index,
    588                                can_use_shared_surface,
    589                                None,
    590                            )
    591                        ).with_uv_rect_kind(surface_rects.uv_rect_kind)
    592                    )
    593                },
    594            );
    595 
    596            primary_render_task_id = render_task_id;
    597 
    598            surface_descriptor = SurfaceDescriptor::new_simple(
    599                render_task_id,
    600                surface_rects.clipped_local,
    601            );
    602        }
    603        PictureCompositeMode::ComponentTransferFilter(..) => {
    604            let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
    605 
    606            let is_opaque = false;
    607            let render_task_id = request_render_task(
    608                frame_state,
    609                snapshot,
    610                &surface_rects,
    611                is_opaque,
    612                &mut|rg_builder, _| {
    613                    rg_builder.add().init(
    614                        RenderTask::new_dynamic(
    615                            surface_rects.task_size,
    616                            RenderTaskKind::new_picture(
    617                                surface_rects.task_size,
    618                                surface_rects.needs_scissor_rect,
    619                                surface_rects.clipped.min,
    620                                surface_spatial_node_index,
    621                                raster_spatial_node_index,
    622                                device_pixel_scale,
    623                                None,
    624                                None,
    625                                None,
    626                                cmd_buffer_index,
    627                                can_use_shared_surface,
    628                                None,
    629                            )
    630                        ).with_uv_rect_kind(surface_rects.uv_rect_kind)
    631                    )
    632                }
    633            );
    634 
    635            primary_render_task_id = render_task_id;
    636 
    637            surface_descriptor = SurfaceDescriptor::new_simple(
    638                render_task_id,
    639                surface_rects.clipped_local,
    640            );
    641        }
    642        PictureCompositeMode::MixBlend(..) |
    643        PictureCompositeMode::Blit(_) => {
    644            let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
    645 
    646            let is_opaque = false;
    647            let render_task_id = request_render_task(
    648                frame_state,
    649                snapshot,
    650                &surface_rects,
    651                is_opaque,
    652                &mut|rg_builder, _| {
    653                    rg_builder.add().init(
    654                        RenderTask::new_dynamic(
    655                            surface_rects.task_size,
    656                            RenderTaskKind::new_picture(
    657                                surface_rects.task_size,
    658                                surface_rects.needs_scissor_rect,
    659                                surface_rects.clipped.min,
    660                                surface_spatial_node_index,
    661                                raster_spatial_node_index,
    662                                device_pixel_scale,
    663                                None,
    664                                None,
    665                                None,
    666                                cmd_buffer_index,
    667                                can_use_shared_surface,
    668                                None,
    669                            )
    670                        ).with_uv_rect_kind(surface_rects.uv_rect_kind)
    671                    )
    672                }
    673            );
    674 
    675            primary_render_task_id = render_task_id;
    676 
    677            surface_descriptor = SurfaceDescriptor::new_simple(
    678                render_task_id,
    679                surface_rects.clipped_local,
    680            );
    681        }
    682        PictureCompositeMode::IntermediateSurface => {
    683            let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
    684 
    685            let is_opaque = false;
    686            let render_task_id = request_render_task(
    687                frame_state,
    688                snapshot,
    689                &surface_rects,
    690                is_opaque,
    691                &mut|rg_builder, _| {
    692                    rg_builder.add().init(
    693                        RenderTask::new_dynamic(
    694                            surface_rects.task_size,
    695                            RenderTaskKind::new_picture(
    696                                surface_rects.task_size,
    697                                surface_rects.needs_scissor_rect,
    698                                surface_rects.clipped.min,
    699                                surface_spatial_node_index,
    700                                raster_spatial_node_index,
    701                                device_pixel_scale,
    702                                None,
    703                                None,
    704                                None,
    705                                cmd_buffer_index,
    706                                can_use_shared_surface,
    707                                None,
    708                            )
    709                        ).with_uv_rect_kind(surface_rects.uv_rect_kind)
    710                    )
    711                }
    712            );
    713 
    714            primary_render_task_id = render_task_id;
    715 
    716            surface_descriptor = SurfaceDescriptor::new_simple(
    717                render_task_id,
    718                surface_rects.clipped_local,
    719            );
    720        }
    721        PictureCompositeMode::SVGFEGraph(ref filters) => {
    722            let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
    723 
    724            let prim_subregion = surface_rects.unclipped;
    725            let target_subregion = surface_rects.clipped;
    726            let source_subregion = surface_rects.source;
    727 
    728            let source_task_size = source_subregion.round_out().size().to_i32();
    729            let source_task_size = if source_task_size.width > 0 && source_task_size.height > 0 {
    730                source_task_size
    731            } else {
    732                DeviceIntSize::new(1,1)
    733            };
    734            let picture_task_id = frame_state.rg_builder.add().init(
    735                RenderTask::new_dynamic(
    736                    source_task_size,
    737                    RenderTaskKind::new_picture(
    738                        source_task_size,
    739                        surface_rects.needs_scissor_rect,
    740                        source_subregion.min,
    741                        surface_spatial_node_index,
    742                        raster_spatial_node_index,
    743                        device_pixel_scale,
    744                        None,
    745                        None,
    746                        None,
    747                        cmd_buffer_index,
    748                        can_use_shared_surface,
    749                        None,
    750                    )
    751                )
    752            );
    753 
    754            let subregion_to_device_scale_x = surface_rects.clipped_notsnapped.width() / surface_rects.clipped_local.width();
    755            let subregion_to_device_scale_y = surface_rects.clipped_notsnapped.height() / surface_rects.clipped_local.height();
    756            let subregion_to_device_offset_x = surface_rects.clipped_notsnapped.min.x - (surface_rects.clipped_local.min.x * subregion_to_device_scale_x).floor();
    757            let subregion_to_device_offset_y = surface_rects.clipped_notsnapped.min.y - (surface_rects.clipped_local.min.y * subregion_to_device_scale_y).floor();
    758 
    759            let filter_task_id = request_render_task(
    760                frame_state,
    761                snapshot,
    762                &surface_rects,
    763                false,
    764                &mut|rg_builder, gpu_buffer| {
    765                    RenderTask::new_svg_filter_graph(
    766                        filters,
    767                        rg_builder,
    768                        gpu_buffer,
    769                        data_stores,
    770                        surface_rects.uv_rect_kind,
    771                        picture_task_id,
    772                        source_subregion.cast_unit(),
    773                        target_subregion.cast_unit(),
    774                        prim_subregion.cast_unit(),
    775                        subregion_to_device_scale_x,
    776                        subregion_to_device_scale_y,
    777                        subregion_to_device_offset_x,
    778                        subregion_to_device_offset_y,
    779                    )
    780                }
    781            );
    782 
    783            primary_render_task_id = filter_task_id;
    784 
    785            surface_descriptor = SurfaceDescriptor::new_chained(
    786                picture_task_id,
    787                filter_task_id,
    788                surface_rects.clipped_local,
    789            );
    790        }
    791    }
    792 
    793    (
    794        surface_descriptor,
    795        [
    796            Some(primary_render_task_id),
    797            secondary_render_task_id,
    798        ]
    799    )
    800 }
    801 
    802 fn request_render_task(
    803    frame_state: &mut FrameBuildingState,
    804    snapshot: &Option<SnapshotInfo>,
    805    surface_rects: &SurfaceAllocInfo,
    806    is_opaque: bool,
    807    f: &mut dyn FnMut(&mut RenderTaskGraphBuilder, &mut GpuBufferBuilderF) -> RenderTaskId,
    808 ) -> RenderTaskId {
    809 
    810    let task_id = match snapshot {
    811        Some(info) => {
    812            let adjustment = AdjustedImageSource::from_rects(
    813                &info.area,
    814                &surface_rects.clipped_local.cast_unit()
    815            );
    816            let task_id = frame_state.resource_cache.render_as_image(
    817                info.key.as_image(),
    818                surface_rects.task_size,
    819                frame_state.rg_builder,
    820                &mut frame_state.frame_gpu_data.f32,
    821                is_opaque,
    822                &adjustment,
    823                f
    824            );
    825 
    826            // TODO(bug 1929809): adding the dependency in the other branch causes
    827            // a panic in reftests/blend/backdrop-filter-blend-container.yaml.
    828            // Presumably if we use backdrop filters with snapshotting it will
    829            // trigger the panic as well.
    830            frame_state.surface_builder.add_child_render_task(
    831                task_id,
    832                frame_state.rg_builder,
    833            );
    834 
    835            frame_state.image_dependencies.insert(info.key.as_image(), task_id);
    836 
    837            task_id
    838        }
    839        None => {
    840            f(
    841                frame_state.rg_builder,
    842                &mut frame_state.frame_gpu_data.f32,
    843            )
    844        }
    845    };
    846 
    847    task_id
    848 }
    849 
    850 /// Information from `get_surface_rects` about the allocated size, UV sampling
    851 /// parameters etc for an off-screen surface
    852 #[derive(Debug)]
    853 pub struct SurfaceAllocInfo {
    854    pub task_size: DeviceIntSize,
    855    pub needs_scissor_rect: bool,
    856    pub clipped: DeviceRect,
    857    pub unclipped: DeviceRect,
    858    // Only used for SVGFEGraph currently, this is the source pixels needed to
    859    // render the pixels in clipped.
    860    pub source: DeviceRect,
    861    // Only used for SVGFEGraph, this is the same as clipped before rounding.
    862    pub clipped_notsnapped: DeviceRect,
    863    pub clipped_local: PictureRect,
    864    pub uv_rect_kind: UvRectKind,
    865 }
    866 
    867 pub fn get_surface_rects(
    868    surface_index: SurfaceIndex,
    869    composite_mode: &PictureCompositeMode,
    870    parent_surface_index: SurfaceIndex,
    871    surfaces: &mut [SurfaceInfo],
    872    spatial_tree: &SpatialTree,
    873    max_surface_size: f32,
    874    force_scissor_rect: bool,
    875 ) -> Option<SurfaceAllocInfo> {
    876    let parent_surface = &surfaces[parent_surface_index.0];
    877 
    878    let local_to_parent = SpaceMapper::new_with_target(
    879        parent_surface.surface_spatial_node_index,
    880        surfaces[surface_index.0].surface_spatial_node_index,
    881        parent_surface.clipping_rect,
    882        spatial_tree,
    883    );
    884 
    885    let local_clip_rect = local_to_parent
    886        .unmap(&parent_surface.clipping_rect)
    887        .unwrap_or(PictureRect::max_rect())
    888        .cast_unit();
    889 
    890    let surface = &mut surfaces[surface_index.0];
    891 
    892    let (clipped_local, unclipped_local, source_local) = match composite_mode {
    893        PictureCompositeMode::SVGFEGraph(ref filters) => {
    894            let prim_subregion = composite_mode.get_rect(surface, None);
    895 
    896            let visible_subregion: LayoutRect =
    897                prim_subregion.cast_unit()
    898                .intersection(&local_clip_rect)
    899                .unwrap_or(PictureRect::zero())
    900                .cast_unit();
    901 
    902            if visible_subregion.is_empty() {
    903                return None;
    904            }
    905 
    906            let source_potential_subregion = get_coverage_source_svgfe(
    907                filters, visible_subregion.cast_unit());
    908            let source_subregion =
    909                source_potential_subregion
    910                .intersection(&surface.unclipped_local_rect.cast_unit())
    911                .unwrap_or(LayoutRect::zero());
    912 
    913            let coverage_subregion = source_subregion.union(&visible_subregion);
    914 
    915            (coverage_subregion.cast_unit(), prim_subregion.cast_unit(), source_subregion.cast_unit())
    916        }
    917        PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
    918            let local_prim_rect = surface.clipped_local_rect;
    919 
    920            let mut required_local_rect = local_prim_rect
    921                .intersection(&local_clip_rect)
    922                .unwrap_or(PictureRect::zero());
    923 
    924            for shadow in shadows {
    925                let (blur_radius_x, blur_radius_y) = surface.clamp_blur_radius(
    926                    shadow.blur_radius,
    927                    shadow.blur_radius,
    928                );
    929                let blur_inflation_x = blur_radius_x * BLUR_SAMPLE_SCALE;
    930                let blur_inflation_y = blur_radius_y * BLUR_SAMPLE_SCALE;
    931 
    932                let local_shadow_rect = local_prim_rect
    933                    .translate(shadow.offset.cast_unit())
    934                    .inflate(blur_inflation_x, blur_inflation_y);
    935 
    936                if let Some(clipped_shadow_rect) = local_clip_rect.intersection(&local_shadow_rect) {
    937                    let required_shadow_rect = clipped_shadow_rect.inflate(blur_inflation_x, blur_inflation_y);
    938 
    939                    let local_clipped_shadow_rect = required_shadow_rect.translate(-shadow.offset.cast_unit());
    940 
    941                    required_local_rect = required_local_rect.union(&local_clipped_shadow_rect);
    942                }
    943            }
    944 
    945            let unclipped = composite_mode.get_rect(surface, None);
    946            let clipped = required_local_rect;
    947 
    948            let clipped = match clipped.intersection(&unclipped.cast_unit()) {
    949                Some(rect) => rect,
    950                None => return None,
    951            };
    952 
    953            (clipped, unclipped, clipped)
    954        }
    955        _ => {
    956            let surface_origin = surface.clipped_local_rect.min.to_vector().cast_unit();
    957 
    958            let normalized_prim_rect = composite_mode
    959                .get_rect(surface, None)
    960                .translate(-surface_origin);
    961 
    962            let normalized_clip_rect = local_clip_rect
    963                .cast_unit()
    964                .translate(-surface_origin);
    965 
    966            let norm_clipped_rect = match normalized_prim_rect.intersection(&normalized_clip_rect) {
    967                Some(rect) => rect,
    968                None => return None,
    969            };
    970 
    971            let norm_clipped_rect = composite_mode.get_rect(surface, Some(norm_clipped_rect));
    972 
    973            let norm_clipped_rect = match norm_clipped_rect.intersection(&normalized_prim_rect) {
    974                Some(rect) => rect,
    975                None => return None,
    976            };
    977 
    978            let unclipped = normalized_prim_rect.translate(surface_origin);
    979            let clipped = norm_clipped_rect.translate(surface_origin);
    980 
    981            (clipped.cast_unit(), unclipped.cast_unit(), clipped.cast_unit())
    982        }
    983    };
    984 
    985    let (mut clipped, mut unclipped, mut source) = if surface.raster_spatial_node_index != surface.surface_spatial_node_index {
    986        assert_eq!(surface.device_pixel_scale.0, 1.0);
    987 
    988        let local_to_world = SpaceMapper::new_with_target(
    989            spatial_tree.root_reference_frame_index(),
    990            surface.surface_spatial_node_index,
    991            WorldRect::max_rect(),
    992            spatial_tree,
    993        );
    994 
    995        let clipped = local_to_world.map(&clipped_local.cast_unit()).unwrap() * surface.device_pixel_scale;
    996        let unclipped = local_to_world.map(&unclipped_local).unwrap() * surface.device_pixel_scale;
    997        let source = local_to_world.map(&source_local.cast_unit()).unwrap() * surface.device_pixel_scale;
    998 
    999        (clipped, unclipped, source)
   1000    } else {
   1001        let clipped = clipped_local.cast_unit() * surface.device_pixel_scale;
   1002        let unclipped = unclipped_local.cast_unit() * surface.device_pixel_scale;
   1003        let source = source_local.cast_unit() * surface.device_pixel_scale;
   1004 
   1005        (clipped, unclipped, source)
   1006    };
   1007    let mut clipped_snapped = clipped.round_out();
   1008    let mut source_snapped = source.round_out();
   1009 
   1010    let max_dimension =
   1011        clipped_snapped.width().max(
   1012            clipped_snapped.height().max(
   1013                source_snapped.width().max(
   1014                    source_snapped.height()
   1015                ))).ceil();
   1016    if max_dimension > max_surface_size {
   1017        let max_dimension =
   1018            clipped_local.width().max(
   1019                clipped_local.height().max(
   1020                    source_local.width().max(
   1021                        source_local.height()
   1022                    ))).ceil();
   1023        surface.raster_spatial_node_index = surface.surface_spatial_node_index;
   1024        surface.device_pixel_scale = Scale::new(max_surface_size / max_dimension);
   1025        surface.local_scale = (1.0, 1.0);
   1026 
   1027        let add_markers = profiler::thread_is_being_profiled();
   1028        if add_markers {
   1029            let new_clipped = (clipped_local.cast_unit() * surface.device_pixel_scale).round();
   1030            let new_source = (source_local.cast_unit() * surface.device_pixel_scale).round();
   1031            profiler::add_text_marker("SurfaceSizeLimited",
   1032                format!("Surface for {:?} reduced from raster {:?} (source {:?}) to local {:?} (source {:?})",
   1033                    composite_mode.kind(),
   1034                    clipped.size(), source.size(),
   1035                    new_clipped, new_source).as_str(),
   1036                Duration::from_secs_f32(new_clipped.width() * new_clipped.height() / 1000000000.0));
   1037        }
   1038 
   1039        clipped = clipped_local.cast_unit() * surface.device_pixel_scale;
   1040        unclipped = unclipped_local.cast_unit() * surface.device_pixel_scale;
   1041        source = source_local.cast_unit() * surface.device_pixel_scale;
   1042        clipped_snapped = clipped.round();
   1043        source_snapped = source.round();
   1044    }
   1045 
   1046    let task_size = clipped_snapped.size().to_i32();
   1047    let task_size = task_size.min(DeviceIntSize::new(max_surface_size as i32, max_surface_size as i32));
   1048    debug_assert!(
   1049        task_size.width <= max_surface_size as i32 &&
   1050        task_size.height <= max_surface_size as i32,
   1051        "task_size {:?} for {:?} must be within max_surface_size {}",
   1052        task_size,
   1053        composite_mode.kind(),
   1054        max_surface_size);
   1055 
   1056    let uv_rect_kind = calculate_uv_rect_kind(
   1057        clipped_snapped,
   1058        unclipped,
   1059    );
   1060 
   1061    if task_size.width == 0 || task_size.height == 0 {
   1062        return None;
   1063    }
   1064 
   1065    let needs_scissor_rect = force_scissor_rect || !clipped_local.contains_box(&surface.unclipped_local_rect);
   1066 
   1067    Some(SurfaceAllocInfo {
   1068        task_size,
   1069        needs_scissor_rect,
   1070        clipped: clipped_snapped,
   1071        unclipped,
   1072        source: source_snapped,
   1073        clipped_notsnapped: clipped,
   1074        clipped_local,
   1075        uv_rect_kind,
   1076    })
   1077 }
   1078 
   1079 pub fn calculate_uv_rect_kind(
   1080    clipped: DeviceRect,
   1081    unclipped: DeviceRect,
   1082 ) -> UvRectKind {
   1083    let top_left = calculate_screen_uv(
   1084        unclipped.top_left().cast_unit(),
   1085        clipped,
   1086    );
   1087 
   1088    let top_right = calculate_screen_uv(
   1089        unclipped.top_right().cast_unit(),
   1090        clipped,
   1091    );
   1092 
   1093    let bottom_left = calculate_screen_uv(
   1094        unclipped.bottom_left().cast_unit(),
   1095        clipped,
   1096    );
   1097 
   1098    let bottom_right = calculate_screen_uv(
   1099        unclipped.bottom_right().cast_unit(),
   1100        clipped,
   1101    );
   1102 
   1103    UvRectKind::Quad {
   1104        top_left,
   1105        top_right,
   1106        bottom_left,
   1107        bottom_right,
   1108    }
   1109 }
   1110 
   1111 /// Represents a hashable description of how a picture primitive
   1112 /// will be composited into its parent.
   1113 #[cfg_attr(feature = "capture", derive(Serialize))]
   1114 #[cfg_attr(feature = "replay", derive(Deserialize))]
   1115 #[derive(Debug, Clone, MallocSizeOf, PartialEq, Hash, Eq)]
   1116 pub enum PictureCompositeKey {
   1117    // No visual compositing effect
   1118    Identity,
   1119 
   1120    // FilterOp
   1121    Blur(Au, Au, bool, BlurEdgeMode),
   1122    Brightness(Au),
   1123    Contrast(Au),
   1124    Grayscale(Au),
   1125    HueRotate(Au),
   1126    Invert(Au),
   1127    Opacity(Au),
   1128    OpacityBinding(PropertyBindingId, Au),
   1129    Saturate(Au),
   1130    Sepia(Au),
   1131    DropShadows(Vec<(VectorKey, Au, ColorU)>),
   1132    ColorMatrix([Au; 20]),
   1133    SrgbToLinear,
   1134    LinearToSrgb,
   1135    ComponentTransfer(ItemUid),
   1136    Flood(ColorU),
   1137    SVGFEGraph(Vec<(FilterGraphNodeKey, FilterGraphOpKey)>),
   1138 
   1139    // MixBlendMode
   1140    Multiply,
   1141    Screen,
   1142    Overlay,
   1143    Darken,
   1144    Lighten,
   1145    ColorDodge,
   1146    ColorBurn,
   1147    HardLight,
   1148    SoftLight,
   1149    Difference,
   1150    Exclusion,
   1151    Hue,
   1152    Saturation,
   1153    Color,
   1154    Luminosity,
   1155    PlusLighter,
   1156 }
   1157 
   1158 impl From<Option<PictureCompositeMode>> for PictureCompositeKey {
   1159    fn from(mode: Option<PictureCompositeMode>) -> Self {
   1160        match mode {
   1161            Some(PictureCompositeMode::MixBlend(mode)) => {
   1162                match mode {
   1163                    MixBlendMode::Normal => PictureCompositeKey::Identity,
   1164                    MixBlendMode::Multiply => PictureCompositeKey::Multiply,
   1165                    MixBlendMode::Screen => PictureCompositeKey::Screen,
   1166                    MixBlendMode::Overlay => PictureCompositeKey::Overlay,
   1167                    MixBlendMode::Darken => PictureCompositeKey::Darken,
   1168                    MixBlendMode::Lighten => PictureCompositeKey::Lighten,
   1169                    MixBlendMode::ColorDodge => PictureCompositeKey::ColorDodge,
   1170                    MixBlendMode::ColorBurn => PictureCompositeKey::ColorBurn,
   1171                    MixBlendMode::HardLight => PictureCompositeKey::HardLight,
   1172                    MixBlendMode::SoftLight => PictureCompositeKey::SoftLight,
   1173                    MixBlendMode::Difference => PictureCompositeKey::Difference,
   1174                    MixBlendMode::Exclusion => PictureCompositeKey::Exclusion,
   1175                    MixBlendMode::Hue => PictureCompositeKey::Hue,
   1176                    MixBlendMode::Saturation => PictureCompositeKey::Saturation,
   1177                    MixBlendMode::Color => PictureCompositeKey::Color,
   1178                    MixBlendMode::Luminosity => PictureCompositeKey::Luminosity,
   1179                    MixBlendMode::PlusLighter => PictureCompositeKey::PlusLighter,
   1180                }
   1181            }
   1182            Some(PictureCompositeMode::Filter(op)) => {
   1183                match op {
   1184                    Filter::Blur { width, height, should_inflate, edge_mode } => {
   1185                        PictureCompositeKey::Blur(
   1186                            Au::from_f32_px(width),
   1187                            Au::from_f32_px(height),
   1188                            should_inflate,
   1189                            edge_mode,
   1190                        )
   1191                    }
   1192                    Filter::Brightness(value) => PictureCompositeKey::Brightness(Au::from_f32_px(value)),
   1193                    Filter::Contrast(value) => PictureCompositeKey::Contrast(Au::from_f32_px(value)),
   1194                    Filter::Grayscale(value) => PictureCompositeKey::Grayscale(Au::from_f32_px(value)),
   1195                    Filter::HueRotate(value) => PictureCompositeKey::HueRotate(Au::from_f32_px(value)),
   1196                    Filter::Invert(value) => PictureCompositeKey::Invert(Au::from_f32_px(value)),
   1197                    Filter::Saturate(value) => PictureCompositeKey::Saturate(Au::from_f32_px(value)),
   1198                    Filter::Sepia(value) => PictureCompositeKey::Sepia(Au::from_f32_px(value)),
   1199                    Filter::SrgbToLinear => PictureCompositeKey::SrgbToLinear,
   1200                    Filter::LinearToSrgb => PictureCompositeKey::LinearToSrgb,
   1201                    Filter::Identity => PictureCompositeKey::Identity,
   1202                    Filter::DropShadows(ref shadows) => {
   1203                        PictureCompositeKey::DropShadows(
   1204                            shadows.iter().map(|shadow| {
   1205                                (shadow.offset.into(), Au::from_f32_px(shadow.blur_radius), shadow.color.into())
   1206                            }).collect()
   1207                        )
   1208                    }
   1209                    Filter::Opacity(binding, _) => {
   1210                        match binding {
   1211                            PropertyBinding::Value(value) => {
   1212                                PictureCompositeKey::Opacity(Au::from_f32_px(value))
   1213                            }
   1214                            PropertyBinding::Binding(key, default) => {
   1215                                PictureCompositeKey::OpacityBinding(key.id, Au::from_f32_px(default))
   1216                            }
   1217                        }
   1218                    }
   1219                    Filter::ColorMatrix(values) => {
   1220                        let mut quantized_values: [Au; 20] = [Au(0); 20];
   1221                        for (value, result) in values.iter().zip(quantized_values.iter_mut()) {
   1222                            *result = Au::from_f32_px(*value);
   1223                        }
   1224                        PictureCompositeKey::ColorMatrix(quantized_values)
   1225                    }
   1226                    Filter::ComponentTransfer => unreachable!(),
   1227                    Filter::Flood(color) => PictureCompositeKey::Flood(color.into()),
   1228                    Filter::SVGGraphNode(_node, _op) => unreachable!(),
   1229                }
   1230            }
   1231            Some(PictureCompositeMode::ComponentTransferFilter(handle)) => {
   1232                PictureCompositeKey::ComponentTransfer(handle.uid())
   1233            }
   1234            Some(PictureCompositeMode::SVGFEGraph(filter_nodes)) => {
   1235                PictureCompositeKey::SVGFEGraph(
   1236                    filter_nodes.into_iter().map(|(node, op)| {
   1237                        (node.into(), op.into())
   1238                    }).collect())
   1239            }
   1240            Some(PictureCompositeMode::Blit(_)) |
   1241            Some(PictureCompositeMode::TileCache { .. }) |
   1242            Some(PictureCompositeMode::IntermediateSurface) |
   1243            None => {
   1244                PictureCompositeKey::Identity
   1245            }
   1246        }
   1247    }
   1248 }