/* * Code got from http://www.java2s.com/Open-Source/Android/android-core/platform-sdk/com/android/monkeyrunner/adb/image/ImageUtils.java.htm * */ package org.safs.android.auto.lib; import java.awt.Color; import java.awt.Point; import java.awt.geom.AffineTransform; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.PixelInterleavedSampleModel; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.io.File; import java.io.IOException; import java.util.Hashtable; import java.util.Iterator; import java.util.Locale; import javax.imageio.IIOImage; import javax.imageio.ImageIO; import javax.imageio.ImageWriteParam; import javax.imageio.ImageWriter; import javax.imageio.plugins.jpeg.JPEGImageWriteParam; import javax.imageio.stream.ImageOutputStream; import com.android.ddmlib.RawImage; public class ImageUtils{ @SuppressWarnings("unchecked") private static Hashtable<?,?> EMPTY_HASH = new Hashtable(); private static int[] BAND_OFFSETS_32 = { 0, 1, 2, 3 }; private static int[] BAND_OFFSETS_16 = { 0, 1 }; /** * Convert a raw image into a buffered image. * * @param rawImage the raw image to convert * @param image the old image to (possibly) recycle * @return the converted image */ public static BufferedImage convertImage(RawImage rawImage, BufferedImage image) { switch (rawImage.bpp) { case 16: return rawImage16toARGB(image, rawImage); case 32: return rawImage32toARGB(rawImage); } return null; } /** * Convert a raw image into a buffered image. * * @param rawImage the image to convert. * @return the converted image. */ public static BufferedImage convertImage(RawImage rawImage) { return convertImage(rawImage, null); } public static int getMask(int length) { int res = 0; for (int i = 0 ; i < length ; i++) { res = (res << 1) + 1; } return res; } private static BufferedImage rawImage32toARGB(RawImage rawImage) { // Do as much as we can to not make an extra copy of the data. This is just a bunch of // classes that wrap's the raw byte array of the image data. DataBufferByte dataBuffer = new DataBufferByte(rawImage.data, rawImage.size); PixelInterleavedSampleModel sampleModel = new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, rawImage.width, rawImage.height, 4, rawImage.width * 4, BAND_OFFSETS_32); WritableRaster raster = Raster.createWritableRaster(sampleModel, dataBuffer, new Point(0, 0)); return new BufferedImage(new ThirtyTwoBitColorModel(rawImage), raster, false, EMPTY_HASH); } private static BufferedImage rawImage16toARGB(BufferedImage image, RawImage rawImage) { // Do as much as we can to not make an extra copy of the data. This is just a bunch of // classes that wrap's the raw byte array of the image data. DataBufferByte dataBuffer = new DataBufferByte(rawImage.data, rawImage.size); PixelInterleavedSampleModel sampleModel = new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, rawImage.width, rawImage.height, 2, rawImage.width * 2, BAND_OFFSETS_16); WritableRaster raster = Raster.createWritableRaster(sampleModel, dataBuffer,new Point(0, 0)); return new BufferedImage(new SixteenBitColorModel(rawImage), raster, false, EMPTY_HASH); } /** * Rotate the Image with 90, 180 or 270 degree. * * @param bufferedimage * @param angle int, the angle the image will be rotated, in 360 degree * only 90, 180 or 270 degree are supported. * @return */ public static BufferedImage rotateImage(BufferedImage bufferedimage, int angle){ int width = bufferedimage.getWidth(); int height = bufferedimage.getHeight(); AffineTransform affineTransform = new AffineTransform(); if(angle==0){ return bufferedimage; }else if (angle == 90) { affineTransform.translate(height, 0); }else if (angle == 180) { affineTransform.translate(width, height); }else if (angle == 270) { affineTransform.translate(0, width); }else{ debug("Rotation degree '"+angle+"' is not supported."); return null; } affineTransform.rotate(java.lang.Math.toRadians(angle)); AffineTransformOp affineTransformOp = new AffineTransformOp(affineTransform, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); return affineTransformOp.filter(bufferedimage, null); } /** * Store our BufferedImage into a File; * If the format is JPG, the third parameter indicate the compression quality. * @param image - BufferedImage for ImageIO to write to file * @param file - valid full absolute File to write to * @param quality - If the file format is JPG, it indicates the compression quality. * It's value should be between 0.0 and 1.0; * @throws AndroidRuntimeException */ public static void saveImageToFile(BufferedImage image, File file, float quality) throws AndroidRuntimeException{ String debugmsg = ImageUtils.class.getName()+".saveImageToFile() "; ImageWriter writer = null; ImageOutputStream ios = null; try { if (file.getName().toLowerCase().endsWith(".jpg")) { debug("IU saveImage attempting to open and write JPG image."); Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName("jpg"); if (iter.hasNext()) { writer = iter.next(); }else{ debug(debugmsg+" Can not create ImageWriter for format jpg."); throw new AndroidRuntimeException(" Can not create ImageWriter for format jpg."); } // Prepare output file ios = ImageIO.createImageOutputStream(file); writer.setOutput(ios); // Set the compression quality ImageWriteParam iwparam = new JPEGImageWriteParam(Locale.getDefault()); iwparam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT) ; iwparam.setCompressionQuality(quality); // Write the image writer.write(null, new IIOImage(image, null, null), iwparam); }else{ saveImageToFile(image,file); } }catch(IOException e){ throw new AndroidRuntimeException("IOException: Can not write to file."+file.getName()); }finally{ try { if (ios != null) { ios.flush(); ios.close(); } } catch (IOException e1) { debug(debugmsg+"Can not close output stream for file "+file.getName()); } if (writer != null) writer.dispose(); } } /** * Store our BufferedImage into a File. * @param image - BufferedImage for ImageIO to write to file * @param file -- valid full absolute File to write to * @throws SecurityException thrown if permission to write to the location is denied * @throws IllegalArgumentException if ImageIO doesn't like our invocation * @throws IOException if an error occurs while writing. * @throws NoClassDefFoundError if support for ImageIO is not found (Java Advanced Imaging) */ public static void saveImageToFile(BufferedImage image, File file) throws SecurityException, IllegalArgumentException, IOException, NoClassDefFoundError{ //Use ImageIO to write image to a file, so that the same content will be used in the verifyGUIImageToFile() debug("IU saveImage attempting to save image to file: "+file.getAbsolutePath()); String lowerCaseFileName = file.getName().toLowerCase(); if (lowerCaseFileName.endsWith(".jpg") || lowerCaseFileName.endsWith("jpeg")) { ImageIO.write(image,"JPEG",file); }else if (lowerCaseFileName.endsWith(".bmp")) { ImageIO.write(image,"BMP",file); }else if (lowerCaseFileName.endsWith(".tif") || lowerCaseFileName.endsWith(".tiff")) { ImageIO.write(image,"TIF",file); }else if (lowerCaseFileName.endsWith(".gif")) { ImageIO.write(image,"GIF",file); }else if (lowerCaseFileName.endsWith(".png")) { ImageIO.write(image,"PNG",file); }else if (lowerCaseFileName.endsWith(".pnm")) { ImageIO.write(image,"PNM",file); }else{ debug("IU saveImage unsupported image format specification!"); throw new IllegalArgumentException("Only JPG, BMP, TIF, GIF, PNG and PNM files are currently supported."); } } /** * <b>Purpose</b> Create a new BufferedImage Object with a certain type.<br> * @param width * @param height * @param defaultType The type of the new BufferedImage * @param altType If the default type can not be used to create a BufferedImage,<br> * the altType will be used. * @return */ public static BufferedImage getVoidBufferImage(int width, int height, int defaultType, int altType){ BufferedImage image = null; try{ image = new BufferedImage(width,height,defaultType); }catch(Exception e){ debug("IU: getVoidBufferImage() "+e.getMessage()); if(altType>BufferedImage.TYPE_CUSTOM && altType<BufferedImage.TYPE_BYTE_INDEXED){ image = new BufferedImage(width,height,altType); }else{ image = new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB); } } return image; } /** * <b>Purpose</b> Create a new BufferedImage object, its size is destWidth*destHeight<br> * If the size is samller than the source image, copy that part from source<br> * image to this new image; While if the size is bigger, copy the whole source<br> * image to this new image, and the part beyond will be filled by initialColor<br> * This method is used when drag to resize an image in ImageManager2<br> * * @param srcImage BufferedImage, The source image to be copied. * @param destWidth int, the destination image width * @param destHeight int, the destination image height * @param initialColor Color, the color to paint on the destination image as initial color * @return */ public static BufferedImage getCopiedImage(BufferedImage srcImage, int destWidth, int destHeight, Color initialColor){ return ImageUtils.getCopiedImage(srcImage, 0, 0, 0, 0, destWidth, destHeight, initialColor); } /** * <b>Purpose</b> Copy the source image to a new image, whose width and height are given<br> * by destWidth and destHeight; The area of new image outside of the source <br> * image will be filled with the color provided by parameter.<br> * * @param srcImage BufferedImage, The source image to be copied. * @param srcOffsetX int, the x coordination in source image to begin copy * @param srcOffsetY int, the y coordination in source image to begin copy * @param destOffsetX int, the x coordination in destination image to begin paste * @param destOffsetY int, the y coordination in destination image to begin paste * @param destWidth int, the destination image width * @param destHeight int, the destination image height * @param initialColor Color, the color to paint on the destination image as initial color * @return */ public static BufferedImage getCopiedImage(BufferedImage srcImage, int srcOffsetX, int srcOffsetY, /* source offset, from where to begin copy*/ int destOffsetX, int destOffsetY, /* dest offset, from where to begin paste*/ int destWidth, int destHeight, /* the new image's width and height*/ Color initialColor){ if(srcImage==null || destWidth<=0 || destHeight<=0 ){ debug("IU.getCopiedImage(): Input Parameter error."); return null; } if(initialColor==null){ initialColor = Color.WHITE; } BufferedImage destImage = getVoidBufferImage(destWidth,destHeight,srcImage.getType(),BufferedImage.TYPE_INT_RGB); WritableRaster destRaster = destImage.getRaster(); ColorModel destColorModel = destImage.getColorModel(); Object inData = destColorModel.getDataElements(initialColor.getRGB(), null); //Set the dest raster to initialColor for (int i = 0; i < destWidth; i++) { for (int j = 0; j < destHeight; j++) { destRaster.setDataElements(i, j, inData); } } //Copy the source raster to the dest raster int rgb = 0; if(srcOffsetX+destWidth-destOffsetX>srcImage.getWidth()){ destWidth = srcImage.getWidth()+destOffsetX-srcOffsetX; } if(srcOffsetY+destHeight-destOffsetY>srcImage.getHeight()){ destHeight = srcImage.getHeight()+destOffsetY-srcOffsetY; } for (int i = destOffsetX; i < destWidth; i++) { for (int j = destOffsetY; j < destHeight; j++) { rgb = srcImage.getRGB(i-destOffsetX+srcOffsetX, j-destOffsetY+srcOffsetY); destImage.setRGB(i, j, rgb); } } return destImage; } public static void debug(String message){ System.out.println(message); } }