Split image into clickable regions - java

Is there any way to split an image to region (right now it's JLabel but I can change it if necessary)?
I use swing in my program and I have an image (square for this example) with some triangles, stars and trapezoids inside it (it can be JPG, PNG, etc).
The idea is that the user will click inside one of those shapes and then I will put another small icon on top of the area the user clicked. The user can click on multiple areas but at the end of the day, I need to know which shapes were clicked.
Seems possible anyone?

Have a look at what I have made:
This is the image I used for testing:
After image has been split:
And here is the source:
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class Test {
private JFrame frame;
private JLabel[] labels;
private static String imagePath = "c:/test.jpg";
private final int rows = 3; //You should decide the values for rows and cols variables
private final int cols = 3;
private final int chunks = rows * cols;
private final int SPACING = 10;//spacing between split images
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Test().createAndShowUI();
}
});
}
private void createAndShowUI() {
frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initComponents();
frame.setResizable(false);
frame.pack();
frame.setVisible(true);
}
private void initComponents() {
BufferedImage[] imgs = getImages();
//set contentpane layout for grid
frame.getContentPane().setLayout(new GridLayout(rows, cols, SPACING, SPACING));
labels = new JLabel[imgs.length];
//create JLabels with split images and add to frame contentPane
for (int i = 0; i < imgs.length; i++) {
labels[i] = new JLabel(new ImageIcon(Toolkit.getDefaultToolkit().createImage(imgs[i].getSource())));
frame.getContentPane().add(labels[i]);
}
}
private BufferedImage[] getImages() {
File file = new File(imagePath); // I have bear.jpg in my working directory
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
} catch (FileNotFoundException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
BufferedImage image = null;
try {
image = ImageIO.read(fis); //reading the image file
} catch (IOException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
int chunkWidth = image.getWidth() / cols; // determines the chunk width and height
int chunkHeight = image.getHeight() / rows;
int count = 0;
BufferedImage imgs[] = new BufferedImage[chunks]; //Image array to hold image chunks
for (int x = 0; x < rows; x++) {
for (int y = 0; y < cols; y++) {
//Initialize the image array with image chunks
imgs[count] = new BufferedImage(chunkWidth, chunkHeight, image.getType());
// draws the image chunk
Graphics2D gr = imgs[count++].createGraphics();
gr.drawImage(image, 0, 0, chunkWidth, chunkHeight, chunkWidth * y, chunkHeight * x, chunkWidth * y + chunkWidth, chunkHeight * x + chunkHeight, null);
gr.dispose();
}
}
return imgs;
}
}
The only flaw is I haven't checked if the image is larger then the screen which could cause problems, that would be resolved by a simple image resize using getScaledInstance(int x,int y, int width, in height) on the image and the separating it into chunks.
Update
Sorry I missed the part if the question in Shapes, have a look at draw(Shape s) method of Graphics2D/Graphics.
I read this:
Any Shape object can be used as a clipping path that restricts the
portion of the drawing area that will be rendered. The clipping path
is part of the Graphics2D context; to set the clip attribute, you call
Graphics2D.setClip and pass in the Shape that defines the clipping
path you want to use.
See here for clipping an u]image to a shape: Clipping the Drawing Region
References:
How to Split an Image into Chunks - Java ImageIO

You can use the getSubImage() method of BufferedImage, illustrated here and here. The example also uses JLabel, but you can add the Icon to a JButton that can be clicked. There are several ways for a button to remember details about it's icon:
Subclass JButton and add a suitable field.
Add a client property to the parent JComponent.
Use the name property of the parent Component.

Related

Swing Graphics color Image directly

Short Question: Is it possible to color an image directly in the graphics2d.drawImage(...) method? I mean not to choose the background color but select a color for every visible pixel on the image.
Here is an example of manipulating pixels of a BufferedImage.
The example inverts this image
to negative by manipulating each pixel:
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class ShowNegativeImage {
public static void main(String[] args) {
BufferedImage bImage = getImage("http://www.digitalphotoartistry.com/rose1.jpg");
image2Negative(bImage);
displayImage(bImage);
}
private static BufferedImage getImage(String path) {
URL url = null;
try {
url = new URL(path);
return ImageIO.read(url);
} catch ( IOException ex) { ex.printStackTrace();}
return null;
}
private static void displayImage(java.awt.Image image){
ImageIcon icon= new ImageIcon(image);
JFrame frame=new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JLabel(icon));
frame.pack();
frame.setVisible(true);
}
private static void image2Negative(BufferedImage bImage) {
for (int x = 0; x < bImage.getWidth(); x++) {
for (int y = 0; y < bImage.getHeight(); y++) {
int rgba = bImage.getRGB(x, y);
Color col = new Color(rgba, true);
col = new Color(255 - col.getRed(),
255 - col.getGreen(),
255 - col.getBlue());
bImage.setRGB(x, y, col.getRGB());
}
}
}
}
Output:

java changing panel field of view

I am trying to create a simple Java 2D physics game, and the current problem that I am running into is that I cannot think of a way to center the camera on the spaceship as it moves around.
I have the model of the ship as a component in the panel Universe which is in the frame main. Currently the panel displays the pixels from (0, 0) to (1000, 1000).
What I want to happen, is if the ship moves 10 pixels to the right, then the panel will display pixels from (10, 0) to (1010, 1000), following the ship, but remaining rigid in the frame.
Or maybe there is a better way to achieve a similar effect.
This is the relevant part I believe:
public class Main extends JFrame {
public static void main(String[] args) {
M = new Main(1000, 1000);
}
public Main(int width, int height) {
boardWidth = width;
boardHeight = height;
FRAMERATE = 60;
setSize(boardWidth, boardHeight);
setTitle("space");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
createUniverse();
createShip();
runThreads();
}
private void createUniverse() {
U = new Universe();
U.setLayout(new BorderLayout());
add(U, BorderLayout.CENTER);
}
private void createShip() {
U.createShip();
}
private void runThreads() {
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(5);
long lengthOfFrame = (long) (1000 / FRAMERATE);
executor.scheduleAtFixedRate(new Input(U.getShip(), FRAMERATE), 0L, lengthOfFrame, TimeUnit.MILLISECONDS);
executor.scheduleAtFixedRate(new UpdatePhysics(U.getObjects(), FRAMERATE), 0L, lengthOfFrame, TimeUnit.MILLISECONDS);
executor.scheduleAtFixedRate(new RepaintFrame(this), 0L, lengthOfFrame, TimeUnit.MILLISECONDS);
this.addKeyListener(new UserInput());
}
}
That is the JFrame that holds this panel:
public class Universe extends JPanel {
public Universe() {
setSize(Main.boardWidth, Main.boardHeight);
setBackground(Color.BLACK);
setVisible(true);
}
public void createShip() {
ship = new Ship(new double[]{getWidth() / 2, getHeight() / 2});
shipM = new ShipModel(ship);
_objects = new MassiveObject[1];
_objects[0] = ship;
add(shipM, BorderLayout.CENTER);
}
}
The ShipModel is just a Java component that is represented by a few polygons.
The frame is painted by the thread in Main which basically just calls Main.repaint() repeatedly.
I think thats all the relevant bits. My bad for the link, never posted before.
My suggestion would be to go a different route, or at least a slightly different route.
You definitely do not want to retrieve pixels from the picture and draw the new pixels every frame (you will draw the pixels every frame, but getting pixels every tick will slow you down)
You could load the background image altogether (which you are likely already doing from your abstract description) and then move that background image's position according to button presses. The ship would always be rendered in the middle. (The edges of the background image is where this gets tricky -- a whole other challenge to tackle)
There is also the option of splitting the background image into tiles and moving those accordingly.
I've done both of the above, and since I wanted the background image to be extremely large, it only moved smoothly after I split it up into tiles programmatically, storing it in memory and rending each tile according to the position.
Like I said, the edges are where it gets tricky if you want your spaceship to move to the edge. If you just show infinite black behind the background image and you don't mind showing that, then you can just keep the same physics and have only half of the screen showing the background image.
So summary, just move the background image within the frame/panel accordingly.
Your question was not super clear and linking to your code is not great practice on Stack Overflow (it's better to include the relevant code snippets in your post using backticks like this:
This is code, I used backticks to surround it
That makes it scroll if it's long.
Consider placing your background image within a JScrollPane, remove the scrollbars by setting the appropriate scrolling policy:
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
You can move the image held within the JScrollPane by calling
backgroundLabel.scrollRectToVisible(rect);
and change the location of this rect based on button presses.
For example:
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.Rectangle;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
#SuppressWarnings("serial")
public class SideScroll extends JLayeredPane {
public static final String BG_IMG_PATH = "https://upload.wikimedia.org/wikipedia/commons"
+ "/a/ad/Tomada_da_cidade_de_S%C3%A3o_Salvador_s%C3%A9culo_XVIII_%28panor%C3%A2mico%29.jpg";
public static final String CAMEL_PATH = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/33/PEO-bactrian_camel.svg/200px-PEO-bactrian_camel.svg.png";
private static final int PREF_W = 800;
private static final int PREF_H = 650;
protected static final int SCALE = 10;
private JLabel backgroundLabel = new JLabel();
JScrollPane scrollPane = new JScrollPane(backgroundLabel);
private JLabel camelLabel = new JLabel();
public SideScroll(Icon bgIcon, Icon camelIcon) {
camelLabel.setIcon(camelIcon);
camelLabel.setSize(camelLabel.getPreferredSize());
JPanel camelPanel = new JPanel(new GridBagLayout());
camelPanel.setOpaque(false);
camelPanel.add(camelLabel);
camelPanel.setSize(getPreferredSize());
backgroundLabel.setIcon(bgIcon);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
scrollPane.setSize(getPreferredSize());
add(scrollPane, JLayeredPane.DEFAULT_LAYER);
add(camelPanel, JLayeredPane.PALETTE_LAYER);
setFocusable(true);
addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
switch (keyCode) {
case KeyEvent.VK_LEFT:
moveImg(-1, 0);
break;
case KeyEvent.VK_RIGHT:
moveImg(1, 0);
break;
case KeyEvent.VK_UP:
moveImg(0, -1);
break;
case KeyEvent.VK_DOWN:
moveImg(0, 1);
break;
default:
break;
}
}
private void moveImg(int right, int down) {
Rectangle rect = backgroundLabel.getVisibleRect();
int x = rect.x + SCALE * right;
int y = rect.y + SCALE * down;
int width = rect.width;
int height = rect.height;
rect = new Rectangle(x, y, width, height);
backgroundLabel.scrollRectToVisible(rect);
}
});
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
Icon bgIcon = null;
BufferedImage camel = null;
Icon camelIcon = null;
BufferedImage bgImg;
try {
URL bgImageUrl = new URL(BG_IMG_PATH);
URL camelUrl = new URL(CAMEL_PATH);
bgImg = ImageIO.read(bgImageUrl);
camel = ImageIO.read(camelUrl);
// make background one quarter the size because it's too big
int imgW = bgImg.getWidth() / 4;
int imgH = bgImg.getHeight() / 4;
BufferedImage bgImage2 = new BufferedImage(imgW, imgH, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = bgImage2.createGraphics();
g2.drawImage(bgImg, 0, 0, imgW, imgH, null);
g2.dispose();
bgIcon = new ImageIcon(bgImage2);
// flip camel image so facing right
imgW = camel.getWidth();
imgH = camel.getHeight();
BufferedImage camelImg = new BufferedImage(imgW, imgH, BufferedImage.TYPE_INT_ARGB);
g2 = camelImg.createGraphics();
AffineTransform xform = AffineTransform.getTranslateInstance(imgW, 0);
xform.scale(-1, 1);
g2.drawImage(camel, xform, null);
g2.dispose();
camelIcon = new ImageIcon(camelImg);
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
JFrame frame = new JFrame("SideScroll");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new SideScroll(bgIcon, camelIcon));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}

How to Export JPanel with scrollable into pdf file

i have design a jpanel with so many swing components with scrollable capability. i want to export whole Jpanle into pdf file.but i am not able to export whole Jpanle.
I have use itext for pdf generation.
My problem is that i am not able to export whole jpanel into pdf. When i export half of Jpanel
components export.but half portion do not export.
this is my code.
public void PrintFrameToPDF(JPanel c, File file) {
try {
Document d = new Document();
PdfWriter writer = PdfWriter.getInstance(d, new FileOutputStream("F://newfile.pdf"));
d.open();
PdfContentByte cb = writer.getDirectContent();
PdfTemplate template = cb.createTemplate(800, 1600);
Graphics2D g2d = template.createGraphics(800, 1600);
// g2d.translate(1.0, 1.0);
c.paintAll(g2d);
// c.addNotify();
// c.validate();
g2d.dispose();
cb.addTemplate(template, 0, 0);
d.close();
} catch (Exception e) {
//
}
}
plz help me .
tnx
Consider creating a java.awt.Image from the panel first (by painting the panel to an Image). You can then get an instance of com.itextpdf.text.Image using:
com.itextpdf.text.Image.getInstance(PdfWriter writer,
java.awt.Image awtImage,
float quality) -
Gets an instance of a com.itextpdf.textImage from a java.awt.Image. The image is added as a JPEG with a user defined quality.
Then you can scale the image with the com.itextpdf.text.Image API methods scaleToFit or scalePercent() (as used in the example below). More information on using images (in iText) can be found here
The following program create a panel of size 2000x2000 in which squares 20x20 (of 100px each) are drawn onto a panel. The panel is contained inside a scroll pane. It is then painted on to an image, where the image will be scaled and added to the pdf document and printed to pdf.
The below image just shows how the entire panel is drawn onto the image then another panel is created, using the previous panel image, to draw onto the the new panel. The new panel is then shown via a dialog.
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.FileOutputStream;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import com.itextpdf.text.Document;
import com.itextpdf.text.Image;
import com.itextpdf.text.pdf.PdfWriter;
/**
* This example requires iText. I retrieved it from Maven repository
*
* <dependency>
* <groupId>com.itextpdf</groupId>
* <artifactId>itextpdf</artifactId>
* <version>5.5.2</version>
* </dependency>
*
* The program can be run without iText if you comment out the entire
* method printToPDF (and iText imports), along with it's method call in
* the class constructor. The result will be the the image above.
*/
public class LargePanelToImageMCVE {
public LargePanelToImageMCVE() {
LargeImagePanel panel = new LargeImagePanel();
JFrame frame = new JFrame();
frame.add(new JScrollPane(panel));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setVisible(true);
final java.awt.Image image = getImageFromPanel(panel);
/* This was just a text panel to make sure the full panel was
* drawn to the panel.
*/
JPanel newPanel = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
};
/* Print Image to PDF */
String fileName = "D://newfile.pdf";
printToPDF(image, fileName);
/* This was just a test to show the newPanel drew the entire
* original panel (scaled)
*/
JOptionPane.showMessageDialog(null, newPanel);
}
public void printToPDF(java.awt.Image awtImage, String fileName) {
try {
Document d = new Document();
PdfWriter writer = PdfWriter.getInstance(d, new FileOutputStream(
fileName));
d.open();
Image iTextImage = Image.getInstance(writer, awtImage, 1);
iTextImage.setAbsolutePosition(50, 50);
iTextImage.scalePercent(25);
d.add(iTextImage);
d.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static java.awt.Image getImageFromPanel(Component component) {
BufferedImage image = new BufferedImage(component.getWidth(),
component.getHeight(), BufferedImage.TYPE_INT_RGB);
component.paint(image.getGraphics());
return image;
}
/**
* Demo panel that is 2000x2000 px with alternating squares
* to check all squares are drawn to image
*/
public class LargeImagePanel extends JPanel {
private static final int FULL_SIZE = 2000;
private static final int PER_ROW_COLUMN = 20;
private static final int SQUARE_SIZE = FULL_SIZE / PER_ROW_COLUMN;
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (int row = 0; row < PER_ROW_COLUMN; row++) {
for (int col = 0; col < PER_ROW_COLUMN; col++) {
if ((row % 2) == (col % 2)) {
g.setColor(Color.BLACK);
} else {
g.setColor(Color.WHITE);
}
g.fillRect(col * SQUARE_SIZE, row * SQUARE_SIZE,
SQUARE_SIZE, SQUARE_SIZE);
}
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(FULL_SIZE, FULL_SIZE);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new LargePanelToImageMCVE();
}
});
}
}

Graphical/File output for Grayscale BufferedImage

I am doing this for a project at school, in which much more functionality will be added later, but I am having trouble getting the basic setup done.
I tried doing this in C++ initially, but decided to switch to Java after reading some documentation on BufferedImage, but I am running into an issue with output. Essentially this is what I am designing the flow of the program to be:
1) Extract original image's (BMP image that is supplied in grayscale) grayscale values. 2) Create a currentImage variable to preserve the original image and perform the modifications to the image I intend to perform in the later stages of the project. 3) Create a reusable output method that will take the currentImage BufferedImage, and output it to a panel (and maybe a file as well.
Here is what I have so far, which currently results in nothing being outputted to the screen, and when I tried doing System logs, it is not setting the pixel values in my draw mechanism:
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.io.File;
import java.io.IOException;
#SuppressWarnings("serial")
public class ImageDriver extends JFrame {
DrawPanel paintPanel;
static int width = 1024, height = 768;
BufferedImage originalImage, currentImage;
File theImageFile;
int[][] imageData;
public ImageDriver() {
super("Image Processing");
setSize(width, height);
setDefaultCloseOperation(EXIT_ON_CLOSE);
paintPanel = new DrawPanel(width, height);
add(paintPanel);
}
public void updateImageDisplay() {
System.out.println("In updateImageDisplay!");
paintPanel.setImage(currentImage);
}
public void readImage() {
try {
theImageFile = new File("images/img1.bmp");
originalImage = ImageIO.read(theImageFile);
currentImage = originalImage;
}
catch (IOException e) {
e.printStackTrace();
}
//get a raster to extract grayscale values
Raster image_raster = currentImage.getData();
//get pixel by pixel
int[] pixel = new int[1];
int[] buffer = new int[1];
imageData = new int[image_raster.getWidth()][image_raster.getHeight()];
for(int i = 0 ; i < image_raster.getWidth() ; i++)
for(int j = 0 ; j < image_raster.getHeight() ; j++) {
pixel = image_raster.getPixel(i, j, buffer);
imageData[i][j] = pixel[0];
//System.out.println("Pixel at: " + i + " x " + j + ": " + imageData[i][j]);
}
}
public void increaseContrast() {
}
public static void main(String[] args) {
ImageDriver driver = new ImageDriver();
driver.setVisible(true);
driver.readImage();
driver.updateImageDisplay();
driver.increaseContrast();
driver.updateImageDisplay();
}
class DrawPanel extends JPanel {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
public DrawPanel(int width, int height) {
setPreferredSize(new Dimension(width, height));
}
public void setImage(BufferedImage image) {
System.out.println("In setImage!");
this.image = image;
repaint();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("in paintPanel component!");
g = image.getGraphics();
g.drawImage(image, 0, 0, null);
}
}
}
I have a hunch that it is because I am declaring image in my DrawPanel to be an empty image at first, and setImage is not properly copying over all of its contents upon assignment. I tried fiddling around with using this:
this.image.setData(image.getData());
but to no avail.
I missing something here? Or is a complete re-write if the mechanism in order?
Thank you for reading.
The first thing that jumps out at me is this (in your paintComponent method)...
g = image.getGraphics();
g.drawImage(image, 0, 0, null);
Basically, you are painting the image back to itself, instead you should do something like like...
g.drawImage(image, 0, 0, this);
Using the Graphics context you were passed

How to simulate video within ImageIcon?

I have JToggleButton and this method capturing events:
tgl_playMouseClicked(java.awt.event.MouseEvent evt) {
new Thread() {
public void run() {
int i = 0;
String outputName = null;
while ((i <= 99)) {
ImageIcon imgThisImg = new ImageIcon("images/" + outputName + i + ".png");
lbl_image.setIcon(imgThisImg);
i++;
}
tgl_play.setSelected(!tgl_play.isSelected());
}
}.start();
}
I try to simulate video by reading and showing single images in imageIcon.
When I fist time click on JToggleButton, all is ok. Video is running. But when I press again, nothing happens. The event is captured as prints are displayed in output, but no refresh on ImageIcon.
I use thread there in order to be able to set some delay between frames.
What's wrong? Help me please
I think that one of your best shot is to use javax.swing.Timer to pace your "video". This will ensure that you are doing everything properly with Swing EDT.
(If the millisecond is not sufficient, then I would take a look at: java.util.concurrent.Executors.newScheduledThreadPool(int) and java.util.concurrent.ScheduledThreadPoolExecutor.scheduleAtFixedRate(Runnable, long, long, TimeUnit) and add Runnable's that immediately call all their code in SwingUtilities.invokeLater())
Here I made a small demo example with a list of images displaying a growing and shrinking circle (the images are created on the fly with some JPanel but this is just for the demo).
import java.awt.Color;
import java.awt.Graphics;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UnsupportedLookAndFeelException;
public class TestAnimation {
private static final int NB_OF_IMAGES = 50;
private static final int NB_OF_IMAGES_PER_SECOND = 25;
private static final int WIDTH = 300;
private static final int HEIGHT = 300;
protected void initUI() {
final JFrame frame = new JFrame(TestAnimation.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
// the label on which I will set images
final JLabel label = new JLabel();
// By adding to the frame, it is set as the central component of the
// BorderLayout of the JFrame. Eventually, the label will have the size of the content pane
frame.add(label);
frame.setSize(WIDTH, HEIGHT);
// Creating a list of images (just for demo purposes)
final List<Image> images = new ArrayList<Image>(NB_OF_IMAGES);
for (int i = 0; i < NB_OF_IMAGES; i++) {
CirclePanel circle = new CirclePanel(WIDTH / 2, WIDTH / 2, 2 * WIDTH * (NB_OF_IMAGES / 2 - Math.abs(i - NB_OF_IMAGES / 2))
/ NB_OF_IMAGES);
circle.setSize(WIDTH, HEIGHT);
BufferedImage image = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration()
.createCompatibleImage(WIDTH, HEIGHT, BufferedImage.TRANSLUCENT);
circle.print(image.getGraphics());
images.add(image);
}
// Here is the timer logic
Timer t = new Timer(1000 / NB_OF_IMAGES_PER_SECOND, new ActionListener() {
private int i = 0;
#Override
public void actionPerformed(ActionEvent e) {
if (i == images.size()) {
i = 0;
}
label.setIcon(new ImageIcon(images.get(i++)));
}
});
frame.setVisible(true);
t.start();
}
// Simple class that draws a red circle centered on x,y and given radius
public static class CirclePanel extends JPanel {
private int x;
private int y;
private int radius;
public CirclePanel(int x, int y, int radius) {
super();
this.x = x;
this.y = y;
this.radius = radius;
setOpaque(false);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
g.drawArc(x - radius / 2, y - radius / 2, radius, radius, 0, 360);
}
}
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException,
UnsupportedLookAndFeelException {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TestAnimation().initUI();
}
});
}
}

Categories