LWJGL swing error - cant find the solution - java

i try to add to my LWJGL canvas some buttons and one image panel
but it wont work.
this is how it look like at the moment example image
i like to render in the grey square right next to the test image.
if i compile my code i get the following error:
Exception in thread "Thread-0" java.lang.RuntimeException: No OpenGL context found in the current thread.
at org.lwjgl.opengl.GLContext.getCapabilities(GLContext.java:124)
at org.lwjgl.opengl.GL20.glCreateProgram(GL20.java:253)
at util.ShaderProgram.createProgram(ShaderProgram.java:53)
at util.ShaderProgram.<init>(ShaderProgram.java:47)
at ExampleApplet$5.run(ExampleApplet.java:163)
what do i have do change?
thanks a lot.
adding swing to lwjgl is driving my crazy
here is my sourcecode:
import math.Mat4;
import math.Vec3;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;
import util.Mesh;
import util.OBJContainer;
import util.OBJGroup;
import util.ShaderProgram;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.glViewport;
import static org.lwjgl.opengl.GL15.GL_STATIC_DRAW;
public class ExampleApplet {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
ExampleApplet window = new ExampleApplet();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
private ShaderProgram shaderProgram;
private ArrayList<Mesh> meshes;
private Mat4 modelMatrix;
private Mat4 viewMatrix;
private volatile float[] vertices;
private JFrame frame;
private Canvas canvas;
private JPanel controlPanel;
private JPanel canvasPanel;
private JPanel imagePanel;
private Thread gameThread;
private boolean running;
private int windowWidth;
private int windowHeight;
private volatile boolean needValidation;
private volatile boolean needUpdateViewport;
public ExampleApplet() {
frame = new JFrame();
frame.addWindowListener(new WindowListener() {
public void windowOpened(WindowEvent arg0) {
}
public void windowIconified(WindowEvent arg0) {
}
public void windowDeiconified(WindowEvent arg0) {
}
public void windowDeactivated(WindowEvent arg0) {
}
public void windowClosing(WindowEvent arg0) {
}
public void windowClosed(WindowEvent arg0) {
terminate();
}
public void windowActivated(WindowEvent arg0) {
}
});
frame.setTitle("Swing + LWJGL");
frame.setSize(1500, 700);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new BorderLayout(0, 0));
canvasPanel = new JPanel(new BorderLayout(0, 0));
imagePanel = new JPanel(new BorderLayout(0, 0));
canvas = new Canvas() {
private static final long serialVersionUID = -1069002023468669595L;
public void removeNotify() {
stopOpenGL();
}
};
canvas.addComponentListener(new ComponentListener() {
public void componentShown(ComponentEvent e) {
setNeedValidation();
}
public void componentResized(ComponentEvent e) {
setNeedValidation();
}
public void componentMoved(ComponentEvent e) {
setNeedValidation();
}
public void componentHidden(ComponentEvent e) {
setNeedValidation();
}
});
canvas.setIgnoreRepaint(true);
canvas.setSize(500,500);
canvasPanel.setSize(500,500);
//canvas.setPreferredSize(new Dimension(500, 500));
// canvas.setMinimumSize(new Dimension(320, 240));
canvas.setVisible(true);
canvasPanel.add(canvas, BorderLayout.CENTER);
try {
BufferedImage myImg = ImageIO.read((new File("/home/manu/workspaces/LWJGL_swing/resources/itworks-HDTV_720P.png")));
JLabel picLabel = new JLabel(new ImageIcon(resize(myImg, 1000, 500)));
imagePanel.add(picLabel);
} catch (Exception e) {
}
controlPanel = new JPanel(new BorderLayout(0, 0));
frame.add(canvasPanel, BorderLayout.LINE_END);
frame.add(imagePanel, BorderLayout.LINE_START);
frame.pack();
JPanel controls = new JPanel(new GridLayout(2,3));
controlPanel.add(controls,BorderLayout.EAST);
JButton openImage = new JButton("Bild öffnen");
JSlider brigthness = new JSlider(0,100,0);
controls.add(openImage);
controls.add(brigthness);
frame.add(controlPanel, BorderLayout.PAGE_END);
startOpenGL();
}
private void setNeedValidation() {
needValidation = true;
needUpdateViewport = true;
}
private void startOpenGL() {
System.out.println("StartOpenGL");
gameThread = new Thread() {
public void run() {
try {
shaderProgram = new ShaderProgram( "/home/manu/workspaces/LWJGL_swing/src/GUISample/Color_vs.glsl", "/home/manu/workspaces/LWJGL_swing/src/GUISample/Color_fs.glsl" );
modelMatrix = new Mat4();
viewMatrix = Mat4.translation( 0.0f, 0.0f, -3.0f );
meshes = new ArrayList<Mesh>();
windowWidth = 500;
windowHeight = 500;
loadObj("monkey.obj");
glEnable( GL_DEPTH_TEST );
Display.create();
Display.setParent(canvas);
running = true;
} catch (LWJGLException e) {
e.printStackTrace();
}
int i=0;
while (running) {
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
float fov = 60;
float near = 0.01f;
float far = 500.0f;
Mat4 projectionMatrix = Mat4.perspective( fov, windowWidth, windowHeight, near, far );
glViewport( 0, 0, windowWidth, windowHeight );
drawMeshes(viewMatrix, projectionMatrix);
GL11.glViewport(0, 0, 500,500);
if (i % 2 == 0) {
GL11.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
} else {
GL11.glClearColor(1.0f, 1.0f, 0.0f, 1.0f);
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
}
Display.update();
try{
Thread.sleep(1000);
}catch (Exception e){
}
i++;
updateGL();
}
if (Display.isCreated()) {
Display.destroy();
}
}
};
gameThread.start();
}
public void drawMeshes( Mat4 viewMatrix, Mat4 projMatrix ) {
shaderProgram.useProgram();
shaderProgram.setUniform("uModel", modelMatrix);
shaderProgram.setUniform("uView", viewMatrix);
shaderProgram.setUniform("uProjection", projMatrix);
shaderProgram.setUniform("uColor",new Vec3(1.0f,0.0f,0.0f) );
shaderProgram.setUniform("uEnableShading", 0);
meshes.get(0).draw();
}
private void setupVertices() {
vertices = new float[4 * 2];
vertices[0] = 0.1f;
vertices[1] = 0.3f;
vertices[2] = 0.2f;
vertices[3] = 0.8f;
vertices[4] = 0.9f;
vertices[5] = 0.6f;
vertices[6] = 0.7f;
vertices[7] = 0.05f;
}
private void updateGL() {
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
render();
Display.update();
Display.sync(60);
if (needUpdateViewport) {
needUpdateViewport = false;
Rectangle rect = canvas.getBounds();
int w = (int) rect.getWidth();
int h = (int) rect.getHeight();
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GL11.glOrtho(0, w, h, 0, -1, 1);
GL11.glViewport(0, 0, w, h);
}
int error = GL11.glGetError();
if (error != GL11.GL_NO_ERROR) {
String msg = "Unknown Error";
switch (error) {
case GL11.GL_INVALID_OPERATION:
msg = "Invalid Operation";
break;
case GL11.GL_INVALID_VALUE:
msg = "Invalid Value";
break;
case GL11.GL_INVALID_ENUM:
msg = "Invalid Enum";
break;
case GL11.GL_STACK_OVERFLOW:
msg = "Stack Overflow";
break;
case GL11.GL_STACK_UNDERFLOW:
msg = "Stack Underflow";
break;
case GL11.GL_OUT_OF_MEMORY:
msg = "Out of memory";
break;
}
throw new RuntimeException(msg);
}
}
private void render() {
float scale = 100;
GL11.glBegin(GL11.GL_QUADS);
GL11.glColor4f(1, 0, 0, 1);
GL11.glVertex3f(vertices[0] * scale, vertices[1] * scale, 0);
GL11.glColor4f(1, 0, 0, 1);
GL11.glVertex3f(vertices[2] * scale, vertices[3] * scale, 0);
GL11.glColor4f(1, 0, 0, 1);
GL11.glVertex3f(vertices[4] * scale, vertices[5] * scale, 0);
GL11.glColor4f(1, 0, 0, 1);
GL11.glVertex3f(vertices[6] * scale, vertices[7] * scale, 0);
GL11.glEnd();
}
private void stopOpenGL() {
System.out.println("StopOpenGL");
running = false;
try {
gameThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void terminate() {
frame.dispose();
System.exit(0);
}
public static BufferedImage resize(BufferedImage img, int newW, int newH) {
Image tmp = img.getScaledInstance(newW, newH, Image.SCALE_SMOOTH);
BufferedImage dimg = new BufferedImage(newW, newH, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = dimg.createGraphics();
g2d.drawImage(tmp, 0, 0, null);
g2d.dispose();
return dimg;
}
public void loadObj( String filename )
{
if( !filename.toLowerCase().endsWith(".obj") )
{
System.err.println( "Error in Sandbox.loadObj(): Invalid file extension, expected \".obj\":\n" + filename );
return;
}
OBJContainer objContainer = OBJContainer.loadFile( "monkey.obj" );
ArrayList<OBJGroup> objGroups = objContainer.getGroups();
for( OBJGroup group : objGroups )
{
float[] positions = group.getPositions();
float[] normals = group.getNormals();
int[] indices = group.getIndices();
Mesh mesh = new Mesh( GL_STATIC_DRAW );
mesh.setAttribute( 0, positions, 3 );
mesh.setAttribute( 1, normals, 3 );
mesh.setIndices( indices );
meshes.add( mesh );
}
}
}

You created the OpenGL context in a different thread. You have to use it in the same thread in which it's created.

Related

Undo Action with ArrayList of BufferedImage

I am programming a sprite editor and I would like to implement an Undo button to my program. In order to achieve that, I have thought to use an ArrayList that store my updated BufferedImage after each action in an ArrayList. Then I will just read the ArrayList and find the right image to draw thanks to an index. However, it seems like it always store the same image. I don't know what I am doing wrong so I am asking for your help and advice.
Here is a test code:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
#SuppressWarnings("serial")
public class FromGraphicsToBufferedImage extends JPanel{
private static final int BI_WIDTH = 600;
private static final int BI_HEIGHT = BI_WIDTH;
private static BufferedImage bImage = new BufferedImage(BI_WIDTH, BI_HEIGHT, BufferedImage.TYPE_INT_ARGB); //Enregistrement de l'image en RGBA
private List<Point> pointList = new ArrayList<Point>();
private JLabel imageLabel;
private boolean isInit = false;
private static ArrayList<BufferedImage> historic = new ArrayList<BufferedImage>();
private int historicIndex = 0;
//Constructeur
public FromGraphicsToBufferedImage() {
imageLabel = new JLabel(new ImageIcon(bImage)) {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
paintInLabel(g);
}
};
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
imageLabel.addMouseListener(myMouseAdapter);
imageLabel.addMouseMotionListener(myMouseAdapter);
imageLabel.setBorder(BorderFactory.createEtchedBorder());
JButton saveImageBtn = new JButton("Save Image");
saveImageBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
saveImageActionPerformed();
}
});
JButton clearImageBtn = new JButton("Clear Image");
clearImageBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Graphics2D g2 = bImage.createGraphics();
g2.setBackground(new Color(255,255,255,0));
g2.clearRect(0, 0, BI_WIDTH, BI_HEIGHT);
g2.dispose();
imageLabel.repaint();
}
});
JButton undo = new JButton("Undo");
undo.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("UNDO");
historicIndex -= 1;
bImage = historic.get(historicIndex);
Graphics2D g2 = bImage.createGraphics();
g2.drawImage(bImage, 0, 0, bImage.getWidth(), bImage.getHeight(), imageLabel);
g2.dispose();
imageLabel.setIcon(new ImageIcon(bImage));
imageLabel.repaint();
}
});
JPanel btnPanel = new JPanel();
btnPanel.add(saveImageBtn);
btnPanel.add(clearImageBtn);
btnPanel.add(undo);
setLayout(new BorderLayout());
add(imageLabel, BorderLayout.CENTER);
add(btnPanel, BorderLayout.SOUTH);
} //Fin du Constructeur
private void saveImageActionPerformed() {
JFileChooser filechooser = new JFileChooser();
FileNameExtensionFilter filter = new FileNameExtensionFilter("PNG Images", "png");
filechooser.setFileFilter(filter);
int result = filechooser.showSaveDialog(this);
if (result == JFileChooser.APPROVE_OPTION) {
File saveFile = filechooser.getSelectedFile();
try {
ImageIO.write(bImage, "png", saveFile);
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void paintInLabel(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d = bImage.createGraphics();
if(isInit == false) {
g2d.setColor(Color.RED);
g2d.fillRect(0, 0, BI_WIDTH, BI_HEIGHT);
g2d.dispose();
isInit = true;
}
else {
g2d.setColor(Color.BLUE);
if(pointList.size() == 0) {
return;
}
//System.out.println(bImage.getRGB(50, 50));
int x1 = pointList.get(0).x;
int y1 = pointList.get(0).y;
int x2 = pointList.get(0).x+32;
int y2 = pointList.get(0).y+32;
g2d.setComposite(AlphaComposite.Src);
g2d.setColor(new Color(255,255,255,0));
g2d.fillRect(x1,y1,32,32);
g2d.dispose();
pointList.clear();
}
historic.add(deepCopy(bImage));
imageLabel.repaint();
}
private class MyMouseAdapter extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
pointList.add(e.getPoint());
imageLabel.repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
//historic.add(deepCopy(bImage));
System.out.println("Historic Size: " + historic.size());
int tailleHistorique = historic.size();
historicIndex = tailleHistorique-1;
if(historic.size() >= 2) {
System.out.println("The 2 images are the same: " + compareImages(historic.get(historicIndex-1), historic.get(historicIndex)));
}
}
}
private static void createAndShowUI() {
JFrame frame = new JFrame("DrawAndSaveImage");
frame.getContentPane().add(new FromGraphicsToBufferedImage());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static BufferedImage getBufferedImage() {
return bImage;
}
public static boolean compareImages(BufferedImage imgA, BufferedImage imgB) {
if (imgA.getWidth() == imgB.getWidth() && imgA.getHeight() == imgB.getHeight()) {
int largeurImage = imgA.getWidth();
int hauteurImage = imgA.getHeight();
for (int y = 0; y < hauteurImage; y++) {
for (int x = 0; x < largeurImage; x++) {
if (imgA.getRGB(x, y) != imgB.getRGB(x, y)){
return false;
}
}
}
}
else {
return false;
}
return true;
}
static BufferedImage deepCopy(BufferedImage bi) {
ColorModel cm = bi.getColorModel();
boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
WritableRaster raster = bi.copyData(null);
return new BufferedImage(cm, raster, isAlphaPremultiplied, null);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
DeepCopy Code Snippet
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
#SuppressWarnings("serial")
public class FromGraphicsToBufferedImage extends JPanel{
private static final int BI_WIDTH = 600;
private static final int BI_HEIGHT = BI_WIDTH;
private static BufferedImage bImage = new BufferedImage(BI_WIDTH, BI_HEIGHT, BufferedImage.TYPE_INT_ARGB); //Enregistrement de l'image en RGBA
private List<Point> pointList = new ArrayList<Point>();
private JLabel imageLabel;
private boolean isInit = false;
private static ArrayList<BufferedImage> historic = new ArrayList<BufferedImage>();
private int historicIndex = 0;
//Constructeur
public FromGraphicsToBufferedImage() {
imageLabel = new JLabel(new ImageIcon(bImage)) {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
paintInLabel(g);
}
};
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
imageLabel.addMouseListener(myMouseAdapter);
imageLabel.addMouseMotionListener(myMouseAdapter);
imageLabel.setBorder(BorderFactory.createEtchedBorder());
JButton saveImageBtn = new JButton("Save Image");
saveImageBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
saveImageActionPerformed();
}
});
JButton clearImageBtn = new JButton("Clear Image");
clearImageBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Graphics2D g2 = bImage.createGraphics();
g2.setBackground(new Color(255,255,255,0));
g2.clearRect(0, 0, BI_WIDTH, BI_HEIGHT);
g2.dispose();
imageLabel.repaint();
}
});
JButton undo = new JButton("Undo");
undo.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("UNDO");
historicIndex -= 1;
bImage = historic.get(historicIndex);
Graphics2D g2 = bImage.createGraphics();
g2.drawImage(bImage, 0, 0, bImage.getWidth(), bImage.getHeight(), imageLabel);
g2.dispose();
imageLabel.setIcon(new ImageIcon(bImage));
imageLabel.repaint();
}
});
JPanel btnPanel = new JPanel();
btnPanel.add(saveImageBtn);
btnPanel.add(clearImageBtn);
btnPanel.add(undo);
setLayout(new BorderLayout());
add(imageLabel, BorderLayout.CENTER);
add(btnPanel, BorderLayout.SOUTH);
} //Fin du Constructeur
private void saveImageActionPerformed() {
JFileChooser filechooser = new JFileChooser();
FileNameExtensionFilter filter = new FileNameExtensionFilter("PNG Images", "png");
filechooser.setFileFilter(filter);
int result = filechooser.showSaveDialog(this);
if (result == JFileChooser.APPROVE_OPTION) {
File saveFile = filechooser.getSelectedFile();
try {
ImageIO.write(bImage, "png", saveFile);
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void paintInLabel(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d = bImage.createGraphics();
if(isInit == false) {
g2d.setColor(Color.RED);
g2d.fillRect(0, 0, BI_WIDTH, BI_HEIGHT);
g2d.dispose();
isInit = true;
}
else {
g2d.setColor(Color.BLUE);
if(pointList.size() == 0) {
return;
}
//System.out.println(bImage.getRGB(50, 50));
int x1 = pointList.get(0).x;
int y1 = pointList.get(0).y;
int x2 = pointList.get(0).x+32;
int y2 = pointList.get(0).y+32;
g2d.setComposite(AlphaComposite.Src);
g2d.setColor(new Color(255,255,255,0));
g2d.fillRect(x1,y1,32,32);
g2d.dispose();
pointList.clear();
}
historic.add(deepCopy(bImage));
imageLabel.repaint();
}
private class MyMouseAdapter extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
pointList.add(e.getPoint());
imageLabel.repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
//historic.add(deepCopy(bImage));
System.out.println("Historic Size: " + historic.size());
int tailleHistorique = historic.size();
historicIndex = tailleHistorique-1;
if(historic.size() >= 2) {
System.out.println("The 2 images are the same: " + compareImages(historic.get(historicIndex-1), historic.get(historicIndex)));
}
}
}
private static void createAndShowUI() {
JFrame frame = new JFrame("DrawAndSaveImage");
frame.getContentPane().add(new FromGraphicsToBufferedImage());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static BufferedImage getBufferedImage() {
return bImage;
}
public static boolean compareImages(BufferedImage imgA, BufferedImage imgB) {
if (imgA.getWidth() == imgB.getWidth() && imgA.getHeight() == imgB.getHeight()) {
int largeurImage = imgA.getWidth();
int hauteurImage = imgA.getHeight();
for (int y = 0; y < hauteurImage; y++) {
for (int x = 0; x < largeurImage; x++) {
if (imgA.getRGB(x, y) != imgB.getRGB(x, y)){
return false;
}
}
}
}
else {
return false;
}
return true;
}
static BufferedImage deepCopy(BufferedImage bi) {
ColorModel cm = bi.getColorModel();
boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
WritableRaster raster = bi.copyData(null);
return new BufferedImage(cm, raster, isAlphaPremultiplied, null);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}

JButton hover effect animation. Changing opacity by Mouse Listener

I'd like to do JButton with nice transition effect. I write a class which extend by JButton and add to it custom MouseAdapter. It almost works, but if opacity should have 0 my one BufferedImage don't vanish.
Here my all source code:
public class ImageHoverButton extends JButton {
public class MouseListener extends MouseAdapter
{
public void mouseExited(MouseEvent me)
{
new Thread(new Runnable()
{
public void run()
{
for (float i = 1f; i >= 0f; i -= .03f)
{
setOpacity(i);
try
{
Thread.sleep(10);
}
catch (Exception e)
{
}
}
}
}).start();
}
public void mouseEntered(MouseEvent me)
{
new Thread(new Runnable()
{
public void run()
{
for (float i = 0f; i <= 1f; i += .03f)
{
setOpacity(i);
try
{
Thread.sleep(10);
}
catch (Exception e)
{
}
}
}
}).start();
}
public void mousePressed(MouseEvent me)
{
new Thread(new Runnable()
{
public void run()
{
for (float i = 1f; i >= 0.6f; i -= .1f)
{
setOpacity(i);
try
{
Thread.sleep(1);
}
catch (Exception e)
{
}
}
}
}).start();
}
}
private static final long serialVersionUID = 1L;
private BufferedImage imgBottom;
private BufferedImage imgHover;
private BufferedImage imgHoverRGB;
// filter to imgInActive
float[] scales = { 1f, 1f, 1f, 0f};
float[] offsets = new float[4];
RescaleOp rop = new RescaleOp(scales, offsets, null);
/**
* Constructor for image path
* #param img
* #param x
* #param y
*/
public ImageHoverButton(String imgBottomPath, String imgHoverPath, int x, int y) {
try {
this.imgBottom = ImageIO.read(new File(imgBottomPath));
this.imgHover = ImageIO.read(new File(imgHoverPath));
imgHoverRGB = new BufferedImage(imgHover.getWidth(null),
imgHover.getHeight(null),
BufferedImage.TYPE_INT_ARGB);
Graphics g = imgHoverRGB.getGraphics();
g.drawImage(imgHover, 0, 0, null);
} catch (IOException e) {
}
this.setBounds(x, y, imgBottom.getWidth() + 40 , imgBottom.getHeight() + 50);
addMouseListener(new MouseListener());
setOpacity(0f);
setOpaque(false);
setBorderPainted(false);
setRolloverEnabled(false);
setCursor(new Cursor(Cursor.HAND_CURSOR));
setLayout(null);
}
public void setOpacity(float opacity) {
scales[3] = opacity;
rop = new RescaleOp(scales, offsets, null);
repaint();
}
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D)g;
g2d.drawImage(imgBottom, 50, 50, null);
g2d.drawImage(imgHoverRGB, rop, 0, 0);
}
}
Have any idea how to improve this?
I'm not so familiar with RescaleOp, and can't remember having used this before. But it seems like the results of applying it in this case are somewhat unexpected.
As an alternative, you might consider an AlphaComposite. The minimum modification that is necessary to achieve the desired effect would then be to change the line
g2d.drawImage(imgHoverRGB, rop, 0, 0);
to
g2d.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, scales[3]));
g2d.drawImage(imgHoverRGB, 0, 0, null);
However, there are several other issues with the code:
don't override paint. Instead, override paintComponent
don't call setBounds on a component (particlularly not in a constructor). The placement should be done by a layout manager
don't swallow Exceptions silently
don't load the images in the constructor of the button
implement getPreferredSize properly
don't spawn hundreds of threads due to mouse movement. (When you quickly move the mouse in and out, you'll have several threads running - some of them increasing the opacity, and some of them decreasing the opacity)
I created an example showing one possible approach: It contains an OpacityAnimator that allows a transition between two opacities, with a predefined delay in milliseconds. This animator is used to increase the opacity of the foreground image when the button is hovered with the mouse, and to decrease it when the mouse leaves the button.
(Note that this could be generalized further, and there are many possible "configuration settings" (like the transition delay) that could be exposed, but this is just intended as an example)
import java.awt.AlphaComposite;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class HoverButtonTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
try
{
createAndShowGUI();
}
catch (IOException e)
{
e.printStackTrace();
}
}
});
}
private static void createAndShowGUI() throws IOException
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
BufferedImage backgroundImage = loadImage("background.png");
BufferedImage foregroundImage = loadImage("foreground.png");
f.getContentPane().setLayout(new FlowLayout());
f.getContentPane().add(
new ImageHoverButton(backgroundImage, foregroundImage));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static BufferedImage loadImage(String path) throws IOException
{
return convertToARGB(ImageIO.read(new File(path)));
}
public static BufferedImage convertToARGB(BufferedImage image)
{
BufferedImage newImage = new BufferedImage(image.getWidth(),
image.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g = newImage.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
return newImage;
}
}
class ImageHoverButton extends JButton
{
private class MouseHoverListener extends MouseAdapter
{
#Override
public void mouseExited(MouseEvent me)
{
opacityAnimator.changeOpacity(0.0f, 250);
}
#Override
public void mouseEntered(MouseEvent me)
{
opacityAnimator.changeOpacity(1.0f, 1000);
}
#Override
public void mousePressed(MouseEvent me)
{
opacityAnimator.changeOpacity(0.5f, 50);
}
}
private class OpacityAnimator
{
private final int DELAY_MS = 10;
private final Timer timer;
private float targetOpacity;
private float currentOpacity;
private float opacityStep;
OpacityAnimator()
{
timer = new Timer(DELAY_MS, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
if (currentOpacity > targetOpacity)
{
currentOpacity += opacityStep;
currentOpacity = Math.max(
currentOpacity, targetOpacity);
}
else if (currentOpacity < targetOpacity)
{
currentOpacity += opacityStep;
currentOpacity = Math.min(
currentOpacity, targetOpacity);
}
if (currentOpacity == targetOpacity)
{
timer.stop();
}
setOpacity(currentOpacity);
}
});
}
void changeOpacity(float targetOpacity, int durationMs)
{
timer.stop();
this.targetOpacity = targetOpacity;
float delta = targetOpacity - currentOpacity;
if (durationMs > 0)
{
opacityStep = (delta / durationMs) * DELAY_MS;
}
else
{
opacityStep = delta;
}
timer.start();
}
}
private final OpacityAnimator opacityAnimator;
private final BufferedImage backgroundImage;
private final BufferedImage foregroundImage;
private float opacity = 0.0f;
public ImageHoverButton(BufferedImage backgroundImage,
BufferedImage foregroundImage)
{
this.backgroundImage = backgroundImage;
this.foregroundImage = foregroundImage;
this.opacityAnimator = new OpacityAnimator();
addMouseListener(new MouseHoverListener());
setOpaque(false);
setBorderPainted(false);
setRolloverEnabled(false);
setCursor(new Cursor(Cursor.HAND_CURSOR));
}
#Override
public Dimension getPreferredSize()
{
if (super.isPreferredSizeSet())
{
return super.getPreferredSize();
}
int w = Math
.max(backgroundImage.getWidth(), foregroundImage.getWidth());
int h = Math.max(backgroundImage.getHeight(),
foregroundImage.getHeight());
return new Dimension(w, h);
}
public void setOpacity(float opacity)
{
this.opacity = opacity;
repaint();
}
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D) gr;
g.drawImage(backgroundImage, 0, 0, null);
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
opacity));
g.drawImage(foregroundImage, 0, 0, null);
}
}
Don't access Swing components from other threads. Use a swing Timer instead.
See How to use swing timers

How to use Netbeans' indeterminate progress-bar style?

I'm programming a Java app using Nimbus look & feel. Unfortunately, the appearance of indeterminate JProgressBars of Nimbus look & feel is AWFUL (see below) :
On the other hand, I've noticed Netbeans with Nimbus look & feel has a different indeterminate JProgressBar style which looks much better (see below) :
How can I use this style in my own application?
You can write your own Painter<JComponent>:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.beans.*;
import javax.swing.*;
import javax.swing.plaf.nimbus.*;
public final class IndeterminateStyleTest {
private final BoundedRangeModel model = new DefaultBoundedRangeModel();
public JComponent makeUI() {
JProgressBar progressBar0 = new JProgressBar(model);
UIDefaults d = new UIDefaults();
d.put("ProgressBar[Enabled+Indeterminate].foregroundPainter", new IndeterminateRegionPainter());
JProgressBar progressBar1 = new JProgressBar(model);
progressBar1.putClientProperty("Nimbus.Overrides", d);
progressBar0.setIndeterminate(true);
progressBar1.setIndeterminate(true);
JPanel p = new JPanel();
p.setBorder(BorderFactory.createEmptyBorder(32, 5, 32, 5));
p.add(progressBar0);
p.add(progressBar1);
return p;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
try {
for (UIManager.LookAndFeelInfo laf : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(laf.getName())) {
UIManager.setLookAndFeel(laf.getClassName());
}
}
} catch (ClassNotFoundException | InstantiationException |
IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new IndeterminateStyleTest().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class IndeterminateRegionPainter extends AbstractRegionPainter {
// Copied from javax.swing.plaf.nimbus.ProgressBarPainter.java
private Color color17 = decodeColor("nimbusOrange", .0f, .0f, .0f, -156);
private Color color18 = decodeColor("nimbusOrange", -.015796512f, .02094239f, -.15294117f, 0);
private Color color19 = decodeColor("nimbusOrange", -.004321605f, .02094239f, -.0745098f, 0);
private Color color20 = decodeColor("nimbusOrange", -.008021399f, .02094239f, -.10196078f, 0);
private Color color21 = decodeColor("nimbusOrange", -.011706904f, -.1790576f, -.02352941f, 0);
private Color color22 = decodeColor("nimbusOrange", -.048691254f, .02094239f, -.3019608f, 0);
private Color color23 = decodeColor("nimbusOrange", .003940329f, -.7375322f, .17647058f, 0);
private Color color24 = decodeColor("nimbusOrange", .005506739f, -.46764207f, .109803915f, 0);
private Color color25 = decodeColor("nimbusOrange", .0042127445f, -.18595415f, .04705882f, 0);
private Color color26 = decodeColor("nimbusOrange", .0047626942f, .02094239f, .0039215684f, 0);
private Color color27 = decodeColor("nimbusOrange", .0047626942f, -.15147138f, .1607843f, 0);
private Color color28 = decodeColor("nimbusOrange", .010665476f, -.27317524f, .25098038f, 0);
private Rectangle2D rect = new Rectangle2D.Float(0, 0, 0, 0);
private Path2D path = new Path2D.Float();
private PaintContext ctx = new PaintContext(new Insets(5, 5, 5, 5), new Dimension(29, 19), false);
#Override public void doPaint(Graphics2D g, JComponent c, int width, int height, Object[] extendedCacheKeys) {
path = decodePath1();
g.setPaint(color17);
g.fill(path);
rect = decodeRect3();
g.setPaint(decodeGradient5(rect));
g.fill(rect);
rect = decodeRect4();
g.setPaint(decodeGradient6(rect));
g.fill(rect);
}
#Override public PaintContext getPaintContext() {
return ctx;
}
private Path2D decodePath1() {
path.reset();
path.moveTo(decodeX(0.6f), decodeY(0.12666667f));
path.curveTo(decodeAnchorX(0.6000000238418579f, -2.0f), decodeAnchorY(0.12666666507720947f, 0.0f), decodeAnchorX(0.12666666507720947f, 0.0f), decodeAnchorY(0.6000000238418579f, -2.0f), decodeX(0.12666667f), decodeY(0.6f));
path.curveTo(decodeAnchorX(0.12666666507720947f, 0.0f), decodeAnchorY(0.6000000238418579f, 2.0f), decodeAnchorX(0.12666666507720947f, 0.0f), decodeAnchorY(2.4000000953674316f, -2.0f), decodeX(0.12666667f), decodeY(2.4f));
path.curveTo(decodeAnchorX(0.12666666507720947f, 0.0f), decodeAnchorY(2.4000000953674316f, 2.0f), decodeAnchorX(0.6000000238418579f, -2.0f), decodeAnchorY(2.8933334350585938f, 0.0f), decodeX(0.6f), decodeY(2.8933334f));
path.curveTo(decodeAnchorX(0.6000000238418579f, 2.0f), decodeAnchorY(2.8933334350585938f, 0.0f), decodeAnchorX(3.0f, 0.0f), decodeAnchorY(2.8933334350585938f, 0.0f), decodeX(3.0f), decodeY(2.8933334f));
path.lineTo(decodeX(3.0f), decodeY(2.6f));
path.lineTo(decodeX(0.4f), decodeY(2.6f));
path.lineTo(decodeX(0.4f), decodeY(0.4f));
path.lineTo(decodeX(3.0f), decodeY(0.4f));
path.lineTo(decodeX(3.0f), decodeY(0.120000005f));
path.curveTo(decodeAnchorX(3.0f, 0.0f), decodeAnchorY(0.12000000476837158f, 0.0f), decodeAnchorX(0.6000000238418579f, 2.0f), decodeAnchorY(0.12666666507720947f, 0.0f), decodeX(0.6f), decodeY(0.12666667f));
path.closePath();
return path;
}
private Rectangle2D decodeRect3() {
rect.setRect(decodeX(0.4f), //x
decodeY(0.4f), //y
decodeX(3.0f) - decodeX(0.4f), //width
decodeY(2.6f) - decodeY(0.4f)); //height
return rect;
}
private Rectangle2D decodeRect4() {
rect.setRect(decodeX(0.6f), //x
decodeY(0.6f), //y
decodeX(2.8f) - decodeX(0.6f), //width
decodeY(2.4f) - decodeY(0.6f)); //height
return rect;
}
private Paint decodeGradient5(Shape s) {
Rectangle2D bounds = s.getBounds2D();
float x = (float)bounds.getX();
float y = (float)bounds.getY();
float w = (float)bounds.getWidth();
float h = (float)bounds.getHeight();
return decodeGradient((0.5f * w) + x, (0.0f * h) + y, (0.5f * w) + x, (1.0f * h) + y,
new float[] { 0.038709678f, 0.05483871f, 0.07096774f, 0.28064516f, 0.4903226f, 0.6967742f, 0.9032258f, 0.9241935f, 0.9451613f },
new Color[] { color18,
decodeColor(color18, color19, 0.5f),
color19,
decodeColor(color19, color20, 0.5f),
color20,
decodeColor(color20, color21, 0.5f),
color21,
decodeColor(color21, color22, 0.5f),
color22
});
}
private Paint decodeGradient6(Shape s) {
Rectangle2D bounds = s.getBounds2D();
float x = (float)bounds.getX();
float y = (float)bounds.getY();
float w = (float)bounds.getWidth();
float h = (float)bounds.getHeight();
return decodeGradient((0.5f * w) + x, (0.0f * h) + y, (0.5f * w) + x, (1.0f * h) + y,
new float[] { 0.038709678f, 0.061290324f, 0.08387097f, 0.27258065f, 0.46129033f, 0.4903226f, 0.5193548f, 0.71774197f, 0.91612905f, 0.92419356f, 0.93225807f },
new Color[] { color23,
decodeColor(color23, color24, 0.5f),
color24,
decodeColor(color24, color25, 0.5f),
color25,
decodeColor(color25, color26, 0.5f),
color26,
decodeColor(color26, color27, 0.5f),
color27,
decodeColor(color27, color28, 0.5f),
color28
});
}
}
You may also subclass from JProgressBar and override paintComponent method. This gives you complete freedom in drawing whatever you like. And even maybe less code than accepted solution.
Here's an example of overriding only the indeterminate behavior of Nimbus with more windows-like gradient look:
The disadvantage of this solution is that you need to explicitly dispose() the component (or maybe I haven't found the appropriate way to shutdown executor automatically)
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.UIManager;
import javax.swing.WindowConstants;
public final class Test
{
public JComponent makeUI()
{
final JProgressBar progressBar=new GradientProgressBar();
progressBar.setPreferredSize(new Dimension(700, 30));
progressBar.setIndeterminate(true);
progressBar.setStringPainted(true);
progressBar.setString("Loading...");
final JPanel p=new JPanel();
p.setBorder(BorderFactory.createEmptyBorder(32, 5, 32, 5));
p.add(progressBar);
return p;
}
public static void main(String[] args)
{
EventQueue.invokeLater(()->createAndShowGUI());
}
public static void createAndShowGUI()
{
try
{
for (UIManager.LookAndFeelInfo laf:UIManager.getInstalledLookAndFeels())
{
if ("Nimbus".equals(laf.getName()))
{
UIManager.setLookAndFeel(laf.getClassName());
}
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
JFrame f=new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new Test().makeUI());
f.setSize(800, 600);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class GradientProgressBar extends JProgressBar
{
private static final long serialVersionUID=8886375125424236114L;
private static final int UPDATE_INTERVAL_MS=50;
private static final AtomicInteger THREAD_NUM=new AtomicInteger();
private static final AtomicReference<ScheduledThreadPoolExecutor> executor=new AtomicReference<>();
private static final Font FONT=new Font("SansSerif", Font.BOLD, 12);
private static final int R_BACK=214;
private static final int G_BACK=217;
private static final int B_BACK=223;
private static final int COLOR_DELTA=20;
private static final Color COLOR_BAR=new Color(191, 98, 4);
private final AtomicInteger position=new AtomicInteger(-20);
#Override
protected void paintComponent(final Graphics g)
{
if (isIndeterminate())
{
drawGradient((Graphics2D) g);
adjustPosition();
}
else
{
super.paintComponent(g);
}
}
private void drawGradient(final Graphics2D g)
{
if (getWidth() > 0 && getHeight() > 0)
{
final int gradWidth=getWidth() / 4;
final float posF=(float) position.get();
for (int y=0;y < getWidth();y++)
{
GradientPaint grad=new GradientPaint(posF, 0.0f, getBackgroundAtY(y), posF + (float) gradWidth / 2.0f, 0.0f, COLOR_BAR);
Rectangle rect=new Rectangle(0, y, position.get() + gradWidth / 2, 1);
g.setPaint(grad);
g.fill(rect);
grad=new GradientPaint(posF + (float) gradWidth / 2.0f, 0.0f, COLOR_BAR, posF + (float) gradWidth, 0.0f, getBackgroundAtY(y));
rect=new Rectangle(position.get() + gradWidth / 2, y, getWidth() - position.get(), 1);
g.setPaint(grad);
g.fill(rect);
}
drawTitle(g);
}
}
public void drawTitle(final Graphics g)
{
if (isStringPainted() && getString() != null && !getString().isBlank())
{
g.setColor(Color.BLACK);
final FontMetrics metrics=g.getFontMetrics(FONT);
final int x=(getWidth() - metrics.stringWidth(getString())) / 2;
final int y=(getHeight() - metrics.getHeight()) / 2 + metrics.getAscent();
g.setFont(FONT);
g.drawString(getString(), x, y);
}
}
private Color getBackgroundAtY(final int y)
{
if (y <= 2 || y >= getHeight() - 2)
{
// Border
return new Color(R_BACK - COLOR_DELTA, G_BACK - COLOR_DELTA, B_BACK - COLOR_DELTA);
}
// Vertical gradient
final int height=(getHeight() - 4) / 2;
final int delta=Math.abs(height - y);
final int colorDelta=COLOR_DELTA * delta / height;
return new Color(R_BACK + colorDelta, G_BACK + colorDelta, B_BACK + colorDelta);
}
#Override
public void setIndeterminate(boolean newValue)
{
if (newValue != isIndeterminate())
{
super.setIndeterminate(newValue);
if (newValue)
{
executor.set(new ScheduledThreadPoolExecutor(1, r -> new Thread(r, "guihelper_progress-" + THREAD_NUM.incrementAndGet())));
executor.get().scheduleAtFixedRate(() -> repaint(), 0, UPDATE_INTERVAL_MS, TimeUnit.MILLISECONDS);
}
else
{
if (executor.get() != null)
{
executor.get().shutdownNow();
}
}
}
}
public void dispose()
{
if (executor.get() != null)
{
executor.get().shutdownNow();
}
}
private void adjustPosition()
{
int step=getWidth() * UPDATE_INTERVAL_MS / 10000;
if (step <= 0)
{
step=1;
}
position.set(position.get() + step);
if (position.get() > getWidth())
{
position.set(-getWidth() / 4);
}
}
}

CardLayout showing two panels, flashing

I'm trying to use CardLayout to show two JPanels, a main menu, and a controls screen. When I add two JPanels to my cards JPanel, it just shows two with flashing images. Here is my code:
package main;
public class MazeGame {
// Layout
public static JPanel cards = new JPanel();
// Window
public static JFrame window;
public static String windowLabel = "2D Maze Game - Before Alpha";
// Window Dimensions and Location
public static int WIDTH = 600;
public static int HEIGHT = 600;
public static Component center = null;
public static int exit = 3;
public static void main(String[] args) {
window = new JFrame(windowLabel);
window.setSize(new Dimension(WIDTH, HEIGHT));
window.setResizable(false);
window.setLocationRelativeTo(center);
window.setDefaultCloseOperation(exit);
cards.setLayout(new CardLayout());
cards.add(new MazeGamePanel(), "main");
cards.add(new MazeControlsPanel(), "controls");
window.add(cards);
CardLayout cl = (CardLayout) cards.getLayout();
cl.show(cards, "main");
window.setVisible(true);
}
}
MazeGamePanel:
public class MazeGamePanel extends JPanel implements Runnable {
private static final long serialVersionUID = 1L;
// Timer
public Timer timer;
// Font
public Font bitTrip;
public Thread thread;
public BufferedImage canvas;
public Graphics2D g;
public boolean running;
public int HEIGHT = MazeGame.HEIGHT;
public int WIDTH = MazeGame.WIDTH;
public int FPS = 30;
public int opacity = 255;
public int selectedOption = 0;
public String option1 = "Play";
public String option2 = "Controls";
public String option3 = "Quit";
public MazeGamePanel() {
this.setFocusable(true);
this.requestFocus();
addKeyListener(new MazeGameKeyListener());
try {
bitTrip = Font.createFont(Font.TRUETYPE_FONT, new File(
"res/font/BitTrip7.TTF"));
} catch (FontFormatException | IOException e) {
e.printStackTrace();
}
/**
ActionListener action = new ActionListener () {
public void actionPerformed (ActionEvent e) {
if(opacity != 0) {
opacity--;
} else {
timer.stop();
opacity = 0;
}
}
};
timer = new Timer(10, action);
timer.setInitialDelay(0);
timer.start();
*/
}
public void addNotify() {
super.addNotify();
if (thread == null) {
thread = new Thread(this);
thread.start();
}
}
public void run() {
running = true;
canvas = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
g = (Graphics2D) canvas.getGraphics();
long startTime = 0;
long millis = 0;
long waitTime = 0;
long targetTime = 1000 / FPS;
while (running) {
startTime = System.nanoTime();
update();
render();
draw();
millis = (System.nanoTime() - startTime) / 1000000;
waitTime = targetTime - millis;
try {
Thread.sleep(waitTime);
} catch (Exception e) {
}
}
}
// TODO
public void render() {
g.setColor(Color.WHITE);
g.fillRect(0, 0, WIDTH, HEIGHT);
bitTrip = bitTrip.deriveFont(40F);
g.setFont(bitTrip);
if (selectedOption == 0) {
//Play
g.setColor(Color.BLACK);
g.drawString(option1, WIDTH / 2 - 200, HEIGHT / 2);
//Controls
g.setColor(Color.GRAY);
g.drawString(option2, WIDTH / 2 - 200, HEIGHT / 2 + 50);
//Quit
g.setColor(Color.GRAY);
g.drawString(option3, WIDTH / 2 - 200, HEIGHT / 2 + 100);
} else if (selectedOption == 1) {
//Play
g.setColor(Color.GRAY);
g.drawString(option1, WIDTH / 2 - 200, HEIGHT / 2);
//Controls
g.setColor(Color.BLACK);
g.drawString(option2, WIDTH / 2 - 200, HEIGHT / 2 + 50);
//Quit
g.setColor(Color.GRAY);
g.drawString(option3, WIDTH / 2 - 200, HEIGHT / 2 + 100);
} else if (selectedOption == 2) {
//Play
g.setColor(Color.GRAY);
g.drawString(option1, WIDTH / 2 - 200, HEIGHT / 2);
//Controls
g.setColor(Color.GRAY);
g.drawString(option2, WIDTH / 2 - 200, HEIGHT / 2 + 50);
//Quit
g.setColor(Color.BLACK);
g.drawString(option3, WIDTH / 2 - 200, HEIGHT / 2 + 100);
}
//g.setColor(new Color(0, 0, 0, opacity));
//g.fillRect(0, 0, WIDTH, HEIGHT);
}
public void update() {
}
public void draw() {
Graphics g2 = this.getGraphics();
g2.drawImage(canvas, 0, 0, null);
g2.dispose();
}
private class MazeGameKeyListener extends KeyAdapter {
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_UP) {
if (selectedOption == 1) {
selectedOption = 0;
} else if (selectedOption == 2) {
selectedOption = 1;
}
}
if (e.getKeyCode() == KeyEvent.VK_DOWN) {
if (selectedOption == 0) {
selectedOption = 1;
} else if (selectedOption == 1) {
selectedOption = 2;
}
}
if(e.getKeyCode() == KeyEvent.VK_ENTER) {
if(selectedOption == 1) {
MazeGame.window.removeAll();
MazeGame.window.add(new MazeControlsPanel());
MazeGame.window.validate();
}
}
}
}
}
MazeControlsPanel:
package screens;
public class MazeControlsPanel extends JPanel implements Runnable {
private static final long serialVersionUID = 1L;
// Font
public Font bitTrip;
public Thread thread;
public BufferedImage canvas;
public Graphics2D g;
public boolean running;
public int HEIGHT = MazeGame.HEIGHT;
public int WIDTH = MazeGame.WIDTH;
public int FPS = 30;
public int opacity = 255;
public int selectedOption = 0;
public MazeControlsPanel() {
this.setFocusable(true);
this.requestFocus();
addKeyListener(new MazeControlsKeyListener());
try {
bitTrip = Font.createFont(Font.TRUETYPE_FONT, new File(
"res/font/BitTrip7.TTF"));
} catch (FontFormatException | IOException e) {
e.printStackTrace();
}
/**
final Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
if (opacity != 0) {
opacity--;
} else {
timer.cancel();
opacity = 0;
}
}
}, 0, 4);
*/
}
public void addNotify() {
super.addNotify();
if (thread == null) {
thread = new Thread(this);
thread.start();
}
}
public void run() {
running = true;
canvas = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
g = (Graphics2D) canvas.getGraphics();
long startTime = 0;
long millis = 0;
long waitTime = 0;
long targetTime = 1000 / FPS;
while (running) {
startTime = System.nanoTime();
update();
render();
draw();
millis = (System.nanoTime() - startTime) / 1000000;
waitTime = targetTime - millis;
try {
Thread.sleep(waitTime);
} catch (Exception e) {
}
}
}
// TODO
public void render() {
g.setColor(Color.WHITE);
g.fillRect(0, 0, WIDTH, HEIGHT);
bitTrip = bitTrip.deriveFont(40F);
g.setFont(bitTrip);
// Quit
g.setColor(Color.BLACK);
g.drawString("Main Menu", WIDTH / 2 - 200, HEIGHT / 2 + 100);
//g.setColor(new Color(0, 0, 0, opacity));
//g.fillRect(0, 0, WIDTH, HEIGHT);
}
public void update() {
}
public void draw() {
Graphics g2 = this.getGraphics();
g2.drawImage(canvas, 0, 0, null);
g2.dispose();
}
private class MazeControlsKeyListener extends KeyAdapter {
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
}
}
}
}
Here's a problem:
final Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
Don't use a java.util.Timer in a Swing program as you will have threading problems. Instead use a Swing Timer.
Also, you're making Swing calls in background threads and using Graphics object obtained by calling getGraphics() on a component, two no-nos for Swing programs.
EDIT
Here is a program that I worked on that swaps components with a CardLayout, but fades one component out as it fades the other one in. What it does:
The program adds all the swapping components to the CardLayout using JPanel.
It also adds a single SwappingImgPanel, a JPanel created to draw two images, one of the component that is fading out, and one of the component that is fading in.
When you swap components, you create images of the two components, the one currently visible, and the one that will next be visible.
You send the images to the SwappingImgPanel instance
You call swap() on the SwappingImgPanel instance.
The SwappingImgPanel will then draw both images but uses a Swing Timer to change the Graphic object's composite value. This is what causes an image to be partially visible.
When the SwappingImgPanel's Timer is done, a done() method is called which sets the SwappingImgPanel's State to State.DONE.
The main GUI is listening to the SwappingImgPanel's state value, and when it achieves State.DONE, the main GUI shows the actual next component (and not an image of it).
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.HashMap;
import java.util.Map;
import javax.swing.*;
#SuppressWarnings("serial")
public class DimmingPanelSwaps extends JPanel {
private static final int DELTA_TIME = 10;
private static final int ELAPSED_TIME = 3000;
private static final String SWAPPING_IMG_PANEL = "swapping img panel";
private CardLayout cardlayout = new CardLayout();
private JPanel cardHolderPanel = new JPanel(cardlayout);
private DefaultComboBoxModel<String> comboModel = new DefaultComboBoxModel<>();
private JComboBox<String> cardCombo = new JComboBox<>(comboModel);
private Map<String, JComponent> componentMap = new HashMap<String, JComponent>();
private String key = "";
private SwappingImgPanel swappingImgPanel = new SwappingImgPanel(DELTA_TIME, ELAPSED_TIME);
public DimmingPanelSwaps() {
registerComponent(createComponentOne(), "one");
registerComponent(createComponentTwo(), "two");
registerComponent(createComponentThree(), "three");
registerComponent(createComponentFour(), "four");
key = "one";
cardHolderPanel.add(swappingImgPanel, SWAPPING_IMG_PANEL);
JPanel southPanel = new JPanel();
southPanel.add(cardCombo);
setLayout(new BorderLayout());
add(cardHolderPanel, BorderLayout.CENTER);
add(southPanel, BorderLayout.SOUTH);
swappingImgPanel.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
if (pcEvt.getNewValue() == State.DONE) {
cardlayout.show(cardHolderPanel, key);
cardCombo.setEnabled(true);
}
}
});
cardCombo.addActionListener(new CardComboListener());
}
private JPanel createComponentFour() {
int rows = 4;
int cols = 4;
int gap = 5;
int tfColumns = 8;
JPanel panel = new JPanel(new GridLayout(rows, cols, gap, gap));
for (int i = 0; i < rows * cols; i++) {
JTextField textField = new JTextField(tfColumns);
JPanel tfPanel = new JPanel();
tfPanel.add(textField);
panel.add(tfPanel);
}
return panel;
}
private JLabel createComponentThree() {
int biWidth = 200;
BufferedImage img = new BufferedImage(biWidth, biWidth, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setPaint(new GradientPaint(0, 0, Color.red, 20, 20, Color.blue, true));
g2.fillOval(0, 0, biWidth, biWidth);
g2.dispose();
Icon icon = new ImageIcon(img);
JLabel label = new JLabel(icon);
return label;
}
private JScrollPane createComponentTwo() {
JTextArea textArea = new JTextArea(15, 40);
JScrollPane scrollpane = new JScrollPane(textArea);
scrollpane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
scrollpane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
return scrollpane;
}
private JPanel createComponentOne() {
JPanel innerPanel = new JPanel(new GridLayout(1, 0, 5, 0));
String[] btnTitles = {"One", "Two", "Three"};
for (String btnTitle : btnTitles) {
JButton btn = new JButton(btnTitle);
innerPanel.add(btn);
}
JPanel panel = new JPanel(new GridBagLayout());
panel.add(innerPanel);
return panel;
}
#SuppressWarnings("hiding")
private void registerComponent(JComponent jComp, String key) {
cardHolderPanel.add(jComp, key);
componentMap.put(key, jComp);
comboModel.addElement(key);
}
private class CardComboListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
final String oldKey = key;
key = (String) cardCombo.getSelectedItem();
cardCombo.setEnabled(false);
final JComponent firstComp = componentMap.get(oldKey);
final BufferedImage firstImg = extractComponentImg(firstComp);
final JComponent secondComp = componentMap.get(key);
final BufferedImage secondImg = extractComponentImg(secondComp);
cardlayout.show(cardHolderPanel, SWAPPING_IMG_PANEL);
swappingImgPanel.setFirstImg(firstImg);
swappingImgPanel.setSecondImg(secondImg);
swappingImgPanel.swap();
}
private BufferedImage extractComponentImg(final JComponent component) {
Dimension size = component.getSize();
BufferedImage img = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
component.paint(g2);
g2.dispose();
return img;
}
}
private static void createAndShowGui() {
DimmingPanelSwaps mainPanel = new DimmingPanelSwaps();
JFrame frame = new JFrame("Dimming Panel Swaps");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
/**
* A JPanel that draws two images
* When swap is called, the first image is shown
* Then a Timer dims the first image while it reveals
* the second image.
* When the elapsed time is complete, it sets its state to State.DONE.
* #author Pete
*
*/
#SuppressWarnings("serial")
class SwappingImgPanel extends JPanel {
public static final String STATE = "state";
private BufferedImage firstImg;
private BufferedImage secondImg;
private int deltaTime;
private int elapsedTime;
// state is a "bound" property, one that is listened to via PropertyChangeSupport
private State state = State.PENDING;
private float alpha1;
private float alpha2;
public SwappingImgPanel(final int deltaTime, final int elapsedTime) {
this.deltaTime = deltaTime;
this.elapsedTime = elapsedTime;
}
public void swap() {
setState(State.STARTED);
if (firstImg == null || secondImg == null) {
done();
}
alpha1 = 1.0f;
alpha2 = 0.0f;
new Timer(deltaTime, new ActionListener() {
private int counter = 0;
private int max = elapsedTime / deltaTime;
#Override
public void actionPerformed(ActionEvent e) {
if (counter >= elapsedTime / deltaTime) {
((Timer)e.getSource()).stop();
done();
return;
}
// set new alpha composite values
alpha1 = ((float)max - counter) / (float) max;
alpha2 = (float) counter / (float) max;
// make sure alphas are within bounds
alpha1 = Math.min(1f, alpha1);
alpha1 = Math.max(0f, alpha1);
alpha2 = Math.min(1f, alpha2);
alpha2 = Math.max(0f, alpha2);
repaint();
counter++;
}
}).start();
}
private void done() {
firstImg = null;
secondImg = null;
setState(State.DONE);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (firstImg == null || secondImg == null) {
return;
}
// create a new Graphics2D object with g.create()
// to avoid any possible side effects from changing the
// composite of the JVM's Graphics object
Graphics2D g2 = (Graphics2D) g.create();
// set the first alpha composite, and draw the image
g2.setComposite(((AlphaComposite)g2.getComposite()).derive(alpha1));
g2.drawImage(firstImg, 0, 0, this);
// set the second alpha composite, and draw the image
g2.setComposite(((AlphaComposite)g2.getComposite()).derive(alpha2));
g2.drawImage(secondImg, 0, 0, this);
g2.dispose(); // can get rid of this Graphics because we created it
}
public void setFirstImg(BufferedImage firstImg) {
this.firstImg = firstImg;
}
public void setSecondImg(BufferedImage secondImg) {
this.secondImg = secondImg;
}
public State getState() {
return state;
}
public void setState(State state) {
State oldValue = this.state;
State newValue = state;
this.state = state;
firePropertyChange(STATE, oldValue, newValue);
}
}
/**
* Modeled on SwingWorker.StateValue
* #author Pete
*
*/
enum State {
PENDING, STARTED, DONE
}

Zooming effect not shown in the frame

Here is my code below. I am trying to implement zooming by moving the slider.
However, the effect does'nt show. Please help me on this. I am lost. I am new to java and I am using Netbeans for this.
I further need to click on the zoomed image and display the corresponding points in the actual image. How can I make this possible?
public class TrialZoom extends javax.swing.JFrame {
/**
* Creates new form TrialZoom
*/
private float scaleX, scaleY;
Point p = new Point();
Point q = new Point();
Vector<Point> v = new Vector();
Vector<Float> v_scale = new Vector();
public TrialZoom() {
initComponents();
}
#SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
jPanel1 = new javax.swing.JPanel();
jButton1 = new javax.swing.JButton();
jPanel2 = new javax.swing.JPanel();
jPanel3 = new javax.swing.JPanel();
jSlider2 = new javax.swing.JSlider();
jPanel4 = new javax.swing.JPanel();
jLabel1 = new javax.swing.JLabel();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
jPanel1.setLayout(new java.awt.GridLayout(1, 0));
jButton1.setText("Done");
jButton1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton1ActionPerformed(evt);
}
});
jPanel1.add(jButton1);
getContentPane().add(jPanel1, java.awt.BorderLayout.PAGE_END);
jPanel2.setLayout(new java.awt.BorderLayout());
jPanel3.setLayout(new java.awt.GridLayout(1, 0));
jSlider2.addChangeListener(new javax.swing.event.ChangeListener() {
public void stateChanged(javax.swing.event.ChangeEvent evt) {
jSlider2StateChanged(evt);
}
});
jPanel3.add(jSlider2);
jPanel2.add(jPanel3, java.awt.BorderLayout.PAGE_END);
jLabel1.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
jLabel1.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
jLabel1MouseClicked(evt);
}
});
javax.swing.GroupLayout jPanel4Layout = new javax.swing.GroupLayout(jPanel4);
jPanel4.setLayout(jPanel4Layout);
jPanel4Layout.setHorizontalGroup(
jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 400, Short.MAX_VALUE)
.addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel4Layout.createSequentialGroup()
.addGap(0, 200, Short.MAX_VALUE)
.addComponent(jLabel1)
.addGap(0, 200, Short.MAX_VALUE)))
);
jPanel4Layout.setVerticalGroup(
jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 251, Short.MAX_VALUE)
.addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel4Layout.createSequentialGroup()
.addGap(0, 125, Short.MAX_VALUE)
.addComponent(jLabel1)
.addGap(0, 126, Short.MAX_VALUE)))
);
jPanel2.add(jPanel4, java.awt.BorderLayout.CENTER);
getContentPane().add(jPanel2, java.awt.BorderLayout.CENTER);
pack();
}// </editor-fold>
private void jSlider2StateChanged(javax.swing.event.ChangeEvent evt) {
// TODO add your handling code here:
int val = ((JSlider) evt.getSource()).getValue();
setScale(val * .01f, val * .01f);
}
private void jLabel1MouseClicked(java.awt.event.MouseEvent evt) {
setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.CROSSHAIR_CURSOR));
p = evt.getPoint();
q = SwingUtilities.convertPoint(evt.getComponent(), p, this);
v.add(p);
v_scale.add(scaleX);
v_scale.add(scaleY);
double c = q.getX();
double d = q.getY();
String x1 = Double.toString(p.getX());
String x2 = Double.toString(p.getY());
Graphics g = this.getGraphics();
paint(g, (int) c, (int) d); // TODO add your handling code here:
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
System.out.println(p.getX() + " " + p.getY() + " " + q.getX() + " " + q.getY());
float sx=v_scale.remove(0);
float sy=v_scale.remove(0);
System.out.println(sx+" "+sy);
dispose();
// TODO add your handling code here:
}
public Vector<Point> first(ImageIcon icon) {
jLabel1.setIcon(icon);
return return_vector();
}
public void paint(Graphics g, int a, int b) {
g.setColor(Color.RED);
g.drawRect(a - 1, b - 1, 3, 3);
g.fillRect(a, b, 2, 2);
}
#Override
public Dimension getPreferredSize() {
int prefWidth;
prefWidth = (int) (jLabel1 == null ? 0 : jPanel4.getWidth() * scaleX);
int prefHeight;
prefHeight = (int) (jLabel1 == null ? 0 : jPanel4.getHeight() * scaleY);
return new Dimension(prefWidth, prefHeight);
}
public void paintComponent(Graphics g) {
if (jLabel1 == null) {
return;
}
int w = (int) (jLabel1.getWidth() * scaleX);
int h = (int) (jLabel1.getHeight() * scaleY);
int x = (getWidth() - w) / 2;
int y = (getHeight() - h) / 2;
ImageIcon img_icon=(ImageIcon) jLabel1.getIcon();
g.drawImage(img_icon.getImage(), x, y, w, h, null);
}
public void setScale(float x, float y) {
this.scaleX = x;
this.scaleY = y;
jLabel1.revalidate();
jLabel1.repaint();
}
public Vector<Point> return_vector() {
return this.v;
}
/**
* #param args the command line arguments
*/
public static void main(String args[]) {
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(TrialZoom.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(TrialZoom.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
java.util.logging.Logger.getLogger(TrialZoom.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(TrialZoom.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
//</editor-fold>
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new TrialZoom().setVisible(true);
}
});
}
// Variables declaration - do not modify
private javax.swing.JButton jButton1;
private javax.swing.JLabel jLabel1;
private javax.swing.JPanel jPanel1;
private javax.swing.JPanel jPanel2;
private javax.swing.JPanel jPanel3;
private javax.swing.JPanel jPanel4;
private javax.swing.JSlider jSlider2;
// End of variables declaration
}
First, start with a component that is capable of managing the image and scaling. This should be as self contained as you can make it. This allows you to decouple your program and focus on individual responsibilities of the application.
Next, you need to maintain a list of normalised points. The reason for normalising them is to ensure that the points will continue to be rendered at the right locations when the image scaled...
Take a look at Performing Custom Painting for more details
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
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 ZoomExample {
public static void main(String[] args) {
new ZoomExample();
}
public ZoomExample() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new ZoomPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class ZoomPane extends JPanel {
private JSlider slider;
private ZoomImagePane zoomImagePane;
public ZoomPane() {
zoomImagePane = new ZoomImagePane();
slider = new JSlider(1, 200);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
zoomImagePane.setScale((float) slider.getValue() / 100f);
}
});
setLayout(new BorderLayout());
add(slider, BorderLayout.SOUTH);
add(zoomImagePane);
slider.setValue(100);
}
}
public class ZoomImagePane extends JPanel {
private float scale = 0f;
private BufferedImage master;
private Image scaled;
private List<Point2D> clickPoints;
public ZoomImagePane() {
clickPoints = new ArrayList<>(25);
try {
master = ImageIO.read(new File("/path/to/image"));
setScale(1f);
} catch (IOException ex) {
ex.printStackTrace();
}
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
Point p = e.getPoint();
Point2D scaledPoint = new Point2D.Float();
int xOffset = (getWidth() - scaled.getWidth(null)) / 2;
int yOffset = (getHeight() - scaled.getHeight(null)) / 2;
float x = (float)(p.x - xOffset) / (float)scaled.getWidth(null);
float y = (float)(p.y - yOffset) / (float)scaled.getHeight(null);
scaledPoint.setLocation(x, y);
clickPoints.add(scaledPoint);
repaint();
}
});
}
protected void setScale(float value) {
if (scale != value) {
scale = value;
scaled = master.getScaledInstance((int) ((float) master.getWidth() * scale), -1, Image.SCALE_SMOOTH);
revalidate();
repaint();
}
}
#Override
public Dimension getPreferredSize() {
Dimension size = new Dimension(200, 200);
if (scaled != null) {
size = new Dimension(scaled.getWidth(this), scaled.getHeight(this));
}
return size;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (scaled != null) {
int x = (getWidth() - scaled.getWidth(this)) / 2;
int y = (getHeight() - scaled.getHeight(this)) / 2;
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(scaled, x, y, this);
g2d.setColor(Color.RED);
for (Point2D p : clickPoints) {
int xPos = x + ((int)(p.getX() * scaled.getWidth(this)) - 5);
int yPos = y + ((int)(p.getY() * scaled.getHeight(this)) - 5);
g2d.fillOval(xPos, yPos, 10, 10);
}
g2d.dispose();
}
}
}
}

Categories