A DESCRIPTION OF THE PROBLEM :
Regardless of setImageSmoothing, smooth filtering is applied when a Canvas is transformed in the scene graph. The original issueJDK-8204060 spoke of nearest-neighbor filtering at various zoom levels but this doesn't appear to happen with the fix.
Working around is impractical in a complex scene - it would require detecting scene scaling changes and applying an inverse scale and redrawing, which is both slow and results in artifacts due to floating point error with the inverse scale.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Scale a canvas with an image drawn with setImageScaling(false).
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Pixel boundaries are clearly visible.
ACTUAL -
Pixel boundaries are not possible to see do to smoothing.
---------- BEGIN SOURCE ----------
public class Appn extends Application {
public static byte[] bytes = {
(byte) 0x89,
(byte) 0x50,
(byte) 0x4e,
(byte) 0x47,
(byte) 0x0d,
(byte) 0x0a,
(byte) 0x1a,
(byte) 0x0a,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x0d,
(byte) 0x49,
(byte) 0x48,
(byte) 0x44,
(byte) 0x52,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x04,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x04,
(byte) 0x08,
(byte) 0x02,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x26,
(byte) 0x93,
(byte) 0x09,
(byte) 0x29,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x3a,
(byte) 0x49,
(byte) 0x44,
(byte) 0x41,
(byte) 0x54,
(byte) 0x08,
(byte) 0xd7,
(byte) 0x05,
(byte) 0xc1,
(byte) 0xb1,
(byte) 0x09,
(byte) 0x00,
(byte) 0x41,
(byte) 0x08,
(byte) 0x04,
(byte) 0x40,
(byte) 0x85,
(byte) 0xc7,
(byte) 0x06,
(byte) 0x6c,
(byte) 0xc5,
(byte) 0x7c,
(byte) 0x7b,
(byte) 0xb0,
(byte) 0x60,
(byte) 0x6b,
(byte) 0x11,
(byte) 0x8c,
(byte) 0xcc,
(byte) 0xe5,
(byte) 0xd2,
(byte) 0xfd,
(byte) 0x19,
(byte) 0x8d,
(byte) 0x08,
(byte) 0x00,
(byte) 0x99,
(byte) 0x69,
(byte) 0x66,
(byte) 0x1f,
(byte) 0x00,
(byte) 0x77,
(byte) 0xbf,
(byte) 0xbb,
(byte) 0xaa,
(byte) 0xd2,
(byte) 0xdd,
(byte) 0x15,
(byte) 0x91,
(byte) 0xee,
(byte) 0x9e,
(byte) 0x19,
(byte) 0x25,
(byte) 0x29,
(byte) 0x22,
(byte) 0xef,
(byte) 0x3d,
(byte) 0x92,
(byte) 0x3f,
(byte) 0xf7,
(byte) 0x60,
(byte) 0x19,
(byte) 0x55,
(byte) 0xa2,
(byte) 0x53,
(byte) 0x51,
(byte) 0xb3,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x49,
(byte) 0x45,
(byte) 0x4e,
(byte) 0x44,
(byte) 0xae,
(byte) 0x42,
(byte) 0x60,
(byte) 0x82,
};
public static void main(String[] argv) {
launch(argv);
}
public void start(Stage stage) throws Exception {
Canvas canvas = new Canvas();
canvas.setWidth(4);
canvas.setHeight(4);
canvas.setScaleX(100);
canvas.setScaleY(100);
canvas.setLayoutX(200);
canvas.setLayoutY(200);
GraphicsContext ctx = canvas.getGraphicsContext2D();
ctx.setImageSmoothing(false);
ctx.drawImage(new Image(new ByteArrayInputStream(bytes)), 0, 0);
Pane container = new Pane();
container.setPrefWidth(400);
container.setPrefHeight(400);
container.getChildren().add(canvas);
stage.setScene(new Scene(new HBox(container)));
stage.show();
stage.sizeToScene();
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Generating a proxy for com.sun.prism.Graphics that calls "setLinearFiltering(false)" before any method that takes a Texture as an argument, subclassing NGImageView to use the proxy in renderContent, then somehow getting ImageView to create the NGImageView subclass (I used an agent with some additional method indirection). Lots of command line flags to open/export modules is also required.
Regardless of setImageSmoothing, smooth filtering is applied when a Canvas is transformed in the scene graph. The original issue
Working around is impractical in a complex scene - it would require detecting scene scaling changes and applying an inverse scale and redrawing, which is both slow and results in artifacts due to floating point error with the inverse scale.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Scale a canvas with an image drawn with setImageScaling(false).
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Pixel boundaries are clearly visible.
ACTUAL -
Pixel boundaries are not possible to see do to smoothing.
---------- BEGIN SOURCE ----------
public class Appn extends Application {
public static byte[] bytes = {
(byte) 0x89,
(byte) 0x50,
(byte) 0x4e,
(byte) 0x47,
(byte) 0x0d,
(byte) 0x0a,
(byte) 0x1a,
(byte) 0x0a,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x0d,
(byte) 0x49,
(byte) 0x48,
(byte) 0x44,
(byte) 0x52,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x04,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x04,
(byte) 0x08,
(byte) 0x02,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x26,
(byte) 0x93,
(byte) 0x09,
(byte) 0x29,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x3a,
(byte) 0x49,
(byte) 0x44,
(byte) 0x41,
(byte) 0x54,
(byte) 0x08,
(byte) 0xd7,
(byte) 0x05,
(byte) 0xc1,
(byte) 0xb1,
(byte) 0x09,
(byte) 0x00,
(byte) 0x41,
(byte) 0x08,
(byte) 0x04,
(byte) 0x40,
(byte) 0x85,
(byte) 0xc7,
(byte) 0x06,
(byte) 0x6c,
(byte) 0xc5,
(byte) 0x7c,
(byte) 0x7b,
(byte) 0xb0,
(byte) 0x60,
(byte) 0x6b,
(byte) 0x11,
(byte) 0x8c,
(byte) 0xcc,
(byte) 0xe5,
(byte) 0xd2,
(byte) 0xfd,
(byte) 0x19,
(byte) 0x8d,
(byte) 0x08,
(byte) 0x00,
(byte) 0x99,
(byte) 0x69,
(byte) 0x66,
(byte) 0x1f,
(byte) 0x00,
(byte) 0x77,
(byte) 0xbf,
(byte) 0xbb,
(byte) 0xaa,
(byte) 0xd2,
(byte) 0xdd,
(byte) 0x15,
(byte) 0x91,
(byte) 0xee,
(byte) 0x9e,
(byte) 0x19,
(byte) 0x25,
(byte) 0x29,
(byte) 0x22,
(byte) 0xef,
(byte) 0x3d,
(byte) 0x92,
(byte) 0x3f,
(byte) 0xf7,
(byte) 0x60,
(byte) 0x19,
(byte) 0x55,
(byte) 0xa2,
(byte) 0x53,
(byte) 0x51,
(byte) 0xb3,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x49,
(byte) 0x45,
(byte) 0x4e,
(byte) 0x44,
(byte) 0xae,
(byte) 0x42,
(byte) 0x60,
(byte) 0x82,
};
public static void main(String[] argv) {
launch(argv);
}
public void start(Stage stage) throws Exception {
Canvas canvas = new Canvas();
canvas.setWidth(4);
canvas.setHeight(4);
canvas.setScaleX(100);
canvas.setScaleY(100);
canvas.setLayoutX(200);
canvas.setLayoutY(200);
GraphicsContext ctx = canvas.getGraphicsContext2D();
ctx.setImageSmoothing(false);
ctx.drawImage(new Image(new ByteArrayInputStream(bytes)), 0, 0);
Pane container = new Pane();
container.setPrefWidth(400);
container.setPrefHeight(400);
container.getChildren().add(canvas);
stage.setScene(new Scene(new HBox(container)));
stage.show();
stage.sizeToScene();
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Generating a proxy for com.sun.prism.Graphics that calls "setLinearFiltering(false)" before any method that takes a Texture as an argument, subclassing NGImageView to use the proxy in renderContent, then somehow getting ImageView to create the NGImageView subclass (I used an agent with some additional method indirection). Lots of command line flags to open/export modules is also required.
- relates to
-
JDK-8090349 Add property to control antialiasing of ImageView
- Open
-
JDK-8204060 [Canvas] Add API in GraphicsContext to control image smoothing
- Resolved