Scaling Graphics2D object shifts it - java

I have a JScrollPane that contains a custom JLabel with an ImageIcon. I want the user to be able to zoom in and out on the image. I'm trying to use the scale() method in the Graphics2D class to do it, but whenever I zoom, the image is being shifted down/up and right/left depending on whether I'm zooming in or out. I don't know why this is happening or how to tell how much I need to translate() the Graphics2D object to counteract this. I really appreciate any help you guys can give me. Here's my code:
class ImageViewer extends JFrame implements ActionListener{
private int WIDTH = 800;
private int HEIGHT = 600;
private JScrollPane scrollPane;
JMenuItem zoomIn, zoomOut;
JPanel panel;
MyLabel label;
private String imageUrl = "picture.jpg";
double scale = 1.0;
public static void main(String[] args) {
ImageViewer viewer = new ImageViewer();
viewer.setVisible(true);
}
private ImageViewer() {
this.setTitle("Image Viewer");
this.setSize(WIDTH, HEIGHT);
this.setBackground(Color.gray);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel = new JPanel();
panel.setLayout(new BorderLayout());
this.getContentPane().add(panel);
JMenuBar menubar = new JMenuBar();
JMenu zoom = new JMenu("Zoom");
zoomIn = new JMenuItem("Zoom In");
zoom.add(zoomIn);
zoomIn.addActionListener(this);
zoomOut = new JMenuItem("Zoom Out");
zoom.add(zoomOut);
zoomOut.addActionListener(this);
menubar.add(zoom);
this.add(menubar, BorderLayout.NORTH);
Icon image = new ImageIcon(imageUrl);
label = new MyLabel(image);
scrollPane = new JScrollPane();
scrollPane.getViewport().add(label);
panel.add(scrollPane, BorderLayout.CENTER);
}
public void actionPerformed(ActionEvent e) {
Object ob = e.getSource();
if (ob == zoomIn) {
scale += .1;
label.revalidate();
label.repaint();
}
if (ob == zoomOut) {
scale -= .1;
label.revalidate();
label.repaint();
}
}
class MyLabel extends JLabel{
public MyLabel(Icon i){
super(i);
}
protected void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g;
AffineTransform at = g2.getTransform();
g2.scale(scale, scale);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
super.paintComponent(g2);
g2.setTransform(at);
}
public Dimension getPreferredSize(){
int w = (int)(scale * getIcon().getIconWidth()),
h = (int)(scale * getIcon().getIconHeight());
return new Dimension(w, h);
}
}
}

Before g2.scale(scale, scale); add g2.translate(desiredX, desiredY);

Related

Placing JButtons at exact locations and creating grid of JLabels?

public class SudukoGUI extends JPanel{
private static int WIDTH;
private static int HEIGHT;
private static int PANEL_HEIGHT;
public SudukoGUI(int width, int height, String title) {
this.WIDTH = width;
this.HEIGHT = height;
this.PANEL_HEIGHT = height - width;
SwingUtilities.invokeLater(() -> initGUI(WIDTH, HEIGHT, title));
}
public int getWidth() {
return WIDTH;
}
public int getHeight() {
return HEIGHT;
}
public void initGUI(int width, int height, String title) {
JFrame frame = new JFrame(title);
JButton b1 = new JButton("Solve");
JButton b2 = new JButton("Clear");
// JPanel bp = new JPanel();
// bp.setLayout(new BorderLayout());
// bp.setPreferredSize(new Dimension(WIDTH, PANEL_HEIGHT));
// frame.add(bp);
this.setPreferredSize(new Dimension(width, height));
this.setLayout(new BorderLayout());
this.add(b1, BorderLayout.SOUTH);
this.add(b2, BorderLayout.SOUTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setSize(width + 16, height);
frame.setVisible(true);
frame.add(this);
//frame.pack();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(new Color(1.0f, 1.0f, 1.0f));
int cellWidth = WIDTH / Suduko.getNumRows();
int cellHeight = (HEIGHT - PANEL_HEIGHT) / Suduko.getNumCols();
int gridHeight = HEIGHT - PANEL_HEIGHT;
int regionWidth = 3, regionHeight = 3;
g2d.fillRect(0, 0, WIDTH, HEIGHT);
g2d.setColor(new Color(0,0,0));
for(int i = 0; i <= WIDTH; i += cellWidth) {
if((i / cellWidth) % regionWidth == 0) {
g2d.setStroke(new BasicStroke(2));
g2d.drawLine(i, 0, i, gridHeight);
}else {
g2d.setStroke(new BasicStroke(1));
g2d.drawLine(i, 0, i, gridHeight);
}
}
for(int i = 0; i <= gridHeight; i += cellHeight) {
if((i / cellHeight) % regionHeight == 0) {
g2d.setStroke(new BasicStroke(2));
g2d.drawLine(0, i, WIDTH, i);
}else {
g2d.setStroke(new BasicStroke(1));
g2d.drawLine(0, i, WIDTH, i);
}
}
}
}
As of now both buttons are at BorderLayout.SOUTH. I don't know how to place them at the bottom of this window and in smaller sizes. I tried creating a separate JPanel with setPreferredSize(new Dimension(WIDTH, PANEL_HEIGHT)); So it would be the blank area at the bottom, but that doesn't work of course, when changing the border layout the buttons take up the entire height. I want to place the buttons at the bottom in the blank area.
I also want to create JLabel components for the grid so I can enter numbers with KeyListener (and having MouseListener for selecting which cell). I'm not sure here how to proceed how to create labels in a grid format and how to set exact sizes to them etc. Any tips?
You can use a 3x3 grid of 3x3 grid layouts to create the effect of a Soduko board. The 'outer' grid layout can be assigned more space between each of the panels (each with its own grid layout with less space) for the effect.
Here is how that screenshot was created, adjust numbers (colors etc) to suit the exact requirement.
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.awt.image.BufferedImage;
public class SudokuBoard {
public SudokuBoard() {
initComponents();
}
public final void initComponents() {
JPanel panel = new JPanel(new GridLayout(3,3,6,6));
panel.setBackground(Color.ORANGE);
panel.setBorder(new EmptyBorder(3,3,3,3));
int s = 20;
BufferedImage image = new BufferedImage(s,s,BufferedImage.TYPE_INT_RGB);
ImageIcon icon = new ImageIcon(image);
for (int ii=0; ii<9; ii++) {
JPanel p = new JPanel(new GridLayout(3,3,2,2));
p.setBackground(Color.CYAN);
for (int jj=0; jj<9; jj++) {
JLabel l = new JLabel(icon);
p.add(l);
}
panel.add(p);
}
JFrame f = new JFrame("Sudoku");
f.add(panel);
f.setResizable(false);
f.pack();
f.setLocationByPlatform(true);
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setVisible(true);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
new SudokuBoard();
}
};
SwingUtilities.invokeLater(r);
}
}

the location(x,y) of java swing 'JLabel' and Graphics elements are not same

I have to draw a JLabel with an onclick event on an drawn circle. The newly created JLabel should be placed placed very closed a line which is already created. I was trying to draw this JLabel at the middle position of the line. But problem is, even after setting fixed calculated coordinates(x,y), the JLabel is not drawn in the given location.(unlike g.drawLine() or g.drawOval()). My code is given below: need help to fix it.
public class ButtonExample extends JFrame{
JFrame frame;
JLabel label1, label2, label3;
private Shape myShape;
private int arrowAdded = 0;
public ButtonExample() {
super("Location test of JLabel and Graphics objects");
label1 = new JLabel("0,0");
//label2 = new JLabel("40,40");
label1.setBounds(0, 0, 50, 50);
label1.setBorder(BorderFactory.createLineBorder(Color.black));
//label2.setBounds(100, 100, 50, 50);
//label2.setBorder(BorderFactory.createLineBorder(Color.black));
add(label1);
//add(label2);
repaint();
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent me) {
super.mouseClicked(me);
if (myShape.contains(me.getPoint())) {
arrowAdded = 1;
repaint();
}
}
});
setLayout(null);
setSize(1000,600);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(1000, 600);
}
public void paint(Graphics g){
super.paint(g);
int startX = 100;
int startY = 100;
int endX = 180;
int endY = 120;
g.setColor(new Color(0, 255, 0));
myShape = new Ellipse2D.Double(startX, startY, 30, 30);
Graphics2D g2d = (Graphics2D) g;
g2d.draw(myShape);
g.drawLine(startX, startY, endX, endY);
int lX = (int)Math.abs(endX-startX)/2;
int lY = (int)Math.abs(endY-startY)/2;
if(endX>startX) {
lX = lX+startX;
}else {
lX = lX+endX;
}
if(endY>startY) {
lY = lY+startY;
}else {
lY = lY+endY;
}
if(arrowAdded == 1) {
label3 = new JLabel();
label3.setBounds(lX, lY, 20, 15);
label3.setBorder(BorderFactory.createLineBorder(Color.black));
add(label3);
g.drawRect(lX, lY, 20, 15);
}enter code here
}
public static void main(String[] args) {
new ButtonExample();
}
}
Don't override paint() on a JFrame!
The frame includes the title bar and borders, so you can't just paint at (0, 0). You would need your painting to be offset by the frame decorations.
Instead, custom painting should be done by overriding the paintComponent(...) method of a JPanel and then you add the panel to the frame. Now the offsets will be relative to the panel, so you can use (0, 0). Of course you would also add the label to the panel at your desired location.
Read the section from the Swing tutorial on Custom Painting for more information and working examples.
Change your code
int lX = (int)Math.abs(endX-startX) / 2;
int lY = (int)Math.abs(endY-startY) / 2;
to
int lX = (endX-startX) / 2;
int lY = (endY-startY) / 2;
and your rectangle (left upper corner) will be placed in the middle position of the line (you can still set an offset if it´s too close :-) )

How can I force an ImageIcon to be a certain size?

Currently, I use this code:
public class Test extends JFrame {
static Test t = null;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
t = new Test();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public static void addComponentsToPane(Container pane) {
JButton button;
pane.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
ImageIcon icon;
icon = new ImageIcon(System.getProperty("user.dir") + "/res/background.png");
Image img = icon.getImage() ;
Image newimg = getScaledImage(img, frame.getWidth(), frame.getHeight()) ;
icon = new ImageIcon( newimg );
JLabel background=new JLabel(icon);
//frame.getContentPane().add(background);
background.setLayout(new FlowLayout());
c.fill = GridBagConstraints.HORIZONTAL;
c.ipady = frame.getHeight() * 10;
c.weightx = 0.0;
c.gridwidth = 3;
c.gridx = 0;
c.gridy = 1;
pane.add(background, c);
}
private static Image getScaledImage(Image srcImg, int w, int h){
BufferedImage resizedImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = resizedImg.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.drawImage(srcImg, 0, 0, w, h, null);
g2.dispose();
return resizedImg;
}
private void createAndShowGUI() {
frame = new JFrame("GridBagLayoutDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
static JFrame frame = null;
public Test() {
createAndShowGUI();
addComponentsToPane(frame.getContentPane());
frame.pack();
frame.setVisible(true);
frame.setExtendedState(frame.getExtendedState() | JFrame.MAXIMIZED_BOTH);
}
}
This works fine for buttons, but for some reason is not working for a JLabel with an image.
How can I make this JLabel/Image fit to the size I would like?
The image is being really tiny at the moment,
When really, the area that the same code with a JButton takes up is more like this:
Also, it is possible for me to get the ImageIcon to be bigger than button 4 as well, which I also would like to avoid.
So how can I stretch/contract the image to fit the area I would like it to have?
you can use the code snippet from http://docs.oracle.com/javase/tutorial/displayCode.html?code=http://docs.oracle.com/javase/tutorial/uiswing/examples/components/IconDemoProject/src/components/IconDemoApp.java to create a scaled image...
(from http://docs.oracle.com/javase/tutorial/uiswing/components/icon.html)
just measure you button and adjust the image size
/**
* Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved.
* Resizes an image using a Graphics2D object backed by a BufferedImage.
* #param srcImg - source image to scale
* #param w - desired width
* #param h - desired height
* #return - the new resized image
*/
private Image getScaledImage(Image srcImg, int w, int h){
BufferedImage resizedImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = resizedImg.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.drawImage(srcImg, 0, 0, w, h, null);
g2.dispose();
return resizedImg;
}
i want to pick up a point from MadProgammer on his hint about using scaledInstance: https://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html
so how to measure a buttons size?
final Button b = new Button(); //create it on your custom way...
b.addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e){
int w = b.getWidth();
int h = b.getHeight();
setIconSize(w,h); //TODO by yourself (sorry, if more help required please say so)
}
});
So how can I stretch/contract the image to fit the area I would like it to have?
Check out Darryl's Stretch Icon. The Icon is automatically painted at the size of the component.

drawing in swing Java

I'm making the game "Who is millionaire".
This is the help panel, which let user choose one of the options such as: calling friend, asking audience, etc.
But I have a problem, the options are ellipses, which are drawn in class Help_Option extends JComponent. When I test this class Help_Option individually, it works fine. But when I add the Help_Option object into the game panel, actually a sub-panel in the frame, it just displays a line on the panel, it doesn't draw my ellipse.
This is my code:
Note: a is JFrame, I don't copy the whole method initialize(JFrame a) cos it's quite long and I don't think that the error comes from there.
/******Helper panel**********/
JPanel help_area_container = new JPanel();
help_area_container.setBorder(BorderFactory.createLineBorder(Color.BLUE, 3));
help_area_container.setLayout(new GridLayout(4,0));
JPanel voting_container = new JPanel();
JPanel calling_container = new JPanel();
JPanel half_container = new JPanel();
JPanel take_container = new JPanel();
JPanel[] all_help_container = new JPanel[]{voting_container, calling_container, half_container, take_container};
for(int i = 0; i < all_help_container.length; i++){
all_help_container[i].setBorder(BorderFactory.createLineBorder(Color.RED));
all_help_container[i].setPreferredSize(new Dimension(350, help_area_container.getPreferredSize().height/4));
}
for(int j = 0; j < all_help_container.length; j++){
help_area_container.add(all_help_container[j]);
}
Help_Option voting_option = new Help_Option(all_help_container[0].getPreferredSize().width, all_help_container[0].getPreferredSize().height);
voting_option.setPreferredSize(new Dimension(all_help_container[0].getPreferredSize().width, all_help_container[0].getPreferredSize().height));
all_help_container[0].add(voting_option);
a.add(help_area_container, BorderLayout.EAST);
/*****************************/
This is the Help_Option class:
class Help_Option extends JComponent implements MouseMotionListener{
private static int x, y;
private Ellipse2D ellipse;
private Color c = Color.BLACK;
public Help_Option(int x, int y){
Help_Option.x = x;
Help_Option.y = y;
ellipse = new Ellipse2D.Double(0, 0, x, y);
this.addMouseMotionListener(this);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.BLUE);
g2d.draw(ellipse);
g2d.setColor(c);
g2d.fill(ellipse);
g2d.setColor(Color.RED);
g2d.setFont(new Font("TimesRoman", Font.BOLD, 20));
g2d.drawString("Here I am", 250, 100);
}
public void setColor(Color c){
this.c = c;
}
#Override
public void mouseDragged(MouseEvent e) {
}
#Override
public void mouseMoved(MouseEvent e) {
if(ellipse.contains(e.getX(), e.getY())){
setColor(Color.GREEN);
repaint();
}else{
setColor(Color.BLACK);
repaint();
}
}
}
And this is the class that i used to test Help_Option class:
public class Help extends JFrame{
public static void main(String [] agrs){
Help h = new Help();
h.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
h.init();
}
public void init(){
this.setLayout(new FlowLayout());
this.setSize(2000, 1000);
JPanel a = new JPanel();
a.setPreferredSize(new Dimension((int)a.getSize().width/3, (int)a.getSize().height/2));
a.setBorder(BorderFactory.createLineBorder(Color.yellow, 3));
Help_Option k = new Help_Option(a.getPreferredSize().width, a.getPreferredSize().height/2);
k.setPreferredSize(new Dimension(a.getPreferredSize().width, a.getPreferredSize().height));
a.add(k);
this.add(a);
this.setVisible(true);
}
}
EDIT
This is the link to my classes, please take a look at them. The error is described above.
It is that you haven't set values for your first couple of JPanels and they are returning preferred sizes of 0. Dimensions of 0,0
So you should add values to the JPanels there.
public static void main(String[] args) {
JPanel help_area_container = new JPanel();
help_area_container.setBorder(BorderFactory.createLineBorder(
Color.BLUE, 3));
help_area_container.setLayout(new GridLayout(4, 0));
//Should have set sizes below
JPanel voting_container = new JPanel();
//voting_container.setSize(50,50);
JPanel calling_container = new JPanel();
JPanel half_container = new JPanel();
JPanel take_container = new JPanel();
JPanel[] all_help_container = new JPanel[] { voting_container,
calling_container, half_container, take_container };
for (int i = 0; i < all_help_container.length; i++) {
all_help_container[i].setBorder(BorderFactory
.createLineBorder(Color.RED));
all_help_container[i].setPreferredSize(new Dimension(350,
help_area_container.getPreferredSize().height / 4));
}
for (int i = 0; i < all_help_container.length; i++) {
System.out.println(all_help_container[i].getSize());
}
}
// where you can change the size
all_help_container[0].setSize(50, 50);
System.out.println("----");
for (int i = 0; i < all_help_container.length; i++) {
System.out.println(all_help_container[i].getSize());
}
}
Hopefully this will help you with the sizing of your GUI.
You will have to adjust the different panels to suit your needs. As I'm guessing that this panels will contain some other stuff in them.
You may want to create custom JPanels for each of these, so that you can easily check if they are the right size.
public class VotingPanel extends JPanel {
public VotingPanel(){
//All your variables such as size and color schemes
}
}

# java how to insert images in a canvas

i am developing a car simulation in java. my coding is as below. I want to put a panel and in it to be the line rotating together with a speedometer image of 180 degrees. My question is how do i do this?
public class House extends JPanel implements ActionListener
{
MyCanvas canvas;
JButton mybutton,mybutton1,mybutton2,mybutton3,mybutton4,mybutton5;
JTextField text1,text2,text3;
JSlider sliderTransX, sliderTransY, sliderRotateTheta, sliderRotateX,
sliderRotateY, sliderScaleX, sliderScaleY, sliderWidth;
`enter code here`int k = 0;
double transX = 0.0;
double transY = 0.0;
double rotateTheta = 0.0;
double rotateX = 345.0;
double rotateY = 250.0;
double scaleX = 1.0;
double scaleY = 1.0;
float width = 1.0f;
Cargame game = new Cargame();
public House()
{
super(new BorderLayout());
JPanel controlPanel = new JPanel(new GridLayout(3, 3));
add(controlPanel, BorderLayout.NORTH);
sliderRotateTheta = setSlider(controlPanel, JSlider.HORIZONTAL, 0, 180,
0, 90, 45);
mybutton = new JButton("Accelerate");
mybutton.addActionListener(this);
mybutton1 = new JButton("ShiftUp");
mybutton1.addActionListener(this);
mybutton2 = new JButton("ShiftDown");
mybutton2.addActionListener(this);
mybutton3 = new JButton("Deaccelerate");
mybutton3.addActionListener(this);
mybutton4 = new JButton("Start Engine");
mybutton4.addActionListener(this);
mybutton5 = new JButton("Stop Engine");
mybutton5.addActionListener(this);
text1=new JTextField(5);
text2=new JTextField(5);
text3=new JTextField(5);
sliderWidth = new JSlider(JSlider.HORIZONTAL, 0, 20, 1);
sliderWidth.addChangeListener(new ChangeListener()
{
public void stateChanged(ChangeEvent e) {
width = sliderWidth.getValue();
canvas.repaint();
}
});
JPanel widthPanel = new JPanel();
widthPanel.setLayout(new GridLayout(3, 3));
widthPanel.add(mybutton);
widthPanel.add(mybutton1);
widthPanel.add(mybutton2);
widthPanel.add(mybutton3);
widthPanel.add(mybutton4);
widthPanel.add(mybutton5);
widthPanel.add(text1);
widthPanel.add(text2);
widthPanel.add(text3);
add(widthPanel, BorderLayout.SOUTH);
canvas = new MyCanvas();
add(canvas, "Center");
}
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == mybutton)
{
game.Accelerate();
}
if (e.getSource() == mybutton1)
{
game.ShiftUp();
}
if (e.getSource() == mybutton2)
{
game.ShiftDown();
}
if (e.getSource() == mybutton3)
{
game.Deaccelerate();
}
JSlider tempSlider=new JSlider();
rotateTheta= ((double)game.getLevel()) * Math.PI / 180;
text1.setText(String.valueOf(game.getLevel()));
text2.setText(String.valueOf(game.getGear()));
text3.setText(String.valueOf(game.Speedometer()));
canvas.repaint();
}
public JSlider setSlider(JPanel panel, int orientation, int minimumValue,
int maximumValue, int initValue, int majorTickSpacing,
int minorTickSpacing) {
JSlider slider = new JSlider(orientation, minimumValue, maximumValue,
initValue);
slider.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
JSlider tempSlider = (JSlider) e.getSource();
sliderRotateTheta.setValue(k);
if(tempSlider.equals(sliderRotateTheta))
{
rotateTheta = sliderRotateTheta.getValue() * Math.PI / 180;
canvas.repaint();
}}
});
slider.setVisible(false);
panel.add(slider);
return slider;
}
class MyCanvas extends Canvas {
public void paint(Graphics g) {
Graphics2D g2D = (Graphics2D) g;
g2D.rotate(rotateTheta, rotateX, rotateY);
BasicStroke stroke = new BasicStroke(width);
g2D.setStroke(stroke);
drawHome(g2D);
}
public void drawHome(Graphics2D g2D) {
Line2D line7 = new Line2D.Float(205f, 250f, 345f, 250f);
g2D.draw(line7);
}
}
public static void main(String[] a) {
JFrame f = new JFrame();
f.getContentPane().add(new House());
f.setDefaultCloseOperation(1);
f.setSize(700, 550);
f.setVisible(true);
}
}
Using JFreeChart, you can control a DialPlot using a JSlider or JSpinner. This related example uses a JSlider to control a line chart, and more examples may be found in the JWS demo.
Addendum: As shown here, you can use the parametric form of the equation of a circle to calculate the free endpoint of your indicator line.

Categories