Friday, December 3, 2010

Preparing Images For Transfer Through XML -- step 1) resize the image

I found myself needing to marshal an image for transfer to a REST client on a mobile phone.  The first issue I ran into was that the phones memory is very limited so I had to limit the size of the XML representation of the picture's data before it was sent across the wire.  In short, I had to decide on a size that I needed on the client end and make sure no more than that was sent.  The following assumes .png because it is easier to work with than jpeg in Java (avoiding JPEGImageWriteParam ).


public static byte[] resizeImageForTransfer(byte[] png, int newWidth, int newHeight) throws IOException{
BufferedImage image = ImageIO.read(new ByteArrayInputStream(png));
int oldHeight = image.getHeight();
int oldWidth = image.getWidth();
BufferedImage outputImage = resize(image,newWidth,newHeight);
ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageIO.write(outputImage, "png", os);
return os.toByteArray();
}

private static BufferedImage resize(BufferedImage image, int width, int height) {
  int type = image.getType() == 0 ? BufferedImage.TYPE_INT_RGB : image.getType();
BufferedImage resizedImage = new BufferedImage(width, height, type);
  Graphics2D graphic = resizedImage.createGraphics();
  graphic.setComposite(AlphaComposite.Src); //makes sure graphic is set to Opaque
  graphic.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
  graphic.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
  graphic.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
  graphic.drawImage(image, 0, 0, width, height, null);
graphic.dispose();
return resizedImage;
}


Because of the way JAXB works, it expects a byte array representation of the picture so here we pass in they byte[] of the picture and the desired width and height to resizeImageForTransfer which returns a similar, hopefully smaller, byte array.

The real work is accomplished in resize method.  setComposite uses a basic AlphaComposite -- this is an easy default to choose.  Setting the KEY_INTERPOLATION hint to VALUE_INTERPOLATION_BILINEAR worked nicely with how much I scaled down the images (cutting the area to about a quarter of the original).  KEY_INTERPOLATION is where experimentation might be in order to find a good fit for your app.  VALUE_INTERPOLATION_NEAREST_NEIGHBOR is simple and fast but blocky, and VALUE_INTERPOLATION_BICUBIC is cleaner for most applications but a bit heavier.  KEY_ANTIALIASING might be a worthy area to experiment with but, in general, I like to have it on.

No comments:

Post a Comment