How to make Java URI class to stop using a file - java

I have a small Java desktop application, it reads some image files and displays them. The problem is when I want to execute something (let me say, an exiftool operation) on these files, it denies because Java is still using them.
edit: This happens only on Windows, you can't write on an animated GIF file (converted to a URL object) which is being processed (being displayed) by Java, but on Ubuntu you can edit that file Metadata, system does not think the file is being processed.
Here is that part of my code.
I read the file;
ImageInputStream iis = null;
ImageReader reader = null;
iis = ImageIO.createImageInputStream(
f);
Iterator<ImageReader> imageReaders = ImageIO.getImageReaders(iis);
reader = (ImageReader) imageReaders.next();
Path pathe = f.toPath();
String mimeType = Files.probeContentType(pathe);
ImageIcon icon = null;
Here is the part I process images, resize them and make JLabel to view them.
The reason why I am not just using Java ImageIO to fill that label with an image, ImageIO can only display the first frame of an animated GIF file. Converting an image to URL this way keep the image animated (even after resized).
ImageIcon icon = null;
Integer labelWidth = this.imageLabel.getWidth();
Integer labelHeight = this.imageLabel.getHeight();
URI img;
img = f.toURI();
URL umg = img.toURL();
icon = new ImageIcon(umg);
//some calculations for setting labelWidth and labelHeight here
icon.setImage(icon.getImage().getScaledInstance(labelWidth, labelHeight,
Image.SCALE_DEFAULT));
At the end, streams are closed.
this.imageLabel.setIcon(icon);
iis.close();
reader.dispose();
And then I try to execute some exiftool commands via a process, it succesfully reads the image Metadata. But when updating the data, it says "Error renaming temporary file to C:/Users/path..." if the image is an animated GIF.
No problems occur if the image is a non animated image, JPEG, PNG or GIF, it can read and update the Metadata, I guess .
When I cancel the image displaying part of the code, I can write on the image Metadata without errors. If I read a JPEG file and display it, still no problems. If I read an animated GIF and display it (animated, does it keep the file connection open?) no modifications on this file can be done, not in my program nor on cmd.exe while the debugging session is not closed. After I quit debugging, exiftool process on cmd.exe starts working normally.
Closing ImageInputStream or ImageReader did not help.
Is there a way to make Java process (if the file is animated, I use URI, URL classes) release the file after read operation? Do these classes I mentioned have methods for releasing, closing, shutting down, kill process etc. I need to read the animated images and display them animated and make update operations on them.

thanks for the comments, here is the problem and the solution.
First of all I removed the unnecessary parts from the code. ImageInputStream and ImageReader were used to check image validation and detect image format (had to use different operations to GIF files) which I do not need anymore.
I still need to use File->URI->URL convertion to display animated GIFs. This is my old code.
URI img = f.toURI();
URL umg = img.toURL();
icon = new ImageIcon(umg);
This code kept connection to file open and blocked other processes editing the image file. (Only animated GIF files, on Windows system)
Here is the new code:
//these 2 lines are same
URI img = f.toURI();
URL umg = img.toURL();
InputStream is = umg.openStream();
byte[] byteChunk = new byte[4096];
int n;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while ((n = is.read(byteChunk)) > 0)
{
baos.write(byteChunk, 0, n);
}
byte[] dd= baos.toByteArray();
icon = new ImageIcon(dd);
is.close();
With this approach URI(image) is read via stream and ImageIcon is created from image file's byte array not directly from URL, and after that operation InputStream is closed, so the block on that file is released. (If "is.close()" line is not executed, file is still being processed and blocked for other write operations)

Have you tried to load the image with Toolkit.createImage method?
Give this a go:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.io.File;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Main {
private static class DrawPanel extends JPanel {
private Image i = null;
public void loadImage(final File f) {
//Most important point: load image via Toolkit.
//I think it must support GIF, JPEG and PNG.
i = getToolkit().createImage(f.getAbsolutePath());
repaint();
}
#Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
if (i != null) {
//g.drawImage(i, 0, 0, this); //Draw full scale image.
g.drawImage(i, 0, 0, getWidth(), getHeight(), this); //Draw scaled/streched image.
//Supplying 'this' in place of ImageObserver argument to the drawImage method is also very important!
}
}
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(() -> {
final DrawPanel imagePanel = new DrawPanel();
imagePanel.setPreferredSize(new Dimension(500, 350));
final JFileChooser fileChooser = new JFileChooser();
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
fileChooser.setMultiSelectionEnabled(true);
final JButton load = new JButton("Load");
load.addActionListener(e -> {
if (fileChooser.showOpenDialog(imagePanel) == JFileChooser.APPROVE_OPTION)
imagePanel.loadImage(fileChooser.getSelectedFile());
});
final JPanel contents = new JPanel(new BorderLayout());
contents.add(imagePanel, BorderLayout.CENTER);
contents.add(load, BorderLayout.PAGE_END);
final JFrame frame = new JFrame("Images");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(contents);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
There are several createImage methods to choose from.
I think they support animated GIFs, and non-animated JPGs and PNGs. I tested only for a GIF, but I have also worked with this in the past for JPGs and PNGs.
I think the animated image is completely loaded into memory (all frames). So you shouldn't have problem modifying the image after reading it.
Get the Toolkit related to the Component which will display the Image. I think also the Toolkit.getDefaultToolkit should do the job under certain circumstances. Then use the drawImage method of the Graphics object to draw the image. There are several drawImage methods to choose from. You can you use for example the one that scales the image on the fly for you (by supplying new width and height along with the draw location). Important: make sure you supply the drawImage with the component which renders it as the ImageObserver (note Component implements ImageObserver).

Related

GrayScale Images rendering to dark area when written using JAI / ImageIO

Can anyone explain why this happens. I read an image and render it into an output writer. If it is a color file (or black and white), it renders fine. However, if the source image is grayscale, all I get is a black box.
Sample files available at https://www.dropbox.com/sh/kyfsh5curobwxrw/AACfWr1NhX8lPUZpzVGWIPQia?dl=0
My pom plugin dependancy snippets follow.
<dependency>
<groupId>javax.media</groupId>
<artifactId>jai_core</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>com.sun.media</groupId>
<artifactId>jai_imageio</artifactId>
<version>1.1</version>
</dependency>
A test program. I understand that this bit of code in itself is really of no value, but in reality it is part of a larger suite of operations. This code represents my efforts to narrow down the issue to a small piece of code.
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public class GrayScaleImaging {
public static void main(String[] args)
throws IOException {
//works
// final File inputFile = new File("/home/vinayb/Downloads/page1_color.tif");
// final File outputFile = new File("/home/vinayb/Downloads/page1_color_mod.tif");
//doesn't work
final File inputFile = new File("/home/vinayb/Downloads/page1_grayscale.tif");
final File outputFile = new File("/home/vinayb/Downloads/page1_grayscale_mod.tif");
if (outputFile.exists()) {
outputFile.delete();
}
ImageReader imageReader = null;
ImageWriter imageWriter = null;
Graphics2D g = null;
try (final ImageInputStream imageInputStream = ImageIO.createImageInputStream(inputFile);
final ImageOutputStream imageOutputStream = ImageIO.createImageOutputStream(outputFile);) {
//setup reader
imageReader = ImageIO.getImageReaders(imageInputStream).next();
imageReader.setInput(imageInputStream);
//read image
final BufferedImage initialImage = imageReader.read(0);
//prepare graphics for the output
final BufferedImage finalImage = new BufferedImage(initialImage.getWidth(), initialImage.getHeight(), imageType(initialImage));
g = finalImage.createGraphics();
//do something to the image
//doSomething(g)
//draw image
g.drawImage(initialImage, 0, 0, initialImage.getWidth(), initialImage.getHeight(), null);
//setup writer based on reader
imageWriter = ImageIO.getImageWriter(imageReader);
imageWriter.setOutput(imageOutputStream);
//write
imageWriter.write(null, new IIOImage(initialImage, null, imageReader.getImageMetadata(0)), imageWriter.getDefaultWriteParam());
} finally {
//cleanup
if (imageWriter != null) {
imageWriter.dispose();
}
if (imageReader != null) {
imageReader.dispose();
}
if (g != null) {
g.dispose();
}
}
}
private static int imageType(BufferedImage bufferedImage) {
return bufferedImage.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : bufferedImage.getType();
}
}
Okay, I have now fixed my decoder, thanks for the sample file! :-)
Anyway, after some research, I have come to the conclusion that the problem is definitively the sample file, not your code nor the library you are using.
The issue with this file is that the TIFF metadata contains PhotometricInterpretation == 3/Palette and ColorMap tags. Ie. the image uses indexed color model/palette. If the image is read as it should according to (my understanding of) the spec, using the supplied color map, the image comes out all black. If instead I ignore this, and rather read it as gray scale (assuming PhotometricInterpretation == 1/BlackIsZero), it comes out as black text on white (light gray) background.
Edit:
A better explanation, is that the values in the color map are all 8 bit quantities (using the low 8 bits of each color entry) instead of using the full 16 bit as they should... If I detect this while reading and creating a palette using only the low 8 bits, the image comes out as intended (as in DropBox). This is still a bad image according to the spec, but detectable.

How to download animated gif from URL with Java? [duplicate]

This question already has answers here:
Displaying Gif animation in java
(4 answers)
Closed 8 years ago.
I have tried a lot but, all i can get is just the first image and not the animated gif.
The best solution at the moment was load an animated gif into icon, but i need the file. Here the code:
final URL url = new URL("http://www.freeallimages.com/wp-content/uploads/2014/09/animated-gif-images-2.gif");
JLabel l = new JLabel(new ImageIcon(url));
JOptionPane.showMessageDialog(null, l);
Can anyone help here?
This code just get the first image of gif (not animated):
public static void saveImage(String imageUrl, String destinationFile) throws IOException {
URL url = new URL(imageUrl);
InputStream is = url.openStream();
File file = new File(destinationFile);
file.getParentFile().mkdirs();
file.createNewFile();
OutputStream os = new FileOutputStream(destinationFile );
byte[] b = new byte[2048];
int length;
while ((length = is.read(b)) != -1) {
os.write(b, 0, length);
}
is.close();
os.close();
}
As far as the code is concerned, it is fine and the GIF should get downloaded properly.(Compare sizes of the original and downloaded GIF for confirmation). Now there are two points:
1. Windows Photo Viewer can not display animated GiF's, use Internet Explorer instead of it.
2. In java do not use Event-Dispatcher thread to display any animated GIF, it will not animate for sure. Try using another thread for displaying any frame containing animated GIF.
As is clear from your code, you have initialized a dialog from JOptionPane soon after initializing a JLabel with animated GIF, don't do this. All dialogs in JOptionPane are modal which will block your current thread execution and therefore you will not see any animation.

Save SWT Image to File with Transparency Mask

This is my code thus far
ImageLoader saver = new ImageLoader();
saver.data = new ImageData[]
{ toSave.getImageData() };
saver.save(fileName, SWT.IMAGE_PNG);
toSave is an image that was loaded using the SWTResourceManager that is transparent in the program. fileName is a string representing the file I want to save the image to (ends with .png)
The result is an image with a black background instead of a transparent background that I want. How do I make the background transparent? I think it has something to do with the transparency mask, but I could be wrong.
Thanks in advance!
SWTResourceManager seems to be causing your problem. I would not recommend to use it.
Try this code, it works for me:
Display d = Display.getDefault();
Image image = new Image(d, "/pictures/Llama.gif");
ImageLoader saver = new ImageLoader();
saver.data = new ImageData[] { image.getImageData() };
saver.save("output.png", SWT.IMAGE_PNG);
image.dispose();
Remember to dispose the Image when you don't need it anymore.

How to save a BufferedImage as a File

I am using the imgscalr Java library to resize an image .
The result of a resize() method call is a BufferedImage object. I now want to save this as a file (usually .jpg).
How can I do that? I want to go from BufferedImage -> File but perhaps this is not the correct approach?
File outputfile = new File("image.jpg");
ImageIO.write(bufferedImage, "jpg", outputfile);
The answer lies within the Java Documentation's Tutorial for Writing/Saving an Image.
The Image I/O class provides the following method for saving an image:
static boolean ImageIO.write(RenderedImage im, String formatName, File output) throws IOException
The tutorial explains that
The BufferedImage class implements the RenderedImage interface.
so it's able to be used in the method.
For example,
try {
BufferedImage bi = getMyImage(); // retrieve image
File outputfile = new File("saved.png");
ImageIO.write(bi, "png", outputfile);
} catch (IOException e) {
// handle exception
}
It's important to surround the write call with a try block because, as per the API, the method throws an IOException "if an error occurs during writing"
Also explained are the method's objective, parameters, returns, and throws, in more detail:
Writes an image using an arbitrary ImageWriter that supports the given format to a File. If there is already a File present, its contents are discarded.
Parameters:
im - a RenderedImage to be written.
formatName - a String containg the informal name of the format.
output - a File to be written to.
Returns:
false if no appropriate writer is found.
Throws:
IllegalArgumentException - if any parameter is null.
IOException - if an error occurs during writing.
However, formatName may still seem rather vague and ambiguous; the tutorial clears it up a bit:
The ImageIO.write method calls the code that implements PNG writing a “PNG writer plug-in”. The term plug-in is used since Image I/O is extensible and can support a wide range of formats.
But the following standard image format plugins : JPEG, PNG, GIF, BMP and WBMP are always be present.
For most applications it is sufficient to use one of these standard plugins. They have the advantage of being readily available.
There are, however, additional formats you can use:
The Image I/O class provides a way to plug in support for additional formats which can be used, and many such plug-ins exist. If you are interested in what file formats are available to load or save in your system, you may use the getReaderFormatNames and getWriterFormatNames methods of the ImageIO class. These methods return an array of strings listing all of the formats supported in this JRE.
String writerNames[] = ImageIO.getWriterFormatNames();
The returned array of names will include any additional plug-ins that are installed and any of these names may be used as a format name to select an image writer.
For a full and practical example, one can refer to Oracle's SaveImage.java example.
You can save a BufferedImage object using write method of the javax.imageio.ImageIO class. The signature of the method is like this:
public static boolean write(RenderedImage im, String formatName, File output) throws IOException
Here im is the RenderedImage to be written, formatName is the String containing the informal name of the format (e.g. png) and output is the file object to be written to. An example usage of the method for PNG file format is shown below:
ImageIO.write(image, "png", file);
Create and save a java.awt.image.bufferedImage to file:
import java.io.*;
import java.awt.image.*;
import javax.imageio.*;
public class Main{
public static void main(String args[]){
try{
BufferedImage img = new BufferedImage(
500, 500, BufferedImage.TYPE_INT_RGB );
File f = new File("MyFile.png");
int r = 5;
int g = 25;
int b = 255;
int col = (r << 16) | (g << 8) | b;
for(int x = 0; x < 500; x++){
for(int y = 20; y < 300; y++){
img.setRGB(x, y, col);
}
}
ImageIO.write(img, "PNG", f);
}
catch(Exception e){
e.printStackTrace();
}
}
}
Notes:
Creates a file called MyFile.png.
Image is 500 by 500 pixels.
Overwrites the existing file.
The color of the image is black with a blue stripe across the top.
Download and add imgscalr-lib-x.x.jar and imgscalr-lib-x.x-javadoc.jar to your Projects Libraries.
In your code:
import static org.imgscalr.Scalr.*;
public static BufferedImage resizeBufferedImage(BufferedImage image, Scalr.Method scalrMethod, Scalr.Mode scalrMode, int width, int height) {
BufferedImage bi = image;
bi = resize( image, scalrMethod, scalrMode, width, height);
return bi;
}
// Save image:
ImageIO.write(Scalr.resize(etotBImage, 150), "jpg", new File(myDir));
As a one liner:
ImageIO.write(Scalr.resize(ImageIO.read(...), 150));

How can I overlay images over one another in Java?

So I have been posting all over and have yet to get a solid answer:
I have created an image resizing class, with a crop method. The cropping works great. The issue that I am having is the background color that I specify in the drawImage function of Graphics is not working correctly. It defaults to black as the background regardless of what I supply (in this case Color.WHITE).
Also, the overlaying image or top most image (comes from a file) is being inverted (I think it is) or otherwise discolored. Just so you can conceptualize this a little bit better, I am taking a jpeg and overlaying it on top of a new BufferedImage, the new buffered image's background is not being set. Here is the code below that I am working with:
public void Crop(int Height, int Width, int SourceX, int SourceY) throws Exception {
//output height and width
int OutputWidth = this.OutputImage.getWidth();
int OutputHeight = this.OutputImage.getHeight();
//create output streams
ByteArrayOutputStream MyByteArrayOutputStream = new ByteArrayOutputStream();
MemoryCacheImageOutputStream MyMemoryCacheImageOutputStream = new MemoryCacheImageOutputStream(MyByteArrayOutputStream);
//Create a new BufferedImage
BufferedImage NewImage = new BufferedImage(Width, Height, BufferedImage.TYPE_INT_RGB);
Graphics MyGraphics = NewImage.createGraphics();
MyGraphics.drawImage(this.OutputImage, -SourceX, -SourceY, OutputWidth, OutputHeight, Color.WHITE, null);
// Get Writer and set compression
Iterator MyIterator = ImageIO.getImageWritersByFormatName("png");
if (MyIterator.hasNext()) {
//get image writer
ImageWriter MyImageWriter = (ImageWriter)MyIterator.next();
//get params
ImageWriteParam MyImageWriteParam = MyImageWriter.getDefaultWriteParam();
//set outputstream
MyImageWriter.setOutput(MyMemoryCacheImageOutputStream);
//create new ioimage
IIOImage MyIIOImage = new IIOImage(NewImage, null, null);
//write new image
MyImageWriter.write(null, MyIIOImage, MyImageWriteParam);
}
//convert output stream back to inputstream
ByteArrayInputStream MyByteArrayInputStream = new ByteArrayInputStream(MyByteArrayOutputStream.toByteArray());
MemoryCacheImageInputStream MyMemoryCacheImageInputStream = new MemoryCacheImageInputStream(MyByteArrayInputStream);
//resassign as a buffered image
this.OutputImage = ImageIO.read(MyMemoryCacheImageInputStream);
}
Can you isolate whether it's the Graphics methods or the ImageIO methods that are mangling your image? It looks like you could test this by short-circuiting the entire ImageIO process and simply assigning
this.OutputImage = NewImage;
For that matter, I assume there's something gained by the ImageIO operations? As the sample is written, it appears to be (ideally) a no-op.
Also, you don't dispose your Graphics2D before you begin the ImageIO process. It often doesn't make a difference, but you don't want to assume that.
On the overlay color distortion problem, make sure your graphics context is in paint mode and not xor mode. (Graphics.setPaintMode()). Otherwise the color bits are XOR'd together.

Categories

Resources