I'm making a small Java application that have to show some images in a JLabel named picLabel.
I have a JList of Photo objects (that contains an InputStream of an image, read from a database).
Here is the code of the JList ValueChanged event listener:
private void photoListValueChanged(javax.swing.event.ListSelectionEvent evt) {
if (evt.getValueIsAdjusting() == false && photoList.getSelectedIndex() != -1) {
photo = (Photo) photoList.getSelectedValue();
BufferedImage image = ImageIO.read(photo.getContent()) ;
if(image != null) {
picLabel.setIcon(new ImageIcon(image));
}
}
It works perfectly for the first time I select each element from the list. But if I choose again an element that was already selected (and the image was already shown), it simply don't show the image, leaving the JLabel as it was before.
Am I missing something?
Once you have read the image once from the input stream, the stream is at its end, and reading a second time won't read anything. The Photo class should read from the stream and store everything read as a byte array, or as a BufferedImage or ImageIcon directly.
Related
Given a list of true-color full frames in BufferedImage and a list of frame durations, how can I create an Image losslessly, that when put on a JLabel, will animate?
From what I can find, I could create an ImageWriter wrapping a ByteArrayOutputStream, write IIOImage frames to it, then Toolkit.getDefaultToolkit().createImage the stream into a ToolkitImage.
There are two problems with this attempt.
ImageWriter can only be instantiated with one of the known image encoders, and there is none for a lossless true-color animated image format (e.g. MNG),
It encodes (compresses) the image, then decompresses it again, becoming an unnecessary performance hazard.
[Edit]
Some more concise constraints and requirements. Please don't come up with anything that bends these rules.
What I don't want:
Making an animation thread and painting/updating each frame of the animation myself,
Using any kind of 3rd party library,
Borrowing any external process, for example a web browser,
Display it in some kind of video player object or 3D-accelerated scene (OpenGL/etc),
Work directly with classes from the sun.* packages
What I do want:
Frame size can be as large as monitor size. Please don't worry about performance. I'll worry about that. You'll just worry about correctness.
Frames all have the same size,
an Image subclass. I should be able to draw the image like g.drawImage(ani, 0, 0, this) and it would animate, or wrap it in an ImageIcon and display it on a JLabel/JButton/etc and it would animate,
Each frame can each have a different delay, from 10ms up to a second,
Animation can loop or can end, and this is defined once per animation (just like GIF),
I can use anything packaged with Oracle Java 8 (e.g. JavaFX),
Whatever happens, it should integrate with SWING
Optional:
Frames can have transparency. If needed, I can opaquify my images beforehand as the animation will be shown on a known background (single color) anyway.
I don't care if I have to subclass Image myself and add an animation thread in there that will cooperate with the ImageObserver, or write my own InputStreamImageSource, but I don't know how.
If I can somehow display a JavaFX scene with some HTML and CSS code that animates my images, then that's fine too. BUT as long as it's all encapsulated in a single SWING-compatible object that I can pass around.
You're right that ImageIO isn't an option, as the only animated format for which support is guaranteed is GIF.
You say you don't want to make an animation thread, but what about a JavaFX Animation object, like a Timeline?
public JComponent createAnimationComponent(List<BufferedImage> images,
List<Long> durations) {
Objects.requireNonNull(images, "Image list cannot be null");
Objects.requireNonNull(durations, "Duration list cannot be null");
if (new ArrayList<Object>(images).contains(null)) {
throw new IllegalArgumentException("Null image not permitted");
}
if (new ArrayList<Object>(durations).contains(null)) {
throw new IllegalArgumentException("Null duration not permitted");
}
int count = images.size();
if (count != durations.size()) {
throw new IllegalArgumentException(
"Lists must have the same number of elements");
}
ImageView view = new ImageView();
ObjectProperty<Image> imageProperty = view.imageProperty();
Rectangle imageSize = new Rectangle();
KeyFrame[] frames = new KeyFrame[count];
long time = 0;
for (int i = 0; i < count; i++) {
Duration duration = Duration.millis(time);
time += durations.get(i);
BufferedImage bufImg = images.get(i);
imageSize.add(bufImg.getWidth(), bufImg.getHeight());
Image image = SwingFXUtils.toFXImage(bufImg, null);
KeyValue imageValue = new KeyValue(imageProperty, image,
Interpolator.DISCRETE);
frames[i] = new KeyFrame(duration, imageValue);
}
Timeline timeline = new Timeline(frames);
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
JFXPanel panel = new JFXPanel();
panel.setScene(new Scene(new Group(view)));
panel.setPreferredSize(imageSize.getSize());
return panel;
}
(I don't know why it's necessary to set the JFXPanel's preferred size explicitly, but it is. Probably a bug.)
Note that, like all JavaFX code, it has to be run in the JavaFX Application Thread. If you're using it from a Swing application, you can do something like this:
public JComponent createAnimationComponentFromAWTThread(
final List<BufferedImage> images,
final List<Long> durations)
throws InterruptedException {
final JComponent[] componentHolder = { null };
Platform.runLater(new Runnable() {
#Override
public void run() {
synchronized (componentHolder) {
componentHolder[0] =
createAnimationComponent(images, durations);
componentHolder.notifyAll();
}
}
});
synchronized (componentHolder) {
while (componentHolder[0] == null) {
componentHolder.wait();
}
return componentHolder[0];
}
}
But that's still not quite enough. You first have to initialize JavaFX by calling Application.launch, either with an explicit method call, or implicitly by specifying your Application subclass as the main class.
something like
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(getImageForCurrentTime(), 3, 4, this);
}
The problem is that on runtime when new label is created it displays on the JPanel but containing the label created before.
The code converts text to bits offprint, like "HI" converts to
but when another text is converted like "OK" the bits label shows both "HI" and "OK"
This is the code from the MouseHandler class in mouseClicked method
//Convert button is clicked.
if(event.getSource().equals(getButton1Tab2()))
{
//convert text to image.
TextOverlay textOverlay = new TextOverlay(getTextArea1Tab2().getText());
//save image bits in ArrayList.
for(int i=0; i<textOverlay.imageBits.length;i++)
{
//add new line after printing each line of bits (bit line length = image width)
if(i!=0 && (i%Control.valves==0)){setBitsString(getBitsString().append("<br />"));}
//add bit to ArrayList
setBitsString(getBitsString().append(textOverlay.imageBits[i]));
}
//add new label to ArrayList of labels, the new label is bits offprint of the text's image.
labelsArray.add(new JLabel("<html>"+getBitsString()+"</html>"));
labelsArray.get(labelsArray.size()-1).addMouseListener(this);
//show binary equivalent on screen
panel2Tab2.add(labelsArray.get(labelsArray.size()-1));
panel2Tab2.validate();
panel2Tab2.repaint();
}
Thanks,
Whatever getBitsString() returns, you always append to it, but you never clear it. You should clear it first when you want to change its content.
It is because of this line:
if(i!=0 && (i%Control.valves==0)){setBitsString(getBitsString().append("<br />")
You append the new bit string and then you add the result of getBitsString() to your label.
I've managed to add an image into a JPanel in netbeans and display it.I wonder how to get to the next one,by pressing a button.
I've added the image using this code:
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt){
// TODO add your handling code here:
JFileChooser fileChooser = new JFileChooser();
int result = fileChooser.showOpenDialog(null);
if ( result == JFileChooser.APPROVE_OPTION ){
String Ruta = fileChooser.getSelectedFile().getAbsolutePath();
jTextField1.setText(Ruta);
Icon icon = new ImageIcon(Ruta);
jLabel2.setIcon(icon);
JOptionPane.showMessageDialog(this,"You chose to open this file: " +
fileChooser.getSelectedFile().getName());
}
}
And when i press a button called "jButton2" to get the next image,without manually selecting it again from folder.
For example:
private void jButton2ActionPerformed(java.awt.event.ActionEvent evt){
// TODO add your handling code here:
}
Thank You very much.
You have to enumerate images in the directory you are browsing in. When the user selects the file, you should keep a list of all images from that directory in order to retrieve them when user click the next button. As well you can get the file list whenever the user clicks the next button.
maybe something like this:
private File allFiles;
private int currentIndex;
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
JFileChooser fileChooser = new JFileChooser();
int result = fileChooser.showOpenDialog(null);
if ( result == JFileChooser.APPROVE_OPTION ){
currentFile = fileChooser.getSelectedFile();
String Ruta = currentFile.getAbsolutePath();
jTextField1.setText(Ruta);
allFiles = currentFile.getParent().listFiles(); // maybe you need a filter to include image files only....
currentIndex = indexOf(allFiles, currentFile);
Icon icon = new ImageIcon(Ruta);
jLabel2.setIcon(icon);
JOptionPane.showMessageDialog(this,"You chose to open this file: " + fileChooser.getSelectedFile().getName());
}
}
private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
if (currentIndex+1 < allFiles.length) {
jtextField1.setText(allFiles[++currentIndex]);
}
}
private int indexOf(File[] files, File f) {
for (int i=0; i+1 < files.length; i++) {
if (files[i].equals(f)) {
return i;
}
}
return -1;
}
Get the parent file if the current image (File.getParent()).
Use one of the File.list..() methods to get the image files.
Sort them into some order that means 'next' to the user.
Iterate that connection until you find the current File then display the next one after that.
I am assuming that you want the next image, if possible in the same directory you chose in your first code excerpt. What you could do is that once the user has chosen the image, you could use Apache's FileUtils to get the extension of files. If the file is a JPG, JPEG, PNG, etc you could load it's location in a List of strings.
This will give you a list of picture paths. You could then use the buttons to traverse the list. Once the button is pressed, you move to the next item, load the image and render it.
EDIT: This is how I would go about it step by step:
Create a global variable of type List.
Create a global variable which will act as a counter;
In your jButton1ActionPerformed method:
Get the parent directory of the file that the user has chosen;
Use Apache's FileUtil class to get the extension of the file names. If the file name is an image, such as PNG, JPG, etc, add it (the path of that file) to your list.
In you jButton2ActionPerformed, increment the counter (if the counter is not smaller than the size of your list, re-initialize it to 0, so as to avoid OutOfBoundsExceptions) and load the the file denoted by the counter using similar logic to your jButton1ActionPerformed method.
In my application, I need to display file system files in a JTable. When I click on the JTree node (which is any system folder), the contents of that folder are shown in the JTable.
In the first column of the JTable (where the name of the file or folder icon is shown), the icon is fetched from the system icon and is displayed.
Every thing is working fine. However, the problem is that when the renderer renders icon, the icon of the first file (first row of JTable) is repeated in all rows. I mean the icon does not change in the subsequent rows of the JTable. Here my code is in which a render gets icon and the model displays it in the JTable
class KeyIconCellRenderer extends DefaultTableCellRenderer {
public KeyIconCellRenderer(String ext) {
File file = new File(ext);
Icon icon = FileSystemView.getFileSystemView().getSystemIcon(file);
setIcon(icon);
}
}
and here is code where I am using render to display
private class Selection implements TreeSelectionListener {
public void valueChanged(TreeSelectionEvent e) {
Object[] myData= new Object[6];
TreePath path = e.getPath();
FileUtil util= new FileUtil();
FileMetaData metaData;
Vector<FileMetaData> vList = new Vector<FileMetaData>();
DefaultMutableTreeNode node = (DefaultMutableTreeNode)treeMainView.getLastSelectedPathComponent();
FileInfo info =(FileInfo)node.getUserObject();
File filePath= info.getFilepath();
vList=util.getChildList(filePath);
dtModel.getDataVector().removeAllElements();
for(int i=0;i<vList.size(); i++){
Vector v= new Vector();
metaData=(FileMetaData)vList.get(i);
v.add(metaData.getName());
tblMainView.getColumnModel().getColumn(0).setCellRenderer(new KeyIconCellRenderer(metaData.getClientpath()));
v.add(metaData.getClientpath());
if(metaData.isDirectory()){
v.add("");
}else
{
v.add((FileHelper.getSizeString(metaData.getSize())));
}
if(metaData.isDirectory()){
v.add("");
}else
{
v.add(new Date(metaData.getTime()));
}
if(metaData.isDirectory()){
v.add("Folder");
}else
{
v.add("File");
}
v.add("Pending Upload");
dtModel.insertRow(0, v);
}
tblMainView.repaint();
}
}
as in the attached image, only the icon of the fist file is repeated in all rows,
Please help, it will be a huge favor,
Thanks
[Your surface mistake is to reset the table column's renderer in the loop, each time hard-coding the current icon in the the renderer's constructor. Consequently, the file-icon is used for all.
The basic problem is that you don't seem to fully understand the concept of a renderer: it's there to display the cell data as it is delivered as a parameter in its getXXCellRendererComponent. So that's the place to look-up the icon to use. The way to go is to store the File object in the table cell and query the appropriate icon every time the method is called.
Cheers
Jeanette
How to save drawing in JComponent into tiff format? I only know how to save the whole Java file but I dont know how to save specific Jcomponent. Help me :-(
EDITED:
Thanks guys, now I am able to save my drawing to Jpeg.
However I just wanted to save one of the component? The c.paintAll(bufferedImage.getGraphics()); seem to save the whole component. However, I just want to save this component c.add(new PaintSurface(), BorderLayout.CENTER); without panel.add(saveBtn); How can I do that? Thanks.
Container c = getContentPane();
c.setLayout(new BorderLayout());
Panel panel = new Panel();
panel.add(saveBtn);
c.add("South", panel);
c.setBackground(Color.WHITE);
c.add(new PaintSurface(), BorderLayout.CENTER);
This is essentially identical to broschb's solution only using correct syntax and actually calling the appropriate JAI routines.
public void saveComponentAsTiff(Component c, String filename, boolean subcomp) throws IOException {
saveComponentTiff(c, "tiff", filename, subcomp);
}
public void saveComponent(Component c, String format, String filename, boolean subcomp) throws IOException {
// Create a renderable image with the same width and height as the component
BufferedImage image = new BufferedImage(c.getWidth(), c.getHeight(), BufferedImage.TYPE_INT_ARGB);
if(subcomp) {
// Render the component and all its sub components
c.paintAll(image.getGraphics());
}
else {
// Render the component and ignoring its sub components
c.paint(image.getGraphics());
}
// Save the image out to file
ImageIO.write(image, format, new File(filename));
}
Documentation for the various functions can be found here:
Component.paint(Graphics)
Component.paintAll(Graphics)
BufferedImage(int, int, int)
BufferedImage.getGraphics()
ImageIO.write(RenderedImage, String, File)
If you want to save in a format other than tiff you can use ImageIO.getWriterFormatNames() to obtain a list of all image output formats currently loaded by the JRE.
UPDATE: If you are not interested in painting sub components you can substitute the call to Component.paintAll(...) with Component.paint(...). I have altered the example code to reflect this. Setting subcomp to true with render the subcompnents and setting it to false will ignore them.
The ScreenImage class allows you to save an image of any component.
you can get a Buffered Image of the component, or the panel that contains the drawing by creating a buffered image the size of the panel. You can then paint the panels contents onto the buffered image. You can then use JAI(Java Advanced Imaging) library to save the buffered image as a tiff. You'll have to check the docs on that here.
JComponent component; //this is your already created component
BufferedImage image = new BufferedImage(component.getWidth(),
component.getHeight(),
Bufferedimage.TYPERGB)
Graphics g = image.getGraphics;
component.paintComponent(g);
The syntax may be slightly off, i'm not at an idea, but that is the general idea. You can then use JAI and convert the buffered image to a TIFF.