Mega Code Archive

 
Categories / Java Tutorial / 2D Graphics
 

Compress and save an image to the disk

/*  * Copyright (c) JForum Team  * All rights reserved.  * Redistribution and use in source and binary forms,   * with or without modification, are permitted provided   * that the following conditions are met:  * 1) Redistributions of source code must retain the above   * copyright notice, this list of conditions and the   * following  disclaimer.  * 2)  Redistributions in binary form must reproduce the   * above copyright notice, this list of conditions and   * the following disclaimer in the documentation and/or   * other materials provided with the distribution.  * 3) Neither the name of "Rafael Steil" nor   * the names of its contributors may be used to endorse   * or promote products derived from this software without   * specific prior written permission.  *   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT   * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY   * EXPRESS OR IMPLIED WARRANTIES, INCLUDING,   * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF   * MERCHANTABILITY AND FITNESS FOR A PARTICULAR   * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL   * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,   * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER   * IN CONTRACT, STRICT LIABILITY, OR TORT   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN   * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE  *   * This file creation date: 21/04/2004 - 19:54:16  * The JForum Project  * http://www.jforum.net  */ import java.awt.Dimension; import java.awt.Image; import java.awt.image.BufferedImage; import java.awt.image.PixelGrabber; import java.io.File; import java.io.IOException; 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; /**  * Utilities methods for image manipulation. It does not support writting of GIF images, but it can  * read from. GIF images will be saved as PNG.  *   * @author Rafael Steil  * @version $Id: ImageUtils.java,v 1.23 2007/09/09 01:05:22 rafaelsteil Exp $  */ public class ImageUtils {   public static final int IMAGE_UNKNOWN = -1;   public static final int IMAGE_JPEG = 0;   public static final int IMAGE_PNG = 1;   public static final int IMAGE_GIF = 2;   /**    * Resizes an image    *     * @param imgName The image name to resize. Must be the complet path to the file    * @param type int    * @param maxWidth The image's max width    * @param maxHeight The image's max height    * @return A resized <code>BufferedImage</code>    */   public static BufferedImage resizeImage(String imgName, int type, int maxWidth, int maxHeight)   {     try {       return resizeImage(ImageIO.read(new File(imgName)), type, maxWidth, maxHeight);     }     catch (IOException e) {       e.printStackTrace();     }   }   /**    * Resizes an image.    *     * @param image    *            The image to resize    * @param maxWidth    *            The image's max width    * @param maxHeight    *            The image's max height    * @return A resized <code>BufferedImage</code>    * @param type    *            int    */   public static BufferedImage resizeImage(BufferedImage image, int type, int maxWidth, int maxHeight)   {     Dimension largestDimension = new Dimension(maxWidth, maxHeight);     // Original size     int imageWidth = image.getWidth(null);     int imageHeight = image.getHeight(null);     float aspectRatio = (float) imageWidth / imageHeight;     if (imageWidth > maxWidth || imageHeight > maxHeight) {       if ((float) largestDimension.width / largestDimension.height > aspectRatio) {         largestDimension.width = (int) Math.ceil(largestDimension.height * aspectRatio);       }       else {         largestDimension.height = (int) Math.ceil(largestDimension.width / aspectRatio);       }       imageWidth = largestDimension.width;       imageHeight = largestDimension.height;     }     return createHeadlessSmoothBufferedImage(image, type, imageWidth, imageHeight);   }   /**    * Saves an image to the disk.    *     * @param image  The image to save    * @param toFileName The filename to use    * @param type The image type. Use <code>ImageUtils.IMAGE_JPEG</code> to save as JPEG images,    *  or <code>ImageUtils.IMAGE_PNG</code> to save as PNG.    * @return <code>false</code> if no appropriate writer is found    */   public static boolean saveImage(BufferedImage image, String toFileName, int type)   {     try {       return ImageIO.write(image, type == IMAGE_JPEG ? "jpg" : "png", new File(toFileName));     }     catch (IOException e) {       e.printStackTrace();     }   }   /**    * Compress and save an image to the disk. Currently this method only supports JPEG images.    *     * @param image The image to save    * @param toFileName The filename to use    * @param type The image type. Use <code>ImageUtils.IMAGE_JPEG</code> to save as JPEG images,    * or <code>ImageUtils.IMAGE_PNG</code> to save as PNG.    */   public static void saveCompressedImage(BufferedImage image, String toFileName, int type)   {     try {       if (type == IMAGE_PNG) {         throw new UnsupportedOperationException("PNG compression not implemented");       }       Iterator iter = ImageIO.getImageWritersByFormatName("jpg");       ImageWriter writer;       writer = (ImageWriter) iter.next();       ImageOutputStream ios = ImageIO.createImageOutputStream(new File(toFileName));       writer.setOutput(ios);       ImageWriteParam iwparam = new JPEGImageWriteParam(Locale.getDefault());       iwparam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);       iwparam.setCompressionQuality(0.7F);       writer.write(null, new IIOImage(image, null, null), iwparam);       ios.flush();       writer.dispose();       ios.close();     }     catch (IOException e) {       e.printStackTrace();     }   }   /**    * Creates a <code>BufferedImage</code> from an <code>Image</code>. This method can    * function on a completely headless system. This especially includes Linux and Unix systems    * that do not have the X11 libraries installed, which are required for the AWT subsystem to    * operate. This method uses nearest neighbor approximation, so it's quite fast. Unfortunately,    * the result is nowhere near as nice looking as the createHeadlessSmoothBufferedImage method.    *     * @param image  The image to convert    * @param w The desired image width    * @param h The desired image height    * @return The converted image    * @param type int    */   public static BufferedImage createHeadlessBufferedImage(BufferedImage image, int type, int width, int height)   {     if (type == ImageUtils.IMAGE_PNG && hasAlpha(image)) {       type = BufferedImage.TYPE_INT_ARGB;     }     else {       type = BufferedImage.TYPE_INT_RGB;     }     BufferedImage bi = new BufferedImage(width, height, type);     for (int y = 0; y < height; y++) {       for (int x = 0; x < width; x++) {         bi.setRGB(x, y, image.getRGB(x * image.getWidth() / width, y * image.getHeight() / height));       }     }     return bi;   }   /**    * Creates a <code>BufferedImage</code> from an <code>Image</code>. This method can    * function on a completely headless system. This especially includes Linux and Unix systems    * that do not have the X11 libraries installed, which are required for the AWT subsystem to    * operate. The resulting image will be smoothly scaled using bilinear filtering.    *     * @param source The image to convert    * @param w The desired image width    * @param h The desired image height    * @return The converted image    * @param type  int    */   public static BufferedImage createHeadlessSmoothBufferedImage(BufferedImage source, int type, int width, int height)   {     if (type == ImageUtils.IMAGE_PNG && hasAlpha(source)) {       type = BufferedImage.TYPE_INT_ARGB;     }     else {       type = BufferedImage.TYPE_INT_RGB;     }     BufferedImage dest = new BufferedImage(width, height, type);     int sourcex;     int sourcey;     double scalex = (double) width / source.getWidth();     double scaley = (double) height / source.getHeight();     int x1;     int y1;     double xdiff;     double ydiff;     int rgb;     int rgb1;     int rgb2;     for (int y = 0; y < height; y++) {       sourcey = y * source.getHeight() / dest.getHeight();       ydiff = scale(y, scaley) - sourcey;       for (int x = 0; x < width; x++) {         sourcex = x * source.getWidth() / dest.getWidth();         xdiff = scale(x, scalex) - sourcex;         x1 = Math.min(source.getWidth() - 1, sourcex + 1);         y1 = Math.min(source.getHeight() - 1, sourcey + 1);         rgb1 = getRGBInterpolation(source.getRGB(sourcex, sourcey), source.getRGB(x1, sourcey), xdiff);         rgb2 = getRGBInterpolation(source.getRGB(sourcex, y1), source.getRGB(x1, y1), xdiff);         rgb = getRGBInterpolation(rgb1, rgb2, ydiff);         dest.setRGB(x, y, rgb);       }     }     return dest;   }   private static double scale(int point, double scale)   {     return point / scale;   }   private static int getRGBInterpolation(int value1, int value2, double distance)   {     int alpha1 = (value1 & 0xFF000000) >>> 24;     int red1 = (value1 & 0x00FF0000) >> 16;     int green1 = (value1 & 0x0000FF00) >> 8;     int blue1 = (value1 & 0x000000FF);     int alpha2 = (value2 & 0xFF000000) >>> 24;     int red2 = (value2 & 0x00FF0000) >> 16;     int green2 = (value2 & 0x0000FF00) >> 8;     int blue2 = (value2 & 0x000000FF);     int rgb = ((int) (alpha1 * (1.0 - distance) + alpha2 * distance) << 24)       | ((int) (red1 * (1.0 - distance) + red2 * distance) << 16)       | ((int) (green1 * (1.0 - distance) + green2 * distance) << 8)       | (int) (blue1 * (1.0 - distance) + blue2 * distance);     return rgb;   }   /**    * Determines if the image has transparent pixels.    *     * @param image The image to check for transparent pixel.s    * @return <code>true</code> of <code>false</code>, according to the result    */   public static boolean hasAlpha(Image image)   {     try {       PixelGrabber pg = new PixelGrabber(image, 0, 0, 1, 1, false);       pg.grabPixels();       return pg.getColorModel().hasAlpha();     }     catch (InterruptedException e) {       return false;     }   } }