javaswingjava-2d

Fitting text into BufferedImage


I wrote this program to find a way I could draw a String centered onto a BufferedImage of any size (in this case, the size of the BufferedImage is the same as the JPanel its in) and location. When I resize the JFrame, the text flickers as it repositions itself in the BufferedImage and I don't know why.

import java.awt.*;
import java.awt.image.BufferedImage;

import javax.swing.*;

@SuppressWarnings("serial")
class test extends JPanel
{
    double scale = 0;
    String draw = "1";

test()
{
    setPreferredSize(new Dimension(600, 600));
}

public void paintComponent(Graphics g)
{
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D)g;
    paintText(g2, 0, 0);
}

public void paintText(Graphics2D g, int x, int y)
{   
    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    BufferedImage bi = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
    Graphics2D big = (Graphics2D) bi.getGraphics();
    FontMetrics fm = big.getFontMetrics(); 

    scale = bi.getHeight()/(fm.getHeight());
    double xt = -(((scale*bi.getWidth())-bi.getWidth())/2);
    double yt = -(((scale*bi.getHeight())-bi.getHeight())/2);
    big.translate(xt, yt); 
    big.scale(scale, scale);
    big.drawString(draw, (bi.getWidth()/2)-(fm.stringWidth(draw)/2), (bi.getHeight()/2)+(fm.getHeight()/4)+1);
    g.drawImage(bi, x, y, this);
}

public static void main(String[] args)
{
    JFrame frame = new JFrame("test");
    frame.add(new test());
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.pack();
    frame.setVisible(true);
}
}

Solution

  • The major problem is your attempt to translate and the center the text within the BufferedImage, the calculations are causing a certain amount of drift as the size of the window is changed, meaning that they don't produce an accurate position.

    After much stuffing around, I basically threw out your BufferedImage and setScale approach and simple derived a new font based on the scale property.

    Now, you could still generate a BufferedImage, but I would use the resulting FontMetrics to determine the actual size of the image and simply render the image at the center position of the frame...but that's me

    ScaledFont

    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Font;
    import java.awt.FontMetrics;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.RenderingHints;
    import java.awt.geom.AffineTransform;
    import java.awt.image.BufferedImage;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    class Test101 extends JPanel {
    
        double scale = 0;
        String draw = "1";
    
        Test101() {
            setPreferredSize(new Dimension(600, 600));
        }
    
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2 = (Graphics2D) g;
            paintText(g2, 0, 0);
    
            int x = getWidth() / 2;
            int y = getHeight() / 2;
            g2.setColor(Color.RED);
            g2.drawLine(x, 0, x, getHeight());
            g2.drawLine(0, y, getWidth(), y);
        }
    
        public void paintText(Graphics2D g, int x, int y) {
            
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setColor(Color.BLACK);
            g2d.fillRect(0, 0, getWidth(), getHeight());
            
            FontMetrics fm = g.getFontMetrics();
            System.out.println(fm.getAscent());
            scale = getHeight() / (fm.getHeight());
            
            Font font = g.getFont().deriveFont(Font.PLAIN, AffineTransform.getScaleInstance(scale, scale));
            g2d.setFont(font);
            g2d.setColor(Color.WHITE);
            fm = g.getFontMetrics(font);
            int xPos = (getWidth() - fm.stringWidth(draw)) / 2;
            int yPos = ((getHeight() - fm.getHeight()) / 2) + fm.getAscent();
            g2d.drawString(draw, xPos, yPos);
            g2d.dispose();
            
        }
    
        public static void main(String[] args) {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    }
    
                    JFrame frame = new JFrame("test");
                    frame.add(new Test101());
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    }