diff --git a/crates/n0/src/drawlist.rs b/crates/n0/src/drawlist.rs index 7346bb64..da9cf277 100644 --- a/crates/n0/src/drawlist.rs +++ b/crates/n0/src/drawlist.rs @@ -104,6 +104,20 @@ pub(crate) enum ResolvedFilterMorphology { Dilate, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum ResolvedFilterTurbulenceKind { + Turbulence, + FractalNoise, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum ResolvedFilterDisplacementChannel { + Red, + Green, + Blue, + Alpha, +} + /// The private filter-operation vocabulary admitted by the painter. #[derive(Debug, Clone, PartialEq)] pub(crate) enum ResolvedFilterPrimitive { @@ -144,6 +158,19 @@ pub(crate) enum ResolvedFilterPrimitive { radius_x: f32, radius_y: f32, }, + Turbulence { + kind: ResolvedFilterTurbulenceKind, + base_frequency_x: f32, + base_frequency_y: f32, + num_octaves: u8, + seed: f32, + stitch_tiles: bool, + }, + DisplacementMap { + scale: f32, + x_channel: ResolvedFilterDisplacementChannel, + y_channel: ResolvedFilterDisplacementChannel, + }, Merge, } @@ -160,6 +187,14 @@ pub(crate) struct ResolvedFilterNode { pub struct ResolvedFilter { pub(crate) region: n0_model::math::RectF, pub(crate) nodes: Arc<[ResolvedFilterNode]>, + /// Whether the graph can create output from a fully transparent source. + /// Such a graph needs one explicit transparent source raster even when + /// its scope contains no draw item, or a lazy layer restore is skipped. + pub(crate) may_paint_transparent_input: bool, + /// The invocation's isolated source is known to be fully transparent. + /// Source references are materialized explicitly so generated/additive + /// graph output is not skipped by a lazy backend layer. + pub(crate) source_is_transparent: bool, } /// Product-local owner slot for a source-neutral glyphless frame. diff --git a/crates/n0/src/glyphless.rs b/crates/n0/src/glyphless.rs index 4bbe4f1a..a3efb80a 100644 --- a/crates/n0/src/glyphless.rs +++ b/crates/n0/src/glyphless.rs @@ -23,17 +23,18 @@ use n0_model::model::{ }; use n0_model::path::ResolvedPathArtifact; use rframe::{ - ClipPath, FilterBlend, FilterColorSpace, FilterComposite, FilterInput, FilterMorphology, - FilterPrimitive, FilterProgram, Frame, FrameItem, Geometry, MaskMode, PaintStack, ScopeEffect, - VisualRef, + ClipPath, FilterBlend, FilterColorSpace, FilterComposite, FilterDisplacementChannel, + FilterInput, FilterMorphology, FilterPrimitive, FilterTurbulenceKind, Frame, FrameItem, + Geometry, MaskMode, PaintStack, ScopeEffect, VisualRef, }; use crate::damage::{diff_inputs, DamageOwner, FrameDamageInput}; use crate::drawlist::{ DrawList, GlyphlessOwnerSlot, Item, ItemKind, PostPaintOpacity, ResolvedClipGeometry, ResolvedClipGeometryKind, ResolvedClipLayer, ResolvedClipPath, ResolvedFilter, - ResolvedFilterBlend, ResolvedFilterColorSpace, ResolvedFilterComposite, ResolvedFilterInput, - ResolvedFilterMorphology, ResolvedFilterNode, ResolvedFilterPrimitive, ResolvedMaskMode, + ResolvedFilterBlend, ResolvedFilterColorSpace, ResolvedFilterComposite, + ResolvedFilterDisplacementChannel, ResolvedFilterInput, ResolvedFilterMorphology, + ResolvedFilterNode, ResolvedFilterPrimitive, ResolvedFilterTurbulenceKind, ResolvedMaskMode, StrokeDashPhase, }; use crate::frame::FrameExecutionError; @@ -356,7 +357,7 @@ pub fn compile(resolved: Frame) -> Result { provenance.owners.push(scope.owner); // Placeholder until the scope closes and its union is known. provenance.coverage.push(None); - let kind = match &scope.effect { + let (kind, initial_coverage) = match &scope.effect { ScopeEffect::Opacity(opacity) => { items.push(Item { node: slot, @@ -365,7 +366,7 @@ pub fn compile(resolved: Frame) -> Result { opacity: opacity.get(), }, }); - OpenScopeKind::Opacity + (OpenScopeKind::Opacity, None) } ScopeEffect::Clip(clip) => { let compiled = Arc::new(compile_clip_path(clip)); @@ -383,10 +384,10 @@ pub fn compile(resolved: Frame) -> Result { world: Affine::IDENTITY, kind: ItemKind::BeginClipPath { clip: compiled }, }); - OpenScopeKind::Clip { bounds } + (OpenScopeKind::Clip { bounds }, None) } ScopeEffect::Filter(filter) => { - let compiled = Arc::new(compile_filter(filter.program(), filter.region())); + let compiled = Arc::new(compile_filter(filter)); if let Err(reason) = crate::paint::preflight_filter(&compiled) { return Err(BuildError::Filter { owner: scope.owner, @@ -402,12 +403,19 @@ pub fn compile(resolved: Frame) -> Result { world: to_affine(filter.transform()), kind: ItemKind::BeginFilter { filter: compiled }, }); - OpenScopeKind::Filter { bounds } + let initial_coverage = if filter.source_is_transparent() + && filter.program().may_paint_transparent_input() + { + bounds + } else { + None + }; + (OpenScopeKind::Filter { bounds }, initial_coverage) } }; open_scopes.push(OpenScope { slot, - coverage: None, + coverage: initial_coverage, kind, }); continue; @@ -871,7 +879,8 @@ fn compile_clip_path(clip: &ClipPath) -> ResolvedClipPath { /// Project one checked, source-neutral filter program into private painter /// material. Every index and scalar has already been validated by `rframe`; /// this is a vocabulary translation, not a second resolver. -fn compile_filter(program: &FilterProgram, region: math2::Rectangle) -> ResolvedFilter { +fn compile_filter(filter: &rframe::Filter) -> ResolvedFilter { + let program = filter.program(); let nodes = program .iter() .map(|node| ResolvedFilterNode { @@ -969,14 +978,65 @@ fn compile_filter(program: &FilterProgram, region: math2::Rectangle) -> Resolved radius_x, radius_y, }, + FilterPrimitive::Turbulence { + kind, + base_frequency_x, + base_frequency_y, + num_octaves, + seed, + stitch_tiles, + } => ResolvedFilterPrimitive::Turbulence { + kind: match kind { + FilterTurbulenceKind::Turbulence => { + ResolvedFilterTurbulenceKind::Turbulence + } + FilterTurbulenceKind::FractalNoise => { + ResolvedFilterTurbulenceKind::FractalNoise + } + }, + base_frequency_x, + base_frequency_y, + num_octaves, + seed, + stitch_tiles, + }, + FilterPrimitive::DisplacementMap { + scale, + x_channel, + y_channel, + } => ResolvedFilterPrimitive::DisplacementMap { + scale, + x_channel: match x_channel { + FilterDisplacementChannel::Red => ResolvedFilterDisplacementChannel::Red, + FilterDisplacementChannel::Green => { + ResolvedFilterDisplacementChannel::Green + } + FilterDisplacementChannel::Blue => ResolvedFilterDisplacementChannel::Blue, + FilterDisplacementChannel::Alpha => { + ResolvedFilterDisplacementChannel::Alpha + } + }, + y_channel: match y_channel { + FilterDisplacementChannel::Red => ResolvedFilterDisplacementChannel::Red, + FilterDisplacementChannel::Green => { + ResolvedFilterDisplacementChannel::Green + } + FilterDisplacementChannel::Blue => ResolvedFilterDisplacementChannel::Blue, + FilterDisplacementChannel::Alpha => { + ResolvedFilterDisplacementChannel::Alpha + } + }, + }, FilterPrimitive::Merge => ResolvedFilterPrimitive::Merge, }, }) .collect::>() .into(); ResolvedFilter { - region: to_rectf(region), + region: to_rectf(filter.region()), nodes, + may_paint_transparent_input: program.may_paint_transparent_input(), + source_is_transparent: filter.source_is_transparent(), } } @@ -1540,8 +1600,8 @@ mod tests { }; use n0_model::resolve::ResolveOptions; use rframe::{ - Filter, FilterNode, FrameItems, FrameNode, Identity, PaintAlphaFactor, Provenance, Scope, - ScopeOpacity, + Filter, FilterNode, FilterProgram, FrameItems, FrameNode, Identity, PaintAlphaFactor, + Provenance, Scope, ScopeOpacity, }; use skia_safe::surfaces; @@ -1805,6 +1865,33 @@ mod tests { }) } + fn empty_turbulence_scope_begin(owner: VisualRef, seed: f32) -> FrameItem { + let region = Rectangle::from_xywh(4.0, 5.0, 10.0, 12.0); + let program = FilterProgram::new(Arc::from([FilterNode::new( + Arc::from([]), + region, + FilterColorSpace::LinearRgb, + FilterPrimitive::Turbulence { + kind: FilterTurbulenceKind::Turbulence, + base_frequency_x: 0.08, + base_frequency_y: 0.11, + num_octaves: 2, + seed, + stitch_tiles: false, + }, + )])) + .expect("test turbulence is a checked generated program"); + let transform = AffineTransform::from_acebdf(1.0, 0.0, 7.0, 0.0, 1.0, 8.0); + FrameItem::ScopeBegin(Scope { + owner, + effect: ScopeEffect::Filter( + Filter::new(transform, region, program) + .expect("test filter is a checked effect") + .with_transparent_source(), + ), + }) + } + fn clip_begin(owner: VisualRef, layers: Vec>) -> FrameItem { let layers = layers .into_iter() @@ -3057,6 +3144,31 @@ mod tests { ); } + #[test] + fn an_empty_generated_filter_edit_damages_its_transformed_region() { + let scene = |seed| { + let items = FrameItems::try_new(vec![ + empty_turbulence_scope_begin(SCOPE_OWNER, seed), + FrameItem::ScopeEnd, + ]) + .expect("a declared generated filter is meaningful without source draws"); + compile(frame_of(items)).expect("admitted generated-filter frame") + }; + let before = scene(2.0); + let after = scene(3.0); + let coverage = Rectangle::from_xywh(11.0, 13.0, 10.0, 12.0); + + assert_eq!(coverage_for(&before, SCOPE_OWNER), Some(to_rectf(coverage))); + assert_eq!( + diff_frame(&before, &after), + Damage { + changed: vec![SCOPE_OWNER], + union_frame: Some(coverage), + } + ); + assert!(diff_frame(&before, &before).is_empty()); + } + /// The contract's union/intersection normal form lowers to one private /// clip item and clips the enclosed paint without inventing a resource or /// an isolated layer. diff --git a/crates/n0/src/paint.rs b/crates/n0/src/paint.rs index d2a5b745..6789ecdf 100644 --- a/crates/n0/src/paint.rs +++ b/crates/n0/src/paint.rs @@ -27,17 +27,18 @@ use skia_safe::canvas::{SaveLayerFlags, SaveLayerRec}; use skia_safe::gradient::{Colors as GradientColors, Gradient, Interpolation}; use skia_safe::{ image::CachingHint, path_effect::PathEffect, shaders, stroke_rec::InitStyle, Blender, Canvas, - ClipOp, Color, Color4f, ColorMatrix, ColorSpace, CubicResampler, Data, Font, Image, - ImageFilter, ImageInfo, Matrix, OpBuilder, Paint, PaintCap, PaintJoin, PaintStyle, Path, - PathBuilder, PathDirection, PathFillType, PathOp, Point, RRect, Rect, SamplingOptions, Shader, - StrokeRec, + ClipOp, Color, Color4f, ColorChannel, ColorMatrix, ColorSpace, CubicResampler, Data, Font, + ISize, Image, ImageFilter, ImageInfo, Matrix, OpBuilder, Paint, PaintCap, PaintJoin, + PaintStyle, Path, PathBuilder, PathDirection, PathFillType, PathOp, Point, RRect, Rect, + SamplingOptions, Shader, StrokeRec, }; use crate::drawlist::{ DrawList, ItemKind, PostPaintOpacity, ResolvedClipGeometry, ResolvedClipGeometryKind, ResolvedClipLayer, ResolvedClipPath, ResolvedFilter, ResolvedFilterBlend, - ResolvedFilterColorSpace, ResolvedFilterComposite, ResolvedFilterInput, - ResolvedFilterMorphology, ResolvedFilterPrimitive, ResolvedMaskMode, StrokeDashPhase, + ResolvedFilterColorSpace, ResolvedFilterComposite, ResolvedFilterDisplacementChannel, + ResolvedFilterInput, ResolvedFilterMorphology, ResolvedFilterPrimitive, + ResolvedFilterTurbulenceKind, ResolvedMaskMode, StrokeDashPhase, }; /// The gradient family whose local matrix could not be represented by the @@ -1938,6 +1939,14 @@ struct BuiltFilterResult { /// input through one additional source-image boundary. Preserve that /// measured boundary through later graph nodes. source_preflatten: bool, + /// An upstream procedural shader needs procedural-scoped blend arithmetic + /// and final restore policy even after an intermediate sRGB result + /// materializes. The next field tracks that narrower blend-domain state. + procedural_provenance: bool, + /// A direct sRGB procedural shader still enters Skia's byte-domain blend + /// stages. Color conversion or another arithmetic primitive promotes that + /// route to floating point even though procedural provenance remains. + procedural_unorm8_blend: bool, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -1999,6 +2008,9 @@ fn convert_filter_space( // matrix-specific sRGB restore boundary. color_restore: None, source_preflatten: result.source_preflatten, + procedural_provenance: result.procedural_provenance, + // Gamma conversion promotes the next blend to Skia's floating path. + procedural_unorm8_blend: false, }) } @@ -2081,6 +2093,31 @@ fn floating_porter_duff_blender(operator: ResolvedFilterComposite) -> Result Result { + // Procedural shaders must stay floating until the final composition, but + // leaving the last N32 quantization implicit selects different Skia CPU + // packing paths on NEON and x86. Quantize only the composed result: doing + // so earlier changes Chromium's blend and morphology arithmetic. + let slot = 21 + porter_duff_slot(operator)?; + let expression = porter_duff_expression(operator)? + .replace("src", "s") + .replace("dst", "d"); + cached_filter_blender(slot, || { + format!( + r#" +half4 main(half4 src, half4 dst) {{ + float4 s = float4(src); + float4 d = float4(dst); + float4 result = {expression}; + return half4(floor(clamp(result, 0.0, 1.0) * 255.0 + 0.5) / 255.0); +}} +"# + ) + }) +} + fn exact_unorm8_blender(operator: ResolvedFilterComposite) -> Result { // Skia's low-precision blend stages deliberately use an approximate // divide-by-255 on x86 while NEON uses the accurate operation. Spell the @@ -2171,6 +2208,37 @@ half4 main(half4 src, half4 dst) {{ ) } +fn floating_filter_blend_source(expression: &str) -> String { + format!( + r#" +float overlay_channel(float s, float d, float sa, float da) {{ + float blend = 2.0 * d <= da + ? 2.0 * s * d + : sa * da - 2.0 * (sa - s) * (da - d); + return s * (1.0 - da) + d * (1.0 - sa) + blend; +}} + +float hard_light_channel(float s, float d, float sa, float da) {{ + float blend = 2.0 * s <= sa + ? 2.0 * s * d + : sa * da - 2.0 * (sa - s) * (da - d); + return s * (1.0 - da) + d * (1.0 - sa) + blend; +}} + +float3 unorm8_product(float3 value) {{ + return floor((value * 65025.0 + 127.0) / 255.0) / 255.0; +}} + +half4 main(half4 src, half4 dst) {{ + float4 s = float4(src); + float4 d = float4(dst); + float4 result = {expression}; + return half4(clamp(result, 0.0, 1.0)); +}} +"# + ) +} + fn deterministic_filter_blender(mode: ResolvedFilterBlend) -> Result { let (slot, expression) = match mode { ResolvedFilterBlend::Normal => (12, "s + div255(d * (255.0 - s.a))"), @@ -2208,27 +2276,96 @@ fn deterministic_filter_blender(mode: ResolvedFilterBlend) -> Result Result { + // The same nine modes that enter Skia's architecture-dependent lowp path + // need explicit formulas for procedural inputs. Most stay floating; direct + // or materialized sRGB difference/exclusion retain one measured unorm8 + // product-rounding step. The remaining modes use Skia's high-precision path. + let (slot, expression) = match mode { + ResolvedFilterBlend::Normal => (27, "s + d * (1.0 - s.a)"), + ResolvedFilterBlend::Multiply => ( + 28, + "s * (1.0 - d.a) + d * (1.0 - s.a) + s * d", + ), + ResolvedFilterBlend::Screen => (29, "s + d - s * d"), + ResolvedFilterBlend::Overlay => ( + 30, + "float4(overlay_channel(s.r, d.r, s.a, d.a), overlay_channel(s.g, d.g, s.a, d.a), overlay_channel(s.b, d.b, s.a, d.a), s.a + d.a - s.a * d.a)", + ), + ResolvedFilterBlend::Darken => (31, "s + d - max(s * d.a, d * s.a)"), + ResolvedFilterBlend::Lighten => (32, "s + d - min(s * d.a, d * s.a)"), + ResolvedFilterBlend::HardLight => ( + 33, + "float4(hard_light_channel(s.r, d.r, s.a, d.a), hard_light_channel(s.g, d.g, s.a, d.a), hard_light_channel(s.b, d.b, s.a, d.a), s.a + d.a - s.a * d.a)", + ), + ResolvedFilterBlend::Difference => ( + if unorm8_input { 36 } else { 34 }, + if unorm8_input { + "float4(s.rgb + d.rgb - 2.0 * unorm8_product(min(s.rgb * d.a, d.rgb * s.a)), s.a + d.a - s.a * d.a)" + } else { + "float4(s.rgb + d.rgb - 2.0 * min(s.rgb * d.a, d.rgb * s.a), s.a + d.a - s.a * d.a)" + }, + ), + ResolvedFilterBlend::Exclusion => ( + if unorm8_input { 37 } else { 35 }, + if unorm8_input { + "float4(s.rgb + d.rgb - 2.0 * unorm8_product(s.rgb * d.rgb), s.a + d.a - s.a * d.a)" + } else { + "float4(s.rgb + d.rgb - 2.0 * s.rgb * d.rgb, s.a + d.a - s.a * d.a)" + }, + ), + _ => return Ok(sk_filter_blend_mode(mode).into()), + }; + cached_filter_blender(slot, || floating_filter_blend_source(expression)) +} + +fn sk_displacement_channel(channel: ResolvedFilterDisplacementChannel) -> ColorChannel { + match channel { + ResolvedFilterDisplacementChannel::Red => ColorChannel::R, + ResolvedFilterDisplacementChannel::Green => ColorChannel::G, + ResolvedFilterDisplacementChannel::Blue => ColorChannel::B, + ResolvedFilterDisplacementChannel::Alpha => ColorChannel::A, + } +} + /// Build one checked private filter graph and its final-composition policy. fn build_filter(filter: &ResolvedFilter) -> Result { + let explicit_transparent_source = if filter.source_is_transparent { + Some(transparent_filter(filter.region).ok_or_else(|| { + "the backend could not construct an explicit transparent filter source".to_string() + })?) + } else { + None + }; let source = BuiltFilterResult { - image_filter: None, + image_filter: explicit_transparent_source.clone(), color_space: ResolvedFilterColorSpace::Srgb, source_dependent: true, requires_exact_restore: false, color_restore: None, source_preflatten: false, + procedural_provenance: false, + procedural_unorm8_blend: false, }; - let source_alpha = - BuiltFilterResult { - image_filter: Some(source_alpha_filter().ok_or_else(|| { + let source_alpha = BuiltFilterResult { + image_filter: if let Some(source) = explicit_transparent_source { + Some(source) + } else { + Some(source_alpha_filter().ok_or_else(|| { "the backend could not construct the SourceAlpha input".to_string() - })?), - color_space: ResolvedFilterColorSpace::Srgb, - source_dependent: true, - requires_exact_restore: false, - color_restore: None, - source_preflatten: false, - }; + })?) + }, + color_space: ResolvedFilterColorSpace::Srgb, + source_dependent: true, + requires_exact_restore: false, + color_restore: None, + source_preflatten: false, + procedural_provenance: false, + procedural_unorm8_blend: false, + }; let mut results: Vec = Vec::with_capacity(filter.nodes.len()); for node in filter.nodes.iter() { let mut inputs = Vec::with_capacity(node.inputs.len()); @@ -2292,6 +2429,19 @@ fn build_filter(filter: &ResolvedFilter) -> Result { ResolvedFilterPrimitive::Morphology { radius_x, radius_y, .. } if source_dependent && (*radius_x > 0.0 || *radius_y > 0.0) ); + let has_procedural_input = inputs.iter().any(|input| input.procedural_provenance); + let mut procedural_provenance = has_procedural_input + || matches!(&node.primitive, ResolvedFilterPrimitive::Turbulence { .. }); + let mut procedural_unorm8_blend = + if matches!(&node.primitive, ResolvedFilterPrimitive::Turbulence { .. }) { + node.color_space == ResolvedFilterColorSpace::Srgb + } else { + has_procedural_input + && inputs + .iter() + .filter(|input| input.procedural_provenance) + .all(|input| input.procedural_unorm8_blend) + }; let (image_filter, output_space, source_dependent, requires_exact_restore) = match node .primitive .clone() @@ -2400,8 +2550,13 @@ fn build_filter(filter: &ResolvedFilter) -> Result { ResolvedFilterPrimitive::Blend { mode } => { let background = inputs.pop().expect("blend has two checked inputs"); let foreground = inputs.pop().expect("blend has two checked inputs"); + let blender = if procedural_provenance { + procedural_filter_blender(mode, procedural_unorm8_blend)? + } else { + deterministic_filter_blender(mode)? + }; let filter = skia_safe::image_filters::blend( - deterministic_filter_blender(mode)?, + blender, background.image_filter, foreground.image_filter, crop, @@ -2413,6 +2568,11 @@ fn build_filter(filter: &ResolvedFilter) -> Result { // own floating-point arithmetic. let requires_exact_restore = requires_exact_restore || node.color_space == ResolvedFilterColorSpace::Srgb; + // The runtime blender computes this operation in the measured + // domain, then an sRGB result materializes before a later blend. + // Linear output remains floating. Final procedural provenance + // is independent and survives either transition. + procedural_unorm8_blend = node.color_space == ResolvedFilterColorSpace::Srgb; ( Some(filter), node.color_space, @@ -2519,6 +2679,7 @@ fn build_filter(filter: &ResolvedFilter) -> Result { } ResolvedFilterPrimitive::ColorMatrix { matrix } => { let input = inputs.pop().expect("color matrix has one checked input"); + procedural_unorm8_blend = false; let color_filter = skia_safe::color_filters::matrix_row_major(&matrix, None); let filter = skia_safe::image_filters::color_filter(color_filter, input.image_filter, crop) @@ -2539,6 +2700,7 @@ fn build_filter(filter: &ResolvedFilter) -> Result { let input = inputs .pop() .expect("component transfer has one checked input"); + procedural_unorm8_blend = false; let color_filter = skia_safe::color_filters::table_argb( Some(&tables[3]), Some(&tables[0]), @@ -2568,6 +2730,19 @@ fn build_filter(filter: &ResolvedFilter) -> Result { } => { let input = inputs.pop().expect("morphology has one checked input"); let active = radius_x > 0.0 || radius_y > 0.0; + // Active sRGB morphology over a direct procedural source + // establishes the exact byte restore Chromium uses for that + // kernel. A preceding exact blend leaves its stronger + // procedural-composition provenance intact. + if active + && node.color_space == ResolvedFilterColorSpace::Srgb + && !input.requires_exact_restore + { + procedural_provenance = false; + } + if active { + procedural_unorm8_blend = false; + } let filter = match operator { ResolvedFilterMorphology::Erode => skia_safe::image_filters::erode( (radius_x, radius_y), @@ -2599,6 +2774,77 @@ fn build_filter(filter: &ResolvedFilter) -> Result { requires_exact_restore, ) } + ResolvedFilterPrimitive::Turbulence { + kind, + base_frequency_x, + base_frequency_y, + num_octaves, + seed, + stitch_tiles, + } => { + // Blink truncates the finite primitive subregion dimensions + // to signed integer tile lengths before constructing a + // stitched Perlin source. Rust's float-to-int cast has the + // same saturating, truncating contract. + let tile_size = + stitch_tiles.then(|| ISize::new(node.region.w as i32, node.region.h as i32)); + let shader = match kind { + ResolvedFilterTurbulenceKind::Turbulence => shaders::turbulence( + (base_frequency_x, base_frequency_y), + usize::from(num_octaves), + seed, + tile_size, + ), + ResolvedFilterTurbulenceKind::FractalNoise => shaders::fractal_noise( + (base_frequency_x, base_frequency_y), + usize::from(num_octaves), + seed, + tile_size, + ), + } + .ok_or_else(|| "the backend could not construct a turbulence shader".to_string())?; + let filter = skia_safe::image_filters::shader(shader, crop).ok_or_else(|| { + "the backend could not construct a turbulence operation".to_string() + })?; + (Some(filter), node.color_space, false, false) + } + ResolvedFilterPrimitive::DisplacementMap { + scale, + x_channel, + y_channel, + } => { + let displacement = inputs + .pop() + .expect("displacement map has two checked inputs"); + let color = inputs + .pop() + .expect("displacement map has two checked inputs"); + let filter = skia_safe::image_filters::displacement_map( + ( + sk_displacement_channel(x_channel), + sk_displacement_channel(y_channel), + ), + scale, + displacement.image_filter, + color.image_filter, + crop, + ) + .ok_or_else(|| { + "the backend could not construct a displacement-map operation".to_string() + })?; + // The first hosted-x86 corpus run isolated the same final + // N32 restore split in every admitted sRGB displacement + // shape: 18 of the 22 failing rung cells (294 pixels total, + // all one code value). Keep the floating sampling operation + // native, then restore its sRGB result through the + // architecture-neutral byte path. + ( + Some(filter), + node.color_space, + source_dependent, + requires_exact_restore || node.color_space == ResolvedFilterColorSpace::Srgb, + ) + } ResolvedFilterPrimitive::Merge => { let mut inputs = inputs.into_iter(); let image_filter = if let Some(first) = inputs.next() { @@ -2648,6 +2894,8 @@ fn build_filter(filter: &ResolvedFilter) -> Result { requires_exact_restore, color_restore, source_preflatten, + procedural_provenance, + procedural_unorm8_blend, }); } let output = results @@ -2673,7 +2921,11 @@ fn build_filter(filter: &ResolvedFilter) -> Result { })?, ); } - let restore_blender = if output.requires_exact_restore { + let restore_blender = if output.procedural_provenance { + Some(quantized_floating_porter_duff_blender( + ResolvedFilterComposite::Over, + )?) + } else if output.requires_exact_restore { Some(exact_unorm8_blender(ResolvedFilterComposite::Over)?) } else if output.color_restore == Some(ColorRestore::Floating) { Some(floating_porter_duff_blender(ResolvedFilterComposite::Over)?) @@ -2738,6 +2990,8 @@ mod filter_policy_tests { matrix: identity(alpha_offset), }, }]), + may_paint_transparent_input: alpha_offset > 0.0, + source_is_transparent: false, } } @@ -2776,6 +3030,8 @@ mod filter_policy_tests { }, }, ]), + may_paint_transparent_input: true, + source_is_transparent: false, }; let generated = build_filter(&generated).expect("generated matrix builds"); assert!(!generated.source_preflatten); @@ -2804,6 +3060,8 @@ mod filter_policy_tests { let source = ResolvedFilter { region: REGION, nodes: Arc::from([transfer(ResolvedFilterInput::Source, identity_tables(0))]), + may_paint_transparent_input: false, + source_is_transparent: false, }; let source = build_filter(&source).expect("source table builds"); assert!(source.source_preflatten); @@ -2812,6 +3070,8 @@ mod filter_policy_tests { let alpha_creating = ResolvedFilter { region: REGION, nodes: Arc::from([transfer(ResolvedFilterInput::Source, identity_tables(127))]), + may_paint_transparent_input: true, + source_is_transparent: false, }; let alpha_creating = build_filter(&alpha_creating).expect("alpha table builds"); assert!(alpha_creating.source_preflatten); @@ -2830,6 +3090,8 @@ mod filter_policy_tests { }, transfer(ResolvedFilterInput::Node(0), identity_tables(0)), ]), + may_paint_transparent_input: true, + source_is_transparent: false, }; let generated = build_filter(&generated).expect("generated table builds"); assert!(!generated.source_preflatten); @@ -2851,6 +3113,8 @@ mod filter_policy_tests { let one = |node| ResolvedFilter { region: REGION, nodes: Arc::from([node]), + may_paint_transparent_input: false, + source_is_transparent: false, }; let active = build_filter(&one(morphology( @@ -2903,6 +3167,8 @@ mod filter_policy_tests { ResolvedFilterColorSpace::Srgb, ), ]), + may_paint_transparent_input: true, + source_is_transparent: false, }; let generated = build_filter(&generated).expect("generated morphology builds"); assert!(!generated.source_preflatten); @@ -3193,6 +3459,13 @@ fn text_path( /// [`crate::frame::FrameProduct::execute`], which refuses a context whose /// incarnation or resource revision differs from the one captured at build. pub fn execute_unchecked(canvas: &Canvas, list: &DrawList, view: &Affine, ctx: &PaintCtx) { + // Install Skia's runtime-selected raster pipeline before any drawlist + // replay. Without this initialization, x86 stays on the baseline SSE + // implementation while ARM enters the default NEON implementation. That + // changes the fused arithmetic used by procedural shaders such as Perlin + // noise and can move a boundary value across N32 quantization. + skia_safe::graphics::init(); + #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum Scope { Opacity, @@ -3339,6 +3612,25 @@ pub fn execute_unchecked(canvas: &Canvas, list: &DrawList, view: &Affine, if built_filter.source_preflatten { canvas.save_layer(&SaveLayerRec::default()); } + if filter.source_is_transparent && filter.may_paint_transparent_input { + // A source-independent primitive still needs a material + // source layer for Skia to evaluate its restore filter. + // Seed that lazy layer with an opaque Src draw; the graph + // cannot observe it because every Source input was replaced + // above by an explicit transparent filter. + let mut seed = Paint::default(); + seed.set_blend_mode(skia_safe::BlendMode::Src); + let region = Rect::from_xywh( + filter.region.x, + filter.region.y, + filter.region.w, + filter.region.h, + ); + // This raster only forces the lazy-layer restore to run and + // cannot leak into graph output. + seed.set_color(Color::BLACK); + canvas.draw_rect(region, &seed); + } scopes.push(Scope::Filter { source_preflatten: built_filter.source_preflatten, }); diff --git a/crates/n0_cli/README.md b/crates/n0_cli/README.md index fb416426..d3f899f0 100644 --- a/crates/n0_cli/README.md +++ b/crates/n0_cli/README.md @@ -403,6 +403,30 @@ cargo run -p n0_cli --bin n0 -- \ retained fill-only ellipse coverage boundary. Rounded rectangles, curved paths, and circle/path strokes stay admitted. That last patrol leaves gridaco/nothing#88 separate and unchanged. + Turbulence carries both procedural formulas: the case-sensitive values + `turbulence` and `fractalNoise`, one/two-axis non-negative `baseFrequency`, + integer `numOctaves` capped at nine, signed `seed`, and the case-sensitive + values `stitch` and `noStitch`. Missing and invalid fields take their measured + initials; either negative frequency resets the pair, zero octaves still + reaches the selected formula, and a negative octave count produces a + transparent image. The generated source is bounded by its primitive region + and participates in its declared filter color space. A user-space filter may + therefore paint a target + whose isolated source is fully transparent; object-box filter units on the + same zero-area target produce no region and paint nothing. + Displacement map carries two ordered image inputs, a finite signed `scale`, + and independent case-sensitive `R | G | B | A` selectors with initial alpha. + Selection reads non-premultiplied map channels after conversion into the + primitive's filter color space. Under object-box primitive units Chromium's + one native displacement scalar uses the target width for both axes. Exact + evidence covers transparent and partial-alpha maps, source/source-alpha and + generated maps, procedural maps, hard regions, stroke/path sources, opacity, + safe axis mappings, exact quarter turns, ``, and non-uniform `viewBox`. + General rotations and shears for either primitive, and geometric clipping + around displacement, refuse by three stable precision names. Axis maps, + fractional translation and scale, reflections, and exact quarter turns stay + admitted. The wider shared graph, region, color, resource, and animation + surfaces retain their own boundaries. `filterUnits` and `primitiveUnits` carry their complete case-sensitive `userSpaceOnUse | objectBoundingBox` grammars, defaults, and invalid-value fallbacks. Filter and primitive regions accept admitted finite numbers, @@ -424,24 +448,48 @@ cargo run -p n0_cli --bin n0 -- \ boundary: the first full-workspace hosted-x86 run differed in nine cells and 1,633 pixels, all by one channel level, at the final filter-layer restore. Its scoped exact byte-domain restore leaves zero radius and later color-space - conversion on their prior paths. The same hosted workspace test now passes, - so all two hundred forty-eight Chromium-baked filter cells are exact on the - current ARM host and hosted x86 without a tolerance. The filter - estate is twenty-six from the chassis/blur slice, sixty from the - shadow-graph rung, twenty-eight from native drop shadow, twenty-seven from - color matrix, thirty-two from component transfer, and thirty-eight from - blend, plus thirty-seven from morphology. The complete corpus is 609 - Chromium-baked cells plus 10 sampled frames, with 140 - named refusal rows. `feFlood`, `feComposite`, `feMerge`, `feMergeNode`, - `feDropShadow`, `feColorMatrix`, `feComponentTransfer`, `feBlend`, - `feMorphology`, `feFuncR`, `feFuncG`, `feFuncB`, `feFuncA`, `k1`–`k4`, - `amplitude`, `exponent`, - `intercept`, `slope`, `tableValues`, and blend-only `mode` close; `feOffset`, - `feGaussianBlur`, ``, + conversion on their prior paths. Procedural sources carry floating + provenance through blend arithmetic. Direct sRGB procedural + `difference`/`exclusion` retains the pinned backend's byte-domain product + rounding; linear or component-transferred inputs use floating products. An + sRGB blend result materializes before a later blend; linear output remains + floating, and a later transfer promotes either route again. The final + procedural restore quantizes only the composed output with explicit + half-up byte rounding. Direct active sRGB morphology materializes procedural + provenance; blend before morphology preserves it. sRGB displacement output + takes the exact byte-domain restore. Empty generated-source scopes seed + damage coverage from their transformed filter region, so procedural + parameter edits damage pixels even without a source draw. Fifteen + operation-order, color-transition, and blend-domain controls guard these + distinctions. The first hosted-x86 run found 294 one-code-value pixels across + eighteen sRGB displacement and four procedural cells. The displacement repair + cleared all eighteen on the second run, which left 729 delta-1 pixels in four + procedural cells. Scoped procedural arithmetic cleared the 726-pixel blend + control on the third run, leaving three singleton direct-noise pixels. Pinned + Skia source located them in an uninitialized runtime raster-pipeline dispatch: + x86 stayed on baseline non-fused Perlin arithmetic while ARM used fused NEON. + Initializing Skia before drawlist replay selects the fused AVX2 path on x86. + The complete 700-cell gate is byte-exact on ARM and hosted x86 without a + tolerance. + The same hosted workspace test covers the earlier two hundred + forty-eight-cell estate; all three hundred thirty-nine + Chromium-baked filter cells are exact on the current ARM host without a + tolerance. The filter estate contains 26 chassis/blur cells, 60 shadow-graph, + 28 native drop-shadow, 27 color-matrix, 32 component-transfer, 38 blend, 37 + morphology, and 91 turbulence/displacement cells. The complete corpus + contains 700 Chromium-baked cells plus 10 sampled frames, with 143 named + refusal rows. `feFlood`, `feComposite`, + `feMerge`, `feMergeNode`, `feDropShadow`, `feColorMatrix`, + `feComponentTransfer`, `feBlend`, + `feMorphology`, `feTurbulence`, `feDisplacementMap`, `feFuncR`, `feFuncG`, + `feFuncB`, `feFuncA`, `k1`–`k4`, `amplitude`, `exponent`, `intercept`, + `slope`, `tableValues`, blend-only `mode`, `baseFrequency`, `numOctaves`, + `seed`, `stitchTiles`, displacement `scale`, `xChannelSelector`, and + `yChannelSelector` close; `feOffset`, `feGaussianBlur`, ``, `filter`, `color-interpolation-filters`, `in`, `in2`, `operator`, `result`, `radius`, `dx`, `dy`, `stdDeviation`, `flood-color`, and `flood-opacity` - remain open for - the named precision, applicability, resource, cascade, or value remainder. + remain open for the named precision, applicability, resource, cascade, or + value remainder. A stroke is centred, its width is a cascaded length in either spelling — numbers, absolute units, `em`/`rem` against an authored or default font-size, percentages against the normalized diagonal, and pure-length diff --git a/crates/rframe/src/filter.rs b/crates/rframe/src/filter.rs index aed647fb..daca866a 100644 --- a/crates/rframe/src/filter.rs +++ b/crates/rframe/src/filter.rs @@ -81,6 +81,22 @@ pub enum FilterMorphology { Dilate, } +/// One source-neutral procedural-noise formula. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum FilterTurbulenceKind { + Turbulence, + FractalNoise, +} + +/// One non-premultiplied channel selected from a displacement image. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum FilterDisplacementChannel { + Red, + Green, + Blue, + Alpha, +} + /// Four exact byte lookup tables for one non-premultiplied RGBA operation. /// /// Channel order is named at construction and access, so a producer cannot @@ -170,6 +186,22 @@ pub enum FilterPrimitive { radius_x: f32, radius_y: f32, }, + /// A bounded four-channel procedural-noise source. The octave count is + /// already capped to the resolved algorithm's meaningful range. + Turbulence { + kind: FilterTurbulenceKind, + base_frequency_x: f32, + base_frequency_y: f32, + num_octaves: u8, + seed: f32, + stitch_tiles: bool, + }, + /// Displace `inputs[0]` by non-premultiplied channels from `inputs[1]`. + DisplacementMap { + scale: f32, + x_channel: FilterDisplacementChannel, + y_channel: FilterDisplacementChannel, + }, Merge, } @@ -254,6 +286,12 @@ pub enum FilterProgramError { InvalidMorphology { node: usize, }, + InvalidTurbulence { + node: usize, + }, + InvalidDisplacementMap { + node: usize, + }, } impl std::fmt::Display for FilterProgramError { @@ -306,6 +344,13 @@ impl std::fmt::Display for FilterProgramError { f, "filter node {node} has a non-finite or negative morphology radius" ), + Self::InvalidTurbulence { node } => write!( + f, + "filter node {node} has a non-finite/negative frequency, non-finite seed, or more than nine octaves" + ), + Self::InvalidDisplacementMap { node } => { + write!(f, "filter node {node} has a non-finite displacement scale") + } } } } @@ -332,8 +377,10 @@ impl FilterProgram { | FilterPrimitive::ColorMatrix { .. } | FilterPrimitive::ComponentTransfer { .. } | FilterPrimitive::Morphology { .. } => Some(1), - FilterPrimitive::SolidColor { .. } => Some(0), - FilterPrimitive::Composite { .. } | FilterPrimitive::Blend { .. } => Some(2), + FilterPrimitive::SolidColor { .. } | FilterPrimitive::Turbulence { .. } => Some(0), + FilterPrimitive::Composite { .. } + | FilterPrimitive::Blend { .. } + | FilterPrimitive::DisplacementMap { .. } => Some(2), FilterPrimitive::Merge => None, }; if let Some(expected) = expected_inputs @@ -399,6 +446,24 @@ impl FilterProgram { { return Err(FilterProgramError::InvalidMorphology { node: index }); } + FilterPrimitive::Turbulence { + base_frequency_x, + base_frequency_y, + num_octaves, + seed, + .. + } if !base_frequency_x.is_finite() + || !base_frequency_y.is_finite() + || base_frequency_x < 0.0 + || base_frequency_y < 0.0 + || num_octaves > 9 + || !seed.is_finite() => + { + return Err(FilterProgramError::InvalidTurbulence { node: index }); + } + FilterPrimitive::DisplacementMap { scale, .. } if !scale.is_finite() => { + return Err(FilterProgramError::InvalidDisplacementMap { node: index }); + } FilterPrimitive::Offset { .. } | FilterPrimitive::SolidColor { .. } | FilterPrimitive::Composite { .. } @@ -407,6 +472,8 @@ impl FilterProgram { | FilterPrimitive::ColorMatrix { .. } | FilterPrimitive::ComponentTransfer { .. } | FilterPrimitive::Morphology { .. } + | FilterPrimitive::Turbulence { .. } + | FilterPrimitive::DisplacementMap { .. } | FilterPrimitive::Merge => {} } } @@ -432,12 +499,14 @@ impl FilterProgram { } => k4 > 0.0, FilterPrimitive::ColorMatrix { matrix } => matrix[19] > 0.0, FilterPrimitive::ComponentTransfer { tables } => tables.alpha()[0] > 0, + FilterPrimitive::Turbulence { .. } => true, FilterPrimitive::GaussianBlur { .. } | FilterPrimitive::Offset { .. } | FilterPrimitive::Composite { .. } | FilterPrimitive::Blend { .. } | FilterPrimitive::DropShadow { .. } | FilterPrimitive::Morphology { .. } + | FilterPrimitive::DisplacementMap { .. } | FilterPrimitive::Merge => false, }) } @@ -469,6 +538,7 @@ pub struct Filter { transform: AffineTransform, region: Rectangle, program: FilterProgram, + source_is_transparent: bool, } impl Filter { @@ -487,9 +557,20 @@ impl Filter { transform, region, program, + source_is_transparent: false, }) } + /// Declare that this invocation's isolated source image is fully + /// transparent. Generated/additive nodes may still paint; a consumer must + /// materialize an explicit transparent source rather than a lazy backend + /// input sentinel for that case. + #[must_use] + pub fn with_transparent_source(mut self) -> Self { + self.source_is_transparent = true; + self + } + #[must_use] pub const fn transform(&self) -> AffineTransform { self.transform @@ -504,6 +585,11 @@ impl Filter { pub const fn program(&self) -> &FilterProgram { &self.program } + + #[must_use] + pub const fn source_is_transparent(&self) -> bool { + self.source_is_transparent + } } fn valid_rect(rect: Rectangle) -> bool { diff --git a/crates/rframe/src/frame.rs b/crates/rframe/src/frame.rs index 9bf7e122..54662304 100644 --- a/crates/rframe/src/frame.rs +++ b/crates/rframe/src/frame.rs @@ -340,9 +340,10 @@ pub struct FrameNode { /// /// A scope's contents are the items between its begin and its end — a /// contiguous span, because an isolated group *is* a contiguous span of -/// painter order. Balance, nesting depth, and non-emptiness are invariants -/// of [`FrameItems`] construction, so a consumer matching on this enum -/// never meets a dangling boundary. +/// painter order. Balance, nesting depth, and meaningful content are +/// invariants of [`FrameItems`] construction, so a consumer matching on this +/// enum never meets a dangling boundary. The one meaningful empty span is a +/// filter whose declared transparent source can itself generate output. #[derive(Clone, Debug, PartialEq)] pub enum FrameItem { /// One resolved painted node. @@ -376,8 +377,8 @@ pub enum FrameItemsError { /// The [`FrameItem::ScopeBegin`] at this index is never closed. UnclosedScope { index: usize }, /// The [`FrameItem::ScopeBegin`] at this index encloses nothing. An - /// empty group is not a resolved visual fact — a producer whose scope - /// resolved to nothing states nothing. + /// empty group is not a resolved visual fact — except for a filter whose + /// declared transparent source can itself generate output. EmptyScope { index: usize }, /// The [`FrameItem::ScopeBegin`] at this index nests deeper than /// [`MAX_SCOPE_DEPTH`]. @@ -439,8 +440,9 @@ impl std::fmt::Display for FrameItemsError { impl std::error::Error for FrameItemsError {} -/// A checked painter-ordered item stream: every scope is balanced, -/// non-empty, and nested within [`MAX_SCOPE_DEPTH`]. +/// A checked painter-ordered item stream: every scope is balanced, meaningful, +/// and nested within [`MAX_SCOPE_DEPTH`]. Only a source-generating filter may +/// be meaningful without enclosed items. /// /// Like [`PathData`], this is a checked type: construction proves the /// invariants once, and a consumer trusts them rather than re-deriving @@ -452,7 +454,7 @@ impl FrameItems { pub fn try_new(items: Vec) -> Result { #[derive(Clone, Copy)] enum OpenKind { - Scope, + Scope { permits_empty: bool }, MaskTarget, MaskSource, } @@ -474,13 +476,20 @@ impl FrameItems { for (index, item) in items.iter().enumerate() { match item { FrameItem::Node(_) => complete_item(&mut open), - FrameItem::ScopeBegin(_) => { + FrameItem::ScopeBegin(scope) => { if open.len() >= MAX_SCOPE_DEPTH { return Err(FrameItemsError::ScopeTooDeep { index }); } open.push(Open { index, - kind: OpenKind::Scope, + kind: OpenKind::Scope { + permits_empty: matches!( + &scope.effect, + crate::scope::ScopeEffect::Filter(filter) + if filter.source_is_transparent() + && filter.program().may_paint_transparent_input() + ), + }, has_content: false, }); } @@ -488,10 +497,10 @@ impl FrameItems { let Some(current) = open.last().copied() else { return Err(FrameItemsError::UnopenedScopeEnd { index }); }; - if !matches!(current.kind, OpenKind::Scope) { + let OpenKind::Scope { permits_empty } = current.kind else { return Err(FrameItemsError::UnopenedScopeEnd { index }); - } - if !current.has_content { + }; + if !current.has_content && !permits_empty { return Err(FrameItemsError::EmptyScope { index: current.index, }); @@ -538,7 +547,7 @@ impl FrameItems { } if let Some(current) = open.first() { return Err(match current.kind { - OpenKind::Scope => FrameItemsError::UnclosedScope { + OpenKind::Scope { .. } => FrameItemsError::UnclosedScope { index: current.index, }, OpenKind::MaskTarget | OpenKind::MaskSource => FrameItemsError::UnclosedMask { diff --git a/crates/rframe/src/lib.rs b/crates/rframe/src/lib.rs index 1dc89455..e183402e 100644 --- a/crates/rframe/src/lib.rs +++ b/crates/rframe/src/lib.rs @@ -25,9 +25,9 @@ pub use clip::{ MAX_CLIP_GEOMETRIES_PER_LAYER, MAX_CLIP_LAYERS, }; pub use filter::{ - Filter, FilterBlend, FilterChannelTables, FilterColorSpace, FilterComposite, FilterError, - FilterInput, FilterMorphology, FilterNode, FilterPrimitive, FilterProgram, FilterProgramError, - MAX_FILTER_NODES, + Filter, FilterBlend, FilterChannelTables, FilterColorSpace, FilterComposite, + FilterDisplacementChannel, FilterError, FilterInput, FilterMorphology, FilterNode, + FilterPrimitive, FilterProgram, FilterProgramError, FilterTurbulenceKind, 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 a401a30e..15a9aa55 100644 --- a/crates/rframe/tests/filter_contract.rs +++ b/crates/rframe/tests/filter_contract.rs @@ -5,9 +5,9 @@ use std::sync::Arc; use math2::Rectangle; use math2::transform::AffineTransform; use rframe::{ - Filter, FilterBlend, FilterChannelTables, FilterColorSpace, FilterComposite, FilterError, - FilterInput, FilterMorphology, FilterNode, FilterPrimitive, FilterProgram, FilterProgramError, - MAX_FILTER_NODES, + Filter, FilterBlend, FilterChannelTables, FilterColorSpace, FilterComposite, + FilterDisplacementChannel, FilterError, FilterInput, FilterMorphology, FilterNode, + FilterPrimitive, FilterProgram, FilterProgramError, FilterTurbulenceKind, MAX_FILTER_NODES, }; fn blur(input: FilterInput, sigma_x: f32, sigma_y: f32) -> FilterNode { @@ -395,6 +395,172 @@ fn morphology_has_one_input_and_checked_non_negative_radii() { } } +#[test] +fn turbulence_is_a_zero_input_generated_source_with_checked_parameters() { + let region = Rectangle::from_xywh(0.0, 0.0, 10.0, 10.0); + let turbulence = |inputs: Arc<[FilterInput]>, + kind, + base_frequency_x, + base_frequency_y, + num_octaves, + seed, + stitch_tiles| { + FilterNode::new( + inputs, + region, + FilterColorSpace::LinearRgb, + FilterPrimitive::Turbulence { + kind, + base_frequency_x, + base_frequency_y, + num_octaves, + seed, + stitch_tiles, + }, + ) + }; + + assert_eq!( + FilterProgram::new(Arc::from([turbulence( + Arc::from([FilterInput::Source]), + FilterTurbulenceKind::Turbulence, + 0.1, + 0.2, + 1, + 0.0, + false, + )])), + Err(FilterProgramError::InvalidInputCount { + node: 0, + expected: 0, + actual: 1, + }) + ); + + for (base_frequency_x, base_frequency_y, num_octaves, seed) in [ + (-0.1, 0.0, 1, 0.0), + (0.0, -0.1, 1, 0.0), + (f32::INFINITY, 0.0, 1, 0.0), + (0.0, f32::NAN, 1, 0.0), + (0.0, 0.0, 10, 0.0), + (0.0, 0.0, 1, f32::INFINITY), + ] { + assert_eq!( + FilterProgram::new(Arc::from([turbulence( + Arc::from([]), + FilterTurbulenceKind::FractalNoise, + base_frequency_x, + base_frequency_y, + num_octaves, + seed, + true, + )])), + Err(FilterProgramError::InvalidTurbulence { node: 0 }) + ); + } + + for kind in [ + FilterTurbulenceKind::Turbulence, + FilterTurbulenceKind::FractalNoise, + ] { + for num_octaves in [0, 1, 9] { + let program = FilterProgram::new(Arc::from([turbulence( + Arc::from([]), + kind, + 0.0, + 256.0, + num_octaves, + -7.5, + true, + )])) + .expect("finite non-negative frequencies and at most nine octaves are resolved facts"); + assert!(program.may_paint_transparent_input()); + } + } +} + +#[test] +fn displacement_map_has_two_ordered_inputs_and_checked_channel_vocabulary() { + let region = Rectangle::from_xywh(0.0, 0.0, 10.0, 10.0); + let displacement = |inputs: Arc<[FilterInput]>, scale, x_channel, y_channel| { + FilterNode::new( + inputs, + region, + FilterColorSpace::Srgb, + FilterPrimitive::DisplacementMap { + scale, + x_channel, + y_channel, + }, + ) + }; + + assert_eq!( + FilterProgram::new(Arc::from([displacement( + Arc::from([FilterInput::Source]), + 20.0, + FilterDisplacementChannel::Red, + FilterDisplacementChannel::Green, + )])), + Err(FilterProgramError::InvalidInputCount { + node: 0, + expected: 2, + actual: 1, + }) + ); + assert_eq!( + FilterProgram::new(Arc::from([displacement( + Arc::from([FilterInput::Source, FilterInput::SourceAlpha]), + f32::NAN, + FilterDisplacementChannel::Blue, + FilterDisplacementChannel::Alpha, + )])), + Err(FilterProgramError::InvalidDisplacementMap { node: 0 }) + ); + + let channels = [ + FilterDisplacementChannel::Red, + FilterDisplacementChannel::Green, + FilterDisplacementChannel::Blue, + FilterDisplacementChannel::Alpha, + ]; + for x_channel in channels { + for y_channel in channels { + let program = FilterProgram::new(Arc::from([displacement( + Arc::from([FilterInput::Source, FilterInput::SourceAlpha]), + -256.5, + x_channel, + y_channel, + )])) + .expect("finite signed scale and two named channels are resolved facts"); + assert!(!program.may_paint_transparent_input()); + } + } +} + +#[test] +fn a_filter_invocation_names_when_its_source_is_fully_transparent() { + let region = Rectangle::from_xywh(0.0, 0.0, 10.0, 10.0); + let program = FilterProgram::new(Arc::from([FilterNode::new( + Arc::from([]), + region, + FilterColorSpace::LinearRgb, + FilterPrimitive::Turbulence { + kind: FilterTurbulenceKind::Turbulence, + base_frequency_x: 0.1, + base_frequency_y: 0.2, + num_octaves: 2, + seed: 3.0, + stitch_tiles: false, + }, + )])) + .expect("generated source program"); + let filter = Filter::new(AffineTransform::identity(), region, program) + .expect("checked filter invocation"); + assert!(!filter.source_is_transparent()); + assert!(filter.with_transparent_source().source_is_transparent()); +} + #[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/rframe/tests/scope_stream.rs b/crates/rframe/tests/scope_stream.rs index 623c1c02..354ecf9b 100644 --- a/crates/rframe/tests/scope_stream.rs +++ b/crates/rframe/tests/scope_stream.rs @@ -10,9 +10,11 @@ use cg::CGColor; use math2::Rectangle; use math2::transform::AffineTransform; use rframe::{ - FrameItem, FrameItems, FrameItemsError, FrameNode, Geometry, Identity, MAX_SCOPE_DEPTH, - PaintStack, Provenance, Scope, ScopeEffect, ScopeOpacity, VisualRef, + Filter, FilterColorSpace, FilterInput, FilterNode, FilterPrimitive, FilterProgram, + FilterTurbulenceKind, FrameItem, FrameItems, FrameItemsError, FrameNode, Geometry, Identity, + MAX_SCOPE_DEPTH, PaintStack, Provenance, Scope, ScopeEffect, ScopeOpacity, VisualRef, }; +use std::sync::Arc; fn owner(id: u64) -> VisualRef { VisualRef::new(Identity::new(id), Provenance::new(id)) @@ -37,6 +39,48 @@ fn begin(id: u64) -> FrameItem { }) } +fn filter_begin(id: u64, generated: bool, transparent_source: bool) -> FrameItem { + let region = Rectangle::from_xywh(0.0, 0.0, 40.0, 40.0); + let primitive = if generated { + FilterPrimitive::Turbulence { + kind: FilterTurbulenceKind::Turbulence, + base_frequency_x: 0.1, + base_frequency_y: 0.2, + num_octaves: 2, + seed: 3.0, + stitch_tiles: false, + } + } else { + FilterPrimitive::GaussianBlur { + sigma_x: 2.0, + sigma_y: 2.0, + } + }; + let inputs: Arc<[FilterInput]> = if generated { + Arc::from([]) + } else { + Arc::from([FilterInput::Source]) + }; + let program = FilterProgram::new(Arc::from([FilterNode::new( + inputs, + region, + FilterColorSpace::LinearRgb, + primitive, + )])) + .expect("test filter graph is checked"); + let filter = Filter::new(AffineTransform::identity(), region, program) + .expect("test filter invocation is checked"); + let filter = if transparent_source { + filter.with_transparent_source() + } else { + filter + }; + FrameItem::ScopeBegin(Scope { + owner: owner(id), + effect: ScopeEffect::Filter(filter), + }) +} + #[test] fn a_balanced_nested_stream_is_admitted() { let items = FrameItems::try_new(vec![ @@ -82,6 +126,24 @@ fn an_empty_scope_is_refused_at_its_begin() { ); } +#[test] +fn a_generated_filter_over_a_declared_transparent_source_is_meaningful_when_empty() { + FrameItems::try_new(vec![filter_begin(1, true, true), FrameItem::ScopeEnd]) + .expect("the generated filter output is the empty span's visual fact"); +} + +#[test] +fn an_empty_filter_cannot_claim_meaning_without_both_contract_facts() { + assert_eq!( + FrameItems::try_new(vec![filter_begin(1, true, false), FrameItem::ScopeEnd]), + Err(FrameItemsError::EmptyScope { index: 0 }) + ); + assert_eq!( + FrameItems::try_new(vec![filter_begin(1, false, true), FrameItem::ScopeEnd]), + Err(FrameItemsError::EmptyScope { index: 0 }) + ); +} + /// A scope holding only another scope is not empty — group-of-group is a /// resolved fact (a container whose one child is itself a real layer). #[test] diff --git a/crates/websem/src/svg.rs b/crates/websem/src/svg.rs index 6d9a4c74..50223236 100644 --- a/crates/websem/src/svg.rs +++ b/crates/websem/src/svg.rs @@ -136,11 +136,11 @@ use math2::Rectangle; use math2::transform::AffineTransform; use rframe::{ ClipGeometry, ClipLayer, ClipPath, FillRule, Filter, FilterBlend, FilterChannelTables, - FilterColorSpace, FilterComposite, FilterInput, FilterMorphology, 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, + FilterColorSpace, FilterComposite, FilterDisplacementChannel, FilterInput, FilterMorphology, + FilterNode, FilterPrimitive, FilterProgram, FilterTurbulenceKind, 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; @@ -2810,10 +2810,13 @@ impl<'a> ChildWalk<'a> { &mut self, checkpoint: usize, mut facts: SpanFacts, - filter: Filter, + mut filter: Filter, ) -> SpanFacts { - if facts.draws == 0 && !facts.has_scope && !filter.program().may_paint_transparent_input() { - return facts; + if facts.draws == 0 && !facts.has_scope { + if !filter.program().may_paint_transparent_input() { + return facts; + } + filter = filter.with_transparent_source(); } self.items .insert(checkpoint, filter_scope_item(&mut self.next_id, filter)); @@ -5009,6 +5012,74 @@ mod filter_resource { } } + fn turbulence_kind(element: HtmlElement<'_>) -> FilterTurbulenceKind { + match get_attr(element, "type").as_deref() { + Some("fractalNoise") => FilterTurbulenceKind::FractalNoise, + // SVG enumerations are case-sensitive and do not trim authored + // text. Missing and every invalid spelling retain turbulence. + _ => FilterTurbulenceKind::Turbulence, + } + } + + /// Complete SVG number-optional-number grammar for `baseFrequency`. + /// Invalid lists select the initial pair; one member duplicates; one + /// negative member makes the whole pair unsupported and therefore initial. + fn turbulence_base_frequency(element: HtmlElement<'_>) -> (f32, f32) { + let values = get_attr(element, "baseFrequency") + .as_deref() + .and_then(crate::svg_number_list::parse); + let pair = match values.as_deref() { + Some([value]) => (*value, *value), + Some([x, y]) => (*x, *y), + _ => (0.0, 0.0), + }; + if pair.0 < 0.0 || pair.1 < 0.0 { + (0.0, 0.0) + } else { + pair + } + } + + /// A resolved non-negative octave count capped to Blink's eight-bit-output + /// ceiling. `None` is the measured negative-count transparent result; + /// zero still reaches the formula (and fractal noise therefore yields its + /// neutral half sample). + fn turbulence_num_octaves(element: HtmlElement<'_>) -> Option { + let parsed = get_attr(element, "numOctaves") + .as_deref() + .map(trim_svg_whitespace) + .and_then(|raw| raw.parse::().ok()) + .unwrap_or(1); + (parsed >= 0).then(|| parsed.min(9) as u8) + } + + /// One SVG number through the shared ordered evaluator. A lone trailing + /// comma is accepted by Blink; every other residual token selects initial. + fn resolved_svg_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 turbulence_stitches(element: HtmlElement<'_>) -> bool { + matches!(get_attr(element, "stitchTiles").as_deref(), Some("stitch")) + } + + fn displacement_channel(element: HtmlElement<'_>, name: &str) -> FilterDisplacementChannel { + match get_attr(element, name).as_deref() { + Some("R") => FilterDisplacementChannel::Red, + Some("G") => FilterDisplacementChannel::Green, + Some("B") => FilterDisplacementChannel::Blue, + // Missing and every invalid, padded, or wrong-case spelling + // retain the initial alpha selector. + _ => FilterDisplacementChannel::Alpha, + } + } + enum ComponentTransferFunction { Identity, Table(Vec), @@ -5556,6 +5627,15 @@ mod filter_resource { axis_or_quarter_turn && [a, b, c, d].into_iter().all(f32::is_finite) } + /// Procedural noise and displacement agree for axis maps and exact + /// quarter turns. General rotations and shears cross the pinned backend's + /// filtered-layer sampling boundary. + fn turbulence_displacement_mapping_is_admitted(target_to_frame: AffineTransform) -> bool { + let [[a, c, _], [b, d, _]] = 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].into_iter().all(f32::is_finite) + } + fn filter_crosses_clip_path(target: HtmlElement<'_>) -> Result { let mut current = Some(target); while let Some(element) = current { @@ -5760,6 +5840,53 @@ mod filter_resource { }, ) } + "feTurbulence" => { + patrol_color_style(element)?; + let (base_frequency_x, base_frequency_y) = turbulence_base_frequency(element); + let primitive = match turbulence_num_octaves(element) { + Some(num_octaves) => FilterPrimitive::Turbulence { + kind: turbulence_kind(element), + base_frequency_x, + base_frequency_y, + num_octaves, + seed: resolved_svg_number(element, "seed", 0.0), + stitch_tiles: turbulence_stitches(element), + }, + // Blink's negative octave count contributes no sample: + // one bounded transparent source is the complete + // resolved image fact. Zero remains in the formula; + // fractal noise produces its neutral half sample. + None => FilterPrimitive::SolidColor { + color: CGColor32F::TRANSPARENT, + }, + }; + (Vec::new(), primitive) + } + "feDisplacementMap" => { + patrol_color_style(element)?; + let input = resolve_input(element, "in", previous, &names); + let input2 = resolve_input(element, "in2", previous, &names); + let mut scale = resolved_svg_number(element, "scale", 0.0); + if primitive_units == Units::ObjectBoundingBox { + // Blink's native filter exposes one scalar and applies + // the horizontal object-box scale to both channels. + scale *= target_box.width; + } + if !scale.is_finite() { + return Err(CompileError::UnsupportedFilter( + "feDisplacementMap scale resolves outside the finite filter range" + .to_string(), + )); + } + ( + vec![input, input2], + FilterPrimitive::DisplacementMap { + scale, + x_channel: displacement_channel(element, "xChannelSelector"), + y_channel: displacement_channel(element, "yChannelSelector"), + }, + ) + } "feMerge" => { patrol_color_style(element)?; let mut inputs = Vec::new(); @@ -5898,6 +6025,8 @@ mod filter_resource { let mut has_source_dependent_multi_input = false; let mut has_blend = false; let mut has_morphology = false; + let mut has_turbulence = false; + let mut has_displacement_map = false; let mut has_source_dependent_morphology = false; let mut has_source_dependent_active_morphology = false; for node in &nodes { @@ -5930,6 +6059,9 @@ mod filter_resource { has_source_dependent_active_morphology |= source_dependent && (radius_x > 0.0 || radius_y > 0.0); } + has_turbulence |= matches!(node.primitive(), FilterPrimitive::Turbulence { .. }); + has_displacement_map |= + matches!(node.primitive(), FilterPrimitive::DisplacementMap { .. }); source_dependencies.push(source_dependent); } if has_blend { @@ -5952,6 +6084,24 @@ mod filter_resource { .to_string(), )); } + if has_turbulence && !turbulence_displacement_mapping_is_admitted(target_to_frame) { + return Err(CompileError::UnsupportedFilter( + "feTurbulence's target mapping crosses the pinned-backend procedural-filter transform precision boundary" + .to_string(), + )); + } + if has_displacement_map && !turbulence_displacement_mapping_is_admitted(target_to_frame) { + return Err(CompileError::UnsupportedFilter( + "feDisplacementMap's target mapping crosses the pinned-backend displacement-filter transform precision boundary" + .to_string(), + )); + } + if has_displacement_map && filter_crosses_clip_path(target)? { + return Err(CompileError::UnsupportedFilter( + "feDisplacementMap crosses the pinned-backend filtered clip-path precision boundary" + .to_string(), + )); + } if has_source_dependent_morphology && filter_source_has_paint_server(target)? { return Err(CompileError::UnsupportedFilter( "feMorphology's source image crosses the pinned-backend morphology paint-server precision boundary" @@ -6109,10 +6259,13 @@ mod filter_resource { "filter resolution needs the target's complete fill-geometry box".to_string(), ) })?; - if target_box.width <= 0.0 || target_box.height <= 0.0 { + let units = filter_units(element); + let primitive_units = primitive_units(element); + if (target_box.width <= 0.0 || target_box.height <= 0.0) + && units == Units::ObjectBoundingBox + { return Ok(Resolution::Hide); } - let units = filter_units(element); let region = filter_region(element, units, target_box, bases)?; if region.width <= 0.0 || region.height <= 0.0 { return Ok(Resolution::Hide); @@ -6120,7 +6273,7 @@ mod filter_resource { let Some(program) = compile_graph( element, target, - primitive_units(element), + primitive_units, target_box, bases, region, diff --git a/crates/websem/tests/filter_contract.rs b/crates/websem/tests/filter_contract.rs index ae2dedec..5e6fa3a5 100644 --- a/crates/websem/tests/filter_contract.rs +++ b/crates/websem/tests/filter_contract.rs @@ -11,8 +11,8 @@ mod support; use math2::Rectangle; use math2::transform::AffineTransform; use rframe::{ - Filter, FilterBlend, FilterColorSpace, FilterComposite, FilterInput, FilterMorphology, - FilterPrimitive, Frame, FrameItem, ScopeEffect, + Filter, FilterBlend, FilterColorSpace, FilterComposite, FilterDisplacementChannel, FilterInput, + FilterMorphology, FilterPrimitive, FilterTurbulenceKind, Frame, FrameItem, ScopeEffect, }; use support::render_through_n0; use websem::{DegradationAction, InitialViewport, SvgFrameSource}; @@ -543,6 +543,262 @@ fn morphology_precision_boundaries_refuse_before_a_wrong_pixel() { ); } +#[test] +fn turbulence_resolves_the_measured_grammar_before_crossing_the_frame_seam() { + let source = |primitive: &str| { + document(&format!( + r##" + {primitive} + + "## + )) + }; + let primitive = |authored: &str| { + let frame = admit_both(&source(authored)); + let filter = resolved_filter(&frame); + assert_eq!(filter.program().iter().count(), 1); + let node = filter.program().iter().next().expect("one turbulence node"); + assert!(node.inputs().is_empty(), "turbulence is a generated source"); + node.primitive() + }; + + assert_eq!( + primitive( + r##""## + ), + FilterPrimitive::Turbulence { + kind: FilterTurbulenceKind::FractalNoise, + base_frequency_x: 0.125, + base_frequency_y: 0.25, + num_octaves: 9, + seed: -3.5, + stitch_tiles: true, + } + ); + assert_eq!( + primitive(r##""##), + FilterPrimitive::Turbulence { + kind: FilterTurbulenceKind::Turbulence, + base_frequency_x: 0.125, + base_frequency_y: 0.125, + num_octaves: 2, + seed: 0.0, + stitch_tiles: false, + }, + "one frequency duplicates across both axes" + ); + + for authored in [ + r##""##, + r##""##, + r##""##, + r##""##, + r##""##, + ] { + let FilterPrimitive::Turbulence { + base_frequency_x, + base_frequency_y, + .. + } = primitive(authored) + else { + panic!("invalid frequency retains the initial turbulence member") + }; + assert_eq!( + (base_frequency_x, base_frequency_y), + (0.0, 0.0), + "{authored}" + ); + } + + for authored in [ + r##""##, + r##""##, + ] { + assert_eq!( + primitive(authored), + FilterPrimitive::Turbulence { + kind: FilterTurbulenceKind::Turbulence, + base_frequency_x: 0.0, + base_frequency_y: 0.0, + num_octaves: 1, + seed: 0.0, + stitch_tiles: false, + }, + "invalid enumeration and scalar spellings retain each initial: {authored}" + ); + } + + assert_eq!( + primitive(r##""##), + FilterPrimitive::Turbulence { + kind: FilterTurbulenceKind::FractalNoise, + base_frequency_x: 0.0, + base_frequency_y: 0.0, + num_octaves: 0, + seed: 0.0, + stitch_tiles: false, + }, + "zero reaches the fractal formula" + ); + assert!( + matches!( + primitive(r##""##), + FilterPrimitive::SolidColor { color } if color.a() == 0.0 + ), + "a negative octave count resolves to a bounded transparent image" + ); +} + +#[test] +fn displacement_map_resolves_ordered_inputs_channels_and_horizontal_object_box_scale() { + let source = |primitive: &str, primitive_units: &str| { + document(&format!( + r##" + + + {primitive} + + "## + )) + }; + let resolved = |primitive: &str, primitive_units: &str| { + let frame = admit_both(&source(primitive, primitive_units)); + let filter = resolved_filter(&frame); + let node = filter.program().iter().last().expect("displacement output"); + assert_eq!(node.inputs(), [FilterInput::Node(0), FilterInput::Node(1)]); + node.primitive() + }; + + assert_eq!( + resolved( + r##""##, + "objectBoundingBox", + ), + FilterPrimitive::DisplacementMap { + scale: 5.0, + x_channel: FilterDisplacementChannel::Red, + y_channel: FilterDisplacementChannel::Green, + }, + "Blink's one native scalar uses target width for both displacement axes" + ); + + let channels = [ + ("R", FilterDisplacementChannel::Red), + ("G", FilterDisplacementChannel::Green), + ("B", FilterDisplacementChannel::Blue), + ("A", FilterDisplacementChannel::Alpha), + ]; + for (x_name, x_channel) in channels { + for (y_name, y_channel) in channels { + assert_eq!( + resolved( + &format!( + r##""## + ), + "userSpaceOnUse", + ), + FilterPrimitive::DisplacementMap { + scale: -12.5, + x_channel, + y_channel, + } + ); + } + } + + for authored in [ + r##""##, + r##""##, + r##""##, + ] { + assert_eq!( + resolved(authored, "userSpaceOnUse"), + FilterPrimitive::DisplacementMap { + scale: 0.0, + x_channel: FilterDisplacementChannel::Alpha, + y_channel: FilterDisplacementChannel::Alpha, + }, + "invalid spellings retain each initial: {authored}" + ); + } +} + +#[test] +fn generated_turbulence_can_paint_a_declared_empty_user_space_source() { + let source = |primitive_units: &str| { + document(&format!( + r##" + + + + "## + )) + }; + let user = admit_both(&source("userSpaceOnUse")); + let object = admit_both(&source("objectBoundingBox")); + for frame in [&user, &object] { + let filter = resolved_filter(frame); + assert!(filter.source_is_transparent()); + let pixels = render_through_n0(frame, 64, 64); + assert_ne!(at(&pixels, 24, 24), [255, 255, 255, 255]); + assert_eq!(at(&pixels, 4, 4), [255, 255, 255, 255]); + } + assert_eq!( + render_through_n0(&user, 64, 64), + render_through_n0(&object, 64, 64), + "base frequency itself is not primitiveUnits-scaled" + ); +} + +#[test] +fn turbulence_and_displacement_precision_boundaries_refuse_before_paint() { + let turbulence = r##" + + "##; + let displacement = r##" + + + "##; + for (filter, target, reason) in [ + ( + turbulence, + r##""##, + "procedural-filter transform precision boundary", + ), + ( + displacement, + r##""##, + "displacement-filter transform precision boundary", + ), + ( + displacement, + r##""##, + "filtered clip-path precision boundary", + ), + ] { + assert_target_skip( + &document(&format!( + r##" + + {filter} + {target}"## + )), + reason, + ); + } + + for transform in ["rotate(90 32 32)", "translate(64 0) scale(-1 1)"] { + admit_both(&document(&format!( + r##" {turbulence} + "## + ))); + } +} + #[test] fn blend_modes_lower_to_one_checked_two_input_operation() { let mode = |attribute: Option<&str>| { diff --git a/crates/websem/tests/unsupported_corpus.rs b/crates/websem/tests/unsupported_corpus.rs index 3460f296..f961fe98 100644 --- a/crates/websem/tests/unsupported_corpus.rs +++ b/crates/websem/tests/unsupported_corpus.rs @@ -175,6 +175,16 @@ const CORPUS: &[(&str, Departure, &str)] = &[ DeclaredByBestEffort, "native-shadow transform precision boundary", ), + ( + "svg-filter-displacement-clip-precision", + DeclaredByBestEffort, + "filtered clip-path precision boundary", + ), + ( + "svg-filter-displacement-transform-precision", + DeclaredByBestEffort, + "displacement-filter transform precision boundary", + ), ( "svg-filter-effect-stack-precision", DeclaredByBestEffort, @@ -280,6 +290,11 @@ const CORPUS: &[(&str, Departure, &str)] = &[ DeclaredByBestEffort, "translucent-source composition precision boundary", ), + ( + "svg-filter-turbulence-transform-precision", + DeclaredByBestEffort, + "procedural-filter transform precision boundary", + ), ( "svg-foreign-object", DeclaredByBestEffort, diff --git a/docs/wg/consolidation/svg-engine-of-record.md b/docs/wg/consolidation/svg-engine-of-record.md index a93182e8..7b4dd136 100644 --- a/docs/wg/consolidation/svg-engine-of-record.md +++ b/docs/wg/consolidation/svg-engine-of-record.md @@ -67,7 +67,8 @@ from the dated addenda below: modes, ordered `feMerge`/`feMergeNode`, native one-input `feDropShadow`, one-input `feColorMatrix`, one-input `feComponentTransfer` with its direct `feFuncR`/`feFuncG`/`feFuncB`/`feFuncA` children, and one-input - `feMorphology`; graph inputs resolve from + `feMorphology`, plus zero-input `feTurbulence` and two-input + `feDisplacementMap`; graph inputs resolve from `SourceGraphic`/`SourceAlpha`/prior and named results, with both filter coordinate systems and color spaces, hard regions, nesting, admitted transforms, ``, and the established effect order; @@ -75,14 +76,14 @@ from the dated addenda below: 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 609 Chromium-baked primitive cells plus 10 sampled frames. +- **The corpus** is 700 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 140 rows. + register has 143 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 @@ -3350,3 +3351,195 @@ Three focused rows join the refusal register, moving it from 137 to 140. The primitive corpus moves from 572 to 609 cells; the ten exact-time sampled frames are unchanged. Exactly one checklist row ticks: ``. This records no conformance score and takes no FLIP action. + +## Rung: `feTurbulence` + `feDisplacementMap` (2026-08-26) + +The verdict is CLOSE/SPLIT. `` and `` close +for their complete static Chromium behavior. Their seven element-specific +attributes close with them: `baseFrequency`, `numOctaves`, `seed`, +`stitchTiles`, `scale`, `xChannelSelector`, and `yChannelSelector`. The shared +`type`, `in`, `in2`, `result`, primitive-region, +`color-interpolation-filters`, filter-resource, and dynamics rows remain open +for their wider applicability. CSS filter functions, animation, and external +resources are separate surfaces. No CSS property row closes. + +Chromium 149.0.7827.55 establishes the turbulence grammar. `type` is the +case-sensitive `turbulence | fractalNoise` enumeration with initial +`turbulence`; missing, empty, invalid, wrong-case, whitespace-padded, and +CSS-wide spellings select that initial. `baseFrequency` is one or two SVG +numbers with initial zero. One member supplies both axes. Leading plus, +exponent, comma-wsp, and one trailing comma are accepted. Missing, empty, +malformed, unit-bearing, percentage, CSS-function, comment-bearing, +extra-member, and overflowing spellings select the initial pair. If either +parsed member is negative, Chromium resets both axes to that pair rather than +clamping one independently. + +`numOctaves` is an integer with initial one. Leading plus and surrounding SVG +whitespace are accepted; decimal, exponent, trailing-comma, and integer- +overflow spellings select one. Positive values cap at nine in the rendered +formula. Zero remains a real formula input: zero-octave turbulence is +transparent, while zero-octave fractal noise is the neutral-half field. A +negative integer produces a transparent result. `seed` is a signed SVG number +with initial zero. Fractional values are accepted and the procedural formula +truncates them toward zero; negative seeds remain active. `stitchTiles` is the +case-sensitive `stitch | noStitch` enumeration with initial `noStitch` and the +same exact-text fallback discipline as `type`. + +The resolved procedural source carries only its formula, two finite +non-negative frequencies, capped octave count, finite seed, stitch decision, +hard region, and operating color space. It has no image input. The +`baseFrequency` pair is not scaled by `primitiveUnits`; object-box units still +govern authored primitive regions. For a stitched source, Chromium truncates +the finite primitive-region width and height to integer tile lengths before +constructing the repeating field. Fractional stitched-region controls and the +corresponding non-stitched field differ and are both exact. + +Chromium establishes a distinct two-input displacement operation. `scale` is +a signed SVG number with initial zero and the same one-member grammar as +`seed`. Each selector is the case-sensitive `R | G | B | A` enumeration with +initial `A`; padded, wrong-case, invalid, and CSS-wide spellings select alpha. +The operation reads non-premultiplied channels from its second image and moves +the first image by `(channel - 0.5) × scale` on each axis. A transparent map +therefore selects the negative half-scale vector rather than zero movement; +the half-alpha control lies near the zero vector at the byte-normalized alpha. +Both source images enter the primitive's declared filter color space before +sampling. This is observable even with alpha selectors because the color input +conversion also participates in the result. + +Object-box displacement follows Blink's one-scalar native route: the authored +scale is multiplied by the target width for both x and y displacement, not by +independent axis bases. A non-square target discriminates that rule from both +the height basis and separate-axis scaling. Hard filter and primitive regions, +percentage regions, bounded displacement maps, SourceGraphic, SourceAlpha, +source-as-map, generated color/map inputs, named/previous input fallback, +procedural maps, and result reuse all have exact evidence. + +The source audit exposed a filter-chassis error rather than a procedural-noise +error. A user-space filter can have a positive region even when the target +contributes no source pixels; a generated primitive must still paint that +region. The prior route discarded every zero-area target before filter-unit +resolution, and the resolved item stream rejected the resulting empty scope. +The corrected contract distinguishes a fully transparent source invocation +from an absent filter: source references become an explicit bounded +transparent image, and a source-generating graph makes the otherwise empty +filter scope meaningful. The same empty target under object-box filter units +has no positive filter region and correctly paints nothing. User-space filter +units with object-box primitive units still paint the unscaled turbulence +field. Empty-source turbulence, turbulence blended with SourceGraphic, and a +generated displacement graph are committed exact controls. That generated-only +scope also owns the transformed filter-region coverage used for frame damage; +changing its seed damages that region even though no source draw contributed a +box. + +The color audit found a second narrow chassis boundary. Missing +`color-interpolation-filters` equals explicit linearRGB; explicit sRGB changes +both noise formulas and a procedural displacement map. Procedural pixels stay +floating through blend arithmetic and color conversion; treating them as an +ordinary generated byte image changes 349 to 490 pixels at maximum deltas 1 +to 6 in three measured blend cases. One narrower distinction remains inside +that statement. A direct sRGB procedural input reaches the pinned backend's +byte-domain product rounding for `difference` and `exclusion`, while a linear +input or an intervening component transfer promotes the blend to floating +arithmetic. An sRGB blend computes in its selected domain and then materializes +before a later blend; linear blend output remains floating, and a later transfer +promotes either route again. Four sixteen-mode atlases covering both color +spaces and direct or transferred alpha discriminate the first split. A +thirty-four-source chain matrix covers offset, blur, matrix, transfer, +morphology, prior blend, displacement, merge, and composite placement around +the affected modes: thirty admitted sources are exact and four reach the +existing source-derived multi-input patrol (measured, not celled). Eight +committed controls retain the two affected modes and the two-blend transition. +Active sRGB morphology directly over a procedural image +establishes a byte-domain boundary, while blend followed by morphology retains +the stronger procedural-composition provenance. The opposite operation order +is an exact control. The final procedural layer quantizes only the composed +result with explicit half-up byte rounding before the N32 boundary; sRGB +displacement uses the established exact byte restore. Generated solids and +earlier byte-domain filter outputs retain their existing policies. Both +turbulence formulas in both color spaces, both operation orders, the full +blend-mode atlases, and the noise-to-displacement chain are exact on the current +ARM host without a tolerance. + +Three remaining silent classes are now stable refusals. Axis maps, fractional +translation and scale, reflection, and exact quarter turns are exact. A +sampled 17-degree turbulence mapping differs by 3,173 pixels at maximum +channel delta 7 and a shear by 3,110 at delta 6. The corresponding displacement +controls differ by 280 pixels at delta 13 and 360 at delta 18. Separate mapping +patrols therefore guard each operation before paint. A geometric clip around +displacement differs by 35 pixels at maximum delta 2; the opacity control +without that clip is exact. One displacement-specific clip patrol quarantines +that class. Existing small-blur and translucent-source multi-input patrols +continue to own their independent graph boundaries (measured, not celled). + +The numeric crux did not reproduce the earlier stroke/geometry provenance +alias in a visible procedural field. Seed values bracketing sampled binary32 +midpoints and adjacent tiny base frequencies produced deterministic equal +pixels at 64×64, so no second numeric refusal was invented from an +undiscriminating raster. The admitted route nevertheless uses the shared +ordered SVG-number evaluator rather than Rust's raw lexical `f32` parser. This +is a negative probe verdict, not a claim that every possible numeric alias is +impossible (measured, not celled). + +Two broad matrices contain 164 twice-deterministic Chromium sources, each also +rendered through both actual command admissions. All 156 admitted sources are +pixel-exact and strict and best-effort agree; eight reach one of the three new +stable names or an older operation-specific patrol. A review-triggered matrix +adds eighteen procedural-operation sources across blend, morphology, matrix, +transfer, displacement, and both color-space directions. Fifteen are admitted +and exact after repair; three reach older shadow or composition patrols. The +former restore policy changes five measured cases: the three blend cases above, +sRGB-to-linear morphology by 2,365 pixels at maximum delta 1, and blend followed +by morphology by 393 pixels at delta 1. The four blend atlases add sixty-four +exact procedural mode/space/alpha combinations, and the operation-chain matrix +adds thirty exact admitted sources (measured, not celled). Ninety-one committed +Chromium cells carry the admitted slice without a new +tolerance. They cover +the complete parameter grammars, both formulas, all selectors, color spaces, +regions and units, source and map profiles, empty targets, graph routing, +safe mappings, source shapes, opacity, ``, `viewBox`, and the combined +procedural-warp graph. + +Gate sensitivity was proved by temporarily mapping turbulence to fractal noise +and the red displacement selector to alpha. `just gate` rejected fifty-five +new cells: turbulence controls changed up to all 4,096 pixels and displacement +controls reached maximum channel delta 202. Restoring both mappings returned +the gate to green. Independently restoring the former procedural-image policy +makes the five dedicated operation-order cells fail, up to 2,365 pixels and +maximum delta 6. Restoring the measured provenance rule returns the complete +700-cell gate to green. Replacing composed-result half-up quantization with +floor made forty-one cells fail, up to 3,648 pixels, all at delta 1; replacing +procedural multiply with normal made five cells fail, up to 1,362 pixels at +delta 116. Finally, forcing the promoted floating route on direct sRGB +`difference`/`exclusion` makes the two dedicated cells fail by 3,497 and 3,532 +pixels, while forcing byte-domain products on the promoted routes makes four +cells fail by 3,287–3,697 pixels. Carrying a floating result across the measured +sRGB blend-output boundary makes the two chain controls fail by 3,468 and 3,700 +pixels. Every restoration returns the complete gate to green. + +The first hosted-x86 workspace run rejected twenty-two rung cells: eighteen +sRGB displacement cases and four procedural outputs, totalling 294 pixels, all +at delta 1. Scoped exact displacement restore cleared all eighteen displacement +failures on the second run. That run left four procedural cells and 729 pixels, +all at delta 1: one pixel each in the default-color, linear-color, and stitched +controls, plus 726 pixels in the procedural blend. Explicit floating mode +arithmetic, the direct-sRGB product exception above, and composed-result-only +half-up quantization cleared the blend control on the third hosted run. Three +singleton delta-1 pixels remained: default-color and linear-color produced +`[203, 190, 203, 255]` where Chromium produced `[203, 189, 203, 255]`, and the +stitched control produced `[173, 159, 171, 255]` where Chromium produced +`[174, 159, 171, 255]`. + +Pinned Skia source located that last direct-noise split in process startup. +The painter had never initialized Skia's runtime-selected raster pipeline, so +hosted x86 retained the baseline non-fused Perlin path while ARM used its fused +NEON path. `skia_safe::graphics::init()` now runs before any drawlist replay; +on hosted x86 it selects the AVX2 pipeline whose fused multiply-add ordering +matches the admitted procedural values. The fourth hosted-x86 gate and the ARM +gate are byte-exact across all 700 cells. No tolerance or pixel exception was +introduced. + +Three focused rows join the refusal register, moving it from 140 to 143. The +primitive corpus moves from 609 to 700 cells; the ten exact-time sampled frames +are unchanged. Exactly nine checklist rows tick: the two elements and seven +element-specific 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 fa16e7b4..221e72de 100644 --- a/docs/wg/consolidation/web-checklist.md +++ b/docs/wg/consolidation/web-checklist.md @@ -1451,7 +1451,7 @@ for attributes the platform ships ahead of the SVG 2 indexes. - [x] `` - [ ] `` - [ ] `` -- [ ] `` +- [x] `` - [ ] `` - [x] `` - [x] `` @@ -1506,7 +1506,50 @@ for attributes the platform ships ahead of the SVG 2 indexes. - [ ] `` - [ ] `` - [ ] `` -- [ ] `` +- [x] `` + +> **2026-08-26 close:** `feTurbulence` carries both static procedural-noise +> formulas, the complete five-parameter attribute vocabulary, generated-source +> regions, both filter color spaces, graph reuse, safe mappings, ``, and +> `viewBox`. `feDisplacementMap` carries two ordered images, signed scale, all +> four non-premultiplied channel selectors, hard regions and object-box units, +> color conversion, source/generated/procedural maps, source shapes, opacity, +> safe mappings, ``, and `viewBox`. A user-space procedural filter may +> paint even when its target contributes a fully transparent source; an +> object-box filter region on the same zero-area target paints nothing. +> Ninety-one exact Chromium cells move the complete gate to 700. General +> affine mappings for both primitives and geometric clipping for displacement +> refuse by three stable precision names before paint. The shared `type`, `in`, +> `in2`, `result`, region, interpolation, filter-resource, and dynamics rows +> remain open for their wider applicability. Swapping the two noise formulas +> and red/alpha displacement selection made fifty-five new cells fail, up to +> all 4,096 pixels and maximum channel delta 202; restoration returned the +> complete gate to green. A review-triggered color-space/operation matrix found +> that procedural images must remain floating through blend arithmetic, while +> active sRGB morphology materializes a direct procedural image. Restoring the +> old policy makes five dedicated cells fail at up to 2,365 pixels and maximum +> channel delta 6. The first hosted-x86 run found 294 one-code-value pixels +> across twenty-two displacement/procedural cells. Its first repair cleared all +> eighteen displacement failures; the second hosted run left 729 delta-1 +> pixels across four procedural cells, including 726 in the blend control. +> Pinned Skia source and a four-atlas, sixty-four-mode Chromium replay then +> separated direct sRGB byte-domain blend products from promoted floating +> products and made every atlas exact. Six committed difference/exclusion +> controls guard both routes: forcing floating arithmetic breaks the two direct +> controls by 3,497 and 3,532 pixels, while forcing byte arithmetic breaks the +> four promoted controls by 3,287–3,697 pixels. A further thirty-four-source +> operation-chain matrix found that an sRGB blend result materializes before a +> later blend; carrying the earlier floating state silently changed 3,468 and +> 3,700 pixels. Two committed chain controls now guard that transition. +> Explicit procedural mode arithmetic and composed-result half-up quantization +> kept all 700 ARM cells exact. The third hosted-x86 run cleared the 726-pixel +> blend control and left three singleton delta-1 pixels: one each in the +> default-color, linear-color, and stitched procedural controls. Pinned Skia +> source located that final split in process startup: no caller initialized the +> runtime-selected raster pipeline, so hosted x86 retained baseline non-fused +> Perlin arithmetic while ARM used its fused NEON path. Initializing Skia before +> drawlist replay selects x86 AVX2's fused path as well. The fourth hosted-x86 +> gate and the ARM gate are byte-exact across all 700 cells, with no tolerance. - [ ] `` > **2026-08-25 split:** the static filter graph carries safe-kernel @@ -1865,7 +1908,13 @@ for attributes the platform ships ahead of the SVG 2 indexes. - [ ] `aria-valuetext` - [ ] `autofocus` - [ ] `azimuth` -- [ ] `baseFrequency` +- [x] `baseFrequency` + +> **2026-08-26 close:** `baseFrequency` applies only to `feTurbulence` and +> carries the complete SVG one-or-two-number grammar. One value supplies both +> axes; comma-wsp, leading plus, exponent, and the measured lone trailing comma +> are carried. Missing, malformed, unit-bearing, and overlong lists select the +> initial zero pair; either negative member makes the whole pair initial. - [ ] `bias` - [ ] `class` - [x] `clipPathUnits` @@ -1945,7 +1994,12 @@ for attributes the platform ships ahead of the SVG 2 indexes. > invalid, wrong-case, whitespace-padded, legacy camelCase, CSS-wide, and > draft-only spellings select the initial `normal`; Chromium ignores the > sampled `no-composite` spelling without changing a valid mode. -- [ ] `numOctaves` +- [x] `numOctaves` + +> **2026-08-26 close:** `numOctaves` applies only to `feTurbulence`. Missing or +> invalid integer text selects one, leading plus and surrounding SVG whitespace +> are carried, positive values cap at nine, zero reaches the selected formula, +> and a negative integer produces the measured transparent result. - [ ] `offset` - [ ] `onabort` - [ ] `onafterprint` @@ -2054,8 +2108,15 @@ for attributes the platform ships ahead of the SVG 2 indexes. - [ ] `result` - [ ] `role` - [ ] `rotate` -- [ ] `scale` -- [ ] `seed` +- [x] `scale` +- [x] `seed` + +> **2026-08-26 close:** displacement `scale` and turbulence `seed` each carry +> the complete signed SVG-number grammar with initial zero, including leading +> plus, exponent, and the measured lone trailing comma. Invalid, unit-bearing, +> percentage, CSS-function, and multi-member text selects the initial. Scale +> keeps its sign and fractions; the noise formula truncates fractional seed +> values toward zero as Chromium does. - [ ] `side` - [x] `slope` - [ ] `spacing` @@ -2064,7 +2125,12 @@ for attributes the platform ships ahead of the SVG 2 indexes. - [x] `spreadMethod` - [ ] `startOffset` - [ ] `stdDeviation` -- [ ] `stitchTiles` +- [x] `stitchTiles` + +> **2026-08-26 close:** `stitchTiles` applies only to `feTurbulence` and carries +> the complete case-sensitive `stitch | noStitch` grammar. Missing, empty, +> invalid, wrong-case, whitespace-padded, and CSS-wide spellings select the +> initial `noStitch`; stitched fractional primitive regions are Chromium-baked. - [ ] `style` - [ ] `surfaceScale` - [ ] `systemLanguage` @@ -2091,13 +2157,19 @@ for attributes the platform ships ahead of the SVG 2 indexes. - [ ] `viewBox` - [ ] `x1` - [ ] `x2` -- [ ] `xChannelSelector` +- [x] `xChannelSelector` - [ ] `xlink:href` - [ ] `xlink:title` - [ ] `xml:space` - [ ] `y1` - [ ] `y2` -- [ ] `yChannelSelector` +- [x] `yChannelSelector` + +> **2026-08-26 close:** both displacement selectors carry the complete +> case-sensitive `R | G | B | A` grammar independently. Missing, empty, +> invalid, wrong-case, whitespace-padded, and CSS-wide spellings select the +> initial `A`; committed pairs prove that selection occurs on the +> non-premultiplied displacement image. - [ ] `z` diff --git a/fixtures/web-first/README.md b/fixtures/web-first/README.md index 9382cecc..160ccdfc 100644 --- a/fixtures/web-first/README.md +++ b/fixtures/web-first/README.md @@ -212,6 +212,14 @@ is exactly what the engine renders pixel-for-pixel. | `svg-filter-morphology-{source-number-alias,radius-large-finite,viewbox-half-step,viewbox-nonuniform,primitive-units,source-alpha,input-previous,result-shadow,region-crop,region-zero-crop,channel-srgb,channel-linear,channel-alpha}.svg` | Numeric, graph, region, and color evidence: the amplified Blink source-number neighbor, a finite huge active radius, post-map half-step rounding, non-uniform and object-box axes, SourceAlpha and prior inputs, last-result shadowing, active and zero-radius hard crops, and distinct premultiplied extrema in sRGB, linearRGB, and alpha-only images. All thirteen are byte-exact. | | `svg-filter-morphology-{axis-fractional,quarter-turn,path,stroke,rounded-rect,source-use,target-opacity,target-clip,target-mask,blur-before,blur-after,transfer-before,transfer-after,blend-before,blend-after,shadow-before,shadow-after,matrix-before,matrix-after,chain-two}.svg` | Source and composition evidence: safe fractional axis mapping and exact quarter turn; path, stroke, rounded-rect, and `` sources; target opacity/clip/mask; two morphology nodes; and ordering around blur, component transfer, blend, native shadow, and generated merge/matrix graphs. All twenty are byte-exact. | | *(measured, not celled — morphology split)* | Four broad scratch matrices contain 187 twice-deterministic Chromium 149 sources, each rendered through both actual CLI admissions. After patrol, all 162 admitted sources are exact and admission-identical; the other 25 reach one of three new stable morphology names or an older operation-specific patrol. Axis maps and quarter turns are exact, while a 17-degree source map differs by 171px/Δ12 and a generated map by 142px/Δ9. Linear-gradient sources differ by 125px/Δ1 even at zero radius and 191px/Δ1 when active; radial and gradient-stroke controls reproduce the class. Active filled circles differ by 3px/Δ9 and a fractional ellipse by 8px/Δ6, while rounded rectangles, curved paths, circle strokes, and round path strokes are exact; this keeps gridaco/nothing#88 separate. A 600×32 bank pins half-up rounding and the 256-device-pixel radius cap. The region audit also found a premature source clip: correcting output-region placement removes 16px/Δ136 and preserves the older filter corpus. Swapping erosion and dilation made thirty-five of the thirty-seven new cells fail, up to 1,560 pixels and Δ255; restoration returned all 609 cells green. The first full-workspace hosted-x86 run then found nine active sRGB morphology cells with 1,633 pixels total at Δ1, isolating the final low-precision filter-layer restore. Scoping the established exact byte-domain restore to active sRGB morphology clears the same hosted workspace test and keeps all 609 ARM cells exact; zero radius and later color-space conversion retain their former paths, and no tolerance was added. The corpus is now 609 cells plus 10 sampled frames, and the named register has 140 rows. | +| `svg-filter-turbulence-{default,type-turbulence,type-fractal,type-grammar,base-one,base-two,base-comma,base-trailing-comma,base-invalid,base-negative-axis,base-plus-exponent,octave-one,octave-two,octave-zero,fractal-octave-zero,octave-negative,octave-nine,octave-cap,octave-grammar,seed-zero,seed-one,seed-fraction,seed-negative,seed-grammar,stitch-none,stitch,stitch-grammar}.svg` | The complete static `feTurbulence` parameter grammar in twenty-seven cells: both formulas and case-sensitive enum fallback; one/two-member SVG number lists, comma-wsp, a lone trailing comma, leading plus and exponent; invalid and negative-frequency fallback; integer octave parsing, zero/negative behavior, and the cap at nine; signed/truncated seeds; and both stitch modes with exact-enum fallback. All twenty-seven are byte-exact. | +| `svg-filter-turbulence-{color-default,color-srgb,color-linear,region-stitch-fraction,region-percent,fractional-translate,quarter-turn,use,viewbox,graph-blend,graph-result-reuse,empty-source}.svg` | Procedural-source operation evidence: default linearRGB and explicit color spaces, fractional stitched and percentage hard regions, safe mappings, non-uniform `viewBox`, ``, named graph reuse, and generated output over a fully transparent target source. All twelve are byte-exact. | +| `svg-filter-turbulence-{linear-blend,linear-srgb-blend,srgb-linear-blend,linear-srgb-morphology,srgb-linear-morphology,blend-morphology-restore,morphology-blend-restore}.svg` | Procedural-image provenance across the admitted blend and morphology family: all-linear and both color-space transition directions, plus both operation orders. A procedural image stays floating through blend arithmetic; active sRGB morphology materializes a direct procedural image, while an earlier blend keeps its stronger provenance. All seven are byte-exact. | +| `svg-filter-turbulence-{difference-linear,difference-srgb-direct,difference-srgb-transfer,exclusion-linear,exclusion-srgb-direct,exclusion-srgb-transfer}.svg` | The pinned blend-domain split for procedural `difference` and `exclusion`: direct sRGB procedural products take exact byte-domain rounding, while linear and component-transferred procedural inputs take floating products. The six full-canvas controls avoid unrelated edge antialiasing and are byte-exact. | +| `svg-filter-turbulence-blend-chain-{difference,exclusion}.svg` | A procedural sRGB blend computes with the selected arithmetic and then materializes before a later blend. These two full-canvas controls guard that second-blend byte-product transition; both are byte-exact. | +| `svg-filter-displacement-{default,scale-zero,scale-positive,scale-negative,scale-fraction,scale-trailing-comma,scale-invalid,selector-default,selector-alpha,selector-red,selector-green,selector-blue,selector-axes,selector-grammar}.svg` | The complete static `feDisplacementMap` parameter grammar in fourteen cells: absent and explicit-zero scale, signed/fractional SVG numbers, the accepted trailing comma and invalid fallback, all four independently selected channels, and case-sensitive selector fallback to alpha. All fourteen are byte-exact. | +| `svg-filter-displacement-{color-srgb,color-linear,primitive-units,transparent-map,half-alpha-map,map-region,source-alpha-map,source-rgb-map,generated-color,turbulence-map,turbulence-map-linear,region-percent,scale-transform,quarter-turn,use,viewbox,stroke,path,opacity,empty-generated,input-default-in2,input-unknown-in}.svg` · `svg-filter-turbulence-displacement-map.svg` | Two-input sampling and composition evidence: non-premultiplied map channels in both filter color spaces; Blink's horizontal object-box scale for both displacement axes; transparent, half-alpha, bounded, source, and generated maps; hard regions, safe mappings, ``, `viewBox`, stroke/path sources and opacity; default/unknown graph inputs; generated output over an empty target; and the procedural-noise-to-warp chain. All twenty-three are byte-exact. | +| *(measured, not celled — turbulence/displacement split)* | Two twice-deterministic Chromium 149 matrices contain 164 sources, each rendered through both actual CLI admissions. All 156 admitted sources are exact and admission-identical; eight reach one of the three new stable names or an older blur/composition patrol. General rotation and shear are the new mapping boundary: turbulence differs by 3,173px/Δ7 and 3,110px/Δ6, while displacement differs by 280px/Δ13 and 360px/Δ18. A clipped displacement differs by 35px/Δ2. Axis maps, fractional translation/scale, reflection, and exact quarter turns are exact. Missing filter interpolation is linearRGB; `sRGB` changes both the procedural field and displacement sampling. Zero fractal octaves produce the neutral-half field while a negative count is transparent. Empty user-space targets still receive generated output, including with object-box primitive units; object-box filter units on the same empty target paint nothing. Seed-midpoint and tiny adjacent-frequency probes found no second source-number divergence class at this raster size. A review-triggered eighteen-source matrix then isolated procedural-image arithmetic across blend, morphology, matrix, transfer, displacement, and both color-space directions: fifteen sources are admitted and exact after repair, while three reach older shadow/composition patrols. The old byte-image policy changed 349px/Δ1 for linear→sRGB blend, 490px/Δ6 for all-linear blend, 450px/Δ6 for sRGB→linear blend, 2,365px/Δ1 for sRGB→linear morphology, and 393px/Δ1 for blend→morphology. Four further sixteen-mode atlases prove the direct-sRGB byte-product versus promoted-floating split for procedural `difference` and `exclusion`; all sixty-four mode/space/alpha combinations are exact in both admissions. A thirty-four-source chain matrix around offset, blur, matrix, transfer, morphology, prior blend, displacement, merge, and composite admits thirty exact sources and routes four through the existing source-derived multi-input patrol. It also proves that an sRGB blend output materializes before a later blend. Eight hard-edged cells guard the affected product and chain branches. Swapping turbulence with fractal noise and red displacement with alpha made fifty-five original rung cells fail, up to all 4,096 pixels and maximum channel delta 202. Restoring the pre-review procedural policy makes five operation-order cells fail. Replacing final half-up quantization with floor makes forty-one cells fail, up to 3,648px/Δ1, and replacing procedural multiply with normal makes five fail, up to 1,362px/Δ116. Forcing floating products breaks the two direct-sRGB route cells by 3,497 and 3,532 pixels; forcing byte products breaks the four promoted-route cells by 3,287–3,697 pixels. Carrying floating state across the sRGB blend-output boundary breaks the two chain cells by 3,468 and 3,700 pixels. Every restoration returns the complete 700-cell gate to green. The first hosted-x86 run found twenty-two rung cells and 294 pixels at Δ1: eighteen sRGB displacement cells plus four procedural outputs. Scoped exact displacement restore cleared the eighteen displacement failures on the second run, which left four procedural cells and 729 pixels at Δ1—726 in the blend control and one each in three direct procedural controls. Explicit procedural mode arithmetic and composed-result-only half-up quantization cleared the 726-pixel blend control on the third run, leaving three singleton direct-noise pixels. Pinned Skia source then located the final split in uninitialized runtime raster-pipeline dispatch: x86 retained baseline non-fused Perlin arithmetic while ARM used its fused NEON path. Initializing Skia before drawlist replay selects fused AVX2 arithmetic on hosted x86. The complete 700-cell gate is byte-exact on ARM and hosted x86 with no tolerance. The corpus is now 700 cells plus 10 sampled frames, and the named register has 143 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 88d3040e..ab653c48 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 (609) +## Chromium-baked cells (700) Each renders byte-exact against its committed Chromium oracle (seven curved cells and four gradient ramps carry a declared, bounded @@ -217,6 +217,42 @@ to its fixture source. No new image is committed for this view. svg-filter-composite-region-default svg-filter-composite-region-union svg-filter-composite-xor +svg-filter-displacement-color-linear +svg-filter-displacement-color-srgb +svg-filter-displacement-default +svg-filter-displacement-empty-generated +svg-filter-displacement-generated-color +svg-filter-displacement-half-alpha-map +svg-filter-displacement-input-default-in2 +svg-filter-displacement-input-unknown-in +svg-filter-displacement-map-region +svg-filter-displacement-opacity +svg-filter-displacement-path +svg-filter-displacement-primitive-units +svg-filter-displacement-quarter-turn +svg-filter-displacement-region-percent +svg-filter-displacement-scale-fraction +svg-filter-displacement-scale-invalid +svg-filter-displacement-scale-negative +svg-filter-displacement-scale-positive +svg-filter-displacement-scale-trailing-comma +svg-filter-displacement-scale-transform +svg-filter-displacement-scale-zero +svg-filter-displacement-selector-alpha +svg-filter-displacement-selector-axes +svg-filter-displacement-selector-blue +svg-filter-displacement-selector-default +svg-filter-displacement-selector-grammar +svg-filter-displacement-selector-green +svg-filter-displacement-selector-red +svg-filter-displacement-source-alpha-map +svg-filter-displacement-source-rgb-map +svg-filter-displacement-stroke +svg-filter-displacement-transparent-map +svg-filter-displacement-turbulence-map +svg-filter-displacement-turbulence-map-linear +svg-filter-displacement-use +svg-filter-displacement-viewbox svg-filter-drop-shadow-anisotropic svg-filter-drop-shadow-blur-input svg-filter-drop-shadow-chain @@ -338,6 +374,61 @@ to its fixture source. No new image is committed for this view. svg-filter-source-alpha svg-filter-stroke svg-filter-target-rotate +svg-filter-turbulence-base-comma +svg-filter-turbulence-base-invalid +svg-filter-turbulence-base-negative-axis +svg-filter-turbulence-base-one +svg-filter-turbulence-base-plus-exponent +svg-filter-turbulence-base-trailing-comma +svg-filter-turbulence-base-two +svg-filter-turbulence-blend-chain-difference +svg-filter-turbulence-blend-chain-exclusion +svg-filter-turbulence-blend-morphology-restore +svg-filter-turbulence-color-default +svg-filter-turbulence-color-linear +svg-filter-turbulence-color-srgb +svg-filter-turbulence-default +svg-filter-turbulence-difference-linear +svg-filter-turbulence-difference-srgb-direct +svg-filter-turbulence-difference-srgb-transfer +svg-filter-turbulence-displacement-map +svg-filter-turbulence-empty-source +svg-filter-turbulence-exclusion-linear +svg-filter-turbulence-exclusion-srgb-direct +svg-filter-turbulence-exclusion-srgb-transfer +svg-filter-turbulence-fractal-octave-zero +svg-filter-turbulence-fractional-translate +svg-filter-turbulence-graph-blend +svg-filter-turbulence-graph-result-reuse +svg-filter-turbulence-linear-blend +svg-filter-turbulence-linear-srgb-blend +svg-filter-turbulence-linear-srgb-morphology +svg-filter-turbulence-morphology-blend-restore +svg-filter-turbulence-octave-cap +svg-filter-turbulence-octave-grammar +svg-filter-turbulence-octave-negative +svg-filter-turbulence-octave-nine +svg-filter-turbulence-octave-one +svg-filter-turbulence-octave-two +svg-filter-turbulence-octave-zero +svg-filter-turbulence-quarter-turn +svg-filter-turbulence-region-percent +svg-filter-turbulence-region-stitch-fraction +svg-filter-turbulence-seed-fraction +svg-filter-turbulence-seed-grammar +svg-filter-turbulence-seed-negative +svg-filter-turbulence-seed-one +svg-filter-turbulence-seed-zero +svg-filter-turbulence-srgb-linear-blend +svg-filter-turbulence-srgb-linear-morphology +svg-filter-turbulence-stitch +svg-filter-turbulence-stitch-grammar +svg-filter-turbulence-stitch-none +svg-filter-turbulence-type-fractal +svg-filter-turbulence-type-grammar +svg-filter-turbulence-type-turbulence +svg-filter-turbulence-use +svg-filter-turbulence-viewbox svg-filter-units-grammar svg-filter-url-comments svg-filter-url-quoted @@ -638,7 +729,7 @@ to its fixture source. No new image is committed for this view. svg-visibility-rule-beats-attribute svg-visibility-unhide -## The refusal register (140) +## The refusal register (143) What the slice refuses, by name, in the compiler's own words — **both refuse** is a document-level contract; **declared** renders @@ -674,6 +765,8 @@ its row into the cells above. | `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-displacement-clip-precision` | declared | skipped svg/path[1]: unsupported SVG filter: feDisplacementMap crosses the pinned-backend filtered clip-path precision boundary | +| `svg-filter-displacement-transform-precision` | declared | skipped svg/rect[2]: unsupported SVG filter: feDisplacementMap's target mapping crosses the pinned-backend displacement-filter transform precision boundary | | `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 | @@ -694,7 +787,7 @@ its row into the cells above. | `svg-filter-offset-blur-precision` | declared | skipped svg/rect[2]: unsupported SVG filter: a filter graph combines feOffset with Gaussian blur, which crosses the pinned-backend composed-operation precision boundary | | `svg-filter-offset-fractional-precision` | declared | skipped svg/rect[2]: unsupported SVG filter: feOffset uses a fractional displacement, which crosses the pinned-backend rasterization boundary | | `svg-filter-offset-transform-precision` | declared | skipped svg/g[1]/rect[1]: unsupported SVG filter: feOffset's target mapping produces a fractional device-space displacement at the pinned-backend rasterization boundary | -| `svg-filter-primitive` | declared | skipped svg/rect[2]: unsupported SVG filter: filter graph contains unsupported primitive | +| `svg-filter-primitive` | declared | skipped svg/rect[2]: unsupported SVG filter: filter graph contains unsupported primitive | | `svg-filter-primitive-empty-region` | declared | skipped svg/rect[2]: unsupported SVG filter: a non-positive filter primitive region is not yet represented as a transparent graph result | | `svg-filter-region-calc` | declared | skipped svg/rect[2]: unsupported SVG filter: filter region x uses calc(), whose computed length is not represented at this Stylo pin | | `svg-filter-region-range` | declared | skipped svg/rect[2]: unsupported SVG filter: filter region width crosses the unimplemented Web used-length range | @@ -702,6 +795,7 @@ its row into the cells above. | `svg-filter-region-var` | declared | skipped svg/rect[2]: unsupported SVG filter: filter region x uses var(), whose computed length is not represented at this Stylo pin | | `svg-filter-root` | **both refuse** | unsupported SVG filter: filter on the root uses the host CSS-layer coordinate route | | `svg-filter-translucent-source-composition-precision` | declared | skipped svg/rect[2]: unsupported SVG filter: a source-derived multi-input filter graph crosses the pinned-backend translucent-source composition precision boundary | +| `svg-filter-turbulence-transform-precision` | declared | skipped svg/rect[2]: unsupported SVG filter: feTurbulence's target mapping crosses the pinned-backend procedural-filter transform precision boundary | | `svg-foreign-object` | declared | skipped svg/foreignObject[1]: unsupported element | | `svg-geometry-calc-values` | declared | skipped svg/circle[1]: attribute cx="calc(8px + 8px)" is not a number; skipped svg/circle[2]: attribute cy="calc(8px \* 4)" is not a number; skipped svg/circle[3]: attribute r="calc(4px + 4px)" is not a number | | `svg-geometry-css-comments` | declared | skipped svg/circle[1]: attribute cx="/\*\*/16" is not a number; skipped svg/circle[2]: attribute cy="/\*\*/32/\*\*/" is not a number; skipped svg/circle[3]: attribute r="/\*\*/8/\*\*/" is not a number | diff --git a/fixtures/web-first/chromium/svg-filter-displacement-color-linear.png b/fixtures/web-first/chromium/svg-filter-displacement-color-linear.png new file mode 100644 index 00000000..d60c279f Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-color-linear.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-color-srgb.png b/fixtures/web-first/chromium/svg-filter-displacement-color-srgb.png new file mode 100644 index 00000000..606cf13b Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-color-srgb.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-default.png b/fixtures/web-first/chromium/svg-filter-displacement-default.png new file mode 100644 index 00000000..606cf13b Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-default.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-empty-generated.png b/fixtures/web-first/chromium/svg-filter-displacement-empty-generated.png new file mode 100644 index 00000000..b74e4fbc Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-empty-generated.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-generated-color.png b/fixtures/web-first/chromium/svg-filter-displacement-generated-color.png new file mode 100644 index 00000000..d6cf8e21 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-generated-color.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-half-alpha-map.png b/fixtures/web-first/chromium/svg-filter-displacement-half-alpha-map.png new file mode 100644 index 00000000..0d80077f Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-half-alpha-map.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-input-default-in2.png b/fixtures/web-first/chromium/svg-filter-displacement-input-default-in2.png new file mode 100644 index 00000000..5d866afd Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-input-default-in2.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-input-unknown-in.png b/fixtures/web-first/chromium/svg-filter-displacement-input-unknown-in.png new file mode 100644 index 00000000..1e128f83 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-input-unknown-in.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-map-region.png b/fixtures/web-first/chromium/svg-filter-displacement-map-region.png new file mode 100644 index 00000000..92266b21 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-map-region.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-opacity.png b/fixtures/web-first/chromium/svg-filter-displacement-opacity.png new file mode 100644 index 00000000..bf41fb54 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-opacity.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-path.png b/fixtures/web-first/chromium/svg-filter-displacement-path.png new file mode 100644 index 00000000..16d1c987 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-path.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-primitive-units.png b/fixtures/web-first/chromium/svg-filter-displacement-primitive-units.png new file mode 100644 index 00000000..f3683cb5 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-primitive-units.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-quarter-turn.png b/fixtures/web-first/chromium/svg-filter-displacement-quarter-turn.png new file mode 100644 index 00000000..e04a9555 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-quarter-turn.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-region-percent.png b/fixtures/web-first/chromium/svg-filter-displacement-region-percent.png new file mode 100644 index 00000000..c4853fca Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-region-percent.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-scale-fraction.png b/fixtures/web-first/chromium/svg-filter-displacement-scale-fraction.png new file mode 100644 index 00000000..694499fc Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-scale-fraction.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-scale-invalid.png b/fixtures/web-first/chromium/svg-filter-displacement-scale-invalid.png new file mode 100644 index 00000000..606cf13b Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-scale-invalid.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-scale-negative.png b/fixtures/web-first/chromium/svg-filter-displacement-scale-negative.png new file mode 100644 index 00000000..00c63c8c Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-scale-negative.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-scale-positive.png b/fixtures/web-first/chromium/svg-filter-displacement-scale-positive.png new file mode 100644 index 00000000..5d866afd Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-scale-positive.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-scale-trailing-comma.png b/fixtures/web-first/chromium/svg-filter-displacement-scale-trailing-comma.png new file mode 100644 index 00000000..5d866afd Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-scale-trailing-comma.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-scale-transform.png b/fixtures/web-first/chromium/svg-filter-displacement-scale-transform.png new file mode 100644 index 00000000..9ad64bf8 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-scale-transform.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-scale-zero.png b/fixtures/web-first/chromium/svg-filter-displacement-scale-zero.png new file mode 100644 index 00000000..606cf13b Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-scale-zero.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-selector-alpha.png b/fixtures/web-first/chromium/svg-filter-displacement-selector-alpha.png new file mode 100644 index 00000000..5d866afd Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-selector-alpha.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-selector-axes.png b/fixtures/web-first/chromium/svg-filter-displacement-selector-axes.png new file mode 100644 index 00000000..014f55f0 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-selector-axes.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-selector-blue.png b/fixtures/web-first/chromium/svg-filter-displacement-selector-blue.png new file mode 100644 index 00000000..606cf13b Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-selector-blue.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-selector-default.png b/fixtures/web-first/chromium/svg-filter-displacement-selector-default.png new file mode 100644 index 00000000..5d866afd Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-selector-default.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-selector-grammar.png b/fixtures/web-first/chromium/svg-filter-displacement-selector-grammar.png new file mode 100644 index 00000000..5d866afd Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-selector-grammar.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-selector-green.png b/fixtures/web-first/chromium/svg-filter-displacement-selector-green.png new file mode 100644 index 00000000..2ccb2154 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-selector-green.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-selector-red.png b/fixtures/web-first/chromium/svg-filter-displacement-selector-red.png new file mode 100644 index 00000000..06645fe6 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-selector-red.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-source-alpha-map.png b/fixtures/web-first/chromium/svg-filter-displacement-source-alpha-map.png new file mode 100644 index 00000000..9a272770 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-source-alpha-map.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-source-rgb-map.png b/fixtures/web-first/chromium/svg-filter-displacement-source-rgb-map.png new file mode 100644 index 00000000..acda18b5 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-source-rgb-map.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-stroke.png b/fixtures/web-first/chromium/svg-filter-displacement-stroke.png new file mode 100644 index 00000000..3b4ffcf4 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-stroke.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-transparent-map.png b/fixtures/web-first/chromium/svg-filter-displacement-transparent-map.png new file mode 100644 index 00000000..bec2d756 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-transparent-map.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-turbulence-map-linear.png b/fixtures/web-first/chromium/svg-filter-displacement-turbulence-map-linear.png new file mode 100644 index 00000000..72faf556 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-turbulence-map-linear.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-turbulence-map.png b/fixtures/web-first/chromium/svg-filter-displacement-turbulence-map.png new file mode 100644 index 00000000..1c5053a5 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-turbulence-map.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-use.png b/fixtures/web-first/chromium/svg-filter-displacement-use.png new file mode 100644 index 00000000..68129f75 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-use.png differ diff --git a/fixtures/web-first/chromium/svg-filter-displacement-viewbox.png b/fixtures/web-first/chromium/svg-filter-displacement-viewbox.png new file mode 100644 index 00000000..cfc422c1 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-displacement-viewbox.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-base-comma.png b/fixtures/web-first/chromium/svg-filter-turbulence-base-comma.png new file mode 100644 index 00000000..ba8de8de Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-base-comma.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-base-invalid.png b/fixtures/web-first/chromium/svg-filter-turbulence-base-invalid.png new file mode 100644 index 00000000..8bb654de Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-base-invalid.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-base-negative-axis.png b/fixtures/web-first/chromium/svg-filter-turbulence-base-negative-axis.png new file mode 100644 index 00000000..8bb654de Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-base-negative-axis.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-base-one.png b/fixtures/web-first/chromium/svg-filter-turbulence-base-one.png new file mode 100644 index 00000000..a6a5f0ec Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-base-one.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-base-plus-exponent.png b/fixtures/web-first/chromium/svg-filter-turbulence-base-plus-exponent.png new file mode 100644 index 00000000..a6a5f0ec Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-base-plus-exponent.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-base-trailing-comma.png b/fixtures/web-first/chromium/svg-filter-turbulence-base-trailing-comma.png new file mode 100644 index 00000000..a6a5f0ec Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-base-trailing-comma.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-base-two.png b/fixtures/web-first/chromium/svg-filter-turbulence-base-two.png new file mode 100644 index 00000000..ba8de8de Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-base-two.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-blend-chain-difference.png b/fixtures/web-first/chromium/svg-filter-turbulence-blend-chain-difference.png new file mode 100644 index 00000000..ca91ffa6 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-blend-chain-difference.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-blend-chain-exclusion.png b/fixtures/web-first/chromium/svg-filter-turbulence-blend-chain-exclusion.png new file mode 100644 index 00000000..9e1e8ce6 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-blend-chain-exclusion.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-blend-morphology-restore.png b/fixtures/web-first/chromium/svg-filter-turbulence-blend-morphology-restore.png new file mode 100644 index 00000000..e4f0dbab Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-blend-morphology-restore.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-color-default.png b/fixtures/web-first/chromium/svg-filter-turbulence-color-default.png new file mode 100644 index 00000000..007cf2b9 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-color-default.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-color-linear.png b/fixtures/web-first/chromium/svg-filter-turbulence-color-linear.png new file mode 100644 index 00000000..007cf2b9 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-color-linear.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-color-srgb.png b/fixtures/web-first/chromium/svg-filter-turbulence-color-srgb.png new file mode 100644 index 00000000..4578984b Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-color-srgb.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-default.png b/fixtures/web-first/chromium/svg-filter-turbulence-default.png new file mode 100644 index 00000000..8bb654de Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-default.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-difference-linear.png b/fixtures/web-first/chromium/svg-filter-turbulence-difference-linear.png new file mode 100644 index 00000000..fef08030 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-difference-linear.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-difference-srgb-direct.png b/fixtures/web-first/chromium/svg-filter-turbulence-difference-srgb-direct.png new file mode 100644 index 00000000..b124e317 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-difference-srgb-direct.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-difference-srgb-transfer.png b/fixtures/web-first/chromium/svg-filter-turbulence-difference-srgb-transfer.png new file mode 100644 index 00000000..f5634c19 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-difference-srgb-transfer.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-displacement-map.png b/fixtures/web-first/chromium/svg-filter-turbulence-displacement-map.png new file mode 100644 index 00000000..26402e8f Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-displacement-map.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-empty-source.png b/fixtures/web-first/chromium/svg-filter-turbulence-empty-source.png new file mode 100644 index 00000000..d20b29f9 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-empty-source.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-exclusion-linear.png b/fixtures/web-first/chromium/svg-filter-turbulence-exclusion-linear.png new file mode 100644 index 00000000..cb884413 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-exclusion-linear.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-exclusion-srgb-direct.png b/fixtures/web-first/chromium/svg-filter-turbulence-exclusion-srgb-direct.png new file mode 100644 index 00000000..5629dc2a Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-exclusion-srgb-direct.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-exclusion-srgb-transfer.png b/fixtures/web-first/chromium/svg-filter-turbulence-exclusion-srgb-transfer.png new file mode 100644 index 00000000..2da662bd Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-exclusion-srgb-transfer.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-fractal-octave-zero.png b/fixtures/web-first/chromium/svg-filter-turbulence-fractal-octave-zero.png new file mode 100644 index 00000000..aeeacaef Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-fractal-octave-zero.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-fractional-translate.png b/fixtures/web-first/chromium/svg-filter-turbulence-fractional-translate.png new file mode 100644 index 00000000..849c2dd1 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-fractional-translate.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-graph-blend.png b/fixtures/web-first/chromium/svg-filter-turbulence-graph-blend.png new file mode 100644 index 00000000..36868122 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-graph-blend.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-graph-result-reuse.png b/fixtures/web-first/chromium/svg-filter-turbulence-graph-result-reuse.png new file mode 100644 index 00000000..d464f3a7 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-graph-result-reuse.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-linear-blend.png b/fixtures/web-first/chromium/svg-filter-turbulence-linear-blend.png new file mode 100644 index 00000000..136c47ec Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-linear-blend.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-linear-srgb-blend.png b/fixtures/web-first/chromium/svg-filter-turbulence-linear-srgb-blend.png new file mode 100644 index 00000000..0a7488d7 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-linear-srgb-blend.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-linear-srgb-morphology.png b/fixtures/web-first/chromium/svg-filter-turbulence-linear-srgb-morphology.png new file mode 100644 index 00000000..2af27179 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-linear-srgb-morphology.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-morphology-blend-restore.png b/fixtures/web-first/chromium/svg-filter-turbulence-morphology-blend-restore.png new file mode 100644 index 00000000..ae28df73 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-morphology-blend-restore.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-octave-cap.png b/fixtures/web-first/chromium/svg-filter-turbulence-octave-cap.png new file mode 100644 index 00000000..c2f31ad3 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-octave-cap.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-octave-grammar.png b/fixtures/web-first/chromium/svg-filter-turbulence-octave-grammar.png new file mode 100644 index 00000000..a6a5f0ec Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-octave-grammar.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-octave-negative.png b/fixtures/web-first/chromium/svg-filter-turbulence-octave-negative.png new file mode 100644 index 00000000..8bb654de Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-octave-negative.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-octave-nine.png b/fixtures/web-first/chromium/svg-filter-turbulence-octave-nine.png new file mode 100644 index 00000000..c2f31ad3 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-octave-nine.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-octave-one.png b/fixtures/web-first/chromium/svg-filter-turbulence-octave-one.png new file mode 100644 index 00000000..a6a5f0ec Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-octave-one.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-octave-two.png b/fixtures/web-first/chromium/svg-filter-turbulence-octave-two.png new file mode 100644 index 00000000..dd8278ea Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-octave-two.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-octave-zero.png b/fixtures/web-first/chromium/svg-filter-turbulence-octave-zero.png new file mode 100644 index 00000000..8bb654de Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-octave-zero.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-quarter-turn.png b/fixtures/web-first/chromium/svg-filter-turbulence-quarter-turn.png new file mode 100644 index 00000000..b24a97d3 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-quarter-turn.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-region-percent.png b/fixtures/web-first/chromium/svg-filter-turbulence-region-percent.png new file mode 100644 index 00000000..5f24986a Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-region-percent.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-region-stitch-fraction.png b/fixtures/web-first/chromium/svg-filter-turbulence-region-stitch-fraction.png new file mode 100644 index 00000000..aa58f6d8 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-region-stitch-fraction.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-seed-fraction.png b/fixtures/web-first/chromium/svg-filter-turbulence-seed-fraction.png new file mode 100644 index 00000000..a6a5f0ec Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-seed-fraction.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-seed-grammar.png b/fixtures/web-first/chromium/svg-filter-turbulence-seed-grammar.png new file mode 100644 index 00000000..a6a5f0ec Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-seed-grammar.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-seed-negative.png b/fixtures/web-first/chromium/svg-filter-turbulence-seed-negative.png new file mode 100644 index 00000000..9a6ff17d Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-seed-negative.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-seed-one.png b/fixtures/web-first/chromium/svg-filter-turbulence-seed-one.png new file mode 100644 index 00000000..a6a5f0ec Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-seed-one.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-seed-zero.png b/fixtures/web-first/chromium/svg-filter-turbulence-seed-zero.png new file mode 100644 index 00000000..a6a5f0ec Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-seed-zero.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-srgb-linear-blend.png b/fixtures/web-first/chromium/svg-filter-turbulence-srgb-linear-blend.png new file mode 100644 index 00000000..a24182a3 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-srgb-linear-blend.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-srgb-linear-morphology.png b/fixtures/web-first/chromium/svg-filter-turbulence-srgb-linear-morphology.png new file mode 100644 index 00000000..ab723c82 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-srgb-linear-morphology.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-stitch-grammar.png b/fixtures/web-first/chromium/svg-filter-turbulence-stitch-grammar.png new file mode 100644 index 00000000..1007cb65 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-stitch-grammar.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-stitch-none.png b/fixtures/web-first/chromium/svg-filter-turbulence-stitch-none.png new file mode 100644 index 00000000..1007cb65 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-stitch-none.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-stitch.png b/fixtures/web-first/chromium/svg-filter-turbulence-stitch.png new file mode 100644 index 00000000..26944f31 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-stitch.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-type-fractal.png b/fixtures/web-first/chromium/svg-filter-turbulence-type-fractal.png new file mode 100644 index 00000000..f07fa3e5 Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-type-fractal.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-type-grammar.png b/fixtures/web-first/chromium/svg-filter-turbulence-type-grammar.png new file mode 100644 index 00000000..a6a5f0ec Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-type-grammar.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-type-turbulence.png b/fixtures/web-first/chromium/svg-filter-turbulence-type-turbulence.png new file mode 100644 index 00000000..a6a5f0ec Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-type-turbulence.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-use.png b/fixtures/web-first/chromium/svg-filter-turbulence-use.png new file mode 100644 index 00000000..aeabf4bf Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-use.png differ diff --git a/fixtures/web-first/chromium/svg-filter-turbulence-viewbox.png b/fixtures/web-first/chromium/svg-filter-turbulence-viewbox.png new file mode 100644 index 00000000..01435f9c Binary files /dev/null and b/fixtures/web-first/chromium/svg-filter-turbulence-viewbox.png differ diff --git a/fixtures/web-first/oracle-bake.json b/fixtures/web-first/oracle-bake.json index b819bfa4..fe28ceaf 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": "65cee4b36d44ccb9c3dfda94b33af827e468849d203cf7801192f114187e3990", + "suite_sha256": "c52554b86bfd62e891dec375bb6bc22fe6f95ebca55294a490a5b184972701b3", "capture": { "device_scale_factor": 1, "omit_background": true, @@ -1720,6 +1720,330 @@ "width": 64, "height": 64 }, + { + "id": "svg-filter-displacement-color-linear", + "source": "svg-filter-displacement-color-linear.svg", + "source_sha256": "80c6a8fd20689e44d3c2970adc88de0a907cffa004f388b9e150264cb210f823", + "oracle": "chromium/svg-filter-displacement-color-linear.png", + "oracle_sha256": "e5333de361b6cac8727beed97f64c55175965cf1dd9fb38c8d00b5429554af64", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-color-srgb", + "source": "svg-filter-displacement-color-srgb.svg", + "source_sha256": "7059700c07b4fa063466bd240bea932aed39ec191c26f3749882dc45cd19e9ba", + "oracle": "chromium/svg-filter-displacement-color-srgb.png", + "oracle_sha256": "50c4f12fb22968ec2793c1807c2dfb6569b344542e3fd381eb3a055bb68267ab", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-default", + "source": "svg-filter-displacement-default.svg", + "source_sha256": "496b25d7b84b981914029d8fb48672b37ffd69e7ab0ba9fbce2e1c1e7ec47c6d", + "oracle": "chromium/svg-filter-displacement-default.png", + "oracle_sha256": "50c4f12fb22968ec2793c1807c2dfb6569b344542e3fd381eb3a055bb68267ab", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-empty-generated", + "source": "svg-filter-displacement-empty-generated.svg", + "source_sha256": "e92b060d143ec290d877d1c627fb4646bf0d4ba43d70641debe8a7e1b6f5df07", + "oracle": "chromium/svg-filter-displacement-empty-generated.png", + "oracle_sha256": "ac6d3d0bb8745741b0143a49d09f2673843d7de051f8491561ebea3c251f17d3", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-generated-color", + "source": "svg-filter-displacement-generated-color.svg", + "source_sha256": "e3325aa8826a2da743312901ba3fb20e6ba87d37d32b457631b5fa4ee5a9fdac", + "oracle": "chromium/svg-filter-displacement-generated-color.png", + "oracle_sha256": "871ea3c8b7db61c0d11ae8326aacef70e8c003b3f9bf3fa30b709b5c47d283ae", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-half-alpha-map", + "source": "svg-filter-displacement-half-alpha-map.svg", + "source_sha256": "077ef133c3b77383de9fc5e6c810507934ab3b6b43be3771226db9b9cb3f43a1", + "oracle": "chromium/svg-filter-displacement-half-alpha-map.png", + "oracle_sha256": "e308116a6b01f8882f33d85ade8800d54e65cabffcb6afc8bd6052f0a6c71a72", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-input-default-in2", + "source": "svg-filter-displacement-input-default-in2.svg", + "source_sha256": "c75175b0dda7f7677d05006672feea1a24ccd3644c7bb9e03769b02852d19ac7", + "oracle": "chromium/svg-filter-displacement-input-default-in2.png", + "oracle_sha256": "ac617ba298bb5ae51819dbac6c1038cd252a6eb74a493f67896eaaea48bb888f", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-input-unknown-in", + "source": "svg-filter-displacement-input-unknown-in.svg", + "source_sha256": "01e430d5ccb3c1d65e4f0491dbbfed0ab5958030814a3efd77ded88dbcc0f231", + "oracle": "chromium/svg-filter-displacement-input-unknown-in.png", + "oracle_sha256": "98a306634de2d8ded0f22a9bce9ae353e15f44ba3e9017317eac932661a26e85", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-map-region", + "source": "svg-filter-displacement-map-region.svg", + "source_sha256": "91da7f31bc6fd04a4a485862f01e420cdde46a9ff9033da9f1e733167b1cf81c", + "oracle": "chromium/svg-filter-displacement-map-region.png", + "oracle_sha256": "c51790800671204a0300ec71280fdceb539cc6caad9bacc5e8d99c03268c325f", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-opacity", + "source": "svg-filter-displacement-opacity.svg", + "source_sha256": "6bbcbc83a28f67f1a8c11dd8e3341ea8a282d08e9a950ae2ebf0b8c077a7e952", + "oracle": "chromium/svg-filter-displacement-opacity.png", + "oracle_sha256": "e4e0e3301c4c4d79f0ab24decad6439f86e02dbecf56ca53d66f7834c269d820", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-path", + "source": "svg-filter-displacement-path.svg", + "source_sha256": "6dbf3672179c2c634c2d53269095cd36d7e48675e5c3da568be6c83834a21190", + "oracle": "chromium/svg-filter-displacement-path.png", + "oracle_sha256": "83f05829e3cd4b6c22290d53c70e34921f5d1c6ac63c91586a18da74a9d514d4", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-primitive-units", + "source": "svg-filter-displacement-primitive-units.svg", + "source_sha256": "72d5ef98462e1a129ec25455eb89db2c4cb52e06aafcb28b2938abef17a1085d", + "oracle": "chromium/svg-filter-displacement-primitive-units.png", + "oracle_sha256": "85d8658ebe43a157546109bb953956f76b192bb1a07587102c8a59ae15890173", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-quarter-turn", + "source": "svg-filter-displacement-quarter-turn.svg", + "source_sha256": "7b1a53caf0d330913b9a2bb7d2355a5fb6095dc9d79c799f388430458526e5a3", + "oracle": "chromium/svg-filter-displacement-quarter-turn.png", + "oracle_sha256": "bb7d849dcbbf49a93439b285b7a3695f4adc4e29f38d43e2c7aec8d161ebd203", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-region-percent", + "source": "svg-filter-displacement-region-percent.svg", + "source_sha256": "36f4245b952df9e46b0ad63464f814ef143954033af95ee7b7011a07a6f1a268", + "oracle": "chromium/svg-filter-displacement-region-percent.png", + "oracle_sha256": "34dbc30d9e39e46a23dbeb2e198cd297b455edabd770622ad227cefd426c37d3", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-scale-fraction", + "source": "svg-filter-displacement-scale-fraction.svg", + "source_sha256": "aaeb87be895571f286cd4f48bc3e6b1dac579da8c97b4cc3f5c783eb81b3557d", + "oracle": "chromium/svg-filter-displacement-scale-fraction.png", + "oracle_sha256": "b31dae4b8c77c28045a3c29295fee29a2143b6595bdd6198c9363c878bdb3b65", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-scale-invalid", + "source": "svg-filter-displacement-scale-invalid.svg", + "source_sha256": "8fddbc8dae390d8f1f00e2d25e886a1edce7bb5f2aa8e5cc0150c85e72d83a2b", + "oracle": "chromium/svg-filter-displacement-scale-invalid.png", + "oracle_sha256": "50c4f12fb22968ec2793c1807c2dfb6569b344542e3fd381eb3a055bb68267ab", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-scale-negative", + "source": "svg-filter-displacement-scale-negative.svg", + "source_sha256": "f17ead830871326163fca43019b9beb9a30eda7aa85c6fe9a3ec9924ccd2dc50", + "oracle": "chromium/svg-filter-displacement-scale-negative.png", + "oracle_sha256": "58bc82de609c3ca90e14b49f23b8cd094d83116c4deb68f632856abd04ca89d6", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-scale-positive", + "source": "svg-filter-displacement-scale-positive.svg", + "source_sha256": "aa11235ad89321bc255e4665760492118cc6c477d3c686775da1ad1b6ed79696", + "oracle": "chromium/svg-filter-displacement-scale-positive.png", + "oracle_sha256": "ac617ba298bb5ae51819dbac6c1038cd252a6eb74a493f67896eaaea48bb888f", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-scale-trailing-comma", + "source": "svg-filter-displacement-scale-trailing-comma.svg", + "source_sha256": "57f05e4c8929ae7acc6398d4153cad6c2c6bb29163dc16ef3d0b1cbb66230590", + "oracle": "chromium/svg-filter-displacement-scale-trailing-comma.png", + "oracle_sha256": "ac617ba298bb5ae51819dbac6c1038cd252a6eb74a493f67896eaaea48bb888f", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-scale-transform", + "source": "svg-filter-displacement-scale-transform.svg", + "source_sha256": "65167a780841f824217e62e68dbe948e1461da5545cbdcb2ce740876b3786b59", + "oracle": "chromium/svg-filter-displacement-scale-transform.png", + "oracle_sha256": "25cd03d7857daaba281e89fbf2c71460dacd685a2f230a90e3402aaba8736f05", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-scale-zero", + "source": "svg-filter-displacement-scale-zero.svg", + "source_sha256": "e13cd07b41a0652095acd3f34544454bf23604eff8520e979b9558cebe0a83f6", + "oracle": "chromium/svg-filter-displacement-scale-zero.png", + "oracle_sha256": "50c4f12fb22968ec2793c1807c2dfb6569b344542e3fd381eb3a055bb68267ab", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-selector-alpha", + "source": "svg-filter-displacement-selector-alpha.svg", + "source_sha256": "b72536f8fe32d8bbcb5c533cb07ebe278f2ccd80b58152f8fa507aedd6b8f4c1", + "oracle": "chromium/svg-filter-displacement-selector-alpha.png", + "oracle_sha256": "ac617ba298bb5ae51819dbac6c1038cd252a6eb74a493f67896eaaea48bb888f", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-selector-axes", + "source": "svg-filter-displacement-selector-axes.svg", + "source_sha256": "381f86382a08598ffaaad8e37f0d9909868100941fcac46d4abb07e0ca7ae32c", + "oracle": "chromium/svg-filter-displacement-selector-axes.png", + "oracle_sha256": "6e802945d80b9ffbb84ea8546edee6410c94b587e0546da1ed549998c93b31cd", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-selector-blue", + "source": "svg-filter-displacement-selector-blue.svg", + "source_sha256": "171d9d5190e8dc908d368a24d0a51131f83c626a4dee596aeeb0f2741121fc54", + "oracle": "chromium/svg-filter-displacement-selector-blue.png", + "oracle_sha256": "50c4f12fb22968ec2793c1807c2dfb6569b344542e3fd381eb3a055bb68267ab", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-selector-default", + "source": "svg-filter-displacement-selector-default.svg", + "source_sha256": "aa11235ad89321bc255e4665760492118cc6c477d3c686775da1ad1b6ed79696", + "oracle": "chromium/svg-filter-displacement-selector-default.png", + "oracle_sha256": "ac617ba298bb5ae51819dbac6c1038cd252a6eb74a493f67896eaaea48bb888f", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-selector-grammar", + "source": "svg-filter-displacement-selector-grammar.svg", + "source_sha256": "566f2d6938a9b0a33842f33e7e3cbd73c54f5024230f4a3de8027122e34c87d2", + "oracle": "chromium/svg-filter-displacement-selector-grammar.png", + "oracle_sha256": "ac617ba298bb5ae51819dbac6c1038cd252a6eb74a493f67896eaaea48bb888f", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-selector-green", + "source": "svg-filter-displacement-selector-green.svg", + "source_sha256": "e4dabb1f7b0672ed112c236bcd25bd9c496b2ad56bfd9e3b17302ec70d1d5809", + "oracle": "chromium/svg-filter-displacement-selector-green.png", + "oracle_sha256": "e7b25a1259378fb92010d786fc1d99d42794477316b5c3ceec4499fd25e710fd", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-selector-red", + "source": "svg-filter-displacement-selector-red.svg", + "source_sha256": "793dc09980252f0fe6de80f8e8f080756a6ad810b86ff2617d9448309d8d4e6e", + "oracle": "chromium/svg-filter-displacement-selector-red.png", + "oracle_sha256": "4eb9f750928b55908b052519bbf671810f93e14a61ad85ccd3d1e69efcb3982b", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-source-alpha-map", + "source": "svg-filter-displacement-source-alpha-map.svg", + "source_sha256": "6d927e45198fb7ce0cf13180e23cf960c05c1f064ec7193c971f68411c532a55", + "oracle": "chromium/svg-filter-displacement-source-alpha-map.png", + "oracle_sha256": "37dfa510a9cc9debc01f4a935a08e74b2930caab0e4613a51312eecaef01e10e", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-source-rgb-map", + "source": "svg-filter-displacement-source-rgb-map.svg", + "source_sha256": "ab6d131acd954ae2c6b3a32f1d37bfbb47f7e3244794b3de6f7512f3b0c03ce4", + "oracle": "chromium/svg-filter-displacement-source-rgb-map.png", + "oracle_sha256": "79d9a21f26b76991c404b935bd0872b6f8678954d18996c1f48d2099606cd938", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-stroke", + "source": "svg-filter-displacement-stroke.svg", + "source_sha256": "1472adcd00def039ed8886f7390e563e34878fffe240b22ca997a81904a9b956", + "oracle": "chromium/svg-filter-displacement-stroke.png", + "oracle_sha256": "ec26f30327ab984d8ed0f979cc435fc620c5a6e3d0c20924b298e0b06f9e514a", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-transparent-map", + "source": "svg-filter-displacement-transparent-map.svg", + "source_sha256": "31927257d0d032f680ff9439bfce3fb23ffd481dfbc45198f9c08aa671fb61a9", + "oracle": "chromium/svg-filter-displacement-transparent-map.png", + "oracle_sha256": "701dd853a22ba4e7e72ea4e80ba46a13712208023c2bdacce041ee70ad007926", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-turbulence-map", + "source": "svg-filter-displacement-turbulence-map.svg", + "source_sha256": "de8ca15c750c9fa181b3e7830ce886421a1c0effc555a528171eed9c1ff11fdc", + "oracle": "chromium/svg-filter-displacement-turbulence-map.png", + "oracle_sha256": "e124e15561aa76af737bbde7ed7187ed87aa62f3d1d6dac549c586d391a45b7a", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-turbulence-map-linear", + "source": "svg-filter-displacement-turbulence-map-linear.svg", + "source_sha256": "f5bcf42876ca00160983f1678b490d29300f494597f5035822d16e4b4927b57c", + "oracle": "chromium/svg-filter-displacement-turbulence-map-linear.png", + "oracle_sha256": "408ae6589b1a2aa979aa05d783fa2274c1c0dac689c4037756da3d6c463d5fb0", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-use", + "source": "svg-filter-displacement-use.svg", + "source_sha256": "9ad7beb95a5b206fe8f977522769115ff7a8612ba210657262a9524bfaa1ba97", + "oracle": "chromium/svg-filter-displacement-use.png", + "oracle_sha256": "1e25439c6202ff993ed5c033190a7e7727fffe36215b8c1ef664cb8e63d23517", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-viewbox", + "source": "svg-filter-displacement-viewbox.svg", + "source_sha256": "24bb30349178d18aac1d17c55357fbd2f51f28ca180a19aea860caf8271849e9", + "oracle": "chromium/svg-filter-displacement-viewbox.png", + "oracle_sha256": "a5d932939e87a6eaa86690f051f24118ffbad8c253e765dd880ff6f29d2b6ebc", + "width": 64, + "height": 64 + }, { "id": "svg-filter-drop-shadow-anisotropic", "source": "svg-filter-drop-shadow-anisotropic.svg", @@ -2809,6 +3133,501 @@ "width": 64, "height": 64 }, + { + "id": "svg-filter-turbulence-base-comma", + "source": "svg-filter-turbulence-base-comma.svg", + "source_sha256": "4f4a5ce21e6726be4847bc234a89ea512dd9c63e3b3205a1df06d7d8100b2a28", + "oracle": "chromium/svg-filter-turbulence-base-comma.png", + "oracle_sha256": "535bfc3966bfa7d30fe8606b1d9df939ebb85b906c886494bcb3697a528cfaad", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-base-invalid", + "source": "svg-filter-turbulence-base-invalid.svg", + "source_sha256": "1b73b8135b917dfec3af727f24543cb10fabd0926efb3400026a64c06ed60625", + "oracle": "chromium/svg-filter-turbulence-base-invalid.png", + "oracle_sha256": "5892610a63d0176452c8b1f3ad1f284dab7ec872b7d1b351daf781799e71e9a3", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-base-negative-axis", + "source": "svg-filter-turbulence-base-negative-axis.svg", + "source_sha256": "09ef3ce4339f08d2b2608f4d76a0916a1c6b36a5925632f8273028fd5e85ebb1", + "oracle": "chromium/svg-filter-turbulence-base-negative-axis.png", + "oracle_sha256": "5892610a63d0176452c8b1f3ad1f284dab7ec872b7d1b351daf781799e71e9a3", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-base-one", + "source": "svg-filter-turbulence-base-one.svg", + "source_sha256": "980c5dbe83b365bfd7bdf0a7f0a1b323e9ad93f31302ba97a9e0d049de3a5146", + "oracle": "chromium/svg-filter-turbulence-base-one.png", + "oracle_sha256": "86c1ae45c0b3986a84c85f011f35679e3146502b8c697729e50583f05f16a844", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-base-plus-exponent", + "source": "svg-filter-turbulence-base-plus-exponent.svg", + "source_sha256": "2be01e260e1ddb8a3b7d6ffe702931a8faabd58a7abbda35d70dcb18fa4ae355", + "oracle": "chromium/svg-filter-turbulence-base-plus-exponent.png", + "oracle_sha256": "86c1ae45c0b3986a84c85f011f35679e3146502b8c697729e50583f05f16a844", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-base-trailing-comma", + "source": "svg-filter-turbulence-base-trailing-comma.svg", + "source_sha256": "016cd9f96309c5ee9b7f35a69b63439554ab565e71c352e7b4f686addf145944", + "oracle": "chromium/svg-filter-turbulence-base-trailing-comma.png", + "oracle_sha256": "86c1ae45c0b3986a84c85f011f35679e3146502b8c697729e50583f05f16a844", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-base-two", + "source": "svg-filter-turbulence-base-two.svg", + "source_sha256": "317cfe94347604d7a8de73b359c43d8b9892dd0054630deea484ff6406a9f91b", + "oracle": "chromium/svg-filter-turbulence-base-two.png", + "oracle_sha256": "535bfc3966bfa7d30fe8606b1d9df939ebb85b906c886494bcb3697a528cfaad", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-blend-chain-difference", + "source": "svg-filter-turbulence-blend-chain-difference.svg", + "source_sha256": "8f2620f8a2987155b84b2e05b2fb97dee47dd0454e2b9b375e9ecca1362ee82b", + "oracle": "chromium/svg-filter-turbulence-blend-chain-difference.png", + "oracle_sha256": "cf72543639dd9492bb8d845bd6ec0d1539f0e72e30007a832a23430bddae5e91", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-blend-chain-exclusion", + "source": "svg-filter-turbulence-blend-chain-exclusion.svg", + "source_sha256": "0be8971522469d8e9dca0b3410a449a8c3aff26ba84f042a5a9b6bbf972558c1", + "oracle": "chromium/svg-filter-turbulence-blend-chain-exclusion.png", + "oracle_sha256": "09640ee194b678c09cc2889ae85e8a3071f67a58ab7672c5d4d4e082cd839ef6", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-blend-morphology-restore", + "source": "svg-filter-turbulence-blend-morphology-restore.svg", + "source_sha256": "de0ddccf3c694015fae1497ece12e619147f715c258f3badf34ec4ec662edd2f", + "oracle": "chromium/svg-filter-turbulence-blend-morphology-restore.png", + "oracle_sha256": "ac1bba196fe00c0ce5705ee12939085f2c001d6fc62af2eb03b0c8c8a3e2b149", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-color-default", + "source": "svg-filter-turbulence-color-default.svg", + "source_sha256": "d197e14b8e70d14c14fec5fc99835614f7ec43bc8677e8e4fea07583ef29653a", + "oracle": "chromium/svg-filter-turbulence-color-default.png", + "oracle_sha256": "12dfb58e8418af5241ed649817f7e187c2a7ef3014facb36df1ffcf0ad34bf6e", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-color-linear", + "source": "svg-filter-turbulence-color-linear.svg", + "source_sha256": "10c9080643915f7ec8c0912b597be7c38fe099aa72ef4be0f3456ed1a90754c0", + "oracle": "chromium/svg-filter-turbulence-color-linear.png", + "oracle_sha256": "12dfb58e8418af5241ed649817f7e187c2a7ef3014facb36df1ffcf0ad34bf6e", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-color-srgb", + "source": "svg-filter-turbulence-color-srgb.svg", + "source_sha256": "80fb7530740d4c391adb9d900439f29d78fa9dc7206f6b5b5e49bd1ba2fbe1fa", + "oracle": "chromium/svg-filter-turbulence-color-srgb.png", + "oracle_sha256": "0dbc51e7d5227990936f59cf3bd6e9b831dd0cc3377ff7b6e88d1ef64849cc27", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-default", + "source": "svg-filter-turbulence-default.svg", + "source_sha256": "16494c9a7f34508b992a3da8a4bc1adec8a6d53683e6cccae92e0806b2003790", + "oracle": "chromium/svg-filter-turbulence-default.png", + "oracle_sha256": "5892610a63d0176452c8b1f3ad1f284dab7ec872b7d1b351daf781799e71e9a3", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-difference-linear", + "source": "svg-filter-turbulence-difference-linear.svg", + "source_sha256": "b18e33d808ea548d5be919a7c113ce540a4ed28b4eab08e2c44c7ec93180f0c8", + "oracle": "chromium/svg-filter-turbulence-difference-linear.png", + "oracle_sha256": "e6d003ca6b10fd129a1eee112c040336a3de71ef58611be143f40943ecc294c6", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-difference-srgb-direct", + "source": "svg-filter-turbulence-difference-srgb-direct.svg", + "source_sha256": "6ffc41c888340b3aed6ef08a6e0024538831523f21a6149e1e0b2236427636eb", + "oracle": "chromium/svg-filter-turbulence-difference-srgb-direct.png", + "oracle_sha256": "9d97fb06a65ac24252de1224eacf5fe9fa9b69011b8d3c99f6411ecec39cc64e", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-difference-srgb-transfer", + "source": "svg-filter-turbulence-difference-srgb-transfer.svg", + "source_sha256": "926a9f74174872f8d420d885abc7854146daa66bbdc9b4190d26a2d6faed9a01", + "oracle": "chromium/svg-filter-turbulence-difference-srgb-transfer.png", + "oracle_sha256": "1429426526b27a8adbc0d249a323dba21ff2b5642d0083d61f26eb191fd0ee15", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-displacement-map", + "source": "svg-filter-turbulence-displacement-map.svg", + "source_sha256": "1a2f3c37c17fd507589ba43035e11d2b20b7c501c8840cfbfb29eef94ef1bae0", + "oracle": "chromium/svg-filter-turbulence-displacement-map.png", + "oracle_sha256": "9969499ef3ab420200916b28bd4ecebd12b36ab2f05faddee117de26fc83fed5", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-empty-source", + "source": "svg-filter-turbulence-empty-source.svg", + "source_sha256": "d8d0bd5fd45e269a66d56b5892d56dfb7369601df8f241f5e06257a3c518fa91", + "oracle": "chromium/svg-filter-turbulence-empty-source.png", + "oracle_sha256": "8970105d789845abb1b37d062a2edd6539bb5e3f66257fc81531a1548d66b695", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-exclusion-linear", + "source": "svg-filter-turbulence-exclusion-linear.svg", + "source_sha256": "2dacfad0a6c1bcc046f77fa647460de327662edb5303cf9f5b758136ee0546f5", + "oracle": "chromium/svg-filter-turbulence-exclusion-linear.png", + "oracle_sha256": "fe26c88827bde44ed27f3b47315fd6aa81c8fdd77b985a8561c07c9eadfca840", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-exclusion-srgb-direct", + "source": "svg-filter-turbulence-exclusion-srgb-direct.svg", + "source_sha256": "0564bd1d3bc4fc6fdbaec933ea8562e0312d60035327d9f8f92a2011cd736285", + "oracle": "chromium/svg-filter-turbulence-exclusion-srgb-direct.png", + "oracle_sha256": "b30c0d315b48a062aa5a1b94efc8cf008fd42257a77cde5264447011a6c22d29", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-exclusion-srgb-transfer", + "source": "svg-filter-turbulence-exclusion-srgb-transfer.svg", + "source_sha256": "215b558d3a69f0cfbd7df479e2ee435aab6616e34940e31815fa4eee04abc514", + "oracle": "chromium/svg-filter-turbulence-exclusion-srgb-transfer.png", + "oracle_sha256": "8e6faf2875a38edd599cfdaf3dc392c078a4cd978bbfdc427583a8f87a504cc3", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-fractal-octave-zero", + "source": "svg-filter-turbulence-fractal-octave-zero.svg", + "source_sha256": "da083c4e54aad62969b1c3707cf418de458b068d8673f5351478b42d47002678", + "oracle": "chromium/svg-filter-turbulence-fractal-octave-zero.png", + "oracle_sha256": "3c47a6c02e9d5b130464e5e0630863c137c83169dfd6cf50fd7d295440f07c9e", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-fractional-translate", + "source": "svg-filter-turbulence-fractional-translate.svg", + "source_sha256": "53a2b993ac0de33a1f6d1806f29c91a06f47342c7eb5a871ccf1acb316df2091", + "oracle": "chromium/svg-filter-turbulence-fractional-translate.png", + "oracle_sha256": "1977b6a13a9da452eefc10dd49c7441deb6f110c3edd43022bff337b244a9e82", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-graph-blend", + "source": "svg-filter-turbulence-graph-blend.svg", + "source_sha256": "7d2b920b58b9c45225548372e2db74242de19fff353ed26e9eab903e42abb1d4", + "oracle": "chromium/svg-filter-turbulence-graph-blend.png", + "oracle_sha256": "82769795ffd6535f811d3f1b238636fe82c4cd679efd5a1c7b9e3524ca444528", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-graph-result-reuse", + "source": "svg-filter-turbulence-graph-result-reuse.svg", + "source_sha256": "5dbb54d317c4609446a0e199484fd00e9bc6bdb1790315995e9f8572eaf248d6", + "oracle": "chromium/svg-filter-turbulence-graph-result-reuse.png", + "oracle_sha256": "e1dd588428c315bf062b8f9381d3beda636cfcb6d5229123689d9b9a13baeffe", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-linear-blend", + "source": "svg-filter-turbulence-linear-blend.svg", + "source_sha256": "db5ad834b261838797f5f5ce86b5a69ec3797a62a3173afe0743b04b7fecf825", + "oracle": "chromium/svg-filter-turbulence-linear-blend.png", + "oracle_sha256": "43837d815e68e4d44993a262687178e24992f494bb7b1c9b946ad2507bba9e6e", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-linear-srgb-blend", + "source": "svg-filter-turbulence-linear-srgb-blend.svg", + "source_sha256": "8ef288ac3d48522f67ba181d54ae19b45bf2db3116e7b379ff04efdf51c638fa", + "oracle": "chromium/svg-filter-turbulence-linear-srgb-blend.png", + "oracle_sha256": "8aa8ad7ef637f73f979b1a58cff663e2ec0361b42c0c305e75ca8fb612f44844", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-linear-srgb-morphology", + "source": "svg-filter-turbulence-linear-srgb-morphology.svg", + "source_sha256": "2b215c27428298e4679714343239d7c6f21078ab18f00d803ea1fd8208282172", + "oracle": "chromium/svg-filter-turbulence-linear-srgb-morphology.png", + "oracle_sha256": "794476cb26e074f8eac472a9c84b39043ec9e654dd78f1ee6aec82a641488612", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-morphology-blend-restore", + "source": "svg-filter-turbulence-morphology-blend-restore.svg", + "source_sha256": "9b7efd5b846c9f46a2d75583684a50041064165222f6010805bcac2103f42f74", + "oracle": "chromium/svg-filter-turbulence-morphology-blend-restore.png", + "oracle_sha256": "bf6243487410162a719a64f5fd81533eb974e471aef3f5b29a231128cb5c31de", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-octave-cap", + "source": "svg-filter-turbulence-octave-cap.svg", + "source_sha256": "c18027810d3b5b459d8f4c4385e4d76a61951f30b06b45d6a5a1a38e36220ecf", + "oracle": "chromium/svg-filter-turbulence-octave-cap.png", + "oracle_sha256": "0a87c7e482fd8d6487d1b4d813494982a47bd409d4953590fef8a357da6d7517", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-octave-grammar", + "source": "svg-filter-turbulence-octave-grammar.svg", + "source_sha256": "d0dc7f2a24f3cfe795698ef9e73bf4f972363d20265660c572bb683bbeed0dd9", + "oracle": "chromium/svg-filter-turbulence-octave-grammar.png", + "oracle_sha256": "86c1ae45c0b3986a84c85f011f35679e3146502b8c697729e50583f05f16a844", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-octave-negative", + "source": "svg-filter-turbulence-octave-negative.svg", + "source_sha256": "7efd4b9931f38ca1f9a6120916b536424aa99ea7fab63fa2b7d067694169c68b", + "oracle": "chromium/svg-filter-turbulence-octave-negative.png", + "oracle_sha256": "5892610a63d0176452c8b1f3ad1f284dab7ec872b7d1b351daf781799e71e9a3", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-octave-nine", + "source": "svg-filter-turbulence-octave-nine.svg", + "source_sha256": "672da89efe7ee94565d9f786181a8cad55ae27c0a15e72ef629df38a7557d6dc", + "oracle": "chromium/svg-filter-turbulence-octave-nine.png", + "oracle_sha256": "0a87c7e482fd8d6487d1b4d813494982a47bd409d4953590fef8a357da6d7517", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-octave-one", + "source": "svg-filter-turbulence-octave-one.svg", + "source_sha256": "5abd5d3b4fb36b19356dfb0b6b9b4f5c6354e3d61ccab4f8201573c42bd13c24", + "oracle": "chromium/svg-filter-turbulence-octave-one.png", + "oracle_sha256": "86c1ae45c0b3986a84c85f011f35679e3146502b8c697729e50583f05f16a844", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-octave-two", + "source": "svg-filter-turbulence-octave-two.svg", + "source_sha256": "4114e2f7b88207df4015293ea003204694cf655d0d01abe2cc8f1b5caca5c65c", + "oracle": "chromium/svg-filter-turbulence-octave-two.png", + "oracle_sha256": "fe9f1d83a04ca260feefc41615fba1b75c02f45acaa873211c9e21e9c76ffdf3", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-octave-zero", + "source": "svg-filter-turbulence-octave-zero.svg", + "source_sha256": "e7ba13a70a58deb37a957f117a04019fdfe36c7386c7fc385e31c78168b80975", + "oracle": "chromium/svg-filter-turbulence-octave-zero.png", + "oracle_sha256": "5892610a63d0176452c8b1f3ad1f284dab7ec872b7d1b351daf781799e71e9a3", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-quarter-turn", + "source": "svg-filter-turbulence-quarter-turn.svg", + "source_sha256": "13c7451ab0ba4ee4ad2c706dd784e05614c08ac56da39f5fea8dfe607593c634", + "oracle": "chromium/svg-filter-turbulence-quarter-turn.png", + "oracle_sha256": "1e1db21306450a211e106c376fcf266fa43fcedee0ee1473165ae03dc80becb8", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-region-percent", + "source": "svg-filter-turbulence-region-percent.svg", + "source_sha256": "ca4f5617cd40d2296986db473dc1c912415423df2571a22ebe4776f7216a3109", + "oracle": "chromium/svg-filter-turbulence-region-percent.png", + "oracle_sha256": "84a48e1f60371c08ef65313d076809296d1dd7653c3bf2c8b71b4ec78dfade00", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-region-stitch-fraction", + "source": "svg-filter-turbulence-region-stitch-fraction.svg", + "source_sha256": "9af0837efa4efb41f52ef57e44e9db01b9e8a62967e34c8dd8ef01b7e4058912", + "oracle": "chromium/svg-filter-turbulence-region-stitch-fraction.png", + "oracle_sha256": "9851b1150466ec9a829d8a6defddbf8903ab8eaa153756a6c0b9c70937f23851", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-seed-fraction", + "source": "svg-filter-turbulence-seed-fraction.svg", + "source_sha256": "a9bc29a6e8c245b40ddc53679ed5acb2dac4ce4449414106152bf0caf0a4abc7", + "oracle": "chromium/svg-filter-turbulence-seed-fraction.png", + "oracle_sha256": "86c1ae45c0b3986a84c85f011f35679e3146502b8c697729e50583f05f16a844", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-seed-grammar", + "source": "svg-filter-turbulence-seed-grammar.svg", + "source_sha256": "66412ef431d32b5892687d67fcfd45e24066db74ef59ebfba6695fdc0663a948", + "oracle": "chromium/svg-filter-turbulence-seed-grammar.png", + "oracle_sha256": "86c1ae45c0b3986a84c85f011f35679e3146502b8c697729e50583f05f16a844", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-seed-negative", + "source": "svg-filter-turbulence-seed-negative.svg", + "source_sha256": "8fb67e596f41806323ef7b426ba95b25a3d76760dccd90d4449aa03b70a6e7e4", + "oracle": "chromium/svg-filter-turbulence-seed-negative.png", + "oracle_sha256": "0085203bbd3eec2d81d16990924e537199ebfffcd1d8184d725033f25be0895f", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-seed-one", + "source": "svg-filter-turbulence-seed-one.svg", + "source_sha256": "620dab51cbdc1de9ff77881301a4fdcb0ad368e71947ba0e3092ee0a720013c6", + "oracle": "chromium/svg-filter-turbulence-seed-one.png", + "oracle_sha256": "86c1ae45c0b3986a84c85f011f35679e3146502b8c697729e50583f05f16a844", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-seed-zero", + "source": "svg-filter-turbulence-seed-zero.svg", + "source_sha256": "772fddf7635aecd9e80f67f58eb73f5579a5c5c38e5525c5fb17574345fa419e", + "oracle": "chromium/svg-filter-turbulence-seed-zero.png", + "oracle_sha256": "86c1ae45c0b3986a84c85f011f35679e3146502b8c697729e50583f05f16a844", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-srgb-linear-blend", + "source": "svg-filter-turbulence-srgb-linear-blend.svg", + "source_sha256": "c9d612359418e8a203404501f69e43199655148a516b5558c8087b6f3b4f15b0", + "oracle": "chromium/svg-filter-turbulence-srgb-linear-blend.png", + "oracle_sha256": "01d3d6b4e2ba3ae4b2c9dbc9cae4384a0cc068caeae21322d32fae6f5e082281", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-srgb-linear-morphology", + "source": "svg-filter-turbulence-srgb-linear-morphology.svg", + "source_sha256": "9b07a43b919e7ac264c6fc8bfb097cdfee74730ea8c4980e6b1b9cf2fd563df1", + "oracle": "chromium/svg-filter-turbulence-srgb-linear-morphology.png", + "oracle_sha256": "5d614123e280ffc33840c003e30396d52161ce6a4d1041e6c05aaa90de70ff03", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-stitch", + "source": "svg-filter-turbulence-stitch.svg", + "source_sha256": "41c8d6291223c87c258499f8d4517dd87f82170f8375a6284d7ca10a4e4e16ad", + "oracle": "chromium/svg-filter-turbulence-stitch.png", + "oracle_sha256": "3f7c45f4014676bf3d614ee6cb9b98764220f2a69f45fdae72eac88db6052ceb", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-stitch-grammar", + "source": "svg-filter-turbulence-stitch-grammar.svg", + "source_sha256": "a05d743b46f5553da4a7151ee5fea595ef8f482be3ca0edd088c8d0038df4e54", + "oracle": "chromium/svg-filter-turbulence-stitch-grammar.png", + "oracle_sha256": "605b14448abce2b32df17c06f8e4e73cabf21472985d2d71605984bc752bfd7f", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-stitch-none", + "source": "svg-filter-turbulence-stitch-none.svg", + "source_sha256": "adb7680e9edcc77bb4d0ca0ac47eb0c22e65e8e32ddb7c2330d8ec81e902a2b9", + "oracle": "chromium/svg-filter-turbulence-stitch-none.png", + "oracle_sha256": "605b14448abce2b32df17c06f8e4e73cabf21472985d2d71605984bc752bfd7f", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-type-fractal", + "source": "svg-filter-turbulence-type-fractal.svg", + "source_sha256": "7fd59f4f8432f5a6a687e971579c5d7ca438b188c31a1b9e7e4fd612dff4f070", + "oracle": "chromium/svg-filter-turbulence-type-fractal.png", + "oracle_sha256": "0e04ea656944c612dee8b41ccc740110fcd9e751d57f15b41927cf8ad74ed5d0", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-type-grammar", + "source": "svg-filter-turbulence-type-grammar.svg", + "source_sha256": "d89d2972975d79680ac1b68688d96ab2764ac8507af56732fa9b0c9a697cae21", + "oracle": "chromium/svg-filter-turbulence-type-grammar.png", + "oracle_sha256": "86c1ae45c0b3986a84c85f011f35679e3146502b8c697729e50583f05f16a844", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-type-turbulence", + "source": "svg-filter-turbulence-type-turbulence.svg", + "source_sha256": "20e5bf1197d210570ed3376b01fc68672f6ee2493e8a65e553602f8fa41327e3", + "oracle": "chromium/svg-filter-turbulence-type-turbulence.png", + "oracle_sha256": "86c1ae45c0b3986a84c85f011f35679e3146502b8c697729e50583f05f16a844", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-use", + "source": "svg-filter-turbulence-use.svg", + "source_sha256": "9b17399e7c7bd90888fa320764000f962a93424c75e55741371dce223de150f5", + "oracle": "chromium/svg-filter-turbulence-use.png", + "oracle_sha256": "bfe0103497040b07a89908436ab241db85eab1d370018b9549ae2fb2c3472ec7", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-viewbox", + "source": "svg-filter-turbulence-viewbox.svg", + "source_sha256": "f647e37e9bd47bf369a7954c652e8fe65b09ac07b56bd03d5bb4c3340fda77ac", + "oracle": "chromium/svg-filter-turbulence-viewbox.png", + "oracle_sha256": "7cd5aa1c5949c8bbf6316371e582aa8f9e4419cbfff91153cfe927edda43d4a3", + "width": 64, + "height": 64 + }, { "id": "svg-filter-units-grammar", "source": "svg-filter-units-grammar.svg", diff --git a/fixtures/web-first/primitives.json b/fixtures/web-first/primitives.json index c3ff1e57..7fbbecb6 100644 --- a/fixtures/web-first/primitives.json +++ b/fixtures/web-first/primitives.json @@ -1591,6 +1591,294 @@ "width": 64, "height": 64 }, + { + "id": "svg-filter-displacement-color-linear", + "source": "svg-filter-displacement-color-linear.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-color-linear.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-color-srgb", + "source": "svg-filter-displacement-color-srgb.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-color-srgb.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-default", + "source": "svg-filter-displacement-default.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-default.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-empty-generated", + "source": "svg-filter-displacement-empty-generated.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-empty-generated.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-generated-color", + "source": "svg-filter-displacement-generated-color.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-generated-color.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-half-alpha-map", + "source": "svg-filter-displacement-half-alpha-map.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-half-alpha-map.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-input-default-in2", + "source": "svg-filter-displacement-input-default-in2.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-input-default-in2.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-input-unknown-in", + "source": "svg-filter-displacement-input-unknown-in.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-input-unknown-in.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-map-region", + "source": "svg-filter-displacement-map-region.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-map-region.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-opacity", + "source": "svg-filter-displacement-opacity.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-opacity.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-path", + "source": "svg-filter-displacement-path.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-path.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-primitive-units", + "source": "svg-filter-displacement-primitive-units.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-primitive-units.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-quarter-turn", + "source": "svg-filter-displacement-quarter-turn.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-quarter-turn.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-region-percent", + "source": "svg-filter-displacement-region-percent.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-region-percent.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-scale-fraction", + "source": "svg-filter-displacement-scale-fraction.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-scale-fraction.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-scale-invalid", + "source": "svg-filter-displacement-scale-invalid.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-scale-invalid.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-scale-negative", + "source": "svg-filter-displacement-scale-negative.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-scale-negative.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-scale-positive", + "source": "svg-filter-displacement-scale-positive.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-scale-positive.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-scale-trailing-comma", + "source": "svg-filter-displacement-scale-trailing-comma.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-scale-trailing-comma.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-scale-transform", + "source": "svg-filter-displacement-scale-transform.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-scale-transform.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-scale-zero", + "source": "svg-filter-displacement-scale-zero.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-scale-zero.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-selector-alpha", + "source": "svg-filter-displacement-selector-alpha.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-selector-alpha.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-selector-axes", + "source": "svg-filter-displacement-selector-axes.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-selector-axes.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-selector-blue", + "source": "svg-filter-displacement-selector-blue.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-selector-blue.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-selector-default", + "source": "svg-filter-displacement-selector-default.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-selector-default.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-selector-grammar", + "source": "svg-filter-displacement-selector-grammar.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-selector-grammar.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-selector-green", + "source": "svg-filter-displacement-selector-green.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-selector-green.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-selector-red", + "source": "svg-filter-displacement-selector-red.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-selector-red.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-source-alpha-map", + "source": "svg-filter-displacement-source-alpha-map.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-source-alpha-map.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-source-rgb-map", + "source": "svg-filter-displacement-source-rgb-map.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-source-rgb-map.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-stroke", + "source": "svg-filter-displacement-stroke.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-stroke.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-transparent-map", + "source": "svg-filter-displacement-transparent-map.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-transparent-map.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-turbulence-map", + "source": "svg-filter-displacement-turbulence-map.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-turbulence-map.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-turbulence-map-linear", + "source": "svg-filter-displacement-turbulence-map-linear.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-turbulence-map-linear.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-use", + "source": "svg-filter-displacement-use.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-use.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-displacement-viewbox", + "source": "svg-filter-displacement-viewbox.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-displacement-viewbox.png", + "width": 64, + "height": 64 + }, { "id": "svg-filter-drop-shadow-anisotropic", "source": "svg-filter-drop-shadow-anisotropic.svg", @@ -2559,6 +2847,446 @@ "width": 64, "height": 64 }, + { + "id": "svg-filter-turbulence-base-comma", + "source": "svg-filter-turbulence-base-comma.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-base-comma.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-base-invalid", + "source": "svg-filter-turbulence-base-invalid.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-base-invalid.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-base-negative-axis", + "source": "svg-filter-turbulence-base-negative-axis.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-base-negative-axis.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-base-one", + "source": "svg-filter-turbulence-base-one.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-base-one.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-base-plus-exponent", + "source": "svg-filter-turbulence-base-plus-exponent.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-base-plus-exponent.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-base-trailing-comma", + "source": "svg-filter-turbulence-base-trailing-comma.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-base-trailing-comma.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-base-two", + "source": "svg-filter-turbulence-base-two.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-base-two.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-blend-chain-difference", + "source": "svg-filter-turbulence-blend-chain-difference.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-blend-chain-difference.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-blend-chain-exclusion", + "source": "svg-filter-turbulence-blend-chain-exclusion.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-blend-chain-exclusion.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-blend-morphology-restore", + "source": "svg-filter-turbulence-blend-morphology-restore.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-blend-morphology-restore.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-color-default", + "source": "svg-filter-turbulence-color-default.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-color-default.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-color-linear", + "source": "svg-filter-turbulence-color-linear.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-color-linear.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-color-srgb", + "source": "svg-filter-turbulence-color-srgb.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-color-srgb.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-default", + "source": "svg-filter-turbulence-default.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-default.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-difference-linear", + "source": "svg-filter-turbulence-difference-linear.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-difference-linear.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-difference-srgb-direct", + "source": "svg-filter-turbulence-difference-srgb-direct.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-difference-srgb-direct.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-difference-srgb-transfer", + "source": "svg-filter-turbulence-difference-srgb-transfer.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-difference-srgb-transfer.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-displacement-map", + "source": "svg-filter-turbulence-displacement-map.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-displacement-map.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-empty-source", + "source": "svg-filter-turbulence-empty-source.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-empty-source.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-exclusion-linear", + "source": "svg-filter-turbulence-exclusion-linear.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-exclusion-linear.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-exclusion-srgb-direct", + "source": "svg-filter-turbulence-exclusion-srgb-direct.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-exclusion-srgb-direct.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-exclusion-srgb-transfer", + "source": "svg-filter-turbulence-exclusion-srgb-transfer.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-exclusion-srgb-transfer.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-fractal-octave-zero", + "source": "svg-filter-turbulence-fractal-octave-zero.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-fractal-octave-zero.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-fractional-translate", + "source": "svg-filter-turbulence-fractional-translate.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-fractional-translate.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-graph-blend", + "source": "svg-filter-turbulence-graph-blend.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-graph-blend.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-graph-result-reuse", + "source": "svg-filter-turbulence-graph-result-reuse.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-graph-result-reuse.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-linear-blend", + "source": "svg-filter-turbulence-linear-blend.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-linear-blend.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-linear-srgb-blend", + "source": "svg-filter-turbulence-linear-srgb-blend.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-linear-srgb-blend.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-linear-srgb-morphology", + "source": "svg-filter-turbulence-linear-srgb-morphology.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-linear-srgb-morphology.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-morphology-blend-restore", + "source": "svg-filter-turbulence-morphology-blend-restore.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-morphology-blend-restore.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-octave-cap", + "source": "svg-filter-turbulence-octave-cap.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-octave-cap.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-octave-grammar", + "source": "svg-filter-turbulence-octave-grammar.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-octave-grammar.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-octave-negative", + "source": "svg-filter-turbulence-octave-negative.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-octave-negative.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-octave-nine", + "source": "svg-filter-turbulence-octave-nine.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-octave-nine.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-octave-one", + "source": "svg-filter-turbulence-octave-one.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-octave-one.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-octave-two", + "source": "svg-filter-turbulence-octave-two.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-octave-two.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-octave-zero", + "source": "svg-filter-turbulence-octave-zero.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-octave-zero.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-quarter-turn", + "source": "svg-filter-turbulence-quarter-turn.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-quarter-turn.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-region-percent", + "source": "svg-filter-turbulence-region-percent.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-region-percent.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-region-stitch-fraction", + "source": "svg-filter-turbulence-region-stitch-fraction.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-region-stitch-fraction.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-seed-fraction", + "source": "svg-filter-turbulence-seed-fraction.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-seed-fraction.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-seed-grammar", + "source": "svg-filter-turbulence-seed-grammar.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-seed-grammar.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-seed-negative", + "source": "svg-filter-turbulence-seed-negative.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-seed-negative.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-seed-one", + "source": "svg-filter-turbulence-seed-one.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-seed-one.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-seed-zero", + "source": "svg-filter-turbulence-seed-zero.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-seed-zero.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-srgb-linear-blend", + "source": "svg-filter-turbulence-srgb-linear-blend.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-srgb-linear-blend.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-srgb-linear-morphology", + "source": "svg-filter-turbulence-srgb-linear-morphology.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-srgb-linear-morphology.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-stitch", + "source": "svg-filter-turbulence-stitch.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-stitch.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-stitch-grammar", + "source": "svg-filter-turbulence-stitch-grammar.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-stitch-grammar.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-stitch-none", + "source": "svg-filter-turbulence-stitch-none.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-stitch-none.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-type-fractal", + "source": "svg-filter-turbulence-type-fractal.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-type-fractal.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-type-grammar", + "source": "svg-filter-turbulence-type-grammar.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-type-grammar.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-type-turbulence", + "source": "svg-filter-turbulence-type-turbulence.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-type-turbulence.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-use", + "source": "svg-filter-turbulence-use.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-use.png", + "width": 64, + "height": 64 + }, + { + "id": "svg-filter-turbulence-viewbox", + "source": "svg-filter-turbulence-viewbox.svg", + "entry": "standalone-svg", + "oracle": "chromium/svg-filter-turbulence-viewbox.png", + "width": 64, + "height": 64 + }, { "id": "svg-filter-units-grammar", "source": "svg-filter-units-grammar.svg", diff --git a/fixtures/web-first/svg-filter-displacement-color-linear.svg b/fixtures/web-first/svg-filter-displacement-color-linear.svg new file mode 100644 index 00000000..8db4685d --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-color-linear.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-color-srgb.svg b/fixtures/web-first/svg-filter-displacement-color-srgb.svg new file mode 100644 index 00000000..dbea17a2 --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-color-srgb.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-default.svg b/fixtures/web-first/svg-filter-displacement-default.svg new file mode 100644 index 00000000..763c9c57 --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-default.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-empty-generated.svg b/fixtures/web-first/svg-filter-displacement-empty-generated.svg new file mode 100644 index 00000000..3803000b --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-empty-generated.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-generated-color.svg b/fixtures/web-first/svg-filter-displacement-generated-color.svg new file mode 100644 index 00000000..838959fb --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-generated-color.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-half-alpha-map.svg b/fixtures/web-first/svg-filter-displacement-half-alpha-map.svg new file mode 100644 index 00000000..30d57ee4 --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-half-alpha-map.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-input-default-in2.svg b/fixtures/web-first/svg-filter-displacement-input-default-in2.svg new file mode 100644 index 00000000..a8ea9879 --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-input-default-in2.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-input-unknown-in.svg b/fixtures/web-first/svg-filter-displacement-input-unknown-in.svg new file mode 100644 index 00000000..c1825355 --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-input-unknown-in.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-map-region.svg b/fixtures/web-first/svg-filter-displacement-map-region.svg new file mode 100644 index 00000000..263764fa --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-map-region.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-opacity.svg b/fixtures/web-first/svg-filter-displacement-opacity.svg new file mode 100644 index 00000000..6fcb15f4 --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-opacity.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-path.svg b/fixtures/web-first/svg-filter-displacement-path.svg new file mode 100644 index 00000000..4d76838c --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-path.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-primitive-units.svg b/fixtures/web-first/svg-filter-displacement-primitive-units.svg new file mode 100644 index 00000000..b5150f28 --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-primitive-units.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-quarter-turn.svg b/fixtures/web-first/svg-filter-displacement-quarter-turn.svg new file mode 100644 index 00000000..2bd1d77c --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-quarter-turn.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-region-percent.svg b/fixtures/web-first/svg-filter-displacement-region-percent.svg new file mode 100644 index 00000000..f2e0403a --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-region-percent.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-scale-fraction.svg b/fixtures/web-first/svg-filter-displacement-scale-fraction.svg new file mode 100644 index 00000000..4073c0ef --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-scale-fraction.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-scale-invalid.svg b/fixtures/web-first/svg-filter-displacement-scale-invalid.svg new file mode 100644 index 00000000..c65615c8 --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-scale-invalid.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-scale-negative.svg b/fixtures/web-first/svg-filter-displacement-scale-negative.svg new file mode 100644 index 00000000..75d58b2f --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-scale-negative.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-scale-positive.svg b/fixtures/web-first/svg-filter-displacement-scale-positive.svg new file mode 100644 index 00000000..4f2541fb --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-scale-positive.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-scale-trailing-comma.svg b/fixtures/web-first/svg-filter-displacement-scale-trailing-comma.svg new file mode 100644 index 00000000..610e58e1 --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-scale-trailing-comma.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-scale-transform.svg b/fixtures/web-first/svg-filter-displacement-scale-transform.svg new file mode 100644 index 00000000..77042e50 --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-scale-transform.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-scale-zero.svg b/fixtures/web-first/svg-filter-displacement-scale-zero.svg new file mode 100644 index 00000000..8960c1f5 --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-scale-zero.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-selector-alpha.svg b/fixtures/web-first/svg-filter-displacement-selector-alpha.svg new file mode 100644 index 00000000..c65d4ef7 --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-selector-alpha.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-selector-axes.svg b/fixtures/web-first/svg-filter-displacement-selector-axes.svg new file mode 100644 index 00000000..97212cdb --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-selector-axes.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-selector-blue.svg b/fixtures/web-first/svg-filter-displacement-selector-blue.svg new file mode 100644 index 00000000..c17d8664 --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-selector-blue.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-selector-default.svg b/fixtures/web-first/svg-filter-displacement-selector-default.svg new file mode 100644 index 00000000..4f2541fb --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-selector-default.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-selector-grammar.svg b/fixtures/web-first/svg-filter-displacement-selector-grammar.svg new file mode 100644 index 00000000..c1645801 --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-selector-grammar.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-selector-green.svg b/fixtures/web-first/svg-filter-displacement-selector-green.svg new file mode 100644 index 00000000..80b1a267 --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-selector-green.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-selector-red.svg b/fixtures/web-first/svg-filter-displacement-selector-red.svg new file mode 100644 index 00000000..5da30022 --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-selector-red.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-source-alpha-map.svg b/fixtures/web-first/svg-filter-displacement-source-alpha-map.svg new file mode 100644 index 00000000..62d7de87 --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-source-alpha-map.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-source-rgb-map.svg b/fixtures/web-first/svg-filter-displacement-source-rgb-map.svg new file mode 100644 index 00000000..6db7b873 --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-source-rgb-map.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-stroke.svg b/fixtures/web-first/svg-filter-displacement-stroke.svg new file mode 100644 index 00000000..c7f27fac --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-stroke.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-transparent-map.svg b/fixtures/web-first/svg-filter-displacement-transparent-map.svg new file mode 100644 index 00000000..ea39e436 --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-transparent-map.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-turbulence-map-linear.svg b/fixtures/web-first/svg-filter-displacement-turbulence-map-linear.svg new file mode 100644 index 00000000..c0ffa76d --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-turbulence-map-linear.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-turbulence-map.svg b/fixtures/web-first/svg-filter-displacement-turbulence-map.svg new file mode 100644 index 00000000..0609f5b6 --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-turbulence-map.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-use.svg b/fixtures/web-first/svg-filter-displacement-use.svg new file mode 100644 index 00000000..ba7e8007 --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-use.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/fixtures/web-first/svg-filter-displacement-viewbox.svg b/fixtures/web-first/svg-filter-displacement-viewbox.svg new file mode 100644 index 00000000..4524e143 --- /dev/null +++ b/fixtures/web-first/svg-filter-displacement-viewbox.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-base-comma.svg b/fixtures/web-first/svg-filter-turbulence-base-comma.svg new file mode 100644 index 00000000..944647ef --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-base-comma.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-base-invalid.svg b/fixtures/web-first/svg-filter-turbulence-base-invalid.svg new file mode 100644 index 00000000..9463ef72 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-base-invalid.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-base-negative-axis.svg b/fixtures/web-first/svg-filter-turbulence-base-negative-axis.svg new file mode 100644 index 00000000..27076833 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-base-negative-axis.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-base-one.svg b/fixtures/web-first/svg-filter-turbulence-base-one.svg new file mode 100644 index 00000000..176842b5 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-base-one.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-base-plus-exponent.svg b/fixtures/web-first/svg-filter-turbulence-base-plus-exponent.svg new file mode 100644 index 00000000..aa5be901 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-base-plus-exponent.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-base-trailing-comma.svg b/fixtures/web-first/svg-filter-turbulence-base-trailing-comma.svg new file mode 100644 index 00000000..f7899077 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-base-trailing-comma.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-base-two.svg b/fixtures/web-first/svg-filter-turbulence-base-two.svg new file mode 100644 index 00000000..e2bd577a --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-base-two.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-blend-chain-difference.svg b/fixtures/web-first/svg-filter-turbulence-blend-chain-difference.svg new file mode 100644 index 00000000..d0e4a24b --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-blend-chain-difference.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-blend-chain-exclusion.svg b/fixtures/web-first/svg-filter-turbulence-blend-chain-exclusion.svg new file mode 100644 index 00000000..ca542f20 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-blend-chain-exclusion.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-blend-morphology-restore.svg b/fixtures/web-first/svg-filter-turbulence-blend-morphology-restore.svg new file mode 100644 index 00000000..50c45255 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-blend-morphology-restore.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-color-default.svg b/fixtures/web-first/svg-filter-turbulence-color-default.svg new file mode 100644 index 00000000..f71fe013 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-color-default.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-color-linear.svg b/fixtures/web-first/svg-filter-turbulence-color-linear.svg new file mode 100644 index 00000000..63abb690 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-color-linear.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-color-srgb.svg b/fixtures/web-first/svg-filter-turbulence-color-srgb.svg new file mode 100644 index 00000000..2a0a48d9 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-color-srgb.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-default.svg b/fixtures/web-first/svg-filter-turbulence-default.svg new file mode 100644 index 00000000..9023d392 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-default.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-difference-linear.svg b/fixtures/web-first/svg-filter-turbulence-difference-linear.svg new file mode 100644 index 00000000..11b05521 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-difference-linear.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-difference-srgb-direct.svg b/fixtures/web-first/svg-filter-turbulence-difference-srgb-direct.svg new file mode 100644 index 00000000..e77b5884 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-difference-srgb-direct.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-difference-srgb-transfer.svg b/fixtures/web-first/svg-filter-turbulence-difference-srgb-transfer.svg new file mode 100644 index 00000000..2dbcdab5 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-difference-srgb-transfer.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-displacement-map.svg b/fixtures/web-first/svg-filter-turbulence-displacement-map.svg new file mode 100644 index 00000000..db56825c --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-displacement-map.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-empty-source.svg b/fixtures/web-first/svg-filter-turbulence-empty-source.svg new file mode 100644 index 00000000..daa8c98f --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-empty-source.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-exclusion-linear.svg b/fixtures/web-first/svg-filter-turbulence-exclusion-linear.svg new file mode 100644 index 00000000..18f75a32 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-exclusion-linear.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-exclusion-srgb-direct.svg b/fixtures/web-first/svg-filter-turbulence-exclusion-srgb-direct.svg new file mode 100644 index 00000000..fb6fced3 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-exclusion-srgb-direct.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-exclusion-srgb-transfer.svg b/fixtures/web-first/svg-filter-turbulence-exclusion-srgb-transfer.svg new file mode 100644 index 00000000..f8627153 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-exclusion-srgb-transfer.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-fractal-octave-zero.svg b/fixtures/web-first/svg-filter-turbulence-fractal-octave-zero.svg new file mode 100644 index 00000000..f5a7a395 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-fractal-octave-zero.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-fractional-translate.svg b/fixtures/web-first/svg-filter-turbulence-fractional-translate.svg new file mode 100644 index 00000000..4daaeb8d --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-fractional-translate.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-graph-blend.svg b/fixtures/web-first/svg-filter-turbulence-graph-blend.svg new file mode 100644 index 00000000..353e060c --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-graph-blend.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-graph-result-reuse.svg b/fixtures/web-first/svg-filter-turbulence-graph-result-reuse.svg new file mode 100644 index 00000000..949992e7 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-graph-result-reuse.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-linear-blend.svg b/fixtures/web-first/svg-filter-turbulence-linear-blend.svg new file mode 100644 index 00000000..0f78c264 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-linear-blend.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-linear-srgb-blend.svg b/fixtures/web-first/svg-filter-turbulence-linear-srgb-blend.svg new file mode 100644 index 00000000..7a177da9 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-linear-srgb-blend.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-linear-srgb-morphology.svg b/fixtures/web-first/svg-filter-turbulence-linear-srgb-morphology.svg new file mode 100644 index 00000000..45c33c23 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-linear-srgb-morphology.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-morphology-blend-restore.svg b/fixtures/web-first/svg-filter-turbulence-morphology-blend-restore.svg new file mode 100644 index 00000000..097f0024 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-morphology-blend-restore.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-octave-cap.svg b/fixtures/web-first/svg-filter-turbulence-octave-cap.svg new file mode 100644 index 00000000..2dafff9a --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-octave-cap.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-octave-grammar.svg b/fixtures/web-first/svg-filter-turbulence-octave-grammar.svg new file mode 100644 index 00000000..0eb69657 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-octave-grammar.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-octave-negative.svg b/fixtures/web-first/svg-filter-turbulence-octave-negative.svg new file mode 100644 index 00000000..a2282ef3 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-octave-negative.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-octave-nine.svg b/fixtures/web-first/svg-filter-turbulence-octave-nine.svg new file mode 100644 index 00000000..5c73e215 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-octave-nine.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-octave-one.svg b/fixtures/web-first/svg-filter-turbulence-octave-one.svg new file mode 100644 index 00000000..0a917453 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-octave-one.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-octave-two.svg b/fixtures/web-first/svg-filter-turbulence-octave-two.svg new file mode 100644 index 00000000..55eda026 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-octave-two.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-octave-zero.svg b/fixtures/web-first/svg-filter-turbulence-octave-zero.svg new file mode 100644 index 00000000..7256dede --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-octave-zero.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-quarter-turn.svg b/fixtures/web-first/svg-filter-turbulence-quarter-turn.svg new file mode 100644 index 00000000..7860c6f3 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-quarter-turn.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-region-percent.svg b/fixtures/web-first/svg-filter-turbulence-region-percent.svg new file mode 100644 index 00000000..1a7ab193 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-region-percent.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-region-stitch-fraction.svg b/fixtures/web-first/svg-filter-turbulence-region-stitch-fraction.svg new file mode 100644 index 00000000..efb133f4 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-region-stitch-fraction.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-seed-fraction.svg b/fixtures/web-first/svg-filter-turbulence-seed-fraction.svg new file mode 100644 index 00000000..42581ca0 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-seed-fraction.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-seed-grammar.svg b/fixtures/web-first/svg-filter-turbulence-seed-grammar.svg new file mode 100644 index 00000000..5380c3aa --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-seed-grammar.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-seed-negative.svg b/fixtures/web-first/svg-filter-turbulence-seed-negative.svg new file mode 100644 index 00000000..16b4bca9 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-seed-negative.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-seed-one.svg b/fixtures/web-first/svg-filter-turbulence-seed-one.svg new file mode 100644 index 00000000..ab01124f --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-seed-one.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-seed-zero.svg b/fixtures/web-first/svg-filter-turbulence-seed-zero.svg new file mode 100644 index 00000000..3fb693ed --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-seed-zero.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-srgb-linear-blend.svg b/fixtures/web-first/svg-filter-turbulence-srgb-linear-blend.svg new file mode 100644 index 00000000..61e65fd4 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-srgb-linear-blend.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-srgb-linear-morphology.svg b/fixtures/web-first/svg-filter-turbulence-srgb-linear-morphology.svg new file mode 100644 index 00000000..04c4a041 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-srgb-linear-morphology.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-stitch-grammar.svg b/fixtures/web-first/svg-filter-turbulence-stitch-grammar.svg new file mode 100644 index 00000000..3eead95f --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-stitch-grammar.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-stitch-none.svg b/fixtures/web-first/svg-filter-turbulence-stitch-none.svg new file mode 100644 index 00000000..cfcda54c --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-stitch-none.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-stitch.svg b/fixtures/web-first/svg-filter-turbulence-stitch.svg new file mode 100644 index 00000000..b574a6e3 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-stitch.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-type-fractal.svg b/fixtures/web-first/svg-filter-turbulence-type-fractal.svg new file mode 100644 index 00000000..5428092d --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-type-fractal.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-type-grammar.svg b/fixtures/web-first/svg-filter-turbulence-type-grammar.svg new file mode 100644 index 00000000..bbf2d0f6 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-type-grammar.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-type-turbulence.svg b/fixtures/web-first/svg-filter-turbulence-type-turbulence.svg new file mode 100644 index 00000000..6ecace51 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-type-turbulence.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-use.svg b/fixtures/web-first/svg-filter-turbulence-use.svg new file mode 100644 index 00000000..88587c92 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-use.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/fixtures/web-first/svg-filter-turbulence-viewbox.svg b/fixtures/web-first/svg-filter-turbulence-viewbox.svg new file mode 100644 index 00000000..af43f001 --- /dev/null +++ b/fixtures/web-first/svg-filter-turbulence-viewbox.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/fixtures/web-first/unsupported/README.md b/fixtures/web-first/unsupported/README.md index efc3c69c..f620c9e5 100644 --- a/fixtures/web-first/unsupported/README.md +++ b/fixtures/web-first/unsupported/README.md @@ -72,7 +72,10 @@ The scannable, generated view of this register (beside the baked cells) is | `svg-filter-css-functions.svg` · `svg-filter-list.svg` · `svg-filter-list-quoted.svg` | The direct presentation reader carries one same-document URL only. Filter functions and a list of multiple operations are valid members of the wider property grammar and refuse by the stable filter name instead of being mistaken for that single URL. Quoted and unquoted URL tokens take the same branch. | | `svg-filter-external.svg` · `svg-filter-root.svg` | External resource resolution and the root host CSS-layer route stay outside this self-contained SVG-local frame. An external reference skips its target; an active root filter refuses in both admissions because there is no smaller honest hole. | | `svg-filter-href.svg` | Refuse `` inheritance before graph construction. Dropping the template edge could change primitive order, inherited attributes, inputs, and every output pixel. | -| `svg-filter-primitive.svg` | Any unrepresented `fe*` child invalidates graph construction transactionally. The witness has advanced to `feTurbulence`; it skips the whole affected target, so a supported prefix can never escape as a plausible filtered result. | +| `svg-filter-primitive.svg` | Any unrepresented `fe*` child invalidates graph construction transactionally. The witness has advanced to `feConvolveMatrix` after a supported flood prefix; it skips the whole affected target, so that prefix can never escape as a plausible filtered result. | +| `svg-filter-turbulence-transform-precision.svg` | Refuse `feTurbulence` under a general rotation or shear. Axis maps, fractional translation, reflection, and exact quarter turns are exact; a sampled 17-degree rotation differs by 3,173 pixels at maximum channel delta 7 and a shear by 3,110 at delta 6 (measured, not celled). | +| `svg-filter-displacement-transform-precision.svg` | Refuse `feDisplacementMap` under a general rotation or shear. Axis maps, fractional translation, reflection, and exact quarter turns are exact; a sampled 17-degree rotation differs by 280 pixels at maximum channel delta 13 and a shear by 360 at delta 18 (measured, not celled). | +| `svg-filter-displacement-clip-precision.svg` | Refuse `feDisplacementMap` across a geometric `clip-path`. The sampled path clip differs by 35 pixels at maximum channel delta 2 (measured, not celled); opacity without the clip is exact and celled. | | `svg-filter-morphology-transform-precision.svg` | Refuse `feMorphology` under a target mapping outside axis maps and exact quarter turns. Fractional translation and scale, reflection, and exact quarter turns are exact. A sampled 17-degree source mapping differs by 171 pixels at Δ12; generated-only rotation differs by 142 pixels at Δ9, and sampled shears reproduce the class (measured, not celled). | | `svg-filter-morphology-paint-server-precision.svg` | Refuse source-dependent `feMorphology` when the isolated source uses a paint-server fill or stroke. A linear gradient differs by 125 pixels at Δ1 even through zero radius and by 191 pixels at Δ1 when active; radial fills and gradient strokes reproduce the class. Generated-only graph inputs do not inherit the patrol (measured, not celled). | | `svg-filter-morphology-filled-ellipse-precision.svg` | Refuse active source-dependent `feMorphology` over a filled native `` or ``. The sampled circle differs by 3 pixels at Δ9 and a fractional ellipse by 8 pixels at Δ6. Rounded rectangles, curved paths, circle strokes, and round path strokes are exact. This is the retained fill-only ellipse coverage boundary tracked separately in gridaco/nothing#88, not a widened morphology gap (measured, not celled). | diff --git a/fixtures/web-first/unsupported/svg-filter-displacement-clip-precision.svg b/fixtures/web-first/unsupported/svg-filter-displacement-clip-precision.svg new file mode 100644 index 00000000..8c12ebb0 --- /dev/null +++ b/fixtures/web-first/unsupported/svg-filter-displacement-clip-precision.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/fixtures/web-first/unsupported/svg-filter-displacement-transform-precision.svg b/fixtures/web-first/unsupported/svg-filter-displacement-transform-precision.svg new file mode 100644 index 00000000..401a38f1 --- /dev/null +++ b/fixtures/web-first/unsupported/svg-filter-displacement-transform-precision.svg @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/fixtures/web-first/unsupported/svg-filter-primitive.svg b/fixtures/web-first/unsupported/svg-filter-primitive.svg index 20c46dbf..3ede989a 100644 --- a/fixtures/web-first/unsupported/svg-filter-primitive.svg +++ b/fixtures/web-first/unsupported/svg-filter-primitive.svg @@ -1,5 +1,8 @@ - + + + + diff --git a/fixtures/web-first/unsupported/svg-filter-turbulence-transform-precision.svg b/fixtures/web-first/unsupported/svg-filter-turbulence-transform-precision.svg new file mode 100644 index 00000000..64b44be7 --- /dev/null +++ b/fixtures/web-first/unsupported/svg-filter-turbulence-transform-precision.svg @@ -0,0 +1,7 @@ + + + + + + +