JDK-8136847 : DRT test fast/canvas/canvas-fillRect-shadow.html fails
  • Type: Bug
  • Component: javafx
  • Sub-Component: web
  • Affected Version: 8u60,9
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • Submitted: 2015-09-21
  • Updated: 2020-01-31
  • Resolved: 2017-01-07
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
8u152Fixed 9Fixed
Related Reports
Duplicate :  
Relates :  
DRT test fast/canvas/canvas-fillRect-shadow.html fails with JDK/JFX8u60.

Could not reproduce the failure with 8u40 (b27) or 8u51 (b16).
Might have been introduced during Webkit merge in early 8u60; unable to find exact "introduced in" build since no pre-b14 DRT artifacts are available (and b14 has failures already).

Reproduced on both MacOSX 10.10 and 10.11
changeset: a2dfc5dc3098 user: arajkumar date: Sun Jan 08 00:02:10 2017 +0530 description: 8136847: DRT test fast/canvas/canvas-fillRect-shadow.html fails Reviewed-by: ghb, mbilla, kcr URL: http://hg.openjdk.java.net/openjfx/9-dev/rt/rev/a2dfc5dc3098




Analysis: JDK-8097381 improves rendering performance by checking every rendering call's coordinates with canvas bounds. If the rendering coordinates intersects with canvas bounds, then it is allowed to draw. Things complicates when canvas has any Shadow effect associated with itself, because we need to consider shadow bounding as well. In order to get the bounds of a given Effect (i.e "DropShadow") we should supply tranformation matrix and NodeEffectInput. In the problematic case, NodeEffectInput has been created with NGRectangle instance (TEMP_NGRECT) which has rendering coordinates updated using "TEMP_NGRECT.updateRectangle(..)". When WCBufferedContext::shouldRenderShape calls shadow.getBounds(..), it actually combines all it's effect's(Offset, Merge, and NodeEffectInput) bounding boxes. NodeEffectInput.getBounds calls it's node.getBounds which is NGRectangle, But NGRectangle's bounds are invalid since it need to be set from scene graph. Solution: Set NGRectangle's content bounds before associating it with NodeEffect. Webrev: http://cr.openjdk.java.net/~arajkumar/8136847/webrev

Issue is not seen in HiDPI platforms. I tried on windows with -Dglass.win.uiScale=2 (which is same as Retina Mac), it works. But the problem is hidden in HiDPI platform because of problem in the intersection calculation when transformation matrix has scale value.

Issue is not reproducible in OSX, but persists on Windows & Linux.

Can be solved by using either of two approaches: 1) check shadowBlur is set in fillRect. [ Have to apply the check in many other cases also i.e fillEcllipse ] 2) check shadowBlur is set in isEmpty. [ Need to verify the behavior thoroughly.]

If we set getPad value to 1, the issue resolves.

Debugged further, analysis: If we use "shadowBlur = .0000000001;", : public LinearConvolveRenderState getRenderState(BaseTransform filtertx) { return new GaussianRenderState(hradius, vradius, getSpread(), // headius and vradius will be set to ".0000000001" this instanceof GaussianShadowState, getShadowColor(), filtertx); } For calculting bound region value, calculate hpad and vpad as: public BaseBounds getBounds(BaseTransform transform, Effect defaultInput) { BaseBounds r = super.getBounds(null, defaultInput); int hpad = state.getPad(0); // Find hpad int vpad = state.getPad(1); RectBounds ret = new RectBounds(r.getMinX(), r.getMinY(), r.getMaxX(), r.getMaxY()); ret.grow(hpad, vpad); Get pad return nearest ceil value, so if shadowBlur = .0000000001 getPad return 1. For any value (0,1] return of get Pad is 1. public int getPad(int pass) { return (int) Math.ceil(getRadius(pass)); } With this padding value, grow() will alter the bounded rect region: public void grow(float h, float v) { minX -= h; maxX += h; minY -= v; maxY += v; } So if no shadowBlur is set, maxX = -1.0 and maxY = -1.0 [ No change from defaults values ], which will cause isEmpty() to return false. Causing the problem.

Canvas fillrect [ i.e boundingRect ] takes into account the value of shadowBlur is set or not. This dependency is causing the issue. We have to remove/decouple this dependency. Working on a much better approach to calculating the bounding rect. Probable approach: Check hradius and vradius are set to 0 i.e. shadowBlur is nor set, return false and continue fillrect normally.

If use "shadowBlur = .0000000001;", we get valid shadow bounds values. Otherwise, shadow bounds will be set to empty values. Code : if (shadow != null) { TEMP_NGRECT.updateRectangle(TEMP_BOUNDS.getMinX(), TEMP_BOUNDS.getMinY(), TEMP_BOUNDS.getWidth(), TEMP_BOUNDS.getHeight(), 0, 0); BaseBounds bb = shadow.getBounds(BaseTransform.IDENTITY_TRANSFORM, new NodeEffectInput(TEMP_NGRECT)); assert bb.getBoundsType() == BaseBounds.BoundsType.RECTANGLE; TEMP_BOUNDS.setBounds((RectBounds)bb); "/// Sets correct value to RectBound if shadowBlur is set" tx = getTransformNoClone(); // to apply further }

One approach could be to set the default values of RectBound to 0.0 .i.e: public RectBounds() { minX = minY = 0.0f; maxX = maxY = -1.0f; ---> 0.0f } Checked with the above changes test cases is getting pass. But, by modifying maxX and maxY to 0.0f implies that isEmpty() will return false in the default case.

public void fillRect(final float x, final float y, final float w, final float h, final Integer rgba) { if (log.isLoggable(Level.FINE)) { String format = (rgba != null) ? "fillRect(%f, %f, %f, %f, 0x%x)" : "fillRect(%f, %f, %f, %f, null)"; log.fine(String.format(format, x, y, w, h, rgba)); } if (!shouldRenderRect(x, y, w, h, state.getShadowNoClone(), null)) { return; // Getting return if no shadowBlur is set } shouldRenderRect will finally call : @Override public boolean isEmpty() { // NaN values will cause the comparisons to fail and return "empty" return !(maxX >= minX && maxY >= minY); // This false id shadowBlur is not set. }

Workaround: set shadowBlur to some small value. ctx.shadowBlur = 0.0000000000001;

Regression has been introduced by JDK-8097381.

It fails on all platforms(win, linux and osx)