Bug 12571 - <clipPath> doesn't correctly handle <text> elements
Summary: <clipPath> doesn't correctly handle <text> elements
Alias: None
Product: WebKit
Classification: Unclassified
Component: SVG (show other bugs)
Version: 420+
Hardware: Mac OS X 10.4
: P2 Normal
Assignee: Dirk Schulze
URL: http://www.w3.org/Graphics/SVG/Test/2...
Depends on:
Reported: 2007-02-04 03:31 PST by Eric Seidel (no email)
Modified: 2010-04-13 10:07 PDT (History)
4 users (show)

See Also:

failing simple clipPath example (459 bytes, image/svg+xml)
2010-02-14 00:42 PST, Dirk Schulze
no flags Details
clipper on clipPath (1.32 KB, image/svg+xml)
2010-02-14 00:47 PST, Dirk Schulze
no flags Details
patch (20.77 KB, patch)
2010-03-19 15:23 PDT, Dirk Schulze
zimmermann: review-
Details | Formatted Diff | Diff
tests (1.89 MB, patch)
2010-03-19 15:24 PDT, Dirk Schulze
eric: review-
Details | Formatted Diff | Diff
patch (19.58 KB, patch)
2010-04-04 06:15 PDT, Dirk Schulze
zimmermann: review-
Details | Formatted Diff | Diff
tests (1.11 MB, patch)
2010-04-04 06:18 PDT, Dirk Schulze
zimmermann: review+
Details | Formatted Diff | Diff
patch (21.12 KB, patch)
2010-04-13 05:09 PDT, Dirk Schulze
zimmermann: review+
Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Eric Seidel (no email) 2007-02-04 03:31:49 PST
WebKit fails SVG text-clip test

Notice that the bottom text-clip is missing.
Comment 1 Eric Seidel (no email) 2007-06-04 22:35:56 PDT
Bleh.  Our current clipping code assumes that the contents of <clipPath> will only be path elements.  It needs to be smarter than that.  It needs to perhaps have a "fast path" where it builds a single combined path element and clips to that, but has fallback code to handle mask-based clipping when necessary.

Another option is to not build a "clip path" at all until it's actually used.  Still for speed it would be best to either create a single path to use for clipping, or to create a mask for clipping.

Either way, <clipPath> needs to be re-written to allow for clipping to text.  We also need to add tests to test clipping more complicated situations than the W3C suite currently handles.
Comment 2 Nikolas Zimmermann 2007-06-11 18:04:02 PDT
Ok, we discussed this.  Eric will fix.  Eric will remove all of the clipping pre-caching code, and just walk each of the clipPath children each time a clip is applied.  This will allow <text> elements to do something differenet at clip-time instead of trying to convert text into a path (which CG doesn't support).
Comment 3 Eric Seidel (no email) 2007-12-27 02:13:57 PST
Perhaps Rob would like to tackle this after fixing the <clipPath> <use> issue. :)
Comment 4 Dirk Schulze 2010-02-14 00:39:48 PST
(In reply to comment #1)
> Another option is to not build a "clip path" at all until it's actually used. 
> Still for speed it would be best to either create a single path to use for
> clipping, or to create a mask for clipping.

Thats the way I would prefer. Never the less. Our current implementation even fails on very simple cases (only paths, one clip-rule - I'll add one of this cases afterwards). 

So there might be no other way to allways use masking. I think even ff masks all the time, but they just have began to support animation. Maybe they also run into speed problems.
 I'll investigate more on it and hopefully find a way to hold the speed on "normal" cases: path+clip-rule:nonzero
Comment 5 Dirk Schulze 2010-02-14 00:42:18 PST
Created attachment 48714 [details]
failing simple clipPath example

This is a simple implementation, where we fail. There are just paths and the same clipping rule for every child of clipPath.
Comment 6 Dirk Schulze 2010-02-14 00:47:41 PST
Created attachment 48715 [details]
clipper on clipPath

Also clipPaths can be clipped. We don't support it yet. But I can imagine a testcase, like this one, but with 360 clippings (one per degree) to get performance data as a reference to e.g. opera. Or one clipping per degree and a slopy scale factor.
Comment 7 Dirk Schulze 2010-03-19 15:23:09 PDT
Created attachment 51191 [details]

This adds support for texts on clipPath, clipping on ClipPath and heterogenous clip rules. We now pass another test on the current W3C test suite and 4 addional tests on the upcoming test suite for SVG1.1SE.

Tests moved into another patch to shrink size.
Comment 8 Dirk Schulze 2010-03-19 15:24:25 PDT
Created attachment 51193 [details]

Tests for clip patch.
Comment 9 Nikolas Zimmermann 2010-03-21 11:55:52 PDT
Comment on attachment 51191 [details]

Hi Dirk,

some random notes:

1) can you comment why  RenderSVGResourceClipper::invalidate now only takes() the value out of the HashMap instead of actually deleting it?
Who's responsible to delete it? I see it's reused in calculateClipper() (which needs a rename to sth. more meaningful btw.. :-) - though what happens if you are invalidating a resource, that is never reused again? Won't your new code just leave the ClipperData object undeleted?

2) In RenderSVGResourceClipper::fastClipper (which should be renamed to sth. like pathOnlyClipping, instead of fast..) can you introduce local variable for RenderSVGSTyle & co instead of callling style()->svgStyle() several times...

3) I am slightly worried about the approach you've chosen to implement the non-fast-clipping mode, especially the RenderSttyle modifications... how about creating a copy of the RenderSTyle that can be safely mutated and stored inside the RenderSVGResourceClipper?

4) ClipperData doesn't need an empty default ctor

The general approach is _great_ though, glad you finally tried to fix that issue!
r- until all issues are resolved.
Comment 10 Eric Seidel (no email) 2010-04-01 23:36:07 PDT
Comment on attachment 51193 [details]

Please do the RenderSVGShadowTreeRootContainer rename change in a separate patch, first.  Before doing any of the rest of this.  That would make these changes much smaller (and then people could actually look at your new tests and decide if they are good or not).
Comment 11 Dirk Schulze 2010-04-04 06:15:56 PDT
Created attachment 52510 [details]

Corrected issues of Niko as far as possible. Checked that the dtor's of ImageBuffer get called, works. Tests are in the following patch.
Comment 12 Dirk Schulze 2010-04-04 06:18:04 PDT
Created attachment 52511 [details]

Following Erics suggestion and removed the ShadowTree name for DRT results. Only clip-path gets tested.
Comment 13 WebKit Review Bot 2010-04-04 06:25:19 PDT
Attachment 52511 [details] did not pass style-queue:

Failed to run "WebKitTools/Scripts/check-webkit-style" exit_code: 1
ERROR: File does not exist: LayoutTests/svg/custom/clip-path-display-none-child.svg

If any of these errors are false positives, please file a bug against check-webkit-style.
Comment 14 Chris Jerdonek 2010-04-08 00:26:46 PDT
(In reply to comment #13)
> Attachment 52511 [details] did not pass style-queue:
> Failed to run "WebKitTools/Scripts/check-webkit-style" exit_code: 1
> ERROR: File does not exist:
> LayoutTests/svg/custom/clip-path-display-none-child.svg

FYI, this was a bug in check-webkit-style that was fixed after this occurred:


(Eric just CC'ed me on this report.)
Comment 15 Nikolas Zimmermann 2010-04-08 00:51:34 PDT
Comment on attachment 52510 [details]

Moin Dirk,

I still have some questions, sorry for the number of iteration with this patch, but it's quite involved :-)

> +    // ClipPath gets clipped.
"If a clipPath gets clipped, we need to use image-based clipping" would be easier to understand.

Ok I see the approach below, pathOnlyClipping() should check wheter we can use path only clipping, or return false if not.
So you optimized for the fast-case, where we only have paths to clip. If there are more complex objects to clip, you'd waste time calling toClipPath() on the shape objects though. I think it's still reasonable to do that. If we ever see this is a bottleneck, we may want to introduce a seperated function which checks wheter path-only clipping is possible _in advance_. (I just remember Beth identifying toPathData() as a bottle-neck in a certain situation...) Hm, feel free to keep this approach for now, or introduce a new function "isPathOnlyClippingPossible".

> +    Path clipPath = Path();
> +    for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) {
> +        if (!svgStyle->clipPath().isEmpty())
> +            return false;
How about adding a comment that clip-path on children inside a <clipPath> disallow path-only clipping?

> +        if (clipPath.isEmpty()) {
> +            clipPath = styled->toClipPath();
> +            clipRule = svgStyle->clipRule();
> +        } else
> +            return false;

Can you explain that approach? You're initially setting clipPath to Path(), then assign a toClipPath() of the first path-only clipping child.
So when there are two <rect> as children of a <clipPath> you will fall-back to image-based clipping? Even when both share the same clip-rule?
Am I overlooking something?

> +    }
> +    if (static_cast<SVGClipPathElement*>(node())->clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
> +        AffineTransform obbTransform;
> +        obbTransform.translate(objectBoundingBox.x(), objectBoundingBox.y());
> +        obbTransform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
> +        clipPath.transform(obbTransform);
Please use "transform" as name, obbTransform looks awkward, and we shouldn't use abbrevations, and "objectBoundingBoxTransform" is way too long ;-)

> +    if (clipPath.isEmpty())
> +        clipPath.addRect(FloatRect());
I know why you need this, but a comment would help for others reading the code.

> +bool RenderSVGResourceClipper::applyClippingToContext(RenderObject* object, const FloatRect& objectBoundingBox,
> +                                                const FloatRect& repaintRect, GraphicsContext* context)
Can you align the "const' with the "RenderObject", looks slightly better.

> +        // Save the old RenderStyle of the current object for restoring after drawing
> +        // it to the MaskImage. The new intermediate RenderStyle needs to inherit from
> +        // the old one.
> +        RefPtr<RenderStyle> oldRenderStyle = RenderStyle::clone(renderer->style());
> +        RefPtr<RenderStyle> newRenderStyle = RenderStyle::clone(renderer->style());
That puzzles me. Why do you need two clones? Can't you just grab the old renderer->style() and stash it into a RefPtr<RenderStyle>? I guess it's a copy/paste error.
Can you check wheter a cloned RenderStyle is automatically "unique" (There's a setUnique() method on RenderStyle, preventing this style to be shared across different renderers, we have to be sure that happens, otherwhise we'll get in trouble)

> +
> +        if (isUseElement)
> +            renderer = childNode->renderer();
A comment regarding the special <use> handling would help here as well.

Other than that, looks great to me. Hope we can resolve the remaining issues quickly...

Comment 16 Dirk Schulze 2010-04-13 05:09:16 PDT
Created attachment 53238 [details]

Added some more comments, so that the code should be more understandable. Fixed the clone issue of RenderStyle and updated the patch to trunk.
Comment 17 Nikolas Zimmermann 2010-04-13 05:36:56 PDT
Comment on attachment 52511 [details]

Repeat after me: 33 tests. W o w.
Comment 18 Nikolas Zimmermann 2010-04-13 05:37:54 PDT
Comment on attachment 53238 [details]

Patch is nice, comments contains some typos though and could be clearer. Talked to Dirk who will those issues before landing, r=me.
Comment 19 Dirk Schulze 2010-04-13 10:07:46 PDT
Landed in http://trac.webkit.org/changeset/57511.