JDK-8088013 : Bleeding Edges - Background Bleeding on Shared Edges for 3D Multi-Polygon Configurations
  • Type: Bug
  • Component: javafx
  • Sub-Component: graphics
  • Affected Version: 8
  • Priority: P3
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2014-07-28
  • Updated: 2018-09-05
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.
Other
tbdUnresolved
Related Reports
Blocks :  
Relates :  
Relates :  
Relates :  
Description
For a set of 3D Polygons configured through attached sets of transformations such that they have shared edges, as in the case of a regular polyhedron, the background color of either the Scene, Stage or Screen, which ever is topmost opaque, bleeds or appears as to be stitching through the shared edges.  This appears to be an artifact of the applied transformations, Translate & Rotate, as this has not occurred for 2D polygons with distinct point sets excepting a common pair of consecutive points, i. e. a shared edge.

This is best illustrated with a test case app which I can provide and is as follows:

The app consists of 3 buttons and 2 groups of polygons:

Group 1:  
This is a pair of 2D triangles (solid fills, 1 black & 1 yellow) which have 2 points in common, i. e. a shared edge, and no transforms applied.
This is visible in the top left of the scene, and does not exhibit the bleeding artifact and is shown for this purpose.

Group 2: 
This is a set of 20 3D polygons (equilateral triangles) configured via applied transformations to each polygon, to form a 3D icosahedron. This 'polyhedron group' has 2 axes of rotation X and Y both with a pivot at the polyhedrons centre. Each polygon can be set with same base color or a distinct color. Each fill is a LinearGradient, and although this app doesn't use solid fills for the polyhedron, if it did the bleeding would occur, but less markedly.
This is visible in the center of the scene,  and exhibits the bleeding artifact to varying degrees on all edges.

All polygons for both groups have null strokes and zero stroke widths.

The buttons are as follows:
Start/Stop Rotation:      starts and stops polyhedron rotations about both X and Y axes.
Multi Color on/off:        switches between a single or multiple colors for the polyhedron faces.
Switch Background:      switches the Scene fill initially from TRANSPARENT to RED, and from RED to WHITE thereafter & vice versa.

Steps to reproduce the bug:
1. Launch the app and observe the bleeding effect, this should be quite obvious.
2. Press the 'Switch Background' button more than once, observe & confirm the effect for the different background colors & background source.
3. Optionally press the 'Start/Stop Rotation' and/or 'Multi Color on/off' to demonstrate the erroneous behavior for multiple circumstances.

The app jars:
There are 2 executable jars to be provided BleedingEdgesEA.jar (8u20 compiled) and BleedingEdges.jar (7u65 compiled), both also contain the identical source files. The jdk7 jar exhibits the correct behavior without artifacts under jdk7 and both jars exhibit erroneous behavior under jdk8
versions even with the fix applied for the resolved RT-38005 bug.

The source packages:
bleedingedges,  This has the main class BleedingEdge.java,  tezzerfx.polyhedra , tezzerfx.vecmath.utils  and the javax.vecmath package.

Since these are all necessary components, placing the main class code in the comments is pointless without at least the non javax code and this is impractical, thus to whomever is assigned to handle this issue, I will gladly email the aforementioned jars.

Comments
I will study this problem, as I am developping a new rasterizer MarlinFX. Probably there is a rounding issue on left or right crossings...
04-11-2016

Reopening it based on an additional comment from the submitter: ------------------------------------------ This is ERRONEOUSLY in resolved status claiming that the Polygons which are used to create a 3D polyhedron has an equivalent "MeshView " representation! This is NOT the case, the individual polygons have node properties which are utilized and CANNOT be duplicated with a MeshView, e.g. YOU CAN ADD an Image to the polygon utilizing "panes"or a Blend effect each with its own set of configurations or controls! This can not be done on individual faces with a "MeshView " ! This bug needs to be reopened and RESOLVED! -------------------------------------------
11-04-2016

MeshView is the right shape for such use case instead of Polygon.
27-01-2016

SQE is ok to defer from 8u40.
26-11-2014

We have decided the proper fix is out of the scope for 8u40. The workaround to this rendering issue is to use the new 3D shape (MeshView introduced in JavaFX 8.0) to do what the user was trying to do.
20-11-2014

1) OK. Will do. 2) Yes, I have created RT-39468 for the current fixes, and leave this bleeding edges issue open.
20-11-2014

For J2DPrismGraphics, could you put the declaration of the new boolean field up near where the rest of the attributes are stored near line 500-510? Also, since we've determined that this particular mechanism is not likely to solve the bleeding edges for the test case in this bug report, should we continue the reviews of this fix in another issue instead?
20-11-2014

Here is a revised webrev with minor changes where the enforcing of forceNonAntialisedShape property is localized to NGShape: http://cr.openjdk.java.net/~ckyang/RT-38063/webrev.02
20-11-2014

I took a quick look at Stroker.java and didn't see any constants to tweak. It does do some measurement on the curves, but it seems more concerned with whether or not the curve is of a nature that it can be "offset" to produce a parallel curve. That is related to how fast it is "turning", but since it outputs curves I'm not sure that it would directly affect how dirty the outlines are. Worst case as I see it - nobody is likely to be modifying Shape.setSmooth() yet since we don't do anything, but if they are then we may silently unleash some poor rendering on them with this release. How likely do we think that developers are playing with an API that isn't hooked up to do anything? Even if they are playing with it, they are probably going to be just as unhappy with the sudden appearance of non-AA jaggies as they would about whether or not they are the "best jaggies". Also, we have some time to try to fix these problems before 8u40 ships as part of the follow-on "reevaluate the BND constants" bug.
19-11-2014

Hi Jim, 1) Thanks! I have fixed it to lastAntialiasedShape (We decided the second "a" in AA should be lowercase too.) 2) I will file a separate JIRA to remove this method plus its relevant prism settings. 3) I doubt this "noisy" curve/edge issue can be resolved without first addressing RT-39424. BTW, we don't expect user of 2D shape to set its smooth property to false (default is true) for typical use case unless it is used with 3D transform as a 3D scene.
19-11-2014

NativePiscesRasterizer -> typo in lastAntiAlisedShape => lastAntiAliasedShape ShapeUtil.rasterizeGlyphOutline isn't used anywhere (sent a note to Phil asking)? Text (which is a Shape) is still antialiased even with the force flag. Running Ensemble8 with the forceNonAA flag... It looks like strokes are still really poor. I'm guessing there are constants in the Stroker that are similar to the BND constants in the Renderer that control how accurate we are with widening curves. Ensemble8/Graphics2D/Shapes/Rectangle is pretty bad - the left edge doesn't even show up (probably because it falls between pixel centers). This is why we have "STROKE_CONTROL" in Java2D to prevent dropouts from occuring when thin strokes slide between boundaries. Stopwatch has what looks like a dashed ring around the edge that is just priceless(ly bad).
19-11-2014

Hi Jim, Thanks for the feedback. Here is the revised webrev that includes all your suggestions: http://cr.openjdk.java.net/~ckyang/RT-38063/webrev.01/
19-11-2014

NGShape.java - you can get rid of all of the changes to the ResourceFactories by just calling rtt.setLinearFiltering (only when non-AA) in this method. CachingShapeRep.java - you need to add the AA flag to the list of things compared when finding a cache entry OpenPiscesPrismUtils - this can be simplified somewhat, an alternate patch for the file is attached to the Jira here. Nod.java - no diffs to this file
19-11-2014

Hi Jim and Kevin, Please review this webrev to support non AA 2D shape rendering: http://cr.openjdk.java.net/~ckyang/RT-38063/webrev.00/ The bleeding edges is still appeared (as red noisy dots) in the test program. This should be resolved once we addressed the 1st of the 3 follow-on JIRAs from this work, other 2 are for performance and quality enhancement. I have tested this work on Windows, Linux and Mac. RT-39424: Need a more accurate center of pixel sampling for supporting 1 sample per pixel rendering RT-39439 : Need to re-evaluate the BND constants set in the Picecs rasterizer for non AA shape RT-39440 : MSAA should apply antialising rendering to 2D shapes as well
19-11-2014

Some thoughts on modifying the Java and Native rasterizers to support AA and non-AA rendering. These classes are initalized with the "number of sub-pixel bits to sample" so the default 3,3 values use an 8x8 sub-pixel grid and you can specify 0,0 to mean a 1x1 sub-pixel grid. Java rasterizer (OpenPiscesPrismUtils.java and related classes): This class has static variables set up as Dasher(Stroker(Renderer(3,3)). This means that the dasher sends its output to the stroker which then sends its output to the renderer. But you need to swap in a Renderer(0,0) for non-AA. We could just leave that chain set up like that and add a Renderer.setSubpixelThingies(n, n) method to the Renderer class, but it stores the sub-pixel parameters in final variables for speed so it might be worthwhile to have 2 separate instances of the Renderer (one for AA=3,3 and one for non-AA=0,0). The Stroker can have its consumer changed via a method so you can reuse the same Dasher and Stroker instance and just tell the stroker which of the two renderers to communicate its values to by calling stroker.setConsumer(aaRenderer or nonAARenderer). That should be all that is needed for the Java rasterizer. Native rasterizer (NativePiscesRasterizer.java and related prism-native project): The Java class calls a static native method init(3, 3) to set up the sub-pixel precision and it calls it from a static initializer block in the Java class. Since these are stored as statics in C it would be awkward to redesign everything to have 2 instances so it would be easier to instead call the setup on each iteration. It's a static method, and the native code stores the values in static variables but since we are only supporting single threading there should be no loss to just call it with init(3,3) or init(0,0) on each shape in the NativePiscesRasterizer.getMaskData() method and remove the call from the static initializer block. The init() method doesn't do much so there shouldn't be any performance penalty. If we want to avoid an extra native method call we could pass a parameter to the two produce*Alpha() native methods and have them call the Renderer_setup() C function themselves, but 2 native method calls per shape should not be a big issue. If it is that important we could remember the last call to init() and only call it if the value is changing.
30-10-2014

Based on a discussion with Jim and Chien today it seems this can be done without writing new shaders, which will reduce the effort needed to implement this. As a result, I am assigning it back to Chien.
30-10-2014

BTW, I was able to run the attached jar without problem with the latest JFX.
26-09-2014

@Morris: the above stack trace indicates that you are missing some class files needed to run the user program. If you want to run it you will need vecmath.jar in your classpath. Btw, we know exactly why this is failing, so no need to bisect it further. As I mentioned in my above comment: "This bug is the result (side-effect) of an intentional change in behavior to fix RT-5534."
26-09-2014

bash-3.2$ java -Xbootclasspath/a:${JAVAFX_HOME}/rt/lib/ext/jfxrt.jar -cp bleed bleedingedges.BleedingEdge Exception in Application start method java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:351) at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:304) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767) Caused by: java.lang.RuntimeException: Exception in Application start method at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:867) at com.sun.javafx.application.LauncherImpl.access$000(LauncherImpl.java:56) at com.sun.javafx.application.LauncherImpl$1.run(LauncherImpl.java:158) at java.lang.Thread.run(Thread.java:745) Caused by: java.lang.NoSuchMethodError: javax.vecmath.Point3d.setX(D)V at tezzerfx.vecmath.utils.VecmathUtilities.ZTransform(VecmathUtilities.java:144) at tezzerfx.polyhedra.Polyhedron.calcPolyPoints(Polyhedron.java:1506) at tezzerfx.polyhedra.Polyhedron.calcPolyPoints(Polyhedron.java:1471) at tezzerfx.polyhedra.Polyhedron.createSolid(Polyhedron.java:819) at tezzerfx.polyhedra.Polyhedron.<init>(Polyhedron.java:114) at bleedingedges.BleedingEdge.start(BleedingEdge.java:81) at com.sun.javafx.application.LauncherImpl$5.run(LauncherImpl.java:810) at com.sun.javafx.application.PlatformImpl$6.run(PlatformImpl.java:273) at com.sun.javafx.application.PlatformImpl$5$1.run(PlatformImpl.java:239) at com.sun.javafx.application.PlatformImpl$5$1.run(PlatformImpl.java:236) at java.security.AccessController.doPrivileged(Native Method) at com.sun.javafx.application.PlatformImpl$5.run(PlatformImpl.java:236) at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95) Exception running application bleedingedges.BleedingEdge
26-09-2014

We should try to fix this for 8u40.
29-07-2014

Yes, I can reproduce the issue, and I will attach screen shots for both FX 2 and FX 8. This bug is the result (side-effect) of an intentional change in behavior to fix RT-5534. See RT-31881 for another bug caused by this fix.
29-07-2014

Cool... Hope you give it a try under jdk's 7&8, so you can see what I'm sayin' ;)
28-07-2014

I just attached the test case you sent.
28-07-2014

Just sent you the jdk 7 compiled jar, this should be sufficient to show the issues & has the source code...
28-07-2014

I didn't get the e-mail, possibly due to the size of the attachment -- it must be < 10 Mbytes, and for attaching to JIRA it should be closer to 1 Mbyte.
28-07-2014

Hi Kevin, I just tried to email the test case jars to you and got: "Delivery to the following recipients failed kevin.rushforth@oracle.com" So do let me know if really failed or not?
28-07-2014