Как исправить качество текста в Java-графике?

код:

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;

import javax.swing.JFrame;

public class TextRectangle extends JFrame {

    private static final long serialVersionUID = 1L;

    public static void main(String[] a) {
        TextRectangle f = new TextRectangle();
        f.setSize(300, 300);
        f.setVisible(true);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public void paint(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(
            RenderingHints.KEY_TEXT_ANTIALIASING,
            RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        g2d.setRenderingHint(
            RenderingHints.KEY_RENDERING,
            RenderingHints.VALUE_RENDER_QUALITY);

        g2d.setColor(Color.WHITE);
        g2d.fillRect(0, 0, getWidth(), getHeight());

        g2d.setColor(Color.BLACK);
        g2d.setFont(new Font("Calibri", Font.BOLD, 18));
        g2d.drawString("Why does this text look so ugly?", 30, 150);
    }

}

Результат:

result screenshot

Как видите, расстояние между буквами неравномерно. Иногда это 1px, иногда это 2 или даже 3 пикселя. Другой проблемой является точка выше "i" шире, чем нижняя часть.

problem details

Тот же текст, написанный в текстовом редакторе, выглядит намного лучше. Вы знаете, как исправить эту проблему?

Моя среда - это Windows 8.1, Java 1.8.

Ответ 1

Графический контекст использует целочисленные показатели по умолчанию - это означает, что округление применяется к векторам местоположения форм глифов.

Вы можете переключиться на дробные показатели, используя:

g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
    RenderingHints.VALUE_FRACTIONALMETRICS_ON);

enter image description here

Как вы можете видеть, не использовалось субпиксельное сглаживание (только серый пиксель сглаживания). Вы можете включить сглаживание подпикселей для лучшей разборчивости на ЖК-экранах:

g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, 
    RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);

enter image description here

Существует 4 режима:

  • VALUE_TEXT_ANTIALIAS_LCD_HRGB: горизонтально ориентированный RGB
  • VALUE_TEXT_ANTIALIAS_LCD_HBGR: горизонтально ориентированный BGR
  • VALUE_TEXT_ANTIALIAS_LCD_VRGB: вертикально ориентированный RGB
  • VALUE_TEXT_ANTIALIAS_LCD_VBGR: вертикально ориентированный BGR

Я не узнал, где запросить соответствующее значение текущего отображения и ориентации. Некоторые дисплеи можно наклонять (пейзаж/портрет) или даже поворачивать, требуя повторного отображения режима при рисовании.

ИЗМЕНИТЬ

Я нашел что-то в Filthy Rich Clients: очевидно, Java AWT Toolkit может предоставить соответствующие подсказки рендеринга:

Map<?, ?> desktopHints = 
    (Map<?, ?>) Toolkit.getDefaultToolkit().getDesktopProperty("awt.font.desktophints");

Graphics2D g2d = (Graphics2D) g;
if (desktopHints != null) {
    g2d.setRenderingHints(desktopHints);
}
// no need to set more rendering hints

В моей системе это делает текст с дробными метриками и сглаживанием LCD HRGB, как и с приведенным выше кодом. В моей системе есть подсказки:

  • Клавиша включения сглаживания текста: Текстовый режим сглаживания ЖК-экрана HRGB
  • Контекстно-зависимая клавиша ЖК-дисплея: 120