w3c / fxtf-drafts

Mirror of https://hg.fxtf.org/drafts
https://drafts.fxtf.org/
Other
69 stars 49 forks source link

[filter-effects-1] feDropShadowElement definition does not match implementations #343

Open cbrewster opened 5 years ago

cbrewster commented 5 years ago

Spec: https://drafts.fxtf.org/filter-effects/#feDropShadowElement According to the spec for <feDropShadow>, it's result should be equivalent to a combination of other filter primitives; however, this is not always the case in browser implementations.

Example Case:

<svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 1024 1024">
    <defs>
        <filter id="specDropShadow">
            <feGaussianBlur in="SourceAlpha" stdDeviation="30"/>
            <feOffset dx="50" dy="50" result="offsetblur"/>
            <feFlood flood-color="green" flood-opacity="1"/>
            <feComposite in2="offsetblur" operator="in"/>
            <feMerge>
                <feMergeNode/>
                <feMergeNode in="SourceGraphic"/>
            </feMerge>
        </filter>
        <filter id="dropShadow">
            <feDropShadow stdDeviation="30" dx="50" dy="50" flood-color="green"/>
        </filter>
    </defs>
    <circle fill="yellow" cx="75" cy="75" r="70" filter="url(#specDropShadow)" />
    <circle fill="yellow" cx="250" cy="75" r="70" filter="url(#dropShadow)" />
</svg>

Result (Firefox 67; Safari and Chrome produce a similar result): image

In this example case, the left circle is the drop shadow using a combination of filter primitives defined by the spec to be equivalent to the feDropShadow primitive and the right circle is using the actual feDropShadow primitive.

Clipping occurs after the first feGaussianBlur, which becomes visible after the blur has been offset; however, most browser implementations don't appear to clip the blur before offsetting.

AmeliaBR commented 5 years ago

Because it's a single primitive, the clipping should logically be performed at the end of the operation. And that's a nicer result for authors, anyway. But this means that the equivalent representation needs to include calculations for the filter region of the intermediate steps that is large enough to avoid premature clipping.

The following code (apply the offset prior to blurring, and set an explicit filter region on the offset to avoid clipping at that stage) creates a fairly close rendering match in Chrome (the blurring algorithm isn't exactly the same, but the blurring algorithm is also not defined in the spec):

        <filter id="proposedDropShadow">
            <feOffset in2="SourceAlpha" dx="50" dy="50"
                      width="calc(120% + 50)" height="calc(120% + 50)"/>
            <feGaussianBlur stdDeviation="30" result="offsetblur" />
            <feFlood flood-color="green" flood-opacity="1"/>
            <feComposite in2="offsetblur" operator="in"/>
            <feMerge>
                <feMergeNode/>
                <feMergeNode in="SourceGraphic"/>
            </feMerge>
        </filter>

But… Firefox still clips the feOffset result, even if I replace the calc() expressions with equivalent large single values (e.g., 250% or 500px). But maybe that's worth a separate issue discussion.

cbrewster commented 5 years ago

https://drafts.fxtf.org/filter-effects/#FilterPrimitiveSubRegion This specifies that the filter primitive subregion can't ever be larger than the filter region:

All intermediate offscreens are defined to not exceed the intersection of the filter primitive subregion with the filter region. The filter region and any of the filter primitive subregions are to be set up such that all offscreens are made big enough to accommodate any pixels which even partly intersect with either the filter region or the filter primitive subregions.

Also filter primitive inputs are supposed to be clipped by the filter region:

The filter region acts as a hard clip clipping rectangle on the filter primitive’s input image(s).

If I am understanding this correctly, it seem like FireFox would be following the spec here.

Maybe the filter region could also be increased and the feMerge primitive could have a subregion that clips back to the original size?