зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1824502 - Implement native SVG filter rendering in WebRender r=gw
Differential Revision: https://phabricator.services.mozilla.com/D209646
This commit is contained in:
Родитель
bba28ff2f5
Коммит
82db1e3cc5
|
@ -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", _, _) => {
|
||||
|
|
Загрузка…
Ссылка в новой задаче