Windows'ta JavaFX'te SwingNode'un bulanık işlenmesi

Aug 17 2020

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 JFramesarmalanmamış 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 JFramesorun olmuyor ve LCD renkleri kullanılmıyor.

Kod

İşte JFramemü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 SwingNodebiraz 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 HTMLPanelkadar 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 SwingNodeiçinde SwingUtilities.invokeLaterhiçbir etkisi yoktur.

JavaFX

Başkası belirtildiği kapalı önbelleğe dönüm yardımcı olduğunu, ama bu bir JavaFX içindi ScrollPanebir 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 Scenekullanımına StrokeType.INSIDEben 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 SwingNodeve kullanıyor olduğu için geçerli görünmüyor JScrollPane.
  • 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 XHTMLPaneliçinde sarılı zaman SwingNodeWindows üzerinde olmak bulanık ve henüz aynı görüntüleyen XHTMLPanelbir de JFrameuygulama görünür berrak aynı JavaFX çalışan? Sorun nasıl çözülebilir?

Sorun içerir SplitPane.

Yanıtlar

1 mipa Aug 19 2020 at 19:17

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

1 weisj Aug 20 2020 at 01:48

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'>&nbsp;</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();
            });
        });
    }
}
DaveJarvis Aug 27 2020 at 01:33

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, JScrollPaneFlyingSaucer'ı 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.