JButton image rendering badly - java

I'm making a application with Swing, and I want to add to the main panel a button with a cross icon on it. But when I draw an image on it, the image is rendering weirdly.
I've already tried several things like resizing the image outside the application, and the cross is made with IllustratorCC so I don't think it's the quality of the source image that's the issue.
import javax.imageio.*;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
public class ImageRenderingBadly extends JPanel
{
BufferedImage cross;
public ImageRenderingBadly()
{
try {
URL url = new URL("https://i.imgur.com/cWGntek.png");
cross = ImageIO.read(url);
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void paintComponent(Graphics g)
{
g.drawImage(cross,0,0,null);
}
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.setSize(new Dimension(200,200));
frame.setBackground(new Color(0));
ImageRenderingBadly panel = new ImageRenderingBadly();
frame.setContentPane(panel);
frame.setVisible(true);
}
}
Source:
Rendering badly:
FOUND THE SOLUTION
Use antialiasing in paintComponent :(https://docs.oracle.com/javase/tutorial/2d/advanced/quality.html)
#Override
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
RenderingHints rh = new RenderingHints(
RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g2.setRenderingHints(rh);
g2.drawImage(cross,0,0,null);
}

It works for me:
Note that I have used the image to create two images and Icons, one for the button, and one for the depressed state, to indicate that it has been pressed.
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class XButtonTest {
private static final String X_IMG_PATH = "https://i.imgur.com/cWGntek.png";
public static void main(String[] args) {
try {
URL xImgUrl = new URL(X_IMG_PATH);
BufferedImage xImage = ImageIO.read(xImgUrl);
int w = xImage.getWidth();
int h = xImage.getHeight();
BufferedImage pressedImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = pressedImg.createGraphics();
g2.setColor(Color.LIGHT_GRAY);
g2.fillRect(0, 0, w, h);
g2.drawImage(xImage, 0, 0, null);
g2.dispose();
Icon icon = new ImageIcon(xImage);
Icon pressedIcon = new ImageIcon(pressedImg);
JButton button = new JButton(icon);
button.setPressedIcon(pressedIcon);
button.setBorderPainted(false);
button.setFocusPainted(false);
button.setContentAreaFilled(false);
JPanel panel = new JPanel();
panel.add(button);
JOptionPane.showMessageDialog(null, panel, "Test", JOptionPane.PLAIN_MESSAGE);
} catch (IOException e) {
e.printStackTrace();
}
}
}

I figured out that the "drawImage" render badly image and that' the problem I think :
public class Panel extends JPanel
{
BufferedImage image;
public Panel() {
super();
try {
image = ImageIO.read(new File("images/BMW-TA.jpg"));
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(image, 0, 0, null);
}
}
Source : https://i.imgur.com/ebr17CV.jpg
Rendering : https://i.imgur.com/Z01I7mn.png
FOUND THE SOLUTION
Use antialiasing in paintComponent :
#Override
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
RenderingHints rh = new RenderingHints(
RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g2.setRenderingHints(rh);
g2.drawImage(cross,0,0,null);
}

Related

Having trouble aligning graphics in Java

So I am using a JFrame object to open up a window and add a bunch of graphics first I am adding an image then I am trying to add some lines, But it seems like the line start from the Y center of the previous image I would like for it to start at the top of the page
here is my code for the JFrame:
JFrame f = new JFrame();
JLabel trebeclef = new JLabel(new ImageIcon(getClass().getClassLoader().getResource("Some image")));
Draw d = new Draw();
f.add(trebeclef);
f.add(d);
f.setSize(1000,1000);
f.getContentPane().setBackground(Color.white);
f.setLayout(new FlowLayout(FlowLayout.LEFT,0,0));
f.pack();
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
here is the code for the Draw class
public void paint(Graphics g2) {
super.paintComponent(g2);
Graphics2D g = (Graphics2D) g2;
g.setStroke(new BasicStroke(2));
g.drawLine(0, 0, 100, 0);
}
it results in this
Any help appreciated, thanks
Trying to layout components like this isn't the easiest thing to to, especially when you consider the complexities of JLabel and the possibilities of other layout constraints.
If you have the image and you're drawing the lines via custom painting, I would just custom paint the whole thing
Starting with...
We can produce something like...
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public final 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 ClefWithLinesPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public class ClefWithLinesPane extends JPanel {
private BufferedImage trebbleClef;
public ClefWithLinesPane() throws IOException {
trebbleClef = ImageIO.read(getClass().getResource("/images/ClefLines.png"));
setBackground(Color.WHITE);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int x = 0;
int y = (getHeight() - trebbleClef.getHeight()) / 2;
g2d.drawImage(trebbleClef, x, y, this);
int[] lines = new int[] {
30, 60, 89, 120, 149
};
x = trebbleClef.getWidth();
g2d.setStroke(new BasicStroke(2));
for (int line = 0; line < lines.length; line++) {
y = lines[line];
g2d.drawLine(x, y, getWidth(), y);
}
g2d.dispose();
}
}
}
But... as you can see, the lines don't "quite" match up, now this is simply an issue with source image and you could spend some time cleaning it up, or, you could just dispense with the lines of the clef itself and do those you're self, for example...
Starting with...
We could produce something like...
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public final 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 ClefWithOutLinesPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public class ClefWithOutLinesPane extends JPanel {
private BufferedImage trebbleClef;
public ClefWithOutLinesPane() throws IOException {
trebbleClef = ImageIO.read(getClass().getResource("/images/Clef.png"));
setBackground(Color.WHITE);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int minLineY = 30;
int maxLineY = 150;
int lineSpacing = (maxLineY - minLineY) / 4;
int x = 10;
g2d.setStroke(new BasicStroke(8));
g2d.drawLine(x, minLineY + 3, x, maxLineY - 3);
int y = (getHeight() - trebbleClef.getHeight()) / 2;
g2d.drawImage(trebbleClef, x + 10, y, this);
g2d.setStroke(new BasicStroke(2));
for (int line = 0; line < 5; line++) {
y = minLineY + (lineSpacing * line);
g2d.drawLine(x, y, getWidth(), y);
}
g2d.dispose();
}
}
}
Adds after setLayout. One often uses an overloaded add with an extra parameter for a layout's specific constraint.
By default the content pane the frame's adds are done upon, has a BorderLayout with center, left and so on.
The horizontal "line" in a flow is vertically centered. Setting a preferred height ensures a line component is filled up.
d.setPreferredSize(new Dimension(100, 1000));
Of course an other layout might serve better; depending on what you want - just experiment.

How to delete shapes drawn on an Image

I am developing a small Paint tool. And I am able to load and draw Lines or Circles and other shapes on an image. Also I have an eraser tool to erase the shapes that I have drawn.
This is code for that:
g.setColor(getColor().WHITE);
g.fillRect(getXAxis() - getThickness(), getYAxis() - getThickness(), getThickness() * 2, getThickness() * 2);
My problem is that, If I have loaded an image and drawn some shapes on it. Then when I tried to erase the shapes, the image is also gets replaced with white color.
Is there any way to set the image as the background while using fillRect() to erase the shape, so that my image will be untouched.
Here is the example. To test it you need to replace my image with your background image.
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
/**
* <code>PaintTryout</code>.
*
* #author smedvynskyy
*/
public class PaintPanel extends JPanel {
private Image backgroundImage;
private BufferedImage paintImage;
public PaintPanel() {
try {
// replace this image with your image
backgroundImage = ImageIO.read(new File("E:\\icons\\blackboard.png"));
paintImage = new BufferedImage(backgroundImage.getWidth(this),
backgroundImage.getHeight(this), BufferedImage.TYPE_INT_ARGB);
} catch (final Exception e) {
e.printStackTrace();
}
}
public void fillRect() {
final Graphics g = paintImage.createGraphics();
g.setColor(Color.RED);
g.fillRect(0, 0, 50, 50);
g.dispose();
repaint();
}
public void clearRect() {
final Graphics2D g = paintImage.createGraphics();
g.setColor(new Color(0, 0, 0, 0));
g.setComposite(AlphaComposite.Clear); // overpaint
g.fillRect(0, 0, 50, 50);
g.dispose();
repaint();
}
/**
* {#inheritDoc}
*/
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(backgroundImage, 0, 0, this);
g.drawImage(paintImage, 0, 0, this);
}
/**
* {#inheritDoc}
*/
#Override
public Dimension getPreferredSize() {
return new Dimension(backgroundImage.getWidth(this),
backgroundImage.getHeight(this));
}
public static void main(String[] args) {
final JFrame frm = new JFrame("Tesp paint");
final PaintPanel p = new PaintPanel();
frm.add(p);
final JPanel buttons = new JPanel();
final JButton fill = new JButton("Fill Rect");
fill.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
p.fillRect();
}
});
final JButton clear = new JButton("Clear Rect");
clear.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
p.clearRect();
}
});
buttons.add(fill);
buttons.add(clear);
frm.add(buttons, BorderLayout.SOUTH);
frm.pack();
frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frm.setLocationRelativeTo(null);
frm.setVisible(true);
}
}
The easy way to do this is to draw the lines in XOR mode. Then, to erase them, you just draw them again.

Displaying an image over another

Currently I am having an issue whereby the Robot.png is replacing the image of my gameboard.png . I want to make it so that the robot .png is ontop of the board and be able to move the robot around the board.
Board.Java
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
public class Board extends JPanel {
private Image gameboard;
public Board(){
initBoard();
}
private void initBoard(){
loadImage();
int w = gameboard.getWidth(this);
int h = gameboard.getHeight(this);
setPreferredSize(new Dimension(w,h));
}
private void loadImage(){
ImageIcon i = new ImageIcon("res/gameboard.png");
gameboard = i.getImage();
}
#Override
public void paintComponent(Graphics g){
g.drawImage(gameboard,0,0,null);
}
}
Player.Java
import javax.swing.*;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import javax.swing.ImageIcon;
public class Player extends JPanel {
public void paint(Graphics g){
Graphics2D g2d = (Graphics2D)g;
ImageIcon ic = new ImageIcon("res/Robot.png");
Image image1 = ic.getImage();
g2d.drawImage(image1, 100, 100, null);
}
}
GameGUI.Java
import javax.swing.JFrame;
public class GameGui extends JFrame {
public GameGui(){
initGui();
}
public void initGui(){
add(new Board());
add(new Player());
setTitle("11+ Game");
pack();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setSize(1240,620);
}
}
You don't need to reload the robot image every time. You should use ImageIO to load it, and save it in a member variable.
Otherwise, the way to not have the image overwrite the whole background image is to use height and width parameters with your drawImage call
public void paint(Graphics g){
Graphics2D g2d = (Graphics2D)g;
ImageIcon ic = new ImageIcon("res/Robot.png");
Image image1 = ic.getImage();
int width = ..., height = ...;
g2d.drawImage(image1, 100, 100, width, height, null);
}

How to use AffineTransform.quadrantRotate to rotate a bitmap?

I want to rotate a bitmap about its center point, and then draw it into a larger graphics context.
The bitmap is 40x40 pixels. The graphics context is 500x500 pixels. This is what I'm doing:
BufferedImage bi = new BufferedImage(500, 500, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bi.createGraphics();
AffineTransform at = new AffineTransform();
at.quadrantRotate(1, -20, -20); // rotate 90 degrees around center point.
at.translate(100, 40); // I want to put its top-left corner at 100,40.
g.drawImage(smallerBitmap, at, null);
...
I'm probably using quadrantRotate() incorrectly - if I remove that line, my image gets drawn at position 100,40 correctly at least.
What am I doing wrong?
Thanks
The order of your transformation matters. Basically your example code is saying "rotate the image by 90 degrees AND then translate it...."
So, using your code (rotate, then translate) produces...
Switching the order (translate then rotate) produces
...
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class TestRotation100 {
public static void main(String[] args) {
new TestRotation100();
}
public TestRotation100() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
final TestPane testPane = new TestPane();
final JSlider slider = new JSlider(0, 3);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
testPane.setQuad(slider.getValue());
}
});
slider.setValue(0);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(testPane);
frame.add(slider, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private BufferedImage img;
private int quad = 0;
public TestPane() {
try {
img = ImageIO.read(new File("/Users/swhitehead/Dropbox/MegaTokyo/Rampage_Small.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
public void setQuad(int quad) {
this.quad = quad;
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
AffineTransform at = new AffineTransform();
at.translate(100, 40);
at.quadrantRotate(quad, img.getWidth() / 2, img.getHeight() / 2);
g2d.drawImage(img, at, this);
g2d.dispose();
}
}
}

Drawing a bordered path with sharp corners in Java

I am working on an application that displays GPS tracks on a map. I want to draw the track as a colored path of arbitrary thickness. I found the GeneralPath class which seems to do just what I want. However, I want the colored path to also have a black border. I couldn't find anything about how to add a border to a path, so I came up with a quick hacky solution of drawing a thick black path first, and then drawing a thin colored path on top.
SSCCE:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.GeneralPath;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class PathBorder {
private JFrame frame;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
PathBorder window = new PathBorder();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public PathBorder() {
initialize();
}
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 450, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel() {
protected void paintComponent(Graphics g) {
GeneralPath path;
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(new BasicStroke(5.5f));
g2d.setColor(Color.BLACK);
path = new GeneralPath();
path.moveTo(0, 0);
path.lineTo(100, 100);
path.lineTo(200, 100);
path.lineTo(100, 80);
g2d.draw(path);
g2d.setStroke(new BasicStroke(3));
g2d.setColor(Color.YELLOW);
g2d.draw(path);
}
};
frame.setBackground(Color.CYAN);
frame.add(panel);
}
}
Here is a pic (taken from the SSCCE) to highlight my problem. See the red circle below, notice how the outside border has a gap in it. I want that gap to be filled in so the border is continuous.
Here are some actual screenshots from my app of a real track:
If you look closely at the sharp hairpin turn in the lower right of the track, you'll see that the border gets lost briefly... the detailed pic below makes it more clear.
I am not sure exactly how to fix this, but I'm open to suggestions, either keeping with the GeneralPath strategy, or using a different idea entirely.
Experiment with the cap and join parameters for a better effect. E.G.
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.GeneralPath;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class PathBorder {
private JFrame frame;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
PathBorder window = new PathBorder();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public PathBorder() {
initialize();
}
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 450, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel() {
GeneralPath path;
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
BasicStroke s = new BasicStroke(
5.5f,
BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND);
g2d.setStroke(s);
g2d.setColor(Color.BLACK);
if (path==null) {
path = new GeneralPath();
path.moveTo(0, 0);
path.lineTo(100, 100);
path.lineTo(200, 100);
path.lineTo(100, 80);
}
g2d.draw(path);
g2d.setStroke(new BasicStroke(3));
g2d.setColor(Color.YELLOW);
g2d.draw(path);
}
};
frame.setBackground(Color.CYAN);
frame.add(panel);
}
}

Categories