From eeb9768c4f823984cf972825ba3bb2748e9ec9d8 Mon Sep 17 00:00:00 2001 From: Universe Date: Tue, 25 Aug 2026 19:02:36 +0900 Subject: [PATCH] websem: close the SVG component-transfer rows --- crates/n0/src/drawlist.rs | 7 +- crates/n0/src/glyphless.rs | 10 + crates/n0/src/paint.rs | 173 ++++++++--- crates/n0_cli/README.md | 43 ++- crates/rframe/src/filter.rs | 53 +++- crates/rframe/src/lib.rs | 4 +- crates/rframe/tests/filter_contract.rs | 39 ++- crates/websem/src/svg.rs | 249 ++++++++++++++- crates/websem/tests/filter_contract.rs | 189 +++++++++++- crates/websem/tests/unsupported_corpus.rs | 15 + docs/wg/consolidation/svg-engine-of-record.md | 108 ++++++- docs/wg/consolidation/web-checklist.md | 43 ++- fixtures/web-first/README.md | 3 + fixtures/web-first/STATUS.md | 39 ++- ...filter-component-transfer-alpha-region.png | Bin 0 -> 242 bytes .../svg-filter-component-transfer-alpha.png | Bin 0 -> 245 bytes ...g-filter-component-transfer-blur-order.png | Bin 0 -> 2289 bytes ...ilter-component-transfer-channel-types.png | Bin 0 -> 260 bytes ...lter-component-transfer-child-dispatch.png | Bin 0 -> 216 bytes .../svg-filter-component-transfer-circle.png | Bin 0 -> 1080 bytes .../svg-filter-component-transfer-clamp.png | Bin 0 -> 189 bytes ...filter-component-transfer-color-linear.png | Bin 0 -> 979 bytes ...g-filter-component-transfer-color-srgb.png | Bin 0 -> 1007 bytes ...ter-component-transfer-composite-merge.png | Bin 0 -> 1069 bytes ...svg-filter-component-transfer-discrete.png | Bin 0 -> 217 bytes .../svg-filter-component-transfer-gamma.png | Bin 0 -> 988 bytes ...vg-filter-component-transfer-generated.png | Bin 0 -> 1279 bytes .../svg-filter-component-transfer-inputs.png | Bin 0 -> 167 bytes ...lter-component-transfer-invalid-values.png | Bin 0 -> 205 bytes .../svg-filter-component-transfer-linear.png | Bin 0 -> 1010 bytes ...filter-component-transfer-matrix-order.png | Bin 0 -> 211 bytes ...filter-component-transfer-number-order.png | Bin 0 -> 218 bytes ...filter-component-transfer-offset-order.png | Bin 0 -> 643 bytes ...filter-component-transfer-opacity-clip.png | Bin 0 -> 975 bytes .../svg-filter-component-transfer-path.png | Bin 0 -> 830 bytes ...ter-component-transfer-primitive-units.png | Bin 0 -> 207 bytes ...lter-component-transfer-scalar-grammar.png | Bin 0 -> 241 bytes ...filter-component-transfer-shadow-order.png | Bin 0 -> 842 bytes .../svg-filter-component-transfer-stroke.png | Bin 0 -> 1405 bytes ...component-transfer-table-interpolation.png | Bin 0 -> 1012 bytes ...g-filter-component-transfer-table-list.png | Bin 0 -> 240 bytes ...er-component-transfer-table-truncation.png | Bin 0 -> 916 bytes ...vg-filter-component-transfer-transform.png | Bin 0 -> 607 bytes ...filter-component-transfer-type-grammar.png | Bin 0 -> 205 bytes .../svg-filter-component-transfer-use.png | Bin 0 -> 669 bytes .../svg-filter-component-transfer-viewbox.png | Bin 0 -> 998 bytes fixtures/web-first/oracle-bake.json | 290 +++++++++++++++++- fixtures/web-first/primitives.json | 256 ++++++++++++++++ ...filter-component-transfer-alpha-region.svg | 1 + .../svg-filter-component-transfer-alpha.svg | 1 + ...g-filter-component-transfer-blur-order.svg | 1 + ...ilter-component-transfer-channel-types.svg | 13 + ...lter-component-transfer-child-dispatch.svg | 13 + .../svg-filter-component-transfer-circle.svg | 1 + .../svg-filter-component-transfer-clamp.svg | 1 + ...filter-component-transfer-color-linear.svg | 1 + ...g-filter-component-transfer-color-srgb.svg | 1 + ...ter-component-transfer-composite-merge.svg | 1 + ...svg-filter-component-transfer-discrete.svg | 1 + .../svg-filter-component-transfer-gamma.svg | 1 + ...vg-filter-component-transfer-generated.svg | 1 + .../svg-filter-component-transfer-inputs.svg | 1 + ...lter-component-transfer-invalid-values.svg | 13 + .../svg-filter-component-transfer-linear.svg | 1 + ...filter-component-transfer-matrix-order.svg | 1 + ...filter-component-transfer-number-order.svg | 13 + ...filter-component-transfer-offset-order.svg | 1 + ...filter-component-transfer-opacity-clip.svg | 1 + .../svg-filter-component-transfer-path.svg | 1 + ...ter-component-transfer-primitive-units.svg | 1 + ...lter-component-transfer-scalar-grammar.svg | 13 + ...filter-component-transfer-shadow-order.svg | 1 + .../svg-filter-component-transfer-stroke.svg | 1 + ...component-transfer-table-interpolation.svg | 1 + ...g-filter-component-transfer-table-list.svg | 13 + ...er-component-transfer-table-truncation.svg | 1 + ...vg-filter-component-transfer-transform.svg | 1 + ...filter-component-transfer-type-grammar.svg | 13 + .../svg-filter-component-transfer-use.svg | 1 + .../svg-filter-component-transfer-viewbox.svg | 1 + fixtures/web-first/unsupported/README.md | 3 + ...ponent-transfer-source-layer-precision.svg | 18 ++ ...component-transfer-transform-precision.svg | 15 + .../svg-filter-effect-stack-precision.svg | 13 + 84 files changed, 1597 insertions(+), 89 deletions(-) create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-alpha-region.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-alpha.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-blur-order.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-channel-types.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-child-dispatch.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-circle.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-clamp.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-color-linear.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-color-srgb.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-composite-merge.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-discrete.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-gamma.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-generated.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-inputs.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-invalid-values.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-linear.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-matrix-order.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-number-order.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-offset-order.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-opacity-clip.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-path.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-primitive-units.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-scalar-grammar.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-shadow-order.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-stroke.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-table-interpolation.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-table-list.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-table-truncation.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-transform.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-type-grammar.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-use.png create mode 100644 fixtures/web-first/chromium/svg-filter-component-transfer-viewbox.png create mode 100644 fixtures/web-first/svg-filter-component-transfer-alpha-region.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-alpha.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-blur-order.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-channel-types.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-child-dispatch.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-circle.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-clamp.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-color-linear.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-color-srgb.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-composite-merge.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-discrete.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-gamma.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-generated.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-inputs.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-invalid-values.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-linear.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-matrix-order.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-number-order.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-offset-order.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-opacity-clip.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-path.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-primitive-units.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-scalar-grammar.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-shadow-order.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-stroke.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-table-interpolation.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-table-list.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-table-truncation.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-transform.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-type-grammar.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-use.svg create mode 100644 fixtures/web-first/svg-filter-component-transfer-viewbox.svg create mode 100644 fixtures/web-first/unsupported/svg-filter-component-transfer-source-layer-precision.svg create mode 100644 fixtures/web-first/unsupported/svg-filter-component-transfer-transform-precision.svg create mode 100644 fixtures/web-first/unsupported/svg-filter-effect-stack-precision.svg diff --git a/crates/n0/src/drawlist.rs b/crates/n0/src/drawlist.rs index 8d647465..2f8c37d1 100644 --- a/crates/n0/src/drawlist.rs +++ b/crates/n0/src/drawlist.rs @@ -79,7 +79,7 @@ pub(crate) enum ResolvedFilterComposite { } /// The private filter-operation vocabulary admitted by the painter. -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub(crate) enum ResolvedFilterPrimitive { GaussianBlur { sigma_x: f32, @@ -105,6 +105,11 @@ pub(crate) enum ResolvedFilterPrimitive { ColorMatrix { matrix: [f32; 20], }, + ComponentTransfer { + /// R, G, B, then A. The source-neutral frame contract names this + /// order before projection into the private drawlist. + tables: Arc<[[u8; 256]; 4]>, + }, Merge, } diff --git a/crates/n0/src/glyphless.rs b/crates/n0/src/glyphless.rs index a29c5978..6e6a5c0f 100644 --- a/crates/n0/src/glyphless.rs +++ b/crates/n0/src/glyphless.rs @@ -925,6 +925,16 @@ fn compile_filter(program: &FilterProgram, region: math2::Rectangle) -> Resolved FilterPrimitive::ColorMatrix { matrix } => { ResolvedFilterPrimitive::ColorMatrix { matrix } } + FilterPrimitive::ComponentTransfer { tables } => { + ResolvedFilterPrimitive::ComponentTransfer { + tables: Arc::new([ + *tables.red(), + *tables.green(), + *tables.blue(), + *tables.alpha(), + ]), + } + } FilterPrimitive::Merge => ResolvedFilterPrimitive::Merge, }, }) diff --git a/crates/n0/src/paint.rs b/crates/n0/src/paint.rs index 8c6a508a..786ea805 100644 --- a/crates/n0/src/paint.rs +++ b/crates/n0/src/paint.rs @@ -1909,18 +1909,18 @@ struct BuiltFilterResult { /// exact byte-domain SrcOver. Native sRGB shadows reach Skia's lowp 8888 /// restore, whose default division rounds differently on NEON and x86. requires_exact_restore: bool, - /// A matrix node exposes a second measured final-layer boundary in the - /// pinned backend. Source-derived matrix output needs an explicit floating - /// SrcOver; generated-only matrix output needs the backend default. - matrix_restore: Option, - /// Chromium snapshots source-derived matrix input through one additional - /// source-image boundary unless the matrix creates alpha from transparent - /// input. Preserve that measured boundary through later graph nodes. - matrix_source_preflatten: bool, + /// Table and matrix color operations expose a measured final-layer + /// boundary in the pinned backend. Source-derived output needs an explicit + /// floating SrcOver; generated-only output needs the backend default. + color_restore: Option, + /// Chromium snapshots source-derived table and matrix input through one + /// additional source-image boundary. Preserve that measured boundary + /// through later graph nodes. + source_preflatten: bool, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] -enum MatrixRestore { +enum ColorRestore { Default, Floating, } @@ -1931,7 +1931,7 @@ struct BuiltFilter { /// between their internal nodes. Source-derived coverage stays on Skia's /// floating restore path. restore_blender: Option, - matrix_source_preflatten: bool, + source_preflatten: bool, } fn source_alpha_filter() -> Option { @@ -1976,8 +1976,8 @@ fn convert_filter_space( requires_exact_restore: false, // Gamma conversion materializes a new floating result and ends the // matrix-specific sRGB restore boundary. - matrix_restore: None, - matrix_source_preflatten: result.matrix_source_preflatten, + color_restore: None, + source_preflatten: result.source_preflatten, }) } @@ -2112,8 +2112,8 @@ fn build_filter(filter: &ResolvedFilter) -> Result { color_space: ResolvedFilterColorSpace::Srgb, source_dependent: true, requires_exact_restore: false, - matrix_restore: None, - matrix_source_preflatten: false, + color_restore: None, + source_preflatten: false, }; let source_alpha = BuiltFilterResult { @@ -2123,8 +2123,8 @@ fn build_filter(filter: &ResolvedFilter) -> Result { color_space: ResolvedFilterColorSpace::Srgb, source_dependent: true, requires_exact_restore: false, - matrix_restore: None, - matrix_source_preflatten: false, + color_restore: None, + source_preflatten: false, }; let mut results: Vec = Vec::with_capacity(filter.nodes.len()); for node in filter.nodes.iter() { @@ -2143,38 +2143,50 @@ fn build_filter(filter: &ResolvedFilter) -> Result { let crop = Rect::from_xywh(node.region.x, node.region.y, node.region.w, node.region.h); let source_dependent = inputs.iter().any(|input| input.source_dependent); let requires_exact_restore = inputs.iter().any(|input| input.requires_exact_restore); - let inherited_matrix_restore = if inputs + let inherited_color_restore = if inputs .iter() - .any(|input| input.matrix_restore == Some(MatrixRestore::Floating)) + .any(|input| input.color_restore == Some(ColorRestore::Floating)) { - Some(MatrixRestore::Floating) + Some(ColorRestore::Floating) } else if inputs .iter() - .any(|input| input.matrix_restore == Some(MatrixRestore::Default)) + .any(|input| input.color_restore == Some(ColorRestore::Default)) { - Some(MatrixRestore::Default) + Some(ColorRestore::Default) } else { None }; - let matrix_restore = match node.primitive { + let color_restore = match node.primitive.clone() { ResolvedFilterPrimitive::ColorMatrix { matrix } => { Some(if matrix[19] > 0.0 || !source_dependent { - MatrixRestore::Default + ColorRestore::Default } else { - MatrixRestore::Floating + ColorRestore::Floating + }) + } + ResolvedFilterPrimitive::ComponentTransfer { tables } => { + Some(if tables[3][0] > 0 || !source_dependent { + ColorRestore::Default + } else { + ColorRestore::Floating }) } ResolvedFilterPrimitive::SolidColor { .. } => None, - _ => inherited_matrix_restore, + _ => inherited_color_restore, }; - let matrix_source_preflatten = inputs.iter().any(|input| input.matrix_source_preflatten) + let source_preflatten = inputs.iter().any(|input| input.source_preflatten) || matches!( &node.primitive, ResolvedFilterPrimitive::ColorMatrix { matrix } if source_dependent && matrix[19] <= 0.0 + ) + || matches!( + &node.primitive, + ResolvedFilterPrimitive::ComponentTransfer { .. } if source_dependent ); let (image_filter, output_space, source_dependent, requires_exact_restore) = match node .primitive + .clone() { ResolvedFilterPrimitive::GaussianBlur { sigma_x, sigma_y } => { let input = inputs.pop().expect("Gaussian blur has one checked input"); @@ -2392,6 +2404,32 @@ fn build_filter(filter: &ResolvedFilter) -> Result { false, ) } + ResolvedFilterPrimitive::ComponentTransfer { tables } => { + let input = inputs + .pop() + .expect("component transfer has one checked input"); + let color_filter = skia_safe::color_filters::table_argb( + Some(&tables[3]), + Some(&tables[0]), + Some(&tables[1]), + Some(&tables[2]), + ) + .ok_or_else(|| { + "the backend could not construct a component-transfer table".to_string() + })?; + let filter = + skia_safe::image_filters::color_filter(color_filter, input.image_filter, crop) + .ok_or_else(|| { + "the backend could not construct a component-transfer operation" + .to_string() + })?; + ( + Some(filter), + node.color_space, + input.source_dependent, + false, + ) + } ResolvedFilterPrimitive::Merge => { let mut inputs = inputs.into_iter(); let image_filter = if let Some(first) = inputs.next() { @@ -2439,8 +2477,8 @@ fn build_filter(filter: &ResolvedFilter) -> Result { color_space: output_space, source_dependent, requires_exact_restore, - matrix_restore, - matrix_source_preflatten, + color_restore, + source_preflatten, }); } let output = results @@ -2468,9 +2506,9 @@ fn build_filter(filter: &ResolvedFilter) -> Result { } let restore_blender = if output.requires_exact_restore { Some(exact_unorm8_blender(ResolvedFilterComposite::Over)?) - } else if output.matrix_restore == Some(MatrixRestore::Floating) { + } else if output.color_restore == Some(ColorRestore::Floating) { Some(floating_porter_duff_blender(ResolvedFilterComposite::Over)?) - } else if output.matrix_restore == Some(MatrixRestore::Default) || output.source_dependent { + } else if output.color_restore == Some(ColorRestore::Default) || output.source_dependent { None } else { Some(exact_unorm8_blender(ResolvedFilterComposite::Over)?) @@ -2478,12 +2516,12 @@ fn build_filter(filter: &ResolvedFilter) -> Result { Ok(BuiltFilter { image_filter: output.image_filter, restore_blender, - matrix_source_preflatten: output.matrix_source_preflatten, + source_preflatten: output.source_preflatten, }) } #[cfg(test)] -mod color_matrix_filter_policy_tests { +mod color_filter_policy_tests { use std::sync::Arc; use n0_model::math::RectF; @@ -2513,6 +2551,13 @@ mod color_matrix_filter_policy_tests { matrix } + fn identity_tables(alpha_zero: u8) -> Arc<[[u8; 256]; 4]> { + let identity = std::array::from_fn(|index| index as u8); + let mut alpha = identity; + alpha[0] = alpha_zero; + Arc::new([identity, identity, identity, alpha]) + } + fn source_matrix(alpha_offset: f32, color_space: ResolvedFilterColorSpace) -> ResolvedFilter { ResolvedFilter { region: REGION, @@ -2531,7 +2576,7 @@ mod color_matrix_filter_policy_tests { fn source_generated_and_alpha_creating_matrices_keep_distinct_layer_policies() { let source = build_filter(&source_matrix(0.0, ResolvedFilterColorSpace::Srgb)) .expect("source matrix builds"); - assert!(source.matrix_source_preflatten); + assert!(source.source_preflatten); assert!( source.restore_blender.is_some(), "source-derived sRGB output uses the measured floating restore" @@ -2539,7 +2584,7 @@ mod color_matrix_filter_policy_tests { let alpha_creating = build_filter(&source_matrix(0.25, ResolvedFilterColorSpace::Srgb)) .expect("alpha-creating matrix builds"); - assert!(!alpha_creating.matrix_source_preflatten); + assert!(!alpha_creating.source_preflatten); assert!(alpha_creating.restore_blender.is_none()); let generated = ResolvedFilter { @@ -2564,7 +2609,7 @@ mod color_matrix_filter_policy_tests { ]), }; let generated = build_filter(&generated).expect("generated matrix builds"); - assert!(!generated.matrix_source_preflatten); + assert!(!generated.source_preflatten); assert!( generated.restore_blender.is_none(), "generated matrix output keeps the backend-default measured restore" @@ -2572,12 +2617,55 @@ mod color_matrix_filter_policy_tests { let converted = build_filter(&source_matrix(0.0, ResolvedFilterColorSpace::LinearRgb)) .expect("linear matrix builds"); - assert!(converted.matrix_source_preflatten); + assert!(converted.source_preflatten); assert!( converted.restore_blender.is_none(), "the output gamma conversion ends the matrix-specific sRGB restore" ); } + + #[test] + fn source_generated_and_alpha_creating_tables_keep_measured_layer_policies() { + let transfer = |input, tables| ResolvedFilterNode { + inputs: Arc::from([input]), + region: REGION, + color_space: ResolvedFilterColorSpace::Srgb, + primitive: ResolvedFilterPrimitive::ComponentTransfer { tables }, + }; + let source = ResolvedFilter { + region: REGION, + nodes: Arc::from([transfer(ResolvedFilterInput::Source, identity_tables(0))]), + }; + let source = build_filter(&source).expect("source table builds"); + assert!(source.source_preflatten); + assert!(source.restore_blender.is_some()); + + let alpha_creating = ResolvedFilter { + region: REGION, + nodes: Arc::from([transfer(ResolvedFilterInput::Source, identity_tables(127))]), + }; + let alpha_creating = build_filter(&alpha_creating).expect("alpha table builds"); + assert!(alpha_creating.source_preflatten); + assert!(alpha_creating.restore_blender.is_none()); + + let generated = ResolvedFilter { + region: REGION, + nodes: Arc::from([ + ResolvedFilterNode { + inputs: Arc::from([]), + region: REGION, + color_space: ResolvedFilterColorSpace::Srgb, + primitive: ResolvedFilterPrimitive::SolidColor { + color: Color32F::new(0.2, 0.4, 0.8, 0.5).expect("unit color"), + }, + }, + transfer(ResolvedFilterInput::Node(0), identity_tables(0)), + ]), + }; + let generated = build_filter(&generated).expect("generated table builds"); + assert!(!generated.source_preflatten); + assert!(generated.restore_blender.is_none()); + } } /// Product-build preflight for a resolved image-filter graph. Replay repeats @@ -2870,7 +2958,7 @@ pub fn execute_unchecked(canvas: &Canvas, list: &DrawList, view: &Affine, Clip, MaskContent, MaskSource, - Filter { matrix_source_preflatten: bool }, + Filter { source_preflatten: bool }, } let initial_save_count = canvas.save_count(); @@ -3009,21 +3097,18 @@ pub fn execute_unchecked(canvas: &Canvas, list: &DrawList, view: &Affine, .bounds(®ion) .paint(&restore_paint); canvas.save_layer(&layer); - if built_filter.matrix_source_preflatten { + if built_filter.source_preflatten { canvas.save_layer(&SaveLayerRec::default()); } scopes.push(Scope::Filter { - matrix_source_preflatten: built_filter.matrix_source_preflatten, + source_preflatten: built_filter.source_preflatten, }); } ItemKind::EndFilter => { let scope = scopes.pop(); debug_assert!(matches!(scope, Some(Scope::Filter { .. }))); - if let Some(Scope::Filter { - matrix_source_preflatten, - }) = scope - { - if matrix_source_preflatten { + if let Some(Scope::Filter { source_preflatten }) = scope { + if source_preflatten { canvas.restore(); } // Restore the filtered layer, then the local-space hard diff --git a/crates/n0_cli/README.md b/crates/n0_cli/README.md index fc35315c..3d9e30ab 100644 --- a/crates/n0_cli/README.md +++ b/crates/n0_cli/README.md @@ -259,7 +259,7 @@ cargo run -p n0_cli --bin n0 -- \ authored result names. Its current operations are `feGaussianBlur`, integer `feOffset`, zero-input `feFlood`, all seven `feComposite` operators, ordered `feMerge`/`feMergeNode`, native one-input `feDropShadow`, and - one-input `feColorMatrix`. Inputs + one-input `feColorMatrix` and `feComponentTransfer`. Inputs resolve to `SourceGraphic`, `SourceAlpha`, the previous result, or an earlier named result before the frame; unknown values follow Chromium's measured first/previous fallback. @@ -324,6 +324,33 @@ cargo run -p n0_cli --bin n0 -- \ Fractional axis maps, reflections, exact quarter turns, target opacity, target clips, circles, and paths are Chromium-baked exact inside the admitted envelope. + Component transfer carries four independent 256-byte channel tables over + straight RGBA. Missing channel functions are identity and the last direct + child for a repeated channel wins. The complete case-sensitive `identity`, + `table`, `discrete`, `linear`, and `gamma` behavior is admitted, including + initial parameters, invalid fallback, singleton and multi-member tables, the + full SVG number-list grammar, out-of-range clamping, negative exponents, and + alpha creation/removal inside the hard primitive region. Authored function + elements, type names, and numeric lists resolve before the frame; the frame + carries only the four checked tables and one input. + Blink's ordered SVG-number normalization is observable: the source + `slope="1.654435761" intercept=".18682"` selects the upper binary32 control + and differs from the raw lexical lower control by 2,304 pixels at delta one. + Table, discrete, and gamma arithmetic then use the measured double route; + linear keeps the measured float products and sum; all four clamp and + truncate to bytes. All 256 source-byte values were probed for every kind + (measured, not celled). + Source-derived and generated inputs, SourceAlpha, both color spaces, hard + regions, both primitive unit systems, safe transforms, paths, circles, + strokes, ``, alpha, clip/opacity on separate scopes, and ordering with + blur, offset, matrix, native shadow, composite, and merge are Chromium-baked + exact. Paint-server source pixels and the unsafe source-transform envelope + refuse by two component-transfer precision names. A third generic patrol + refuses any filtered descendant under one element that combines geometric + clipping with partial opacity: even identity filters reproduce that + backend effect-stack split. Transfer-function animation remains outside the + static slice. The separately tracked fill-only ellipse/box-world boundary + is unchanged. `filterUnits` and `primitiveUnits` carry their complete case-sensitive `userSpaceOnUse | objectBoundingBox` grammars, defaults, and invalid-value fallbacks. Filter and primitive regions accept admitted finite numbers, @@ -341,13 +368,15 @@ cargo run -p n0_cli --bin n0 -- \ one-input merge has no internal composition stage, so the final restore is where its generated-only rounding is enforced. Native sRGB shadow descendants add the independently measured exact-restore case described above. ARM and - x86 are exact without a tolerance. One hundred forty-one + x86 are exact without a tolerance. One hundred seventy-three Chromium-baked filter cells are exact: twenty-six from the chassis/blur slice, - sixty from the shadow-graph rung, twenty-eight from native drop shadow, and - twenty-seven from color matrix. The complete corpus is 502 Chromium-baked - cells plus 10 sampled frames, with 131 named refusal rows. `feFlood`, - `feComposite`, `feMerge`, `feMergeNode`, `feDropShadow`, `feColorMatrix`, and - `k1`–`k4` close; `feOffset`, `feGaussianBlur`, ``, + sixty from the shadow-graph rung, twenty-eight from native drop shadow, + twenty-seven from color matrix, and thirty-two from component transfer. The + complete corpus is 534 Chromium-baked cells plus 10 sampled frames, with 134 + named refusal rows. `feFlood`, `feComposite`, `feMerge`, `feMergeNode`, + `feDropShadow`, `feColorMatrix`, `feComponentTransfer`, `feFuncR`, `feFuncG`, + `feFuncB`, `feFuncA`, `k1`–`k4`, `amplitude`, `exponent`, `intercept`, `slope`, + and `tableValues` close; `feOffset`, `feGaussianBlur`, ``, `filter`, `color-interpolation-filters`, `in`, `in2`, `operator`, `result`, `dx`, `dy`, `stdDeviation`, `flood-color`, and `flood-opacity` remain open for the named precision, applicability, resource, cascade, or value remainder. diff --git a/crates/rframe/src/filter.rs b/crates/rframe/src/filter.rs index 9e5b947b..ebef751b 100644 --- a/crates/rframe/src/filter.rs +++ b/crates/rframe/src/filter.rs @@ -49,12 +49,48 @@ pub enum FilterComposite { Arithmetic { k1: f32, k2: f32, k3: f32, k4: f32 }, } +/// Four exact byte lookup tables for one non-premultiplied RGBA operation. +/// +/// Channel order is named at construction and access, so a producer cannot +/// leak its source grammar or make the painter infer an ordering convention. +/// The fixed array shape is the contract's length check: every admitted +/// channel maps all 256 possible input bytes. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct FilterChannelTables(Arc<[[u8; 256]; 4]>); + +impl FilterChannelTables { + #[must_use] + pub fn new(red: [u8; 256], green: [u8; 256], blue: [u8; 256], alpha: [u8; 256]) -> Self { + Self(Arc::new([red, green, blue, alpha])) + } + + #[must_use] + pub fn red(&self) -> &[u8; 256] { + &self.0[0] + } + + #[must_use] + pub fn green(&self) -> &[u8; 256] { + &self.0[1] + } + + #[must_use] + pub fn blue(&self) -> &[u8; 256] { + &self.0[2] + } + + #[must_use] + pub fn alpha(&self) -> &[u8; 256] { + &self.0[3] + } +} + /// The admitted operation vocabulary of a resolved filter node. /// /// This enum grows only when a producer proves a new operation through the /// same resolved seam. It states only operations a producer has proved, not /// the eventual family. -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub enum FilterPrimitive { GaussianBlur { sigma_x: f32, @@ -88,6 +124,10 @@ pub enum FilterPrimitive { ColorMatrix { matrix: [f32; 20], }, + /// Four exact byte lookups over non-premultiplied RGBA. + ComponentTransfer { + tables: FilterChannelTables, + }, Merge, } @@ -132,8 +172,8 @@ impl FilterNode { } #[must_use] - pub const fn primitive(&self) -> FilterPrimitive { - self.primitive + pub fn primitive(&self) -> FilterPrimitive { + self.primitive.clone() } } @@ -240,7 +280,8 @@ impl FilterProgram { FilterPrimitive::GaussianBlur { .. } | FilterPrimitive::Offset { .. } | FilterPrimitive::DropShadow { .. } - | FilterPrimitive::ColorMatrix { .. } => Some(1), + | FilterPrimitive::ColorMatrix { .. } + | FilterPrimitive::ComponentTransfer { .. } => Some(1), FilterPrimitive::SolidColor { .. } => Some(0), FilterPrimitive::Composite { .. } => Some(2), FilterPrimitive::Merge => None, @@ -304,6 +345,7 @@ impl FilterProgram { | FilterPrimitive::Composite { .. } | FilterPrimitive::DropShadow { .. } | FilterPrimitive::ColorMatrix { .. } + | FilterPrimitive::ComponentTransfer { .. } | FilterPrimitive::Merge => {} } } @@ -322,12 +364,13 @@ impl FilterProgram { /// additive coefficient could create visible output. #[must_use] pub fn may_paint_transparent_input(&self) -> bool { - self.0.iter().any(|node| match node.primitive { + self.0.iter().any(|node| match node.primitive() { FilterPrimitive::SolidColor { color } => color.a() > 0.0, FilterPrimitive::Composite { operator: FilterComposite::Arithmetic { k4, .. }, } => k4 > 0.0, FilterPrimitive::ColorMatrix { matrix } => matrix[19] > 0.0, + FilterPrimitive::ComponentTransfer { tables } => tables.alpha()[0] > 0, FilterPrimitive::GaussianBlur { .. } | FilterPrimitive::Offset { .. } | FilterPrimitive::Composite { .. } diff --git a/crates/rframe/src/lib.rs b/crates/rframe/src/lib.rs index ec5930ca..fa92a585 100644 --- a/crates/rframe/src/lib.rs +++ b/crates/rframe/src/lib.rs @@ -25,8 +25,8 @@ pub use clip::{ MAX_CLIP_GEOMETRIES_PER_LAYER, MAX_CLIP_LAYERS, }; pub use filter::{ - Filter, FilterColorSpace, FilterComposite, FilterError, FilterInput, FilterNode, - FilterPrimitive, FilterProgram, FilterProgramError, MAX_FILTER_NODES, + Filter, FilterChannelTables, FilterColorSpace, FilterComposite, FilterError, FilterInput, + FilterNode, FilterPrimitive, FilterProgram, FilterProgramError, MAX_FILTER_NODES, }; pub use frame::{ Frame, FrameItem, FrameItems, FrameItemsError, FrameNode, Geometry, Identity, MAX_SCOPE_DEPTH, diff --git a/crates/rframe/tests/filter_contract.rs b/crates/rframe/tests/filter_contract.rs index cefc1e3b..452ec737 100644 --- a/crates/rframe/tests/filter_contract.rs +++ b/crates/rframe/tests/filter_contract.rs @@ -5,8 +5,8 @@ use std::sync::Arc; use math2::Rectangle; use math2::transform::AffineTransform; use rframe::{ - Filter, FilterColorSpace, FilterComposite, FilterError, FilterInput, FilterNode, - FilterPrimitive, FilterProgram, FilterProgramError, MAX_FILTER_NODES, + Filter, FilterChannelTables, FilterColorSpace, FilterComposite, FilterError, FilterInput, + FilterNode, FilterPrimitive, FilterProgram, FilterProgramError, MAX_FILTER_NODES, }; fn blur(input: FilterInput, sigma_x: f32, sigma_y: f32) -> FilterNode { @@ -253,6 +253,41 @@ fn color_matrix_has_one_input_and_only_finite_coefficients() { .expect("finite signed color coefficients are resolved facts"); } +#[test] +fn component_transfer_has_one_input_and_four_exact_named_tables() { + let region = Rectangle::from_xywh(0.0, 0.0, 10.0, 10.0); + let identity = std::array::from_fn(|index| index as u8); + let mut alpha = identity; + alpha[0] = 127; + let tables = FilterChannelTables::new(identity, [17; 256], [29; 256], alpha); + assert_eq!(tables.red()[255], 255); + assert_eq!(tables.green()[128], 17); + assert_eq!(tables.blue()[128], 29); + assert_eq!(tables.alpha()[0], 127); + + let transfer = |inputs| { + FilterNode::new( + inputs, + region, + FilterColorSpace::Srgb, + FilterPrimitive::ComponentTransfer { + tables: tables.clone(), + }, + ) + }; + assert_eq!( + FilterProgram::new(Arc::from([transfer(Arc::from([]))])), + Err(FilterProgramError::InvalidInputCount { + node: 0, + expected: 1, + actual: 0, + }) + ); + let program = FilterProgram::new(Arc::from([transfer(Arc::from([FilterInput::Source]))])) + .expect("a full four-table transfer is checked by construction"); + assert!(program.may_paint_transparent_input()); +} + #[test] fn generated_and_additive_nodes_keep_a_transparent_input_scope_alive() { let region = Rectangle::from_xywh(0.0, 0.0, 10.0, 10.0); diff --git a/crates/websem/src/svg.rs b/crates/websem/src/svg.rs index 100b6c2d..6b3e1d8b 100644 --- a/crates/websem/src/svg.rs +++ b/crates/websem/src/svg.rs @@ -135,11 +135,11 @@ use cg::{CGColor, CGColor32F}; use math2::Rectangle; use math2::transform::AffineTransform; use rframe::{ - ClipGeometry, ClipLayer, ClipPath, FillRule, Filter, FilterColorSpace, FilterComposite, - FilterInput, FilterNode, FilterPrimitive, FilterProgram, Frame, FrameItem, FrameItems, - FrameItemsError, FrameNode, Geometry, Identity, Mask, MaskMode, PaintAlphaFactor, PaintStack, - PathData, Provenance, Scope, ScopeEffect, ScopeOpacity, Stroke, StrokeCap, StrokeDash, - StrokeDashIntervals, StrokeDashIntervalsError, StrokeJoin, VisualRef, + ClipGeometry, ClipLayer, ClipPath, FillRule, Filter, FilterChannelTables, FilterColorSpace, + FilterComposite, FilterInput, FilterNode, FilterPrimitive, FilterProgram, Frame, FrameItem, + FrameItems, FrameItemsError, FrameNode, Geometry, Identity, Mask, MaskMode, PaintAlphaFactor, + PaintStack, PathData, Provenance, Scope, ScopeEffect, ScopeOpacity, Stroke, StrokeCap, + StrokeDash, StrokeDashIntervals, StrokeDashIntervalsError, StrokeJoin, VisualRef, }; use std::sync::Arc; @@ -4957,6 +4957,154 @@ mod filter_resource { } } + enum ComponentTransferFunction { + Identity, + Table(Vec), + Discrete(Vec), + Linear { + slope: f32, + intercept: f32, + }, + Gamma { + amplitude: f32, + exponent: f32, + offset: f32, + }, + } + + /// One SVG `` field through Blink's ordered number evaluation. + /// The same complete-list parser also preserves the measured lone trailing + /// comma while rejecting every form that leaves a second member. + fn component_transfer_number(element: HtmlElement<'_>, name: &str, initial: f32) -> f32 { + let Some(raw) = get_attr(element, name) else { + return initial; + }; + match crate::svg_number_list::parse(&raw).as_deref() { + Some([value]) => *value, + _ => initial, + } + } + + fn component_transfer_function(element: HtmlElement<'_>) -> ComponentTransferFunction { + match get_attr(element, "type").as_deref() { + Some("table") => ComponentTransferFunction::Table( + get_attr(element, "tableValues") + .as_deref() + .and_then(crate::svg_number_list::parse) + .unwrap_or_default(), + ), + Some("discrete") => ComponentTransferFunction::Discrete( + get_attr(element, "tableValues") + .as_deref() + .and_then(crate::svg_number_list::parse) + .unwrap_or_default(), + ), + Some("linear") => ComponentTransferFunction::Linear { + slope: component_transfer_number(element, "slope", 1.0), + intercept: component_transfer_number(element, "intercept", 0.0), + }, + Some("gamma") => ComponentTransferFunction::Gamma { + amplitude: component_transfer_number(element, "amplitude", 1.0), + exponent: component_transfer_number(element, "exponent", 1.0), + offset: component_transfer_number(element, "offset", 0.0), + }, + // Missing, empty, invalid, wrong-case, and whitespace-padded enum + // values all select the initial identity member in Blink. + _ => ComponentTransferFunction::Identity, + } + } + + fn component_transfer_byte(value: f64) -> u8 { + if value.is_nan() { + return 0; + } + value.clamp(0.0, 255.0) as u8 + } + + fn component_transfer_table(function: ComponentTransferFunction) -> [u8; 256] { + let mut table = std::array::from_fn(|index| index as u8); + match function { + ComponentTransferFunction::Identity => {} + ComponentTransferFunction::Table(values) if values.is_empty() => {} + ComponentTransferFunction::Table(values) => { + let last = values.len() - 1; + for (index, byte) in table.iter_mut().enumerate() { + let c = index as f64 / 255.0; + let position = c * last as f64; + let lower = position as usize; + let v1 = f64::from(values[lower]); + let v2 = f64::from(values[(lower + 1).min(last)]); + *byte = component_transfer_byte( + 255.0 * (v1 + (position - lower as f64) * (v2 - v1)), + ); + } + } + ComponentTransferFunction::Discrete(values) if values.is_empty() => {} + ComponentTransferFunction::Discrete(values) => { + let last = values.len() - 1; + for (index, byte) in table.iter_mut().enumerate() { + let member = ((index * values.len()) as f64 / 255.0) as usize; + *byte = component_transfer_byte(255.0 * f64::from(values[member.min(last)])); + } + } + ComponentTransferFunction::Linear { slope, intercept } => { + for (index, byte) in table.iter_mut().enumerate() { + // Both products and their sum are float expressions in + // pinned Blink before the result promotes to double for + // clamping. Retaining these three f32 roundings is + // observable for negative intercepts. + let scaled = slope * index as f32; + let translated = 255.0_f32 * intercept; + *byte = component_transfer_byte(f64::from(scaled + translated)); + } + } + ComponentTransferFunction::Gamma { + amplitude, + exponent, + offset, + } => { + let exponent = f64::from(exponent); + for (index, byte) in table.iter_mut().enumerate() { + let c = index as f64 / 255.0; + *byte = component_transfer_byte( + 255.0 * (f64::from(amplitude) * c.powf(exponent) + f64::from(offset)), + ); + } + } + } + table + } + + fn component_transfer( + primitive: HtmlElement<'_>, + override_skips: &HashMap, + ) -> Result { + let mut functions: [ComponentTransferFunction; 4] = + std::array::from_fn(|_| ComponentTransferFunction::Identity); + let mut child = primitive.first_element_child(); + while let Some(candidate) = child { + child = candidate.next_element_sibling(); + let channel = match candidate.local_name_string().as_str() { + "feFuncR" => 0, + "feFuncG" => 1, + "feFuncB" => 2, + "feFuncA" => 3, + _ => continue, + }; + if let Some(reason) = override_skips.get(&candidate.node_id()) { + return Err(CompileError::UnsupportedFilter(format!( + "the <{}> authored state is overridden at document load: {reason}", + candidate.local_name_string() + ))); + } + // Blink walks direct children in source order; a later function + // for the same channel replaces the earlier one. + functions[channel] = component_transfer_function(candidate); + } + let [red, green, blue, alpha] = functions.map(component_transfer_table); + Ok(FilterChannelTables::new(red, green, blue, alpha)) + } + const IDENTITY_COLOR_MATRIX: [f32; 20] = [ 1.0, 0.0, 0.0, 0.0, 0.0, // 0.0, 1.0, 0.0, 0.0, 0.0, // @@ -5255,6 +5403,62 @@ mod filter_resource { axis_or_quarter_turn && [a, b, c, d].into_iter().all(f32::is_finite) } + fn component_transfer_source_has_paint_server( + target: HtmlElement<'_>, + ) -> Result { + let mut stack = vec![target]; + while let Some(element) = stack.pop() { + for property in [PaintProperty::Fill, PaintProperty::Stroke] { + if matches!( + computed_paint(element, property)?.kind, + SVGPaintKind::PaintServer(_) + ) { + return Ok(true); + } + } + let mut child = element.first_element_child(); + while let Some(next) = child { + stack.push(next); + child = next.next_element_sibling(); + } + } + Ok(false) + } + + /// The pinned backend and Chromium take different byte-domain restore + /// routes when one element owns both a geometric clip and partial opacity + /// around any filtered descendant. Keeping those effects on separate + /// elements establishes a distinct, exact compositing boundary. + fn filter_crosses_same_scope_clip_opacity( + target: HtmlElement<'_>, + ) -> Result { + let mut current = Some(target); + while let Some(element) = current { + let data = element + .borrow_data() + .ok_or(CompileError::MissingComputedStyle)?; + let opacity = data.styles.primary().clone_opacity().clamp(0.0, 1.0); + if opacity > 0.0 && opacity < 1.0 && element_has_computed_clip_path(element)? { + return Ok(true); + } + current = element.traversal_parent(); + } + Ok(false) + } + + /// Source-derived component transfer is exact for finite axis maps and + /// exact quarter turns at integer translations. Fractional translation + /// and sampled general rotations expose distinct table-source raster + /// boundaries. Generated-only table inputs do not inherit this patrol. + fn component_transfer_mapping_is_admitted(target_to_frame: AffineTransform) -> bool { + let [[a, c, tx], [b, d, ty]] = target_to_frame.matrix; + let axis_or_quarter_turn = (b == 0.0 && c == 0.0) || (a == 0.0 && d == 0.0); + axis_or_quarter_turn + && [a, b, c, d, tx, ty].into_iter().all(f32::is_finite) + && tx.fract() == 0.0 + && ty.fract() == 0.0 + } + fn compile_graph( filter: HtmlElement<'_>, target: HtmlElement<'_>, @@ -5353,6 +5557,15 @@ mod filter_resource { }, ) } + "feComponentTransfer" => { + patrol_color_style(element)?; + ( + vec![resolve_input(element, "in", previous, &names)], + FilterPrimitive::ComponentTransfer { + tables: component_transfer(element, override_skips)?, + }, + ) + } "feMerge" => { patrol_color_style(element)?; let mut inputs = Vec::new(); @@ -5487,6 +5700,7 @@ mod filter_resource { } let mut source_dependencies = Vec::with_capacity(nodes.len()); let mut has_source_dependent_color_matrix = false; + let mut has_source_dependent_component_transfer = false; for node in &nodes { let source_dependent = node.inputs().iter().any(|input| match *input { FilterInput::Source | FilterInput::SourceAlpha => true, @@ -5495,6 +5709,11 @@ mod filter_resource { if source_dependent && matches!(node.primitive(), FilterPrimitive::ColorMatrix { .. }) { has_source_dependent_color_matrix = true; } + if source_dependent + && matches!(node.primitive(), FilterPrimitive::ComponentTransfer { .. }) + { + has_source_dependent_component_transfer = true; + } source_dependencies.push(source_dependent); } if has_source_dependent_color_matrix { @@ -5522,6 +5741,20 @@ mod filter_resource { )); } } + if has_source_dependent_component_transfer { + if component_transfer_source_has_paint_server(target)? { + return Err(CompileError::UnsupportedFilter( + "feComponentTransfer's source image crosses the pinned-backend table-filter paint-server precision boundary" + .to_string(), + )); + } + if !component_transfer_mapping_is_admitted(target_to_frame) { + return Err(CompileError::UnsupportedFilter( + "feComponentTransfer's target mapping crosses the pinned-backend table-filter transform precision boundary" + .to_string(), + )); + } + } if nodes.iter().any(|node| { matches!( node.primitive(), @@ -5607,6 +5840,12 @@ mod filter_resource { " href inheritance is not yet resolved".to_string(), )); } + if filter_crosses_same_scope_clip_opacity(target)? { + return Err(CompileError::UnsupportedFilter( + "a filtered target is enclosed by one element that combines clip-path with partial opacity at the pinned-backend effect-stack precision boundary" + .to_string(), + )); + } patrol_color_style(element)?; let target_box = target_box.ok_or_else(|| { diff --git a/crates/websem/tests/filter_contract.rs b/crates/websem/tests/filter_contract.rs index 3b1da435..8fc6f36e 100644 --- a/crates/websem/tests/filter_contract.rs +++ b/crates/websem/tests/filter_contract.rs @@ -323,6 +323,101 @@ fn color_matrix_types_lower_to_one_finite_checked_matrix() { assert_eq!(&luminance[15..], &[0.2125, 0.7154, 0.0721, 0.0, 0.0]); } +#[test] +fn component_transfer_children_lower_to_four_exact_byte_tables() { + let source = |primitive: &str| { + document(&format!( + r##" + {primitive} + "## + )) + }; + let tables = |primitive: &str| { + let frame = admit_both(&source(primitive)); + let filter = resolved_filter(&frame); + assert_eq!(filter.program().iter().count(), 1, "one transfer node"); + let node = filter.program().iter().next().expect("one transfer node"); + assert_eq!(node.inputs(), [FilterInput::Source]); + assert_eq!(node.color_space(), FilterColorSpace::Srgb); + let FilterPrimitive::ComponentTransfer { tables } = node.primitive() else { + panic!("the authored function vocabulary resolves before the frame") + }; + tables + }; + + let identity = std::array::from_fn(|index| index as u8); + for primitive in [ + r##""##, + r##""##, + r##""##, + r##""##, + r##""##, + ] { + let actual = tables(primitive); + assert_eq!(actual.red(), &identity, "{primitive}"); + assert_eq!(actual.green(), &identity, "{primitive}"); + assert_eq!(actual.blue(), &identity, "{primitive}"); + assert_eq!(actual.alpha(), &identity, "{primitive}"); + } + + let actual = tables( + r##" + + + + + "##, + ); + assert_eq!( + (actual.red()[175], actual.red()[185], actual.red()[195]), + (234, 243, 252) + ); + assert_eq!( + ( + actual.green()[84], + actual.green()[85], + actual.green()[169], + actual.green()[170] + ), + (255, 127, 127, 0) + ); + assert_eq!( + (actual.blue()[0], actual.blue()[127], actual.blue()[255]), + (28, 120, 214) + ); + assert_eq!( + (actual.alpha()[0], actual.alpha()[127], actual.alpha()[255]), + (17, 103, 229) + ); + + let ordered = tables( + r##""##, + ); + assert_eq!(ordered.red()[25], 89, "Blink's ordered SVG-number f32 wins"); + let linear = tables( + r##""##, + ); + assert_eq!(linear.green()[52], 2, "linear products and sum stay f32"); + let singleton = tables( + r##""##, + ); + assert_eq!( + singleton.red()[0], + 127, + "LUT conversion truncates, not rounds" + ); + + let duplicate = tables( + r##""##, + ); + assert_eq!( + duplicate.red(), + &identity, + "the last same-channel child wins" + ); +} + #[test] fn color_matrix_can_cross_channels_and_create_alpha_inside_its_hard_region() { let frame = admit_both(&document( @@ -394,6 +489,94 @@ fn color_matrix_precision_patrols_name_source_transform_and_spatial_boundaries() } } +#[test] +fn component_transfer_precision_patrols_name_paint_server_and_transform_boundaries() { + for (source, reason) in [ + ( + document( + r##" + + + "##, + ), + "table-filter paint-server precision boundary", + ), + ( + document( + r##" + + "##, + ), + "table-filter transform precision boundary", + ), + ] { + assert_target_skip(&source, reason); + } + + admit_both(&document( + r##" + + + + + "##, + )); +} + +#[test] +fn filtered_descendants_refuse_same_scope_clip_and_partial_opacity() { + let defs = r##" + + + + + "##; + assert_target_skip( + &document(&format!( + r##" +{defs} + + + "## + )), + "effect-stack precision boundary", + ); + + admit_both(&document(&format!( + r##"{defs} + + + "## + ))); +} + +#[test] +fn load_active_function_animation_refuses_before_authored_tables_escape() { + let source = document( + r##" + + + + + + "##, + ); + let strict = SvgFrameSource::from_standalone_svg(source.as_str(), viewport()) + .expect_err("strict must refuse"); + assert!( + strict.to_string().contains("animation element "), + "{strict}" + ); + let best = SvgFrameSource::from_standalone_svg_best_effort(source.as_str(), viewport()) + .expect("best effort declares the overridden function"); + assert!(best.degradations().iter().any(|degradation| { + degradation.action() == DegradationAction::Skipped + && degradation + .reason() + .contains(" authored state is overridden") + })); +} + #[test] fn drop_shadow_number_defaults_clamps_and_units_follow_blink() { let source = |primitive: &str, filter_extra: &str, target: &str| { @@ -685,7 +868,7 @@ fn quoted_urls_share_the_url_branch_and_lists_refuse_by_name() { } #[test] -fn same_element_order_is_clip_then_opacity_then_mask_then_filter() { +fn admitted_effect_order_is_clip_then_opacity_then_mask_then_filter() { let frame = admit_both(&document( r##" @@ -693,10 +876,10 @@ fn same_element_order_is_clip_then_opacity_then_mask_then_filter() { - + - "##, + "##, )); let tags: Vec<_> = frame .items diff --git a/crates/websem/tests/unsupported_corpus.rs b/crates/websem/tests/unsupported_corpus.rs index e25c94df..4ecfb28a 100644 --- a/crates/websem/tests/unsupported_corpus.rs +++ b/crates/websem/tests/unsupported_corpus.rs @@ -120,6 +120,16 @@ const CORPUS: &[(&str, Departure, &str)] = &[ DeclaredByBestEffort, "color-matrix transform precision boundary", ), + ( + "svg-filter-component-transfer-source-layer-precision", + DeclaredByBestEffort, + "table-filter paint-server precision boundary", + ), + ( + "svg-filter-component-transfer-transform-precision", + DeclaredByBestEffort, + "table-filter transform precision boundary", + ), ( "svg-filter-color-raw-syntax", DeclaredByBestEffort, @@ -155,6 +165,11 @@ const CORPUS: &[(&str, Departure, &str)] = &[ DeclaredByBestEffort, "native-shadow transform precision boundary", ), + ( + "svg-filter-effect-stack-precision", + DeclaredByBestEffort, + "effect-stack precision boundary", + ), ("svg-filter-external", DeclaredByBestEffort, "external"), ( "svg-filter-flood-color-syntax", diff --git a/docs/wg/consolidation/svg-engine-of-record.md b/docs/wg/consolidation/svg-engine-of-record.md index 502a399c..db870d5b 100644 --- a/docs/wg/consolidation/svg-engine-of-record.md +++ b/docs/wg/consolidation/svg-engine-of-record.md @@ -64,23 +64,24 @@ from the dated addenda below: same-document SVG filters on non-root admitted targets through a checked resolved graph of safe-kernel `feGaussianBlur`, integer `feOffset`, zero-input `feFlood`, all `feComposite` operators, ordered - `feMerge`/`feMergeNode`, native one-input `feDropShadow`, and one-input - `feColorMatrix`, with - `SourceGraphic`/`SourceAlpha`/prior and named inputs, both filter coordinate - systems and color spaces, hard regions, nesting, transforms, ``, and the - established effect order; + `feMerge`/`feMergeNode`, native one-input `feDropShadow`, one-input + `feColorMatrix`, and one-input `feComponentTransfer` with its direct + `feFuncR`/`feFuncG`/`feFuncB`/`feFuncA` children; graph inputs resolve from + `SourceGraphic`/`SourceAlpha`/prior and named results, with both filter + coordinate systems and color spaces, hard regions, nesting, transforms, + ``, and the established effect order; one declared-font, single-run `` profile; viewBox-only root sizing with the full `preserveAspectRatio` grammar; and one exact-time `` on a top-level ``. `crates/n0_cli/README.md` is the statement of record. -- **The corpus** is 502 Chromium-baked primitive cells plus 10 sampled frames. +- **The corpus** is 534 Chromium-baked primitive cells plus 10 sampled frames. All byte-exact except seven curved cells carrying a declared, geometrically confined tolerance (the native-oval/conic boundary) and four gradient cells carrying a declared one-code-value ramp-quantization tolerance (one pixel against Chromium's Skia; 18 knife-edge pixels between this engine's own macOS and Linux Skia builds; 336 ramp pixels under an isolated layer's restore; 576 after a masked ramp becomes luminance alpha). The named refusal - register has 131 rows. + register has 134 rows. - **Not claimed:** no conformance score exists or may be computed — FLIP is unratified. The FLIP record and identity-changing review are prepared, but only the owner act on gridaco/nothing#49 may authorize them and the first @@ -3026,3 +3027,96 @@ Three focused rows join the refusal register, moving it from 128 to 131. The primitive corpus moves from 475 to 502 cells; the ten exact-time sampled frames are unchanged. Only `` ticks. This records no conformance score and takes no FLIP action. + +## Rung: `feComponentTransfer` and `feFunc*` (2026-08-25) + +The verdict is CLOSE/SPLIT. `` and all four channel +function elements close for their complete static direct-child vocabulary. +The function-only `amplitude`, `exponent`, `intercept`, `slope`, and +`tableValues` attributes close with them. The shared `type`, `offset`, `in`, +`result`, primitive-region, `color-interpolation-filters`, `filter`, and +`` rows remain open for their wider applicability, cascade, resource, +host, or dynamics surface. Transfer-function animation remains outside this +static rung. No CSS property row closes. + +The resolved graph carries four independent 256-byte lookup tables over +straight RGBA and exactly one input. No authored function element, type name, +number list, result name, document node, or backend object crosses that +boundary. The existing graph continues to resolve SourceGraphic, SourceAlpha, +the previous result, and an earlier named result, and the primitive region +continues to hard-crop the output. A table that creates nonzero alpha from zero +is explicitly visible to the graph's transparent-input accounting. + +Chromium 149.0.7827.55 establishes the source vocabulary. Only direct +`feFuncR`, `feFuncG`, `feFuncB`, and `feFuncA` children participate; a nested +function is inert, and the last direct function for a repeated channel wins. +A missing channel is identity. The case-sensitive types are `identity`, +`table`, `discrete`, `linear`, and `gamma`; missing and invalid `type` use +identity. Linear defaults to slope one and intercept zero. Gamma defaults to +amplitude one, exponent one, and offset zero. Invalid scalar text uses that +parameter's initial value. Empty table/discrete lists are identity, a singleton +is active, and longer lists use the complete ordered SVG number-list grammar. + +The numeric audit carries signs, leading dots, exponents, SVG whitespace, +comma-only and mixed separators, one trailing comma, adjacent signed numbers, +and a second dot beginning another number. Malformed separators, CSS tokens, +parsed-number overflow, and non-ASCII whitespace invalidate the complete list. +Values beyond the unit interval remain active before final clamping. Negative +gamma exponents are likewise active, including their non-finite operation +result at source zero. + +Blink's source normalization order is observable. The linear source +`slope="1.654435761" intercept=".18682"` selects the upper binary32 control; +parsing the same text through the lower adjacent float changes all 2,304 source +pixels by one code value. A `.9` table member separately exposes three lookup +bytes at that normalization boundary. Table, discrete, and gamma evaluation +then follow the measured double-precision route, while linear uses the measured +float products and sum. Results clamp to the byte range and truncate. All 256 +input byte values were swept twice for every function kind (measured, not +celled). + +The committed cells carry identity and every active type; defaults and invalid +fallbacks; list interpolation, indexing, clamping, and truncation; alpha +creation and removal; SourceAlpha, generated and named inputs; both filter +color spaces; regions and both primitive unit systems; direct shapes, paths, +stroke, ``, transforms, and `viewBox`; and ordering with blur, offset, +color matrix, native shadow, composite, and merge. Keeping a geometric clip +and partial opacity on separate enclosing elements is exact. + +Three silent raster classes were quarantined before close. A source-derived +transfer over a paint-server fill or stroke differs from Chromium: the sampled +linear-gradient identity case changes 848 pixels at maximum channel delta 2, +an active transfer changes 434 at delta 3, a radial source reaches delta 115, +and a gradient stroke reaches delta 6. Fractional target translation changes +two pixels at delta 7, and a 17-degree rotation changes 58 at delta 12; the +admitted mapping envelope retains integer translation, axis maps, reflection, +and exact quarter turns. Generated-only inputs do not inherit either +source-dependent patrol (measured, not celled). + +The third class is operation-independent. When one enclosing element owns both +a geometric clip and partial opacity, identity component transfer, identity +color matrix, zero blur, zero offset, one-input merge, and a transparent +zero-shadow each reproduce the same 1,154-pixel restore difference at maximum +delta 2; an active transfer reaches 1,162 pixels at delta 3. The unfiltered +control and the split-scope clip/opacity control are exact. One generic +effect-stack refusal therefore guards every active filter rather than +mislabeling this as component-transfer grammar (measured, not celled). + +A separate direct-circle edge probe also differed without any filter. The +matching unfiltered controls place it in the already tracked fill-only +ellipse/box-world boundary, so this rung neither changes nor hides that issue +(measured, not celled). + +Thirty-two Chromium cells carry the admitted slice, all byte-exact. Every +scratch and committed source rendered through both actual command admissions, +and strict and best-effort output agree for every admitted cell. Gate +sensitivity was proved by temporarily swapping the red and blue painter +tables: `just gate` rejected twenty-eight component-transfer cells, with up to +all 4,096 pixels differing and maximum channel delta 255. Restoring the channel +order returned all 534 cells to green. + +Three focused rows join the refusal register, moving it from 131 to 134. The +primitive corpus moves from 502 to 534 cells; the ten exact-time sampled frames +are unchanged. Exactly ten checklist rows tick: the five elements and five +function-parameter attributes named above. This records no conformance score +and takes no FLIP action. diff --git a/docs/wg/consolidation/web-checklist.md b/docs/wg/consolidation/web-checklist.md index 1511b8c8..62d7ee3b 100644 --- a/docs/wg/consolidation/web-checklist.md +++ b/docs/wg/consolidation/web-checklist.md @@ -1425,7 +1425,7 @@ for attributes the platform ships ahead of the SVG 2 indexes. - [x] `` - [ ] `` - [x] `` -- [ ] `` +- [x] `` - [x] `` - [ ] `` - [ ] `` @@ -1433,10 +1433,22 @@ for attributes the platform ships ahead of the SVG 2 indexes. - [ ] `` - [x] `` - [x] `` -- [ ] `` -- [ ] `` -- [ ] `` -- [ ] `` +- [x] `` +- [x] `` +- [x] `` +- [x] `` + +> **2026-08-25 close:** component transfer carries the complete static +> direct-child vocabulary for all four channels: missing channels are +> identity, later same-channel children win, and `identity | table | +> discrete | linear | gamma` resolve through exact byte lookup tables. The +> committed Chromium evidence covers straight RGBA, alpha creation/removal, +> both filter color spaces, graph inputs, regions and units, safe transforms, +> shapes, stroke, ``, and neighboring filter operations. Paint-server +> sources and the unsafe transform envelope remain named backend-precision +> refusals; the generic same-scope clip-plus-opacity boundary is likewise +> quarantined before paint. Transfer-function animation remains a separate +> open axis. - [ ] `` - [ ] `` - [x] `` @@ -1755,7 +1767,7 @@ for attributes the platform ships ahead of the SVG 2 indexes. ### SVG attributes -- [ ] `amplitude` +- [x] `amplitude` - [ ] `aria-activedescendant` - [ ] `aria-atomic` - [ ] `aria-autocomplete` @@ -1827,7 +1839,7 @@ for attributes the platform ships ahead of the SVG 2 indexes. - [ ] `dy` - [ ] `edgeMode` - [ ] `elevation` -- [ ] `exponent` +- [x] `exponent` - [ ] `fetchpriority` - [x] `filterUnits` @@ -1846,7 +1858,7 @@ for attributes the platform ships ahead of the SVG 2 indexes. - [ ] `id` - [ ] `in` - [ ] `in2` -- [ ] `intercept` +- [x] `intercept` - [x] `k1` - [x] `k2` - [x] `k3` @@ -1990,7 +2002,7 @@ for attributes the platform ships ahead of the SVG 2 indexes. - [ ] `scale` - [ ] `seed` - [ ] `side` -- [ ] `slope` +- [x] `slope` - [ ] `spacing` - [ ] `specularConstant` - [ ] `specularExponent` @@ -2002,7 +2014,18 @@ for attributes the platform ships ahead of the SVG 2 indexes. - [ ] `surfaceScale` - [ ] `systemLanguage` - [ ] `tabindex` -- [ ] `tableValues` +- [x] `tableValues` + +> **2026-08-25 close:** the five function-parameter attributes close at their +> complete applicability to ``/`G`/`B`/`A`. `amplitude` and +> `exponent` carry gamma's initial one; `offset` remains open because it is a +> wider shared attribute. `slope` carries linear's initial one, `intercept` +> its initial zero, and `tableValues` carries the complete SVG number-list +> grammar for table/discrete, including absent, empty, singleton, multiple, +> malformed, signed, exponent, separator, trailing-comma, and out-of-range +> cases. Blink's ordered source-number normalization, function arithmetic, +> clamping, and byte truncation are Chromium-gated. The wider shared `type` +> row remains open. - [ ] `target` - [ ] `targetX` - [ ] `targetY` diff --git a/fixtures/web-first/README.md b/fixtures/web-first/README.md index 369aca64..c8b0fbcb 100644 --- a/fixtures/web-first/README.md +++ b/fixtures/web-first/README.md @@ -201,6 +201,9 @@ is exactly what the engine renders pixel-for-pixel. | `svg-filter-color-matrix-{type-grammar,matrix-count,number-list,invalid-number-list,saturate-grammar,hue-grammar,luminance-values,number-underflow}.svg` | The complete static source grammar in eight multi-panel cells: missing and every invalid `type` fall back to `matrix`; the enum itself stays case-sensitive; matrix requires exactly twenty values, saturation and hue require one, and luminance ignores the attribute. SVG comma-wsp, one trailing comma, leading signs and exponents, adjacent signed values, and a second dot starting a number are active; malformed separators, CSS tokens, overflow, and non-ASCII whitespace clear the whole list. Saturation is not clamped and hue keeps the measured large-angle float route. The direct-shape re-probe corrected the first scratch note before celling: wrong-case and padded hue spellings are invalid, not trimmed. All eight are byte-exact. | | `svg-filter-color-matrix-{identity,rgb-scale,alpha-scale,alpha-create,source-alpha,saturate-negative,saturate-over,hue-quarter,hue-large,luminance,color-linear,flood-composite-input,generated-flood,axis-fractional,quarter-turn,reflection,target-opacity,target-clip,path}.svg` | One checked non-premultiplied RGBA matrix: channel and alpha scaling/crossing, alpha creation across the hard region, SourceAlpha and generated graph inputs, both color spaces, unclamped convenience matrices, the large-angle no-modulo witness, fractional axis mapping, exact quarter turn, reflection, opacity, clip, and two geometry classes. All nineteen are byte-exact. | | *(measured, not celled — color-matrix split)* | Twelve twice-captured Chromium matrices measured grammar, straight-channel arithmetic, alpha creation, color conversion, graph routing, source provenance, geometry, transforms, and neighboring operations. Source- and generated-input restores remain observably distinct even when their transparent PNG samples are equal: the sampled source→matrix white composite is `[209,194,168,255]`, generated flood→matrix is `[209,193,168,255]`, and both transparent samples are `[123,80,6,89]`. Curved strokes, translucent anti-aliased fills, paint-server fills, descendant opacity, overlapping source groups, non-quarter rotations, and source-derived matrix-plus-blur/shadow graphs exposed silent edge differences; three stable refusal rows now guard those classes. The admitted profile deliberately over-refuses exact controls. Every committed candidate rendered through the actual CLI. Adding `0.25` to the first painter coefficient made twenty-five cells fail, up to 2,304 pixels and maximum delta 61; restoration returned all 502 cells to green. The corpus is now 502 cells plus 10 sampled frames, and the named register has 131 rows. | +| `svg-filter-component-transfer-{channel-types,child-dispatch,clamp,discrete,gamma,invalid-values,linear,number-order,scalar-grammar,table-interpolation,table-list,table-truncation,type-grammar}.svg` | The complete static source vocabulary and arithmetic: four independent channels; direct-child and last-channel-wins dispatch; missing/invalid identity fallback; every identity/table/discrete/linear/gamma branch and initial parameter; singleton and multi-member tables; the ordered SVG number-list grammar; out-of-range and negative-exponent behavior; Blink's binary32 source-normalization witness; function-specific evaluation precision; byte clamping and truncation. All thirteen are byte-exact. | +| `svg-filter-component-transfer-{alpha-region,alpha,blur-order,circle,color-linear,color-srgb,composite-merge,generated,inputs,matrix-order,offset-order,opacity-clip,path,primitive-units,shadow-order,stroke,transform,use,viewbox}.svg` | Graph and composition evidence: alpha creation/removal and hard regions; first, SourceAlpha, previous, named, and generated inputs; both filter color spaces and primitive unit systems; paths, circles, stroke, transforms, ``, and `viewBox`; split-scope clip/opacity; and ordering with blur, offset, color matrix, native shadow, composite, and merge. All nineteen are byte-exact. | +| *(measured, not celled — component-transfer split)* | Scratch matrices used Chromium 149.0.7827.55 through the shared capture module, and every candidate also rendered through both actual CLI admissions. Twice-repeated sweeps over all 256 source bytes established four lookup tables: table/discrete/gamma use the measured double route, linear keeps float products and sum, and every branch clamps then truncates. The `1.654435761` slope alias changes 2,304 pixels at Δ1 when normalized to the wrong adjacent binary32 value; a `.9` table member exposes three differing entries. Source paint servers differ at 210–848 pixels, up to Δ115, while fractional translation differs at 2px/Δ7 and a 17-degree rotation at 58px/Δ12; two stable component-transfer refusals guard those classes. A separate same-scope clip-plus-partial-opacity audit reproduced one 1,154px/Δ2 restore split through six identity or zero filters and now has one generic effect-stack refusal; splitting the two effects across nested elements is exact. Unfiltered direct-circle controls reproduced the separately tracked fill-only ellipse/box-world edge difference, so this rung does not fold that issue in. Swapping the painter's red and blue tables made twenty-eight of the thirty-two new cells fail, up to all 4,096 pixels and Δ255; restoration returned all 534 cells to green. The corpus is now 534 cells plus 10 sampled frames, and the named register has 134 rows. | | `svg-gradient-linear.svg` · `svg-gradient-linear-userspace.svg` · `svg-gradient-linear-bbox-offset.svg` | The gradient rung's base cells: the default objectBoundingBox ramp, the byte-identical userSpaceOnUse equivalent (the canary for the box-inverse fold), and a bbox-relative ramp on offset geometry. | | `svg-gradient-transform.svg` · `svg-gradient-css-transform.svg` | `gradientTransform` and an author `transform` declaration are one computed value — the attribute cell and the non-quarter CSS rotation cell (the discriminating measurement: the value applies about the raw origin of gradient space, both spellings). | | `svg-gradient-spread-reflect.svg` · `svg-gradient-spread-repeat.svg` · `svg-gradient-hard-stop.svg` · `svg-gradient-stop-nonmonotonic.svg` | The ramp grammar: both non-pad spread methods with their measured seams, equal-offset hard stops rendering crisp, and non-monotonic offsets clamping to the running maximum (never sorted). | diff --git a/fixtures/web-first/STATUS.md b/fixtures/web-first/STATUS.md index 72ddafb7..959e70bb 100644 --- a/fixtures/web-first/STATUS.md +++ b/fixtures/web-first/STATUS.md @@ -19,7 +19,7 @@ Not a conformance claim: no score is computed or implied (FLIP is unratified), and the corpus enumerates constructs, not the SVG surface. -## Chromium-baked cells (502) +## Chromium-baked cells (534) Each renders byte-exact against its committed Chromium oracle (seven curved cells and four gradient ramps carry a declared, bounded @@ -125,6 +125,38 @@ to its fixture source. No new image is committed for this view. svg-filter-color-matrix-target-opacity svg-filter-color-matrix-type-grammar svg-filter-color-srgb +svg-filter-component-transfer-alpha +svg-filter-component-transfer-alpha-region +svg-filter-component-transfer-blur-order +svg-filter-component-transfer-channel-types +svg-filter-component-transfer-child-dispatch +svg-filter-component-transfer-circle +svg-filter-component-transfer-clamp +svg-filter-component-transfer-color-linear +svg-filter-component-transfer-color-srgb +svg-filter-component-transfer-composite-merge +svg-filter-component-transfer-discrete +svg-filter-component-transfer-gamma +svg-filter-component-transfer-generated +svg-filter-component-transfer-inputs +svg-filter-component-transfer-invalid-values +svg-filter-component-transfer-linear +svg-filter-component-transfer-matrix-order +svg-filter-component-transfer-number-order +svg-filter-component-transfer-offset-order +svg-filter-component-transfer-opacity-clip +svg-filter-component-transfer-path +svg-filter-component-transfer-primitive-units +svg-filter-component-transfer-scalar-grammar +svg-filter-component-transfer-shadow-order +svg-filter-component-transfer-stroke +svg-filter-component-transfer-table-interpolation +svg-filter-component-transfer-table-list +svg-filter-component-transfer-table-truncation +svg-filter-component-transfer-transform +svg-filter-component-transfer-type-grammar +svg-filter-component-transfer-use +svg-filter-component-transfer-viewbox svg-filter-composite-arithmetic-default svg-filter-composite-arithmetic-k1 svg-filter-composite-arithmetic-k2 @@ -531,7 +563,7 @@ to its fixture source. No new image is committed for this view. svg-visibility-rule-beats-attribute svg-visibility-unhide -## The refusal register (131) +## The refusal register (134) What the slice refuses, by name, in the compiler's own words — **both refuse** is a document-level contract; **declared** renders @@ -561,12 +593,15 @@ its row into the cells above. | `svg-filter-color-matrix-spatial-precision` | declared | skipped svg/circle[1]: unsupported SVG filter: a source-derived filter graph combines feColorMatrix with a spatial filter at the pinned-backend composed-operation precision boundary | | `svg-filter-color-matrix-transform-precision` | declared | skipped svg/circle[1]: unsupported SVG filter: feColorMatrix's target mapping crosses the pinned-backend color-matrix transform precision boundary | | `svg-filter-color-raw-syntax` | declared | skipped svg/rect[2]: unsupported SVG filter: color-interpolation-filters presentation attribute contains a CSS comment; the direct attribute decoder does not tokenize comments | +| `svg-filter-component-transfer-source-layer-precision` | declared | skipped svg/circle[1]: unsupported SVG filter: feComponentTransfer's source image crosses the pinned-backend table-filter paint-server precision boundary; skipped svg/circle[2]: unsupported SVG filter: feComponentTransfer's source image crosses the pinned-backend table-filter paint-server precision boundary; skipped svg/circle[3]: unsupported SVG filter: feComponentTransfer's source image crosses the pinned-backend table-filter paint-server precision boundary | +| `svg-filter-component-transfer-transform-precision` | declared | skipped svg/rect[2]: unsupported SVG filter: feComponentTransfer's target mapping crosses the pinned-backend table-filter transform precision boundary; skipped svg/rect[3]: unsupported SVG filter: feComponentTransfer's target mapping crosses the pinned-backend table-filter transform precision boundary | | `svg-filter-css-functions` | declared | skipped svg/rect[2]: unsupported SVG filter: filter presentation attribute uses CSS filter functions, which are a separate unresolved operation grammar | | `svg-filter-css-property` | declared | skipped svg/rect[2]: unsupported computed style: style attribute on declares filter, which this cascade does not represent | | `svg-filter-drop-shadow-color-precision` | declared | skipped svg/rect[2]: unsupported SVG filter: feDropShadow with an interior-channel flood color in linearRGB crosses the pinned-backend native-shadow color-conversion precision boundary | | `svg-filter-drop-shadow-range` | declared | skipped svg/rect[2]: unsupported SVG filter: feDropShadow parameters cross the admitted native-shadow range | | `svg-filter-drop-shadow-source-layer-precision` | declared | skipped svg/rect[2]: unsupported SVG filter: feDropShadow's source subtree crosses the pinned-backend native-shadow source-layer precision boundary | | `svg-filter-drop-shadow-transform-precision` | declared | skipped svg/g[1]: unsupported SVG filter: feDropShadow's target mapping crosses the pinned-backend native-shadow transform precision boundary | +| `svg-filter-effect-stack-precision` | declared | skipped svg/g[1]/rect[1]: unsupported SVG filter: a filtered target is enclosed by one element that combines clip-path with partial opacity at the pinned-backend effect-stack precision boundary | | `svg-filter-external` | declared | skipped svg/rect[2]: unsupported SVG filter: url(https://example.test/filter.svg#f) is external; this compiler owns no resource I/O | | `svg-filter-flood-color-syntax` | declared | skipped svg/rect[2]: unsupported SVG filter: feFlood flood-color is outside the admitted color slice: color space Hsl is not yet gated against Chromium | | `svg-filter-flood-css-property` | declared | declaration ignored at svg/style[1]: a stylesheet declares flood-color, which this cascade does not represent; elements it matches render without it | diff --git a/fixtures/web-first/chromium/svg-filter-component-transfer-alpha-region.png b/fixtures/web-first/chromium/svg-filter-component-transfer-alpha-region.png new file mode 100644 index 0000000000000000000000000000000000000000..61fc6669b0c050ed7f2c7d373abe9243d5247fe2 GIT binary patch literal 242 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1SD0tpLGJM<(@8%Ar*7p+%V)iXDFn?2q z(GPppW=GyE1CD?0lO<9T6MI^>Zrrx1|M0QOOM5>ac-pr8PtEERTkhv7Use-4#}H+? zZ%_RB8Rs8XNS*8dt^2HaUrhUE|Bf_ymqIbB^av!J*z{`$f&X#lk!bz5*Tl|G(x+_ai_C8_3nn3`Sd}uE@D{ RuLO!Pc)I$ztaD0e0sv5_V7&kU literal 0 HcmV?d00001 diff --git a/fixtures/web-first/chromium/svg-filter-component-transfer-blur-order.png b/fixtures/web-first/chromium/svg-filter-component-transfer-blur-order.png new file mode 100644 index 0000000000000000000000000000000000000000..f220932122e63fc816b0f60aeed4d72d1cd07270 GIT binary patch literal 2289 zcmV000Q4Nkl8cOMlP-_mxc%_@`_&SiFCU$T=p^NAok4EAZe zuIc)^X{)BGo33rUmSU*^q-aOEu6DKZUDw0KL6b#s2D1}PfHZYmw!m!Lwm-piTLl$dclO{y$_ha=XSpktW+Nr4OZMJ{iW-r_PWs|>U`7SS^03Ml~L%d?4QRpR6aF@gnRg7@J zI)=nDV#MzwtijwLVPP>HGqEJ zWWQcEKVP;_n{1Qi@IYDBwFIn*R}YcuLzpkRAx-dXU{Rt78?HAhjF|Nd^L6+1w%hFT z9i~-ChgKkgX(8-=-%Z0{mzH}#3Z})P^p0sSdVt37VJf6-lkLmbZT{O z*={q!A$&XHnaXV=xqyf;*q2O4T9HOV3Be;i)q@fR1hc5#w)vA_{&?Cv5p%z$Ml=2ak5ufcw|{M`VEX81bG?->m*pk5sHaJCs_!R zV#F*EMcYhlU$e28vT=%uN4U(D@-zMBdxD9g6jh;TN}N56Q8bY`a1ypgQ=}2EsGgam zrpd8^4fMU?PeXzin)i4Oaf>OOSg^oLb)?%4n0$n+Mcp*EH&$E5L@cx&UW2NbBABIM zBCPPdpIk775Es%h2YTQ`21Mdh)mX$ZrtO)*p3#Bz>!TQ}{Pnh;tyni2aHr_p8 zTDdS;Wk@oICNsZ4CK|9ZgDp%DFr?#H@AuiJEHMX zPFgW_0yUc=1XHWP^FXIU-$(oKNn{E1Nx<4jSP7xOzHb(zBxA=sG>wUkQ`J z8*%7k!d)7JJfe6v(mRCp2xB;I`-~i|Kp*1JL-)N;V^C{!O(P7Z#*6SI%%2i70%lH1 z%Q|JF;i7#kVPYbDl?1Yq6#+3Fj^!3jqG*B4K`Tv$j6I6Cio*L3;1?8iQm zIW06Rj(jNmxK1AL(uZ{nO^h(a0N&%aA@J5wn6F~;R@29Zfmv6#P5Y6|Ax$tj4*a`S z^tejD-K9WYlbd)ZE$4Mq&(_aXLT2Q7brOzLTqHR{{XxhU>2w&lK!3bT{(f9R*uyGG z5PJt>VY&+CPu$zwXj|V)~I_Dy5eDfRqyaPWG3e zYph~(SU{GBAP(G5HZ(B*U+eVS!wMh7Nh0w>$|c#Ru~tN8A2ebRsLvpA$(WK z^uO;{-|kmyU?Lp_)3VR+$ZRGMJm??*dLEu|Zw17_)5xT3UlrVCoNOt>;69BW*059j zuujq#Rmf+dIaG=f)AzVLkz(LVNi~?*)GTZ%p+FivuHn!4UNBL>=9p&`5X~IPAyh*K ziI@sCWSy3an|dgKq(_NZB@t2$f&+4PlWFg!GMM3rIlA;NFBdoUF)>-P5Sf65hj}n@ zg1Ml;qyxP#HO}m?n1aGjg%5r_RzVcu1*pLz5S$LV`nXP91f|~kqm;JZylzd1#~0(Thr8C-$0FwJ>QcxG8Y$ zaZ}*hRnuP zmE9z@%4gohtgq42D$D9!SDmlA$hS`5EW<2D6Xp!I3p@)X9gG{&7~lfCE+_pLnz1g1 z>GDpeEsT2`AAglz`R#V*%>4JkOKzPhfBS&zB8DEAK<<<0`4L|k>w#|n|KB&Px(3K# c19^g(L3f_eh6PGq8bA>SPgg&ebxsLQ0N~zXm;e9( literal 0 HcmV?d00001 diff --git a/fixtures/web-first/chromium/svg-filter-component-transfer-child-dispatch.png b/fixtures/web-first/chromium/svg-filter-component-transfer-child-dispatch.png new file mode 100644 index 0000000000000000000000000000000000000000..9e1f59d441acd9ddfea97179c73a0e9249292ecd GIT binary patch literal 216 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1SD0tpLGJMUQZXtkcv5PZfxXiaNu#cs1a^4 zYqCY_(qN0u#j;6>PkjsSseg!{s`WuJ)NAkj6FGfPF0oHBTYFnDHum|wXG_0-`lb85 zC4=n(&jLvYVWiTj_wQ{4rN0;rDrueW?*)Caof|NjRs hepmt-Y#@g*GyL2u>K_>MR1_$};OXk;vd$@?2>=*rPW%7> literal 0 HcmV?d00001 diff --git a/fixtures/web-first/chromium/svg-filter-component-transfer-circle.png b/fixtures/web-first/chromium/svg-filter-component-transfer-circle.png new file mode 100644 index 0000000000000000000000000000000000000000..c5d4fb3681341f86780c7a5be64b7008533e05d8 GIT binary patch literal 1080 zcmV-81jqY{P)000B+Nkl+jv&XXl*X&iUOd>+0=? zaH3T!Ae>0yA4e2mjVQnxQGhj~0BdBxE1fp`leTtCkFBTA)#G&a_Vr67px3BVH3|Ae zbyk`#-#8&trv?6y@UoS7`qpyg{-c&Qi{0(UhLctCdB&`j#WVA=4G=P+3Ut{VN9(TM zd+`eBnKym%o;6Fyg*G7E1zaBQsrrV?O^-f}eg>{YNTsXi6zp8FNFB>@&&N@~?e&$P zs4-hQfDdL&GE|k9DxxBR9xeh_hpX&ZWw*l#{E?;Co!V78R+|WvPzyY7?<_xA)9>{H z-=JAkQ?bpIIhN`wy8i)RU}*2z3*q?o2x9*YWNd^2v!OHpu5QIY)>%D;?!Z4{o_4Qk*@PiK#AD zDoG$n-7i6n_00z>Q6Cfd!=Tq4EL}%fd47hA03r|^-f^t@X3KNngF@4!{TtV!;sf+> z6o7Bd>PvT<_no`s^7w!&Xxfx5Em@qOiymfjgS)_YT!Y>NwU;k6J{Sd>rZ~DWNJ{di z?O0Y47ehDcfBl>g1-{errv3Dd#>S^Fe8FLCI7%+hH%>&wKIi`o2pN6^et=fRjmPG9 z-Mxc;|A5!$aDQzGsFeyuWTY}WYJ5ua>?x)K(?sA8o&|mfd#DW&@nsLzhytt;1y~~r yutpSM%|9>j1pom5|HT{~zyJUM21!IgR09Clol|?RYiG^?0000c`2U`07 k|ACv?JU|8;$aZFi7n51G$xOPw7AV5t>FVdQ&MBb@043c%{r~^~ literal 0 HcmV?d00001 diff --git a/fixtures/web-first/chromium/svg-filter-component-transfer-color-linear.png b/fixtures/web-first/chromium/svg-filter-component-transfer-color-linear.png new file mode 100644 index 0000000000000000000000000000000000000000..e7f621e844f73dfa57af2065f9c9901f9678832d GIT binary patch literal 979 zcmV;^11$WBP)000AsNklS?cGm-5Hf+`3G48r4LHMWwV207h=vdd_yYdUfnMO4`>&pRd>i<90)dy7{_QZM zH3tK=E>u%r?r)$-@dT?C`NP)@OFTgw2=_yPY>K3Bvd*B`Wk`oh#64>zjfOn7! z5l$fB2j21c*%S~Y1E?QGJ;X8@%Ufp_kzxAxdac&!`VIV|QAaMj4^9Fq1JN25cHg9E z)u@vZ)V&dXLq*2p7xdK<{4vbtg;G8ijh(CHhE)xZn2h2_*s02xfM zCvL4ZMJiRmF)F#Dp{xcUPavRHDz7!41CD7kwI~$|d^}lSs4}mxrMeC{!&F>Vka>z| z2ythwt)VF^ITbh{J0-oT+606U3rHkzgGC>^khP7`(I%y|hABMENN=~YqP@b(v- zW-D16a6c+?iY}aPijdOj_0tsC!~EWIulos;ei3zocCo&>Jf{%o!bpH((Y4w=)IT_G z+X;n;21ZDvC0Y6PrBzhb01RL%u!r00p&FX~wlU0sjUqyB%qy-dGO|*ovm@}AZw|X< z>eCO$Pk(Tex~Q`p*aekJsaB{WmFnY(#|kpClM|AFJy;g_AI!Fp3F4;)LL&+YjVK^A zqJYqd0zxAS2+bb=00960Nto6p00006Nkl000A|NklDXV~!6k-OXLS-2*o3(3o}5PRXbxi*&&TU9eFf zu{?0+`cVA>Z{QA!lU``hKJlWwvC`7jW*wY0yl$x>dSh7P-YBDf0kDRx0KN1lZ|d_Z znGE1WeIztP9NCDQiHhwa%E8Z`C|+`DTvG zMO+7Bfg9Q7mX1E)fx2%$QXdrqA!Gu%6-{}S&A=PaOB*sv>WMBP5EvSrNXxARp1Ja&A@v|C5MBl3Y3590KqA)YsJJ+KOswJF9^@S^kN?59(V(RUkYx00d`ckO31z3GCav0t6>| z!%|EPB@h<4a9N-p1SG`h8yet;Nn+dST@lMcKz3~iLng5Tr_5VbUK|fn|A*2`eq#3~ zWEd+DWbi#{-VA~e7agJ3Vh2P7@5IT6Odh6;Xzyqf6;IpwNAcR3T2I;Q`Vj~HNjU*s8l7QGq0%9Wxh|Mnm00960GUR+j00006 dNkl>C)H-NR#qFW!jiSadaO zN)tCP+ao5%g{@_~dKRlKGoEWc_nbqV&%Nhn`9IIqSb5jy8|~r{Zhy%ltKIn0h55f_ zNP`J3w5vziY4XLs>$Z9E3;w_U@Zo&ny413HOV{jJu&!$3<-m!?mpNW>Y(BQU=j_=t z@BJg%CqKLYb5_92vMuL6+)U+|6&O-wH7Vjse@|=l`>Oov8XupU9>?{!W5Vy=c(5tz z%njKG;$M~5tSnu$OLy0l#I28hYHU3EW8K9kn-_7O2wQOZ|HG2Yy#*?V`41${OSIXR zzxs5b;y&gc2lZrY(|vk8Y$s;$pZR#FZ2Cff8~fOX)Q6vP%e`mMX5FdKU>hCw{>~Pz zg*crpf52H0$GU0Pm&*^ARsXf!5$Atp-qof0lll*wz7Z5%!PLL& zUSrn0eaE=A7(ZPg#edkkeNX)(|M-24S-b4{AMCO_#_eGG{;})I**1+CIb3mDy;}^7 zw5IoPI4x5MYzVafI&-B&!*}M7?;2~o`Boh3;y-zOy2+=L*L{}dePR(ir^|G;pjB&K z_QE`lE6=mtgUlS(d-9 zS?A%hoPw}Zk~a+&SARdFcS0!7Yp>yidwsD%Re$%U&(1p)6EyF}_OmYq))yCed+8_J z8}AEyHtAE!JLlz!8uRowuJ~_fRQB=I-gD;i4E>uo{Rv2RR{VErV#Jkt|7UR=YhU%; zbWn9@&x`QJT@Q%Xh7F+}~c|CQHMb9|?6w(!qqFC}kH z`LF-&_3dc)jx>g|46_(bm^0We@GOvYFm51DAnGII)a{oIm^1W&4*LIJlR;h-$Y2Ax ajG3Ws9e;v~-$omt2!p4qpUXO@geCx1{YTdT literal 0 HcmV?d00001 diff --git a/fixtures/web-first/chromium/svg-filter-component-transfer-gamma.png b/fixtures/web-first/chromium/svg-filter-component-transfer-gamma.png new file mode 100644 index 0000000000000000000000000000000000000000..526b0e9676c827cd022683b3327437f8a4d13af9 GIT binary patch literal 988 zcmV<210(#2P)000A#Nkl{K zXSQ|9V&@#YQO?B$CCgf7#YSc z<{IOzpd#;jzaW#S3Uu<_;sGks9`9<4cVUt!fmhsnP@UT7TTBdf3pJ^G{|wO_Ys17) z0zJYnP@M>m`cb4nFQ`r=DW3qF)IcDLdljH2aRw#x)>%f3nEt)TWT^V1U`~El`b$x@7v3 zPZoi{eB!=Vg000EENkli9(+^+*j*sW%z9OX<930^y0pr zWyEOP_!H3Lm7&rvM_#~G5l-oaj8;tQc`j7i68;1<)id*0W8=E|7RE?P=-JGyTMi@B z7;P3G0(7Qzcw_Orpy>s#!~Ubc2FJX-BO_H<`d^Y26I46p^o2jBw z*xJTj00QtOZ_!rKX?(`sz~Hh{;sD)J6c5oq z*z5%G%7G{v2QC6|@7uga@=*{+#(|3fBygAmBpFfKK?paFT?EKEevlWM$1VbbC2;Eo z(b_zA5`fJ9-U;05;iMwzu)MWL&gqL-W{t{D0%UcdCF@&T0T?`31CVqutnaMvZ3W0V zeUp@i54E1L6(HyM$y@x7!Eyn1J1ASn48SJK1xS^^nP4Ry7yEN3mkW?}{2VQwwCe$> z6F4h;asU2;opVL-Kzjs>jK(^x0_?hKy;uVq8h2T8l~wkh33<7li>jr1BV383Y%MSXf#GT4MS93iB}E0ph@W2Zw60Xo6RS4s6ZWw z6By!Ji`8nZ3^Axx(Z=y|`)RQUmeOTy%i$Fm^YsUCwML&H zY)@(8=_`Q*;L;($OG_5)4<@7|ajMO_dL&qt5Fv<$fQ5F(C3Ck$mn z1i_nmUtZx_)QbZ(V5{2p00Zh(mA6pRrgL*$5#aZc!2Q_!#be`D3NRVdb^T_lz!&$& zzH2MSux>@xPZZTk2U;t&G{oUKxbC&R=m&;H<1yfE6rlVV}|0Bju{aXMNJ)=VDr_5Nwt@ z`b_FEM|AVr-RFPYl$kHQg7XSX7L#e?EC=5OY5^p0zX6T^|G)X5dH|5Y2C|2lLAr}6 T$tTDiB*Nh7>gTe~DWM4ffqgOU literal 0 HcmV?d00001 diff --git a/fixtures/web-first/chromium/svg-filter-component-transfer-invalid-values.png b/fixtures/web-first/chromium/svg-filter-component-transfer-invalid-values.png new file mode 100644 index 0000000000000000000000000000000000000000..40e6259a4bbabaa4740981c03ef5cb7e2d03bff6 GIT binary patch literal 205 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1SD0tpLGJMCQlc~kcv5PZfxW{>>$E=aC2bc zx+9?$C8Fz&EahD2FjGq)iZ5+ZH=kJDmuaQ9kDr;BTy&XT=lSce|N6ZD*3Xx=D|a$w z#16N%ms+VUNQ+y~xdJfcuwuLBfe@O1Ta JS?83{1OVC3K==Rv literal 0 HcmV?d00001 diff --git a/fixtures/web-first/chromium/svg-filter-component-transfer-linear.png b/fixtures/web-first/chromium/svg-filter-component-transfer-linear.png new file mode 100644 index 0000000000000000000000000000000000000000..bac132af9e29e45ba02ea3eb9e6775ca3a14f8e0 GIT binary patch literal 1010 zcmV000B0NklAZz6A9lR16{_qM|VJ zLl+29=|Y6g6rIfwrNl(d%jT_3r<={q4ha!d`n>0waDJcv@Am%oob#Udea;jePB-vL zbfgyeLl08a4@jCNPHyBs~9wr|6oo}qTPjH2{$21BgD z5EnT=bwS+bcuk}RxP#(kFErxvHr%tfzit`$+&695lpM3vnvi-tO=r>pYuF0VQ?I{O z-B{=t_m2||RBDuKkF873$zZ9$T!8V^e`iu7<6LIAF&r4d@E!;SLiPe1J$qnBz!#{x zQ5hHwSg-GrE19e)Oe!$u_LXEkb=o_DI~H1E4?oXhaDNj^hk~J!BTu-UJZAz%wt; zmwH{p_&k9?ZGJ^?jQNU*gkG0yx#jpgR$!>Z-SoH~1flMJExrg?f!ciAL~sHGfiA+k zD{qlu?1RkZ=mr64D{n<6Q5A5v47k4xfPnNj_xB_Ag@6=jEo%nB`ShX*6GL4>E`FX| zQ{!yt!NgDkqrD>_I9{h06Z?+^a4RWN0QF;Vi?;{$rKp=&uIBjY%pnR)|2`r{*WWw_ z!lI6z)jK$S5l|J-&gJ0rO-gw5EKCe_ZD{B5`QiY3GiuB5FW3r9GSua< z<&}SUV8^^}QmU1SSt-fKQ?;fDU=23{zo6e!G(KzSs_pa-`g|V0-{YH@0M%@jN~=~y zYE0`En0F>br^Enva4hgAm}4Ouq`w-7jU*s8l7QGq0%9Wxh>av5Ha`FW0RR6M+%iD` g000I_L_t&o0OevorQ-6qaR2}S07*qoM6N<$f`pREz5oCK literal 0 HcmV?d00001 diff --git a/fixtures/web-first/chromium/svg-filter-component-transfer-matrix-order.png b/fixtures/web-first/chromium/svg-filter-component-transfer-matrix-order.png new file mode 100644 index 0000000000000000000000000000000000000000..9608a2b7b7ee796b06a0a1eb1b880317c1687d10 GIT binary patch literal 211 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1SD0tpLGJM4o?@ykcv5PZf)dbaujg57`o2F zSb8Iiv-bu;V{hS|4<7CQ@nG|ZxNXurH8TpP-LIE5-raYSa|HzIwts*7Q7Zk%zWMF* ze|t{0|9vuSh1vq*pjqvKqgRuB9DKnrmzja#|NrLuEBJtJVgtE}nIY>5Pr_PJEk&RR NgQu&X%Q~loCIG>!O)>xg literal 0 HcmV?d00001 diff --git a/fixtures/web-first/chromium/svg-filter-component-transfer-number-order.png b/fixtures/web-first/chromium/svg-filter-component-transfer-number-order.png new file mode 100644 index 0000000000000000000000000000000000000000..aaf7f7e56878c398d3efe2301928213b895f007c GIT binary patch literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1SD0tpLGJMeoq(2kcv5PZWwYk7znUl)Hry_ z_|?fTJz+{>WwV&q_=r6VUCG4k`T2F_UDeGSRi@i5*tfiNmFksiHs}89edoN$vp~|p zxFL<Jp%_bg zNDnigg}e*>J6LgmBjkB^)^JVJ=*&IzqqJ{yd8t(mhw|v4_a4dWkuiS{dgt(-$CyT zzvWX7+$xhO=aQe>AS1V&`TOarSB49NYlM$Kn$Ek{@#kEDm^%_L4ldxFo7DL2-o=|! zeFILOxx~ii`$B}{fv*?`pR*H7ld^>Qu3!ZNgN*{6aSl!{i=IRHCZbHdJ!jWS+|{%= zG0nW(k@@rLXt#1MmUBvh7aEdc?w{NJFZJBqd2CVvJ+E(T3RKLS$p~ehsGrFTW&W7- z|J1xHexXA%(~j@BIG2gB`qkh4_y0@~_}}#hWX&JVFTYe;JH4=war?_czaL%efy_VMa8xDedT?%WyRm+|Ed_~bNTAulQ>XZ!&mdKI;Vst E08VQaLI3~& literal 0 HcmV?d00001 diff --git a/fixtures/web-first/chromium/svg-filter-component-transfer-opacity-clip.png b/fixtures/web-first/chromium/svg-filter-component-transfer-opacity-clip.png new file mode 100644 index 0000000000000000000000000000000000000000..9b8d165970afc48eb5e4580d419cf844719d4fe4 GIT binary patch literal 975 zcmV;=12FuFP)000AoNklE^7}R<18?wffM%R%?A}tE~@x&|0Q0v*}DG zrp81G6cs@bP(aMv+1T7d#d9wAWp4L3#upyA=XcI?4(B{a8Qe;MkXVd*5E2Dka%2Ii zkp-ki7LXcQKx$+Gsk!t5O5l}5djD4-zsL)*R2nl9S70x_4vymgJP-3E|vrC zD#U0tAR;;qjTwn6P^Hs?h%g%5{=%IDj25>`c!B>BKxc3ekw&u-GZI(eMvDzqL_ieK zgIo?9W-zWmZB13P#SEg*U^eP`Pa%ZM(FCG^3V8Uz1OkIyE)&Cmz!(_aZI=^2o-d+67&XEQacKJ6WO7!LD5CB@GeB9pygb)i@&Bhn^ z2T&~yctt@`PX_wx3^hOqmv%Utt&eVX1Fzh>+0kOH19B*I2LYV$g~G3f$Fli+>97lT zqbPHp4)&u<1KSW^8q}=v8Ky7s0c@X)sTQoRKfOrA8Kz8d*SUWC5v> x1*Aq6keYu000960XR6Ew00006Nkl4&tyl56r+Prj3K-YZt`mXZ6U3l;5-TYZWrO#AWoaEFf z+QE>GiA)wSJv%GA{Q7rok$WG1o%S;QYI^ng`MG-|f@_}!*=qQ9Hu3r@{dCV3y61m> zC)2_IY_C?Wo;6!4KX!juWE>mU!Wjpw58ugtt5T$(rmcM{I{*Hj>cxUn92gr@UVV6Z zU_oMPZ&t|VtEb=Uo)ulXRDpjd)9;Nx4zIJDoz4FubH!@eiYrCGrmR^Oc`aCDmc@?i z|GaL!5?pDOd8XF+_#yOW*6>Yw`;Og0-{nD$jQm3StSHyY8NURlt;O3;Ubya()TmPLZGG z{(?u7>(_y6>l(k@S68tBpNKbnCH+25a+QmXJAAr?ZhZTZuDfdfzsF~ya+K;*JB41% zpK-3?P`;i`#;%8wi)W}rGVHB#@8zCQ{-sIM*SBXP>z2<>D$^SF2zMU+>YbLv1&$u6le1T7Sv3Z* zSv1~oob>SL$tFL+C^oeQVFR7@#y0km7noRnivhVa45Jq^%s6g#f5++A$yfPU57?AR zT9zm13ly;*4q&J_Ea)?PT5!Xv)dA86CvN8Ha%Q;P^<+&WlV*d?4DH2}QoN3;$v)&i z?%?}le$ASPOejdRs<9rJ`2YWx{Bqn6$Y28{e`bd3zg#NnEA!ofA`G6celF{r5}E*I Clxs%- literal 0 HcmV?d00001 diff --git a/fixtures/web-first/chromium/svg-filter-component-transfer-primitive-units.png b/fixtures/web-first/chromium/svg-filter-component-transfer-primitive-units.png new file mode 100644 index 0000000000000000000000000000000000000000..bf9fb5ef33af150cc76d56400728a0b317681fd0 GIT binary patch literal 207 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1SD0tpLGJM7Ec$)kcv5PZf@jlFyLVgY}VPR z86055I+se$(ESYWn{Qo%8*-YihPgI)m=*lPQybUJ0J00092NklqI_pOaGT=a?5hbjmsJ^Eo;EEtO3)q229HuFfD7qw5$QsqO(`qww)cT$Z?$6@dxqg zEV|{wx29?OD?|;AudeH|Z*r#LltX^2!FMEorUGBT{UGnv@1Ng2&vU&_7)*3>GPjCKj3_bOe>%{!JorZ5Qb44MM;7mS*>}w zm*PKhejEee@h1kFhG^*UxQ~1 zVB07Ro4OtWwJUHqaX$7>2-Wq{uYu!{(SsmK)2ga!ZU9>9HE`toAP7>)r({w6D+(-p zRhH+fnl8V-ilV@}mu1H)EaeS9$_j#T_?C$gJ-6q2o1WT8vN)T|4$C)149O)MM5D5zzrBLR_ahzq@ zHqUntyKTPRY%)4p9tbIxC4XdRq)(pTFjkx(jG}bC#*Mhe=YiO{Y!D6kNVg&%hRXaX ziV(Qd0e@x-xomFckC|hV(CSZ%imsYe@0AUqyFW%@}$xk?+JrGZ~ ziu`VC@V1KJ&s2bwSoDlM$^vdxwLAxrw_^7owYHUIEr9`@YlTRdns2Kxfx#n(vWtOks+;v88qoZl2V-*>0ud6?V zjuzzG!<=bZ1EysSn3gqQTGoJRSp%kJ4Vc!=8+ZW#0RR7Gl1Rw_000I_L_t&o07jq^ UL8wYFIsgCw07*qoM6N<$g12vvLI3~& literal 0 HcmV?d00001 diff --git a/fixtures/web-first/chromium/svg-filter-component-transfer-stroke.png b/fixtures/web-first/chromium/svg-filter-component-transfer-stroke.png new file mode 100644 index 0000000000000000000000000000000000000000..202920ddd318ca40ecfaf7c466aba070b0a15b8e GIT binary patch literal 1405 zcmV-@1%mpCP)000FtNkl=6EN z;uaSUMU+c$8#buemZMM|>o_VR9vsSma+Hp%dlLT9K2O`X7jI17&mT?t{r!H;`|aEJ zd4A8YmiI;`fjL>vvH|AA0zBhT2FQjoKsJ;CvY`x+4P}6ACN?WcsMp*d0kj16i?61VG|r|)@8)K{PxyZm*-f(M9@rf z9%$+AE9W=ESAzL5fxdf^6QTpSz-Wv$Fr`p@U0%iS{0rz0FDmTl?hJTafDssFpt-C2 ztCI5*Gqb=@SavoA@9&BYdJ!0k;Ra6CHx@Ts0jA){&a|B5cwi6)E~DR9)|u++A;(HD zepgWk48o9A=-YGEmDfeub8M`wyx43XS2oj%@z3D>-Uma|3Z<6(!m8hu^9%XeX+Tf( zHt=OhWu2&9Q`Bx9Ga)2!dqhZ}r~8VsGL=xNKu2$X)s41#QKwv?z}6Pm^JnIi$8&Z7 zebDb2I8oJ5a^;$)==Sj7PqNaitr$;=YGajI<$p&`mDLKxnm%N2%GQrFQvhM~Fd**j z&->|TY-lZmemEkMQ*d#31?*%V!n&r%SOR zYX;-wfxXzP*Th?|yj1$>ezkkGf(ocU{PCA5#T>zpB5EMNxO8$Bv);q+zUZSp+>I_k zPd0mdoH_J?6`hWT9?9hgip~IH5HfJCNzmSR8yor|?_EESjldwnxn8Fa=3+zq&aRTi zW#t!73A$g^~I&7Z_2B+*IH_s-^(5hH+I}Y^~A8i)Gc9v z7>~whh@|;@ctrVeh0-21y;jhk5gYBb!C6zGwma~}Zx^uo;T@^K4CJM3L&as4s;K(1 zmMV<~`tA?Gb|2N@DTKpb56pnK3;Wg00jMsJ_6&|b)>Ng@Kp9_vifd>V2QzuVY~=Ei zENC=TuNJgwD#06ASX{i=+=A-qv5~G$j=*fVKkt+jj%i6Qw{|WqEn`dY1{zu=*!Dd) zB?*|BY~0=gH`8mkq}Y7Cfr=)~c_qY$!*O;3W(Gb9zHY9lUL$P7=3@*Djg5<>m;p&f zTr^k}cv0b~*xD@{lTV{M#z3vG1NO9PB9lSqMTde_;kC$623`HA2rubR@l7-tW1va$ zH!3Dag~7fbtO~G2j}Hz+b&<3e71a&ElXXY$9W?KaD56`hNk|L}LPc?3zY;mbsT;T| zkuD+4&ssV?%6}7BMIyN`!26&k!mg~XN2VsK8)&+T`TfKN`(yVP^I^HhB7%$QvC(Ep z-9V$b3oVNe#q99rW=jBO-YDqOYM^`Y;lzx3^9aWT@ge?T)rk%ALq&L89vB{dQh92% zf}>VArj*F;bjk+c-xDih=q67i2grtDzmDnW;U>L%Z{@9%y}dujEr8>0`}EO=rkr2I z)(`(XA(7pc+#cY%xunNL0z)Bq9>8)*B<>#`3FLVAd%8K<+hKDw8ZZ^c7$5@mFD2Pf z2FQjoKsJ;CvY`x+4P}6AC000B2Nkl!us2tHArZiU^@Q!HcLwObJWP z@+-w@(lkG^NX^WTr7m-Gn>uxFc0h=r51;q!d?cLT#jgHmyuUr?eb2LJbDx-*2QJy+ z?FC%o23s6aKxjk(p%DdyMida5Eid2>tYTSQem6SyNk27XG#X51gURA1k@!(6KVK?< z@($a#JN97Ec7+_+L&8cgWV9|m@9gRt`aC`}yJ znv=K=!~(@N^~2-SzySjz<9Exefece4ap{thsH~&hV_jL!V0z>Ra)7tiX)P+|b4e7a7us|=pvNV zHX*~-BD)4o7Zu+|kcx2LG95-y<7XnhC7wp<^_UBBU_j(sIsFrvD94#|10j8`8}ml zEG@49ztGKJgNeeN(H>5R&K|{P8-8K>_~2JNx{1VPYtO zl&FKiJBgt|m>5bRaHsE{o!-DBem?5mz7!^jZKvbHgMmll!$OcrtU&JZM461INw7@n zb}lIn8OE;3k5cC(Cjl>5-G1fX@>MC_Ft1b2EWs zu4iS)JqS7%vfDZ}CNeKg1DuhqiAy>}c4QOp%Yv-5$OD1E0ns7*^V5=n5Mlv|1a4o= z+d(OTReV&+!i!lF^0|j(d6mh0oSudkRn%6sk&OZW#)+87g3OcKwlO)T847GrrPbY` z$FZ(V>)h6_ojsL$I1-rRk^s$^VXmx6Tlc2dVRsN+NPphR(c#%g69RnH-~z4+Y+#tT zl)h}O?(AM&-Mkz!nKV5n>P%9sFRtVNH|z-fWMEX^+|&Qz%cNy#WzlA{+LmCQet^go zavA*^8W@-uaUhu0=A31L-@$w>bOrIR20|kW2#qKpG@^jehyp?*3JA>)00030{}1}w iY5)KL21!IgR09Aryg$rRw4d?-0000%?tv%}Au$(0B_Av#V}GV#Eyns*8sXxYe7T_4c@FuA=v|;my9TJdVKL z(#re3qCY=w+b{Q>`y$T*NeAPGG={Sbvlva7GuSS`1l~Tj;x%sgcILJ3o65blXJ7BH zms@me`&XMkPAJMy^cBY~SCuaSy7T}4D=`eefDAT}gP9q`qJ>rdiL%=RMHoC?{an^L HB{Ts5D1B96 literal 0 HcmV?d00001 diff --git a/fixtures/web-first/chromium/svg-filter-component-transfer-table-truncation.png b/fixtures/web-first/chromium/svg-filter-component-transfer-table-truncation.png new file mode 100644 index 0000000000000000000000000000000000000000..4c593e4bba8681a2568b005cdedad4d669fdb87c GIT binary patch literal 916 zcmV;F18e+=P)0009?NklWFeWHLkUt6t1BFi5WWX4s zTerbF*~+$V?Ys*L1lZc!d+!>O{<1XtbKCTH+nsanDYqwOfF-v(cwk8rTyq=&yKw~U z#u2a^N8l>r8@Zi%85#ly3=lkt)RuDhKHqk$85l$V3LJ)y_l2XPqRl*yKm}MsUqJ9C zPtIEuvTMT$U<_q}BdNH%cvc<$7>K8K6<`Tvfz?4t*Us)6y<8dY{EfE244MMRa%ub9 z#o&_)D_>rPff+Of1aIQ}^q=Az62mL3qAE23^;io|9M<}Eq-(Q~D+2?l3H%yOYN}rA z*IZg1d%6V-pd)~$fBjRc{+PgYN*JdfRp|%>#t&3QGaNSlY&#sbQkP1}vihneTwaeExkytPblpqz@nw5{@MXX)4 zYI+WqiaOAMRAAL30X~^{VX|ijXh0+o@%_P_{!@OA#HplbB7rpzQCvsqSxKuvhIIk7 zZbZv2AONN34_%u;hIIi{>jN?9E<*Pb5MWUt8B(N;9ME9$Wtb#ATNDsH)T)}3^lW+2 zbEBYQSGjU5-}7XdZbk6m(Vd5n?*TqmuE1PhlzO|t?@d?>3D^Q?`qwA%Y6mk^DlpX@ zDdVSSY=OlA@yc~0IoU*U{xx=;hbMp{xBoN4R!?XqjoXvN6Yvd+WlBX@E#`Zp`Hb1( z9Iyn6N_lnEO6i8hzn-LNFa(y~i;Ox87eGD9^^a!bgjs>IsxJ@4piwOK#pI}|^lVlD zRVm84Cnv4$@*CYyvfG=6dT*pYb!Y#Q`RFzpJ^ag?G7cx>~9) z&dpcfR6p`u^-c97_x~GLo#Pk??D%9UWOm`gxtm&fuC<*zztvp&mFI*6fCdHvsCM8x z+!xLXZQI(LFMo*yE*~M8rn7LXg_Q*j7OVxFC-rvY2-uAyU^k9{-8cev;|SP|BVadw q0RRC1|DQq*1ONa421!IgR09Crq?HK1zzO>R00000006MNklUtfxpU6B&NG+u+fB{6-GArhKKHqy#M3i^EC&H+0&oktey?xuMVD6(FRtz$9@7jf zEh=oT3y<{mbau3YmGyNK$G3KeCg&kyZfvkS(hfe3B{UmbgCL0^{&LJf+rZfULjq!K zx(N6dm173P?;gp!C$wj_4o?OqqO=a7k^iTV=0z2P-;OV@pppLpq}HK4^CaeG0&p_{ zxH$rp6cs?24OWf-O|@YNv%ty`V7RvnLE{KOmHoo_NTj)eN=xsdHv95&)c21< z*6HFjbV+#%No{DQfpSs?klN5n1LdR)AibeAp9EH#&Rm8aiX^UAO3SnsnVA6GOaN{s z0JlH)ec(IlbyByG`fceuR#d}L6-0G}=5D*klbEUr>eoU9y;10_COno9)j|*?F+_#O z5~4~Df+U8h@CcAe9jZP=g*isKnE>2=-ozU_`;$wt^XnUAJ$HbYS9@w{CAu89v!QF1 z0e67SgQFR}Pa;L`0JE_;0>vF*|Lg*R;tp_gdk;1Fd3n&t>lhW^$(1F=(8&J}-I10? zXyi2mpWk0R=i=iJ_2Fu0l=%!;%76G3rWWF60&p_{xc!69obVNMa-orX25QR7pr*E} t0vf#m009609&Eb@00006Nkl^cAd002ovPDHLkV1g$|5O@Fp literal 0 HcmV?d00001 diff --git a/fixtures/web-first/chromium/svg-filter-component-transfer-type-grammar.png b/fixtures/web-first/chromium/svg-filter-component-transfer-type-grammar.png new file mode 100644 index 0000000000000000000000000000000000000000..40e6259a4bbabaa4740981c03ef5cb7e2d03bff6 GIT binary patch literal 205 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1SD0tpLGJMCQlc~kcv5PZfxW{>>$E=aC2bc zx+9?$C8Fz&EahD2FjGq)iZ5+ZH=kJDmuaQ9kDr;BTy&XT=lSce|N6ZD*3Xx=D|a$w z#16N%ms+VUNQ+y~xdJfcuwuLBfe@O1Ta JS?83{1OVC3K==Rv literal 0 HcmV?d00001 diff --git a/fixtures/web-first/chromium/svg-filter-component-transfer-use.png b/fixtures/web-first/chromium/svg-filter-component-transfer-use.png new file mode 100644 index 0000000000000000000000000000000000000000..bbc892fbe0461b2e88e57a7dcfadd1aebcc21d82 GIT binary patch literal 669 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1SD0tpLH@YFa>$KIEGZrc{3~8J1J12ZNFrb z_-j_#H5VqZp=-NAA6OCiWqi)y9P1M`r1|fB$~| zef^yOLCaI=jy%vUaXYrN1e5sqt4=^u1kiGCjVx6Pf^wpeh4eVZx9yxzM z|7kiHAv2%n0K-g%bN8iYY85WnW!#e9AZ5fjM}B^cD&L#=aV*QGH%Lh_-v7zk`|#uE zFQyZmlm((9G;Q}kcz5u(_zUJB4wiYJeoi~+dGc`a?Au!=x+@6GiQczo-&Fhm_5%C9 z%QbQYGtSvxTN3tCdCSCftp!&58w@`6uoUsnd-aFu&YS|Y157>M%VZG&+#`9c1NutsUsPz3GE`dZa^Z(<|#upNW zDo-(O`ONL3;>=Q%-p~Ghcyj8ThFmnH759Lb of#Lsu1&$0EU?O7!r7mWM+hM88I4tL#28u9vy85}Sb4q9e05b(4y#N3J literal 0 HcmV?d00001 diff --git a/fixtures/web-first/chromium/svg-filter-component-transfer-viewbox.png b/fixtures/web-first/chromium/svg-filter-component-transfer-viewbox.png new file mode 100644 index 0000000000000000000000000000000000000000..90fb61fb4046315b7d4d59f53863ed61b4075b12 GIT binary patch literal 998 zcmV000AKr#IvF11otQ zhy`wyy!_V3^6YJV>el5vECxcz1j-tl%e5`Q8BbqppVT)JJwhNbHf6qBS_vGJ`>1qe zViKPx5Xi5pU9cjBMY~0QrumXm>6mes9o2t0IC9h-dlioc5U5^iJ=5yjvfHs2@TwbiJ=5` zcxrq-HNYd@8nur`jY(pU)1iTWz$2l7{>UU&AU!VJK_&xU*dUXpM4m*3u>xDw?rCx1 zzzfOYAu4o(h~Q3~l^CmZlmo{&ImpvuBk*|w0cV9gH~lPd%#GADj&cWlo~$nvfBZ;h zd<<~L`IxY%!vRE(5O?OXlM{mW?gS19_St<0RlVk?7wk)6cuddlhe;G;kn zMgkPQ1H<{1b!G2ATC8N_NbT+8f(}t%%Diz$hLvC{U}MlYkylY$-0)%Evib)bg-n(h zax5kCgokP)Foqq0zkKfLt8Q*<*Xd2>Y17Q?_5e0-s6c8FwKxjk(q4@&<0RR7Z2#{s~000I_L_t&o01pL2 UHDxVfHUIzs07*qoM6N<$g2L#;tN;K2 literal 0 HcmV?d00001 diff --git a/fixtures/web-first/oracle-bake.json b/fixtures/web-first/oracle-bake.json index d7e870a9..fcb10329 100644 --- a/fixtures/web-first/oracle-bake.json +++ b/fixtures/web-first/oracle-bake.json @@ -5,7 +5,7 @@ "bake_script_sha256": "2bdb5f933d072a1e87c9a675c3342fcf506c0955c0f5c26e2988f4e8fa37c4f2", "capture_module_sha256": "15ba5c3156f3ed0bfd0f32b6ad773e1d228c0f678396db08c8de1d972cf5bced", "suite": "primitives.json", - "suite_sha256": "b3072862c92a3fb20e1ee1c295c135188223136f19745f7d72ff5b4cc3f6aa77", + "suite_sha256": "92e34f55916a88b5f814b0ede14d67a1f912483a056466c9783ff9ad6012f912", "capture": { "device_scale_factor": 1, "omit_background": true, @@ -892,6 +892,294 @@ "width": 64, "height": 64 }, + { + "id": "svg-filter-component-transfer-alpha", + "source": "svg-filter-component-transfer-alpha.svg", + "source_sha256": "c1aa1c9dd55498f3abee0ae3343611697d56eb2904e1d2e57726a5453a2e8ddc", + "oracle": "chromium/svg-filter-component-transfer-alpha.png", + "oracle_sha256": "85d40b24b719e937e4540286d737c1fb404315fbad846a1a6f3f71e65e74a574", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-alpha-region", + "source": "svg-filter-component-transfer-alpha-region.svg", + "source_sha256": "6b866553e48fec999b2aca13416c3ea680ded2547b57a9e60aa5f56667c139ac", + "oracle": "chromium/svg-filter-component-transfer-alpha-region.png", + "oracle_sha256": "7118e37c665473b9e31e5aaa4d0683328716c536cc907e2efcea7bb253bdf3e0", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-blur-order", + "source": "svg-filter-component-transfer-blur-order.svg", + "source_sha256": "05beec0866e08328cc7ca5c71959a46c333f41b11ff1aa7d40c21763c20e4869", + "oracle": "chromium/svg-filter-component-transfer-blur-order.png", + "oracle_sha256": "73a7029d47d061cdbd11f2960484dc4a1bff540fcc594e1fa977ca0648f32559", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-channel-types", + "source": "svg-filter-component-transfer-channel-types.svg", + "source_sha256": "16a060ce891f1866b77d99f381e471edeea70c6661720d376ea10495e2641a31", + "oracle": "chromium/svg-filter-component-transfer-channel-types.png", + "oracle_sha256": "39a7f35ad3316141d795c1459a10e2370e67064fa2ef6f1c11aae64928c74e2e", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-child-dispatch", + "source": "svg-filter-component-transfer-child-dispatch.svg", + "source_sha256": "01a5699604c1a5776b49177cfb3e082954eca678badb32a9863ba762ed3283f5", + "oracle": "chromium/svg-filter-component-transfer-child-dispatch.png", + "oracle_sha256": "52a971e441a8637006bbaf4789001f16b298750bb482217b16558376f19c874c", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-circle", + "source": "svg-filter-component-transfer-circle.svg", + "source_sha256": "0ba1b0cc05707b34109b4570473c6b92613e9ffbad08db13af223f11633a65dc", + "oracle": "chromium/svg-filter-component-transfer-circle.png", + "oracle_sha256": "9f9df12ebfd8042c9cd2ffca7cdf2a2dc953a6ea8d7e1049c0e677eb98784087", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-clamp", + "source": "svg-filter-component-transfer-clamp.svg", + "source_sha256": "172c886581fd898c2c013b725bceec1fec0f98fb37aad15d9440b41886fd0a32", + "oracle": "chromium/svg-filter-component-transfer-clamp.png", + "oracle_sha256": "30b5174207a07630a3746aebd7aef83813244d2be0e686cbea5d0e3e5a52d2b7", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-color-linear", + "source": "svg-filter-component-transfer-color-linear.svg", + "source_sha256": "4813ecacfdd6744caab87e218292f67de705cee371255f3eddc0f33741c6f158", + "oracle": "chromium/svg-filter-component-transfer-color-linear.png", + "oracle_sha256": "42672b41a3821cc7c902703ef935d34eedb900b7f22c4739da1d702fdfc980e4", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-color-srgb", + "source": "svg-filter-component-transfer-color-srgb.svg", + "source_sha256": "72dbbae0e2c4198f6b6955ef0a554b0c16caaa162a419b287c1b2aea62e3dc8c", + "oracle": "chromium/svg-filter-component-transfer-color-srgb.png", + "oracle_sha256": "2e74ec5ffb9d8920835251f59e578f66a13e11f67d06f5f1d04817f4a994b3d7", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-composite-merge", + "source": "svg-filter-component-transfer-composite-merge.svg", + "source_sha256": "1af03bcbe223b3fa0eb031aabd3e334ae168eabd7c35154289c8dad1c213010e", + "oracle": "chromium/svg-filter-component-transfer-composite-merge.png", + "oracle_sha256": "1637589877f5e6772664210fd3ffb3f06796eb0323037fdf553ec1e8d363f463", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-discrete", + "source": "svg-filter-component-transfer-discrete.svg", + "source_sha256": "c7a48762596841d48ba64128fdb0b9d99722da6b2ebc21cfe5493e97323ddfc0", + "oracle": "chromium/svg-filter-component-transfer-discrete.png", + "oracle_sha256": "00adf5caf8ff7e4f84df7cfa2575acd247a399ac4ca9f5270c9aa3b2f34e1500", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-gamma", + "source": "svg-filter-component-transfer-gamma.svg", + "source_sha256": "fa4a6c17a2e35622f351de8b554521f973bb2bd610df344a0b2d8ad2e88598f4", + "oracle": "chromium/svg-filter-component-transfer-gamma.png", + "oracle_sha256": "fa330ddd2e02344310614aae4b24cfc02e0af004eaba535e2498c37286fc3b21", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-generated", + "source": "svg-filter-component-transfer-generated.svg", + "source_sha256": "29c492a9fb7f3f5123dee401325542a51dbb636f1820f3b7c2f4c787a7c6aaf2", + "oracle": "chromium/svg-filter-component-transfer-generated.png", + "oracle_sha256": "d84ff2d60733350213b3188335af0e9b62760fcd33b8f519b7266a233611fdd4", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-inputs", + "source": "svg-filter-component-transfer-inputs.svg", + "source_sha256": "5a165add2f903bd7049a6267c9611abb38c15ba7d13b2e0cf65e85ee62a56495", + "oracle": "chromium/svg-filter-component-transfer-inputs.png", + "oracle_sha256": "f87455a959ff2a5cbc86b01db03278ab34d1dae197a58428c53cccfa30899a05", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-invalid-values", + "source": "svg-filter-component-transfer-invalid-values.svg", + "source_sha256": "316a9193d60fa48da195f05b269a70f8464e207f8edf629dd81d73ece7f5b40d", + "oracle": "chromium/svg-filter-component-transfer-invalid-values.png", + "oracle_sha256": "f1c51ca12d940a7f17bc9783baaef034f7e757d81ce675f1cd258feb410ab144", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-linear", + "source": "svg-filter-component-transfer-linear.svg", + "source_sha256": "224031b43997456aebde5f78f5a817d822e43f183ff29f7c406c7caa714cd5ed", + "oracle": "chromium/svg-filter-component-transfer-linear.png", + "oracle_sha256": "8d10476d316c9370db73096fa153007561d0eacff7990daa9ae5522b6526d61c", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-matrix-order", + "source": "svg-filter-component-transfer-matrix-order.svg", + "source_sha256": "d0108cdd86f798bb095bc165f1b82d8a7e5e3139daba1f63658feec628efcb2a", + "oracle": "chromium/svg-filter-component-transfer-matrix-order.png", + "oracle_sha256": "6ec13935fc6a61037ef4fcf05ae33d2ef08d467b8a1a70c276f360fa827068f1", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-number-order", + "source": "svg-filter-component-transfer-number-order.svg", + "source_sha256": "886048f78d999e4d5b21f13e4b4aa3674f246f036b80063e6786da912f22ddf8", + "oracle": "chromium/svg-filter-component-transfer-number-order.png", + "oracle_sha256": "d0d27d5e897a7ab09241650fd440ca7216135576881f2a64860af25f91eadd21", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-offset-order", + "source": "svg-filter-component-transfer-offset-order.svg", + "source_sha256": "60af83c9d6c658df689f28cdfe4be412c31acf6dbbea380bcb682208ee6eb98a", + "oracle": "chromium/svg-filter-component-transfer-offset-order.png", + "oracle_sha256": "209a1187d1471a24dc8471770fbdf3a5d4d85018b6ee2965f351a0a395bb9bc5", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-opacity-clip", + "source": "svg-filter-component-transfer-opacity-clip.svg", + "source_sha256": "15cf55027e77e5d0b946b3d8d9bad7e3fcdd100b71561275ae01c986bdc28c3b", + "oracle": "chromium/svg-filter-component-transfer-opacity-clip.png", + "oracle_sha256": "aab0f6bc5b9e15e6f9766206207fa62d54bc5376e7761e2c532f7136a64938e3", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-path", + "source": "svg-filter-component-transfer-path.svg", + "source_sha256": "8c6b241c46116c2bf3816864da3b181d530ae362c9086af3af60df06bcf39f39", + "oracle": "chromium/svg-filter-component-transfer-path.png", + "oracle_sha256": "549154477b59152dbff976623af400f35e91c64d813c7c502c8e7312d92999ef", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-primitive-units", + "source": "svg-filter-component-transfer-primitive-units.svg", + "source_sha256": "e61e066190bee9e0f7a027d4f06dcb1305e5623e508115e033e06d7c43a030c6", + "oracle": "chromium/svg-filter-component-transfer-primitive-units.png", + "oracle_sha256": "d13a39ff0d969ccc703f1384ab845687a6b93f8a44983bd6c5f36731b250494f", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-scalar-grammar", + "source": "svg-filter-component-transfer-scalar-grammar.svg", + "source_sha256": "d59c481aa45de74d163bec4acd291b945dd944b72b323cf127a61c099169b3b2", + "oracle": "chromium/svg-filter-component-transfer-scalar-grammar.png", + "oracle_sha256": "48539577ab91ef2348c4a444354df74a6ae3f5d9381b6d674203b43be69f666b", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-shadow-order", + "source": "svg-filter-component-transfer-shadow-order.svg", + "source_sha256": "e48dc00efe6236e7a2b34db3644d3bde53d191c12cb37c21f3878fcb5bc5ba4c", + "oracle": "chromium/svg-filter-component-transfer-shadow-order.png", + "oracle_sha256": "f1d54ed3088b0475d698d6b8ef0f022eca3f7b55cd5d13050e4eac4a4b642488", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-stroke", + "source": "svg-filter-component-transfer-stroke.svg", + "source_sha256": "0499af001a7c3f625b38600a4533ce5c525b742098c0ab487f898eb956264e07", + "oracle": "chromium/svg-filter-component-transfer-stroke.png", + "oracle_sha256": "9529770c3060993bc0ddf4f2b10bb0f5263d306e742a2eae951cf495302cf90e", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-table-interpolation", + "source": "svg-filter-component-transfer-table-interpolation.svg", + "source_sha256": "e14160e14c76dfc881c347983b16b724b188d354067ac8fa6bfa7d9b2ac6e0bc", + "oracle": "chromium/svg-filter-component-transfer-table-interpolation.png", + "oracle_sha256": "5395ac4df35278294de071590ea558ea81ff0a6407ef95830ecaa1418d6bf792", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-table-list", + "source": "svg-filter-component-transfer-table-list.svg", + "source_sha256": "fb70baf7254003ebafef4693ed65adf9c0c6de9d7a5a3e64664ac50f0f17d6cd", + "oracle": "chromium/svg-filter-component-transfer-table-list.png", + "oracle_sha256": "ed2f60e60a7804c8291461e7c9b8118f708ceabb7a3e7e41be4b02b9f7319868", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-table-truncation", + "source": "svg-filter-component-transfer-table-truncation.svg", + "source_sha256": "32b3923e2dd4e18005933aebb4cf3f323c7b13a80cab9ca628006242566b788d", + "oracle": "chromium/svg-filter-component-transfer-table-truncation.png", + "oracle_sha256": "0bc4a00e4ade9d048354798b69c83cc1298a63a9aa604e6544788f354cbd3559", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-transform", + "source": "svg-filter-component-transfer-transform.svg", + "source_sha256": "1fe9ce1c7dd262ed8f314457e5dbaaed1c969b81d214dcb155918ac6dac3d958", + "oracle": "chromium/svg-filter-component-transfer-transform.png", + "oracle_sha256": "677e2746e770dd016ae468c8806cc6c9b8cba4dc9e72f18e6d88d80f4d7efff1", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-type-grammar", + "source": "svg-filter-component-transfer-type-grammar.svg", + "source_sha256": "98089f58b7cb33a2820d8221505959e436e6ea66dcdf6f721d4c28924f268077", + "oracle": "chromium/svg-filter-component-transfer-type-grammar.png", + "oracle_sha256": "f1c51ca12d940a7f17bc9783baaef034f7e757d81ce675f1cd258feb410ab144", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-use", + "source": "svg-filter-component-transfer-use.svg", + "source_sha256": "f2f397f6b7604bd5c3e86ed8ce86bd9e2627f888c31db74087c0028e62072847", + "oracle": "chromium/svg-filter-component-transfer-use.png", + "oracle_sha256": "acc2d51d76950482a9938e551458115998079ed6f18aa287b0407ec6cb148af5", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-viewbox", + "source": "svg-filter-component-transfer-viewbox.svg", + "source_sha256": "cd44334ca0814e5d97d23cd41502e574280f80a17faf7f19df80ade805e87e99", + "oracle": "chromium/svg-filter-component-transfer-viewbox.png", + "oracle_sha256": "6275308d6fe1ee01f915608f04863c0dcb7143da1489beece2f9de574d3280bd", + "width": 64, + "height": 64 + }, { "id": "svg-filter-composite-arithmetic-default", "source": "svg-filter-composite-arithmetic-default.svg", diff --git a/fixtures/web-first/primitives.json b/fixtures/web-first/primitives.json index b00ddc19..9bfaa38d 100644 --- a/fixtures/web-first/primitives.json +++ b/fixtures/web-first/primitives.json @@ -855,6 +855,262 @@ "width": 64, "height": 64 }, + { + "id": "svg-filter-component-transfer-alpha", + "source": "svg-filter-component-transfer-alpha.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-alpha.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-alpha-region", + "source": "svg-filter-component-transfer-alpha-region.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-alpha-region.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-blur-order", + "source": "svg-filter-component-transfer-blur-order.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-blur-order.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-channel-types", + "source": "svg-filter-component-transfer-channel-types.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-channel-types.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-child-dispatch", + "source": "svg-filter-component-transfer-child-dispatch.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-child-dispatch.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-circle", + "source": "svg-filter-component-transfer-circle.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-circle.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-clamp", + "source": "svg-filter-component-transfer-clamp.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-clamp.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-color-linear", + "source": "svg-filter-component-transfer-color-linear.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-color-linear.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-color-srgb", + "source": "svg-filter-component-transfer-color-srgb.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-color-srgb.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-composite-merge", + "source": "svg-filter-component-transfer-composite-merge.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-composite-merge.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-discrete", + "source": "svg-filter-component-transfer-discrete.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-discrete.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-gamma", + "source": "svg-filter-component-transfer-gamma.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-gamma.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-generated", + "source": "svg-filter-component-transfer-generated.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-generated.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-inputs", + "source": "svg-filter-component-transfer-inputs.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-inputs.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-invalid-values", + "source": "svg-filter-component-transfer-invalid-values.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-invalid-values.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-linear", + "source": "svg-filter-component-transfer-linear.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-linear.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-matrix-order", + "source": "svg-filter-component-transfer-matrix-order.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-matrix-order.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-number-order", + "source": "svg-filter-component-transfer-number-order.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-number-order.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-offset-order", + "source": "svg-filter-component-transfer-offset-order.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-offset-order.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-opacity-clip", + "source": "svg-filter-component-transfer-opacity-clip.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-opacity-clip.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-path", + "source": "svg-filter-component-transfer-path.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-path.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-primitive-units", + "source": "svg-filter-component-transfer-primitive-units.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-primitive-units.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-scalar-grammar", + "source": "svg-filter-component-transfer-scalar-grammar.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-scalar-grammar.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-shadow-order", + "source": "svg-filter-component-transfer-shadow-order.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-shadow-order.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-stroke", + "source": "svg-filter-component-transfer-stroke.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-stroke.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-table-interpolation", + "source": "svg-filter-component-transfer-table-interpolation.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-table-interpolation.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-table-list", + "source": "svg-filter-component-transfer-table-list.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-table-list.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-table-truncation", + "source": "svg-filter-component-transfer-table-truncation.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-table-truncation.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-transform", + "source": "svg-filter-component-transfer-transform.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-transform.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-type-grammar", + "source": "svg-filter-component-transfer-type-grammar.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-type-grammar.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-use", + "source": "svg-filter-component-transfer-use.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-use.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-component-transfer-viewbox", + "source": "svg-filter-component-transfer-viewbox.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-component-transfer-viewbox.png", + "width": 64, + "height": 64 + }, { "id": "svg-filter-composite-arithmetic-default", "source": "svg-filter-composite-arithmetic-default.svg", diff --git a/fixtures/web-first/svg-filter-component-transfer-alpha-region.svg b/fixtures/web-first/svg-filter-component-transfer-alpha-region.svg new file mode 100644 index 00000000..8b6336d8 --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-alpha-region.svg @@ -0,0 +1 @@ + diff --git a/fixtures/web-first/svg-filter-component-transfer-alpha.svg b/fixtures/web-first/svg-filter-component-transfer-alpha.svg new file mode 100644 index 00000000..e37e4138 --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-alpha.svg @@ -0,0 +1 @@ + diff --git a/fixtures/web-first/svg-filter-component-transfer-blur-order.svg b/fixtures/web-first/svg-filter-component-transfer-blur-order.svg new file mode 100644 index 00000000..466f76cb --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-blur-order.svg @@ -0,0 +1 @@ + diff --git a/fixtures/web-first/svg-filter-component-transfer-channel-types.svg b/fixtures/web-first/svg-filter-component-transfer-channel-types.svg new file mode 100644 index 00000000..5bf7d678 --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-channel-types.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-component-transfer-child-dispatch.svg b/fixtures/web-first/svg-filter-component-transfer-child-dispatch.svg new file mode 100644 index 00000000..3fe48ddd --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-child-dispatch.svg @@ -0,0 +1,13 @@ + + + + + + + text + + + + + + diff --git a/fixtures/web-first/svg-filter-component-transfer-circle.svg b/fixtures/web-first/svg-filter-component-transfer-circle.svg new file mode 100644 index 00000000..9ae0bda0 --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-circle.svg @@ -0,0 +1 @@ + diff --git a/fixtures/web-first/svg-filter-component-transfer-clamp.svg b/fixtures/web-first/svg-filter-component-transfer-clamp.svg new file mode 100644 index 00000000..d5a31c60 --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-clamp.svg @@ -0,0 +1 @@ + diff --git a/fixtures/web-first/svg-filter-component-transfer-color-linear.svg b/fixtures/web-first/svg-filter-component-transfer-color-linear.svg new file mode 100644 index 00000000..f56b93b2 --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-color-linear.svg @@ -0,0 +1 @@ + diff --git a/fixtures/web-first/svg-filter-component-transfer-color-srgb.svg b/fixtures/web-first/svg-filter-component-transfer-color-srgb.svg new file mode 100644 index 00000000..5700a2d8 --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-color-srgb.svg @@ -0,0 +1 @@ + diff --git a/fixtures/web-first/svg-filter-component-transfer-composite-merge.svg b/fixtures/web-first/svg-filter-component-transfer-composite-merge.svg new file mode 100644 index 00000000..5845e915 --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-composite-merge.svg @@ -0,0 +1 @@ + diff --git a/fixtures/web-first/svg-filter-component-transfer-discrete.svg b/fixtures/web-first/svg-filter-component-transfer-discrete.svg new file mode 100644 index 00000000..bf20b48d --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-discrete.svg @@ -0,0 +1 @@ + diff --git a/fixtures/web-first/svg-filter-component-transfer-gamma.svg b/fixtures/web-first/svg-filter-component-transfer-gamma.svg new file mode 100644 index 00000000..b003b4db --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-gamma.svg @@ -0,0 +1 @@ + diff --git a/fixtures/web-first/svg-filter-component-transfer-generated.svg b/fixtures/web-first/svg-filter-component-transfer-generated.svg new file mode 100644 index 00000000..f615851a --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-generated.svg @@ -0,0 +1 @@ + diff --git a/fixtures/web-first/svg-filter-component-transfer-inputs.svg b/fixtures/web-first/svg-filter-component-transfer-inputs.svg new file mode 100644 index 00000000..0673a3da --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-inputs.svg @@ -0,0 +1 @@ + diff --git a/fixtures/web-first/svg-filter-component-transfer-invalid-values.svg b/fixtures/web-first/svg-filter-component-transfer-invalid-values.svg new file mode 100644 index 00000000..a23c52fc --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-invalid-values.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-component-transfer-linear.svg b/fixtures/web-first/svg-filter-component-transfer-linear.svg new file mode 100644 index 00000000..a6ca0432 --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-linear.svg @@ -0,0 +1 @@ + diff --git a/fixtures/web-first/svg-filter-component-transfer-matrix-order.svg b/fixtures/web-first/svg-filter-component-transfer-matrix-order.svg new file mode 100644 index 00000000..bde8bb41 --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-matrix-order.svg @@ -0,0 +1 @@ + diff --git a/fixtures/web-first/svg-filter-component-transfer-number-order.svg b/fixtures/web-first/svg-filter-component-transfer-number-order.svg new file mode 100644 index 00000000..9a98027e --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-number-order.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-component-transfer-offset-order.svg b/fixtures/web-first/svg-filter-component-transfer-offset-order.svg new file mode 100644 index 00000000..589d7e57 --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-offset-order.svg @@ -0,0 +1 @@ + diff --git a/fixtures/web-first/svg-filter-component-transfer-opacity-clip.svg b/fixtures/web-first/svg-filter-component-transfer-opacity-clip.svg new file mode 100644 index 00000000..bdec77a9 --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-opacity-clip.svg @@ -0,0 +1 @@ + diff --git a/fixtures/web-first/svg-filter-component-transfer-path.svg b/fixtures/web-first/svg-filter-component-transfer-path.svg new file mode 100644 index 00000000..8870103d --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-path.svg @@ -0,0 +1 @@ + diff --git a/fixtures/web-first/svg-filter-component-transfer-primitive-units.svg b/fixtures/web-first/svg-filter-component-transfer-primitive-units.svg new file mode 100644 index 00000000..69f99855 --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-primitive-units.svg @@ -0,0 +1 @@ + diff --git a/fixtures/web-first/svg-filter-component-transfer-scalar-grammar.svg b/fixtures/web-first/svg-filter-component-transfer-scalar-grammar.svg new file mode 100644 index 00000000..75296cdf --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-scalar-grammar.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-component-transfer-shadow-order.svg b/fixtures/web-first/svg-filter-component-transfer-shadow-order.svg new file mode 100644 index 00000000..28ee307c --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-shadow-order.svg @@ -0,0 +1 @@ + diff --git a/fixtures/web-first/svg-filter-component-transfer-stroke.svg b/fixtures/web-first/svg-filter-component-transfer-stroke.svg new file mode 100644 index 00000000..bdb56b3a --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-stroke.svg @@ -0,0 +1 @@ + diff --git a/fixtures/web-first/svg-filter-component-transfer-table-interpolation.svg b/fixtures/web-first/svg-filter-component-transfer-table-interpolation.svg new file mode 100644 index 00000000..2444990d --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-table-interpolation.svg @@ -0,0 +1 @@ + diff --git a/fixtures/web-first/svg-filter-component-transfer-table-list.svg b/fixtures/web-first/svg-filter-component-transfer-table-list.svg new file mode 100644 index 00000000..14826e76 --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-table-list.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-component-transfer-table-truncation.svg b/fixtures/web-first/svg-filter-component-transfer-table-truncation.svg new file mode 100644 index 00000000..44dbd52a --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-table-truncation.svg @@ -0,0 +1 @@ + diff --git a/fixtures/web-first/svg-filter-component-transfer-transform.svg b/fixtures/web-first/svg-filter-component-transfer-transform.svg new file mode 100644 index 00000000..3a8a1a99 --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-transform.svg @@ -0,0 +1 @@ + diff --git a/fixtures/web-first/svg-filter-component-transfer-type-grammar.svg b/fixtures/web-first/svg-filter-component-transfer-type-grammar.svg new file mode 100644 index 00000000..d5baa155 --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-type-grammar.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-component-transfer-use.svg b/fixtures/web-first/svg-filter-component-transfer-use.svg new file mode 100644 index 00000000..7906f4ee --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-use.svg @@ -0,0 +1 @@ + diff --git a/fixtures/web-first/svg-filter-component-transfer-viewbox.svg b/fixtures/web-first/svg-filter-component-transfer-viewbox.svg new file mode 100644 index 00000000..5f9ffa1d --- /dev/null +++ b/fixtures/web-first/svg-filter-component-transfer-viewbox.svg @@ -0,0 +1 @@ + diff --git a/fixtures/web-first/unsupported/README.md b/fixtures/web-first/unsupported/README.md index b2a9f5b1..19ec2d98 100644 --- a/fixtures/web-first/unsupported/README.md +++ b/fixtures/web-first/unsupported/README.md @@ -80,7 +80,10 @@ The scannable, generated view of this register (beside the baked cells) is | `svg-filter-drop-shadow-transform-precision.svg` | Refuse target mappings outside the measured native-shadow envelope. Pure translations, exact quarter turns, integer axis scales, and an integer `viewBox` scale are exact. A 19-degree rotation differs even with zero offset or zero blur, and sampled fractional axis maps also differ; those mappings now stop before rasterization instead of emitting a plausible wrong shadow (measured, not celled). | | `svg-filter-drop-shadow-source-layer-precision.svg` | Refuse native shadows whose source subtree crosses the pinned backend's source-layer precision split. Solid fill/stroke sources, groups, target opacity, clips, masks, and `` are exact. Sampled gradient sources and a non-target descendant opacity produce different pixels from Chromium, so paint-server sources and descendant opacity conservatively share this named boundary (measured, not celled). | | `svg-filter-drop-shadow-color-precision.svg` | Refuse an interior-channel native shadow color under `linearRGB`. The admitted sRGB route and a default-linear endpoint-color route are celled exact, but the sampled linearRGB interior color differs from Chromium at 194 pixels by one code value after backend color conversion. The wider interior class is named before conversion rather than hidden behind tolerance (measured, not celled). | +| `svg-filter-effect-stack-precision.svg` | Refuse any filtered descendant enclosed by one element that owns both a geometric clip and partial opacity. The no-filter control is exact, but identity `feComponentTransfer`, identity `feColorMatrix`, zero `feGaussianBlur`, zero `feOffset`, one-input `feMerge`, and a transparent zero native shadow each reproduce the same 1,154-pixel / maximum-delta-2 restore split; an active transfer reaches 1,162 pixels / delta 3. Keeping clip and opacity on separate elements is exact. This is a generic effect-stack boundary, not a component-transfer grammar gap (measured, not celled). | | `svg-filter-color-matrix-source-layer-precision.svg` | Refuse a source-dependent `feColorMatrix` when its source crosses the pinned backend's source-layer precision split. Curved strokes, translucent anti-aliased fills, paint-server fills, descendant opacity, and an overlapping source group all reproduced wrong edge pixels; the sampled maximum channel delta reached 64. The admitted source is conservatively one direct `rect`, `circle`, `ellipse`, `path`, `polygon`, or `polyline` with one opaque solid fill, no stroke, and no children. A matrix fed only by a generated graph source does not inherit this patrol. This over-refuses additional exact cases instead of guessing from an incomplete raster profile (measured, not celled). | +| `svg-filter-component-transfer-source-layer-precision.svg` | Refuse a source-dependent `feComponentTransfer` whose isolated source contains a paint-server fill or stroke. After the measured source snapshot made every other sampled source profile exact, linear-gradient fill still differed at 848 pixels / Δ2 for identity tables and 434 / Δ3 for active tables; radial fill differed at 482 / Δ115, and a gradient stroke at 210 / Δ6. Generated-only filter inputs do not inherit the patrol. The row conservatively covers every paint server rather than guessing from three gradients (measured, not celled). | +| `svg-filter-component-transfer-transform-precision.svg` | Refuse a source-dependent `feComponentTransfer` outside the measured transform envelope. Fractional target translation differed at two pixels / Δ7 and a 17-degree rotation at 58 / Δ12. Integer translation, arbitrary sampled axis scales, reflection, exact quarter turns, and an axis `viewBox` map were exact. A sampled shear also happened to be exact, but general affine maps conservatively remain quarantined; generated-only transfer input stays admitted through the same rotation (measured, not celled). | | `svg-filter-color-matrix-transform-precision.svg` | Refuse a source-dependent `feColorMatrix` under a target map outside the measured axis-aligned/exact-quarter-turn envelope. Fractional axis scales and reflections are exact, as are exact quarter turns; sampled non-quarter rotations changed 8–303 pixels at maximum delta 1. The patrol names the mapping before paint rather than hiding those edge pixels behind tolerance (measured, not celled). | | `svg-filter-color-matrix-spatial-precision.svg` | Refuse a source-dependent color-matrix graph that also contains `feGaussianBlur` or `feDropShadow`. A circle with matrix plus blur changed 23 pixels at maximum delta 1; matrix plus native shadow changed 15–16 pixels at maximum delta 3. Sampled path controls were exact, but the graph-wide patrol deliberately over-refuses that narrower class until a stable source-independent boundary is known (measured, not celled). | | `svg-filter-region-unit.svg` · `svg-filter-region-calc.svg` · `svg-filter-region-var.svg` | A non-`px` unit, CSS math, and custom-property substitution in a filter-region field all paint in Chromium and require a basis or computation context this direct reader does not own. Each refuses by its exact field under the independently listed value-family rows. | diff --git a/fixtures/web-first/unsupported/svg-filter-component-transfer-source-layer-precision.svg b/fixtures/web-first/unsupported/svg-filter-component-transfer-source-layer-precision.svg new file mode 100644 index 00000000..9b0ba78b --- /dev/null +++ b/fixtures/web-first/unsupported/svg-filter-component-transfer-source-layer-precision.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/fixtures/web-first/unsupported/svg-filter-component-transfer-transform-precision.svg b/fixtures/web-first/unsupported/svg-filter-component-transfer-transform-precision.svg new file mode 100644 index 00000000..1cc44dc0 --- /dev/null +++ b/fixtures/web-first/unsupported/svg-filter-component-transfer-transform-precision.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/fixtures/web-first/unsupported/svg-filter-effect-stack-precision.svg b/fixtures/web-first/unsupported/svg-filter-effect-stack-precision.svg new file mode 100644 index 00000000..4238eabb --- /dev/null +++ b/fixtures/web-first/unsupported/svg-filter-effect-stack-precision.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + +