RESOLVED FIXED 202815
[FTW] Correct radial gradient handling of various radius orderings
https://bugs.webkit.org/show_bug.cgi?id=202815
Summary [FTW] Correct radial gradient handling of various radius orderings
Brent Fulgham
Reported 2019-10-10 11:21:08 PDT
The initial Direct2D Radial Gradient implementation did not properly handle cases where the order of the two radius arguments were not small-to-large. This led to incorrect drawing in some cases tested in the 'canvas/Philip' test set. This patch handles these cases.
Attachments
Patch (4.35 KB, patch)
2019-10-10 11:27 PDT, Brent Fulgham
no flags
Patch (4.25 KB, patch)
2019-10-16 09:31 PDT, Brent Fulgham
no flags
Patch (5.12 KB, patch)
2019-10-16 17:32 PDT, Brent Fulgham
no flags
Brent Fulgham
Comment 1 2019-10-10 11:27:27 PDT
Brent Fulgham
Comment 2 2019-10-10 11:29:03 PDT
This corrects three radius gradient test cases.
Per Arne Vollan
Comment 3 2019-10-10 13:18:31 PDT
Comment on attachment 380664 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=380664&action=review > Source/WebCore/platform/graphics/win/GradientDirect2D.cpp:137 > - [&] (const RadialData& data) { > + [&] (RadialData& data) { It does not seem like the data parameter is modified, can we keep the const modifier?
Brent Fulgham
Comment 4 2019-10-10 14:07:10 PDT
Comment on attachment 380664 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=380664&action=review >> Source/WebCore/platform/graphics/win/GradientDirect2D.cpp:137 >> + [&] (RadialData& data) { > > It does not seem like the data parameter is modified, can we keep the const modifier? Yes. I modified while experimenting with another change, and forgot to put this back. I'll fix it.
Per Arne Vollan
Comment 5 2019-10-10 17:57:36 PDT
Comment on attachment 380664 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=380664&action=review > Source/WebCore/platform/graphics/win/GradientDirect2D.cpp:99 > + std::stable_sort(gradientStops.begin(), gradientStops.end(), [](const D2D1_GRADIENT_STOP& a, const D2D1_GRADIENT_STOP& b) { > + return a.position < b.position; > + }); Is sorting needed here, or is it sufficient to reverse the list of gradient stops?
Fujii Hironori
Comment 6 2019-10-10 18:52:24 PDT
Comment on attachment 380664 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=380664&action=review > Source/WebCore/platform/graphics/win/GradientDirect2D.cpp:79 > if (data.startRadius && data.endRadius) { Which test cases are using this code path? I can't find code setting startRadius and endRadius. Can they be negative values?
Fujii Hironori
Comment 7 2019-10-10 18:54:26 PDT
Comment on attachment 380664 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=380664&action=review > Source/WebCore/PlatformFTW.cmake:3 > +else () Use NOT. And, indent the the inside. if (NOT APPLE) include(platform/ImageDecoders.cmake) endif ()
Fujii Hironori
Comment 8 2019-10-10 18:58:41 PDT
Comment on attachment 380664 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=380664&action=review >> Source/WebCore/platform/graphics/win/GradientDirect2D.cpp:79 >> if (data.startRadius && data.endRadius) { > > Which test cases are using this code path? > I can't find code setting startRadius and endRadius. > Can they be negative values? I found it. It is canvas.createRadialGradient. https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/createRadialGradient negative values are checked in CanvasRenderingContext2DBase::createRadialGradient.
Fujii Hironori
Comment 9 2019-10-10 19:16:43 PDT
Comment on attachment 380664 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=380664&action=review > Source/WebCore/platform/graphics/win/GradientDirect2D.cpp:83 > startPosition = 1.0 / startPosition; Can startPosition be zero? Then, zero-devision can happen.
Fujii Hironori
Comment 10 2019-10-10 19:25:10 PDT
Comment on attachment 380664 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=380664&action=review >> Source/WebCore/platform/graphics/win/GradientDirect2D.cpp:83 >> startPosition = 1.0 / startPosition; > > Can startPosition be zero? Then, zero-devision can happen. Oops. Never mind. It is always >1.0.
Fujii Hironori
Comment 11 2019-10-10 19:41:45 PDT
Comment on attachment 380664 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=380664&action=review >>> Source/WebCore/platform/graphics/win/GradientDirect2D.cpp:79 >>> if (data.startRadius && data.endRadius) { >> >> Which test cases are using this code path? >> I can't find code setting startRadius and endRadius. >> Can they be negative values? > > I found it. It is canvas.createRadialGradient. > https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/createRadialGradient > > negative values are checked in CanvasRenderingContext2DBase::createRadialGradient. I think this is not a right condition for restoring start position. It should be if (data.startRadius || data.endRadius) { I think swapping startRadius and endRadius should be done before. Then, this condition can be: if (data.startRadius) { I think this is the condition which is easy to understand when restoring start position is needed.
Fujii Hironori
Comment 12 2019-10-10 19:55:48 PDT
Comment on attachment 380664 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=380664&action=review >>>> Source/WebCore/platform/graphics/win/GradientDirect2D.cpp:79 >>>> if (data.startRadius && data.endRadius) { >>> >>> Which test cases are using this code path? >>> I can't find code setting startRadius and endRadius. >>> Can they be negative values? >> >> I found it. It is canvas.createRadialGradient. >> https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/createRadialGradient >> >> negative values are checked in CanvasRenderingContext2DBase::createRadialGradient. > > I think this is not a right condition for restoring start position. It should be > > if (data.startRadius || data.endRadius) { > > I think swapping startRadius and endRadius should be done before. Then, this condition can be: > > if (data.startRadius) { > > I think this is the condition which is easy to understand when restoring start position is needed. You should split reversing stops and restoring the first position if (data.startRadius > data.endRadius) { // Reverse stops... std::swap(data.startRadius, data.endRadius); } if (data.startRadius) { // Restore first position... }
Fujii Hironori
Comment 13 2019-10-10 21:20:01 PDT
Comment on attachment 380664 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=380664&action=review > Source/WebCore/platform/graphics/win/GradientDirect2D.cpp:143 > + offset.setHeight(offset.height() - radiusDiff); In my understanding this is the real offset. Am I worng? https://ibb.co/hFfD7D0
Brent Fulgham
Comment 14 2019-10-15 18:33:09 PDT
(In reply to Fujii Hironori from comment #6) > Comment on attachment 380664 [details] > Patch > > View in context: > https://bugs.webkit.org/attachment.cgi?id=380664&action=review > > > Source/WebCore/platform/graphics/win/GradientDirect2D.cpp:79 > > if (data.startRadius && data.endRadius) { > > Which test cases are using this code path? We hit this code path in the following cases: canvas/philip/tests/2d.gradient.radial.inside2.html canvas/philip/tests/2d.gradient.radial.inside3.html canvas/philip/tests/2d.gradient.radial.outside2.html canvas/philip/tests/2d.gradient.radial.outside3.html canvas/philip/tests/2d.gradient.radial.touch2.html
Brent Fulgham
Comment 15 2019-10-15 18:43:55 PDT
Comment on attachment 380664 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=380664&action=review >> Source/WebCore/PlatformFTW.cmake:3 >> +else () > > Use NOT. And, indent the the inside. > > if (NOT APPLE) > include(platform/ImageDecoders.cmake) > endif () Okay >> Source/WebCore/platform/graphics/win/GradientDirect2D.cpp:143 >> + offset.setHeight(offset.height() - radiusDiff); > > In my understanding this is the real offset. Am I worng? https://ibb.co/hFfD7D0 Yes -- I think that is correct.
Fujii Hironori
Comment 16 2019-10-16 01:07:18 PDT
Comment on attachment 380664 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=380664&action=review >>> Source/WebCore/platform/graphics/win/GradientDirect2D.cpp:143 >>> + offset.setHeight(offset.height() - radiusDiff); >> >> In my understanding this is the real offset. Am I worng? https://ibb.co/hFfD7D0 > > Yes -- I think that is correct. Then, offset should look like: offset = (point1 * startRadius - point0 * endRadius) / (startRadius - endRadius)
Brent Fulgham
Comment 17 2019-10-16 09:31:06 PDT
Brent Fulgham
Comment 18 2019-10-16 09:33:05 PDT
(In reply to Fujii Hironori from comment #16) > Comment on attachment 380664 [details] > Patch > > View in context: > https://bugs.webkit.org/attachment.cgi?id=380664&action=review > > >>> Source/WebCore/platform/graphics/win/GradientDirect2D.cpp:143 > >>> + offset.setHeight(offset.height() - radiusDiff); > >> > >> In my understanding this is the real offset. Am I worng? https://ibb.co/hFfD7D0 > > > > Yes -- I think that is correct. > > Then, offset should look like: > > offset = (point1 * startRadius - point0 * endRadius) / (startRadius - > endRadius) Thank you -- that actually corrected one additional test! :-)
Per Arne Vollan
Comment 19 2019-10-16 13:47:40 PDT
Comment on attachment 381074 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=381074&action=review > Source/WebCore/platform/graphics/win/GradientDirect2D.cpp:92 > + if (data.startRadius) { > float startPosition = std::abs(data.startRadius / data.endRadius); Do we need to check that data.endRadius > 0 here, to avoid division by zero?
Brent Fulgham
Comment 20 2019-10-16 17:09:59 PDT
Comment on attachment 381074 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=381074&action=review >> Source/WebCore/platform/graphics/win/GradientDirect2D.cpp:92 >> float startPosition = std::abs(data.startRadius / data.endRadius); > > Do we need to check that data.endRadius > 0 here, to avoid division by zero? We require that data.startRadius and data.endRadius are > 0 on lines 79/80, so I don't think so?
Per Arne Vollan
Comment 21 2019-10-16 17:16:03 PDT
Comment on attachment 381074 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=381074&action=review >>> Source/WebCore/platform/graphics/win/GradientDirect2D.cpp:92 >>> float startPosition = std::abs(data.startRadius / data.endRadius); >> >> Do we need to check that data.endRadius > 0 here, to avoid division by zero? > > We require that data.startRadius and data.endRadius are > 0 on lines 79/80, so I don't think so? We seem to require that data.endRadius >= 0, perhaps the release assert should be changed?
Brent Fulgham
Comment 22 2019-10-16 17:23:42 PDT
Comment on attachment 381074 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=381074&action=review >>>> Source/WebCore/platform/graphics/win/GradientDirect2D.cpp:92 >>>> float startPosition = std::abs(data.startRadius / data.endRadius); >>> >>> Do we need to check that data.endRadius > 0 here, to avoid division by zero? >> >> We require that data.startRadius and data.endRadius are > 0 on lines 79/80, so I don't think so? > > We seem to require that data.endRadius >= 0, perhaps the release assert should be changed? Oh, I see. Good point. I'll change that. However, we require that data.startRadius <= data.endRadius, and then check that data.startRadius is non-zero, so I don't think it's possible to get here with a zero data.endRadius. But I'll add a RELEASE_ASSERT just to be sure.
Brent Fulgham
Comment 23 2019-10-16 17:32:32 PDT
Per Arne Vollan
Comment 24 2019-10-16 18:08:07 PDT
Comment on attachment 381138 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=381138&action=review R=me. > Source/WebCore/platform/graphics/win/GradientDirect2D.cpp:147 > + if (!circleIsEntirelyContained(data.point0, data.startRadius, data.point1, data.endRadius)) > + offset = (data.point1.scaled(data.startRadius) - data.point0.scaled(data.endRadius)) / (data.endRadius - data.startRadius); Why was this check needed? Did adding this check fix more tests?
WebKit Commit Bot
Comment 25 2019-10-16 18:53:24 PDT
Comment on attachment 381138 [details] Patch Clearing flags on attachment: 381138 Committed r251221: <https://trac.webkit.org/changeset/251221>
WebKit Commit Bot
Comment 26 2019-10-16 18:53:26 PDT
All reviewed patches have been landed. Closing bug.
Radar WebKit Bug Importer
Comment 27 2019-10-16 18:54:15 PDT
Brent Fulgham
Comment 28 2019-10-16 19:13:09 PDT
Comment on attachment 381138 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=381138&action=review >> Source/WebCore/platform/graphics/win/GradientDirect2D.cpp:147 >> + offset = (data.point1.scaled(data.startRadius) - data.point0.scaled(data.endRadius)) / (data.endRadius - data.startRadius); > > Why was this check needed? Did adding this check fix more tests? Yes. The behavior of the gradient is different if one circle is completely contained within the other. Then the focus calculation that Fujii proposed is not valid and produces incorrect rendering.
Note You need to log in before you can comment on or make changes to this bug.