JavaFX中GraphicsContext.clearRect()行为异常问题咨询
JavaFX GraphicsContext.clearRect() 行为异常的原因分析
问题描述
使用JavaFX 18.0.2搭配OpenJDK 17.0.9+9时,GraphicsContext.clearRect()出现不符合预期的行为:
- 正常HiDPI模式下,调用
clearRect(30, 30, 10, 10)后,目标清除区域出现额外过渡色(0x4040ffff、0x8080ffff),虽统计到的白色像素为100个(符合10×10),但杂色存在。 - 添加参数
-Dprism.allowhidpi=false禁用HiDPI后,杂色消失,但清除区域变为11×11(白色像素121个);而fillRect()始终能精准清除10×10的区域。
测试输出
正常模式(清除操作)
Color:0xffffffff : 100 Color:0x0000ffff : 22379 Color:0x4040ffff : 1 Color:0x8080ffff : 20
正常模式(填充操作)
Color:0xffffffff : 100 Color:0x0000ffff : 22400
禁用HiDPI(清除操作)
Color:0xffffffff : 121 Color:0x0000ffff : 22379
禁用HiDPI(填充操作)
Color:0xffffffff > 100 Color:0x0000ffff > 22400
测试代码
import java.util.HashMap; import java.util.Map; import javafx.application.Application; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; import javafx.scene.image.PixelReader; import javafx.scene.image.WritableImage; import javafx.scene.paint.Color; import javafx.stage.Stage; public class ClearRectTest extends Application { @Override public void start(Stage primaryStage) throws Exception { Canvas canvas = new Canvas(150, 150); Group root = new Group(); root.getChildren().add(canvas); primaryStage.setScene(new Scene(root)); primaryStage.show(); GraphicsContext gc = canvas.getGraphicsContext2D(); System.out.println("\nTry with Clear\n-"); gc.setFill(Color.BLUE); gc.fillRect(0, 0, 150, 150); gc.clearRect(30, 30, 10, 10); { Map<Color, Integer> colorMap = countColors(canvas); for (Color c : colorMap.keySet()) { System.out.println("Color:" + c + " : " + colorMap.get(c)); } } System.out.println("\n\nTry with fill\n-"); gc.setFill(Color.BLUE); gc.fillRect(0, 0, 150, 150); gc.setFill(Color.WHITE); gc.fillRect(30, 30, 10, 10); { Map<Color, Integer> colorMap = countColors(canvas); for (Color c : colorMap.keySet()) { System.out.println("Color:" + c + " : " + colorMap.get(c)); } } } public static Map<Color, Integer> countColors(Canvas canvas) { WritableImage fxImage = canvas.snapshot(null, null); Map<Color, Integer> map = new HashMap<>(); PixelReader pixelReader = fxImage.getPixelReader(); for (int y = 0; y < fxImage.getHeight(); ++y){ for (int x = 0; x < fxImage.getWidth(); ++x) { Color c = pixelReader.getColor(x, y); Integer i = map.get(c); map.put(c, i == null ? 1 : i + 1); } } return map; } public static void main(String[] args) { launch(args); } }
原因分析
HiDPI模式下的像素混合问题
JavaFX的Prism渲染引擎在HiDPI显示器上会按设备像素比将逻辑像素转换为物理像素。clearRect()属于透明通道操作,当逻辑像素与物理像素未完全对齐时,边缘会触发抗锯齿混合,产生白色与蓝色的过渡色(0x4040ffff、0x8080ffff)。而fillRect()是不透明填充,直接覆盖像素,不会出现混合色。禁用HiDPI后的尺寸舍入差异
禁用HiDPI后,Prism停止DPI缩放,但clearRect()对尺寸的处理采用向上舍入逻辑,导致10×10的逻辑区域被扩展为11×11的实际像素区域。fillRect()则采用严格的整数对齐处理,因此能保持10×10的精准区域。底层渲染路径的优化差异
clearRect()调用的是底层图形API的透明清除操作,边界处理逻辑与不透明填充(fillRect())不同。JavaFX对两种操作的渲染路径做了不同优化,导致不同缩放模式下的行为一致性较差。
内容的提问来源于stack exchange,提问作者Christoph
相关产品推荐
相关产品推荐

