Bug 1824502 - Implement native SVG filter rendering in WebRender r=gw

Differential Revision: https://phabricator.services.mozilla.com/D209646
This commit is contained in:
Ashley Hale 2024-05-08 04:38:26 +00:00
Родитель bba28ff2f5
Коммит 82db1e3cc5
39 изменённых файлов: 6002 добавлений и 65 удалений

Просмотреть файл

@ -106,6 +106,7 @@ float force_scalar(Float f) { return f[0]; }
int32_t force_scalar(I32 i) { return i[0]; }
struct vec3;
struct vec4;
struct ivec2;
@ -2287,6 +2288,12 @@ bvec2_scalar notEqual(vec2_scalar x, vec2_scalar y) {
return bvec2_scalar(notEqual(x.x, y.x), notEqual(x.y, y.y));
}
vec3 floor(vec3 v) { return vec3(floor(v.x), floor(v.y), floor(v.z)); }
vec4 floor(vec4 v) {
return vec4(floor(v.x), floor(v.y), floor(v.z), floor(v.w));
}
struct mat4_scalar;
struct mat2_scalar {

Просмотреть файл

@ -0,0 +1,859 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
Notes about how this shader works and the requirements it faces:
* Each filter has a _CONVERTSRGB variant that converts to linear before
performing the operation and converts back to sRGB for output. Since the
inputs and output of this shader are premultiplied alpha, we have to undo the
premultiply and then convert the sRGB color to linearRGB color, perform the
desired operations, and then convert back to sRGB and then premultiply again.
* For some operations the _CONVERTSRGB variant is never invoked by WebRender, an
example is OPACITY where the two modes have identical results, as scaling RGBA
by a single scalar value only changes the opacity, without changing color
relative to alpha, the sRGB vs linearRGB gamut mapping is relative to alpha.
* SVG filters are usually in linear space so the _CONVERTSRGB variant is used
heavily in SVG filter graphs, whereas CSS filters use the regular variant.
* Handling of color-interpolation for feFlood and feDropShadow is out of scope
for this shader, the values can be converted in the WebRender or Gecko code if
necessary.
* All SVG filters have a subregion rect to clip the operation to, in many cases
this can just be an alteration of the task uvrect in WebRender, but in some
cases we might need to enforce it in the shader.
* All filters have an offset for each input, this is an optimization for folding
feOffset into the downstream nodes of the graph, because it is inefficient to
be copying an image just to scroll it, and feOffset is not rare.
Notes about specific filter kinds:
* FILTER_BLEND_* kinds follow spec
https://drafts.fxtf.org/compositing-1/#blending which says to mix from
Rs to B() based on Rb.a, then mix from Rb to that color based on Rs.a.
* FILTER_COMPOSITE_* kinds use math from Skia as it is elegant.
* FILTER_COMPONENT_TRANSFER_* kinds assume a [4][256] table in gpucache.
* FILTER_DROP_SHADOW_* composites Rs source over the dropshadow in Rb.a,
it's not actually a composite per se, and needs to be composited onto
the destination using a separate blend.
*/
#define WR_FEATURE_TEXTURE_2D
#include shared,prim_shared
varying highp vec2 vInput1Uv;
varying highp vec2 vInput2Uv;
flat varying highp vec4 vInput1UvRect;
flat varying highp vec4 vInput2UvRect;
flat varying mediump ivec4 vData;
flat varying mediump vec4 vFilterData0;
flat varying mediump vec4 vFilterData1;
// x: Filter input count, y: Filter kind.
// Packed in to a vector to work around bug 1630356.
flat varying mediump ivec2 vFilterInputCountFilterKindVec;
#define vFilterInputCount vFilterInputCountFilterKindVec.x
#define vFilterKind vFilterInputCountFilterKindVec.y
// Packed in to a vector to work around bug 1630356.
flat varying mediump vec2 vFloat0;
flat varying mediump mat4 vColorMat;
flat varying mediump ivec4 vFuncs;
// must match add_svg_filter_node_instances in render_target.rs
#define FILTER_IDENTITY 0
#define FILTER_IDENTITY_CONVERTSRGB 1
#define FILTER_OPACITY 2
#define FILTER_OPACITY_CONVERTSRGB 3
#define FILTER_TO_ALPHA 4
#define FILTER_TO_ALPHA_CONVERTSRGB 5
#define FILTER_BLEND_COLOR 6
#define FILTER_BLEND_COLOR_CONVERTSRGB 7
#define FILTER_BLEND_COLOR_BURN 8
#define FILTER_BLEND_COLOR_BURN_CONVERTSRGB 9
#define FILTER_BLEND_COLOR_DODGE 10
#define FILTER_BLEND_COLOR_DODGE_CONVERTSRGB 11
#define FILTER_BLEND_DARKEN 12
#define FILTER_BLEND_DARKEN_CONVERTSRGB 13
#define FILTER_BLEND_DIFFERENCE 14
#define FILTER_BLEND_DIFFERENCE_CONVERTSRGB 15
#define FILTER_BLEND_EXCLUSION 16
#define FILTER_BLEND_EXCLUSION_CONVERTSRGB 17
#define FILTER_BLEND_HARD_LIGHT 18
#define FILTER_BLEND_HARD_LIGHT_CONVERTSRGB 19
#define FILTER_BLEND_HUE 20
#define FILTER_BLEND_HUE_CONVERTSRGB 21
#define FILTER_BLEND_LIGHTEN 22
#define FILTER_BLEND_LIGHTEN_CONVERTSRGB 23
#define FILTER_BLEND_LUMINOSITY 24
#define FILTER_BLEND_LUMINOSITY_CONVERTSRGB 25
#define FILTER_BLEND_MULTIPLY 26
#define FILTER_BLEND_MULTIPLY_CONVERTSRGB 27
#define FILTER_BLEND_NORMAL 28
#define FILTER_BLEND_NORMAL_CONVERTSRGB 29
#define FILTER_BLEND_OVERLAY 30
#define FILTER_BLEND_OVERLAY_CONVERTSRGB 31
#define FILTER_BLEND_SATURATION 32
#define FILTER_BLEND_SATURATION_CONVERTSRGB 33
#define FILTER_BLEND_SCREEN 34
#define FILTER_BLEND_SCREEN_CONVERTSRGB 35
#define FILTER_BLEND_SOFT_LIGHT 36
#define FILTER_BLEND_SOFT_LIGHT_CONVERTSRGB 37
#define FILTER_COLOR_MATRIX 38
#define FILTER_COLOR_MATRIX_CONVERTSRGB 39
#define FILTER_COMPONENT_TRANSFER 40
#define FILTER_COMPONENT_TRANSFER_CONVERTSRGB 41
#define FILTER_COMPOSITE_ARITHMETIC 42
#define FILTER_COMPOSITE_ARITHMETIC_CONVERTSRGB 43
#define FILTER_COMPOSITE_ATOP 44
#define FILTER_COMPOSITE_ATOP_CONVERTSRGB 45
#define FILTER_COMPOSITE_IN 46
#define FILTER_COMPOSITE_IN_CONVERTSRGB 47
#define FILTER_COMPOSITE_LIGHTER 48
#define FILTER_COMPOSITE_LIGHTER_CONVERTSRGB 49
#define FILTER_COMPOSITE_OUT 50
#define FILTER_COMPOSITE_OUT_CONVERTSRGB 51
#define FILTER_COMPOSITE_OVER 52
#define FILTER_COMPOSITE_OVER_CONVERTSRGB 53
#define FILTER_COMPOSITE_XOR 54
#define FILTER_COMPOSITE_XOR_CONVERTSRGB 55
#define FILTER_CONVOLVE_MATRIX_EDGE_MODE_DUPLICATE 56
#define FILTER_CONVOLVE_MATRIX_EDGE_MODE_DUPLICATE_CONVERTSRGB 57
#define FILTER_CONVOLVE_MATRIX_EDGE_MODE_NONE 58
#define FILTER_CONVOLVE_MATRIX_EDGE_MODE_NONE_CONVERTSRGB 59
#define FILTER_CONVOLVE_MATRIX_EDGE_MODE_WRAP 60
#define FILTER_CONVOLVE_MATRIX_EDGE_MODE_WRAP_CONVERTSRGB 61
#define FILTER_DIFFUSE_LIGHTING_DISTANT 62
#define FILTER_DIFFUSE_LIGHTING_DISTANT_CONVERTSRGB 63
#define FILTER_DIFFUSE_LIGHTING_POINT 64
#define FILTER_DIFFUSE_LIGHTING_POINT_CONVERTSRGB 65
#define FILTER_DIFFUSE_LIGHTING_SPOT 66
#define FILTER_DIFFUSE_LIGHTING_SPOT_CONVERTSRGB 67
#define FILTER_DISPLACEMENT_MAP 68
#define FILTER_DISPLACEMENT_MAP_CONVERTSRGB 69
#define FILTER_DROP_SHADOW 70
#define FILTER_DROP_SHADOW_CONVERTSRGB 71
#define FILTER_FLOOD 72
#define FILTER_FLOOD_CONVERTSRGB 73
#define FILTER_GAUSSIAN_BLUR 74
#define FILTER_GAUSSIAN_BLUR_CONVERTSRGB 75
#define FILTER_IMAGE 76
#define FILTER_IMAGE_CONVERTSRGB 77
#define FILTER_MORPHOLOGY_DILATE 80
#define FILTER_MORPHOLOGY_DILATE_CONVERTSRGB 81
#define FILTER_MORPHOLOGY_ERODE 82
#define FILTER_MORPHOLOGY_ERODE_CONVERTSRGB 83
#define FILTER_SPECULAR_LIGHTING_DISTANT 86
#define FILTER_SPECULAR_LIGHTING_DISTANT_CONVERTSRGB 87
#define FILTER_SPECULAR_LIGHTING_POINT 88
#define FILTER_SPECULAR_LIGHTING_POINT_CONVERTSRGB 89
#define FILTER_SPECULAR_LIGHTING_SPOT 90
#define FILTER_SPECULAR_LIGHTING_SPOT_CONVERTSRGB 91
#define FILTER_TILE 92
#define FILTER_TILE_CONVERTSRGB 93
#define FILTER_TURBULENCE_WITH_FRACTAL_NOISE_WITH_NO_STITCHING 94
#define FILTER_TURBULENCE_WITH_FRACTAL_NOISE_WITH_NO_STITCHING_CONVERTSRGB 95
#define FILTER_TURBULENCE_WITH_FRACTAL_NOISE_WITH_STITCHING 96
#define FILTER_TURBULENCE_WITH_FRACTAL_NOISE_WITH_STITCHING_CONVERTSRGB 97
#define FILTER_TURBULENCE_WITH_TURBULENCE_NOISE_WITH_NO_STITCHING 98
#define FILTER_TURBULENCE_WITH_TURBULENCE_NOISE_WITH_NO_STITCHING_CONVERTSRGB 99
#define FILTER_TURBULENCE_WITH_TURBULENCE_NOISE_WITH_STITCHING 100
#define FILTER_TURBULENCE_WITH_TURBULENCE_NOISE_WITH_STITCHING_CONVERTSRGB 101
// All of the _CONVERTSRGB variants match this mask
#define FILTER_BITFLAGS_CONVERTSRGB 1
#ifdef WR_VERTEX_SHADER
// due to padding around the target rect, we need to know both target and render task rect
PER_INSTANCE in vec4 aFilterTargetRect;
PER_INSTANCE in vec4 aFilterInput1ContentScaleAndOffset;
PER_INSTANCE in vec4 aFilterInput2ContentScaleAndOffset;
PER_INSTANCE in int aFilterInput1TaskAddress;
PER_INSTANCE in int aFilterInput2TaskAddress;
PER_INSTANCE in int aFilterKind;
PER_INSTANCE in int aFilterInputCount;
PER_INSTANCE in ivec2 aFilterExtraDataAddress;
// used for feFlood and feDropShadow colors
// this is based on SrgbToLinear below, but that version hits SWGL compile
// errors when used in vertex shaders for some reason
vec3 vertexSrgbToLinear(vec3 color) {
vec3 c1 = color * vec3(1.0 / 12.92);
vec3 c2;
c2.r = pow(color.r * (1.0 / 1.055) + (0.055 / 1.055), 2.4);
c2.g = pow(color.g * (1.0 / 1.055) + (0.055 / 1.055), 2.4);
c2.b = pow(color.b * (1.0 / 1.055) + (0.055 / 1.055), 2.4);
return mix(c1, c2, step(vec3(0.04045), color));
}
vec4 compute_uv_rect(RectWithEndpoint task_rect, vec2 texture_size) {
vec4 uvRect = vec4(task_rect.p0 + vec2(0.5),
task_rect.p1 - vec2(0.5));
uvRect /= texture_size.xyxy;
return uvRect;
}
vec2 compute_uv(RectWithEndpoint task_rect, vec4 scale_and_offset, vec2 target_size, vec2 texture_size) {
// SVG spec dictates that we want to *not* scale coordinates between nodes,
// must be able to sample at offsets, and must be able to fetch outside the
// clamp rect as transparent black, so we have custom texcoords for the
// fetch area, separate from the clamp rect
return (task_rect.p0 + scale_and_offset.zw + scale_and_offset.xy * aPosition.xy) / texture_size.xy;
}
void main(void) {
vec2 pos = mix(aFilterTargetRect.xy, aFilterTargetRect.zw, aPosition.xy);
RectWithEndpoint input_1_task;
if (aFilterInputCount > 0) {
vec2 texture_size = vec2(TEX_SIZE(sColor0).xy);
input_1_task = fetch_render_task_rect(aFilterInput1TaskAddress);
vInput1UvRect = compute_uv_rect(input_1_task, texture_size);
vInput1Uv = compute_uv(input_1_task, aFilterInput1ContentScaleAndOffset, aFilterTargetRect.zw - aFilterTargetRect.xy, texture_size);
}
RectWithEndpoint input_2_task;
if (aFilterInputCount > 1) {
vec2 texture_size = vec2(TEX_SIZE(sColor1).xy);
input_2_task = fetch_render_task_rect(aFilterInput2TaskAddress);
vInput2UvRect = compute_uv_rect(input_2_task, texture_size);
vInput2Uv = compute_uv(input_2_task, aFilterInput2ContentScaleAndOffset, aFilterTargetRect.zw - aFilterTargetRect.xy, texture_size);
}
vFilterInputCount = aFilterInputCount;
vFilterKind = aFilterKind;
switch (aFilterKind) {
case FILTER_IDENTITY:
case FILTER_IDENTITY_CONVERTSRGB:
break;
case FILTER_OPACITY:
case FILTER_OPACITY_CONVERTSRGB:
// opacity takes one input and an alpha value, so we just stuffed
// that in the unused input 2 content rect
vFloat0.x = aFilterInput2ContentScaleAndOffset.x;
break;
case FILTER_TO_ALPHA:
case FILTER_TO_ALPHA_CONVERTSRGB:
break;
case FILTER_BLEND_COLOR:
case FILTER_BLEND_COLOR_CONVERTSRGB:
case FILTER_BLEND_COLOR_BURN:
case FILTER_BLEND_COLOR_BURN_CONVERTSRGB:
case FILTER_BLEND_COLOR_DODGE:
case FILTER_BLEND_COLOR_DODGE_CONVERTSRGB:
case FILTER_BLEND_DARKEN:
case FILTER_BLEND_DARKEN_CONVERTSRGB:
case FILTER_BLEND_DIFFERENCE:
case FILTER_BLEND_DIFFERENCE_CONVERTSRGB:
case FILTER_BLEND_EXCLUSION:
case FILTER_BLEND_EXCLUSION_CONVERTSRGB:
case FILTER_BLEND_HARD_LIGHT:
case FILTER_BLEND_HARD_LIGHT_CONVERTSRGB:
case FILTER_BLEND_HUE:
case FILTER_BLEND_HUE_CONVERTSRGB:
case FILTER_BLEND_LIGHTEN:
case FILTER_BLEND_LIGHTEN_CONVERTSRGB:
case FILTER_BLEND_LUMINOSITY:
case FILTER_BLEND_LUMINOSITY_CONVERTSRGB:
case FILTER_BLEND_MULTIPLY:
case FILTER_BLEND_MULTIPLY_CONVERTSRGB:
case FILTER_BLEND_NORMAL:
case FILTER_BLEND_NORMAL_CONVERTSRGB:
case FILTER_BLEND_OVERLAY:
case FILTER_BLEND_OVERLAY_CONVERTSRGB:
case FILTER_BLEND_SATURATION:
case FILTER_BLEND_SATURATION_CONVERTSRGB:
case FILTER_BLEND_SCREEN:
case FILTER_BLEND_SCREEN_CONVERTSRGB:
case FILTER_BLEND_SOFT_LIGHT:
case FILTER_BLEND_SOFT_LIGHT_CONVERTSRGB:
break;
case FILTER_COLOR_MATRIX:
case FILTER_COLOR_MATRIX_CONVERTSRGB:
vec4 mat_data[4] = fetch_from_gpu_cache_4_direct(aFilterExtraDataAddress);
vColorMat = mat4(mat_data[0], mat_data[1], mat_data[2], mat_data[3]);
vFilterData0 = fetch_from_gpu_cache_1_direct(aFilterExtraDataAddress + ivec2(4, 0));
break;
case FILTER_COMPONENT_TRANSFER:
case FILTER_COMPONENT_TRANSFER_CONVERTSRGB:
vData = ivec4(aFilterExtraDataAddress, 0, 0);
break;
case FILTER_COMPOSITE_ARITHMETIC:
case FILTER_COMPOSITE_ARITHMETIC_CONVERTSRGB:
// arithmetic parameters
vFilterData0 = fetch_from_gpu_cache_1_direct(aFilterExtraDataAddress);
break;
case FILTER_COMPOSITE_ATOP:
case FILTER_COMPOSITE_ATOP_CONVERTSRGB:
case FILTER_COMPOSITE_IN:
case FILTER_COMPOSITE_IN_CONVERTSRGB:
case FILTER_COMPOSITE_LIGHTER:
case FILTER_COMPOSITE_LIGHTER_CONVERTSRGB:
case FILTER_COMPOSITE_OUT:
case FILTER_COMPOSITE_OUT_CONVERTSRGB:
case FILTER_COMPOSITE_OVER:
case FILTER_COMPOSITE_OVER_CONVERTSRGB:
case FILTER_COMPOSITE_XOR:
case FILTER_COMPOSITE_XOR_CONVERTSRGB:
break;
case FILTER_CONVOLVE_MATRIX_EDGE_MODE_DUPLICATE:
case FILTER_CONVOLVE_MATRIX_EDGE_MODE_DUPLICATE_CONVERTSRGB:
// TODO
break;
case FILTER_CONVOLVE_MATRIX_EDGE_MODE_NONE:
case FILTER_CONVOLVE_MATRIX_EDGE_MODE_NONE_CONVERTSRGB:
// TODO
break;
case FILTER_CONVOLVE_MATRIX_EDGE_MODE_WRAP:
case FILTER_CONVOLVE_MATRIX_EDGE_MODE_WRAP_CONVERTSRGB:
// TODO
break;
case FILTER_DIFFUSE_LIGHTING_DISTANT:
case FILTER_DIFFUSE_LIGHTING_DISTANT_CONVERTSRGB:
// TODO
break;
case FILTER_DIFFUSE_LIGHTING_POINT:
case FILTER_DIFFUSE_LIGHTING_POINT_CONVERTSRGB:
// TODO
break;
case FILTER_DIFFUSE_LIGHTING_SPOT:
case FILTER_DIFFUSE_LIGHTING_SPOT_CONVERTSRGB:
// TODO
break;
case FILTER_DISPLACEMENT_MAP:
case FILTER_DISPLACEMENT_MAP_CONVERTSRGB:
// TODO
break;
case FILTER_DROP_SHADOW:
vFilterData0 = fetch_from_gpu_cache_1_direct(aFilterExtraDataAddress);
// premultiply the color
vFilterData0.rgb = vFilterData0.rgb * vFilterData0.a;
break;
case FILTER_DROP_SHADOW_CONVERTSRGB:
vFilterData0 = fetch_from_gpu_cache_1_direct(aFilterExtraDataAddress);
// convert from sRGB to linearRGB and premultiply by alpha
vFilterData0.rgb = vertexSrgbToLinear(vFilterData0.rgb);
vFilterData0.rgb = vFilterData0.rgb * vFilterData0.a;
break;
case FILTER_FLOOD:
// feFlood has no actual input textures, so input 2 rect is color
vFilterData0 = aFilterInput2ContentScaleAndOffset;
// premultiply the color
vFilterData0.rgb = vFilterData0.rgb * vFilterData0.a;
break;
case FILTER_FLOOD_CONVERTSRGB:
// feFlood has no actual input textures, so input 2 rect is color
vFilterData0 = aFilterInput2ContentScaleAndOffset;
// convert from sRGB to linearRGB and premultiply by alpha
vFilterData0.rgb = vertexSrgbToLinear(vFilterData0.rgb);
vFilterData0.rgb = vFilterData0.rgb * vFilterData0.a;
break;
case FILTER_GAUSSIAN_BLUR:
case FILTER_GAUSSIAN_BLUR_CONVERTSRGB:
break;
case FILTER_IMAGE:
case FILTER_IMAGE_CONVERTSRGB:
// TODO
break;
case FILTER_MORPHOLOGY_DILATE:
case FILTER_MORPHOLOGY_DILATE_CONVERTSRGB:
case FILTER_MORPHOLOGY_ERODE:
case FILTER_MORPHOLOGY_ERODE_CONVERTSRGB:
// morphology filters have radius values in second input rect
vFilterData0 = aFilterInput2ContentScaleAndOffset;
break;
case FILTER_SPECULAR_LIGHTING_DISTANT:
case FILTER_SPECULAR_LIGHTING_DISTANT_CONVERTSRGB:
// TODO
break;
case FILTER_SPECULAR_LIGHTING_POINT:
case FILTER_SPECULAR_LIGHTING_POINT_CONVERTSRGB:
// TODO
break;
case FILTER_SPECULAR_LIGHTING_SPOT:
case FILTER_SPECULAR_LIGHTING_SPOT_CONVERTSRGB:
// TODO
break;
case FILTER_TILE:
case FILTER_TILE_CONVERTSRGB:
// TODO
break;
case FILTER_TURBULENCE_WITH_FRACTAL_NOISE_WITH_NO_STITCHING:
case FILTER_TURBULENCE_WITH_FRACTAL_NOISE_WITH_NO_STITCHING_CONVERTSRGB:
case FILTER_TURBULENCE_WITH_FRACTAL_NOISE_WITH_STITCHING:
case FILTER_TURBULENCE_WITH_FRACTAL_NOISE_WITH_STITCHING_CONVERTSRGB:
case FILTER_TURBULENCE_WITH_TURBULENCE_NOISE_WITH_NO_STITCHING:
case FILTER_TURBULENCE_WITH_TURBULENCE_NOISE_WITH_NO_STITCHING_CONVERTSRGB:
case FILTER_TURBULENCE_WITH_TURBULENCE_NOISE_WITH_STITCHING:
case FILTER_TURBULENCE_WITH_TURBULENCE_NOISE_WITH_STITCHING_CONVERTSRGB:
// TODO
break;
default:
break;
}
gl_Position = uTransform * vec4(pos, 0.0, 1.0);
}
#endif
#ifdef WR_FRAGMENT_SHADER
vec3 Multiply(vec3 Cb, vec3 Cs) {
return Cb * Cs;
}
vec3 Screen(vec3 Cb, vec3 Cs) {
return Cb + Cs - (Cb * Cs);
}
vec3 HardLight(vec3 Cb, vec3 Cs) {
vec3 m = Multiply(Cb, 2.0 * Cs);
vec3 s = Screen(Cb, 2.0 * Cs - 1.0);
vec3 edge = vec3(0.5, 0.5, 0.5);
return mix(m, s, step(edge, Cs));
}
// TODO: Worth doing with mix/step? Check GLSL output.
float ColorDodge(float Cb, float Cs) {
if (Cb == 0.0)
return 0.0;
else if (Cs == 1.0)
return 1.0;
else
return min(1.0, Cb / (1.0 - Cs));
}
// TODO: Worth doing with mix/step? Check GLSL output.
float ColorBurn(float Cb, float Cs) {
if (Cb == 1.0)
return 1.0;
else if (Cs == 0.0)
return 0.0;
else
return 1.0 - min(1.0, (1.0 - Cb) / Cs);
}
float SoftLight(float Cb, float Cs) {
if (Cs <= 0.5) {
return Cb - (1.0 - 2.0 * Cs) * Cb * (1.0 - Cb);
} else {
float D;
if (Cb <= 0.25)
D = ((16.0 * Cb - 12.0) * Cb + 4.0) * Cb;
else
D = sqrt(Cb);
return Cb + (2.0 * Cs - 1.0) * (D - Cb);
}
}
vec3 Difference(vec3 Cb, vec3 Cs) {
return abs(Cb - Cs);
}
vec3 Exclusion(vec3 Cb, vec3 Cs) {
return Cb + Cs - 2.0 * Cb * Cs;
}
// These functions below are taken from the spec.
// There's probably a much quicker way to implement
// them in GLSL...
float Sat(vec3 c) {
return max(c.r, max(c.g, c.b)) - min(c.r, min(c.g, c.b));
}
float Lum(vec3 c) {
vec3 f = vec3(0.3, 0.59, 0.11);
return dot(c, f);
}
vec3 ClipColor(vec3 C) {
float L = Lum(C);
float n = min(C.r, min(C.g, C.b));
float x = max(C.r, max(C.g, C.b));
if (n < 0.0)
C = L + (((C - L) * L) / (L - n));
if (x > 1.0)
C = L + (((C - L) * (1.0 - L)) / (x - L));
return C;
}
vec3 SetLum(vec3 C, float l) {
float d = l - Lum(C);
return ClipColor(C + d);
}
void SetSatInner(inout float Cmin, inout float Cmid, inout float Cmax, float s) {
if (Cmax > Cmin) {
Cmid = (((Cmid - Cmin) * s) / (Cmax - Cmin));
Cmax = s;
} else {
Cmid = 0.0;
Cmax = 0.0;
}
Cmin = 0.0;
}
vec3 SetSat(vec3 C, float s) {
if (C.r <= C.g) {
if (C.g <= C.b) {
SetSatInner(C.r, C.g, C.b, s);
} else {
if (C.r <= C.b) {
SetSatInner(C.r, C.b, C.g, s);
} else {
SetSatInner(C.b, C.r, C.g, s);
}
}
} else {
if (C.r <= C.b) {
SetSatInner(C.g, C.r, C.b, s);
} else {
if (C.g <= C.b) {
SetSatInner(C.g, C.b, C.r, s);
} else {
SetSatInner(C.b, C.g, C.r, s);
}
}
}
return C;
}
vec3 Hue(vec3 Cb, vec3 Cs) {
return SetLum(SetSat(Cs, Sat(Cb)), Lum(Cb));
}
vec3 Saturation(vec3 Cb, vec3 Cs) {
return SetLum(SetSat(Cb, Sat(Cs)), Lum(Cb));
}
vec3 Color(vec3 Cb, vec3 Cs) {
return SetLum(Cs, Lum(Cb));
}
vec3 Luminosity(vec3 Cb, vec3 Cs) {
return SetLum(Cb, Lum(Cs));
}
// Based on the Gecko implementation in
// https://hg.mozilla.org/mozilla-central/file/91b4c3687d75/gfx/src/FilterSupport.cpp#l24
// These could be made faster by sampling a lookup table stored in a float texture
// with linear interpolation.
vec3 SrgbToLinear(vec3 color) {
vec3 c1 = color / 12.92;
vec3 c2 = pow(color / 1.055 + vec3(0.055 / 1.055), vec3(2.4));
return if_then_else(lessThanEqual(color, vec3(0.04045)), c1, c2);
}
vec3 LinearToSrgb(vec3 color) {
vec3 c1 = color * 12.92;
vec3 c2 = vec3(1.055) * pow(color, vec3(1.0 / 2.4)) - vec3(0.055);
return if_then_else(lessThanEqual(color, vec3(0.0031308)), c1, c2);
}
vec4 sampleInUvRect(sampler2D sampler, vec2 uv, vec4 uvRect) {
vec2 clamped = clamp(uv.xy, uvRect.xy, uvRect.zw);
return texture(sampler, clamped);
}
vec4 sampleInUvRectRepeat(sampler2D sampler, vec2 uv, vec4 uvRect) {
vec2 size = (uvRect.zw - uvRect.xy);
vec2 tiled = uv.xy - floor((uv.xy - uvRect.xy) / size) * size;
return texture(sampler, tiled);
}
void main(void) {
// Raw premultiplied color of source texture
vec4 Rs = vec4(0.0, 0.0, 0.0, 0.0);
// Raw premultiplied color of destination texture
vec4 Rb = vec4(0.0, 0.0, 0.0, 0.0);
// Normalized (non-premultiplied) color of source texture
vec4 Ns = vec4(0.0, 0.0, 0.0, 0.0);
// Normalized (non-premultiplied) color of destination texture
vec4 Nb = vec4(0.0, 0.0, 0.0, 0.0);
// used in FILTER_COMPONENT_TRANSFER
ivec4 k;
if (vFilterInputCount > 0) {
Rs = sampleInUvRect(sColor0, vInput1Uv, vInput1UvRect);
Ns.rgb = Rs.rgb * (1.0 / max(0.000001, Rs.a));
Ns.a = Rs.a;
if ((vFilterKind & FILTER_BITFLAGS_CONVERTSRGB) != 0) {
Ns.rgb = SrgbToLinear(Ns.rgb);
Rs.rgb = Ns.rgb * Rs.a;
}
}
if (vFilterInputCount > 1) {
Rb = sampleInUvRect(sColor1, vInput2Uv, vInput2UvRect);
Nb.rgb = Rb.rgb * (1.0 / max(0.000001, Rb.a));
Nb.a = Rb.a;
if ((vFilterKind & FILTER_BITFLAGS_CONVERTSRGB) != 0) {
Nb.rgb = SrgbToLinear(Nb.rgb);
Rb.rgb = Nb.rgb * Rb.a;
}
}
vec4 result = vec4(1.0, 0.0, 0.0, 1.0);
switch (vFilterKind) {
case FILTER_IDENTITY:
case FILTER_IDENTITY_CONVERTSRGB:
result = Rs;
break;
case FILTER_OPACITY:
case FILTER_OPACITY_CONVERTSRGB:
result = Rs * vFloat0.x;
break;
case FILTER_TO_ALPHA:
case FILTER_TO_ALPHA_CONVERTSRGB:
// Just return the alpha, we have literally nothing to do on the RGB
// values here, this also means CONVERTSRGB is irrelevant.
oFragColor = vec4(0.0, 0.0, 0.0, Rs.a);
return;
case FILTER_BLEND_COLOR:
case FILTER_BLEND_COLOR_CONVERTSRGB:
result.rgb = Color(Nb.rgb, Ns.rgb);
result.rgb = (1.0 - Rb.a) * Rs.rgb + (1.0 - Rs.a) * Rb.rgb + Rs.a * Rb.a * result.rgb;
result.a = Rb.a * (1.0 - Rs.a) + Rs.a;
break;
case FILTER_BLEND_COLOR_BURN:
case FILTER_BLEND_COLOR_BURN_CONVERTSRGB:
result.rgb = vec3(ColorBurn(Nb.r, Ns.r), ColorBurn(Nb.g, Ns.g), ColorBurn(Nb.b, Ns.b));
result.rgb = (1.0 - Rb.a) * Rs.rgb + (1.0 - Rs.a) * Rb.rgb + Rs.a * Rb.a * result.rgb;
result.a = Rb.a * (1.0 - Rs.a) + Rs.a;
break;
case FILTER_BLEND_COLOR_DODGE:
case FILTER_BLEND_COLOR_DODGE_CONVERTSRGB:
result.rgb = vec3(ColorDodge(Nb.r, Ns.r), ColorDodge(Nb.g, Ns.g), ColorDodge(Nb.b, Ns.b));
result.rgb = (1.0 - Rb.a) * Rs.rgb + (1.0 - Rs.a) * Rb.rgb + Rs.a * Rb.a * result.rgb;
result.a = Rb.a * (1.0 - Rs.a) + Rs.a;
break;
case FILTER_BLEND_DARKEN:
case FILTER_BLEND_DARKEN_CONVERTSRGB:
result.rgb = Rs.rgb + Rb.rgb - max(Rs.rgb * Rb.a, Rb.rgb * Rs.a);
result.a = Rb.a * (1.0 - Rs.a) + Rs.a;
break;
case FILTER_BLEND_DIFFERENCE:
case FILTER_BLEND_DIFFERENCE_CONVERTSRGB:
result.rgb = Rs.rgb + Rb.rgb - 2.0 * min(Rs.rgb * Rb.a, Rb.rgb * Rs.a);
result.a = Rb.a * (1.0 - Rs.a) + Rs.a;
break;
case FILTER_BLEND_EXCLUSION:
case FILTER_BLEND_EXCLUSION_CONVERTSRGB:
result.rgb = Rs.rgb + Rb.rgb - 2.0 * (Rs.rgb * Rb.rgb);
result.a = Rb.a * (1.0 - Rs.a) + Rs.a;
break;
case FILTER_BLEND_HARD_LIGHT:
case FILTER_BLEND_HARD_LIGHT_CONVERTSRGB:
result.rgb = HardLight(Nb.rgb, Ns.rgb);
result.rgb = (1.0 - Rb.a) * Rs.rgb + (1.0 - Rs.a) * Rb.rgb + Rs.a * Rb.a * result.rgb;
result.a = Rb.a * (1.0 - Rs.a) + Rs.a;
break;
case FILTER_BLEND_HUE:
case FILTER_BLEND_HUE_CONVERTSRGB:
result.rgb = Hue(Nb.rgb, Ns.rgb);
result.rgb = (1.0 - Rb.a) * Rs.rgb + (1.0 - Rs.a) * Rb.rgb + Rs.a * Rb.a * result.rgb;
result.a = Rb.a * (1.0 - Rs.a) + Rs.a;
break;
case FILTER_BLEND_LIGHTEN:
case FILTER_BLEND_LIGHTEN_CONVERTSRGB:
result.rgb = Rs.rgb + Rb.rgb - min(Rs.rgb * Rb.a, Rb.rgb * Rs.a);
result.a = Rb.a * (1.0 - Rs.a) + Rs.a;
break;
case FILTER_BLEND_LUMINOSITY:
case FILTER_BLEND_LUMINOSITY_CONVERTSRGB:
result.rgb = Luminosity(Nb.rgb, Ns.rgb);
result.rgb = (1.0 - Rb.a) * Rs.rgb + (1.0 - Rs.a) * Rb.rgb + Rs.a * Rb.a * result.rgb;
result.a = Rb.a * (1.0 - Rs.a) + Rs.a;
break;
case FILTER_BLEND_MULTIPLY:
case FILTER_BLEND_MULTIPLY_CONVERTSRGB:
result.rgb = Rs.rgb * (1.0 - Rb.a) + Rb.rgb * (1.0 - Rs.a) + Rs.rgb * Rb.rgb;
result.a = Rb.a * (1.0 - Rs.a) + Rs.a;
break;
case FILTER_BLEND_NORMAL:
case FILTER_BLEND_NORMAL_CONVERTSRGB:
result = Rb * (1.0 - Rs.a) + Rs;
break;
case FILTER_BLEND_OVERLAY:
case FILTER_BLEND_OVERLAY_CONVERTSRGB:
// Overlay is inverse of Hardlight
result.rgb = HardLight(Ns.rgb, Nb.rgb);
result.rgb = (1.0 - Rb.a) * Rs.rgb + (1.0 - Rs.a) * Rb.rgb + Rs.a * Rb.a * result.rgb;
result.a = Rb.a * (1.0 - Rs.a) + Rs.a;
break;
case FILTER_BLEND_SATURATION:
case FILTER_BLEND_SATURATION_CONVERTSRGB:
result.rgb = Saturation(Nb.rgb, Ns.rgb);
result.rgb = (1.0 - Rb.a) * Rs.rgb + (1.0 - Rs.a) * Rb.rgb + Rs.a * Rb.a * result.rgb;
result.a = Rb.a * (1.0 - Rs.a) + Rs.a;
break;
case FILTER_BLEND_SCREEN:
case FILTER_BLEND_SCREEN_CONVERTSRGB:
result.rgb = Rs.rgb + Rb.rgb - (Rs.rgb * Rb.rgb);
result.a = Rb.a * (1.0 - Rs.a) + Rs.a;
break;
case FILTER_BLEND_SOFT_LIGHT:
case FILTER_BLEND_SOFT_LIGHT_CONVERTSRGB:
result.rgb = vec3(SoftLight(Nb.r, Ns.r), SoftLight(Nb.g, Ns.g), SoftLight(Nb.b, Ns.b));
result.rgb = (1.0 - Rb.a) * Rs.rgb + (1.0 - Rs.a) * Rb.rgb + Rs.a * Rb.a * result.rgb;
result.a = Rb.a * (1.0 - Rs.a) + Rs.a;
break;
case FILTER_COLOR_MATRIX:
case FILTER_COLOR_MATRIX_CONVERTSRGB:
result = vColorMat * Ns + vFilterData0;
result = clamp(result, vec4(0.0), vec4(1.0));
result.rgb = result.rgb * result.a;
break;
case FILTER_COMPONENT_TRANSFER:
case FILTER_COMPONENT_TRANSFER_CONVERTSRGB:
// fetch new value for each channel from the RGBA lookup table.
result = floor(clamp(Ns * 255.0, vec4(0.0), vec4(255.0)));
// SWGL doesn't have an intrinsic for ivec4(vec4)
k = ivec4(int(result.r), int(result.g), int(result.b), int(result.a));
result.r = fetch_from_gpu_cache_1_direct(vData.xy + ivec2(k.r, 0)).r;
result.g = fetch_from_gpu_cache_1_direct(vData.xy + ivec2(k.g, 0)).g;
result.b = fetch_from_gpu_cache_1_direct(vData.xy + ivec2(k.b, 0)).b;
result.a = fetch_from_gpu_cache_1_direct(vData.xy + ivec2(k.a, 0)).a;
result.rgb = result.rgb * result.a;
break;
case FILTER_COMPOSITE_ARITHMETIC:
case FILTER_COMPOSITE_ARITHMETIC_CONVERTSRGB:
result = Rs * Rb * vFilterData0.x + Rs * vFilterData0.y + Rb * vFilterData0.z + vec4(vFilterData0.w);
result = clamp(result, vec4(0.0), vec4(1.0));
break;
case FILTER_COMPOSITE_ATOP:
case FILTER_COMPOSITE_ATOP_CONVERTSRGB:
result = Rs * Rb.a + Rb * (1.0 - Rs.a);
break;
case FILTER_COMPOSITE_IN:
case FILTER_COMPOSITE_IN_CONVERTSRGB:
result = Rs * Rb.a;
break;
case FILTER_COMPOSITE_LIGHTER:
case FILTER_COMPOSITE_LIGHTER_CONVERTSRGB:
result = Rs + Rb;
result = clamp(result, vec4(0.0), vec4(1.0));
break;
case FILTER_COMPOSITE_OUT:
case FILTER_COMPOSITE_OUT_CONVERTSRGB:
result = Rs * (1.0 - Rb.a);
break;
case FILTER_COMPOSITE_OVER:
case FILTER_COMPOSITE_OVER_CONVERTSRGB:
result = Rs + Rb * (1.0 - Rs.a);
break;
case FILTER_COMPOSITE_XOR:
case FILTER_COMPOSITE_XOR_CONVERTSRGB:
result = Rs * (1.0 - Rb.a) + Rb * (1.0 - Rs.a);
break;
case FILTER_CONVOLVE_MATRIX_EDGE_MODE_DUPLICATE:
case FILTER_CONVOLVE_MATRIX_EDGE_MODE_DUPLICATE_CONVERTSRGB:
// TODO
break;
case FILTER_CONVOLVE_MATRIX_EDGE_MODE_NONE:
case FILTER_CONVOLVE_MATRIX_EDGE_MODE_NONE_CONVERTSRGB:
// TODO
break;
case FILTER_CONVOLVE_MATRIX_EDGE_MODE_WRAP:
case FILTER_CONVOLVE_MATRIX_EDGE_MODE_WRAP_CONVERTSRGB:
// TODO
break;
case FILTER_DIFFUSE_LIGHTING_DISTANT:
case FILTER_DIFFUSE_LIGHTING_DISTANT_CONVERTSRGB:
// TODO
break;
case FILTER_DIFFUSE_LIGHTING_POINT:
case FILTER_DIFFUSE_LIGHTING_POINT_CONVERTSRGB:
// TODO
break;
case FILTER_DIFFUSE_LIGHTING_SPOT:
case FILTER_DIFFUSE_LIGHTING_SPOT_CONVERTSRGB:
// TODO
break;
case FILTER_DISPLACEMENT_MAP:
case FILTER_DISPLACEMENT_MAP_CONVERTSRGB:
// TODO
break;
case FILTER_DROP_SHADOW:
case FILTER_DROP_SHADOW_CONVERTSRGB:
// First input is original image, second input is offset and blurred
// image, we replace color of second input with vFilterData.rgb and
// composite with mode OVER.
// This color is already premultiplied, so it's ready to use
result = Rs + vFilterData0 * (Rb.a * (1.0 - Rs.a));
break;
case FILTER_FLOOD:
case FILTER_FLOOD_CONVERTSRGB:
result = vFilterData0;
break;
case FILTER_GAUSSIAN_BLUR:
case FILTER_GAUSSIAN_BLUR_CONVERTSRGB:
// unused - the IDENTITY filter is used for composing this
break;
case FILTER_IMAGE:
case FILTER_IMAGE_CONVERTSRGB:
// TODO - we need to get the uvrect set up in the code before
// this shader case will matter, best to leave it at the fallback
// color for now when it is known to be broken.
break;
case FILTER_MORPHOLOGY_DILATE:
case FILTER_MORPHOLOGY_DILATE_CONVERTSRGB:
// TODO
break;
case FILTER_MORPHOLOGY_ERODE:
case FILTER_MORPHOLOGY_ERODE_CONVERTSRGB:
// TODO
break;
case FILTER_SPECULAR_LIGHTING_DISTANT:
case FILTER_SPECULAR_LIGHTING_DISTANT_CONVERTSRGB:
// TODO
break;
case FILTER_SPECULAR_LIGHTING_POINT:
case FILTER_SPECULAR_LIGHTING_POINT_CONVERTSRGB:
// TODO
break;
case FILTER_SPECULAR_LIGHTING_SPOT:
case FILTER_SPECULAR_LIGHTING_SPOT_CONVERTSRGB:
// TODO
break;
case FILTER_TILE:
case FILTER_TILE_CONVERTSRGB:
// TODO
// we can just return the texel without doing anything else
vec2 tileUv = rect_repeat(vInput1Uv, vInput1UvRect.xy, vInput1UvRect.zw);
oFragColor = sampleInUvRect(sColor0, tileUv, vInput1UvRect);
return;
case FILTER_TURBULENCE_WITH_FRACTAL_NOISE_WITH_NO_STITCHING:
case FILTER_TURBULENCE_WITH_FRACTAL_NOISE_WITH_NO_STITCHING_CONVERTSRGB:
// TODO
break;
case FILTER_TURBULENCE_WITH_FRACTAL_NOISE_WITH_STITCHING:
case FILTER_TURBULENCE_WITH_FRACTAL_NOISE_WITH_STITCHING_CONVERTSRGB:
// TODO
break;
case FILTER_TURBULENCE_WITH_TURBULENCE_NOISE_WITH_NO_STITCHING:
case FILTER_TURBULENCE_WITH_TURBULENCE_NOISE_WITH_NO_STITCHING_CONVERTSRGB:
// TODO
break;
case FILTER_TURBULENCE_WITH_TURBULENCE_NOISE_WITH_STITCHING:
case FILTER_TURBULENCE_WITH_TURBULENCE_NOISE_WITH_STITCHING_CONVERTSRGB:
// TODO
break;
default:
break;
}
if ((vFilterKind & FILTER_BITFLAGS_CONVERTSRGB) != 0) {
// convert back to sRGB in unmultiplied color space
result.rgb = LinearToSrgb(result.rgb * (1.0 / max(0.000001, result.a))) * result.a;
}
oFragColor = result;
}
#endif

Просмотреть файл

@ -38,3 +38,12 @@ vec2 rect_clamp(RectWithEndpoint rect, vec2 pt) {
vec2 rect_size(RectWithEndpoint rect) {
return rect.p1 - rect.p0;
}
// this is similar to rect_clamp but repeats the image for coordinates outside
// the rect, used in SVG feTile filter
vec2 rect_repeat(vec2 p, vec2 p0, vec2 p1) {
vec2 r = p - p0;
vec2 s = p1 - p0;
vec2 is = 1.0 / max(s, vec2(0.000001));
return p0 + s * fract(is * r);
}

Просмотреть файл

@ -1745,7 +1745,8 @@ impl BatchBuilder {
Filter::ComponentTransfer |
Filter::Blur { .. } |
Filter::DropShadows(..) |
Filter::Opacity(..) => unreachable!(),
Filter::Opacity(..) |
Filter::SVGGraphNode(..) => unreachable!(),
};
// Other filters that may introduce opacity are handled via different
@ -2173,6 +2174,53 @@ impl BatchBuilder {
}.encode(),
);
self.add_brush_instance_to_batches(
key,
batch_features,
bounding_rect,
z_id,
INVALID_SEGMENT_INDEX,
EdgeAaSegmentMask::all(),
clip_task_address,
brush_flags,
prim_header_index,
uv_rect_address.as_int(),
);
}
PictureCompositeMode::SVGFEGraph(..) => {
let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
prim_info.clip_task_index,
render_tasks,
).unwrap();
let kind = BatchKind::Brush(
BrushBatchKind::Image(ImageBufferKind::Texture2D)
);
let (uv_rect_address, texture) = render_tasks.resolve_location(
pic_task_id,
gpu_cache,
).unwrap();
let textures = BatchTextures::prim_textured(
texture,
clip_mask_texture_id,
);
let key = BatchKey::new(
kind,
blend_mode,
textures,
);
let prim_header_index = prim_headers.push(
&prim_header,
z_id,
self.batcher.render_task_address,
ImageBrushData {
color_mode: ShaderColorMode::Image,
alpha_type: AlphaType::PremultipliedAlpha,
raster_space: RasterizationSpace::Screen,
opacity: 1.0,
}.encode(),
);
self.add_brush_instance_to_batches(
key,
batch_features,

Просмотреть файл

@ -453,6 +453,7 @@ impl FrameBuilder {
SubpixelMode::Allow,
&mut frame_state,
&frame_context,
data_stores,
&mut scratch.primitive,
tile_caches,
)

Просмотреть файл

@ -129,6 +129,21 @@ pub struct SvgFilterInstance {
pub extra_data_address: GpuCacheAddress,
}
#[derive(Clone, Debug)]
#[repr(C)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct SVGFEFilterInstance {
pub target_rect: DeviceRect,
pub input_1_content_scale_and_offset: [f32; 4],
pub input_2_content_scale_and_offset: [f32; 4],
pub input_1_task_address: RenderTaskAddress,
pub input_2_task_address: RenderTaskAddress,
pub kind: u16,
pub input_count: u16,
pub extra_data_address: GpuCacheAddress,
}
#[derive(Copy, Clone, Debug, Hash, MallocSizeOf, PartialEq, Eq)]
#[repr(C)]
#[cfg_attr(feature = "capture", derive(Serialize))]

Просмотреть файл

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{ColorF, DocumentId, ExternalImageId, PrimitiveFlags, Parameter, RenderReasons};
use api::{ImageFormat, NotificationRequest, Shadow, FilterOp, ImageBufferKind};
use api::{ImageFormat, NotificationRequest, Shadow, FilterOpGraphPictureBufferId, FilterOpGraphPictureReference, FilterOpGraphNode, FilterOp, ImageBufferKind};
use api::FramePublishId;
use api::units::*;
use crate::render_api::DebugCommand;
@ -15,6 +15,7 @@ use crate::frame_builder::Frame;
use crate::profiler::TransactionProfile;
use crate::spatial_tree::SpatialNodeIndex;
use crate::prim_store::PrimitiveInstanceIndex;
use crate::filterdata::FilterDataHandle;
use fxhash::FxHasher;
use plane_split::BspSplitter;
use smallvec::SmallVec;
@ -208,8 +209,557 @@ pub struct PlaneSplitterIndex(pub usize);
/// An arbitrary number which we assume opacity is invisible below.
const OPACITY_EPSILON: f32 = 0.001;
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct FilterGraphPictureReference {
/// Id of the picture in question in a namespace unique to this filter DAG,
/// some are special values like
/// FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic.
pub buffer_id: FilterOpGraphPictureBufferId,
/// Set by wrap_prim_with_filters to the subregion of the input node, may
/// also have been offset for feDropShadow or feOffset
pub subregion: LayoutRect,
/// During scene build this is the offset to apply to the input subregion
/// for feOffset, which can be optimized away by pushing its offset and
/// subregion crop to downstream nodes. This is always zero in render tasks
/// where it has already been applied to subregion by that point. Not used
/// in get_coverage_svgfe because source_padding/target_padding represent
/// the offset there.
pub offset: LayoutVector2D,
/// Equal to the inflate value of the referenced buffer, or 0
pub inflate: i16,
/// Padding on each side to represent how this input is read relative to the
/// node's output subregion, this represents what the operation needs to
/// read from ths input, which may be blurred or offset.
pub source_padding: LayoutRect,
/// Padding on each side to represent how this input affects the node's
/// subregion, this can be used to calculate target subregion based on
/// SourceGraphic subregion. This is usually equal to source_padding except
/// offset in the opposite direction, inflates typically do the same thing
/// to both types of padding.
pub target_padding: LayoutRect,
}
impl From<FilterOpGraphPictureReference> for FilterGraphPictureReference {
fn from(pic: FilterOpGraphPictureReference) -> Self {
FilterGraphPictureReference{
buffer_id: pic.buffer_id,
// All of these are set by wrap_prim_with_filters
subregion: LayoutRect::zero(),
offset: LayoutVector2D::zero(),
inflate: 0,
source_padding: LayoutRect::zero(),
target_padding: LayoutRect::zero(),
}
}
}
pub const SVGFE_CONVOLVE_DIAMETER_LIMIT: usize = 5;
pub const SVGFE_CONVOLVE_VALUES_LIMIT: usize = SVGFE_CONVOLVE_DIAMETER_LIMIT *
SVGFE_CONVOLVE_DIAMETER_LIMIT;
#[derive(Clone, Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum FilterGraphOp {
/// Filter that copies the SourceGraphic image into the specified subregion,
/// This is intentionally the only way to get SourceGraphic into the graph,
/// as the filter region must be applied before it is used.
/// parameters: FilterOpGraphNode
/// SVG filter semantics - no inputs, no linear
SVGFESourceGraphic,
/// Filter that copies the SourceAlpha image into the specified subregion,
/// This is intentionally the only way to get SourceAlpha into the graph,
/// as the filter region must be applied before it is used.
/// parameters: FilterOpGraphNode
/// SVG filter semantics - no inputs, no linear
SVGFESourceAlpha,
/// Filter that does no transformation of the colors, used to implement a
/// few things like SVGFEOffset, and this is the default value in
/// impl_default_for_enums.
/// parameters: FilterGraphNode
/// SVG filter semantics - selectable input with offset
SVGFEIdentity,
/// represents CSS opacity property as a graph node like the rest of the
/// SVGFE* filters
/// parameters: FilterGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
SVGFEOpacity{valuebinding: api::PropertyBinding<f32>, value: f32},
/// convert a color image to an alpha channel - internal use; generated by
/// SVGFilterInstance::GetOrCreateSourceAlphaIndex().
SVGFEToAlpha,
/// combine 2 images with SVG_FEBLEND_MODE_DARKEN
/// parameters: FilterGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement
SVGFEBlendDarken,
/// combine 2 images with SVG_FEBLEND_MODE_LIGHTEN
/// parameters: FilterGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement
SVGFEBlendLighten,
/// combine 2 images with SVG_FEBLEND_MODE_MULTIPLY
/// parameters: FilterGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement
SVGFEBlendMultiply,
/// combine 2 images with SVG_FEBLEND_MODE_NORMAL
/// parameters: FilterGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement
SVGFEBlendNormal,
/// combine 2 images with SVG_FEBLEND_MODE_SCREEN
/// parameters: FilterGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement
SVGFEBlendScreen,
/// combine 2 images with SVG_FEBLEND_MODE_OVERLAY
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendOverlay,
/// combine 2 images with SVG_FEBLEND_MODE_COLOR_DODGE
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendColorDodge,
/// combine 2 images with SVG_FEBLEND_MODE_COLOR_BURN
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendColorBurn,
/// combine 2 images with SVG_FEBLEND_MODE_HARD_LIGHT
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendHardLight,
/// combine 2 images with SVG_FEBLEND_MODE_SOFT_LIGHT
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendSoftLight,
/// combine 2 images with SVG_FEBLEND_MODE_DIFFERENCE
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendDifference,
/// combine 2 images with SVG_FEBLEND_MODE_EXCLUSION
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendExclusion,
/// combine 2 images with SVG_FEBLEND_MODE_HUE
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendHue,
/// combine 2 images with SVG_FEBLEND_MODE_SATURATION
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendSaturation,
/// combine 2 images with SVG_FEBLEND_MODE_COLOR
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendColor,
/// combine 2 images with SVG_FEBLEND_MODE_LUMINOSITY
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendLuminosity,
/// transform colors of image through 5x4 color matrix (transposed for
/// efficiency)
/// parameters: FilterGraphNode, matrix[5][4]
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feColorMatrixElement
SVGFEColorMatrix{values: [f32; 20]},
/// transform colors of image through configurable gradients with component
/// swizzle
/// parameters: FilterGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feComponentTransferElement
SVGFEComponentTransfer,
/// Processed version of SVGFEComponentTransfer with the FilterData
/// replaced by an interned handle, this is made in wrap_prim_with_filters.
/// Aside from the interned handle, creates_pixels indicates if the transfer
/// parameters will probably fill the entire subregion with non-zero alpha.
SVGFEComponentTransferInterned{handle: FilterDataHandle, creates_pixels: bool},
/// composite 2 images with chosen composite mode with parameters for that
/// mode
/// parameters: FilterGraphNode, k1, k2, k3, k4
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement
SVGFECompositeArithmetic{k1: f32, k2: f32, k3: f32, k4: f32},
/// composite 2 images with chosen composite mode with parameters for that
/// mode
/// parameters: FilterGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement
SVGFECompositeATop,
/// composite 2 images with chosen composite mode with parameters for that
/// mode
/// parameters: FilterGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement
SVGFECompositeIn,
/// composite 2 images with chosen composite mode with parameters for that
/// mode
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Docs: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feComposite
SVGFECompositeLighter,
/// composite 2 images with chosen composite mode with parameters for that
/// mode
/// parameters: FilterGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement
SVGFECompositeOut,
/// composite 2 images with chosen composite mode with parameters for that
/// mode
/// parameters: FilterGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement
SVGFECompositeOver,
/// composite 2 images with chosen composite mode with parameters for that
/// mode
/// parameters: FilterGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement
SVGFECompositeXOR,
/// transform image through convolution matrix of up to 25 values (spec
/// allows more but for performance reasons we do not)
/// parameters: FilterGraphNode, orderX, orderY, kernelValues[25], divisor,
/// bias, targetX, targetY, kernelUnitLengthX, kernelUnitLengthY,
/// preserveAlpha
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feConvolveMatrixElement
SVGFEConvolveMatrixEdgeModeDuplicate{order_x: i32, order_y: i32,
kernel: [f32; SVGFE_CONVOLVE_VALUES_LIMIT], divisor: f32, bias: f32,
target_x: i32, target_y: i32, kernel_unit_length_x: f32,
kernel_unit_length_y: f32, preserve_alpha: i32},
/// transform image through convolution matrix of up to 25 values (spec
/// allows more but for performance reasons we do not)
/// parameters: FilterGraphNode, orderX, orderY, kernelValues[25], divisor,
/// bias, targetX, targetY, kernelUnitLengthX, kernelUnitLengthY,
/// preserveAlpha
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feConvolveMatrixElement
SVGFEConvolveMatrixEdgeModeNone{order_x: i32, order_y: i32,
kernel: [f32; SVGFE_CONVOLVE_VALUES_LIMIT], divisor: f32, bias: f32,
target_x: i32, target_y: i32, kernel_unit_length_x: f32,
kernel_unit_length_y: f32, preserve_alpha: i32},
/// transform image through convolution matrix of up to 25 values (spec
/// allows more but for performance reasons we do not)
/// parameters: FilterGraphNode, orderX, orderY, kernelValues[25], divisor,
/// bias, targetX, targetY, kernelUnitLengthX, kernelUnitLengthY,
/// preserveAlpha
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feConvolveMatrixElement
SVGFEConvolveMatrixEdgeModeWrap{order_x: i32, order_y: i32,
kernel: [f32; SVGFE_CONVOLVE_VALUES_LIMIT], divisor: f32, bias: f32,
target_x: i32, target_y: i32, kernel_unit_length_x: f32,
kernel_unit_length_y: f32, preserve_alpha: i32},
/// calculate lighting based on heightmap image with provided values for a
/// distant light source with specified direction
/// parameters: FilterGraphNode, surfaceScale, diffuseConstant,
/// kernelUnitLengthX, kernelUnitLengthY, azimuth, elevation
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDiffuseLightingElement
/// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDistantLightElement
SVGFEDiffuseLightingDistant{surface_scale: f32, diffuse_constant: f32,
kernel_unit_length_x: f32, kernel_unit_length_y: f32, azimuth: f32,
elevation: f32},
/// calculate lighting based on heightmap image with provided values for a
/// point light source at specified location
/// parameters: FilterGraphNode, surfaceScale, diffuseConstant,
/// kernelUnitLengthX, kernelUnitLengthY, x, y, z
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDiffuseLightingElement
/// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEPointLightElement
SVGFEDiffuseLightingPoint{surface_scale: f32, diffuse_constant: f32,
kernel_unit_length_x: f32, kernel_unit_length_y: f32, x: f32, y: f32,
z: f32},
/// calculate lighting based on heightmap image with provided values for a
/// spot light source at specified location pointing at specified target
/// location with specified hotspot sharpness and cone angle
/// parameters: FilterGraphNode, surfaceScale, diffuseConstant,
/// kernelUnitLengthX, kernelUnitLengthY, x, y, z, pointsAtX, pointsAtY,
/// pointsAtZ, specularExponent, limitingConeAngle
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDiffuseLightingElement
/// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpotLightElement
SVGFEDiffuseLightingSpot{surface_scale: f32, diffuse_constant: f32,
kernel_unit_length_x: f32, kernel_unit_length_y: f32, x: f32, y: f32,
z: f32, points_at_x: f32, points_at_y: f32, points_at_z: f32,
cone_exponent: f32, limiting_cone_angle: f32},
/// calculate a distorted version of first input image using offset values
/// from second input image at specified intensity
/// parameters: FilterGraphNode, scale, xChannelSelector, yChannelSelector
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDisplacementMapElement
SVGFEDisplacementMap{scale: f32, x_channel_selector: u32,
y_channel_selector: u32},
/// create and merge a dropshadow version of the specified image's alpha
/// channel with specified offset and blur radius
/// parameters: FilterGraphNode, flood_color, flood_opacity, dx, dy,
/// stdDeviationX, stdDeviationY
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDropShadowElement
SVGFEDropShadow{color: ColorF, dx: f32, dy: f32, std_deviation_x: f32,
std_deviation_y: f32},
/// synthesize a new image of specified size containing a solid color
/// parameters: FilterGraphNode, color
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEFloodElement
SVGFEFlood{color: ColorF},
/// create a blurred version of the input image
/// parameters: FilterGraphNode, stdDeviationX, stdDeviationY
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEGaussianBlurElement
SVGFEGaussianBlur{std_deviation_x: f32, std_deviation_y: f32},
/// synthesize a new image based on a url (i.e. blob image source)
/// parameters: FilterGraphNode,
/// samplingFilter (see SamplingFilter in Types.h), transform
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEImageElement
SVGFEImage{sampling_filter: u32, matrix: [f32; 6]},
/// create a new image based on the input image with the contour stretched
/// outward (dilate operator)
/// parameters: FilterGraphNode, radiusX, radiusY
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEMorphologyElement
SVGFEMorphologyDilate{radius_x: f32, radius_y: f32},
/// create a new image based on the input image with the contour shrunken
/// inward (erode operator)
/// parameters: FilterGraphNode, radiusX, radiusY
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEMorphologyElement
SVGFEMorphologyErode{radius_x: f32, radius_y: f32},
/// calculate lighting based on heightmap image with provided values for a
/// distant light source with specified direction
/// parameters: FilerData, surfaceScale, specularConstant, specularExponent,
/// kernelUnitLengthX, kernelUnitLengthY, azimuth, elevation
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpecularLightingElement
/// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDistantLightElement
SVGFESpecularLightingDistant{surface_scale: f32, specular_constant: f32,
specular_exponent: f32, kernel_unit_length_x: f32,
kernel_unit_length_y: f32, azimuth: f32, elevation: f32},
/// calculate lighting based on heightmap image with provided values for a
/// point light source at specified location
/// parameters: FilterGraphNode, surfaceScale, specularConstant,
/// specularExponent, kernelUnitLengthX, kernelUnitLengthY, x, y, z
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpecularLightingElement
/// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEPointLightElement
SVGFESpecularLightingPoint{surface_scale: f32, specular_constant: f32,
specular_exponent: f32, kernel_unit_length_x: f32,
kernel_unit_length_y: f32, x: f32, y: f32, z: f32},
/// calculate lighting based on heightmap image with provided values for a
/// spot light source at specified location pointing at specified target
/// location with specified hotspot sharpness and cone angle
/// parameters: FilterGraphNode, surfaceScale, specularConstant,
/// specularExponent, kernelUnitLengthX, kernelUnitLengthY, x, y, z,
/// pointsAtX, pointsAtY, pointsAtZ, specularExponent, limitingConeAngle
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpecularLightingElement
/// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpotLightElement
SVGFESpecularLightingSpot{surface_scale: f32, specular_constant: f32,
specular_exponent: f32, kernel_unit_length_x: f32,
kernel_unit_length_y: f32, x: f32, y: f32, z: f32, points_at_x: f32,
points_at_y: f32, points_at_z: f32, cone_exponent: f32,
limiting_cone_angle: f32},
/// create a new image based on the input image, repeated throughout the
/// output rectangle
/// parameters: FilterGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETileElement
SVGFETile,
/// synthesize a new image based on Fractal Noise (Perlin) with the chosen
/// stitching mode
/// parameters: FilterGraphNode, baseFrequencyX, baseFrequencyY, numOctaves,
/// seed
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETurbulenceElement
SVGFETurbulenceWithFractalNoiseWithNoStitching{base_frequency_x: f32,
base_frequency_y: f32, num_octaves: u32, seed: u32},
/// synthesize a new image based on Fractal Noise (Perlin) with the chosen
/// stitching mode
/// parameters: FilterGraphNode, baseFrequencyX, baseFrequencyY, numOctaves,
/// seed
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETurbulenceElement
SVGFETurbulenceWithFractalNoiseWithStitching{base_frequency_x: f32,
base_frequency_y: f32, num_octaves: u32, seed: u32},
/// synthesize a new image based on Turbulence Noise (offset vectors)
/// parameters: FilterGraphNode, baseFrequencyX, baseFrequencyY, numOctaves,
/// seed
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETurbulenceElement
SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{base_frequency_x: f32,
base_frequency_y: f32, num_octaves: u32, seed: u32},
/// synthesize a new image based on Turbulence Noise (offset vectors)
/// parameters: FilterGraphNode, baseFrequencyX, baseFrequencyY, numOctaves,
/// seed
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETurbulenceElement
SVGFETurbulenceWithTurbulenceNoiseWithStitching{base_frequency_x: f32,
base_frequency_y: f32, num_octaves: u32, seed: u32},
}
impl FilterGraphOp {
pub fn kind(&self) -> &'static str {
match *self {
FilterGraphOp::SVGFEBlendColor => "SVGFEBlendColor",
FilterGraphOp::SVGFEBlendColorBurn => "SVGFEBlendColorBurn",
FilterGraphOp::SVGFEBlendColorDodge => "SVGFEBlendColorDodge",
FilterGraphOp::SVGFEBlendDarken => "SVGFEBlendDarken",
FilterGraphOp::SVGFEBlendDifference => "SVGFEBlendDifference",
FilterGraphOp::SVGFEBlendExclusion => "SVGFEBlendExclusion",
FilterGraphOp::SVGFEBlendHardLight => "SVGFEBlendHardLight",
FilterGraphOp::SVGFEBlendHue => "SVGFEBlendHue",
FilterGraphOp::SVGFEBlendLighten => "SVGFEBlendLighten",
FilterGraphOp::SVGFEBlendLuminosity => "SVGFEBlendLuminosity",
FilterGraphOp::SVGFEBlendMultiply => "SVGFEBlendMultiply",
FilterGraphOp::SVGFEBlendNormal => "SVGFEBlendNormal",
FilterGraphOp::SVGFEBlendOverlay => "SVGFEBlendOverlay",
FilterGraphOp::SVGFEBlendSaturation => "SVGFEBlendSaturation",
FilterGraphOp::SVGFEBlendScreen => "SVGFEBlendScreen",
FilterGraphOp::SVGFEBlendSoftLight => "SVGFEBlendSoftLight",
FilterGraphOp::SVGFEColorMatrix{..} => "SVGFEColorMatrix",
FilterGraphOp::SVGFEComponentTransfer => "SVGFEComponentTransfer",
FilterGraphOp::SVGFEComponentTransferInterned{..} => "SVGFEComponentTransferInterned",
FilterGraphOp::SVGFECompositeArithmetic{..} => "SVGFECompositeArithmetic",
FilterGraphOp::SVGFECompositeATop => "SVGFECompositeATop",
FilterGraphOp::SVGFECompositeIn => "SVGFECompositeIn",
FilterGraphOp::SVGFECompositeLighter => "SVGFECompositeLighter",
FilterGraphOp::SVGFECompositeOut => "SVGFECompositeOut",
FilterGraphOp::SVGFECompositeOver => "SVGFECompositeOver",
FilterGraphOp::SVGFECompositeXOR => "SVGFECompositeXOR",
FilterGraphOp::SVGFEConvolveMatrixEdgeModeDuplicate{..} => "SVGFEConvolveMatrixEdgeModeDuplicate",
FilterGraphOp::SVGFEConvolveMatrixEdgeModeNone{..} => "SVGFEConvolveMatrixEdgeModeNone",
FilterGraphOp::SVGFEConvolveMatrixEdgeModeWrap{..} => "SVGFEConvolveMatrixEdgeModeWrap",
FilterGraphOp::SVGFEDiffuseLightingDistant{..} => "SVGFEDiffuseLightingDistant",
FilterGraphOp::SVGFEDiffuseLightingPoint{..} => "SVGFEDiffuseLightingPoint",
FilterGraphOp::SVGFEDiffuseLightingSpot{..} => "SVGFEDiffuseLightingSpot",
FilterGraphOp::SVGFEDisplacementMap{..} => "SVGFEDisplacementMap",
FilterGraphOp::SVGFEDropShadow{..} => "SVGFEDropShadow",
FilterGraphOp::SVGFEFlood{..} => "SVGFEFlood",
FilterGraphOp::SVGFEGaussianBlur{..} => "SVGFEGaussianBlur",
FilterGraphOp::SVGFEIdentity => "SVGFEIdentity",
FilterGraphOp::SVGFEImage{..} => "SVGFEImage",
FilterGraphOp::SVGFEMorphologyDilate{..} => "SVGFEMorphologyDilate",
FilterGraphOp::SVGFEMorphologyErode{..} => "SVGFEMorphologyErode",
FilterGraphOp::SVGFEOpacity{..} => "SVGFEOpacity",
FilterGraphOp::SVGFESourceAlpha => "SVGFESourceAlpha",
FilterGraphOp::SVGFESourceGraphic => "SVGFESourceGraphic",
FilterGraphOp::SVGFESpecularLightingDistant{..} => "SVGFESpecularLightingDistant",
FilterGraphOp::SVGFESpecularLightingPoint{..} => "SVGFESpecularLightingPoint",
FilterGraphOp::SVGFESpecularLightingSpot{..} => "SVGFESpecularLightingSpot",
FilterGraphOp::SVGFETile => "SVGFETile",
FilterGraphOp::SVGFEToAlpha => "SVGFEToAlpha",
FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{..} => "SVGFETurbulenceWithFractalNoiseWithNoStitching",
FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithStitching{..} => "SVGFETurbulenceWithFractalNoiseWithStitching",
FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{..} => "SVGFETurbulenceWithTurbulenceNoiseWithNoStitching",
FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching{..} => "SVGFETurbulenceWithTurbulenceNoiseWithStitching",
}
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct FilterGraphNode {
/// Indicates this graph node was marked as necessary by the DAG optimizer
pub kept_by_optimizer: bool,
/// true if color_interpolation_filter == LinearRgb; shader will convert
/// sRGB texture pixel colors on load and convert back on store, for correct
/// interpolation
pub linear: bool,
/// padding for output rect if we need a border to get correct clamping, or
/// to account for larger final subregion than source rect (see bug 1869672)
pub inflate: i16,
/// virtualized picture input bindings, these refer to other filter outputs
/// by number within the graph, usually there is one element
pub inputs: Vec<FilterGraphPictureReference>,
/// clipping rect for filter node output
pub subregion: LayoutRect,
}
impl From<FilterOpGraphNode> for FilterGraphNode {
fn from(node: FilterOpGraphNode) -> Self {
let mut inputs: Vec<FilterGraphPictureReference> = Vec::new();
if node.input.buffer_id != FilterOpGraphPictureBufferId::None {
inputs.push(node.input.into());
}
if node.input2.buffer_id != FilterOpGraphPictureBufferId::None {
inputs.push(node.input2.into());
}
// If the op used by this node is a feMerge, it will add more inputs
// after this invocation.
FilterGraphNode{
linear: node.linear,
inputs,
subregion: node.subregion,
// These are computed later in scene_building
kept_by_optimizer: true,
inflate: 0,
}
}
}
/// Equivalent to api::FilterOp with added internal information
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum Filter {
@ -233,6 +783,7 @@ pub enum Filter {
LinearToSrgb,
ComponentTransfer,
Flood(ColorF),
SVGGraphNode(FilterGraphNode, FilterGraphOp),
}
impl Filter {
@ -258,6 +809,7 @@ impl Filter {
Filter::Flood(color) => {
color.a > OPACITY_EPSILON
}
Filter::SVGGraphNode(..) => true,
}
}
@ -296,6 +848,7 @@ impl Filter {
Filter::LinearToSrgb |
Filter::ComponentTransfer |
Filter::Flood(..) => false,
Filter::SVGGraphNode(..) => false,
}
}
@ -319,6 +872,7 @@ impl Filter {
Filter::Blur { .. } => 12,
Filter::DropShadows(..) => 13,
Filter::Opacity(..) => 14,
Filter::SVGGraphNode(..) => unreachable!("SVGGraphNode handled elsewhere"),
}
}
}
@ -342,6 +896,76 @@ impl From<FilterOp> for Filter {
FilterOp::ComponentTransfer => Filter::ComponentTransfer,
FilterOp::DropShadow(shadow) => Filter::DropShadows(smallvec![shadow]),
FilterOp::Flood(color) => Filter::Flood(color),
FilterOp::SVGFEBlendColor{node} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEBlendColor),
FilterOp::SVGFEBlendColorBurn{node} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEBlendColorBurn),
FilterOp::SVGFEBlendColorDodge{node} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEBlendColorDodge),
FilterOp::SVGFEBlendDarken{node} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEBlendDarken),
FilterOp::SVGFEBlendDifference{node} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEBlendDifference),
FilterOp::SVGFEBlendExclusion{node} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEBlendExclusion),
FilterOp::SVGFEBlendHardLight{node} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEBlendHardLight),
FilterOp::SVGFEBlendHue{node} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEBlendHue),
FilterOp::SVGFEBlendLighten{node} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEBlendLighten),
FilterOp::SVGFEBlendLuminosity{node} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEBlendLuminosity),
FilterOp::SVGFEBlendMultiply{node} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEBlendMultiply),
FilterOp::SVGFEBlendNormal{node} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEBlendNormal),
FilterOp::SVGFEBlendOverlay{node} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEBlendOverlay),
FilterOp::SVGFEBlendSaturation{node} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEBlendSaturation),
FilterOp::SVGFEBlendScreen{node} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEBlendScreen),
FilterOp::SVGFEBlendSoftLight{node} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEBlendSoftLight),
FilterOp::SVGFEColorMatrix{node, values} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEColorMatrix{values}),
FilterOp::SVGFEComponentTransfer{node} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEComponentTransfer),
FilterOp::SVGFECompositeArithmetic{node, k1, k2, k3, k4} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFECompositeArithmetic{k1, k2, k3, k4}),
FilterOp::SVGFECompositeATop{node} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFECompositeATop),
FilterOp::SVGFECompositeIn{node} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFECompositeIn),
FilterOp::SVGFECompositeLighter{node} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFECompositeLighter),
FilterOp::SVGFECompositeOut{node} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFECompositeOut),
FilterOp::SVGFECompositeOver{node} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFECompositeOver),
FilterOp::SVGFECompositeXOR{node} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFECompositeXOR),
FilterOp::SVGFEConvolveMatrixEdgeModeDuplicate{node, order_x, order_y, kernel, divisor, bias, target_x, target_y, kernel_unit_length_x, kernel_unit_length_y, preserve_alpha} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEConvolveMatrixEdgeModeDuplicate{order_x, order_y, kernel, divisor, bias, target_x, target_y, kernel_unit_length_x, kernel_unit_length_y, preserve_alpha}),
FilterOp::SVGFEConvolveMatrixEdgeModeNone{node, order_x, order_y, kernel, divisor, bias, target_x, target_y, kernel_unit_length_x, kernel_unit_length_y, preserve_alpha} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEConvolveMatrixEdgeModeNone{order_x, order_y, kernel, divisor, bias, target_x, target_y, kernel_unit_length_x, kernel_unit_length_y, preserve_alpha}),
FilterOp::SVGFEConvolveMatrixEdgeModeWrap{node, order_x, order_y, kernel, divisor, bias, target_x, target_y, kernel_unit_length_x, kernel_unit_length_y, preserve_alpha} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEConvolveMatrixEdgeModeWrap{order_x, order_y, kernel, divisor, bias, target_x, target_y, kernel_unit_length_x, kernel_unit_length_y, preserve_alpha}),
FilterOp::SVGFEDiffuseLightingDistant{node, surface_scale, diffuse_constant, kernel_unit_length_x, kernel_unit_length_y, azimuth, elevation} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEDiffuseLightingDistant{surface_scale, diffuse_constant, kernel_unit_length_x, kernel_unit_length_y, azimuth, elevation}),
FilterOp::SVGFEDiffuseLightingPoint{node, surface_scale, diffuse_constant, kernel_unit_length_x, kernel_unit_length_y, x, y, z} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEDiffuseLightingPoint{surface_scale, diffuse_constant, kernel_unit_length_x, kernel_unit_length_y, x, y, z}),
FilterOp::SVGFEDiffuseLightingSpot{node, surface_scale, diffuse_constant, kernel_unit_length_x, kernel_unit_length_y, x, y, z, points_at_x, points_at_y, points_at_z, cone_exponent, limiting_cone_angle} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEDiffuseLightingSpot{surface_scale, diffuse_constant, kernel_unit_length_x, kernel_unit_length_y, x, y, z, points_at_x, points_at_y, points_at_z, cone_exponent, limiting_cone_angle}),
FilterOp::SVGFEDisplacementMap{node, scale, x_channel_selector, y_channel_selector} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEDisplacementMap{scale, x_channel_selector, y_channel_selector}),
FilterOp::SVGFEDropShadow{node, color, dx, dy, std_deviation_x, std_deviation_y} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEDropShadow{color, dx, dy, std_deviation_x, std_deviation_y}),
FilterOp::SVGFEFlood{node, color} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEFlood{color}),
FilterOp::SVGFEGaussianBlur{node, std_deviation_x, std_deviation_y} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEGaussianBlur{std_deviation_x, std_deviation_y}),
FilterOp::SVGFEIdentity{node} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEIdentity),
FilterOp::SVGFEImage{node, sampling_filter, matrix} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEImage{sampling_filter, matrix}),
FilterOp::SVGFEMorphologyDilate{node, radius_x, radius_y} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEMorphologyDilate{radius_x, radius_y}),
FilterOp::SVGFEMorphologyErode{node, radius_x, radius_y} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEMorphologyErode{radius_x, radius_y}),
FilterOp::SVGFEOffset{node, offset_x, offset_y} => {
Filter::SVGGraphNode(
FilterGraphNode {
kept_by_optimizer: true, // computed later in scene_building
linear: node.linear,
inflate: 0, // computed later in scene_building
inputs: [FilterGraphPictureReference {
buffer_id: node.input.buffer_id,
offset: LayoutVector2D::new(offset_x, offset_y),
subregion: LayoutRect::zero(),
inflate: 0,
source_padding: LayoutRect::zero(),
target_padding: LayoutRect::zero(),
}].to_vec(),
subregion: node.subregion,
},
FilterGraphOp::SVGFEIdentity,
)
},
FilterOp::SVGFEOpacity{node, valuebinding, value} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEOpacity{valuebinding, value}),
FilterOp::SVGFESourceAlpha{node} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFESourceAlpha),
FilterOp::SVGFESourceGraphic{node} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFESourceGraphic),
FilterOp::SVGFESpecularLightingDistant{node, surface_scale, specular_constant, specular_exponent, kernel_unit_length_x, kernel_unit_length_y, azimuth, elevation} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFESpecularLightingDistant{surface_scale, specular_constant, specular_exponent, kernel_unit_length_x, kernel_unit_length_y, azimuth, elevation}),
FilterOp::SVGFESpecularLightingPoint{node, surface_scale, specular_constant, specular_exponent, kernel_unit_length_x, kernel_unit_length_y, x, y, z} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFESpecularLightingPoint{surface_scale, specular_constant, specular_exponent, kernel_unit_length_x, kernel_unit_length_y, x, y, z}),
FilterOp::SVGFESpecularLightingSpot{node, surface_scale, specular_constant, specular_exponent, kernel_unit_length_x, kernel_unit_length_y, x, y, z, points_at_x, points_at_y, points_at_z, cone_exponent, limiting_cone_angle} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFESpecularLightingSpot{surface_scale, specular_constant, specular_exponent, kernel_unit_length_x, kernel_unit_length_y, x, y, z, points_at_x, points_at_y, points_at_z, cone_exponent, limiting_cone_angle}),
FilterOp::SVGFETile{node} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFETile),
FilterOp::SVGFEToAlpha{node} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFEToAlpha),
FilterOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{node, base_frequency_x, base_frequency_y, num_octaves, seed} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{base_frequency_x, base_frequency_y, num_octaves, seed}),
FilterOp::SVGFETurbulenceWithFractalNoiseWithStitching{node, base_frequency_x, base_frequency_y, num_octaves, seed} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithStitching{base_frequency_x, base_frequency_y, num_octaves, seed}),
FilterOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{node, base_frequency_x, base_frequency_y, num_octaves, seed} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{base_frequency_x, base_frequency_y, num_octaves, seed}),
FilterOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching{node, base_frequency_x, base_frequency_y, num_octaves, seed} => Filter::SVGGraphNode(node.into(), FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching{base_frequency_x, base_frequency_y, num_octaves, seed}),
}
}
}

Просмотреть файл

@ -95,7 +95,7 @@
//! improved as a follow up).
use api::{MixBlendMode, PremultipliedColorF, FilterPrimitiveKind};
use api::{PropertyBinding, PropertyBindingId, FilterPrimitive, RasterSpace};
use api::{PropertyBinding, PropertyBindingId, FilterPrimitive, FilterOpGraphPictureBufferId, RasterSpace};
use api::{DebugFlags, ImageKey, ColorF, ColorU, PrimitiveFlags};
use api::{ImageRendering, ColorDepth, YuvRangedColorSpace, YuvFormat, AlphaType};
use api::units::*;
@ -111,7 +111,7 @@ use euclid::{vec3, Point2D, Scale, Vector2D, Box2D};
use euclid::approxeq::ApproxEq;
use crate::filterdata::SFilterData;
use crate::intern::ItemUid;
use crate::internal_types::{FastHashMap, FastHashSet, PlaneSplitter, Filter, FrameId};
use crate::internal_types::{FastHashMap, FastHashSet, PlaneSplitter, FilterGraphOp, FilterGraphNode, Filter, FrameId};
use crate::internal_types::{PlaneSplitterIndex, PlaneSplitAnchor, TextureSource};
use crate::frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState, PictureContext};
use crate::gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
@ -281,9 +281,10 @@ pub const TILE_SIZE_SCROLLBAR_VERTICAL: DeviceIntSize = DeviceIntSize {
_unit: marker::PhantomData,
};
/// The maximum size per axis of a surface,
/// in WorldPixel coordinates.
const MAX_SURFACE_SIZE: usize = 4096;
/// The maximum size per axis of a surface, in DevicePixel coordinates.
/// Render tasks larger than this size are scaled down to fit, which may cause
/// some blurriness.
pub const MAX_SURFACE_SIZE: usize = 4096;
/// Maximum size of a compositor surface.
const MAX_COMPOSITOR_SURFACES_SIZE: f32 = 8192.0;
@ -3821,7 +3822,7 @@ pub struct SurfaceIndex(pub usize);
/// frames and display lists.
pub struct SurfaceInfo {
/// A local rect defining the size of this surface, in the
/// coordinate system of the surface itself. This contains
/// coordinate system of the parent surface. This contains
/// the unclipped bounding rect of child primitives.
pub unclipped_local_rect: PictureRect,
/// The local space coverage of child primitives after they are
@ -4048,6 +4049,8 @@ pub enum PictureCompositeMode {
},
/// Apply an SVG filter
SvgFilter(Vec<FilterPrimitive>, Vec<SFilterData>),
/// Apply an SVG filter graph
SVGFEGraph(Vec<(FilterGraphNode, FilterGraphOp)>),
/// A surface that is used as an input to another primitive
IntermediateSurface,
}
@ -4137,6 +4140,9 @@ impl PictureCompositeMode {
}
result_rect
}
PictureCompositeMode::SVGFEGraph(ref filters) => {
self.get_coverage_svgfe(filters, surface_rect.cast_unit(), true, false).0
}
_ => {
surface_rect
}
@ -4232,11 +4238,338 @@ impl PictureCompositeMode {
}
result_rect
}
PictureCompositeMode::SVGFEGraph(ref filters) => {
let mut rect = self.get_coverage_svgfe(filters, surface_rect.cast_unit(), true, true).0;
// Inflate a bit for invalidation purposes, but we don't do this in get_surface_rects or get_surface_rect.'
if !rect.is_empty() {
rect = rect.inflate(1.0, 1.0);
}
rect
}
_ => {
surface_rect
}
}
}
/// Returns a static str describing the type of PictureCompositeMode (and
/// filter type if applicable)
pub fn kind(&self) -> &'static str {
match *self {
PictureCompositeMode::Blit(..) => "Blit",
PictureCompositeMode::ComponentTransferFilter(..) => "ComponentTransferFilter",
PictureCompositeMode::IntermediateSurface => "IntermediateSurface",
PictureCompositeMode::MixBlend(..) => "MixBlend",
PictureCompositeMode::SVGFEGraph(..) => "SVGFEGraph",
PictureCompositeMode::SvgFilter(..) => "SvgFilter",
PictureCompositeMode::TileCache{..} => "TileCache",
PictureCompositeMode::Filter(Filter::Blur{..}) => "Filter::Blur",
PictureCompositeMode::Filter(Filter::Brightness(..)) => "Filter::Brightness",
PictureCompositeMode::Filter(Filter::ColorMatrix(..)) => "Filter::ColorMatrix",
PictureCompositeMode::Filter(Filter::ComponentTransfer) => "Filter::ComponentTransfer",
PictureCompositeMode::Filter(Filter::Contrast(..)) => "Filter::Contrast",
PictureCompositeMode::Filter(Filter::DropShadows(..)) => "Filter::DropShadows",
PictureCompositeMode::Filter(Filter::Flood(..)) => "Filter::Flood",
PictureCompositeMode::Filter(Filter::Grayscale(..)) => "Filter::Grayscale",
PictureCompositeMode::Filter(Filter::HueRotate(..)) => "Filter::HueRotate",
PictureCompositeMode::Filter(Filter::Identity) => "Filter::Identity",
PictureCompositeMode::Filter(Filter::Invert(..)) => "Filter::Invert",
PictureCompositeMode::Filter(Filter::LinearToSrgb) => "Filter::LinearToSrgb",
PictureCompositeMode::Filter(Filter::Opacity(..)) => "Filter::Opacity",
PictureCompositeMode::Filter(Filter::Saturate(..)) => "Filter::Saturate",
PictureCompositeMode::Filter(Filter::Sepia(..)) => "Filter::Sepia",
PictureCompositeMode::Filter(Filter::SrgbToLinear) => "Filter::SrgbToLinear",
PictureCompositeMode::Filter(Filter::SVGGraphNode(..)) => "Filter::SVGGraphNode",
}
}
/// Here we compute the source and target rects for SVGFEGraph by walking
/// the whole graph and propagating subregions based on the provided
/// invalidation rect (in either source or target space), and we want it to
/// be a tight fit so we don't waste time applying multiple filters to
/// pixels that do not contribute to the invalidated rect.
///
/// The interesting parts of the handling of SVG filters are:
/// * scene_building.rs : wrap_prim_with_filters
/// * picture.rs : get_coverage_svgfe (you are here)
/// * render_task.rs : new_svg_filter_graph
/// * render_target.rs : add_svg_filter_node_instances
pub fn get_coverage_svgfe(
&self,
filters: &[(FilterGraphNode, FilterGraphOp)],
surface_rect: LayoutRect,
surface_rect_is_source: bool,
skip_subregion_clips: bool,
) -> (LayoutRect, LayoutRect, LayoutRect) {
// The value of BUFFER_LIMIT here must be the same as in
// scene_building.rs, or we'll hit asserts here.
const BUFFER_LIMIT: usize = 256;
fn calc_target_from_source(
source_rect: LayoutRect,
filters: &[(FilterGraphNode, FilterGraphOp)],
skip_subregion_clips: bool,
) -> LayoutRect {
// We need to evaluate the subregions based on the proposed
// SourceGraphic rect as it isn't known at scene build time.
let mut subregion_by_buffer_id: [LayoutRect; BUFFER_LIMIT] = [LayoutRect::zero(); BUFFER_LIMIT];
for (id, (node, op)) in filters.iter().enumerate() {
let full_subregion = node.subregion;
let mut used_subregion = LayoutRect::zero();
for input in &node.inputs {
match input.buffer_id {
FilterOpGraphPictureBufferId::BufferId(id) => {
assert!((id as usize) < BUFFER_LIMIT, "BUFFER_LIMIT must be the same in frame building and scene building");
// This id lookup should always succeed.
let input_subregion = subregion_by_buffer_id[id as usize];
// Now add the padding that transforms from
// source to target, this was determined during
// scene build based on the operation.
let input_subregion =
LayoutRect::new(
LayoutPoint::new(
input_subregion.min.x + input.target_padding.min.x,
input_subregion.min.y + input.target_padding.min.y,
),
LayoutPoint::new(
input_subregion.max.x + input.target_padding.max.x,
input_subregion.max.y + input.target_padding.max.y,
),
);
used_subregion = used_subregion
.union(&input_subregion);
}
FilterOpGraphPictureBufferId::None => {
panic!("Unsupported BufferId type");
}
}
}
// We can clip the used subregion.
if !skip_subregion_clips {
used_subregion = used_subregion
.intersection(&full_subregion)
.unwrap_or(LayoutRect::zero());
}
match op {
FilterGraphOp::SVGFEBlendColor => {}
FilterGraphOp::SVGFEBlendColorBurn => {}
FilterGraphOp::SVGFEBlendColorDodge => {}
FilterGraphOp::SVGFEBlendDarken => {}
FilterGraphOp::SVGFEBlendDifference => {}
FilterGraphOp::SVGFEBlendExclusion => {}
FilterGraphOp::SVGFEBlendHardLight => {}
FilterGraphOp::SVGFEBlendHue => {}
FilterGraphOp::SVGFEBlendLighten => {}
FilterGraphOp::SVGFEBlendLuminosity => {}
FilterGraphOp::SVGFEBlendMultiply => {}
FilterGraphOp::SVGFEBlendNormal => {}
FilterGraphOp::SVGFEBlendOverlay => {}
FilterGraphOp::SVGFEBlendSaturation => {}
FilterGraphOp::SVGFEBlendScreen => {}
FilterGraphOp::SVGFEBlendSoftLight => {}
FilterGraphOp::SVGFEColorMatrix { values } => {
if values[3] != 0.0 ||
values[7] != 0.0 ||
values[11] != 0.0 ||
values[19] != 0.0 {
// Manipulating alpha can easily create new
// pixels outside of input subregions
used_subregion = full_subregion;
}
}
FilterGraphOp::SVGFEComponentTransfer => unreachable!(),
FilterGraphOp::SVGFEComponentTransferInterned{handle: _, creates_pixels} => {
// Check if the value of alpha[0] is modified, if so
// the whole subregion is used because it will be
// creating new pixels outside of input subregions
if *creates_pixels {
used_subregion = full_subregion;
}
}
FilterGraphOp::SVGFECompositeArithmetic { k1, k2, k3, k4 } => {
// Optimization opportunity - some inputs may be
// smaller subregions due to the way the math works,
// k1 is the intersection of the two inputs, k2 is
// the first input only, k3 is the second input
// only, and k4 changes the whole subregion.
//
// See logic for SVG_FECOMPOSITE_OPERATOR_ARITHMETIC
// in FilterSupport.cpp
//
// We can at least ignore the entire node if
// everything is zero.
if *k1 <= 0.0 &&
*k2 <= 0.0 &&
*k3 <= 0.0 {
used_subregion = LayoutRect::zero();
}
// Check if alpha is added to pixels as it means it
// can fill pixels outside input subregions
if *k4 > 0.0 {
used_subregion = full_subregion;
}
}
FilterGraphOp::SVGFECompositeATop => {}
FilterGraphOp::SVGFECompositeIn => {}
FilterGraphOp::SVGFECompositeLighter => {}
FilterGraphOp::SVGFECompositeOut => {}
FilterGraphOp::SVGFECompositeOver => {}
FilterGraphOp::SVGFECompositeXOR => {}
FilterGraphOp::SVGFEConvolveMatrixEdgeModeDuplicate{..} => {}
FilterGraphOp::SVGFEConvolveMatrixEdgeModeNone{..} => {}
FilterGraphOp::SVGFEConvolveMatrixEdgeModeWrap{..} => {}
FilterGraphOp::SVGFEDiffuseLightingDistant{..} => {}
FilterGraphOp::SVGFEDiffuseLightingPoint{..} => {}
FilterGraphOp::SVGFEDiffuseLightingSpot{..} => {}
FilterGraphOp::SVGFEDisplacementMap{..} => {}
FilterGraphOp::SVGFEDropShadow{..} => {}
FilterGraphOp::SVGFEFlood { color } => {
// Subregion needs to be set to the full node
// subregion for fills (unless the fill is a no-op)
if color.a > 0.0 {
used_subregion = full_subregion;
}
}
FilterGraphOp::SVGFEGaussianBlur{..} => {}
FilterGraphOp::SVGFEIdentity => {}
FilterGraphOp::SVGFEImage { sampling_filter: _sampling_filter, matrix: _matrix } => {
// TODO: calculate the actual subregion
used_subregion = full_subregion;
}
FilterGraphOp::SVGFEMorphologyDilate{..} => {}
FilterGraphOp::SVGFEMorphologyErode{..} => {}
FilterGraphOp::SVGFEOpacity { valuebinding: _valuebinding, value } => {
// If fully transparent, we can ignore this node
if *value <= 0.0 {
used_subregion = LayoutRect::zero();
}
}
FilterGraphOp::SVGFESourceAlpha |
FilterGraphOp::SVGFESourceGraphic => {
used_subregion = source_rect;
}
FilterGraphOp::SVGFESpecularLightingDistant{..} => {}
FilterGraphOp::SVGFESpecularLightingPoint{..} => {}
FilterGraphOp::SVGFESpecularLightingSpot{..} => {}
FilterGraphOp::SVGFETile => {
// feTile fills the entire output with
// source pixels, so it's effectively a flood.
used_subregion = full_subregion;
}
FilterGraphOp::SVGFEToAlpha => {}
FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{..} |
FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithStitching{..} |
FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{..} |
FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching{..} => {
// Turbulence produces pixel values throughout the
// node subregion.
used_subregion = full_subregion;
}
}
// Store the subregion so later nodes can refer back
// to this and propagate rects properly
assert!((id as usize) < BUFFER_LIMIT, "BUFFER_LIMIT must be the same in frame building and scene building");
subregion_by_buffer_id[id] = used_subregion;
}
subregion_by_buffer_id[filters.len() - 1]
}
fn calc_source_from_target(
target_rect: LayoutRect,
filters: &[(FilterGraphNode, FilterGraphOp)],
skip_subregion_clips: bool,
) -> LayoutRect {
// We're solving the source rect from target rect (e.g. due
// to invalidation of a region, we need to know how much of
// SourceGraphic is needed to draw that region accurately),
// so we need to walk the DAG in reverse and accumulate the source
// subregion for each input onto the referenced node, which can then
// propagate that to its inputs when it is iterated.
let mut source_subregion = LayoutRect::zero();
let mut subregion_by_buffer_id: [LayoutRect; BUFFER_LIMIT] =
[LayoutRect::zero(); BUFFER_LIMIT];
let final_buffer_id = filters.len() - 1;
assert!(final_buffer_id < BUFFER_LIMIT, "BUFFER_LIMIT must be the same in frame building and scene building");
subregion_by_buffer_id[final_buffer_id] = target_rect;
for (node_buffer_id, (node, op)) in filters.iter().enumerate().rev() {
// This is the subregion this node outputs, we can clip
// the inputs based on source_padding relative to this,
// and accumulate a new subregion for them.
assert!(node_buffer_id < BUFFER_LIMIT, "BUFFER_LIMIT must be the same in frame building and scene building");
let full_subregion = node.subregion;
let mut used_subregion =
subregion_by_buffer_id[node_buffer_id];
// We can clip the used subregion.
if !skip_subregion_clips {
used_subregion = used_subregion
.intersection(&full_subregion)
.unwrap_or(LayoutRect::zero());
}
if !used_subregion.is_empty() {
for input in &node.inputs {
let input_subregion = LayoutRect::new(
LayoutPoint::new(
used_subregion.min.x + input.source_padding.min.x,
used_subregion.min.y + input.source_padding.min.y,
),
LayoutPoint::new(
used_subregion.max.x + input.source_padding.max.x,
used_subregion.max.y + input.source_padding.max.y,
),
);
match input.buffer_id {
FilterOpGraphPictureBufferId::BufferId(id) => {
// Add the used area to the input, later when
// the referneced node is iterated as a node it
// will propagate the used bounds.
subregion_by_buffer_id[id as usize] =
subregion_by_buffer_id[id as usize]
.union(&input_subregion);
}
FilterOpGraphPictureBufferId::None => {}
}
}
}
// If this is the SourceGraphic, we now have the subregion.
match op {
FilterGraphOp::SVGFESourceAlpha |
FilterGraphOp::SVGFESourceGraphic => {
source_subregion = used_subregion;
}
_ => {}
}
}
// Note that this can be zero if SourceGraphic is not in the graph.
source_subregion
}
let (source, target) = match surface_rect_is_source {
true => {
// If we have a surface_rect for SourceGraphic, transform
// it to a target rect, and then transform the target
// rect back to a source rect (because blurs need the
// source to be enlarged).
let target = calc_target_from_source(surface_rect, filters, skip_subregion_clips);
let source = calc_source_from_target(target, filters, skip_subregion_clips);
(source, target)
}
false => {
// If we have a surface_rect for invalidation of target,
// we want to calculate the source rect from it
let target = surface_rect;
let source = calc_source_from_target(target, filters, skip_subregion_clips);
(source, target)
}
};
// Combine the source and target rect because other code assumes just
// a single rect expanded for blurs
let combined = source.union(&target);
(combined, source, target)
}
}
/// Enum value describing the place of a picture in a 3D context.
@ -4500,6 +4833,7 @@ pub struct PicturePrimitive {
/// it will be considered invisible.
pub is_backface_visible: bool,
/// All render tasks have 0-2 input tasks.
pub primary_render_task_id: Option<RenderTaskId>,
/// If a mix-blend-mode, contains the render task for
/// the readback of the framebuffer that we use to sample
@ -4507,6 +4841,8 @@ pub struct PicturePrimitive {
/// For drop-shadow filter, this will store the original
/// picture task which would be rendered on screen after
/// blur pass.
/// This is also used by SVGFEBlend, SVGFEComposite and
/// SVGFEDisplacementMap filters.
pub secondary_render_task_id: Option<RenderTaskId>,
/// How this picture should be composited.
/// If None, don't composite - just draw directly on parent surface.
@ -4646,6 +4982,7 @@ impl PicturePrimitive {
parent_subpixel_mode: SubpixelMode,
frame_state: &mut FrameBuildingState,
frame_context: &FrameBuildingContext,
data_stores: &mut DataStores,
scratch: &mut PrimitiveScratchBuffer,
tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>,
) -> Option<(PictureContext, PictureState, PrimitiveList)> {
@ -5740,6 +6077,47 @@ impl PicturePrimitive {
primary_render_task_id = filter_task_id;
surface_descriptor = SurfaceDescriptor::new_chained(
picture_task_id,
filter_task_id,
surface_rects.clipped_local,
);
}
PictureCompositeMode::SVGFEGraph(ref filters) => {
let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
let picture_task_id = frame_state.rg_builder.add().init(
RenderTask::new_dynamic(
surface_rects.task_size,
RenderTaskKind::new_picture(
surface_rects.task_size,
surface_rects.needs_scissor_rect,
surface_rects.clipped.min,
surface_spatial_node_index,
raster_spatial_node_index,
device_pixel_scale,
None,
None,
None,
cmd_buffer_index,
can_use_shared_surface,
)
).with_uv_rect_kind(surface_rects.uv_rect_kind)
);
let filter_task_id = RenderTask::new_svg_filter_graph(
filters,
frame_state,
data_stores,
surface_rects.uv_rect_kind,
picture_task_id,
surface_rects.task_size,
surface_rects.clipped,
surface_rects.clipped_local,
);
primary_render_task_id = filter_task_id;
surface_descriptor = SurfaceDescriptor::new_chained(
picture_task_id,
filter_task_id,
@ -5792,7 +6170,8 @@ impl PicturePrimitive {
PictureCompositeMode::Filter(..) |
PictureCompositeMode::MixBlend(..) |
PictureCompositeMode::IntermediateSurface |
PictureCompositeMode::SvgFilter(..) => {
PictureCompositeMode::SvgFilter(..) |
PictureCompositeMode::SVGFEGraph(..) => {
// TODO(gw): We can take advantage of the same logic that
// exists in the opaque rect detection for tile
// caches, to allow subpixel text on other surfaces
@ -6425,6 +6804,18 @@ impl PicturePrimitive {
PictureCompositeMode::Blit(_) |
PictureCompositeMode::IntermediateSurface |
PictureCompositeMode::SvgFilter(..) => {}
PictureCompositeMode::SVGFEGraph(ref filters) => {
// Update interned filter data
for (_node, op) in filters {
match op {
FilterGraphOp::SVGFEComponentTransferInterned { handle, creates_pixels: _ } => {
let filter_data = &mut data_stores.filter_data[*handle];
filter_data.update(frame_state);
}
_ => {}
}
}
}
}
true
@ -7109,6 +7500,38 @@ fn get_surface_rects(
let surface = &mut surfaces[surface_index.0];
let (clipped_local, unclipped_local) = match composite_mode {
PictureCompositeMode::SVGFEGraph(ref filters) => {
// We need to get the primitive rect, and get_coverage for
// SVGFEGraph requires the provided rect is in user space (defined
// in SVG spec) for subregion calculations to work properly
let clipped: LayoutRect = surface.clipped_local_rect
.cast_unit();
let unclipped: LayoutRect = surface.unclipped_local_rect
.cast_unit();
// Get the rects of SourceGraphic and target based on the local rect
// and clip rect.
let (coverage, _source, target) = composite_mode.get_coverage_svgfe(
filters, clipped, true, false);
// If no part of the source rect contributes to target pixels, we're
// done here; this is the hot path for quick culling of composited
// pictures, where the view doesn't overlap the target.
//
// Note that the filter may contain fill regions such as feFlood
// which do not depend on the source at all, so the source rect is
// largely irrelevant to our decision here as it may be empty.
if target.is_empty() {
return None;
}
// Since the design of WebRender PictureCompositeMode does not
// actually permit source and target rects as separate concepts, we
// have to use the combined coverage rect.
let clipped = coverage;
(clipped.cast_unit(), unclipped)
}
PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
let local_prim_rect = surface.clipped_local_rect;

Просмотреть файл

@ -174,6 +174,7 @@ fn prepare_prim_for_render(
pic_context.subpixel_mode,
frame_state,
frame_context,
data_stores,
scratch,
tile_caches,
) {
@ -961,7 +962,8 @@ fn prepare_interned_prim_for_render(
// may have changed due to downscaling. We could handle this separate
// case as a follow up.
Some(PictureCompositeMode::Filter(Filter::Blur { .. })) |
Some(PictureCompositeMode::Filter(Filter::DropShadows { .. })) => {
Some(PictureCompositeMode::Filter(Filter::DropShadows { .. })) |
Some(PictureCompositeMode::SVGFEGraph( .. )) => {
true
}
_ => {

Просмотреть файл

@ -3,15 +3,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{
ColorU, MixBlendMode, FilterPrimitiveInput, FilterPrimitiveKind, ColorSpace,
PropertyBinding, PropertyBindingId, CompositeOperator, RasterSpace,
ColorU, MixBlendMode, FilterPrimitiveInput, FilterPrimitiveKind,
ColorSpace, PropertyBinding, PropertyBindingId, CompositeOperator,
RasterSpace, FilterOpGraphPictureBufferId,
};
use api::units::{Au, LayoutVector2D};
use crate::scene_building::IsVisible;
use crate::filterdata::SFilterData;
use crate::intern::ItemUid;
use crate::intern::{Internable, InternDebug, Handle as InternHandle};
use crate::internal_types::{LayoutPrimitiveInfo, Filter};
use crate::internal_types::{LayoutPrimitiveInfo, FilterGraphPictureReference,
FilterGraphOp, FilterGraphNode, SVGFE_CONVOLVE_VALUES_LIMIT, Filter};
use crate::picture::PictureCompositeMode;
use crate::prim_store::{
PrimitiveInstanceKind, PrimitiveStore, VectorKey,
@ -69,6 +71,758 @@ pub enum FilterPrimitiveKey {
Composite(ColorSpace, FilterPrimitiveInput, FilterPrimitiveInput, CompositeOperatorKey),
}
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Debug, Clone, Copy, Default, MallocSizeOf, PartialEq, Hash, Eq)]
pub enum FilterGraphPictureBufferIdKey {
#[default]
/// empty slot in feMerge inputs
None,
/// reference to another (earlier) node in filter graph
BufferId(i16),
}
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Debug, Clone, Copy, Default, MallocSizeOf, PartialEq, Hash, Eq)]
pub struct FilterGraphPictureReferenceKey {
/// Id of the picture in question in a namespace unique to this filter DAG,
/// some are special values like
/// FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic.
pub buffer_id: FilterGraphPictureBufferIdKey,
/// Place the input image here in Layout space (like node.subregion)
pub subregion: [Au; 4],
/// Translate the subregion by this amount
pub offset: [Au; 2],
}
impl From<FilterGraphPictureReference> for FilterGraphPictureReferenceKey {
fn from(pic: FilterGraphPictureReference) -> Self {
FilterGraphPictureReferenceKey{
buffer_id: match pic.buffer_id {
FilterOpGraphPictureBufferId::None => FilterGraphPictureBufferIdKey::None,
FilterOpGraphPictureBufferId::BufferId(id) => FilterGraphPictureBufferIdKey::BufferId(id),
},
subregion: [
Au::from_f32_px(pic.subregion.min.x),
Au::from_f32_px(pic.subregion.min.y),
Au::from_f32_px(pic.subregion.max.x),
Au::from_f32_px(pic.subregion.max.y),
],
offset: [
Au::from_f32_px(pic.offset.x),
Au::from_f32_px(pic.offset.y),
],
}
}
}
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Debug, Clone, MallocSizeOf, PartialEq, Hash, Eq)]
pub enum FilterGraphOpKey {
/// combine 2 images with SVG_FEBLEND_MODE_DARKEN
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement
SVGFEBlendDarken,
/// combine 2 images with SVG_FEBLEND_MODE_LIGHTEN
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement
SVGFEBlendLighten,
/// combine 2 images with SVG_FEBLEND_MODE_MULTIPLY
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement
SVGFEBlendMultiply,
/// combine 2 images with SVG_FEBLEND_MODE_NORMAL
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement
SVGFEBlendNormal,
/// combine 2 images with SVG_FEBLEND_MODE_SCREEN
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement
SVGFEBlendScreen,
/// combine 2 images with SVG_FEBLEND_MODE_OVERLAY
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendOverlay,
/// combine 2 images with SVG_FEBLEND_MODE_COLOR_DODGE
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendColorDodge,
/// combine 2 images with SVG_FEBLEND_MODE_COLOR_BURN
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendColorBurn,
/// combine 2 images with SVG_FEBLEND_MODE_HARD_LIGHT
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendHardLight,
/// combine 2 images with SVG_FEBLEND_MODE_SOFT_LIGHT
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendSoftLight,
/// combine 2 images with SVG_FEBLEND_MODE_DIFFERENCE
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendDifference,
/// combine 2 images with SVG_FEBLEND_MODE_EXCLUSION
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendExclusion,
/// combine 2 images with SVG_FEBLEND_MODE_HUE
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendHue,
/// combine 2 images with SVG_FEBLEND_MODE_SATURATION
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendSaturation,
/// combine 2 images with SVG_FEBLEND_MODE_COLOR
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendColor,
/// combine 2 images with SVG_FEBLEND_MODE_LUMINOSITY
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendLuminosity,
/// transform colors of image through 5x4 color matrix (transposed for
/// efficiency)
/// parameters: FilterOpGraphNode, matrix[5][4]
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feColorMatrixElement
SVGFEColorMatrix{values: [Au; 20]},
/// transform colors of image through configurable gradients with component
/// swizzle
/// parameters: FilterOpGraphNode, FilterData
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feComponentTransferElement
SVGFEComponentTransferInterned{handle: ItemUid, creates_pixels: bool},
/// composite 2 images with chosen composite mode with parameters for that
/// mode
/// parameters: FilterOpGraphNode, k1, k2, k3, k4
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement
SVGFECompositeArithmetic{k1: Au, k2: Au, k3: Au, k4: Au},
/// composite 2 images with chosen composite mode with parameters for that
/// mode
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement
SVGFECompositeATop,
/// composite 2 images with chosen composite mode with parameters for that
/// mode
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement
SVGFECompositeIn,
/// composite 2 images with chosen composite mode with parameters for that
/// mode
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Docs: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feComposite
SVGFECompositeLighter,
/// composite 2 images with chosen composite mode with parameters for that
/// mode
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement
SVGFECompositeOut,
/// composite 2 images with chosen composite mode with parameters for that
/// mode
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement
SVGFECompositeOver,
/// composite 2 images with chosen composite mode with parameters for that
/// mode
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement
SVGFECompositeXOR,
/// transform image through convolution matrix of up to 25 values (spec
/// allows more but for performance reasons we do not)
/// parameters: FilterOpGraphNode, orderX, orderY, kernelValues[25],
/// divisor, bias, targetX, targetY, kernelUnitLengthX, kernelUnitLengthY,
/// preserveAlpha
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feConvolveMatrixElement
SVGFEConvolveMatrixEdgeModeDuplicate{order_x: i32, order_y: i32,
kernel: [Au; SVGFE_CONVOLVE_VALUES_LIMIT], divisor: Au, bias: Au,
target_x: i32, target_y: i32, kernel_unit_length_x: Au,
kernel_unit_length_y: Au, preserve_alpha: i32},
/// transform image through convolution matrix of up to 25 values (spec
/// allows more but for performance reasons we do not)
/// parameters: FilterOpGraphNode, orderX, orderY, kernelValues[25],
/// divisor, bias, targetX, targetY, kernelUnitLengthX, kernelUnitLengthY,
/// preserveAlpha
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feConvolveMatrixElement
SVGFEConvolveMatrixEdgeModeNone{order_x: i32, order_y: i32,
kernel: [Au; SVGFE_CONVOLVE_VALUES_LIMIT], divisor: Au, bias: Au,
target_x: i32, target_y: i32, kernel_unit_length_x: Au,
kernel_unit_length_y: Au, preserve_alpha: i32},
/// transform image through convolution matrix of up to 25 values (spec
/// allows more but for performance reasons we do not)
/// parameters: FilterOpGraphNode, orderX, orderY, kernelValues[25],
/// divisor, bias, targetX, targetY, kernelUnitLengthX, kernelUnitLengthY,
/// preserveAlpha
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feConvolveMatrixElement
SVGFEConvolveMatrixEdgeModeWrap{order_x: i32, order_y: i32,
kernel: [Au; SVGFE_CONVOLVE_VALUES_LIMIT], divisor: Au, bias: Au,
target_x: i32, target_y: i32, kernel_unit_length_x: Au,
kernel_unit_length_y: Au, preserve_alpha: i32},
/// calculate lighting based on heightmap image with provided values for a
/// distant light source with specified direction
/// parameters: FilterOpGraphNode, surfaceScale, diffuseConstant,
/// kernelUnitLengthX, kernelUnitLengthY, azimuth, elevation
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDiffuseLightingElement
/// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDistantLightElement
SVGFEDiffuseLightingDistant{surface_scale: Au, diffuse_constant: Au,
kernel_unit_length_x: Au, kernel_unit_length_y: Au, azimuth: Au,
elevation: Au},
/// calculate lighting based on heightmap image with provided values for a
/// point light source at specified location
/// parameters: FilterOpGraphNode, surfaceScale, diffuseConstant,
/// kernelUnitLengthX, kernelUnitLengthY, x, y, z
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDiffuseLightingElement
/// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEPointLightElement
SVGFEDiffuseLightingPoint{surface_scale: Au, diffuse_constant: Au,
kernel_unit_length_x: Au, kernel_unit_length_y: Au, x: Au, y: Au,
z: Au},
/// calculate lighting based on heightmap image with provided values for a
/// spot light source at specified location pointing at specified target
/// location with specified hotspot sharpness and cone angle
/// parameters: FilterOpGraphNode, surfaceScale, diffuseConstant,
/// kernelUnitLengthX, kernelUnitLengthY, x, y, z, pointsAtX, pointsAtY,
/// pointsAtZ, specularExponent, limitingConeAngle
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDiffuseLightingElement
/// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpotLightElement
SVGFEDiffuseLightingSpot{surface_scale: Au, diffuse_constant: Au,
kernel_unit_length_x: Au, kernel_unit_length_y: Au, x: Au, y: Au, z: Au,
points_at_x: Au, points_at_y: Au, points_at_z: Au, cone_exponent: Au,
limiting_cone_angle: Au},
/// calculate a distorted version of first input image using offset values
/// from second input image at specified intensity
/// parameters: FilterOpGraphNode, scale, xChannelSelector, yChannelSelector
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDisplacementMapElement
SVGFEDisplacementMap{scale: Au, x_channel_selector: u32,
y_channel_selector: u32},
/// create and merge a dropshadow version of the specified image's alpha
/// channel with specified offset and blur radius
/// parameters: FilterOpGraphNode, flood_color, flood_opacity, dx, dy,
/// stdDeviationX, stdDeviationY
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDropShadowElement
SVGFEDropShadow{color: ColorU, dx: Au, dy: Au, std_deviation_x: Au,
std_deviation_y: Au},
/// synthesize a new image of specified size containing a solid color
/// parameters: FilterOpGraphNode, color
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEFloodElement
SVGFEFlood{color: ColorU},
/// create a blurred version of the input image
/// parameters: FilterOpGraphNode, stdDeviationX, stdDeviationY
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEGaussianBlurElement
SVGFEGaussianBlur{std_deviation_x: Au, std_deviation_y: Au},
/// Filter that does no transformation of the colors, needed for
/// debug purposes, and is the default value in impl_default_for_enums.
SVGFEIdentity,
/// synthesize a new image based on a url (i.e. blob image source)
/// parameters: FilterOpGraphNode, sampling_filter (see SamplingFilter in
/// Types.h), transform
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEImageElement
SVGFEImage{sampling_filter: u32, matrix: [Au; 6]},
/// create a new image based on the input image with the contour stretched
/// outward (dilate operator)
/// parameters: FilterOpGraphNode, radiusX, radiusY
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEMorphologyElement
SVGFEMorphologyDilate{radius_x: Au, radius_y: Au},
/// create a new image based on the input image with the contour shrunken
/// inward (erode operator)
/// parameters: FilterOpGraphNode, radiusX, radiusY
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEMorphologyElement
SVGFEMorphologyErode{radius_x: Au, radius_y: Au},
/// represents CSS opacity property as a graph node like the rest of the
/// SVGFE* filters
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
SVGFEOpacity{value: Au},
/// represents CSS opacity property as a graph node like the rest of the
/// SVGFE* filters
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
SVGFEOpacityBinding{valuebindingid: PropertyBindingId, value: Au},
/// Filter that copies the SourceGraphic image into the specified subregion,
/// This is intentionally the only way to get SourceGraphic into the graph,
/// as the filter region must be applied before it is used.
/// parameters: FilterOpGraphNode
/// SVG filter semantics - no inputs, no linear
SVGFESourceGraphic,
/// Filter that copies the SourceAlpha image into the specified subregion,
/// This is intentionally the only way to get SourceAlpha into the graph,
/// as the filter region must be applied before it is used.
/// parameters: FilterOpGraphNode
/// SVG filter semantics - no inputs, no linear
SVGFESourceAlpha,
/// calculate lighting based on heightmap image with provided values for a
/// distant light source with specified direction
/// parameters: FilerData, surfaceScale, specularConstant, specularExponent,
/// kernelUnitLengthX, kernelUnitLengthY, azimuth, elevation
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpecularLightingElement
/// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDistantLightElement
SVGFESpecularLightingDistant{surface_scale: Au, specular_constant: Au,
specular_exponent: Au, kernel_unit_length_x: Au,
kernel_unit_length_y: Au, azimuth: Au, elevation: Au},
/// calculate lighting based on heightmap image with provided values for a
/// point light source at specified location
/// parameters: FilterOpGraphNode, surfaceScale, specularConstant,
/// specularExponent, kernelUnitLengthX, kernelUnitLengthY, x, y, z
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpecularLightingElement
/// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEPointLightElement
SVGFESpecularLightingPoint{surface_scale: Au, specular_constant: Au,
specular_exponent: Au, kernel_unit_length_x: Au,
kernel_unit_length_y: Au, x: Au, y: Au, z: Au},
/// calculate lighting based on heightmap image with provided values for a
/// spot light source at specified location pointing at specified target
/// location with specified hotspot sharpness and cone angle
/// parameters: FilterOpGraphNode, surfaceScale, specularConstant,
/// specularExponent, kernelUnitLengthX, kernelUnitLengthY, x, y, z,
/// pointsAtX, pointsAtY, pointsAtZ, specularExponent, limitingConeAngle
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpecularLightingElement
/// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpotLightElement
SVGFESpecularLightingSpot{surface_scale: Au, specular_constant: Au,
specular_exponent: Au, kernel_unit_length_x: Au,
kernel_unit_length_y: Au, x: Au, y: Au, z: Au, points_at_x: Au,
points_at_y: Au, points_at_z: Au, cone_exponent: Au,
limiting_cone_angle: Au},
/// create a new image based on the input image, repeated throughout the
/// output rectangle
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETileElement
SVGFETile,
/// convert a color image to an alpha channel - internal use; generated by
/// SVGFilterInstance::GetOrCreateSourceAlphaIndex().
SVGFEToAlpha,
/// synthesize a new image based on Fractal Noise (Perlin) with the chosen
/// stitching mode
/// parameters: FilterOpGraphNode, baseFrequencyX, baseFrequencyY,
/// numOctaves, seed
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETurbulenceElement
SVGFETurbulenceWithFractalNoiseWithNoStitching{base_frequency_x: Au,
base_frequency_y: Au, num_octaves: u32, seed: u32},
/// synthesize a new image based on Fractal Noise (Perlin) with the chosen
/// stitching mode
/// parameters: FilterOpGraphNode, baseFrequencyX, baseFrequencyY,
/// numOctaves, seed
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETurbulenceElement
SVGFETurbulenceWithFractalNoiseWithStitching{base_frequency_x: Au,
base_frequency_y: Au, num_octaves: u32, seed: u32},
/// synthesize a new image based on Turbulence Noise (offset vectors)
/// parameters: FilterOpGraphNode, baseFrequencyX, baseFrequencyY,
/// numOctaves, seed
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETurbulenceElement
SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{base_frequency_x: Au,
base_frequency_y: Au, num_octaves: u32, seed: u32},
/// synthesize a new image based on Turbulence Noise (offset vectors)
/// parameters: FilterOpGraphNode, baseFrequencyX, baseFrequencyY,
/// numOctaves, seed
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETurbulenceElement
SVGFETurbulenceWithTurbulenceNoiseWithStitching{base_frequency_x: Au,
base_frequency_y: Au, num_octaves: u32, seed: u32},
}
impl From<FilterGraphOp> for FilterGraphOpKey {
fn from(op: FilterGraphOp) -> Self {
match op {
FilterGraphOp::SVGFEBlendDarken => FilterGraphOpKey::SVGFEBlendDarken,
FilterGraphOp::SVGFEBlendLighten => FilterGraphOpKey::SVGFEBlendLighten,
FilterGraphOp::SVGFEBlendMultiply => FilterGraphOpKey::SVGFEBlendMultiply,
FilterGraphOp::SVGFEBlendNormal => FilterGraphOpKey::SVGFEBlendNormal,
FilterGraphOp::SVGFEBlendScreen => FilterGraphOpKey::SVGFEBlendScreen,
FilterGraphOp::SVGFEBlendOverlay => FilterGraphOpKey::SVGFEBlendOverlay,
FilterGraphOp::SVGFEBlendColorDodge => FilterGraphOpKey::SVGFEBlendColorDodge,
FilterGraphOp::SVGFEBlendColorBurn => FilterGraphOpKey::SVGFEBlendColorBurn,
FilterGraphOp::SVGFEBlendHardLight => FilterGraphOpKey::SVGFEBlendHardLight,
FilterGraphOp::SVGFEBlendSoftLight => FilterGraphOpKey::SVGFEBlendSoftLight,
FilterGraphOp::SVGFEBlendDifference => FilterGraphOpKey::SVGFEBlendDifference,
FilterGraphOp::SVGFEBlendExclusion => FilterGraphOpKey::SVGFEBlendExclusion,
FilterGraphOp::SVGFEBlendHue => FilterGraphOpKey::SVGFEBlendHue,
FilterGraphOp::SVGFEBlendSaturation => FilterGraphOpKey::SVGFEBlendSaturation,
FilterGraphOp::SVGFEBlendColor => FilterGraphOpKey::SVGFEBlendColor,
FilterGraphOp::SVGFEBlendLuminosity => FilterGraphOpKey::SVGFEBlendLuminosity,
FilterGraphOp::SVGFEColorMatrix { values: color_matrix } => {
let mut quantized_values: [Au; 20] = [Au(0); 20];
for (value, result) in color_matrix.iter().zip(quantized_values.iter_mut()) {
*result = Au::from_f32_px(*value);
}
FilterGraphOpKey::SVGFEColorMatrix{values: quantized_values}
}
FilterGraphOp::SVGFEComponentTransfer => unreachable!(),
FilterGraphOp::SVGFEComponentTransferInterned { handle, creates_pixels } => FilterGraphOpKey::SVGFEComponentTransferInterned{
handle: handle.uid(),
creates_pixels,
},
FilterGraphOp::SVGFECompositeArithmetic { k1, k2, k3, k4 } => {
FilterGraphOpKey::SVGFECompositeArithmetic{
k1: Au::from_f32_px(k1),
k2: Au::from_f32_px(k2),
k3: Au::from_f32_px(k3),
k4: Au::from_f32_px(k4),
}
}
FilterGraphOp::SVGFECompositeATop => FilterGraphOpKey::SVGFECompositeATop,
FilterGraphOp::SVGFECompositeIn => FilterGraphOpKey::SVGFECompositeIn,
FilterGraphOp::SVGFECompositeLighter => FilterGraphOpKey::SVGFECompositeLighter,
FilterGraphOp::SVGFECompositeOut => FilterGraphOpKey::SVGFECompositeOut,
FilterGraphOp::SVGFECompositeOver => FilterGraphOpKey::SVGFECompositeOver,
FilterGraphOp::SVGFECompositeXOR => FilterGraphOpKey::SVGFECompositeXOR,
FilterGraphOp::SVGFEConvolveMatrixEdgeModeDuplicate { order_x, order_y, kernel, divisor, bias, target_x, target_y, kernel_unit_length_x, kernel_unit_length_y, preserve_alpha } => {
let mut values: [Au; SVGFE_CONVOLVE_VALUES_LIMIT] = [Au(0); SVGFE_CONVOLVE_VALUES_LIMIT];
for (value, result) in kernel.iter().zip(values.iter_mut()) {
*result = Au::from_f32_px(*value)
}
FilterGraphOpKey::SVGFEConvolveMatrixEdgeModeDuplicate{
order_x,
order_y,
kernel: values,
divisor: Au::from_f32_px(divisor),
bias: Au::from_f32_px(bias),
target_x,
target_y,
kernel_unit_length_x: Au::from_f32_px(kernel_unit_length_x),
kernel_unit_length_y: Au::from_f32_px(kernel_unit_length_y),
preserve_alpha,
}
}
FilterGraphOp::SVGFEConvolveMatrixEdgeModeNone { order_x, order_y, kernel, divisor, bias, target_x, target_y, kernel_unit_length_x, kernel_unit_length_y, preserve_alpha } => {
let mut values: [Au; SVGFE_CONVOLVE_VALUES_LIMIT] = [Au(0); SVGFE_CONVOLVE_VALUES_LIMIT];
for (value, result) in kernel.iter().zip(values.iter_mut()) {
*result = Au::from_f32_px(*value)
}
FilterGraphOpKey::SVGFEConvolveMatrixEdgeModeNone{
order_x,
order_y,
kernel: values,
divisor: Au::from_f32_px(divisor),
bias: Au::from_f32_px(bias),
target_x,
target_y,
kernel_unit_length_x: Au::from_f32_px(kernel_unit_length_x),
kernel_unit_length_y: Au::from_f32_px(kernel_unit_length_y),
preserve_alpha,
}
}
FilterGraphOp::SVGFEConvolveMatrixEdgeModeWrap { order_x, order_y, kernel, divisor, bias, target_x, target_y, kernel_unit_length_x, kernel_unit_length_y, preserve_alpha } => {
let mut values: [Au; SVGFE_CONVOLVE_VALUES_LIMIT] = [Au(0); SVGFE_CONVOLVE_VALUES_LIMIT];
for (value, result) in kernel.iter().zip(values.iter_mut()) {
*result = Au::from_f32_px(*value)
}
FilterGraphOpKey::SVGFEConvolveMatrixEdgeModeWrap{
order_x,
order_y,
kernel: values,
divisor: Au::from_f32_px(divisor),
bias: Au::from_f32_px(bias),
target_x,
target_y,
kernel_unit_length_x: Au::from_f32_px(kernel_unit_length_x),
kernel_unit_length_y: Au::from_f32_px(kernel_unit_length_y),
preserve_alpha,
}
}
FilterGraphOp::SVGFEDiffuseLightingDistant { surface_scale, diffuse_constant, kernel_unit_length_x, kernel_unit_length_y, azimuth, elevation } => {
FilterGraphOpKey::SVGFEDiffuseLightingDistant{
surface_scale: Au::from_f32_px(surface_scale),
diffuse_constant: Au::from_f32_px(diffuse_constant),
kernel_unit_length_x: Au::from_f32_px(kernel_unit_length_x),
kernel_unit_length_y: Au::from_f32_px(kernel_unit_length_y),
azimuth: Au::from_f32_px(azimuth),
elevation: Au::from_f32_px(elevation),
}
}
FilterGraphOp::SVGFEDiffuseLightingPoint { surface_scale, diffuse_constant, kernel_unit_length_x, kernel_unit_length_y, x, y, z } => {
FilterGraphOpKey::SVGFEDiffuseLightingPoint{
surface_scale: Au::from_f32_px(surface_scale),
diffuse_constant: Au::from_f32_px(diffuse_constant),
kernel_unit_length_x: Au::from_f32_px(kernel_unit_length_x),
kernel_unit_length_y: Au::from_f32_px(kernel_unit_length_y),
x: Au::from_f32_px(x),
y: Au::from_f32_px(y),
z: Au::from_f32_px(z),
}
}
FilterGraphOp::SVGFEDiffuseLightingSpot { surface_scale, diffuse_constant, kernel_unit_length_x, kernel_unit_length_y, x, y, z, points_at_x, points_at_y, points_at_z, cone_exponent, limiting_cone_angle } => {
FilterGraphOpKey::SVGFEDiffuseLightingSpot{
surface_scale: Au::from_f32_px(surface_scale),
diffuse_constant: Au::from_f32_px(diffuse_constant),
kernel_unit_length_x: Au::from_f32_px(kernel_unit_length_x),
kernel_unit_length_y: Au::from_f32_px(kernel_unit_length_y),
x: Au::from_f32_px(x),
y: Au::from_f32_px(y),
z: Au::from_f32_px(z),
points_at_x: Au::from_f32_px(points_at_x),
points_at_y: Au::from_f32_px(points_at_y),
points_at_z: Au::from_f32_px(points_at_z),
cone_exponent: Au::from_f32_px(cone_exponent),
limiting_cone_angle: Au::from_f32_px(limiting_cone_angle),
}
}
FilterGraphOp::SVGFEDisplacementMap { scale, x_channel_selector, y_channel_selector } => {
FilterGraphOpKey::SVGFEDisplacementMap{
scale: Au::from_f32_px(scale),
x_channel_selector,
y_channel_selector,
}
}
FilterGraphOp::SVGFEDropShadow { color, dx, dy, std_deviation_x, std_deviation_y } => {
FilterGraphOpKey::SVGFEDropShadow{
color: color.into(),
dx: Au::from_f32_px(dx),
dy: Au::from_f32_px(dy),
std_deviation_x: Au::from_f32_px(std_deviation_x),
std_deviation_y: Au::from_f32_px(std_deviation_y),
}
}
FilterGraphOp::SVGFEFlood { color } => FilterGraphOpKey::SVGFEFlood{color: color.into()},
FilterGraphOp::SVGFEGaussianBlur { std_deviation_x, std_deviation_y } => {
FilterGraphOpKey::SVGFEGaussianBlur{
std_deviation_x: Au::from_f32_px(std_deviation_x),
std_deviation_y: Au::from_f32_px(std_deviation_y),
}
}
FilterGraphOp::SVGFEIdentity => FilterGraphOpKey::SVGFEIdentity,
FilterGraphOp::SVGFEImage { sampling_filter, matrix } => {
let mut values: [Au; 6] = [Au(0); 6];
for (value, result) in matrix.iter().zip(values.iter_mut()) {
*result = Au::from_f32_px(*value)
}
FilterGraphOpKey::SVGFEImage{
sampling_filter,
matrix: values,
}
}
FilterGraphOp::SVGFEMorphologyDilate { radius_x, radius_y } => {
FilterGraphOpKey::SVGFEMorphologyDilate{
radius_x: Au::from_f32_px(radius_x),
radius_y: Au::from_f32_px(radius_y),
}
}
FilterGraphOp::SVGFEMorphologyErode { radius_x, radius_y } => {
FilterGraphOpKey::SVGFEMorphologyErode{
radius_x: Au::from_f32_px(radius_x),
radius_y: Au::from_f32_px(radius_y),
}
}
FilterGraphOp::SVGFEOpacity{valuebinding: binding, value: _} => {
match binding {
PropertyBinding::Value(value) => {
FilterGraphOpKey::SVGFEOpacity{value: Au::from_f32_px(value)}
}
PropertyBinding::Binding(key, default) => {
FilterGraphOpKey::SVGFEOpacityBinding{valuebindingid: key.id, value: Au::from_f32_px(default)}
}
}
}
FilterGraphOp::SVGFESourceAlpha => FilterGraphOpKey::SVGFESourceAlpha,
FilterGraphOp::SVGFESourceGraphic => FilterGraphOpKey::SVGFESourceGraphic,
FilterGraphOp::SVGFESpecularLightingDistant { surface_scale, specular_constant, specular_exponent, kernel_unit_length_x, kernel_unit_length_y, azimuth, elevation } => {
FilterGraphOpKey::SVGFESpecularLightingDistant{
surface_scale: Au::from_f32_px(surface_scale),
specular_constant: Au::from_f32_px(specular_constant),
specular_exponent: Au::from_f32_px(specular_exponent),
kernel_unit_length_x: Au::from_f32_px(kernel_unit_length_x),
kernel_unit_length_y: Au::from_f32_px(kernel_unit_length_y),
azimuth: Au::from_f32_px(azimuth),
elevation: Au::from_f32_px(elevation),
}
}
FilterGraphOp::SVGFESpecularLightingPoint { surface_scale, specular_constant, specular_exponent, kernel_unit_length_x, kernel_unit_length_y, x, y, z } => {
FilterGraphOpKey::SVGFESpecularLightingPoint{
surface_scale: Au::from_f32_px(surface_scale),
specular_constant: Au::from_f32_px(specular_constant),
specular_exponent: Au::from_f32_px(specular_exponent),
kernel_unit_length_x: Au::from_f32_px(kernel_unit_length_x),
kernel_unit_length_y: Au::from_f32_px(kernel_unit_length_y),
x: Au::from_f32_px(x),
y: Au::from_f32_px(y),
z: Au::from_f32_px(z),
}
}
FilterGraphOp::SVGFESpecularLightingSpot { surface_scale, specular_constant, specular_exponent, kernel_unit_length_x, kernel_unit_length_y, x, y, z, points_at_x, points_at_y, points_at_z, cone_exponent, limiting_cone_angle } => {
FilterGraphOpKey::SVGFESpecularLightingSpot{
surface_scale: Au::from_f32_px(surface_scale),
specular_constant: Au::from_f32_px(specular_constant),
specular_exponent: Au::from_f32_px(specular_exponent),
kernel_unit_length_x: Au::from_f32_px(kernel_unit_length_x),
kernel_unit_length_y: Au::from_f32_px(kernel_unit_length_y),
x: Au::from_f32_px(x),
y: Au::from_f32_px(y),
z: Au::from_f32_px(z),
points_at_x: Au::from_f32_px(points_at_x),
points_at_y: Au::from_f32_px(points_at_y),
points_at_z: Au::from_f32_px(points_at_z),
cone_exponent: Au::from_f32_px(cone_exponent),
limiting_cone_angle: Au::from_f32_px(limiting_cone_angle),
}
}
FilterGraphOp::SVGFETile => FilterGraphOpKey::SVGFETile,
FilterGraphOp::SVGFEToAlpha => FilterGraphOpKey::SVGFEToAlpha,
FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching { base_frequency_x, base_frequency_y, num_octaves, seed } => {
FilterGraphOpKey::SVGFETurbulenceWithFractalNoiseWithNoStitching {
base_frequency_x: Au::from_f32_px(base_frequency_x),
base_frequency_y: Au::from_f32_px(base_frequency_y),
num_octaves,
seed,
}
}
FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithStitching { base_frequency_x, base_frequency_y, num_octaves, seed } => {
FilterGraphOpKey::SVGFETurbulenceWithFractalNoiseWithStitching {
base_frequency_x: Au::from_f32_px(base_frequency_x),
base_frequency_y: Au::from_f32_px(base_frequency_y),
num_octaves,
seed,
}
}
FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching { base_frequency_x, base_frequency_y, num_octaves, seed } => {
FilterGraphOpKey::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching {
base_frequency_x: Au::from_f32_px(base_frequency_x),
base_frequency_y: Au::from_f32_px(base_frequency_y),
num_octaves,
seed,
}
}
FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching { base_frequency_x, base_frequency_y, num_octaves, seed } => {
FilterGraphOpKey::SVGFETurbulenceWithTurbulenceNoiseWithStitching {
base_frequency_x: Au::from_f32_px(base_frequency_x),
base_frequency_y: Au::from_f32_px(base_frequency_y),
num_octaves,
seed,
}
}
}
}
}
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Debug, Clone, MallocSizeOf, PartialEq, Hash, Eq)]
pub struct FilterGraphNodeKey {
/// Indicates this graph node was marked as unnecessary by the DAG optimizer
/// (for example SVGFEOffset can often be folded into downstream nodes)
pub kept_by_optimizer: bool,
/// True if color_interpolation_filter == LinearRgb; shader will convert
/// sRGB texture pixel colors on load and convert back on store, for correct
/// interpolation
pub linear: bool,
/// padding for output rect if we need a border to get correct clamping, or
/// to account for larger final subregion than source rect (see bug 1869672)
pub inflate: i16,
/// virtualized picture input binding 1 (i.e. texture source), typically
/// this is used, but certain filters do not use it
pub inputs: Vec<FilterGraphPictureReferenceKey>,
/// rect this node will render into, in filter space, does not account for
/// inflate or device_pixel_scale
pub subregion: [Au; 4],
}
impl From<FilterGraphNode> for FilterGraphNodeKey {
fn from(node: FilterGraphNode) -> Self {
FilterGraphNodeKey{
kept_by_optimizer: node.kept_by_optimizer,
linear: node.linear,
inflate: node.inflate,
inputs: node.inputs.into_iter().map(|node| {node.into()}).collect(),
subregion: [
Au::from_f32_px(node.subregion.min.x),
Au::from_f32_px(node.subregion.min.y),
Au::from_f32_px(node.subregion.max.x),
Au::from_f32_px(node.subregion.max.y),
],
}
}
}
/// Represents a hashable description of how a picture primitive
/// will be composited into its parent.
#[cfg_attr(feature = "capture", derive(Serialize))]
@ -96,6 +850,7 @@ pub enum PictureCompositeKey {
ComponentTransfer(ItemUid),
Flood(ColorU),
SvgFilter(Vec<FilterPrimitiveKey>),
SVGFEGraph(Vec<(FilterGraphNodeKey, FilterGraphOpKey)>),
// MixBlendMode
Multiply,
@ -180,6 +935,7 @@ impl From<Option<PictureCompositeMode>> for PictureCompositeKey {
}
Filter::ComponentTransfer => unreachable!(),
Filter::Flood(color) => PictureCompositeKey::Flood(color.into()),
Filter::SVGGraphNode(_node, _op) => unreachable!(),
}
}
Some(PictureCompositeMode::ComponentTransferFilter(handle)) => {
@ -222,6 +978,12 @@ impl From<Option<PictureCompositeMode>> for PictureCompositeKey {
}
}).collect())
}
Some(PictureCompositeMode::SVGFEGraph(filter_nodes)) => {
PictureCompositeKey::SVGFEGraph(
filter_nodes.into_iter().map(|(node, op)| {
(node.into(), op.into())
}).collect())
}
Some(PictureCompositeMode::Blit(_)) |
Some(PictureCompositeMode::TileCache { .. }) |
Some(PictureCompositeMode::IntermediateSurface) |

Просмотреть файл

@ -14,10 +14,10 @@ use crate::spatial_tree::SpatialTree;
use crate::clip::{ClipStore, ClipItemKind};
use crate::frame_builder::{FrameGlobalResources};
use crate::gpu_cache::{GpuCache, GpuCacheAddress};
use crate::gpu_types::{BorderInstance, SvgFilterInstance, BlurDirection, BlurInstance, PrimitiveHeaders, ScalingInstance};
use crate::gpu_types::{BorderInstance, SvgFilterInstance, SVGFEFilterInstance, BlurDirection, BlurInstance, PrimitiveHeaders, ScalingInstance};
use crate::gpu_types::{TransformPalette, ZBufferIdGenerator, MaskInstance, ClipSpace};
use crate::gpu_types::{ZBufferId, QuadSegment, PrimitiveInstanceData, TransformPaletteId};
use crate::internal_types::{FastHashMap, TextureSource, CacheTextureId};
use crate::internal_types::{FastHashMap, TextureSource, CacheTextureId, FilterGraphOp};
use crate::picture::{SliceId, SurfaceInfo, ResolvedSurfaceTexture, TileCacheInstance};
use crate::quad;
use crate::prim_store::{PrimitiveInstance, PrimitiveStore, PrimitiveScratchBuffer};
@ -28,7 +28,7 @@ use crate::prim_store::gradient::{
use crate::renderer::{GpuBufferAddress, GpuBufferBuilder};
use crate::render_backend::DataStores;
use crate::render_task::{RenderTaskKind, RenderTaskAddress, SubPass};
use crate::render_task::{RenderTask, ScalingTask, SvgFilterInfo, MaskSubPass};
use crate::render_task::{RenderTask, ScalingTask, SvgFilterInfo, MaskSubPass, SVGFEFilterTask};
use crate::render_task_graph::{RenderTaskGraph, RenderTaskId};
use crate::resource_cache::ResourceCache;
use crate::spatial_tree::{SpatialNodeIndex};
@ -226,6 +226,7 @@ pub struct ColorRenderTarget {
pub horizontal_blurs: FastHashMap<TextureSource, Vec<BlurInstance>>,
pub scalings: FastHashMap<TextureSource, Vec<ScalingInstance>>,
pub svg_filters: Vec<(BatchTextures, Vec<SvgFilterInstance>)>,
pub svg_nodes: Vec<(BatchTextures, Vec<SVGFEFilterInstance>)>,
pub blits: Vec<BlitJob>,
alpha_tasks: Vec<RenderTaskId>,
screen_size: DeviceIntSize,
@ -256,6 +257,7 @@ impl RenderTarget for ColorRenderTarget {
horizontal_blurs: FastHashMap::default(),
scalings: FastHashMap::default(),
svg_filters: Vec::new(),
svg_nodes: Vec::new(),
blits: Vec::new(),
alpha_tasks: Vec::new(),
screen_size,
@ -438,6 +440,17 @@ impl RenderTarget for ColorRenderTarget {
task_info.extra_gpu_cache_handle.map(|handle| gpu_cache.get_address(&handle)),
)
}
RenderTaskKind::SVGFENode(ref task_info) => {
add_svg_filter_node_instances(
&mut self.svg_nodes,
render_tasks,
&task_info,
task,
task.children.get(0).cloned(),
task.children.get(1).cloned(),
task_info.extra_gpu_cache_handle.map(|handle| gpu_cache.get_address(&handle)),
)
}
RenderTaskKind::Image(..) |
RenderTaskKind::Cached(..) |
RenderTaskKind::ClipRegion(..) |
@ -559,7 +572,8 @@ impl RenderTarget for AlphaRenderTarget {
RenderTaskKind::ConicGradient(..) |
RenderTaskKind::TileComposite(..) |
RenderTaskKind::Prim(..) |
RenderTaskKind::SvgFilter(..) => {
RenderTaskKind::SvgFilter(..) |
RenderTaskKind::SVGFENode(..) => {
panic!("BUG: should not be added to alpha target!");
}
RenderTaskKind::Empty(..) => {
@ -799,7 +813,8 @@ impl TextureCacheRenderTarget {
RenderTaskKind::Scaling(..) |
RenderTaskKind::TileComposite(..) |
RenderTaskKind::Empty(..) |
RenderTaskKind::SvgFilter(..) => {
RenderTaskKind::SvgFilter(..) |
RenderTaskKind::SVGFENode(..) => {
panic!("BUG: unexpected task kind for texture cache target");
}
#[cfg(test)]
@ -945,6 +960,175 @@ fn add_svg_filter_instances(
instances.push((textures, vec![instance]));
}
/// Generates SVGFEFilterInstances from a single SVGFEFilterTask, this is what
/// prepares vertex data for the shader, and adds it to the appropriate batch.
///
/// The interesting parts of the handling of SVG filters are:
/// * scene_building.rs : wrap_prim_with_filters
/// * picture.rs : get_coverage_svgfe
/// * render_task.rs : new_svg_filter_graph
/// * render_target.rs : add_svg_filter_node_instances (you are here)
fn add_svg_filter_node_instances(
instances: &mut Vec<(BatchTextures, Vec<SVGFEFilterInstance>)>,
render_tasks: &RenderTaskGraph,
task_info: &SVGFEFilterTask,
target_task: &RenderTask,
input_1_task: Option<RenderTaskId>,
input_2_task: Option<RenderTaskId>,
extra_data_address: Option<GpuCacheAddress>,
) {
let node = &task_info.node;
let op = &task_info.op;
let mut textures = BatchTextures::empty();
// We have to undo the inflate here as the inflated target rect is meant to
// have a blank border
let target_rect = target_task
.get_target_rect()
.inner_box(DeviceIntSideOffsets::new(node.inflate as i32, node.inflate as i32, node.inflate as i32, node.inflate as i32))
.to_f32();
let mut instance = SVGFEFilterInstance {
target_rect,
input_1_content_scale_and_offset: [0.0; 4],
input_2_content_scale_and_offset: [0.0; 4],
input_1_task_address: RenderTaskId::INVALID.into(),
input_2_task_address: RenderTaskId::INVALID.into(),
kind: 0,
input_count: node.inputs.len() as u16,
extra_data_address: extra_data_address.unwrap_or(GpuCacheAddress::INVALID),
};
// Must match FILTER_* in cs_svg_filter_node.glsl
instance.kind = match op {
// Identity does not modify color, no linear case
FilterGraphOp::SVGFEIdentity => 0,
// SourceGraphic does not have its own shader mode, it uses Identity.
FilterGraphOp::SVGFESourceGraphic => 0,
// SourceAlpha does not have its own shader mode, it uses ToAlpha.
FilterGraphOp::SVGFESourceAlpha => 4,
// Opacity scales the entire rgba color, so it does not need a linear
// case as the rgb / a ratio does not change (sRGB is a curve on the RGB
// before alpha multiply, not after)
FilterGraphOp::SVGFEOpacity{..} => 2,
FilterGraphOp::SVGFEToAlpha => 4,
FilterGraphOp::SVGFEBlendColor => {match node.linear {false => 6, true => 7}},
FilterGraphOp::SVGFEBlendColorBurn => {match node.linear {false => 8, true => 9}},
FilterGraphOp::SVGFEBlendColorDodge => {match node.linear {false => 10, true => 11}},
FilterGraphOp::SVGFEBlendDarken => {match node.linear {false => 12, true => 13}},
FilterGraphOp::SVGFEBlendDifference => {match node.linear {false => 14, true => 15}},
FilterGraphOp::SVGFEBlendExclusion => {match node.linear {false => 16, true => 17}},
FilterGraphOp::SVGFEBlendHardLight => {match node.linear {false => 18, true => 19}},
FilterGraphOp::SVGFEBlendHue => {match node.linear {false => 20, true => 21}},
FilterGraphOp::SVGFEBlendLighten => {match node.linear {false => 22, true => 23}},
FilterGraphOp::SVGFEBlendLuminosity => {match node.linear {false => 24, true => 25}},
FilterGraphOp::SVGFEBlendMultiply => {match node.linear {false => 26, true => 27}},
FilterGraphOp::SVGFEBlendNormal => {match node.linear {false => 28, true => 29}},
FilterGraphOp::SVGFEBlendOverlay => {match node.linear {false => 30, true => 31}},
FilterGraphOp::SVGFEBlendSaturation => {match node.linear {false => 32, true => 33}},
FilterGraphOp::SVGFEBlendScreen => {match node.linear {false => 34, true => 35}},
FilterGraphOp::SVGFEBlendSoftLight => {match node.linear {false => 36, true => 37}},
FilterGraphOp::SVGFEColorMatrix{..} => {match node.linear {false => 38, true => 39}},
FilterGraphOp::SVGFEComponentTransfer => unreachable!(),
FilterGraphOp::SVGFEComponentTransferInterned{..} => {match node.linear {false => 40, true => 41}},
FilterGraphOp::SVGFECompositeArithmetic{..} => {match node.linear {false => 42, true => 43}},
FilterGraphOp::SVGFECompositeATop => {match node.linear {false => 44, true => 45}},
FilterGraphOp::SVGFECompositeIn => {match node.linear {false => 46, true => 47}},
FilterGraphOp::SVGFECompositeLighter => {match node.linear {false => 48, true => 49}},
FilterGraphOp::SVGFECompositeOut => {match node.linear {false => 50, true => 51}},
FilterGraphOp::SVGFECompositeOver => {match node.linear {false => 52, true => 53}},
FilterGraphOp::SVGFECompositeXOR => {match node.linear {false => 54, true => 55}},
FilterGraphOp::SVGFEConvolveMatrixEdgeModeDuplicate{..} => {match node.linear {false => 56, true => 57}},
FilterGraphOp::SVGFEConvolveMatrixEdgeModeNone{..} => {match node.linear {false => 58, true => 59}},
FilterGraphOp::SVGFEConvolveMatrixEdgeModeWrap{..} => {match node.linear {false => 60, true => 61}},
FilterGraphOp::SVGFEDiffuseLightingDistant{..} => {match node.linear {false => 62, true => 63}},
FilterGraphOp::SVGFEDiffuseLightingPoint{..} => {match node.linear {false => 64, true => 65}},
FilterGraphOp::SVGFEDiffuseLightingSpot{..} => {match node.linear {false => 66, true => 67}},
FilterGraphOp::SVGFEDisplacementMap{..} => {match node.linear {false => 68, true => 69}},
FilterGraphOp::SVGFEDropShadow{..} => {match node.linear {false => 70, true => 71}},
// feFlood takes an sRGB color and does no math on it, no linear case
FilterGraphOp::SVGFEFlood{..} => 72,
FilterGraphOp::SVGFEGaussianBlur{..} => {match node.linear {false => 74, true => 75}},
// feImage does not meaningfully modify the color of its input, though a
// case could be made for gamma-correct image scaling, that's a bit out
// of scope for now
FilterGraphOp::SVGFEImage{..} => 76,
FilterGraphOp::SVGFEMorphologyDilate{..} => {match node.linear {false => 80, true => 81}},
FilterGraphOp::SVGFEMorphologyErode{..} => {match node.linear {false => 82, true => 83}},
FilterGraphOp::SVGFESpecularLightingDistant{..} => {match node.linear {false => 86, true => 87}},
FilterGraphOp::SVGFESpecularLightingPoint{..} => {match node.linear {false => 88, true => 89}},
FilterGraphOp::SVGFESpecularLightingSpot{..} => {match node.linear {false => 90, true => 91}},
// feTile does not modify color, no linear case
FilterGraphOp::SVGFETile => 92,
FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{..} => {match node.linear {false => 94, true => 95}},
FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithStitching{..} => {match node.linear {false => 96, true => 97}},
FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{..} => {match node.linear {false => 98, true => 99}},
FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching{..} => {match node.linear {false => 100, true => 101}},
};
// This is a bit of an ugly way to do this, but avoids code duplication.
let mut resolve_input = |index: usize, src_task: Option<RenderTaskId>| -> (RenderTaskAddress, [f32; 4]) {
let mut src_task_id = RenderTaskId::INVALID;
let mut resolved_scale_and_offset: [f32; 4] = [0.0; 4];
if let Some(input) = node.inputs.get(index) {
src_task_id = src_task.unwrap();
let src_task = &render_tasks[src_task_id];
textures.input.colors[index] = src_task.get_texture_source();
let src_task_size = src_task.location.size();
let src_scale_x = (src_task_size.width as f32 - input.inflate as f32 * 2.0) / input.subregion.width();
let src_scale_y = (src_task_size.height as f32 - input.inflate as f32 * 2.0) / input.subregion.height();
let scale_x = src_scale_x * node.subregion.width();
let scale_y = src_scale_y * node.subregion.height();
let offset_x = src_scale_x * (node.subregion.min.x - input.subregion.min.x) + input.inflate as f32;
let offset_y = src_scale_y * (node.subregion.min.y - input.subregion.min.y) + input.inflate as f32;
resolved_scale_and_offset = [
scale_x,
scale_y,
offset_x,
offset_y];
}
let address: RenderTaskAddress = src_task_id.into();
(address, resolved_scale_and_offset)
};
(instance.input_1_task_address, instance.input_1_content_scale_and_offset) = resolve_input(0, input_1_task);
(instance.input_2_task_address, instance.input_2_content_scale_and_offset) = resolve_input(1, input_2_task);
// Additional instance modifications for certain filters
match op {
FilterGraphOp::SVGFEOpacity { valuebinding: _, value } => {
// opacity only has one input so we can use the other
// components to store the opacity value
instance.input_2_content_scale_and_offset = [*value, 0.0, 0.0, 0.0];
},
FilterGraphOp::SVGFEMorphologyDilate { radius_x, radius_y } |
FilterGraphOp::SVGFEMorphologyErode { radius_x, radius_y } => {
// morphology filters only use one input, so we use the
// second offset coord to store the radius values.
instance.input_2_content_scale_and_offset = [*radius_x, *radius_y, 0.0, 0.0];
},
FilterGraphOp::SVGFEFlood { color } => {
// flood filters don't use inputs, so we store color here.
// We can't do the same trick on DropShadow because it does have two
// inputs.
instance.input_2_content_scale_and_offset = [color.r, color.g, color.b, color.a];
},
_ => {},
}
for (ref mut batch_textures, ref mut batch) in instances.iter_mut() {
if let Some(combined_textures) = batch_textures.combine_textures(textures) {
batch.push(instance);
// Update the batch textures to the newly combined batch textures
*batch_textures = combined_textures;
// is this really the intended behavior?
return;
}
}
instances.push((textures, vec![instance]));
}
// Information required to do a blit from a source to a target.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -591,9 +591,12 @@ impl RenderTaskGraphBuilder {
}
}
// By now, all surfaces that were borrowed from the render target pool must
// be returned to the resource cache, or we are leaking intermediate surfaces!
assert!(self.active_surfaces.is_empty());
if !self.active_surfaces.is_empty() {
graph.print();
// By now, all surfaces that were borrowed from the render target pool must
// be returned to the resource cache, or we are leaking intermediate surfaces!
assert!(self.active_surfaces.is_empty());
}
// Each task is now allocated to a surface and target rect. Write that to the
// GPU blocks and task_data. After this point, the graph is returned and is
@ -656,29 +659,30 @@ impl RenderTaskGraph {
pub fn print(
&self,
) {
debug!("-- RenderTaskGraph --");
print!("-- RenderTaskGraph --\n");
for (i, task) in self.tasks.iter().enumerate() {
debug!("Task {} [{}]: render_on={} free_after={} children={:?}",
print!("Task {} [{}]: render_on={} free_after={} children={:?} target_size={:?}\n",
i,
task.kind.as_str(),
task.render_on.0,
task.free_after.0,
task.children,
task.get_target_size(),
);
}
for (p, pass) in self.passes.iter().enumerate() {
debug!("Pass {}:", p);
print!("Pass {}:\n", p);
for (s, sub_pass) in pass.sub_passes.iter().enumerate() {
debug!("\tSubPass {}: {:?}",
print!("\tSubPass {}: {:?}\n",
s,
sub_pass.surface,
);
for task_id in &sub_pass.task_ids {
debug!("\t\tTask {:?}", task_id.index);
print!("\t\tTask {:?}\n", task_id.index);
}
}
}

Просмотреть файл

@ -69,7 +69,7 @@ use crate::frame_builder::Frame;
use glyph_rasterizer::GlyphFormat;
use crate::gpu_cache::{GpuCacheUpdate, GpuCacheUpdateList};
use crate::gpu_cache::{GpuCacheDebugChunk, GpuCacheDebugCmd};
use crate::gpu_types::{ScalingInstance, SvgFilterInstance, CopyInstance, PrimitiveInstanceData};
use crate::gpu_types::{ScalingInstance, SvgFilterInstance, SVGFEFilterInstance, CopyInstance, PrimitiveInstanceData};
use crate::gpu_types::{BlurInstance, ClearInstance, CompositeInstance, CompositorTransform};
use crate::internal_types::{TextureSource, TextureCacheCategory, FrameId};
#[cfg(any(feature = "capture", feature = "replay"))]
@ -257,6 +257,10 @@ const GPU_TAG_SVG_FILTER: GpuProfileTag = GpuProfileTag {
label: "SvgFilter",
color: debug_colors::LEMONCHIFFON,
};
const GPU_TAG_SVG_FILTER_NODES: GpuProfileTag = GpuProfileTag {
label: "SvgFilterNodes",
color: debug_colors::LEMONCHIFFON,
};
const GPU_TAG_COMPOSITE: GpuProfileTag = GpuProfileTag {
label: "Composite",
color: debug_colors::TOMATO,
@ -2529,6 +2533,35 @@ impl Renderer {
);
}
fn handle_svg_nodes(
&mut self,
textures: &BatchTextures,
svg_filters: &[SVGFEFilterInstance],
projection: &default::Transform3D<f32>,
stats: &mut RendererStats,
) {
if svg_filters.is_empty() {
return;
}
let _timer = self.gpu_profiler.start_timer(GPU_TAG_SVG_FILTER_NODES);
self.shaders.borrow_mut().cs_svg_filter_node.bind(
&mut self.device,
&projection,
None,
&mut self.renderer_errors,
&mut self.profile,
);
self.draw_instanced_batch(
&svg_filters,
VertexArrayKind::SvgFilterNode,
textures,
stats,
);
}
fn handle_resolve(
&mut self,
resolve_op: &ResolveOp,
@ -3578,6 +3611,10 @@ impl Renderer {
);
}
for (ref textures, ref filters) in &target.svg_nodes {
self.handle_svg_nodes(textures, filters, projection, stats);
}
for alpha_batch_container in &target.alpha_batch_containers {
self.draw_alpha_batch_container(
alpha_batch_container,

Просмотреть файл

@ -263,6 +263,7 @@ impl LazilyCompiledShader {
VertexArrayKind::Scale => &desc::SCALE,
VertexArrayKind::Resolve => &desc::RESOLVE,
VertexArrayKind::SvgFilter => &desc::SVG_FILTER,
VertexArrayKind::SvgFilterNode => &desc::SVG_FILTER_NODE,
VertexArrayKind::Composite => &desc::COMPOSITE,
VertexArrayKind::Clear => &desc::CLEAR,
VertexArrayKind::Copy => &desc::COPY,
@ -601,6 +602,7 @@ pub struct Shaders {
pub cs_radial_gradient: LazilyCompiledShader,
pub cs_conic_gradient: LazilyCompiledShader,
pub cs_svg_filter: LazilyCompiledShader,
pub cs_svg_filter_node: LazilyCompiledShader,
// Brush shaders
brush_solid: BrushShader,
@ -770,6 +772,16 @@ impl Shaders {
profile,
)?;
let cs_svg_filter_node = LazilyCompiledShader::new(
ShaderKind::Cache(VertexArrayKind::SvgFilterNode),
"cs_svg_filter_node",
&[],
device,
options.precache_flags,
&shader_list,
profile,
)?;
let ps_mask = LazilyCompiledShader::new(
ShaderKind::Cache(VertexArrayKind::Mask),
"ps_quad_mask",
@ -1129,6 +1141,7 @@ impl Shaders {
cs_border_solid,
cs_scale,
cs_svg_filter,
cs_svg_filter_node,
brush_solid,
brush_image,
brush_fast_image,
@ -1300,6 +1313,7 @@ impl Shaders {
self.cs_blur_a8.deinit(device);
self.cs_blur_rgba8.deinit(device);
self.cs_svg_filter.deinit(device);
self.cs_svg_filter_node.deinit(device);
self.brush_solid.deinit(device);
self.brush_blend.deinit(device);
self.brush_mix_blend.deinit(device);

Просмотреть файл

@ -567,6 +567,56 @@ pub mod desc {
],
};
pub const SVG_FILTER_NODE: VertexDescriptor = VertexDescriptor {
vertex_attributes: &[VertexAttribute {
name: "aPosition",
count: 2,
kind: VertexAttributeKind::U8Norm,
}],
instance_attributes: &[
VertexAttribute {
name: "aFilterTargetRect",
count: 4,
kind: VertexAttributeKind::F32,
},
VertexAttribute {
name: "aFilterInput1ContentScaleAndOffset",
count: 4,
kind: VertexAttributeKind::F32,
},
VertexAttribute {
name: "aFilterInput2ContentScaleAndOffset",
count: 4,
kind: VertexAttributeKind::F32,
},
VertexAttribute {
name: "aFilterInput1TaskAddress",
count: 1,
kind: VertexAttributeKind::I32,
},
VertexAttribute {
name: "aFilterInput2TaskAddress",
count: 1,
kind: VertexAttributeKind::I32,
},
VertexAttribute {
name: "aFilterKind",
count: 1,
kind: VertexAttributeKind::U16,
},
VertexAttribute {
name: "aFilterInputCount",
count: 1,
kind: VertexAttributeKind::U16,
},
VertexAttribute {
name: "aFilterExtraDataAddress",
count: 2,
kind: VertexAttributeKind::U16,
},
],
};
pub const MASK: VertexDescriptor = VertexDescriptor {
vertex_attributes: &[VertexAttribute {
name: "aPosition",
@ -780,6 +830,7 @@ pub enum VertexArrayKind {
ConicGradient,
Resolve,
SvgFilter,
SvgFilterNode,
Composite,
Clear,
Copy,
@ -1004,6 +1055,7 @@ pub struct RendererVAOs {
conic_gradient_vao: VAO,
resolve_vao: VAO,
svg_filter_vao: VAO,
svg_filter_node_vao: VAO,
composite_vao: VAO,
clear_vao: VAO,
copy_vao: VAO,
@ -1051,6 +1103,7 @@ impl RendererVAOs {
conic_gradient_vao: device.create_vao_with_new_instances(&desc::CONIC_GRADIENT, &prim_vao),
resolve_vao: device.create_vao_with_new_instances(&desc::RESOLVE, &prim_vao),
svg_filter_vao: device.create_vao_with_new_instances(&desc::SVG_FILTER, &prim_vao),
svg_filter_node_vao: device.create_vao_with_new_instances(&desc::SVG_FILTER_NODE, &prim_vao),
composite_vao: device.create_vao_with_new_instances(&desc::COMPOSITE, &prim_vao),
clear_vao: device.create_vao_with_new_instances(&desc::CLEAR, &prim_vao),
copy_vao: device.create_vao_with_new_instances(&desc::COPY, &prim_vao),
@ -1073,6 +1126,7 @@ impl RendererVAOs {
device.delete_vao(self.border_vao);
device.delete_vao(self.scale_vao);
device.delete_vao(self.svg_filter_vao);
device.delete_vao(self.svg_filter_node_vao);
device.delete_vao(self.composite_vao);
device.delete_vao(self.clear_vao);
device.delete_vao(self.copy_vao);
@ -1098,6 +1152,7 @@ impl ops::Index<VertexArrayKind> for RendererVAOs {
VertexArrayKind::ConicGradient => &self.conic_gradient_vao,
VertexArrayKind::Resolve => &self.resolve_vao,
VertexArrayKind::SvgFilter => &self.svg_filter_vao,
VertexArrayKind::SvgFilterNode => &self.svg_filter_node_vao,
VertexArrayKind::Composite => &self.composite_vao,
VertexArrayKind::Clear => &self.clear_vao,
VertexArrayKind::Copy => &self.copy_vao,

Просмотреть файл

@ -45,8 +45,10 @@ use api::{PropertyBinding, ReferenceFrameKind, ScrollFrameDescriptor, ReferenceF
use api::{APZScrollGeneration, HasScrollLinkedEffect, Shadow, SpatialId, StickyFrameDescriptor, ImageMask, ItemTag};
use api::{ClipMode, PrimitiveKeyKind, TransformStyle, YuvColorSpace, ColorRange, YuvData, TempFilterData};
use api::{ReferenceTransformBinding, Rotation, FillRule, SpatialTreeItem, ReferenceFrameDescriptor};
use api::FilterOpGraphPictureBufferId;
use api::units::*;
use crate::image_tiling::simplify_repeated_primitive;
use crate::box_shadow::BLUR_SAMPLE_SCALE;
use crate::clip::{ClipItemKey, ClipStore, ClipItemKeyKind, ClipIntern};
use crate::clip::{ClipInternData, ClipNodeId, ClipLeafId};
use crate::clip::{PolygonDataHandle, ClipTreeBuilder};
@ -56,7 +58,7 @@ use crate::frame_builder::{FrameBuilderConfig};
use glyph_rasterizer::{FontInstance, SharedFontResources};
use crate::hit_test::HitTestingScene;
use crate::intern::Interner;
use crate::internal_types::{FastHashMap, LayoutPrimitiveInfo, Filter, PlaneSplitterIndex, PipelineInstanceId};
use crate::internal_types::{FastHashMap, LayoutPrimitiveInfo, Filter, FilterGraphNode, FilterGraphOp, FilterGraphPictureReference, PlaneSplitterIndex, PipelineInstanceId};
use crate::picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive};
use crate::picture::{BlitReason, OrderedPictureChild, PrimitiveList, SurfaceInfo, PictureFlags};
use crate::picture_graph::PictureGraph;
@ -90,6 +92,7 @@ use std::collections::vec_deque::VecDeque;
use std::sync::Arc;
use crate::util::{VecHelper, MaxRect};
use crate::filterdata::{SFilterDataComponent, SFilterData, SFilterDataKey};
use log::Level;
/// Offsets primitives (and clips) by the external scroll offset
/// supplied to scroll nodes.
@ -192,6 +195,7 @@ impl CompositeOps {
return true;
}
}
Filter::SVGGraphNode(..) => {return true;}
_ => {
if filter.is_noop() {
continue;
@ -724,6 +728,7 @@ impl<'a> SceneBuilder<'a> {
Some(PictureCompositeMode::Filter(Filter::Blur { .. })) => true,
Some(PictureCompositeMode::Filter(Filter::DropShadows { .. })) => true,
Some(PictureCompositeMode::SvgFilter( .. )) => true,
Some(PictureCompositeMode::SVGFEGraph( .. )) => true,
_ => false,
};
@ -899,7 +904,11 @@ impl<'a> SceneBuilder<'a> {
let spatial_node_index = self.get_space(info.spatial_id);
let mut subtraversal = item.sub_iter();
// Avoid doing unnecessary work for empty stacking contexts.
if subtraversal.current_stacking_context_empty() {
// We still have to process it if it has filters, they
// may be things like SVGFEFlood or various specific
// ways to use ComponentTransfer, ColorMatrix, Composite
// which are still visible on an empty stacking context
if subtraversal.current_stacking_context_empty() && item.filters().is_empty() {
subtraversal.skip_current_stacking_context();
traversal = subtraversal;
continue;
@ -982,8 +991,8 @@ impl<'a> SceneBuilder<'a> {
match bc.kind {
ContextKind::Root => {}
ContextKind::StackingContext { sc_info } => {
self.rf_mapper.pop_offset();
self.pop_stacking_context(sc_info);
self.rf_mapper.pop_offset();
}
ContextKind::ReferenceFrame => {
self.rf_mapper.pop_scope();
@ -2527,6 +2536,7 @@ impl<'a> SceneBuilder<'a> {
let has_filters = stacking_context.composite_ops.has_valid_filters();
let spatial_node_context_offset = self.current_offset(stacking_context.spatial_node_index);
source = self.wrap_prim_with_filters(
source,
stacking_context.clip_node_id,
@ -2534,6 +2544,7 @@ impl<'a> SceneBuilder<'a> {
stacking_context.composite_ops.filter_primitives,
stacking_context.composite_ops.filter_datas,
None,
spatial_node_context_offset,
);
// Same for mix-blend-mode, except we can skip if this primitive is the first in the parent
@ -3670,6 +3681,7 @@ impl<'a> SceneBuilder<'a> {
filter_primitives,
filter_datas,
Some(false),
LayoutVector2D::zero(),
);
// If all the filters were no-ops (e.g. opacity(0)) then we don't get a picture here
@ -3768,6 +3780,7 @@ impl<'a> SceneBuilder<'a> {
mut filter_primitives: Vec<FilterPrimitive>,
filter_datas: Vec<FilterData>,
should_inflate_override: Option<bool>,
context_offset: LayoutVector2D,
) -> PictureChainBuilder {
// TODO(cbrewster): Currently CSS and SVG filters live side by side in WebRender, but unexpected results will
// happen if they are used simulataneously. Gecko only provides either filter ops or filter primitives.
@ -3777,6 +3790,495 @@ impl<'a> SceneBuilder<'a> {
// For each filter, create a new image with that composite mode.
let mut current_filter_data_index = 0;
// Check if the filter chain is actually an SVGFE filter graph DAG
if let Some(Filter::SVGGraphNode(..)) = filter_ops.first() {
// The interesting parts of the handling of SVG filters are:
// * scene_building.rs : wrap_prim_with_filters (you are here)
// * picture.rs : get_coverage_svgfe
// * render_task.rs : new_svg_filter_graph
// * render_target.rs : add_svg_filter_node_instances
// The SVG spec allows us to drop the entire filter graph if it is
// unreasonable, so we limit the number of filters in a graph
const BUFFER_LIMIT: usize = 256;
// Easily tunable for debugging proper handling of inflated rects,
// this should normally be 1
const SVGFE_INFLATE: i16 = 1;
// Easily tunable for debugging proper handling of inflated rects,
// this should normally be 0
const SVGFE_INFLATE_OUTPUT: i16 = 0;
// Validate inputs to all filters.
//
// Several assumptions can be made about the DAG:
// * All filters take a specific number of inputs (feMerge is not
// supported, the code that built the display items had to convert
// any feMerge ops to SVGFECompositeOver already).
// * All input buffer ids are < the output buffer id of the node.
// * If SourceGraphic or SourceAlpha are used, they are standalone
// nodes with no inputs.
// * Whenever subregion of a node is smaller than the subregion
// of the inputs, it is a deliberate clip of those inputs to the
// new rect, this can occur before/after blur and dropshadow for
// example, so we must explicitly handle subregion correctly, but
// we do not have to allocate the unused pixels as the transparent
// black has no efect on any of the filters, only certain filters
// like feFlood can generate something from nothing.
// * Coordinate basis of the graph has to be adjusted by
// context_offset to put the subregions in the same space that the
// primitives are in, as they do that offset as well.
let mut reference_for_buffer_id: [FilterGraphPictureReference; BUFFER_LIMIT] = [
FilterGraphPictureReference{
// This value is deliberately invalid, but not a magic
// number, it's just this way to guarantee an assertion
// failure if something goes wrong.
buffer_id: FilterOpGraphPictureBufferId::BufferId(-1),
subregion: LayoutRect::zero(), // Always overridden
offset: LayoutVector2D::zero(),
inflate: 0,
source_padding: LayoutRect::zero(),
target_padding: LayoutRect::zero(),
}; BUFFER_LIMIT];
let mut filters: Vec<(FilterGraphNode, FilterGraphOp)> = Vec::new();
filters.reserve(BUFFER_LIMIT);
for (original_id, parsefilter) in filter_ops.iter().enumerate() {
match parsefilter {
Filter::SVGGraphNode(parsenode, op) => {
if filters.len() >= BUFFER_LIMIT {
// If the DAG is too large we drop it entirely, the spec
// allows this.
return source;
}
// We need to offset the subregion by the stacking context
// offset or we'd be in the wrong coordinate system, prims
// are already offset by this same amount.
let clip_region = parsenode.subregion
.translate(context_offset);
let mut newnode = FilterGraphNode {
kept_by_optimizer: false,
linear: parsenode.linear,
inflate: SVGFE_INFLATE,
inputs: Vec::new(),
subregion: clip_region,
};
// Initialize remapped versions of the inputs, this is
// done here to share code between the enum variants.
let mut remapped_inputs: Vec<FilterGraphPictureReference> = Vec::new();
remapped_inputs.reserve_exact(parsenode.inputs.len());
for input in &parsenode.inputs {
match input.buffer_id {
FilterOpGraphPictureBufferId::BufferId(buffer_id) => {
// Reference to earlier node output, if this
// is None, it's a bug
let pic = *reference_for_buffer_id
.get(buffer_id as usize)
.expect("BufferId not valid?");
// We have to adjust the subregion and
// padding based on the input offset for
// feOffset ops, the padding may be inflated
// further by other ops such as blurs below.
let offset = input.offset;
let subregion = pic.subregion
.translate(offset);
let source_padding = LayoutRect::zero()
.translate(-offset);
let target_padding = LayoutRect::zero()
.translate(offset);
remapped_inputs.push(
FilterGraphPictureReference {
buffer_id: pic.buffer_id,
subregion,
offset,
inflate: pic.inflate,
source_padding,
target_padding,
});
}
FilterOpGraphPictureBufferId::None => panic!("Unsupported FilterOpGraphPictureBufferId"),
}
}
fn union_unchecked(a: LayoutRect, b: LayoutRect) -> LayoutRect {
let mut r = a;
if r.min.x > b.min.x {r.min.x = b.min.x}
if r.min.y > b.min.y {r.min.y = b.min.y}
if r.max.x < b.max.x {r.max.x = b.max.x}
if r.max.y < b.max.y {r.max.y = b.max.y}
r
}
match op {
FilterGraphOp::SVGFEFlood{..} |
FilterGraphOp::SVGFESourceAlpha |
FilterGraphOp::SVGFESourceGraphic |
FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{..} |
FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithStitching{..} |
FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{..} |
FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching{..} => {
assert!(remapped_inputs.len() == 0);
filters.push((newnode.clone(), op.clone()));
}
FilterGraphOp::SVGFEColorMatrix{..} |
FilterGraphOp::SVGFEIdentity |
FilterGraphOp::SVGFEImage{..} |
FilterGraphOp::SVGFEOpacity{..} |
FilterGraphOp::SVGFEToAlpha => {
assert!(remapped_inputs.len() == 1);
newnode.inputs = remapped_inputs;
filters.push((newnode.clone(), op.clone()));
}
FilterGraphOp::SVGFEComponentTransfer => {
assert!(remapped_inputs.len() == 1);
// Convert to SVGFEComponentTransferInterned
let filter_data =
&filter_datas[current_filter_data_index];
let filter_data = filter_data.sanitize();
current_filter_data_index = current_filter_data_index + 1;
// filter data is 4KiB of gamma ramps used
// only by SVGFEComponentTransferWithHandle.
//
// The gamma ramps are interleaved as RGBA32F
// pixels (unlike in regular ComponentTransfer,
// where the values are not interleaved), so
// r_values[3] is the alpha of the first color,
// not the 4th red value. This layout makes the
// shader more compatible with buggy compilers that
// do not like indexing components on a vec4.
let creates_pixels =
if let Some(a) = filter_data.r_values.get(3) {
*a != 0.0
} else {
false
};
let filter_data_key = SFilterDataKey {
data:
SFilterData {
r_func: SFilterDataComponent::from_functype_values(
filter_data.func_r_type, &filter_data.r_values),
g_func: SFilterDataComponent::from_functype_values(
filter_data.func_g_type, &filter_data.g_values),
b_func: SFilterDataComponent::from_functype_values(
filter_data.func_b_type, &filter_data.b_values),
a_func: SFilterDataComponent::from_functype_values(
filter_data.func_a_type, &filter_data.a_values),
},
};
let handle = self.interners
.filter_data
.intern(&filter_data_key, || ());
newnode.inputs = remapped_inputs;
filters.push((newnode.clone(), FilterGraphOp::SVGFEComponentTransferInterned{handle, creates_pixels}));
}
FilterGraphOp::SVGFEComponentTransferInterned{..} => unreachable!(),
FilterGraphOp::SVGFETile => {
assert!(remapped_inputs.len() == 1);
// feTile usually uses every pixel of input
remapped_inputs[0].source_padding =
LayoutRect::max_rect();
remapped_inputs[0].target_padding =
LayoutRect::max_rect();
newnode.inputs = remapped_inputs;
filters.push((newnode.clone(), op.clone()));
}
FilterGraphOp::SVGFEConvolveMatrixEdgeModeDuplicate{kernel_unit_length_x, kernel_unit_length_y, ..} |
FilterGraphOp::SVGFEConvolveMatrixEdgeModeNone{kernel_unit_length_x, kernel_unit_length_y, ..} |
FilterGraphOp::SVGFEConvolveMatrixEdgeModeWrap{kernel_unit_length_x, kernel_unit_length_y, ..} |
FilterGraphOp::SVGFEMorphologyDilate{radius_x: kernel_unit_length_x, radius_y: kernel_unit_length_y} => {
assert!(remapped_inputs.len() == 1);
let padding = LayoutSize::new(
kernel_unit_length_x.ceil(),
kernel_unit_length_y.ceil(),
);
// Add source padding to represent the kernel pixels
// needed relative to target pixels
remapped_inputs[0].source_padding =
remapped_inputs[0].source_padding
.inflate(padding.width, padding.height);
// Add target padding to represent the area affected
// by a source pixel
remapped_inputs[0].target_padding =
remapped_inputs[0].target_padding
.inflate(padding.width, padding.height);
newnode.inputs = remapped_inputs;
filters.push((newnode.clone(), op.clone()));
},
FilterGraphOp::SVGFEDiffuseLightingDistant{kernel_unit_length_x, kernel_unit_length_y, ..} |
FilterGraphOp::SVGFEDiffuseLightingPoint{kernel_unit_length_x, kernel_unit_length_y, ..} |
FilterGraphOp::SVGFEDiffuseLightingSpot{kernel_unit_length_x, kernel_unit_length_y, ..} |
FilterGraphOp::SVGFESpecularLightingDistant{kernel_unit_length_x, kernel_unit_length_y, ..} |
FilterGraphOp::SVGFESpecularLightingPoint{kernel_unit_length_x, kernel_unit_length_y, ..} |
FilterGraphOp::SVGFESpecularLightingSpot{kernel_unit_length_x, kernel_unit_length_y, ..} |
FilterGraphOp::SVGFEMorphologyErode{radius_x: kernel_unit_length_x, radius_y: kernel_unit_length_y} => {
assert!(remapped_inputs.len() == 1);
let padding = LayoutSize::new(
kernel_unit_length_x.ceil(),
kernel_unit_length_y.ceil(),
);
// Add source padding to represent the kernel pixels
// needed relative to target pixels
remapped_inputs[0].source_padding =
remapped_inputs[0].source_padding
.inflate(padding.width, padding.height);
// Add target padding to represent the area affected
// by a source pixel
remapped_inputs[0].target_padding =
remapped_inputs[0].target_padding
.inflate(padding.width, padding.height);
newnode.inputs = remapped_inputs;
filters.push((newnode.clone(), op.clone()));
},
FilterGraphOp::SVGFEDisplacementMap { scale, .. } => {
assert!(remapped_inputs.len() == 2);
let padding = LayoutSize::new(
scale.ceil(),
scale.ceil(),
);
// Add padding to both inputs for source and target
// rects, we might be able to skip some of these,
// but it's not that important to optimize here, a
// loose fit is fine.
remapped_inputs[0].source_padding =
remapped_inputs[0].source_padding
.inflate(padding.width, padding.height);
remapped_inputs[1].source_padding =
remapped_inputs[1].source_padding
.inflate(padding.width, padding.height);
remapped_inputs[0].target_padding =
remapped_inputs[0].target_padding
.inflate(padding.width, padding.height);
remapped_inputs[1].target_padding =
remapped_inputs[1].target_padding
.inflate(padding.width, padding.height);
newnode.inputs = remapped_inputs;
filters.push((newnode.clone(), op.clone()));
},
FilterGraphOp::SVGFEDropShadow{ dx, dy, std_deviation_x, std_deviation_y, .. } => {
assert!(remapped_inputs.len() == 1);
let padding = LayoutSize::new(
std_deviation_x.ceil() * BLUR_SAMPLE_SCALE,
std_deviation_y.ceil() * BLUR_SAMPLE_SCALE,
);
// Add source padding to represent the shadow
remapped_inputs[0].source_padding =
union_unchecked(
remapped_inputs[0].source_padding,
remapped_inputs[0].source_padding
.inflate(padding.width, padding.height)
.translate(
LayoutVector2D::new(-dx, -dy)
)
);
// Add target padding to represent the area needed
// to calculate pixels of the shadow
remapped_inputs[0].target_padding =
union_unchecked(
remapped_inputs[0].target_padding,
remapped_inputs[0].target_padding
.inflate(padding.width, padding.height)
.translate(
LayoutVector2D::new(*dx, *dy)
)
);
newnode.inputs = remapped_inputs;
filters.push((newnode.clone(), op.clone()));
},
FilterGraphOp::SVGFEGaussianBlur{std_deviation_x, std_deviation_y} => {
assert!(remapped_inputs.len() == 1);
let padding = LayoutSize::new(
std_deviation_x.ceil() * BLUR_SAMPLE_SCALE,
std_deviation_y.ceil() * BLUR_SAMPLE_SCALE,
);
// Add source padding to represent the blur
remapped_inputs[0].source_padding =
remapped_inputs[0].source_padding
.inflate(padding.width, padding.height);
// Add target padding to represent the blur
remapped_inputs[0].target_padding =
remapped_inputs[0].target_padding
.inflate(padding.width, padding.height);
newnode.inputs = remapped_inputs;
filters.push((newnode.clone(), op.clone()));
}
FilterGraphOp::SVGFEBlendColor |
FilterGraphOp::SVGFEBlendColorBurn |
FilterGraphOp::SVGFEBlendColorDodge |
FilterGraphOp::SVGFEBlendDarken |
FilterGraphOp::SVGFEBlendDifference |
FilterGraphOp::SVGFEBlendExclusion |
FilterGraphOp::SVGFEBlendHardLight |
FilterGraphOp::SVGFEBlendHue |
FilterGraphOp::SVGFEBlendLighten |
FilterGraphOp::SVGFEBlendLuminosity|
FilterGraphOp::SVGFEBlendMultiply |
FilterGraphOp::SVGFEBlendNormal |
FilterGraphOp::SVGFEBlendOverlay |
FilterGraphOp::SVGFEBlendSaturation |
FilterGraphOp::SVGFEBlendScreen |
FilterGraphOp::SVGFEBlendSoftLight |
FilterGraphOp::SVGFECompositeArithmetic{..} |
FilterGraphOp::SVGFECompositeATop |
FilterGraphOp::SVGFECompositeIn |
FilterGraphOp::SVGFECompositeLighter |
FilterGraphOp::SVGFECompositeOut |
FilterGraphOp::SVGFECompositeOver |
FilterGraphOp::SVGFECompositeXOR => {
assert!(remapped_inputs.len() == 2);
newnode.inputs = remapped_inputs;
filters.push((newnode.clone(), op.clone()));
}
}
// Set the reference remapping for the last (or only) node
// that we just pushed
let id = (filters.len() - 1) as i16;
if let Some(pic) = reference_for_buffer_id.get_mut(original_id as usize) {
*pic = FilterGraphPictureReference {
buffer_id: FilterOpGraphPictureBufferId::BufferId(id),
subregion: newnode.subregion,
offset: LayoutVector2D::zero(),
inflate: newnode.inflate,
source_padding: LayoutRect::zero(),
target_padding: LayoutRect::zero(),
};
}
}
_ => {
panic!("wrap_prim_with_filters: Mixed SVG and CSS filters?")
}
}
}
// Push a special output node at the end, this will correctly handle
// the final subregion, which may not have the same bounds as the
// surface it is being blitted into, so it needs to properly handle
// the cropping and UvRectKind, it also has no inflate.
if filters.len() >= BUFFER_LIMIT {
// If the DAG is too large we drop it entirely
return source;
}
let mut outputnode = FilterGraphNode {
kept_by_optimizer: true,
linear: false,
inflate: SVGFE_INFLATE_OUTPUT,
inputs: Vec::new(),
subregion: LayoutRect::max_rect(),
};
outputnode.inputs.push(reference_for_buffer_id[filter_ops.len() - 1]);
filters.push((
outputnode,
FilterGraphOp::SVGFEIdentity,
));
// We want to optimize the filter DAG and then wrap it in a single
// picture, we will use a custom RenderTask method to process the
// DAG later, there's not really an easy way to keep it as a series
// of pictures like CSS filters use.
//
// The main optimization we can do here is looking for feOffset
// filters we can merge away - because all of the node inputs
// support offset capability implicitly. We can also remove no-op
// filters (identity) if Gecko produced any.
//
// TODO: optimize the graph here
// Mark used graph nodes, starting at the last graph node, since
// this is a DAG in sorted order we can just iterate backwards and
// know we will find children before parents in order.
//
// Per SVG spec the last node (which is the first we encounter this
// way) is the final output, so its dependencies are what we want to
// mark as kept_by_optimizer
let mut kept_node_by_buffer_id = [false; BUFFER_LIMIT];
kept_node_by_buffer_id[filters.len() - 1] = true;
for (index, (node, _op)) in filters.iter_mut().enumerate().rev() {
let mut keep = false;
// Check if this node's output was marked to be kept
if let Some(k) = kept_node_by_buffer_id.get(index) {
if *k {
keep = true;
}
}
if keep {
// If this node contributes to the final output we need
// to mark its inputs as also contributing when they are
// encountered later
node.kept_by_optimizer = true;
for input in &node.inputs {
if let FilterOpGraphPictureBufferId::BufferId(id) = input.buffer_id {
if let Some(k) = kept_node_by_buffer_id.get_mut(id as usize) {
*k = true;
}
}
}
}
}
// Validate the DAG nature of the graph again - if we find anything
// wrong here it means the above code is bugged.
let mut invalid_dag = false;
for (id, (node, _op)) in filters.iter().enumerate() {
for input in &node.inputs {
if let FilterOpGraphPictureBufferId::BufferId(buffer_id) = input.buffer_id {
if buffer_id < 0 || buffer_id as usize >= id {
invalid_dag = true;
}
}
}
}
if invalid_dag {
log!(Level::Warn, "List of FilterOp::SVGGraphNode filter primitives appears to be invalid!");
for (id, (node, op)) in filters.iter().enumerate() {
log!(Level::Warn, " node: buffer=BufferId({}) op={} inflate={} subregion {:?} linear={} kept={}",
id, op.kind(), node.inflate,
node.subregion,
node.linear,
node.kept_by_optimizer,
);
for input in &node.inputs {
log!(Level::Warn, "input: buffer={} inflate={} subregion {:?} offset {:?} target_padding={:?} source_padding={:?}",
match input.buffer_id {
FilterOpGraphPictureBufferId::BufferId(id) => format!("BufferId({})", id),
FilterOpGraphPictureBufferId::None => "None".into(),
},
input.inflate,
input.subregion,
input.offset,
input.target_padding,
input.source_padding,
);
}
}
}
if invalid_dag {
// if the DAG is invalid, we can't render it
return source;
}
let composite_mode = PictureCompositeMode::SVGFEGraph(
filters,
);
source = source.add_picture(
composite_mode,
clip_node_id,
Picture3DContext::Out,
&mut self.interners,
&mut self.prim_store,
&mut self.prim_instances,
&mut self.clip_tree_builder,
);
return source;
}
// Handle regular CSS filter chains
for filter in &mut filter_ops {
let composite_mode = match filter {
Filter::ComponentTransfer => {
@ -3807,6 +4309,10 @@ impl<'a> SceneBuilder<'a> {
PictureCompositeMode::ComponentTransferFilter(handle)
}
}
Filter::SVGGraphNode(_, _) => {
// SVG filter graphs were handled above
panic!("SVGGraphNode encountered in regular CSS filter chain?");
}
_ => {
if filter.is_noop() {
continue;

Просмотреть файл

@ -8,6 +8,7 @@ use std::ops::Not;
// local imports
use crate::font;
use crate::{APZScrollGeneration, HasScrollLinkedEffect, PipelineId, PropertyBinding};
use crate::serde::{Serialize, Deserialize};
use crate::color::ColorF;
use crate::image::{ColorDepth, ImageKey};
use crate::units::*;
@ -1206,28 +1207,495 @@ impl FilterPrimitive {
}
}
/// CSS filter.
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize, PeekPoke)]
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize, PeekPoke)]
pub enum FilterOpGraphPictureBufferId {
#[default]
/// empty slot in feMerge inputs
None,
/// reference to another (earlier) node in filter graph
BufferId(i16),
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PeekPoke)]
pub struct FilterOpGraphPictureReference {
/// Id of the picture in question in a namespace unique to this filter DAG
pub buffer_id: FilterOpGraphPictureBufferId,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PeekPoke)]
pub struct FilterOpGraphNode {
/// True if color_interpolation_filter == LinearRgb; shader will convert
/// sRGB texture pixel colors on load and convert back on store, for correct
/// interpolation
pub linear: bool,
/// virtualized picture input binding 1 (i.e. texture source), typically
/// this is used, but certain filters do not use it
pub input: FilterOpGraphPictureReference,
/// virtualized picture input binding 2 (i.e. texture sources), only certain
/// filters use this
pub input2: FilterOpGraphPictureReference,
/// rect this node will render into, in filter space
pub subregion: LayoutRect,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Deserialize, Serialize, PeekPoke)]
pub enum FilterOp {
/// Filter that does no transformation of the colors, needed for
/// debug purposes only.
/// debug purposes, and is the default value in impl_default_for_enums.
/// parameters: none
/// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
Identity,
/// apply blur effect
/// parameters: stdDeviationX, stdDeviationY
/// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
Blur(f32, f32),
/// apply brightness effect
/// parameters: amount
/// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
Brightness(f32),
/// apply contrast effect
/// parameters: amount
/// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
Contrast(f32),
/// fade image toward greyscale version of image
/// parameters: amount
/// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
Grayscale(f32),
/// fade image toward hue-rotated version of image (rotate RGB around color wheel)
/// parameters: angle
/// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
HueRotate(f32),
/// fade image toward inverted image (1 - RGB)
/// parameters: amount
/// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
Invert(f32),
/// multiplies color and alpha by opacity
/// parameters: amount
/// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
Opacity(PropertyBinding<f32>, f32),
/// multiply saturation of colors
/// parameters: amount
/// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
Saturate(f32),
/// fade image toward sepia tone version of image
/// parameters: amount
/// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
Sepia(f32),
/// add drop shadow version of image to the image
/// parameters: shadow
/// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
DropShadow(Shadow),
/// transform color and alpha in image through 4x5 color matrix (transposed for efficiency)
/// parameters: matrix[5][4]
/// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
ColorMatrix([f32; 20]),
/// internal use - convert sRGB input to linear output
/// parameters: none
/// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
SrgbToLinear,
/// internal use - convert linear input to sRGB output
/// parameters: none
/// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
LinearToSrgb,
/// remap RGBA with color gradients and component swizzle
/// parameters: FilterData
/// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
ComponentTransfer,
/// replace image with a solid color
/// NOTE: UNUSED; Gecko never produces this filter
/// parameters: color
/// CSS filter semantics - operates on previous picture,uses sRGB space (non-linear)
Flood(ColorF),
/// Filter that copies the SourceGraphic image into the specified subregion,
/// This is intentionally the only way to get SourceGraphic into the graph,
/// as the filter region must be applied before it is used.
/// parameters: FilterOpGraphNode
/// SVG filter semantics - no inputs, no linear
SVGFESourceGraphic{node: FilterOpGraphNode},
/// Filter that copies the SourceAlpha image into the specified subregion,
/// This is intentionally the only way to get SourceGraphic into the graph,
/// as the filter region must be applied before it is used.
/// parameters: FilterOpGraphNode
/// SVG filter semantics - no inputs, no linear
SVGFESourceAlpha{node: FilterOpGraphNode},
/// Filter that does no transformation of the colors, used for subregion
/// cropping only.
SVGFEIdentity{node: FilterOpGraphNode},
/// represents CSS opacity property as a graph node like the rest of the SVGFE* filters
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
SVGFEOpacity{node: FilterOpGraphNode, valuebinding: PropertyBinding<f32>, value: f32},
/// convert a color image to an alpha channel - internal use; generated by
/// SVGFilterInstance::GetOrCreateSourceAlphaIndex().
SVGFEToAlpha{node: FilterOpGraphNode},
/// combine 2 images with SVG_FEBLEND_MODE_DARKEN
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement
SVGFEBlendDarken{node: FilterOpGraphNode},
/// combine 2 images with SVG_FEBLEND_MODE_LIGHTEN
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement
SVGFEBlendLighten{node: FilterOpGraphNode},
/// combine 2 images with SVG_FEBLEND_MODE_MULTIPLY
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement
SVGFEBlendMultiply{node: FilterOpGraphNode},
/// combine 2 images with SVG_FEBLEND_MODE_NORMAL
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement
SVGFEBlendNormal{node: FilterOpGraphNode},
/// combine 2 images with SVG_FEBLEND_MODE_SCREEN
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement
SVGFEBlendScreen{node: FilterOpGraphNode},
/// combine 2 images with SVG_FEBLEND_MODE_OVERLAY
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendOverlay{node: FilterOpGraphNode},
/// combine 2 images with SVG_FEBLEND_MODE_COLOR_DODGE
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendColorDodge{node: FilterOpGraphNode},
/// combine 2 images with SVG_FEBLEND_MODE_COLOR_BURN
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendColorBurn{node: FilterOpGraphNode},
/// combine 2 images with SVG_FEBLEND_MODE_HARD_LIGHT
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendHardLight{node: FilterOpGraphNode},
/// combine 2 images with SVG_FEBLEND_MODE_SOFT_LIGHT
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendSoftLight{node: FilterOpGraphNode},
/// combine 2 images with SVG_FEBLEND_MODE_DIFFERENCE
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendDifference{node: FilterOpGraphNode},
/// combine 2 images with SVG_FEBLEND_MODE_EXCLUSION
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendExclusion{node: FilterOpGraphNode},
/// combine 2 images with SVG_FEBLEND_MODE_HUE
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendHue{node: FilterOpGraphNode},
/// combine 2 images with SVG_FEBLEND_MODE_SATURATION
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendSaturation{node: FilterOpGraphNode},
/// combine 2 images with SVG_FEBLEND_MODE_COLOR
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendColor{node: FilterOpGraphNode},
/// combine 2 images with SVG_FEBLEND_MODE_LUMINOSITY
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
SVGFEBlendLuminosity{node: FilterOpGraphNode},
/// transform colors of image through 5x4 color matrix (transposed for efficiency)
/// parameters: FilterOpGraphNode, matrix[5][4]
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feColorMatrixElement
SVGFEColorMatrix{node: FilterOpGraphNode, values: [f32; 20]},
/// transform colors of image through configurable gradients with component swizzle
/// parameters: FilterOpGraphNode, FilterData
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feComponentTransferElement
SVGFEComponentTransfer{node: FilterOpGraphNode},
/// composite 2 images with chosen composite mode with parameters for that mode
/// parameters: FilterOpGraphNode, k1, k2, k3, k4
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement
SVGFECompositeArithmetic{node: FilterOpGraphNode, k1: f32, k2: f32, k3: f32,
k4: f32},
/// composite 2 images with chosen composite mode with parameters for that mode
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement
SVGFECompositeATop{node: FilterOpGraphNode},
/// composite 2 images with chosen composite mode with parameters for that mode
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement
SVGFECompositeIn{node: FilterOpGraphNode},
/// composite 2 images with chosen composite mode with parameters for that mode
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Docs: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feComposite
SVGFECompositeLighter{node: FilterOpGraphNode},
/// composite 2 images with chosen composite mode with parameters for that mode
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement
SVGFECompositeOut{node: FilterOpGraphNode},
/// composite 2 images with chosen composite mode with parameters for that mode
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement
SVGFECompositeOver{node: FilterOpGraphNode},
/// composite 2 images with chosen composite mode with parameters for that mode
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement
SVGFECompositeXOR{node: FilterOpGraphNode},
/// transform image through convolution matrix of up to 25 values (spec
/// allows more but for performance reasons we do not)
/// parameters: FilterOpGraphNode, orderX, orderY, kernelValues[25],
/// divisor, bias, targetX, targetY, kernelUnitLengthX, kernelUnitLengthY,
/// preserveAlpha
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feConvolveMatrixElement
SVGFEConvolveMatrixEdgeModeDuplicate{node: FilterOpGraphNode, order_x: i32,
order_y: i32, kernel: [f32; 25], divisor: f32, bias: f32, target_x: i32,
target_y: i32, kernel_unit_length_x: f32, kernel_unit_length_y: f32,
preserve_alpha: i32},
/// transform image through convolution matrix of up to 25 values (spec
/// allows more but for performance reasons we do not)
/// parameters: FilterOpGraphNode, orderX, orderY, kernelValues[25],
/// divisor, bias, targetX, targetY, kernelUnitLengthX, kernelUnitLengthY,
/// preserveAlpha
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feConvolveMatrixElement
SVGFEConvolveMatrixEdgeModeNone{node: FilterOpGraphNode, order_x: i32,
order_y: i32, kernel: [f32; 25], divisor: f32, bias: f32, target_x: i32,
target_y: i32, kernel_unit_length_x: f32, kernel_unit_length_y: f32,
preserve_alpha: i32},
/// transform image through convolution matrix of up to 25 values (spec
/// allows more but for performance reasons we do not)
/// parameters: FilterOpGraphNode, orderX, orderY, kernelValues[25],
/// divisor, bias, targetX, targetY, kernelUnitLengthX, kernelUnitLengthY,
/// preserveAlpha
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#feConvolveMatrixElement
SVGFEConvolveMatrixEdgeModeWrap{node: FilterOpGraphNode, order_x: i32,
order_y: i32, kernel: [f32; 25], divisor: f32, bias: f32, target_x: i32,
target_y: i32, kernel_unit_length_x: f32, kernel_unit_length_y: f32,
preserve_alpha: i32},
/// calculate lighting based on heightmap image with provided values for a
/// distant light source with specified direction
/// parameters: FilterOpGraphNode, surfaceScale, diffuseConstant,
/// kernelUnitLengthX, kernelUnitLengthY, azimuth, elevation
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDiffuseLightingElement
/// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDistantLightElement
SVGFEDiffuseLightingDistant{node: FilterOpGraphNode, surface_scale: f32,
diffuse_constant: f32, kernel_unit_length_x: f32,
kernel_unit_length_y: f32, azimuth: f32, elevation: f32},
/// calculate lighting based on heightmap image with provided values for a
/// point light source at specified location
/// parameters: FilterOpGraphNode, surfaceScale, diffuseConstant,
/// kernelUnitLengthX, kernelUnitLengthY, x, y, z
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDiffuseLightingElement
/// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEPointLightElement
SVGFEDiffuseLightingPoint{node: FilterOpGraphNode, surface_scale: f32,
diffuse_constant: f32, kernel_unit_length_x: f32,
kernel_unit_length_y: f32, x: f32, y: f32, z: f32},
/// calculate lighting based on heightmap image with provided values for a
/// spot light source at specified location pointing at specified target
/// location with specified hotspot sharpness and cone angle
/// parameters: FilterOpGraphNode, surfaceScale, diffuseConstant,
/// kernelUnitLengthX, kernelUnitLengthY, x, y, z, pointsAtX, pointsAtY,
/// pointsAtZ, specularExponent, limitingConeAngle
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDiffuseLightingElement
/// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpotLightElement
SVGFEDiffuseLightingSpot{node: FilterOpGraphNode, surface_scale: f32,
diffuse_constant: f32, kernel_unit_length_x: f32,
kernel_unit_length_y: f32, x: f32, y: f32, z: f32, points_at_x: f32,
points_at_y: f32, points_at_z: f32, cone_exponent: f32,
limiting_cone_angle: f32},
/// calculate a distorted version of first input image using offset values
/// from second input image at specified intensity
/// parameters: FilterOpGraphNode, scale, xChannelSelector, yChannelSelector
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDisplacementMapElement
SVGFEDisplacementMap{node: FilterOpGraphNode, scale: f32,
x_channel_selector: u32, y_channel_selector: u32},
/// create and merge a dropshadow version of the specified image's alpha
/// channel with specified offset and blur radius
/// parameters: FilterOpGraphNode, flood_color, flood_opacity, dx, dy,
/// stdDeviationX, stdDeviationY
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDropShadowElement
SVGFEDropShadow{node: FilterOpGraphNode, color: ColorF, dx: f32, dy: f32,
std_deviation_x: f32, std_deviation_y: f32},
/// synthesize a new image of specified size containing a solid color
/// parameters: FilterOpGraphNode, color
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEFloodElement
SVGFEFlood{node: FilterOpGraphNode, color: ColorF},
/// create a blurred version of the input image
/// parameters: FilterOpGraphNode, stdDeviationX, stdDeviationY
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEGaussianBlurElement
SVGFEGaussianBlur{node: FilterOpGraphNode, std_deviation_x: f32, std_deviation_y: f32},
/// synthesize a new image based on a url (i.e. blob image source)
/// parameters: FilterOpGraphNode, sampling_filter (see SamplingFilter in Types.h), transform
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEImageElement
SVGFEImage{node: FilterOpGraphNode, sampling_filter: u32, matrix: [f32; 6]},
/// create a new image based on the input image with the contour stretched
/// outward (dilate operator)
/// parameters: FilterOpGraphNode, radiusX, radiusY
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEMorphologyElement
SVGFEMorphologyDilate{node: FilterOpGraphNode, radius_x: f32, radius_y: f32},
/// create a new image based on the input image with the contour shrunken
/// inward (erode operator)
/// parameters: FilterOpGraphNode, radiusX, radiusY
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEMorphologyElement
SVGFEMorphologyErode{node: FilterOpGraphNode, radius_x: f32, radius_y: f32},
/// create a new image that is a scrolled version of the input image, this
/// is basically a no-op as we support offset in the graph node
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEOffsetElement
SVGFEOffset{node: FilterOpGraphNode, offset_x: f32, offset_y: f32},
/// calculate lighting based on heightmap image with provided values for a
/// distant light source with specified direction
/// parameters: FilerData, surfaceScale, specularConstant, specularExponent,
/// kernelUnitLengthX, kernelUnitLengthY, azimuth, elevation
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpecularLightingElement
/// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDistantLightElement
SVGFESpecularLightingDistant{node: FilterOpGraphNode, surface_scale: f32,
specular_constant: f32, specular_exponent: f32,
kernel_unit_length_x: f32, kernel_unit_length_y: f32, azimuth: f32,
elevation: f32},
/// calculate lighting based on heightmap image with provided values for a
/// point light source at specified location
/// parameters: FilterOpGraphNode, surfaceScale, specularConstant,
/// specularExponent, kernelUnitLengthX, kernelUnitLengthY, x, y, z
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpecularLightingElement
/// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEPointLightElement
SVGFESpecularLightingPoint{node: FilterOpGraphNode, surface_scale: f32,
specular_constant: f32, specular_exponent: f32,
kernel_unit_length_x: f32, kernel_unit_length_y: f32, x: f32, y: f32,
z: f32},
/// calculate lighting based on heightmap image with provided values for a
/// spot light source at specified location pointing at specified target
/// location with specified hotspot sharpness and cone angle
/// parameters: FilterOpGraphNode, surfaceScale, specularConstant,
/// specularExponent, kernelUnitLengthX, kernelUnitLengthY, x, y, z,
/// pointsAtX, pointsAtY, pointsAtZ, specularExponent, limitingConeAngle
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpecularLightingElement
/// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpotLightElement
SVGFESpecularLightingSpot{node: FilterOpGraphNode, surface_scale: f32,
specular_constant: f32, specular_exponent: f32,
kernel_unit_length_x: f32, kernel_unit_length_y: f32, x: f32, y: f32,
z: f32, points_at_x: f32, points_at_y: f32, points_at_z: f32,
cone_exponent: f32, limiting_cone_angle: f32},
/// create a new image based on the input image, repeated throughout the
/// output rectangle
/// parameters: FilterOpGraphNode
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETileElement
SVGFETile{node: FilterOpGraphNode},
/// synthesize a new image based on Fractal Noise (Perlin) with the chosen
/// stitching mode
/// parameters: FilterOpGraphNode, baseFrequencyX, baseFrequencyY,
/// numOctaves, seed
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETurbulenceElement
SVGFETurbulenceWithFractalNoiseWithNoStitching{node: FilterOpGraphNode,
base_frequency_x: f32, base_frequency_y: f32, num_octaves: u32,
seed: u32},
/// synthesize a new image based on Fractal Noise (Perlin) with the chosen
/// stitching mode
/// parameters: FilterOpGraphNode, baseFrequencyX, baseFrequencyY,
/// numOctaves, seed
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETurbulenceElement
SVGFETurbulenceWithFractalNoiseWithStitching{node: FilterOpGraphNode,
base_frequency_x: f32, base_frequency_y: f32, num_octaves: u32,
seed: u32},
/// synthesize a new image based on Turbulence Noise (offset vectors)
/// parameters: FilterOpGraphNode, baseFrequencyX, baseFrequencyY,
/// numOctaves, seed
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETurbulenceElement
SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{node: FilterOpGraphNode,
base_frequency_x: f32, base_frequency_y: f32, num_octaves: u32,
seed: u32},
/// synthesize a new image based on Turbulence Noise (offset vectors)
/// parameters: FilterOpGraphNode, baseFrequencyX, baseFrequencyY,
/// numOctaves, seed
/// SVG filter semantics - selectable input(s), selectable between linear
/// (default) and sRGB color space for calculations
/// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETurbulenceElement
SVGFETurbulenceWithTurbulenceNoiseWithStitching{node: FilterOpGraphNode,
base_frequency_x: f32, base_frequency_y: f32, num_octaves: u32, seed: u32},
}
#[repr(u8)]
@ -1242,6 +1710,7 @@ pub enum ComponentTransferFuncType {
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct FilterData {
/// ComponentTransfer / SVGFEComponentTransfer
pub func_r_type: ComponentTransferFuncType,
pub r_values: Vec<f32>,
pub func_g_type: ComponentTransferFuncType,

Просмотреть файл

@ -79,6 +79,7 @@ pub fn get_shader_features(flags: ShaderFeatureFlags) -> ShaderFeatures {
"cs_border_segment",
"cs_border_solid",
"cs_svg_filter",
"cs_svg_filter_node",
] {
shaders.insert(name, vec![String::new()]);
}

Просмотреть файл

@ -1,3 +1,12 @@
fuzzy(2,10000) == svgfe-blenddarken-linear.yaml svgfe-blenddarken-linear-ref.yaml
fuzzy(2,10000) == svgfe-blendmultiply-linear.yaml svgfe-blendmultiply-linear-ref.yaml
fuzzy(2,10000) == svgfe-blendnormal-linear.yaml svgfe-blendnormal-linear-ref.yaml
fuzzy(2,10000) == svgfe-colormatrix.yaml svgfe-colormatrix-ref.yaml
fuzzy(4,10000) == svgfe-dropshadow.yaml svgfe-dropshadow-ref.yaml
fuzzy(2,10000) == svgfe-opacity-linear.yaml svgfe-opacity-linear-ref.yaml
fuzzy(2,10000) == svgfe-opacity.yaml svgfe-opacity-ref.yaml
fuzzy(2,10000) == svgfe-subregion-bigger.yaml svgfe-subregion-bigger-ref.yaml
fuzzy(2,10000) == svgfe-subregion-offset-stacking-context.yaml svgfe-subregion-offset-stacking-context-ref.yaml
== filter-grayscale.yaml filter-grayscale-ref.yaml
platform(linux,mac) == draw_calls(7) color_targets(7) alpha_targets(0) filter-blur.yaml filter-blur.png
platform(linux,mac) == filter-blur-downscale-fractional.yaml filter-blur-downscale-fractional.png

Просмотреть файл

@ -0,0 +1,9 @@
---
root:
items:
- type: stacking-context
bounds: [0, 0, 120, 120]
items:
- type: rect
bounds: [10, 10, 100, 100]
color: [164, 82, 95, 1]

Просмотреть файл

@ -0,0 +1,21 @@
---
root:
items:
- type: stacking-context
bounds: [0, 0, 120, 120]
filters:
- svgfe: SourceGraphic
- svgfe: SourceAlpha
- svgfe: flood
linear: true
subregion: [10, 10, 100, 100]
color: [192, 96, 128, 1]
- svgfe: blenddarken
linear: true
subregion: [0, 0, 120, 120]
in: 0
in2: 2
items:
- type: rect
bounds: [10, 10, 100, 100]
color: [128, 64, 32, 0.5]

Просмотреть файл

@ -0,0 +1,9 @@
---
root:
items:
- type: stacking-context
bounds: [0, 0, 120, 120]
items:
- type: rect
bounds: [10, 10, 100, 100]
color: [153, 70, 93, 1]

Просмотреть файл

@ -0,0 +1,21 @@
---
root:
items:
- type: stacking-context
bounds: [0, 0, 120, 120]
filters:
- svgfe: SourceGraphic
- svgfe: SourceAlpha
- svgfe: flood
linear: true
subregion: [10, 10, 100, 100]
color: [192, 96, 128, 1]
- svgfe: blendmultiply
linear: true
subregion: [10, 10, 100, 100]
in: 0
in2: 2
items:
- type: rect
bounds: [10, 10, 100, 100]
color: [128, 64, 32, 0.5]

Просмотреть файл

@ -0,0 +1,9 @@
---
root:
items:
- type: stacking-context
bounds: [0, 0, 120, 120]
items:
- type: rect
bounds: [10, 10, 100, 100]
color: [164, 82, 95, 1]

Просмотреть файл

@ -0,0 +1,21 @@
---
root:
items:
- type: stacking-context
bounds: [0, 0, 120, 120]
filters:
- svgfe: SourceGraphic
- svgfe: SourceAlpha
- svgfe: flood
linear: true
subregion: [10, 10, 100, 100]
color: [192, 96, 128, 1]
- svgfe: blendnormal
linear: true
subregion: [10, 10, 100, 100]
in: 0
in2: 2
items:
- type: rect
bounds: [10, 10, 100, 100]
color: [128, 64, 32, 0.5]

Просмотреть файл

@ -0,0 +1,10 @@
---
root:
items:
- type: stacking-context
bounds: [0, 0, 120, 120]
items:
- type: rect
bounds: [10, 10, 100, 100]
# note: this is sRGB converted to match linear result of sRGB 64*2,32*2,16*2
color: [90, 47, 26, 1]

Просмотреть файл

@ -0,0 +1,18 @@
---
root:
items:
- type: stacking-context
bounds: [0, 0, 120, 120]
filters:
- svgfe: SourceGraphic
- svgfe: SourceAlpha
- svgfe: colormatrix
linear: true,
subregion: [0, 0, 120, 120]
in: 0
# note this is transposed to be suitable for shader, not SVG order
matrix: [2,0,0,0, 0,2,0,0, 0,0,2,0, 0,0,0,1, 0,0,0,0]
items:
- type: rect
bounds: [10, 10, 100, 100]
color: [64, 32, 16, 1]

Просмотреть файл

@ -0,0 +1,16 @@
---
root:
items:
- type: stacking-context
bounds: [10, 10, 200, 200]
filters: drop-shadow([20, 20], 10, red)
items:
- type: stacking-context
bounds: [10, 10, 200, 200]
items:
- type: rect
bounds: [10, 10, 100, 100]
color: [127, 127, 127, 1]
- type: rect
bounds: [50, 30, 100, 60]
color: [192, 192, 192, 1]

Просмотреть файл

@ -0,0 +1,27 @@
---
root:
items:
- type: stacking-context
bounds: [10, 10, 510, 510]
filters:
- svgfe: SourceGraphic
- svgfe: SourceAlpha
- svgfe: dropshadow
linear: false
subregion: [-20, -20, 320, 320]
color: red
dx: 20
dy: 20
std_deviation_x: 10
std_deviation_y: 10
in: 0
items:
- type: stacking-context
bounds: [10, 10, 150, 110]
items:
- type: rect
bounds: [10, 10, 100, 100]
color: [127, 127, 127, 1]
- type: rect
bounds: [50, 30, 100, 60]
color: [192, 192, 192, 1]

Просмотреть файл

@ -0,0 +1,9 @@
---
root:
items:
- type: stacking-context
bounds: [0, 0, 120, 120]
items:
- type: rect
bounds: [10, 10, 100, 100]
color: [128, 64, 32, 0.5]

Просмотреть файл

@ -0,0 +1,20 @@
---
root:
items:
- type: stacking-context
bounds: [0, 0, 120, 120]
items:
- type: stacking-context
bounds: [10, 10, 100, 100]
filters:
- svgfe: SourceGraphic
- svgfe: SourceAlpha
- svgfe: opacity
linear: true,
subregion: [0, 0, 400, 400]
in: 0
value: 0.5
items:
- type: rect
bounds: [0, 0, 100, 100]
color: [128, 64, 32, 1]

Просмотреть файл

@ -0,0 +1,9 @@
---
root:
items:
- type: stacking-context
bounds: [0, 0, 100, 100]
items:
- type: rect
bounds: [0, 0, 100, 100]
color: [128, 0, 0, 0.5]

Просмотреть файл

@ -0,0 +1,17 @@
---
root:
items:
- type: stacking-context
bounds: [0, 0, 100, 100]
filters:
- svgfe: SourceGraphic
- svgfe: SourceAlpha
- svgfe: opacity
linear: false
subregion: [0, 0, 100, 100]
in: 0
value: 0.5
items:
- type: rect
bounds: [0, 0, 100, 100]
color: [128, 0, 0, 1]

Просмотреть файл

@ -0,0 +1,15 @@
---
root:
items:
- type: stacking-context
bounds: [0, 50, 200, 100]
items:
- type: rect
bounds: [10, 10, 80, 100]
color: [255, 0, 0, 0.7]
- type: rect
bounds: [50, 10, 80, 100]
color: [0, 255, 0, 0.7]
- type: rect
bounds: [90, 10, 80, 100]
color: [0, 0, 255, 0.7]

Просмотреть файл

@ -0,0 +1,35 @@
---
root:
items:
- type: stacking-context
bounds: [0, 50, 800, 800]
filters:
- svgfe: SourceGraphic
- svgfe: SourceAlpha
- svgfe: flood
linear: false
# this is expected to be at 10,60
subregion: [10, 10, 80, 100]
color: [255, 0, 0, 0.7]
- svgfe: flood
linear: false
# this is expected to be at 50,60
subregion: [50, 10, 80, 100]
color: [0, 255, 0, 0.7]
- svgfe: blendnormal
linear: false
# this is expected to be at 10,60
subregion: [10, 10, 160, 100]
in: 3
in2: 2
- svgfe: blendnormal
linear: false
# this is expected to be at 10,60
subregion: [10, 10, 160, 100]
in: 0
in2: 4
items:
- type: rect
# this is expected to be at 90,60
bounds: [90, 10, 80, 100]
color: [0, 0, 255, 0.7]

Просмотреть файл

@ -0,0 +1,21 @@
---
root:
items:
- type: stacking-context
bounds: [0, 0, 800, 800]
items:
- type: rect
bounds: [0, 200, 800, 5]
color: [64, 64, 64, 1]
- type: rect
bounds: [100, 500, 100, 100]
color: [0, 0, 255, 1]
- type: rect
bounds: [0, 500, 90, 5]
color: [0, 0, 255, 1]
- type: rect
bounds: [0, 0, 90, 5]
color: [255, 0, 0, 1]
- type: rect
bounds: [0, 300, 90, 5]
color: [0, 255, 0, 1]

Просмотреть файл

@ -0,0 +1,51 @@
---
root:
items:
- type: stacking-context
bounds: [0, 0, 800, 800]
items:
- type: stacking-context
bounds: [0, 200, 800, 800]
items:
- type: stacking-context
bounds: [0, 0, 800, 800]
filters:
- svgfe: SourceGraphic
- svgfe: SourceAlpha
- svgfe: flood
color: [64, 64, 64, 1]
# this is expected to show up at 0,200
subregion: [0, 0, 800, 5]
- svgfe: flood
color: [127, 127, 127, 1]
# this is expected to show up at 100,500 and be completely
# hidden by the blue rect (SourceGraphic)
subregion: [100, 300, 90, 100]
- svgfe: compositeover
linear: false
subregion: [0, 0, 800, 800]
in: 3
in2: 2
- svgfe: compositeover
linear: false
subregion: [0, 0, 800, 800]
in: 0
in2: 4
items:
- type: rect
# blue rect is expected to show up at 100,500, this should
# hide the gray rect
bounds: [100, 300, 100, 100]
color: [0, 0, 255, 1]
- type: rect
# this is expected to show up at 0,500
bounds: [0, 300, 90, 5]
color: [0, 0, 255, 1]
- type: rect
# this is expected to show up at 0,0
bounds: [0, 0, 90, 5]
color: [255, 0, 0, 1]
- type: rect
# this is expected to show up at 0,300
bounds: [0, 300, 90, 5]
color: [0, 255, 0, 1]

Просмотреть файл

@ -9,6 +9,7 @@ use std::str::FromStr;
use webrender::api::*;
use webrender::api::units::*;
use yaml_rust::{Yaml, YamlLoader};
use log::Level;
pub trait YamlHelper {
fn as_f32(&self) -> Option<f32>;
@ -33,6 +34,7 @@ pub trait YamlHelper {
fn as_transform_style(&self) -> Option<TransformStyle>;
fn as_raster_space(&self) -> Option<RasterSpace>;
fn as_clip_mode(&self) -> Option<ClipMode>;
fn as_graph_picture_reference(&self) -> Option<FilterOpGraphPictureReference>;
fn as_mix_blend_mode(&self) -> Option<MixBlendMode>;
fn as_filter_op(&self) -> Option<FilterOp>;
fn as_vec_filter_op(&self) -> Option<Vec<FilterOp>>;
@ -537,7 +539,335 @@ impl YamlHelper for Yaml {
self.as_str().and_then(StringEnum::from_str)
}
fn as_graph_picture_reference(&self) -> Option<FilterOpGraphPictureReference> {
match self.as_i64() {
Some(n) => Some(FilterOpGraphPictureReference{
buffer_id: FilterOpGraphPictureBufferId::BufferId(n as i16),
}),
None => None,
}
}
fn as_filter_op(&self) -> Option<FilterOp> {
if let Some(filter_op) = self["svgfe"].as_str() {
let subregion = self["subregion"].as_rect().unwrap_or(
LayoutRect::new(
LayoutPoint::new(0.0, 0.0),
LayoutPoint::new(1024.0, 1024.0),
));
let node = FilterOpGraphNode {
linear: self["linear"].as_bool().unwrap_or(true),
subregion,
input: self["in"].as_graph_picture_reference().unwrap_or(
FilterOpGraphPictureReference{
buffer_id: FilterOpGraphPictureBufferId::None,
}),
input2: self["in2"].as_graph_picture_reference().unwrap_or(
FilterOpGraphPictureReference{
buffer_id: FilterOpGraphPictureBufferId::None,
}),
};
let debug_print_input = |input: FilterOpGraphPictureReference| -> String {
match input.buffer_id {
FilterOpGraphPictureBufferId::BufferId(id) => format!("BufferId{}", id),
FilterOpGraphPictureBufferId::None => "None".into(),
}
};
log!(Level::Debug, "svgfe parsed: {} linear: {} in: {} in2: {} subregion: [{}, {}, {}, {}]",
filter_op, node.linear,
debug_print_input(node.input), debug_print_input(node.input2),
node.subregion.min.x, node.subregion.min.y, node.subregion.max.x, node.subregion.max.y,
);
return match filter_op {
"identity" => Some(FilterOp::SVGFEIdentity{node}),
"opacity" => {
let value = self["value"].as_f32().unwrap();
Some(FilterOp::SVGFEOpacity{node, valuebinding: value.into(), value})
},
"toalpha" => Some(FilterOp::SVGFEToAlpha{node}),
"blendcolor" => Some(FilterOp::SVGFEBlendColor{node}),
"blendcolorburn" => Some(FilterOp::SVGFEBlendColorBurn{node}),
"blendcolordodge" => Some(FilterOp::SVGFEBlendColorDodge{node}),
"blenddarken" => Some(FilterOp::SVGFEBlendDarken{node}),
"blenddifference" => Some(FilterOp::SVGFEBlendDifference{node}),
"blendexclusion" => Some(FilterOp::SVGFEBlendExclusion{node}),
"blendhardlight" => Some(FilterOp::SVGFEBlendHardLight{node}),
"blendhue" => Some(FilterOp::SVGFEBlendHue{node}),
"blendlighten" => Some(FilterOp::SVGFEBlendLighten{node}),
"blendluminosity" => Some(FilterOp::SVGFEBlendLuminosity{node}),
"blendmultiply" => Some(FilterOp::SVGFEBlendMultiply{node}),
"blendnormal" => Some(FilterOp::SVGFEBlendNormal{node}),
"blendoverlay" => Some(FilterOp::SVGFEBlendOverlay{node}),
"blendsaturation" => Some(FilterOp::SVGFEBlendSaturation{node}),
"blendscreen" => Some(FilterOp::SVGFEBlendScreen{node}),
"blendsoftlight" => Some(FilterOp::SVGFEBlendSoftLight{node}),
"colormatrix" => {
let m: Vec<f32> = self["matrix"].as_vec_f32().unwrap();
let mut matrix: [f32; 20] = [0.0; 20];
matrix.clone_from_slice(&m);
Some(FilterOp::SVGFEColorMatrix{node, values: matrix})
}
"componenttransfer" => Some(FilterOp::SVGFEComponentTransfer{node}),
"compositearithmetic" => {
let k: Vec<f32> = self["k"].as_vec_f32().unwrap();
Some(FilterOp::SVGFECompositeArithmetic{
node,
k1: k[0],
k2: k[1],
k3: k[2],
k4: k[3],
})
}
"compositeatop" => Some(FilterOp::SVGFECompositeATop{node}),
"compositein" => Some(FilterOp::SVGFECompositeIn{node}),
"compositelighter" => Some(FilterOp::SVGFECompositeLighter{node}),
"compositeout" => Some(FilterOp::SVGFECompositeOut{node}),
"compositeover" => Some(FilterOp::SVGFECompositeOver{node}),
"compositexor" => Some(FilterOp::SVGFECompositeXOR{node}),
"convolvematrixedgemodeduplicate" => {
let order_x = self["order_x"].as_i64().unwrap() as i32;
let order_y = self["order_y"].as_i64().unwrap() as i32;
let m: Vec<f32> = self["kernel"].as_vec_f32().unwrap();
let mut kernel: [f32; 25] = [0.0; 25];
kernel.clone_from_slice(&m);
let divisor = self["divisor"].as_f32().unwrap();
let bias = self["bias"].as_f32().unwrap();
let target_x = self["target_x"].as_i64().unwrap() as i32;
let target_y = self["target_y"].as_i64().unwrap() as i32;
let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap();
let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap();
let preserve_alpha = match self["preserve_alpha"].as_bool() {
Some(true) => 1,
Some(false) => 0,
_ => 1,
};
Some(FilterOp::SVGFEConvolveMatrixEdgeModeDuplicate{
node, order_x, order_y, kernel, divisor, bias,
target_x, target_y, kernel_unit_length_x,
kernel_unit_length_y, preserve_alpha})
},
"convolvematrixedgemodenone" => {
let order_x = self["order_x"].as_i64().unwrap() as i32;
let order_y = self["order_y"].as_i64().unwrap() as i32;
let m: Vec<f32> = self["kernel"].as_vec_f32().unwrap();
let mut kernel: [f32; 25] = [0.0; 25];
kernel.clone_from_slice(&m);
let divisor = self["divisor"].as_f32().unwrap();
let bias = self["bias"].as_f32().unwrap();
let target_x = self["target_x"].as_i64().unwrap() as i32;
let target_y = self["target_y"].as_i64().unwrap() as i32;
let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap();
let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap();
let preserve_alpha = match self["preserve_alpha"].as_bool() {
Some(true) => 1,
Some(false) => 0,
_ => 1,
};
Some(FilterOp::SVGFEConvolveMatrixEdgeModeNone{
node, order_x, order_y, kernel, divisor, bias,
target_x, target_y, kernel_unit_length_x,
kernel_unit_length_y, preserve_alpha})
},
"convolvematrixedgemodewrap" => {
let order_x = self["order_x"].as_i64().unwrap() as i32;
let order_y = self["order_y"].as_i64().unwrap() as i32;
let m: Vec<f32> = self["kernel"].as_vec_f32().unwrap();
let mut kernel: [f32; 25] = [0.0; 25];
kernel.clone_from_slice(&m);
let divisor = self["divisor"].as_f32().unwrap();
let bias = self["bias"].as_f32().unwrap();
let target_x = self["target_x"].as_i64().unwrap() as i32;
let target_y = self["target_y"].as_i64().unwrap() as i32;
let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap();
let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap();
let preserve_alpha = match self["preserve_alpha"].as_bool() {
Some(true) => 1,
Some(false) => 0,
_ => 1,
};
Some(FilterOp::SVGFEConvolveMatrixEdgeModeWrap{
node, order_x, order_y, kernel, divisor, bias,
target_x, target_y, kernel_unit_length_x,
kernel_unit_length_y, preserve_alpha})
},
"diffuselightingdistant" => {
let surface_scale = self["surface_scale"].as_f32().unwrap();
let diffuse_constant = self["diffuse_constant"].as_f32().unwrap();
let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap();
let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap();
let azimuth = self["azimuth"].as_f32().unwrap();
let elevation = self["elevation"].as_f32().unwrap();
Some(FilterOp::SVGFEDiffuseLightingDistant{
node, surface_scale, diffuse_constant,
kernel_unit_length_x, kernel_unit_length_y,
azimuth, elevation})
},
"diffuselightingpoint" => {
let surface_scale = self["surface_scale"].as_f32().unwrap();
let diffuse_constant = self["diffuse_constant"].as_f32().unwrap();
let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap();
let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap();
let x = self["x"].as_f32().unwrap();
let y = self["y"].as_f32().unwrap();
let z = self["z"].as_f32().unwrap();
Some(FilterOp::SVGFEDiffuseLightingPoint{
node, surface_scale, diffuse_constant,
kernel_unit_length_x, kernel_unit_length_y, x, y, z})
},
"diffuselightingspot" => {
let surface_scale = self["surface_scale"].as_f32().unwrap();
let diffuse_constant = self["diffuse_constant"].as_f32().unwrap();
let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap();
let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap();
let x = self["x"].as_f32().unwrap();
let y = self["y"].as_f32().unwrap();
let z = self["z"].as_f32().unwrap();
let points_at_x = self["points_at_x"].as_f32().unwrap();
let points_at_y = self["points_at_y"].as_f32().unwrap();
let points_at_z = self["points_at_z"].as_f32().unwrap();
let cone_exponent = self["cone_exponent"].as_f32().unwrap();
let limiting_cone_angle = self["limiting_cone_angle"].as_f32().unwrap();
Some(FilterOp::SVGFEDiffuseLightingSpot{
node, surface_scale, diffuse_constant,
kernel_unit_length_x, kernel_unit_length_y, x, y, z,
points_at_x, points_at_y, points_at_z, cone_exponent,
limiting_cone_angle})
},
"displacementmap" => {
let scale = self["scale"].as_f32().unwrap();
let x_channel_selector = self["x_channel_selector"].as_i64().unwrap() as u32;
let y_channel_selector = self["y_channel_selector"].as_i64().unwrap() as u32;
Some(FilterOp::SVGFEDisplacementMap{node, scale, x_channel_selector, y_channel_selector})
},
"dropshadow" => {
let color = self["color"].as_colorf().unwrap();
let dx = self["dx"].as_f32().unwrap();
let dy = self["dy"].as_f32().unwrap();
let std_deviation_x = self["std_deviation_x"].as_f32().unwrap();
let std_deviation_y = self["std_deviation_y"].as_f32().unwrap();
Some(FilterOp::SVGFEDropShadow{node, color, dx, dy, std_deviation_x, std_deviation_y})
},
"flood" => Some(FilterOp::SVGFEFlood{node, color: self["color"].as_colorf().unwrap()}),
"gaussianblur" => {
let std_deviation_x = self["std_deviation_x"].as_f32().unwrap();
let std_deviation_y = self["std_deviation_y"].as_f32().unwrap();
Some(FilterOp::SVGFEGaussianBlur{node, std_deviation_x, std_deviation_y})
},
"image" => {
let sampling_filter = match self["sampling_filter"].as_str() {
Some("GOOD") => 0,
Some("LINEAR") => 1,
Some("POINT") => 2,
_ => 0,
};
let m: Vec<f32> = self["matrix"].as_vec_f32().unwrap();
let mut matrix: [f32; 6] = [0.0; 6];
matrix.clone_from_slice(&m);
Some(FilterOp::SVGFEImage{node, sampling_filter, matrix})
},
"morphologydilate" => {
let radius_x = self["radius_x"].as_f32().unwrap();
let radius_y = self["radius_y"].as_f32().unwrap();
Some(FilterOp::SVGFEMorphologyDilate{node, radius_x, radius_y})
},
"morphologyerode" => {
let radius_x = self["radius_x"].as_f32().unwrap();
let radius_y = self["radius_y"].as_f32().unwrap();
Some(FilterOp::SVGFEMorphologyErode{node, radius_x, radius_y})
},
"offset" => {
let offset = self["offset"].as_vec_f32().unwrap();
Some(FilterOp::SVGFEOffset{node, offset_x: offset[0], offset_y: offset[1]})
},
"SourceAlpha" => Some(FilterOp::SVGFESourceAlpha{node}),
"SourceGraphic" => Some(FilterOp::SVGFESourceGraphic{node}),
"sourcealpha" => Some(FilterOp::SVGFESourceAlpha{node}),
"sourcegraphic" => Some(FilterOp::SVGFESourceGraphic{node}),
"specularlightingdistant" => {
let surface_scale = self["surface_scale"].as_f32().unwrap();
let specular_constant = self["specular_constant"].as_f32().unwrap();
let specular_exponent = self["specular_exponent"].as_f32().unwrap();
let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap();
let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap();
let azimuth = self["azimuth"].as_f32().unwrap();
let elevation = self["elevation"].as_f32().unwrap();
Some(FilterOp::SVGFESpecularLightingDistant{
node, surface_scale, specular_constant,
specular_exponent, kernel_unit_length_x,
kernel_unit_length_y, azimuth, elevation})
},
"specularlightingpoint" => {
let surface_scale = self["surface_scale"].as_f32().unwrap();
let specular_constant = self["specular_constant"].as_f32().unwrap();
let specular_exponent = self["specular_exponent"].as_f32().unwrap();
let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap();
let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap();
let x = self["x"].as_f32().unwrap();
let y = self["y"].as_f32().unwrap();
let z = self["z"].as_f32().unwrap();
Some(FilterOp::SVGFESpecularLightingPoint{
node, surface_scale, specular_constant,
specular_exponent, kernel_unit_length_x,
kernel_unit_length_y, x, y, z})
},
"specularlightingspot" => {
let surface_scale = self["surface_scale"].as_f32().unwrap();
let specular_constant = self["specular_constant"].as_f32().unwrap();
let specular_exponent = self["specular_exponent"].as_f32().unwrap();
let kernel_unit_length_x = self["kernel_unit_length_x"].as_f32().unwrap();
let kernel_unit_length_y = self["kernel_unit_length_y"].as_f32().unwrap();
let x = self["x"].as_f32().unwrap();
let y = self["y"].as_f32().unwrap();
let z = self["z"].as_f32().unwrap();
let points_at_x = self["points_at_x"].as_f32().unwrap();
let points_at_y = self["points_at_y"].as_f32().unwrap();
let points_at_z = self["points_at_z"].as_f32().unwrap();
let cone_exponent = self["cone_exponent"].as_f32().unwrap();
let limiting_cone_angle = self["limiting_cone_angle"].as_f32().unwrap();
Some(FilterOp::SVGFESpecularLightingSpot{
node, surface_scale, specular_constant,
specular_exponent, kernel_unit_length_x,
kernel_unit_length_y, x, y, z, points_at_x,
points_at_y, points_at_z, limiting_cone_angle,
cone_exponent})
},
"tile" => Some(FilterOp::SVGFETile{node}),
"turbulencewithfractalnoisewithnostitching" => {
let base_frequency_x = self["base_frequency_x"].as_f32().unwrap();
let base_frequency_y = self["base_frequency_y"].as_f32().unwrap();
let num_octaves = self["num_octaves"].as_i64().unwrap() as u32;
let seed = self["seed"].as_i64().unwrap() as u32;
Some(FilterOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{
node, base_frequency_x, base_frequency_y, num_octaves, seed})
},
"turbulencewithfractalnoisewithstitching" => {
let base_frequency_x = self["base_frequency_x"].as_f32().unwrap();
let base_frequency_y = self["base_frequency_y"].as_f32().unwrap();
let num_octaves = self["num_octaves"].as_i64().unwrap() as u32;
let seed = self["seed"].as_i64().unwrap() as u32;
Some(FilterOp::SVGFETurbulenceWithFractalNoiseWithStitching{
node, base_frequency_x, base_frequency_y, num_octaves, seed})
},
"turbulencewithturbulencenoisewithnostitching" => {
let base_frequency_x = self["base_frequency_x"].as_f32().unwrap();
let base_frequency_y = self["base_frequency_y"].as_f32().unwrap();
let num_octaves = self["num_octaves"].as_i64().unwrap() as u32;
let seed = self["seed"].as_i64().unwrap() as u32;
Some(FilterOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{
node, base_frequency_x, base_frequency_y, num_octaves, seed})
},
"turbulencewithturbulencenoisewithstitching" => {
let base_frequency_x = self["base_frequency_x"].as_f32().unwrap();
let base_frequency_y = self["base_frequency_y"].as_f32().unwrap();
let num_octaves = self["num_octaves"].as_i64().unwrap() as u32;
let seed = self["seed"].as_i64().unwrap() as u32;
Some(FilterOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching{
node, base_frequency_x, base_frequency_y, num_octaves, seed})
},
_ => None,
}
}
if let Some(s) = self.as_str() {
match parse_function(s) {
("identity", _, _) => {