Using Graphics2D to Overlay Text on a Bufferedimage and Return a Bufferedimage

Using Graphics2D to overlay text on a BufferedImage and return a BufferedImage

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();
}
});
}
}

Graphics2D overlaying image + text problem: text is wrong

Painting graphics is a lot like painting on a real world canvas, you are simply painting over the top of what is already there. If you want to "replace" a portion of the image, you need to paint over the top of it first.

In this case...

You could...

Fill a rectangle with a default color over the area that the text will be painted to.

This is problematic as you need to either know the "maximum" possible area covered or the size of the content that was painted previously.

Alternatively...

You could...

Start with a blank slate on each cycle. That is, start with a copy of the "master" image on each cycle and simply repaint the state into.

For example...

Background

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Main {
public static void main(String[] args) {
new Main();
}

public Main() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
ex.printStackTrace();
}
}
});
}

public class TestPane extends JPanel {

private BufferedImage master;
private BufferedImage background;
private Timer timer;

private DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("hh:mm:ss a");

public TestPane() throws IOException {
master = ImageIO.read(getClass().getResource("/images/background.jpg"));
background = new BufferedImage(master.getWidth(), master.getHeight(), BufferedImage.TYPE_INT_ARGB);
renderBackground();
}

protected void renderBackground() {
if (master == null) {
return;
}
Graphics2D g2d = background.createGraphics();
g2d.drawImage(master, 0, 0, this);

g2d.setFont(getFont());
FontMetrics fm = g2d.getFontMetrics();

String time = LocalTime.now().format(timeFormatter);

int x = (background.getWidth() - fm.stringWidth(time)) / 2;
int y = ((background.getHeight() - fm.getHeight()) / 2) + fm.getAscent();

g2d.drawString(time, x, y);

g2d.dispose();

repaint();
}

@Override
public Dimension getPreferredSize() {
if (master == null) {
return new Dimension(200, 200);
}

return new Dimension(master.getWidth(), master.getHeight());
}

@Override
public void addNotify() {
super.addNotify();
if (timer != null) {
timer.stop();
}
timer = new Timer(500, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
renderBackground();
}
});
timer.start();
}

@Override
public void removeNotify() {
super.removeNotify();
if (timer != null) {
timer.stop();
}
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (background == null) {
return;
}
Graphics2D g2d = (Graphics2D) g.create();
int x = (getWidth() - background.getWidth()) / 2;
int y = (getHeight() - background.getHeight()) / 2;
g2d.drawImage(background, x, y, this);
g2d.dispose();
}

}

}

Convert a Graphics2D to an Image or BufferedImage

I do it that way, and works very well:


BufferedImage awtImage = new BufferedImage(drawPanel.getWidth(), drawPanel.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics g = awtImage.getGraphics();
drawPanel.printAll(g);

try
{
String caminhoImagem = System.getProperty("user.home") + "\\temps\\assinatura.jpg";
FileOutputStream fos = new FileOutputStream(caminhoImagem);
JPEGImageEncoderImpl j = new JPEGImageEncoderImpl(fos);
j.encode(awtImage);
fos.close();
} catch(e) {..... }

That's all :)
Thanks everyone :)

Image Quality Loss When Drawing One BufferedImage to Another Using Graphics2D

The problem is this line from the clearLayers() method:

master = new BufferedImage((int)size.getWidth(), (int)size.getHeight(), frames[0].getType());

As the GIF uses a palette, the BufferedImage type will be TYPE_BYTE_INDEXED. However, if you pass this parameter to the BufferedImage constructor, it will use a default IndexColorModel (a built-in, fixed 256 color palette), not the palette from your GIF. Thus, the frames from the GIF will have to be dithered into the destination, as the colors doesn't match.

Instead, use TYPE_INT_RGB/TYPE_INT_ARGB for type, or use the constructor that also takes an IndexColorModel parameter and pass the IndexColorModel from the frames of the GIF.

In code:

master = new BufferedImage((int)size.getWidth(), (int)size.getHeight(), BufferedImage.TYPE_INT_ARGB);

Alternatively, the following should also work if all frames of the GIF uses the same palette (not necessarily the case):

master = new BufferedImage((int)size.getWidth(), (int)size.getHeight(), frames[0].getType(), (IndexColorModel) frames[0].getColorModel());

However, as the OP reports back the latter option doesn't work for him, the first option is probably safer. :-)

Fitting text into BufferedImage

The major problem is you 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);
}
});
}
}


Related Topics



Leave a reply



Submit