Using Graphics2D to overlay text on a BufferedImage and return a BufferedImage
Asked Answered
L

1

47

I have checked similarly named questions, but they don't answer this use case.

Basically, I was to overlay some text (text) at a given coordinate (x,y) I have the below function in a package;

protected BufferedImage Process2(BufferedImage image){
    Graphics2D gO = image.createGraphics();
    gO.setColor(Color.red);
    gO.setFont(new Font( "SansSerif", Font.BOLD, 12 ));
    gO.drawString(this.text, this.x, this.y);
    System.err.println(this.text+this.x+this.y);
    return image;
}

I feel like im missing something patently obvious; every reference to Graphics2D I can find is dealing with either games or writing directly to a file but I just want a BufferedImage returned. with the overlay 'rendered'

In the current code, the image appears out the end unchanged.

Thanks!

Landman answered 17/4, 2010 at 13:48 Comment(6)
Are you trying to modify an image file? What you do should work but you're only modifying an in memory representation of the image.Distill
Also, are you sure that x and y are inside the image? Try to paint a square at coords (0,0) for a first testDistill
As an aside, this is one time it's appropriate to call dispose() on the Graphics2D instance.Workshop
@Distill no, its a pre-processed BufferedImage, no file-connection atall. Basically, I have an image source, and I have a completely separate input to define where x,y are, and the text, so i literally want to slap that text at those coords ontop of the buffered imageLandman
@Workshop I promise I'll clean it up once it works :DLandman
Your text drawing code works fine for me. I think the issue is outside the Process2 method.Cahilly
W
81

The method drawString() uses x and y for the leftmost character's baseline. Numbers typically have no descenders; if the same is true of text, a string drawn at position (0,0) will be rendered entirely outside the image. See this example.

Addendum: You may be having trouble with an incompatible color model in your image. One simple expedient is to render the image and then modify it in situ.

Hello

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.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;

/**
 * @see https://stackoverflow.com/questions/2658663
 */
public class TextOverlay extends JPanel {

    private BufferedImage image;

    public TextOverlay() {
        try {
            image = ImageIO.read(new URL(
                "http://cdn.sstatic.net/stackexchange/img/logos/so/so-logo.png"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        image = process(image);
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(image.getWidth(), image.getHeight());
    }

    private BufferedImage process(BufferedImage old) {
        int w = old.getWidth() / 3;
        int h = old.getHeight() / 3;
        BufferedImage img = new BufferedImage(
            w, h, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = img.createGraphics();
        g2d.drawImage(old, 0, 0, w, h, this);
        g2d.setPaint(Color.red);
        g2d.setFont(new Font("Serif", Font.BOLD, 20));
        String s = "Hello, world!";
        FontMetrics fm = g2d.getFontMetrics();
        int x = img.getWidth() - fm.stringWidth(s) - 5;
        int y = fm.getHeight();
        g2d.drawString(s, x, y);
        g2d.dispose();
        return img;
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(image, 0, 0, null);
    }

    private static void create() {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(new TextOverlay());
        f.pack();
        f.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                create();
            }
        });
    }
}
Workshop answered 17/4, 2010 at 14:27 Comment(5)
Well for my tests I'm using x = image.getHeight()/2 and likewise for y, so I dont think that that is the issue, but thanks!Landman
Any chance you're calling paintComponent() with the old image instead of the new?Workshop
I'm not using any JComponents so no, no paintComponents; but if im missing something fundamental, please slap me with it, I am not a GUI coder and have very little knowledge of dealing with graphics in Java so this could be completely the wrong approachLandman
Pulled out the relevent bit of code, thank you very much for your help!Landman
Works great, but needs some fix if running on Amazon Linux on EC2. See brandon.fuller.name/archives/2011/09/12/00.05.15 for details.Ileum

© 2022 - 2024 — McMap. All rights reserved.