Convert GIF images to PNG format, 32 bits depth - java

Here is a solution to convert GIF images to PNG format. The problem is that the generated PNG images are in 8 bits depth (in my case at least).
Is there a way to force them to be in 32 bits depth? The best would be using the javax.imageio.ImageIO library.

Just convert the BufferedImage, redrawing a copy with the desidered colour model. Not extremely efficient, but practical:
File input = new File("/tmp/input.gif");
File output = new File("/tmp/output.png");
BufferedImage im1 = ImageIO.read( input );
BufferedImage im2 = new BufferedImage(im1.getWidth(), im1.getHeight(),
BufferedImage.TYPE_4BYTE_ABGR);
im2.getGraphics().drawImage(im1, 0, 0, null);
ImageIO.write(im2 , "png", output);

Related

Grayscale image becomes monochrome when using BufferedImage

i need to write a program which takes an image, resizes and rotates it and then saves it out. The first 2 point are done, but now I have a problem. Every time I convert a grayscale image it becomes a monochrome image.
I load the target image with the following command:
BufferedImage sourceimg = ImageIO.read(input);
And after I scaled and rotated it I save it out with the following command:
BufferedImage newimg = new BufferedImage(sourceimg.getHeight(), sourceimg.getWidth(), sourceimg.getType());
op.filter(sourceimg, newimg);
sourceimg = newimg;
ImageIO.write(sourceimg, "png", outputFile);
This works fine for every image except grayscale images. I have already tried a workaround by setting the type of every image to ARGB but there has to be another way. Is there a way to get the IndexColorModel of an given image?
The problem ist now solved, i just had to change:
BufferedImage newimg = new BufferedImage(sourceimg.getHeight(), sourceimg.getWidth(), sourceimg.getType());
to:
BufferedImage newimg = new BufferedImage(sourceimg.getWidth(), sourceimg.getHeight(), BufferedImage.TYPE_INT_ARGB);
There are other solutions, especially if you like to keep the original image type (ie. keep palette of IndexColorModel images etc.).
In fact, the easiest is to just do (assuming op is a standard BufferedImageOp):
BufferedImage newimg = op.filter(sourceimg, null);
Here a new, compatible image will be created for you, and will be of correct size to keep the result of the operation.
Another option, which will preserve the image type and color model, is slightly more verbose:
ColorModel sourceCM = sourceimg.getColorModel(); // Will be the IndexColorModel in your case
// I'm assuming you are deliberately switching width/height to rotate 90 deg
WritableRaster raster = sourceCM.createCompatibleWritableRaster(sourceimg.getHeight(), sourceimg.getWidth());
BufferedImage newimg = new BufferedImage(sourceCM, raster, sourceCM.isAlphaPremultiplied(), null);
op.filter(sourceimg, newimg);

BufferedImage red mask

I have to take screenshot of some area on the desktop.
I am doing it this way:
BufferedImage image = new Robot().createScreenCapture(area.areaRect);
ImageIO.write(image, "jpg", new File(current));
//then I paint in on JPanel
and every screenshot saved to .jpg looks like this one:
It doesn't happen for pngs and gifs.
I googled about this problem and found a solution, creating image by the Toolkit class:
Image toolkitImage = Toolkit.getDefaultToolkit().createImage(imageUrl);
but I have no idea how can I take screenshot with it.
The JPEG writer in ImageIO doesn't take the ColorModel into account when writing the file, as JPEG does not have an alpha channel. The Alpha channel is the A part of ARGB that specifies the transparency value for each pixel. Most image clients will assume that JPEG don't have alpha values as that is the standard behavior.
The writer in ImageIO will write a file with the colors shifted, like the lovely shade of salmon as background in the picture. This is because the writer will write down the values wrong when it really should be writing them in RBG.
The workaround is to paint the image to a BufferedImage with RBG as ColorModel. Here is some sample code:
// argbBuffer is the ARGB Image that should be written as JPEG:
WritableRaster raster = argbBuffer.getRaster();
WritableRaster newRaster = raster.createWritableChild(0, 0, width, height, 0, 0, new int[] {0, 1, 2});
// create a ColorModel that represents the one of the ARGB except the alpha channel:
DirectColorModel cm = (DirectColorModel)argbBuffer.getColorModel();
DirectColorModel newCM = new DirectColorModel(cm.getPixelSize(),
cm.getRedMask(), cm.getGreenMask(), cm.getBlueMask());
// now create the new buffer that is used ot write the image:
BufferedImage rgbBuffer = new BufferedImage(newCM, newRaster, false, null);

Loading only part of a file into an Image

[Java] I'm using Images / BufferedImages (I don't currently know a better way) to load external pictures unto the canvas.
Q: Is there a possibility of loading / displaying only a part of the picture? For example, using only a 60x60 area from a larger 500x300 picture file. (I believe this is called a Sprite) Loading the file using ImageIO.
Thanks in advance.
BufferedImage input = ImageIO.read(new File(inputFile));
BufferedImage output = new BufferedImage(60, 60, BufferedImage.TYPE_INT_ARGB)
Graphics g = output .getGraphics();
g.drawImage(input, 0, 0, null); //adjust 0, 0 to some x, y if necessary
ImageIO.write(output, "png", new File(outputFile));

How to save jpeg Image file size after read--rotate-write operations in Java?

Im trying to read a JPEG image as BufferedImage, rotate and save it as another jpeg image from file system. But there is a problem : after these operations I cannot proceed same file size.
Here the code
//read Image
BufferedImage img = ImageIO.read(new File(path));
//rotate Image
BufferedImage rotatedImage = new BufferedImage(image.getHeight(),
image.getWidth(), BufferedImage.TYPE_3BYTE_BGR);
Graphics2D g2d = (Graphics2D) rotatedImage.getGraphics();
g2d.rotate(Math.toRadians(PhotoConstants.ROTATE_LEFT));
int height=-rotatedImage.getHeight(null);
g2d.drawImage(image, height, 0, null);
g2d.dispose();
//Write Image
Iterator iter = ImageIO.getImageWritersByFormatName("jpeg");
ImageWriter writer = (ImageWriter)iter.next();
// instantiate an ImageWriteParam object with default compression options
ImageWriteParam iwp = writer.getDefaultWriteParam();
try {
FileImageOutputStream output = null;
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
iwp.setCompressionQuality(0.98f); // an integer between 0 and 1
// 1 specifies minimum compression and maximum quality
File file = new File(path);
output = new FileImageOutputStream(file);
writer.setOutput(output);
IIOImage iioImage = new IIOImage(image, null, null);
writer.write(null, iioImage, iwp);
output.flush();
output.close();
writer.dispose();
Is it possible to access compressionQuality parameter of original jpeg image in the beginning. when I set 1 to compression quality, the image gets bigger size. Otherwise I set 0.9 or less the image gets smaller size. How can i proceed the image size after these operations?
Thank you,
What do you mean - do you want to read the compression rate of the original file?
Please note that even with the same compression settings, the filesize might vary, due to the JPEG compression algorithm. So a rotated image does not always have the exact same size as the unrotated/original version, even if all options (like compression rate, quality settings etc.) are the same.
If there is really now way to read the compression quality from the metadata, as a last resort, what you could do is use a binary search for the quality. Start with 0.5, if the file is too small try 0.75 and so on (up to 5 tries or so). This is a bit slow of course, but depending on your use case it might be OK.

Write to 16 bit BufferedImage TYPE_USHORT_GRAY

I'm trying to write 16 bit grayscale imagedata to a png using BufferedImage.TYPE_USHORT_GRAY. Normally I write to an image like so:
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
then:
image.setRGB(x,y,Color.getRGB);
to set the pixels, and finally:
ImageIO.write(image, "png", new File(path + ".png"));
to write to a png image.
But now I have this as image:
BufferedImage imageGray = new BufferedImage(width, height, BufferedImage.TYPE_USHORT_GRAY);
How do I go about saving pixels to that format? Using setRGB() with a 16 bit integer doesn't seem to work, when I open the saved png file I see a lot of banding happening.
I tried saving a simple gradient from 0 to 65535 and then using the setRGB() on the grayscale image, and checked the results in Photoshop. I can see the image consists of smaller gradients every 256 rows. I'm guessing either setRGB() or imageIO doesn't work as I would like it to.
Are there workarounds for this? Does imageIO even support the BufferedImage.TYPE_USHORT_GRAY format? Or can it only save 8 bit data? And if it can save 16bit data, how would I go about saving pixel data, preferably in a way like setRGB() works (so for a certain x,y coordinate)?
As pst already commented below my question:
Try using the Raster directly?
Accessing the Raster directly solved the problem :
BufferedImage bi = BufferedImage(width, height, BufferedImage.TYPE_USHORT_GRAY)
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
Short s = shortData[x][y]
bi.getRaster().setDataElements(x, y, Short[] {s})
}
}
From BufferedImage you can read
public static final int TYPE_USHORT_GRAY
Represents an unsigned short grayscale image, non-indexed). This image has a ComponentColorModel with a CS_GRAY ColorSpace.
So try instantiating your own ColorSpace with the CS_GRAY type (ColorSpace.getInstance(ColorSpace.CS_GRAY) should do it I suppose). This object has a method called fromRGB which you should be able to use...
You probably need to widen the signed 16bit shorts to ints and remove the sign:
int ushort = (int)(shortData[x][y]) & 0xFFFF;
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_USHORT_GRAY);
short[] dataArray = ((DataBufferUShort)image.getRaster().getDataBuffer()).getData();
dataArray[y*width+x] = color;
ImageIO.write(image, "png", new File(path + ".png"));

Categories