I've tried multiple image resizing libraries posted here on SO, as well as with raw java using getScaledInstance
. While everything works most of the time, there are two jpeg images for which the colors are always messed up whenever I resize them.
First image:
Result:
Second image:
Result:
I've tried these images with multiple libraries including Scalr, Thumbnailator, and raw java using image.getScaledInstance()
(see here for code), but the result is the same.
Any ideas what the problem is?
I've found a solution, with a lot of help from this answer:
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
public class ImgUtility
{
/**
* Takes a file, and resizes it to the given width and height, while keeping
* original proportions. Note: It resizes a new file rather than resizing
* the original one. Resulting file is always written as a png file due to issues
* with resizing jpeg files which results in color loss. See:
* https://stackoverflow.com/a/19654452/49153
* for details, including the comments.
*
*/
public static File resize(File file, int width, int height) throws Exception
{
Image img = Toolkit.getDefaultToolkit().getImage( file.getAbsolutePath() );
loadCompletely(img);
BufferedImage bm = toBufferedImage(img);
bm = resize(bm, width, height);
StringBuilder sb = new StringBuilder();
sb.append( bm.hashCode() ).append(".png");
String filename = sb.toString();
File result = new File( filename );
ImageIO.write(bm, "png", result);
return result;
}
public static BufferedImage toBufferedImage(Image img)
{
if (img instanceof BufferedImage)
{
return (BufferedImage) img;
}
BufferedImage bimage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB);
bimage.getGraphics().drawImage(img, 0, 0 , null);
bimage.getGraphics().dispose();
return bimage;
}
public static BufferedImage resize(BufferedImage image, int areaWidth, int areaHeight)
{
float scaleX = (float) areaWidth / image.getWidth();
float scaleY = (float) areaHeight / image.getHeight();
float scale = Math.min(scaleX, scaleY);
int w = Math.round(image.getWidth() * scale);
int h = Math.round(image.getHeight() * scale);
int type = image.getTransparency() == Transparency.OPAQUE ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
boolean scaleDown = scale < 1;
if (scaleDown) {
// multi-pass bilinear div 2
int currentW = image.getWidth();
int currentH = image.getHeight();
BufferedImage resized = image;
while (currentW > w || currentH > h) {
currentW = Math.max(w, currentW / 2);
currentH = Math.max(h, currentH / 2);
BufferedImage temp = new BufferedImage(currentW, currentH, type);
Graphics2D g2 = temp.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.drawImage(resized, 0, 0, currentW, currentH, null);
g2.dispose();
resized = temp;
}
return resized;
} else {
Object hint = scale > 2 ? RenderingHints.VALUE_INTERPOLATION_BICUBIC : RenderingHints.VALUE_INTERPOLATION_BILINEAR;
BufferedImage resized = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = resized.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
g2.drawImage(image, 0, 0, w, h, null);
g2.dispose();
return resized;
}
}
/**
* Since some methods like toolkit.getImage() are asynchronous, this
* method should be called to load them completely.
*/
public static void loadCompletely (Image img)
{
MediaTracker tracker = new MediaTracker(new JPanel());
tracker.addImage(img, 0);
try {
tracker.waitForID(0);
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With