Skip to content
This repository was archived by the owner on Jul 24, 2024. It is now read-only.

Commit 32fda11

Browse files
author
Johan Vos
committed
8211294: ScrollPane content is blurry with 125% scaling
Backport-of: 9c84c77
1 parent debddf7 commit 32fda11

File tree

3 files changed

+149
-25
lines changed

3 files changed

+149
-25
lines changed

modules/javafx.graphics/src/main/java/javafx/scene/layout/Region.java

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -986,20 +986,23 @@ void fireValueChanged() {
986986
private double snappedBottomInset = 0;
987987
private double snappedLeftInset = 0;
988988

989+
/**
990+
* Cached snapScale values, used to determine if snapped cached insets values
991+
* should be invalidated because screen scale has changed.
992+
*/
993+
private double lastUsedSnapScaleY = 0;
994+
private double lastUsedSnapScaleX = 0;
995+
989996
/** Called to update the cached snapped insets */
990997
private void updateSnappedInsets() {
998+
lastUsedSnapScaleX = getSnapScaleX();
999+
lastUsedSnapScaleY = getSnapScaleY();
9911000
final Insets insets = getInsets();
992-
if (_snapToPixel) {
993-
snappedTopInset = Math.ceil(insets.getTop());
994-
snappedRightInset = Math.ceil(insets.getRight());
995-
snappedBottomInset = Math.ceil(insets.getBottom());
996-
snappedLeftInset = Math.ceil(insets.getLeft());
997-
} else {
998-
snappedTopInset = insets.getTop();
999-
snappedRightInset = insets.getRight();
1000-
snappedBottomInset = insets.getBottom();
1001-
snappedLeftInset = insets.getLeft();
1002-
}
1001+
final boolean snap = isSnapToPixel();
1002+
snappedTopInset = snapSpaceY(insets.getTop(), snap);
1003+
snappedRightInset = snapSpaceX(insets.getRight(), snap);
1004+
snappedBottomInset = snapSpaceY(insets.getBottom(), snap);
1005+
snappedLeftInset = snapSpaceX(insets.getLeft(), snap);
10031006
}
10041007

10051008
/**
@@ -1861,6 +1864,11 @@ public double snapPositionY(double value) {
18611864
* @return Rounded up insets top
18621865
*/
18631866
public final double snappedTopInset() {
1867+
// invalidate the cached values for snapped inset dimensions
1868+
// if the screen scale changed since they were last computed.
1869+
if (lastUsedSnapScaleY != getSnapScaleY()) {
1870+
updateSnappedInsets();
1871+
}
18641872
return snappedTopInset;
18651873
}
18661874

@@ -1872,6 +1880,11 @@ public final double snappedTopInset() {
18721880
* @return Rounded up insets bottom
18731881
*/
18741882
public final double snappedBottomInset() {
1883+
// invalidate the cached values for snapped inset dimensions
1884+
// if the screen scale changed since they were last computed.
1885+
if (lastUsedSnapScaleY != getSnapScaleY()) {
1886+
updateSnappedInsets();
1887+
}
18751888
return snappedBottomInset;
18761889
}
18771890

@@ -1883,6 +1896,11 @@ public final double snappedBottomInset() {
18831896
* @return Rounded up insets left
18841897
*/
18851898
public final double snappedLeftInset() {
1899+
// invalidate the cached values for snapped inset dimensions
1900+
// if the screen scale changed since they were last computed.
1901+
if (lastUsedSnapScaleX != getSnapScaleX()) {
1902+
updateSnappedInsets();
1903+
}
18861904
return snappedLeftInset;
18871905
}
18881906

@@ -1894,6 +1912,11 @@ public final double snappedLeftInset() {
18941912
* @return Rounded up insets right
18951913
*/
18961914
public final double snappedRightInset() {
1915+
// invalidate the cached values for snapped inset dimensions
1916+
// if the screen scale changed since they were last computed.
1917+
if (lastUsedSnapScaleX != getSnapScaleX()) {
1918+
updateSnappedInsets();
1919+
}
18971920
return snappedRightInset;
18981921
}
18991922

modules/javafx.graphics/src/main/java/javafx/scene/text/TextFlow.java

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -554,10 +554,6 @@ private static class StyleableProperties {
554554
}
555555

556556
/* The methods in this section are copied from Region due to package visibility restriction */
557-
private static double snapSpace(double value, boolean snapToPixel) {
558-
return snapToPixel ? Math.round(value) : value;
559-
}
560-
561557
static double boundedSize(double min, double pref, double max) {
562558
double a = pref >= min ? pref : min;
563559
double b = min >= max ? min : max;
@@ -569,11 +565,10 @@ static double boundedSize(double min, double pref, double max) {
569565
}
570566

571567
double computeChildPrefAreaWidth(Node child, Insets margin, double height) {
572-
final boolean snap = isSnapToPixel();
573-
double top = margin != null? snapSpace(margin.getTop(), snap) : 0;
574-
double bottom = margin != null? snapSpace(margin.getBottom(), snap) : 0;
575-
double left = margin != null? snapSpace(margin.getLeft(), snap) : 0;
576-
double right = margin != null? snapSpace(margin.getRight(), snap) : 0;
568+
double top = margin != null? snapSpaceY(margin.getTop()) : 0;
569+
double bottom = margin != null? snapSpaceY(margin.getBottom()) : 0;
570+
double left = margin != null? snapSpaceX(margin.getLeft()) : 0;
571+
double right = margin != null? snapSpaceX(margin.getRight()) : 0;
577572
double alt = -1;
578573
if (child.getContentBias() == Orientation.VERTICAL) { // width depends on height
579574
alt = snapSizeY(boundedSize(
@@ -588,11 +583,10 @@ static double boundedSize(double min, double pref, double max) {
588583
}
589584

590585
double computeChildPrefAreaHeight(Node child, Insets margin, double width) {
591-
final boolean snap = isSnapToPixel();
592-
double top = margin != null? snapSpace(margin.getTop(), snap) : 0;
593-
double bottom = margin != null? snapSpace(margin.getBottom(), snap) : 0;
594-
double left = margin != null? snapSpace(margin.getLeft(), snap) : 0;
595-
double right = margin != null? snapSpace(margin.getRight(), snap) : 0;
586+
double top = margin != null? snapSpaceY(margin.getTop()) : 0;
587+
double bottom = margin != null? snapSpaceY(margin.getBottom()) : 0;
588+
double left = margin != null? snapSpaceX(margin.getLeft()) : 0;
589+
double right = margin != null? snapSpaceX(margin.getRight()) : 0;
596590
double alt = -1;
597591
if (child.getContentBias() == Orientation.HORIZONTAL) { // height depends on width
598592
alt = snapSizeX(boundedSize(
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package test.javafx.scene;
26+
27+
import com.sun.javafx.PlatformUtil;
28+
import javafx.application.Application;
29+
import javafx.application.Platform;
30+
import javafx.scene.Node;
31+
import javafx.scene.Scene;
32+
import javafx.scene.control.Label;
33+
import javafx.scene.control.ScrollPane;
34+
import javafx.scene.layout.VBox;
35+
import javafx.stage.Stage;
36+
import javafx.stage.WindowEvent;
37+
import junit.framework.Assert;
38+
import org.junit.AfterClass;
39+
import org.junit.BeforeClass;
40+
import org.junit.Test;
41+
42+
import java.util.concurrent.CountDownLatch;
43+
import java.util.concurrent.TimeUnit;
44+
45+
import static org.junit.Assert.assertTrue;
46+
import static org.junit.Assume.assumeTrue;
47+
48+
public class UIRenderSnapToPixelTest {
49+
private static final double scale = 1.25;
50+
private static CountDownLatch startupLatch;
51+
private static volatile Stage stage;
52+
private static final double epsilon = 0.00001;
53+
54+
@BeforeClass
55+
public static void setupOnce() throws Exception {
56+
System.setProperty("glass.win.uiScale", String.valueOf(scale));
57+
System.setProperty("glass.gtk.uiScale", String.valueOf(scale));
58+
startupLatch = new CountDownLatch(1);
59+
new Thread(() -> Application.launch(TestApp.class, (String[]) null)).start();
60+
assertTrue("Timeout waiting for FX runtime to start", startupLatch.await(15, TimeUnit.SECONDS));
61+
}
62+
63+
@AfterClass
64+
public static void teardown() {
65+
Platform.runLater(stage::hide);
66+
Platform.exit();
67+
}
68+
69+
@Test
70+
public void testScrollPaneSnapChildrenToPixels() {
71+
assumeTrue(PlatformUtil.isLinux() || PlatformUtil.isWindows());
72+
73+
Assert.assertEquals("Wrong render scale", scale, stage.getRenderScaleY(), 0.0001);
74+
75+
for (Node node : stage.getScene().getRoot().getChildrenUnmodifiable()) {
76+
if (node instanceof ScrollPane) {
77+
var sp = (ScrollPane) node;
78+
Assert.assertEquals("Top inset not snapped to pixel", 0, ((sp.snappedTopInset() * scale) + epsilon) % 1, 0.0001);
79+
Assert.assertEquals("Bottom inset not snapped to pixel", 0, ((sp.snappedBottomInset() * scale) + epsilon) % 1, 0.0001);
80+
Assert.assertEquals("Left inset not snapped to pixel", 0, ((sp.snappedLeftInset() * scale) + epsilon) % 1, 0.0001);
81+
Assert.assertEquals("Right inset not snapped to pixel", 0, ((sp.snappedRightInset() * scale) + epsilon) % 1, 0.0001);
82+
}
83+
}
84+
}
85+
86+
public static class TestApp extends Application {
87+
private static void run() {
88+
startupLatch.countDown();
89+
}
90+
91+
@Override
92+
public void start(Stage primaryStage) throws Exception {
93+
final Label label = new Label("This text may appear blurry at some screen scale without the fix for JDK-8211294");
94+
final ScrollPane scrollpane = new ScrollPane(label);
95+
scrollpane.setSnapToPixel(true);
96+
final VBox root = new VBox();
97+
root.getChildren().add(new Label("This text should be sharp at all screen scale"));
98+
root.getChildren().add(scrollpane);
99+
final Scene scene = new Scene(root);
100+
primaryStage.setScene(scene);
101+
stage = primaryStage;
102+
stage.addEventHandler(WindowEvent.WINDOW_SHOWN, e -> Platform.runLater(TestApp::run));
103+
stage.show();
104+
}
105+
}
106+
107+
}

0 commit comments

Comments
 (0)