Windows'ta JavaFX'te SwingNode'un bulanık işlenmesi
Genel Bakış
FlyingSaucer'ı bir JavaFX uygulamasında çeşitli nedenlerle WebView'dan kaçınmak için kullanma:
- senkronize davranış için kaydırma çubuklarına doğrudan API erişimi sağlamaz;
- benim kullanım durumum için büyük bir şişkinlik olan JavaScript'i paketler; ve
- Windows'ta çalıştırılamadı.
FlyingSaucer, JavaFX ile birlikte kullanmak için XHTMLPanel
(bir alt sınıfını JPanel
) a içinde sarmalamayı gerektiren Swing'i kullanır SwingNode
. Her şey harika çalışıyor, uygulama Markdown'u gerçek zamanlı olarak sunuyor ve yanıt veriyor. İşte Linux üzerinde çalışan uygulamanın bir demo videosu .
Sorun
Windows'ta metin işleme bulanık. A ile JFrame
sarmalanmamış SwingNode
, ancak videoda gösterilen aynı uygulamanın bir parçası olarak çalıştırıldığında, metnin kalitesi kusursuzdur. Ekran görüntüsü SwingNode
, yukarıda belirtilenlerle birlikte JFrame
(üstte) uygulamanın ana penceresini (altta) gösterir . Birinin neden keskin ve diğerinin bulanık olduğunu görmek için "l" veya "k" işaretinin düz kenarına yakınlaştırmanız gerekebilir:

Bu yalnızca Windows'ta olur. Yazı tipini Windows'ta sistemin yazı tipi önizleme programı aracılığıyla görüntülerken, yazı tipleri LCD renkleri kullanılarak yumuşatılır. Uygulama gri tonlama kullanır. Oluşturmayı gri tonlama yerine kenar yumuşatma için rengi kullanmaya zorlamanın bir yolu varsa, sorunun ortadan kalkabileceğinden şüpheleniyorum. Sonra yine kendi içinde çalışırken JFrame
sorun olmuyor ve LCD renkleri kullanılmıyor.
Kod
İşte JFrame
mükemmel bir işlemeye sahip olanın kodu :
private static class Flawless {
private final XHTMLPanel panel = new XHTMLPanel();
private final JFrame frame = new JFrame( "Single Page Demo" );
private Flawless() {
frame.getContentPane().add( new JScrollPane( panel ) );
frame.pack();
frame.setSize( 1024, 768 );
}
private void update( final org.w3c.dom.Document html ) {
frame.setVisible( true );
try {
panel.setDocument( html );
} catch( Exception ignored ) {
}
}
}
Bulanık olanın kodu SwingNode
biraz daha karmaşıktır ( tam listeye bakın ), ancak burada bazı ilgili parçacıklar ( yalnızca güncellemeler sırasında istenmeyen bazı otomatik kaydırmayı bastırmaya HTMLPanel
kadar uzanan unutmayın XHTMLPanel
):
private final HTMLPanel mHtmlRenderer = new HTMLPanel();
private final SwingNode mSwingNode = new SwingNode();
private final JScrollPane mScrollPane = new JScrollPane( mHtmlRenderer );
// ...
final var context = getSharedContext();
final var textRenderer = context.getTextRenderer();
textRenderer.setSmoothingThreshold( 0 );
mSwingNode.setContent( mScrollPane );
// ...
// The "preview pane" contains the SwingNode.
final SplitPane splitPane = new SplitPane(
getDefinitionPane().getNode(),
getFileEditorPane().getNode(),
getPreviewPane().getNode() );
Minimal Çalışma Örneği
İşte oldukça asgari bağımsız bir örnek:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.embed.swing.SwingNode;
import javafx.scene.Scene;
import javafx.scene.control.SplitPane;
import javafx.stage.Stage;
import org.jsoup.Jsoup;
import org.jsoup.helper.W3CDom;
import org.xhtmlrenderer.simple.XHTMLPanel;
import javax.swing.*;
import static javax.swing.SwingUtilities.invokeLater;
import static javax.swing.UIManager.getSystemLookAndFeelClassName;
import static javax.swing.UIManager.setLookAndFeel;
public class FlyingSourceTest extends Application {
private final static String HTML = "<!DOCTYPE html><html><head" +
"><style type='text/css'>body{font-family:serif; background-color: " +
"#fff; color:#454545;}</style></head><body><p style=\"font-size: " +
"300px\">TEST</p></body></html>";
public static void main( String[] args ) {
Application.launch( args );
}
@Override
public void start( Stage primaryStage ) {
invokeLater( () -> {
try {
setLookAndFeel( getSystemLookAndFeelClassName() );
} catch( Exception ignored ) {
}
primaryStage.setTitle( "Hello World!" );
final var renderer = new XHTMLPanel();
renderer.getSharedContext().getTextRenderer().setSmoothingThreshold( 0 );
renderer.setDocument( new W3CDom().fromJsoup( Jsoup.parse( HTML ) ) );
final var swingNode = new SwingNode();
swingNode.setContent( new JScrollPane( renderer ) );
final var root = new SplitPane( swingNode, swingNode );
// ----------
// Here be dragons? Using a StackPane, instead of a SplitPane, works.
// ----------
//StackPane root = new StackPane();
//root.getChildren().add( mSwingNode );
Platform.runLater( () -> {
primaryStage.setScene( new Scene( root, 300, 250 ) );
primaryStage.show();
} );
} );
}
}
Minimum çalışma örneğinden bulanık yakalama; yakınlaştırmak, harf kenarlarının keskin kontrastlar yerine büyük ölçüde yumuşatıldığını ortaya çıkarır:

A kullanmak JLabel
, aynı bulanık işlemeyi de sergiler:
final var label = new JLabel( "TEST" );
label.setFont( label.getFont().deriveFont( Font.BOLD, 128f ) );
final var swingNode = new SwingNode();
swingNode.setContent( label );
Denemeler
Bulanıklığı gidermeye çalıştığım yöntemlerin çoğu burada.
Java
Java tarafında, birisi uygulamayı aşağıdakileri kullanarak çalıştırmayı önerdi :
-Dawt.useSystemAAFontSettings=off
-Dswing.aatext=false
Metin oluşturma ipuçlarının hiçbiri yardımcı olmadı.
İçeriğini ayarlama SwingNode
içinde SwingUtilities.invokeLater
hiçbir etkisi yoktur.
JavaFX
Başkası belirtildiği kapalı önbelleğe dönüm yardımcı olduğunu, ama bu bir JavaFX içindi ScrollPane
bir dahilinde bir değil, SwingNode
. İşe yaramadı.
JScrollPane
İçerdiği SwingNode
, sırasıyla 0.5 ve 0.5, uyumunun X ve hizalama Y grubu vardır. Başka yerlerde yarım piksel ofset sağlanması önerilir . Ben ayarlama düşünemiyorum Scene
kullanımına StrokeType.INSIDE
ben boşuna 1'lik bir vuruş genişliği kullanmayı deneyin rağmen, herhangi bir fark yapar.
FlyingSaucer
FlyingSaucer'ın bir dizi yapılandırma seçeneği vardır . Çeşitli ayar kombinasyonları şunları içerir:
java -Dxr.text.fractional-font-metrics=true \
-Dxr.text.aa-smoothing-level=0 \
-Dxr.image.render-quality=java.awt.RenderingHints.VALUE_INTERPOLATION_BICUBIC
-Dxr.image.scale=HIGH \
-Dxr.text.aa-rendering-hint=VALUE_TEXT_ANTIALIAS_GASP -jar ...
xr.image.
Ayarları yalnızca ziyade FlyingSaucer çıktısı içinde JavaFX tarafından oluşturulma biçimini daha FlyingSaucer tarafından işlenmiş görüntüleri etkiler SwingNode
.
CSS , yazı tipi boyutları için noktalar kullanır .
Araştırma
- https://stackoverflow.com/a/26227562/59087 - birkaç çözüm yardımcı olabilir gibi görünüyor.
- https://bugs.openjdk.java.net/browse/JDK-8089499- bu
SwingNode
ve kullanıyor olduğu için geçerli görünmüyorJScrollPane
. - https://stackoverflow.com/a/24124020/59087 - kullanımda XML sahne oluşturucu olmadığı için muhtemelen alakalı değildir.
- https://www.cs.mcgill.ca/media/tech_reports/42_Lessons_Learned_in_Migrating_from_Swing_to_JavaFX_LzXl9Xv.pdf - sayfa 8, 0,5 piksel kaydırmayı açıklar, ancak nasıl?
- https://dlsc.com/2014/07/17/javafx-tip-9-do-not-mix-swing-javafx/ - JavaFX ve Swing'i karıştırmamayı önerir, ancak saf Swing'e geçmek bir seçenek değildir: Uygulamayı yakında başka bir dilde yeniden yazarım.
OpenJDK / JavaFX'e karşı bir hata olarak kabul edildi:
- https://bugs.openjdk.java.net/browse/JDK-8252255
JDK ve JRE
Kullanılması Bellsoft JavaFX ile 'ın OpenJDK birlikte. Bildiğim kadarıyla, OpenJDK bir süredir Freetype desteğine sahip. Ayrıca yazı tipi Linux'ta harika görünüyor, bu yüzden muhtemelen JDK değil.
Ekran
Aşağıdaki ekran özellikleri sorunu göstermektedir, ancak diğer insanlar (şüphesiz farklı monitör ve çözünürlüklerde görüntüleme) sorundan bahsetmiştir.
- 15,6 "4: 3 HD (1366x768)
- Full HD (1920x1080)
- Geniş Görüş Açılı LED Arka Işık
- ASUS n56v
Soru
Neden FlyingSaucer en does XHTMLPanel
içinde sarılı zaman SwingNode
Windows üzerinde olmak bulanık ve henüz aynı görüntüleyen XHTMLPanel
bir de JFrame
uygulama görünür berrak aynı JavaFX çalışan? Sorun nasıl çözülebilir?
Sorun içerir SplitPane
.
Yanıtlar
FlyingSaucer'ı ve API'sini bilmediğimi itiraf etmem gerekse de deneyebileceğiniz birkaç seçenek var.
FlyingSaucer'ın farklı oluşturucuları vardır. Bu nedenle, tüm işlemeyi doğrudan JavaFX'te yapmak için bu kitaplığı kullanarak Swing / AWT oluşturmadan tamamen kaçınmak mümkün olabilir.https://github.com/jfree/fxgraphics2d
Diğer bir olasılık, FlyingSaucer'ın JavaFX'te doğrudan tamponlar aracılığıyla çok verimli bir şekilde görüntülenebilen bir görüntüye dönüştürülmesine izin vermektir. Depomdaki AWTImage koduna buradan bakın:https://github.com/mipastgt/JFXToolsAndDemos
Sorunu kendim yeniden oluşturamadım, bu nedenle kullandığınız JDK / JavaFX sürümünün kombinasyonunda bazı sorunlar olabilir. Sorunun yalnızca belirli bir ekran boyutu ve ekran ölçeklendirme kombinasyonundan kaynaklanması da mümkündür.
Kurulumum şu şekildedir:
- JavaFX 14
- OpenJDK 14
import javafx.application.Application;
import javafx.application.Platform;
import javafx.embed.swing.SwingNode;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import org.jsoup.Jsoup;
import org.jsoup.helper.W3CDom;
import org.jsoup.nodes.Document;
import org.xhtmlrenderer.simple.XHTMLPanel;
import javax.swing.*;
public class FlyingSourceTest extends Application {
private final static String HTML_PREFIX = "<!DOCTYPE html>\n"
+ "<html>\n"
+ "<body>\n";
private static final String HTML_CONTENT =
"<p style=\"font-size:500px\">TEST</p>";
private final static String HTML_SUFFIX = "<p style='height=2em'> </p></body></html>";
public static void main(String[] args) {
Application.launch(args);
}
@Override
public void start(Stage primaryStage) {
SwingUtilities.invokeLater(() -> {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
primaryStage.setTitle("Hello World!");
XHTMLPanel mHtmlRenderer = new XHTMLPanel();
mHtmlRenderer.getSharedContext().getTextRenderer().setSmoothingThreshold(0);
SwingNode mSwingNode = new SwingNode();
JScrollPane mScrollPane = new JScrollPane(mHtmlRenderer);
String htmlContent = HTML_PREFIX + HTML_CONTENT + HTML_SUFFIX;
Document jsoupDoc = Jsoup.parse(htmlContent);
org.w3c.dom.Document w3cDoc = new W3CDom().fromJsoup(jsoupDoc);
mHtmlRenderer.setDocument(w3cDoc);
mSwingNode.setContent(mScrollPane);
// AnchorPane anchorPane = new AnchorPane();
// anchorPane.getChildren().add(mSwingNode);
// AnchorPane.setTopAnchor(mSwingNode, 0.5);
// AnchorPane.setLeftAnchor(mSwingNode, 0.5);
// mSwingNode.setTranslateX(0.5);
// mSwingNode.setTranslateY(0.5);
StackPane root = new StackPane();
root.getChildren().add(mSwingNode);
Platform.runLater(() -> {
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
});
});
}
}
Sorun, OpenJDK / JavaFX'e karşı bir hata olarak kabul edildi:
- https://bugs.openjdk.java.net/browse/JDK-8252255
Mipa'nın önerileri pratikte işe yaramayacaktı. FlyingSaucer, JScrollPane
FlyingSaucer'ı JavaFX tabanlı bir panelde işlemeye zorlama olasılığını ortadan kaldıran a ile sıkı bir şekilde entegre edilmiştir .
Diğer bir olasılık, ters yöne gitmektir: bir Swing uygulaması oluşturmak ve bir JFXPanel kullanmak gibi JavaFX kontrollerini gömmek ; ancak, hata ortadan kaldırılıncaya kadar bulanık davranışı kabul etmek daha mantıklı görünebilir.