2017-07-18 3 views
2

Je suis bloqué (au-delà des limites de l'amusement) en essayant de corriger la qualité du texte avec la double mise en mémoire tampon de l'image hors écran. Screen capture worth a thousand words.Texte misérable lorsque vous dessinez des images hors écran en Java

  • Le String laid est tiré à une image hors écran, puis copiés sur l'argument de GraphicspaintComponent.
  • Le beau String est écrit directement à l'argument Graphics de paintComponent, en contournant l'image hors écran.

Les deux instances (Graphics à l'écran et hors écran) sont configurés de manière identique en termes de qualité de rendu, anticrénelage, et ainsi de suite ...

Merci beaucoup à l'avance pour votre sagesse.


Le code très simple suit:

public class AcceleratedPanel extends JPanel { 
    private Dimension  osd; //offscreen dimension 
    private BufferedImage osi; //offscreen image 
    private Graphics  osg; //offscreen graphic 

    public AcceleratedPanel() { 
     super(); 
    } 

    @Override 
    public final void paintComponent(Graphics g) { 
     super.paintComponent(g); 

     // -------------------------------------- 
     //OffScreen painting 

     Graphics2D osg2D = getOffscreenGraphics(); 
     setupGraphics(osg2D); 
     osg2D.drawString("Offscreen painting", 10, 20); 

     //Dump offscreen buffer to screen 
     g.drawImage(osi, 0, 0, this); 

     // -------------------------------------- 
     // OnScreen painting 
     Graphics2D gg = (Graphics2D)g; 
     setupGraphics(gg); 
     gg.drawString("Direct painting", 10, 35); 
    } 

    /* 
    To make sure same settings are used in different Graphics instances, 
    a unique setup procedure is used. 
    */ 
    private void setupGraphics(Graphics2D g) { 
     g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); 
     g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 
     g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); 
     g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); 
    } 

    private Graphics2D getOffscreenGraphics() { 
     //Graphics Acceleration 
     Dimension currentDimension = getSize(); 
     if (osi == null || !currentDimension.equals(osd)) { 
      osi = (BufferedImage)createImage(currentDimension.width, currentDimension.height); 
      osg = osi.createGraphics(); 
      osd = currentDimension; 
     } 
     return (Graphics2D) osg; 
    } 

} //End of mistery 
+0

"... au-delà des limites du plaisir". Bienvenue à * travailler *. –

+0

La méthode de peinture "directe" profite du fait que le contexte 'Graphics' a été configuré pour le périphérique d'écran - il peut représenter un DPI plus élevé, alors qu'en revanche, votre' BufferedImage' fonctionne probablement entre 72-92 (?) DPI - Malheureusement, la dernière fois que j'ai vérifié (il y a un moment), il n'y a pas de moyen facile d'obtenir le DPI de l'écran (cela a peut-être changé récemment), donc vous pourriez passer un peu de temps la meilleure taille pour faire votre 'BufferedImage' – MadProgrammer

Répondre

0

Vous n'êtes pas vos deux chaînes dessin avec la même couleur. La couleur par défaut pour les graphiques hors écran est rgb (0, 0, 0) (c'est-à-dire, noir pur), tandis que Swing définit la couleur d'un objet Graphics à la couleur par défaut de l'aspect et du toucher. 7, en utilisant le thème par défaut, est rgb (51, 51, 51), ou gris foncé.

Essayez de placer g.setColor(Color.BLACK); dans votre méthode setupGraphics, pour vous assurer que les deux chaînes sont dessinées avec la même couleur.

0

Merci pour les réponses. En mentionnant DPI, MadProgrammer m'a conduit à une solution de travail que je propose ici plus comme solution de contournement que comme une solution «propre» pour être fier. Cela résout le problème, de toute façon.

J'ai remarqué que, bien que ma résolution d'écran soit 2880x1800 (affichage Retina), la méthode MouseEventgetPoint() lit x = 1440, y = 900 dans le coin inférieur droit de l'écran. Ensuite, la taille JPanel correspond à la moitié de la résolution de l'écran, bien qu'elle couvre tout l'écran.

Cette vue, la solution est la suivante:

  • d'abord, créer une image correspondant à offscreen la résolution d'écran, pas le JPanel.getSize() comme suggéré dans des dizaines d'articles à double tampon. Dessiner ensuite dans l'image hors écran en appliquant une transformée grossissante, plus grande que nécessaire, en particulier mise à l'échelle par rapport r = dimension de l'écran/dimension du panneau. Enfin, copiez une version réduite de l'image hors écran dans l'écran, en appliquant un facteur de réduction de r (ou facteur de mise à l'échelle 1/r).


La mise en œuvre de la solution est divisée en deux méthodes:

  • Une version du paintComponent des règlements ultérieurs initial posté plus tôt,
  • une méthode d'assistance getDPIFactor() expliqué par la suite.

La méthode paintComponent suit des règlements ultérieurs:

public final void paintComponent(Graphics g) { 
    super.paintComponent(g); 
    double dpiFactor = getDPIFactor(); 
    // -------------------------------------- 
    //OffScreen painting 

    Graphics2D osg2D = getOffscreenGraphics(); 
    setupGraphics(osg2D); 
    //Paint stuff bigger than needed 
    osg2D.setTransform(AffineTransform.getScaleInstance(dpiFactor, dpiFactor)); 
    //Custom painting 
    performPainting(osg2D); 

    //Shrink offscreen buffer to screen. 
    ((Graphics2D)g).drawImage(
      osi, 
      AffineTransform.getScaleInstance(1.0/dpiFactor, 1.0/dpiFactor), 
      this); 

    // -------------------------------------- 
    // OnScreen painting 
    Graphics2D gg = (Graphics2D)g; 
    setupGraphics(gg); 
    gg.drawString("Direct painting", 10, 35); 
} 

Pour terminer la tâche, il faut obtenir la résolution d'écran.

Un appel à Toolkit.getDefaultToolkit().getScreenResolution() ne résout pas le problème, car il renvoie la taille d'un JPanel couvrant tout l'écran. Comme vu ci-dessus, ce chiffre ne correspond pas à la taille réelle de l'écran dans les points physiques.

La façon d'obtenir cette donnée est autorisée par Sarge Bosch au this stackoverflow post. J'ai adapté son code pour implémenter la dernière partie du puzzle, getDPIFactor().


/* 
* Adapted from Sarge Bosch post in StackOverflow. 
* https://stackoverflow.com/questions/40399643/how-to-find-real-display-density-dpi-from-java-code 
*/ 
private Double getDPIFactor() { 
    GraphicsDevice defaultScreenDevice = 
      GraphicsEnvironment.getLocalGraphicsEnvironment() 
        .getDefaultScreenDevice(); 

    // on OS X, it would be CGraphicsDevice 
    if (defaultScreenDevice instanceof CGraphicsDevice) { 
     CGraphicsDevice device = (CGraphicsDevice) defaultScreenDevice; 

     // this is the missing correction factor. 
     // It's equal to 2 on HiDPI a.k.a. Retina displays 
     int scaleFactor = device.getScaleFactor(); 

     // now we can compute the real DPI of the screen 
     return scaleFactor * (device.getXResolution() + device.getYResolution())/2 
       /Toolkit.getDefaultToolkit().getScreenResolution(); 
    } else 
     return 1.0; 
} 

Ce code résout le problème pour les écrans Retina Mac, mais je suis affraid nulle part ailleurs, puisque CGraphicsDevice est une mention explicite à une mise en œuvre exclusive de GraphicsDevice.

Je n'ai pas d'autre matériel HDPI avec lequel jouer pour avoir une chance d'offrir une solution plus large.