Java2D / Swing: рендеринг компонента с сглаживанием текста в BufferedImage - PullRequest
5 голосов
/ 03 ноября 2011

Я хотел бы визуализировать компонент Java Swing, например JButton, который я также помещаю в JFrame, в BufferedImage. В целом это работает, но с основным недостатком: сглаживание текста, особенно режим сглаживания «LCD», не работает при рендеринге в BufferedImage.

Я собрал несколько примеров кода, чтобы продемонстрировать проблему, но сначала моя системная информация:

  • ОС : Windows 7, 64-битная
  • JVM : 1.6.0_26-b03 (32 бита)

Следующий пример кода создаст простой JFrame, поместит в него JButton и затем отобразит JButton в файл "test.png":

public class TextAntiAliasingTest
{
  public TextAntiAliasingTest() throws IOException
  {
    // Create Test-Button which will be rendered to an image
    JButton button = new JButton( "The Test-Button" );
    button.setSize( 200, 70 );
    button.setLocation( 200, 150 );

    // Create JFrame
    final JFrame frame = new JFrame();
    frame.setSize( 800, 600 );
    frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
    frame.setLayout( null );
    frame.setLocationRelativeTo( null );
    frame.add( button );

    // Show JFrame
    SwingUtilities.invokeLater( new Runnable() {
        @Override public void run() {
            frame.setVisible( true );
        }
    });

    // Render JButton to an BufferedImage
    BufferedImage image = new BufferedImage( 800, 600, BufferedImage.TYPE_INT_ARGB );
    Graphics2D g2d = (Graphics2D)image.getGraphics();
    button.paint( g2d );

    // Write BufferedImage to a PNG file
    ImageIO.write( image, "PNG", new File( "test.png" ) );
  }

  public static void main( String[] args ) throws Exception
  {
    UIManager.setLookAndFeel( "com.sun.java.swing.plaf.windows.WindowsLookAndFeel" );
    System.setProperty( "awt.useSystemAAFontSettings", "lcd" );

    new TextAntiAliasingTest();
  }
}

На следующем изображении показана разница между JButton в JFrame на экране и тем же визуализированным JButton в файле изображения:

enter image description here

На самом деле на изображении присутствует некоторое сглаживание текста, но не оптимизированное сглаживание на ЖК-дисплее, которое отображается на экране в JFrame (эта проблема возникает также при использовании LookAndFeel по умолчанию, а не только с WindowsLookAndFeel).

Я уже пытался явно установить RenderingHint для сглаживания текста в «g2d», контекст Graphics2D объекта BufferedImage выглядит так:

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

Но это никак не повлияло.

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

Я очень ценю любую помощь - большое спасибо!

Ответы [ 3 ]

3 голосов
/ 13 октября 2013

Это ошибка JVM

Я столкнулся с той же проблемой, что и вы. И я пришел к выводу, что проблема действительно в том, чтобы нарисовать полупрозрачное растровое изображение. Я вполне уверен, что мы попали: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6749069

Обход

А пока я рисую в непрозрачном растровом изображении и добавляю его в мое целевое растровое изображение. Если цвет фона позади вашего текста простой, это должно работать:

private void drawString(String text, int x, int y, Graphics2D g, Color bg){    
    // Prepare an off-screen image to draw the string to
    Rectangle2D bounds = g.getFontMetrics().getStringBounds(text, g);
    BufferedImage image = configuration.createCompatibleImage(
                              (int)(bounds.getWidth() + 1.0f), 
                              (int)(bounds.getHeight() + 1.0f),
                              Transparency.OPAQUE);
    Graphics2D ig = image.createGraphics();

    // Fill the background color
    ig.setColor(bg);
    ig.fillRect(0, 0, image.getWidth(), image.getHeight());

    // Draw the string
    int x0 = 0;
    int y0 = ig.getFontMetrics().getAscent();
    ig.setColor(g.getColor());
    ig.setRenderingHints(g.getRenderingHints());
    ig.setFont(g.getFont());
    ig.drawString(text, x0, y0);
    ig.dispose();

    // Blit the image to the destination
    g.drawImage(image, x-x0, y-y0, null);
}

Если ваш фон не является простым цветом, вам придется отобразить текст белым на черном фоне в растровое изображение, A. Затем заполните другое растровое изображение цветом текста C. Затем добавьте это к вашему целевому изображению, D как: D += A*C или D = D*(1-A)+A*C или другую подходящую функцию смешивания.

2 голосов
/ 25 марта 2012

Возможно, уже слишком поздно.Проблема может быть связана с используемым полупрозрачным BufferedImage изображением.Кажется, работает следующее:

BufferedImage image = new BufferedImage( 800, 600, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = (Graphics2D)image.getGraphics();
g2d.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);

Оглядываясь вокруг, я обнаружил похожие жалуется .

0 голосов
/ 06 мая 2012

Либо вы хотите, чтобы (a) ваш полупрозрачный BufferedImage выглядел так же хорошо, как ваш JButton, или (b) вы хотите, чтобы ваши JButton и BufferedImage выглядели одинаково.

Если (б), можете ли вы иметь полупрозрачный JButton? Будет ли он выглядеть так же, как ваш полупрозрачный BufferedImage?

Если (а) то сложный способ сделать это, вы можете нарисовать JButton в непрозрачный BufferedImage (если у Java есть BufferedImage в оттенках серого, используйте это). Это в конечном итоге будет отрицательным для вашего окончательного альфа-канала BufferedImage (черный пиксель становится полностью непрозрачным, белый - полностью прозрачным). Чтобы получить цвет для пикселей в BufferedImage, вам нужно будет установить любой непрозрачный пиксель. к черному - прозрачность пикселя должна сделать его таким, каким вы хотите.

...