I have a large PNG image (600x600) and my application makes the image opaque and writes out the file. The problem is that the performance with ImageIO is terrible. Are there any other alternatives? I require the image to be opaque. Below is what I am doing:
BufferedImage buf = ImageIO.read(localUrl);
float[] scales = {1f, 1f, 1f, 1f}; // R, G, B, A
float[] offsets = {0f, 0f, 0f, 1f}; // R, G, B, A
RescaleOp rescaler = new RescaleOp(scales, offsets, null);
BufferedImage opaque = rescaler.filter(buf, null);
File outputfile = new File(localUrl.getPath());
ImageIO.write(opaque, "png", outputfile);
Using a RescaleOp isn't entirely necessary here if you just want to get rid of transparency. A simpler solution would be drawing the image on a background like so:
Color bgColor = Color.WHITE;
BufferedImage foreground = ImageIO.read(localUrl);
int width = foreground.getWidth();
int height = foreground.getHeight();
BufferedImage background = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = background.createGraphics();
g.setColor(bgColor);
g.fillRect(0, 0, width, height);
g.drawImage(foreground, 0, 0, null);
g.dispose();
File outputfile = new File(localUrl.getPath());
ImageIO.write(background, "png", outputfile);
This seems like a simpler method of doing things and would probably require less processing power, but I doubt there would be a huge difference. If you're not satisfied with the speed the image can be read / written from the hard drive, there's little you can do to speed that up.
With PNGJ:
private static void removeAlpha(File file1,File file2) {
PngReaderByte reader = new PngReaderByte(file1);
ImageInfo info = reader.imgInfo;
PngWriter writer = new PngWriter(file2,info);
writer.setFilterPreserve(true);
writer.setCompLevel(6);
writer.copyChunksFrom(reader.getChunksList(), ChunkCopyBehaviour.COPY_ALL_SAFE);
if( info.bitDepth != 8 ||info.channels!=4) throw new RuntimeException("expected 8bits RGBA ");
while(reader.hasMoreRows()) {
ImageLineByte line = reader.readRowByte();
byte [] buf = line.getScanlineByte();
for(int i=0,j=3;i<info.cols;i++,j+=4)
buf[j]=(byte)255;
writer.writeRow(line);
}
reader.end();
writer.end();
}
I'm not sure if this would enhance the perfomance. Bear in mind, also that (contrarily to Parker Hoyes' answer) this simply kills the alpha channel, but it does not blend with some background color (hence the "original" color will appear in the previously-transparent now-opaque regions).
Related
I have a PNG image and I want to resize it. How can I do that? Though I have gone through this I can't understand the snippet.
If you have an java.awt.Image, resizing it doesn't require any additional libraries. Just do:
Image newImage = yourImage.getScaledInstance(newWidth, newHeight, Image.SCALE_DEFAULT);
Obviously, replace newWidth and newHeight with the dimensions of the specified image.
Notice the last parameter: it tells the runtime the algorithm you want to use for resizing.
There are algorithms that produce a very precise result, however these take a large time to complete.
You can use any of the following algorithms:
Image.SCALE_DEFAULT: Use the default image-scaling algorithm.
Image.SCALE_FAST: Choose an image-scaling algorithm that gives higher priority to scaling speed than smoothness of the scaled image.
Image.SCALE_SMOOTH: Choose an image-scaling algorithm that gives higher priority to image smoothness than scaling speed.
Image.SCALE_AREA_AVERAGING: Use the Area Averaging image scaling algorithm.
Image.SCALE_REPLICATE: Use the image scaling algorithm embodied in the ReplicateScaleFilter class.
See the Javadoc for more info.
We're doing this to create thumbnails of images:
BufferedImage tThumbImage = new BufferedImage( tThumbWidth, tThumbHeight, BufferedImage.TYPE_INT_RGB );
Graphics2D tGraphics2D = tThumbImage.createGraphics(); //create a graphics object to paint to
tGraphics2D.setBackground( Color.WHITE );
tGraphics2D.setPaint( Color.WHITE );
tGraphics2D.fillRect( 0, 0, tThumbWidth, tThumbHeight );
tGraphics2D.setRenderingHint( RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR );
tGraphics2D.drawImage( tOriginalImage, 0, 0, tThumbWidth, tThumbHeight, null ); //draw the image scaled
ImageIO.write( tThumbImage, "JPG", tThumbnailTarget ); //write the image to a file
Try this:
ImageIcon icon = new ImageIcon(UrlToPngFile);
Image scaleImage = icon.getImage().getScaledInstance(28, 28,Image.SCALE_DEFAULT);
Resize image with high quality:
private static InputStream resizeImage(InputStream uploadedInputStream, String fileName, int width, int height) {
try {
BufferedImage image = ImageIO.read(uploadedInputStream);
Image originalImage= image.getScaledInstance(width, height, Image.SCALE_DEFAULT);
int type = ((image.getType() == 0) ? BufferedImage.TYPE_INT_ARGB : image.getType());
BufferedImage resizedImage = new BufferedImage(width, height, type);
Graphics2D g2d = resizedImage.createGraphics();
g2d.drawImage(originalImage, 0, 0, width, height, null);
g2d.dispose();
g2d.setComposite(AlphaComposite.Src);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ImageIO.write(resizedImage, fileName.split("\\.")[1], byteArrayOutputStream);
return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
} catch (IOException e) {
// Something is going wrong while resizing image
return uploadedInputStream;
}
}
int newHeight = 150;
int newWidth = 150;
holder.iv_arrow.requestLayout();
holder.iv_arrow.getLayoutParams().height = newHeight;
holder.iv_arrow.getLayoutParams().width = newWidth;
holder.iv_arrow.setScaleType(ImageView.ScaleType.FIT_XY);
holder.iv_arrow.setImageResource(R.drawable.video_menu);
Simple way in Java
public void resize(String inputImagePath,
String outputImagePath, int scaledWidth, int scaledHeight)
throws IOException {
// reads input image
File inputFile = new File(inputImagePath);
BufferedImage inputImage = ImageIO.read(inputFile);
// creates output image
BufferedImage outputImage = new BufferedImage(scaledWidth,
scaledHeight, inputImage.getType());
// scales the input image to the output image
Graphics2D g2d = outputImage.createGraphics();
g2d.drawImage(inputImage, 0, 0, scaledWidth, scaledHeight, null);
g2d.dispose();
// extracts extension of output file
String formatName = outputImagePath.substring(outputImagePath
.lastIndexOf(".") + 1);
// writes to output file
ImageIO.write(outputImage, formatName, new File(outputImagePath));
}
Design jLabel first:
JLabel label1 = new JLabel("");
label1.setHorizontalAlignment(SwingConstants.CENTER);
label1.setBounds(628, 28, 169, 125);
frame1.getContentPane().add(label1); //frame1 = "Jframe name"
Then you can code below code:
ImageIcon imageIcon1 = new ImageIcon(new ImageIcon("add location url").getImage().getScaledInstance(100, 100, Image.SCALE_DEFAULT)); //100, 100 add your own size
label1.setIcon(imageIcon1);
Is it possible to implement the first example with Scalr?
My code is the following:
BufferedImage thumbnail = Scalr.resize(ImageIO.read(sourceFile), Scalr.Method.ULTRA_QUALITY, Scalr.Mode.FIT_TO_WIDTH,
width, height, Scalr.OP_ANTIALIAS);
ImageIO.write(thumbnail, destinationfile.getExtension(), destinationfile);
What I want is to receive the image like this:
where the blue bars are the space I want to fill with the color.
Thank you
Update: maybe it is possible to implement with Thumbnailator?
Just done! Perhaps it can help you!
public static BufferedImage resizeAndCrop(BufferedImage bufferedImage) throws IOException {
int himg = bufferedImage.getHeight();
int wimg = bufferedImage.getWidth();
double rateh = himg/dim;
double ratew = wimg/dim;
double rate = ratew;
if(rateh>ratew)
rate = rateh;
int dimhimg = (int) (himg/rate);
int dimwimg = (int) (wimg/rate);
double startw = dim/2 - dimwimg/2;
double starth = dim/2 - dimhimg/2;
BufferedImage tThumbImage = new BufferedImage( dim, dim, BufferedImage.TYPE_INT_RGB );
Graphics2D tGraphics2D = tThumbImage.createGraphics(); //create a graphics object to paint to
tGraphics2D.setBackground( Color.WHITE );
tGraphics2D.setPaint( Color.WHITE );
tGraphics2D.fillRect( 0, 0, dim, dim );
tGraphics2D.setRenderingHint( RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
tGraphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
tGraphics2D.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
tGraphics2D.drawImage( bufferedImage, (int)startw, (int)starth, dimwimg, dimhimg, null ); //draw the image scaled
File ff = new File(path + "jdata/tmp/prova.jpg");
ImageIO.write( tThumbImage, "JPG", ff); //write the image to a file
BufferedImage croppedContainMethod = ImageIO.read(ff);
return croppedContainMethod;
}
Nobody has idea so I will publish my solution...
I decided to continue to use Scalr (I didn't checked the Thumbnailator's last version but the previous ones failed on big pictures).
So first of all I call resize method, and then, if sizes of the new thumbnail are bigger then given ones I call crop method that crops a thumbnail by the center.. The code is the following:
BufferedImage thumbnail = Scalr.resize(sourceFile, Scalr.Method.ULTRA_QUALITY, Scalr.Mode.AUTOMATIC, destinationSize.width, destinationSize.height);
if (thumbnail.getWidth() > destinationSize.width)
thumbnail = Scalr.crop(thumbnail, (thumbnail.getWidth() - destinationSize.width) / 2, 0, destinationSize.width, destinationSize.height);
else if (thumbnail.getHeight() > destinationSize.height)
thumbnail = Scalr.crop(thumbnail, 0, (thumbnail.getHeight() - destinationSize.height) / 2, destinationSize.width, destinationSize.height);
It is not ideal, but at least it handles 'wide' images after generation of thumbnails
I'm trying to rotate an image using this code:
File imgPath = new File("c:\\tmp\\7.jpg");
BufferedImage src = ImageIO.read(imgPath);
AffineTransform tx = new AffineTransform();
int width = src.getWidth();
int height = src.getHeight();
tx.rotate(radiant ,width, height);
AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BICUBIC);
BufferedImage out = op.filter(src, null);
File outFile = new File("c:\\tmp\\out.jpg");
ImageIO.write(out, "jpg", outFile);
For some reason the background after the rotation is black.
How can make the background white or transparent?
When you are using AffineTransformOp.filter(src, null) for creating new images, the new image uses the same ColorModel as the source image.
Your input image is a jpeg, which means it is not transparent, so the destination image is an RGB image, without the alpha (transparency) level.
When rotated with such a small angle, the image no longer occupies exactly the same bounds, so the background is visible in its edges and because there is no alpha level, it is normal that the background is black.
However, if you save it to a format that supports transparency like gif or png, your image will not display the black background anymore.
ImageIO.write(out, "gif", outFile);
The full code:
try {
File imgPath = new File("d:\\downloads\\about.jpg");
BufferedImage src = ImageIO.read(imgPath);
AffineTransform tx = new AffineTransform();
int width = src.getWidth();
int height = src.getHeight();
tx.rotate(0.02050493823247637, width, height);
AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BICUBIC);
BufferedImage out = op.filter(src, null);
File outFile = new File("d:\\downloads\\about0.gif");
ImageIO.write(out, "gif", outFile);
} catch (Exception e) {
e.printStackTrace();
}
Take a look at this for even more information and tricks.
Here is my image after rotation to gif:
I was looking at the ImageConverter class, trying to figure out how to convert a BufferedImage to 8-bit color, but I have no idea how I would do this. I was also searching around the internet and I could find no simple answer, they were all talking about 8 bit grayscale images. I simply want to convert the colors of an image to 8 bit... nothing else, no resizing no nothing. Does anyone mind telling me how to do this.
You can use JAI (Java Advanced Imaging), the official Sun (now Oracle) image library to do that.
The ColorQuantizerDescriptor shows the choice of quantization processes you can apply.
This code snippet from the article "Transparent gifs in Java" at G-Man's Uber Software Engineering Blog works well:
public static void main(String[] args) throws Exception {
BufferedImage src = convertRGBAToIndexed(ImageIO.read(new File("/src.jpg")));
ImageIO.write(src, "gif", new File("/dest.gif"));
}
public static BufferedImage convertRGBAToIndexed(BufferedImage src) {
BufferedImage dest = new BufferedImage(src.getWidth(), src.getHeight(), BufferedImage.TYPE_BYTE_INDEXED);
Graphics g = dest.getGraphics();
g.setColor(new Color(231, 20, 189));
// fill with a hideous color and make it transparent
g.fillRect(0, 0, dest.getWidth(), dest.getHeight());
dest = makeTransparent(dest, 0, 0);
dest.createGraphics().drawImage(src, 0, 0, null);
return dest;
}
public static BufferedImage makeTransparent(BufferedImage image, int x, int y) {
ColorModel cm = image.getColorModel();
if (!(cm instanceof IndexColorModel))
return image; // sorry...
IndexColorModel icm = (IndexColorModel) cm;
WritableRaster raster = image.getRaster();
int pixel = raster.getSample(x, y, 0); // pixel is offset in ICM's palette
int size = icm.getMapSize();
byte[] reds = new byte[size];
byte[] greens = new byte[size];
byte[] blues = new byte[size];
icm.getReds(reds);
icm.getGreens(greens);
icm.getBlues(blues);
IndexColorModel icm2 = new IndexColorModel(8, size, reds, greens, blues, pixel);
return new BufferedImage(icm2, raster, image.isAlphaPremultiplied(), null);
}
You can use the convert8 method in the ConvertUtil class.
For details have a look here.
I am trying to write out a png file from a java.awt.image.BufferedImage. Everything works fine but the resulting png is a 32-bit file.
Is there a way to make the png file be 8-bit? The image is grayscale, but I do need transparency as this is an overlay image. I am using java 6, and I would prefer to return an OutputStream so that I can have the calling class deal with writing out the file to disk/db.
Here is the relevant portion of the code:
public static ByteArrayOutputStream createImage(InputStream originalStream)
throws IOException {
ByteArrayOutputStream oStream = null;
java.awt.Image newImg = javax.imageio.ImageIO.read(originalStream);
int imgWidth = newImg.getWidth(null);
int imgHeight = newImg.getHeight(null);
java.awt.image.BufferedImage bim = new java.awt.image.BufferedImage(imgWidth,
imgHeight, java.awt.image.BufferedImage.TYPE_INT_ARGB);
Color bckgrndColor = new Color(0x80, 0x80, 0x80);
Graphics2D gf = (Graphics2D)bim.getGraphics();
// set transparency for fill image
gf.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f));
gf.setColor(bckgrndColor);
gf.fillRect(0, 0, imgWidth, imgHeight);
oStream = new ByteArrayOutputStream();
javax.imageio.ImageIO.write(bim, "png", oStream);
oStream.close();
return oStream;
}
The build in imageio png writer will write 32bit png files on all the platforms I have used it on, no matter what the source image is. You should also be aware that many people have complained that the resulting compression is much lower than what is possible with the png format. There are several independent png libraries available that allow you to specify the exact format, but I don't actually have any experience with any of them.
I found the answer as to how to convert RGBA to Indexed here: http://www.eichberger.de/2007/07/transparent-gifs-in-java.html
However, the resulting 8-bit png file only has 100% or 0% transparency. You could probably tweak the IndexColorModel arrays, but we have decided to make the generated file (what was an overlay mask) into an underlay jpg and use what was the static base as the transparent overlay.
It is an interesting question... It is late, I will experiment tomorrow. I will first try and use a BufferedImage.TYPE_BYTE_INDEXED (perhaps after drawing) to see if Java is smart enough to generate an 8bit PNG.
Or perhaps some image library can allow that.
[EDIT] Some years later... Actually, I made the code at the time, but forgot to update this thread... I used the code pointed at by Kat, with a little refinement on the handling of transparency, and saving in PNG format instead of Gif format. It works in making a 8-bit PNG file with all-or-nothing transparency.
You can find a working test file at http://bazaar.launchpad.net/~philho/+junk/Java/view/head:/Tests/src/org/philhosoft/tests/image/AddTransparency.java
using my ImageUtil class.
Since the code isn't that big, for posterity sake, I post it here, without the JavaDoc to save some lines.
public class ImageUtil
{
public static int ALPHA_BIT_MASK = 0xFF000000;
public static BufferedImage imageToBufferedImage(Image image, int width, int height)
{
return imageToBufferedImage(image, width, height, BufferedImage.TYPE_INT_ARGB);
}
public static BufferedImage imageToBufferedImage(Image image, int width, int height, int type)
{
BufferedImage dest = new BufferedImage(width, height, type);
Graphics2D g2 = dest.createGraphics();
g2.drawImage(image, 0, 0, null);
g2.dispose();
return dest;
}
public static BufferedImage convertRGBAToIndexed(BufferedImage srcImage)
{
// Create a non-transparent palletized image
Image flattenedImage = transformTransparencyToMagenta(srcImage);
BufferedImage flatImage = imageToBufferedImage(flattenedImage,
srcImage.getWidth(), srcImage.getHeight(), BufferedImage.TYPE_BYTE_INDEXED);
BufferedImage destImage = makeColorTransparent(flatImage, 0, 0);
return destImage;
}
private static Image transformTransparencyToMagenta(BufferedImage image)
{
ImageFilter filter = new RGBImageFilter()
{
#Override
public final int filterRGB(int x, int y, int rgb)
{
int pixelValue = 0;
int opacity = (rgb & ALPHA_BIT_MASK) >>> 24;
if (opacity < 128)
{
// Quite transparent: replace color with transparent magenta
// (traditional color for binary transparency)
pixelValue = 0x00FF00FF;
}
else
{
// Quite opaque: get pure color
pixelValue = (rgb & 0xFFFFFF) | ALPHA_BIT_MASK;
}
return pixelValue;
}
};
ImageProducer ip = new FilteredImageSource(image.getSource(), filter);
return Toolkit.getDefaultToolkit().createImage(ip);
}
public static BufferedImage makeColorTransparent(BufferedImage image, int x, int y)
{
ColorModel cm = image.getColorModel();
if (!(cm instanceof IndexColorModel))
return image; // No transparency added as we don't have an indexed image
IndexColorModel originalICM = (IndexColorModel) cm;
WritableRaster raster = image.getRaster();
int colorIndex = raster.getSample(x, y, 0); // colorIndex is an offset in the palette of the ICM'
// Number of indexed colors
int size = originalICM.getMapSize();
byte[] reds = new byte[size];
byte[] greens = new byte[size];
byte[] blues = new byte[size];
originalICM.getReds(reds);
originalICM.getGreens(greens);
originalICM.getBlues(blues);
IndexColorModel newICM = new IndexColorModel(8, size, reds, greens, blues, colorIndex);
return new BufferedImage(newICM, raster, image.isAlphaPremultiplied(), null);
}
}
Thanks for responding, I was going to try an TYPE_BYTE_INDEXED with an IndexColorModel and may still but if ImageIO writes out 32-bit regardless it appears that I may be wasting my time there.
The image I am trying to write out can be very large (up to 8000x4000) but is just a simple mask for the image underneath, so will only have a ~30% transparent gray and a 100% transparent cutout. I would use a GIF but IE6 seems to have trouble with displaying one that large.
It only gets generated once and in an internal set-up type screen, so performance isn't an issue either, but it does have to be done within the java code and not by an offline utility.
The libraries that you specified might be used to transform it while writing... I am going to go check that out.
If anyone has a better way, please let me know!!
Thanks!