Bug (including fix): CoreGraphicsContext::clipToPath() does not respect setUsingNonZeroWinding(false)


#1

observation:

I have the following code inside paint():

Path clipPath;
clipPath.setUsingNonZeroWinding(false);
clipPath.addEllipse(getLocalBounds().toFloat());
clipPath.addRoundedRectangle(getLocalBounds(), 5.0f);
g.reduceClipRegion(clipPath);
g.setColour(Colours::black);
g.fillAll();

The OpenGL renderer produces the following output:

image

The CoreGraphicsRenderer produces this:

image

expected: both renderers should produce (virtually) the same output.

discussion: CoreGraphicsContext::clipToPath() uses CGContextClip to set the clip path. CGContext clip uses “[…] the winding-number fill rule for deciding what’s inside the path”. But there is also CGContextEOClip which uses “[…] the even-odd fill rule for deciding what’s inside the path”. The documentation of setUsingNonZeroWinding() states: “If set to false, it uses an alternate-winding rule.”. Iff alternate-winding rule means the same as even-odd fill rule, then the following fix is proposed:

fix: change CoreGraphicsContext::clipToPath to:

void CoreGraphicsContext::clipToPath (const Path& path, const AffineTransform& transform)
{
    createPath (path, transform);
    if(path.isUsingNonZeroWinding())
        CGContextClip (context);
    else
        CGContextEOClip (context);
    lastClipRectIsValid = false;
}

Note: CoreGraphicsContext::fillPath() is doing:

...
if (path.isUsingNonZeroWinding())
    CGContextFillPath (context);
else
    CGContextEOFillPath (context);
...

Which further supports, that the suggested fix is correct.

@jules patch attachedfix_CoreGraphicsContext_clipToPath.mm (728 Bytes) (rename to .patch)


#2

Great, thanks for the heads-up, I’ll take a look at this today!