My application needs to resize ImageData. Unfortunately, I haven't gotten the results I want with GC (with antialias on and interpolation on high), or ImageData.scaledTo(). The resulting image is of too low a quality to be acceptable. What's the best way to do a high quality ImageData resize?
Edit: I'm scaling down.
The higher the resolution, the more pixels there are in the image, and the better the quality. If you want to resize an image without losing quality, you need to make sure that the "Resample Image" checkbox is unchecked. This checkbox tells Photoshop to change the number of pixels in the image.
Does resizing an image affect its quality? It definitely can! Typically, making an image smaller will not impact the quality, but an image can suffer quality loss when scaled beyond its original size.
AWT offers image scaling with different modes. For downscaling, Area Averaging offers the best quality. One could of course re-implement the area averaging scaling algorithm for SWT, or, for a quick solution which often suffices:
The code to convert between SWT and AWT images can be found here.
/**
* Resizes an image, using the given scaling factor. Constructs a new image resource, please take care of resource
* disposal if you no longer need the original one. This method is optimized for quality, not for speed.
*
* @param image source image
* @param scale scale factor (<1 = downscaling, >1 = upscaling)
* @return scaled image
*/
public static org.eclipse.swt.graphics.Image resize (org.eclipse.swt.graphics.Image image, float scale) {
int w = image.getBounds().width;
int h = image.getBounds().height;
// convert to buffered image
BufferedImage img = convertToAWT(image.getImageData());
// resize buffered image
int newWidth = Math.round(scale * w);
int newHeight = Math.round(scale * h);
// determine scaling mode for best result: if downsizing, use area averaging, if upsizing, use smooth scaling
// (usually bilinear).
int mode = scale < 1 ? BufferedImage.SCALE_AREA_AVERAGING : BufferedImage.SCALE_SMOOTH;
java.awt.Image scaledImage = img.getScaledInstance(newWidth, newHeight, mode);
// convert the scaled image back to a buffered image
img = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
img.getGraphics().drawImage(scaledImage, 0, 0, null);
// reconstruct swt image
ImageData imageData = convertToSWT(img);
return new org.eclipse.swt.graphics.Image(Display.getDefault(), imageData);
}
public static BufferedImage convertToAWT (ImageData data) {
ColorModel colorModel = null;
PaletteData palette = data.palette;
if (palette.isDirect) {
colorModel = new DirectColorModel(data.depth, palette.redMask, palette.greenMask, palette.blueMask);
BufferedImage bufferedImage = new BufferedImage(colorModel, colorModel.createCompatibleWritableRaster(data.width, data.height),
false, null);
WritableRaster raster = bufferedImage.getRaster();
int[] pixelArray = new int[3];
for (int y = 0; y < data.height; y++) {
for (int x = 0; x < data.width; x++) {
int pixel = data.getPixel(x, y);
RGB rgb = palette.getRGB(pixel);
pixelArray[0] = rgb.red;
pixelArray[1] = rgb.green;
pixelArray[2] = rgb.blue;
raster.setPixels(x, y, 1, 1, pixelArray);
}
}
return bufferedImage;
} else {
RGB[] rgbs = palette.getRGBs();
byte[] red = new byte[rgbs.length];
byte[] green = new byte[rgbs.length];
byte[] blue = new byte[rgbs.length];
for (int i = 0; i < rgbs.length; i++) {
RGB rgb = rgbs[i];
red[i] = (byte) rgb.red;
green[i] = (byte) rgb.green;
blue[i] = (byte) rgb.blue;
}
if (data.transparentPixel != -1) {
colorModel = new IndexColorModel(data.depth, rgbs.length, red, green, blue, data.transparentPixel);
} else {
colorModel = new IndexColorModel(data.depth, rgbs.length, red, green, blue);
}
BufferedImage bufferedImage = new BufferedImage(colorModel, colorModel.createCompatibleWritableRaster(data.width, data.height),
false, null);
WritableRaster raster = bufferedImage.getRaster();
int[] pixelArray = new int[1];
for (int y = 0; y < data.height; y++) {
for (int x = 0; x < data.width; x++) {
int pixel = data.getPixel(x, y);
pixelArray[0] = pixel;
raster.setPixel(x, y, pixelArray);
}
}
return bufferedImage;
}
}
public static ImageData convertToSWT (BufferedImage bufferedImage) {
if (bufferedImage.getColorModel() instanceof DirectColorModel) {
DirectColorModel colorModel = (DirectColorModel) bufferedImage.getColorModel();
PaletteData palette = new PaletteData(colorModel.getRedMask(), colorModel.getGreenMask(), colorModel.getBlueMask());
ImageData data = new ImageData(bufferedImage.getWidth(), bufferedImage.getHeight(), colorModel.getPixelSize(), palette);
WritableRaster raster = bufferedImage.getRaster();
int[] pixelArray = new int[3];
for (int y = 0; y < data.height; y++) {
for (int x = 0; x < data.width; x++) {
raster.getPixel(x, y, pixelArray);
int pixel = palette.getPixel(new RGB(pixelArray[0], pixelArray[1], pixelArray[2]));
data.setPixel(x, y, pixel);
}
}
return data;
} else if (bufferedImage.getColorModel() instanceof IndexColorModel) {
IndexColorModel colorModel = (IndexColorModel) bufferedImage.getColorModel();
int size = colorModel.getMapSize();
byte[] reds = new byte[size];
byte[] greens = new byte[size];
byte[] blues = new byte[size];
colorModel.getReds(reds);
colorModel.getGreens(greens);
colorModel.getBlues(blues);
RGB[] rgbs = new RGB[size];
for (int i = 0; i < rgbs.length; i++) {
rgbs[i] = new RGB(reds[i] & 0xFF, greens[i] & 0xFF, blues[i] & 0xFF);
}
PaletteData palette = new PaletteData(rgbs);
ImageData data = new ImageData(bufferedImage.getWidth(), bufferedImage.getHeight(), colorModel.getPixelSize(), palette);
data.transparentPixel = colorModel.getTransparentPixel();
WritableRaster raster = bufferedImage.getRaster();
int[] pixelArray = new int[1];
for (int y = 0; y < data.height; y++) {
for (int x = 0; x < data.width; x++) {
raster.getPixel(x, y, pixelArray);
data.setPixel(x, y, pixelArray[0]);
}
}
return data;
}
return null;
}
The accepted solution doesn't deal with transparency. Here's a snippet I've came across that does hi-quality resize and preserves transparency as well:
public static Image resize(Image image, int width, int height) {
Image scaled = new Image(Display.getDefault(), width, height);
GC gc = new GC(scaled);
gc.setAntialias(SWT.ON);
gc.setInterpolation(SWT.HIGH);
gc.drawImage(image, 0, 0,image.getBounds().width, image.getBounds().height, 0, 0, width, height);
gc.dispose();
image.dispose(); // don't forget about me!
return scaled;
}
I've found it here:
http://aniszczyk.org/2007/08/09/resizing-images-using-swt/
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