I'd like to have a component that shows an image in Java without zooming on HiDPI screens.
The obvious candidate is to use a JLabel. I've succesfully done it by overwriting the AffineTransform in paintComponent but it remains the problem that the label preferred size is incorrect on HiDPI.
The following example seems to work even when I move the window from HiDPI (4K scaled 1.75) to FullHD (scale 1.0) except for the size of the component on HiDPI which is larger than the image displayed.
How can I achieve to have the preferred size of a component not to be zoomed on a HiDPI screen?
public class UnscaledImage extends JPanel {
private AffineTransform noChangeTransform = new AffineTransform();
private JLabel imageLabel;
private JLabel colorLabel = new JLabel("Color");
private BufferedImage image;
private double scale = 1.0;
public UnscaledImage() {
initUI();
packAndShow("Unscaled Image");
}
public void initUI() {
setLayout(new BorderLayout());
ImageIcon imageIcon = (ImageIcon) UIManager.getIcon("OptionPane.warningIcon");
image = new BufferedImage(imageIcon.getIconWidth(), imageIcon.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = image.createGraphics();
g2d.drawImage(imageIcon.getImage(), 0, 0, null);
g2d.dispose();
imageLabel = new JLabel() {
#Override
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
AffineTransform oldTransform = g2d.getTransform();
scale = oldTransform.getScaleX();
Dimension labelPrefSize = imageLabel.getPreferredSize();
if (scale != 1.0 && labelPrefSize.width == image.getWidth()) {
imageLabel.setSize(new Dimension((int) (labelPrefSize.width / scale), (int) (labelPrefSize.height / scale)));
SwingUtilities.windowForComponent(imageLabel).pack();
}
g2d.setTransform(noChangeTransform);
super.paintComponent(g);
g2d.setTransform(oldTransform);
}
};
imageLabel.setIcon(imageIcon);
imageLabel.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseMoved(MouseEvent me) {
int mouseX = (int) (me.getX() * scale);
int mouseY = (int) (me.getY() * scale);
if (mouseX < image.getWidth() && mouseY < image.getHeight()) {
int color = image.getRGB(mouseX, mouseY);
colorLabel.setBackground(new Color(color));
}
}
});
colorLabel.setOpaque(true);
colorLabel.setBackground(Color.WHITE);
add(imageLabel, BorderLayout.CENTER);
add(colorLabel, BorderLayout.SOUTH);
}
public void packAndShow(String title) {
JFrame frame = new JFrame(title);
frame.add(this);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public final static void main(String[] args) {
SwingUtilities.invokeLater(() -> new UnscaledImage());
}
}
Related
I having trouble displaying the x,y coordinates of the mouse on JPopupMenu under mouse pointer. the reason that i use JlayerPane in the code is to be able adding cartesian coordinate system dynamically to the JPopupMenu like what you can see in the CAD application.
Does anyone know why it does not display while it paint correctly?
public void show popupwindow(JPanel parentPanel, int xLocation, int yLocation){
Box selectionBox = createSelectionBox();
JPopupMenu popupMenu = new JPopupMenu();
// create an instance of my custom mouse cursor label
XYMouseLabel mouseLabel = new XYMouseLabel();
mouseLabel.setBounds(0, 0, selectionBox.getWidth(), selectionBox.getHeight());
JPanel panel = new JPanel();
panel.setSize(new Dimension(500, 400));
JLayeredPane layeredPane = new JLayeredPane();
layeredPane.setPreferredSize(new Dimension(400, 300));
layeredPane.setBorder(BorderFactory.createTitledBorder("Move the Mouse to get coordinate"));
layeredPane.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseMoved(MouseEvent me) {
super.mouseMoved(me);
mouseLabel.x = me.getX();
mouseLabel.y = me.getY();
mouseLabel.repaint();
}
});
layeredPane.add(mouseLabel , JLayeredPane.DRAG_LAYER);
layeredPane.add(selectionBox, 2, 0);
panel.add(layeredPane);
popupMenu.add(panel);
popupMenu.show(parentPanel , xLocation,yLocation)
}
public class XYMouseLabel extends JComponent {
public int x;
public int y;
public XYMouseLabel() {
this.setBackground(Color.green);
}
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//hier paint the cartesian coordinate system
String s = x + ", " + y;
g2.setColor(Color.red);
g2.drawString(s, x, y);
}
}
I've a class extending JLabel. This JLabel has a particolar shape and I draw that in the method paintComponent. I want to show a text in the center of the jLabel but this text is not shown. Could anyone help me.
My simple HLabel class in the following:
private class Scudetto extends JLabel {
private static final long serialVersionUID = 1L;
public Scudetto(String line_point)
{
super(line_point, SwingUtilities.CENTER);
this.setOpaque(true);
this.setBackground(Color.BLACK);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Dimension d = this.getSize();
int[] x = { 0, d.width, d.width, d.width / 2, 0 };
int[] y = { 0, 0, d.height / 2, d.height, d.height / 2 };
g.setColor(Color.WHITE);
g.fillPolygon(x, y, 5);
g.setColor(Color.BLACK);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(10, 20);
}
}
I want to show a text in the center of the jLabel but this text is not shown.
The super.paintComponent() will paint the text, but then your custom painting will paint the polygon over top of the text.
Don't override the JLabel. Instead you can create a PolygonIcon. Then you add the Icon and text to the JLabel.
You can have the text centered on the label by using:
JLabel label = new JLabel("your text");
label.setIcon( polygonIcon );
label.setHorizontalTextPosition(JLabel.CENTER);
label.setVerticalTextPosition(JLabel.CENTER);
Here is a simple example of creating a rectangular Icon:
import java.awt.*;
import javax.swing.*;
public class ColorIcon implements Icon
{
private Color color;
private int width;
private int height;
public ColorIcon(Color color, int width, int height)
{
this.color = color;
this.width = width;
this.height = height;
}
public int getIconWidth()
{
return width;
}
public int getIconHeight()
{
return height;
}
public void paintIcon(Component c, Graphics g, int x, int y)
{
g.setColor(color);
g.fillRect(x, y, width, height);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI()
{
JPanel panel = new JPanel( new GridLayout(2, 2) );
for (int i = 0; i < 4; i++)
{
Icon icon = new ColorIcon(Color.RED, 50, 50);
JLabel label = new JLabel( icon );
label.setText("" + i);
label.setHorizontalTextPosition(JLabel.CENTER);
label.setVerticalTextPosition(JLabel.CENTER);
panel.add(label);
}
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(panel);
f.setSize(200, 200);
f.setLocationRelativeTo( null );
f.setVisible(true);
}
}
I'll let you modify the code for a polygon.
You can also check out Playing With Shapes for some fun ways to create an Icon of different shapes.
I have created a custom JSlider that is used to zoom in and out on an image. I want to add a scroll bar when the image becomes to large to fit into my 400x400 frame so that the user is able to pan across the image, there should not be a scroll bar if the image fits the frame. I am very new to Swing so any help would be greatly appreciated I cant seem to get anything to work.
public class GraphicsOnly extends JComponent implements ChangeListener {
JPanel gui;
/** Displays the image. */
JLabel imageCanvas;
Dimension size;
double scale = 1.0;
private BufferedImage image;
public GraphicsOnly() {
size = new Dimension(10,10);
setBackground(Color.black);
try {
image = ImageIO.read(new File("car.jpg"));
} catch (IOException ex) {
}
}
public void setImage(Image image) {
imageCanvas.setIcon(new ImageIcon(image));
}
public void initComponents() {
if (gui==null) {
gui = new JPanel(new BorderLayout());
gui.setBorder(new EmptyBorder(5,5,5,5));
imageCanvas = new JLabel();
JPanel imageCenter = new JPanel(new GridBagLayout());
imageCenter.add(imageCanvas);
JScrollPane imageScroll = new JScrollPane(imageCenter);
imageScroll.setPreferredSize(new Dimension(300,100));
gui.add(imageScroll, BorderLayout.CENTER);
}
}
public Container getGui() {
initComponents();
return gui;
}
public void stateChanged(ChangeEvent e) {
int value = ((JSlider)e.getSource()).getValue();
scale = value/100.0;
repaint();
revalidate();
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int w = getWidth();
int h = getHeight();
int imageWidth = image.getWidth();
int imageHeight = image.getHeight();
double x = (w - scale * imageWidth)/2;
double y = (h - scale * imageHeight)/2;
AffineTransform at = AffineTransform.getTranslateInstance(x,y);
at.scale(scale, scale);
g2.drawRenderedImage(image, at);
setImage(image);
}
public Dimension getPreferredSize() {
int w = (int)(scale*size.width);
int h = (int)(scale*size.height);
return new Dimension(w, h);
}
private JSlider getControl() {
JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 500, 50);
slider.setMajorTickSpacing(50);
slider.setMinorTickSpacing(25);
slider.setPaintTicks(true);
slider.setPaintLabels(true);
slider.addChangeListener(this);
return slider;
}
public static void main(String[] args) {
GraphicsOnly app = new GraphicsOnly();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(app.getGui());
app.setImage(app.image);
// frame.getContentPane().add(new JScrollPane(app));
frame.getContentPane().add(app.getControl(), "Last");
frame.setSize(700, 500);
frame.setLocation(200,200);
frame.setVisible(true);
}
}
This version works. There were a number of problems with the attempt seen above, including that it was now mixing component painting with custom painting. I adapted the paintComponent method to instead paint a scaled image.
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.*;
import java.net.URL;
import javax.imageio.ImageIO;
public class GraphicsOnly extends JComponent implements ChangeListener {
JPanel gui;
/**
* Displays the image.
*/
JLabel imageCanvas;
Dimension size;
double scale = 1.0;
private BufferedImage image;
public GraphicsOnly() {
size = new Dimension(10, 10);
setBackground(Color.black);
try {
image = ImageIO.read(new URL("http://i.stack.imgur.com/7bI1Y.jpg"));
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void setImage(Image image) {
imageCanvas.setIcon(new ImageIcon(image));
}
public void initComponents() {
if (gui == null) {
gui = new JPanel(new BorderLayout());
gui.setBorder(new EmptyBorder(5, 5, 5, 5));
imageCanvas = new JLabel();
JPanel imageCenter = new JPanel(new GridBagLayout());
imageCenter.add(imageCanvas);
JScrollPane imageScroll = new JScrollPane(imageCenter);
imageScroll.setPreferredSize(new Dimension(300, 100));
gui.add(imageScroll, BorderLayout.CENTER);
}
}
public Container getGui() {
initComponents();
return gui;
}
public void stateChanged(ChangeEvent e) {
int value = ((JSlider) e.getSource()).getValue();
scale = value / 100.0;
paintImage();
}
protected void paintImage() {
int w = getWidth();
int h = getHeight();
int imageWidth = image.getWidth();
int imageHeight = image.getHeight();
BufferedImage bi = new BufferedImage(
(int)(imageWidth*scale),
(int)(imageHeight*scale),
image.getType());
Graphics2D g2 = bi.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
double x = (w - scale * imageWidth) / 2;
double y = (h - scale * imageHeight) / 2;
AffineTransform at = AffineTransform.getTranslateInstance(0, 0);
at.scale(scale, scale);
g2.drawRenderedImage(image, at);
setImage(bi);
}
public Dimension getPreferredSize() {
int w = (int) (scale * size.width);
int h = (int) (scale * size.height);
return new Dimension(w, h);
}
private JSlider getControl() {
JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 500, 50);
slider.setMajorTickSpacing(50);
slider.setMinorTickSpacing(25);
slider.setPaintTicks(true);
slider.setPaintLabels(true);
slider.addChangeListener(this);
return slider;
}
public static void main(String[] args) {
GraphicsOnly app = new GraphicsOnly();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(app.getGui());
app.setImage(app.image);
// frame.getContentPane().add(new JScrollPane(app));
frame.getContentPane().add(app.getControl(), "Last");
frame.setSize(700, 500);
frame.setLocation(200, 200);
frame.setVisible(true);
}
}
I would like to make four panels using different backgrounds, and merge them together using a BorderLayout. I used JLabel, but I can't add any component to a JLabel, therefore I need to make it as a background.
I've search some code but it only tell how to add a background in JFrame.
import javax.swing.*;
import java.awt.*;
public class LoginPanel extends JFrame{
private ImageIcon top = new ImageIcon("C:/Users/user/Desktop/top.png");
private ImageIcon mid = new ImageIcon("C:/Users/user/Desktop/mid.png");
private ImageIcon center = new ImageIcon("C:/Users/user/Desktop/center.png");
private ImageIcon bottom = new ImageIcon("C:/Users/user/Desktop/bottom.png");
public LoginPanel(){
JPanel topp = new JPanel();
topp.setLayout(new BorderLayout(0,0));
topp.add(new JLabel(top),BorderLayout.NORTH);
JPanel centerp = new JPanel();
centerp.setLayout(new BorderLayout(0,0));
centerp.add(new JLabel(mid),BorderLayout.NORTH);
centerp.add(new JLabel(center),BorderLayout.SOUTH);
topp.add(new JLabel(bottom),BorderLayout.SOUTH);
topp.add(centerp,BorderLayout.CENTER);
add(topp);
}
public static void main(String[] args) {
LoginPanel frame = new LoginPanel();
frame.setTitle("Test");
frame.setSize(812, 640);
frame.setLocationRelativeTo(null); // Center the frame
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
I would make a new class called JImagePanel, and then use that:
class JImagePanel extends JComponent {
private static final long serialVersionUID = 1L;
public BufferedImage image;
public JImagePanel(BufferedImage image)
{
this.image = image;
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
// scale image
BufferedImage before = image;
int w = before.getWidth();
int h = before.getHeight();
BufferedImage after = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
AffineTransform at = new AffineTransform();
at.scale(2.0, 2.0);
AffineTransformOp scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
after = scaleOp.filter(before, after);
// center image and draw
Graphics2D g2d = (Graphics2D) g;
int x = (getWidth() - 1 - image.getWidth(this)) / 2;
int y = (getHeight() - 1 - image.getHeight(this)) / 2;
g2d.drawImage(image, x, y, this);
g2d.dispose();
}
}
Check out Custom Painting and 2D Graphics
Check out
Add image to panel not using swings
Java: maintaining aspect ratio of JPanel background image
Java background JFrame with a Jpanel arranging images in a grid
I've done days of searching for a way to draw pixels to a window in java with mouse capture. I'm looking for some framework I can just plug in. It seems like it would be so simple... Any help will be greatly appreciated.
(EDIT)
Here is some non-working code.
public class Base extends JPanel implements MouseMotionListener {
public static void main(String[] args) {
new Base();
}
final static int width = 800;
final static int height = 600;
BufferedImage img;
Base() {
img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE);
JFrame frame = new JFrame();
frame.addMouseMotionListener(this);
frame.add(this);
frame.setSize(width, height);
frame.setEnabled(true);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
#Override
public void mouseDragged(MouseEvent e) {
}
#Override
public void mouseMoved(MouseEvent e) {
Graphics g = img.getGraphics();
g.drawRect(1, 1, width - 2, height - 2);
g.dispose();
repaint();
}
#Override
public void paintComponent(Graphics g) {
g.drawImage(img, 0, 0, null);
}
}
See comments in the code.
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
public class Base extends JPanel implements MouseMotionListener {
public static void main(String[] args) {
new Base();
}
final static int width = 400;
final static int height = 300;
BufferedImage img;
Base() {
img = new BufferedImage(width, height,
BufferedImage.TYPE_INT_ARGB_PRE);
// do in preference to setting the frame size..
setPreferredSize(new Dimension(width, height));
JFrame frame = new JFrame();
this.addMouseMotionListener(this); // INSTEAD OF THE FRAME
frame.add(this);
//frame.setSize(width, height); DO INSTEAD...
frame.pack();
//frame.setEnabled(true); REDUNDANT
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // good call!
}
#Override
public void mouseDragged(MouseEvent e) {
}
#Override
public void mouseMoved(MouseEvent e) {
Graphics g = img.getGraphics();
g.setColor(Color.RED); // SET A COLOR
g.drawRect(1, 1, width - 2, height - 2);
// DO SOMETHING UGLY
g.setColor(Color.blue);
Point p = e.getPoint();
g.fillOval(p.x,p.y,5,5);
g.dispose();
repaint();
}
#Override
public void paintComponent(Graphics g) {
g.drawImage(img, 0, 0, null);
}
}
Use a local BufferedImage on which you want to draw. Add a MouseMotionListener and implement the mouseDragged(MouseMotionEvent evt) method. In that method draw onto the BufferedImage by doing something like this:
// Assume img is your BufferedImage
Graphics g = img.getGraphics();
g.drawRect(evt.getX()-1, evt.getY()-1, 3, 3);
g.dispose();
// repaint your swing component
repaint();
And in your overrided paintComponent(Graphics g) method, draw like this:
g.drawImage(img, 0, 0, null);
Initialize your BufferedImage like this:
img = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_ARGB_PRE);
// Assuming `this` is your swing component