|
What this is
Other links
The source code/* * $Header: /cvsroot/mvnforum/myvietnam/src/net/myvietnam/mvncore/util/ImageUtil.java,v 1.14 2005/01/18 12:16:45 minhnn Exp $ * $Author: minhnn $ * $Revision: 1.14 $ * $Date: 2005/01/18 12:16:45 $ * * ==================================================================== * * Copyright (C) 2002-2005 by MyVietnam.net * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or any later version. * * All copyright notices regarding MyVietnam and MyVietnam CoreLib * MUST remain intact in the scripts and source code. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Correspondence and Marketing Questions can be sent to: * info@MyVietnam.net * * @author: Minh Nguyen minhnn@MyVietnam.net * @author: Mai Nguyen mai.nh@MyVietnam.net */ package net.myvietnam.mvncore.util; import java.io.*; import java.awt.image.BufferedImage; import java.awt.geom.AffineTransform; import java.awt.*; import java.awt.image.*; import javax.swing.ImageIcon; import com.sun.image.codec.jpeg.*; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import net.myvietnam.mvncore.thirdparty.JpegEncoder; import net.myvietnam.mvncore.exception.*; import net.myvietnam.mvncore.util.FileUtil; public final class ImageUtil { private static Log log = LogFactory.getLog(ImageUtil.class); private ImageUtil() {// prevent instantiation } /** * @todo: xem lai ham nay, neu kich thuoc nho hon max thi ta luu truc tiep * inputStream xuong thumbnailFile luon * * This method create a thumbnail and reserve the ratio of the output image * NOTE: This method closes the inputStream after it have done its work. * * @param inputStream the stream of a jpeg file * @param thumbnailFile the output file, must have the ".jpg" extension * @param maxWidth the maximun width of the image * @param maxHeight the maximun height of the image * @throws IOException * @throws BadInputException * @throws AssertionException */ public static void createThumbnail(InputStream inputStream, String thumbnailFile, int maxWidth, int maxHeight) throws IOException, BadInputException, AssertionException { //boolean useSun = false; String lowerName = thumbnailFile.toLowerCase(); if (!lowerName.endsWith(".jpg")) { throw new BadInputException("Cannot create a thumbnail with the extension other than '.jpg'."); } OutputStream outputStream = null; try { //JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(inputStream); //BufferedImage srcImage = decoder.decodeAsBufferedImage(); byte[] srcByte = FileUtil.getBytes(inputStream); ImageIcon imageIcon = new ImageIcon(srcByte); Image srcImage = imageIcon.getImage(); int imgWidth = srcImage.getWidth(null); int imgHeight = srcImage.getHeight(null); //log.trace("width = " + imgWidth + " height = " + imgHeight); if ( (imgWidth <= 0) || (imgHeight <= 0) ) { // imgWidth or imgHeight could be -1, which is considered as an assertion throw new AssertionException("Assertion: ImageUtil: cannot get the image size."); } // Set the scale. AffineTransform tx = new AffineTransform(); if ((imgWidth > maxWidth) || (imgHeight > maxHeight)) { double scaleX = (double)maxWidth/imgWidth; double scaleY = (double)maxHeight/imgHeight; double scaleRatio = (scaleX < scaleY) ? scaleX : scaleY; imgWidth = (int)(imgWidth * scaleRatio); imgHeight = (int)(imgHeight * scaleRatio); // scale as needed tx.scale(scaleRatio, scaleRatio); } else {// we dont need any transform here, just save it to file and return outputStream = new FileOutputStream(thumbnailFile); outputStream.write(srcByte); return; } // create thumbnail image BufferedImage bufferedImage = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB); Graphics2D g = bufferedImage.createGraphics(); boolean useTransform = false; if (useTransform) {// use transfrom to draw //log.trace("use transform"); g.drawImage(srcImage, tx, null); } else {// use java filter //log.trace("use filter"); Image scaleImage = getScaledInstance(srcImage, imgWidth, imgHeight); g.drawImage(scaleImage, 0, 0, null); } g.dispose();// free resource // write it to disk outputStream = new FileOutputStream(thumbnailFile); JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(outputStream); encoder.encode(bufferedImage); } catch (IOException e) { log.error("Error", e); throw e; } finally {// this finally is very important inputStream.close(); if (outputStream != null) outputStream.close(); } } /** * This method returns a fit-sized image for a source image, * this method retains the ratio of the source image */ public static Image getFitSizeImage(Image srcImage, int fitWidth, int fitHeight) throws IOException { if ((fitWidth < 100) || (fitHeight < 100)) throw new IllegalArgumentException("Cannot accept values < 100"); int srcWidth = srcImage.getWidth(null);//xem lai cho nay vi neu dung BufferedImage thi khong can null int srcHeight = srcImage.getHeight(null); //log.trace("src w = " + srcWidth + " h = " + srcHeight); // dont need any transforms if ((srcWidth == fitWidth) && (srcHeight == fitHeight)) return srcImage; int newWidth = srcWidth; int newHeight = srcHeight; double fitRatio = (double)fitWidth / fitHeight; double srcRatio = (double)srcWidth / srcHeight; if (srcRatio > fitRatio) {// must cut the width of the source image newWidth = (int)(srcHeight * fitRatio); } else {// must cut the height of the source image newHeight = (int)(srcWidth / fitRatio); } //log.trace("new w = " + newWidth + " h = " + newHeight); ImageFilter cropFilter = new CropImageFilter((srcWidth-newWidth)/2, (srcHeight-newHeight)/2, newWidth, newHeight); ImageProducer cropProd = new FilteredImageSource(srcImage.getSource(), cropFilter); Image cropImage = Toolkit.getDefaultToolkit().createImage(cropProd); Image retImage = new ImageIcon(getScaledInstance(cropImage, fitWidth, fitHeight)).getImage(); return retImage; } /** * This method returns a fit-sized image for a source image, * this method retains the ratio of the source image */ public static Image getFitSizeImage(InputStream inputStream, int fitWidth, int fitHeight) throws IOException { try { JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(inputStream); BufferedImage srcImage = decoder.decodeAsBufferedImage(); return getFitSizeImage(srcImage, fitWidth, fitHeight); } catch (IOException e) { log.error("Cannot run getFitSizeImage", e); throw e; } finally {// this finally is very important inputStream.close(); } } /** * This method write the image to a stream. * It auto detect the image is Image or BufferedImage. * This method close the output stream before it return. * * @param image Image * @param outputStream OutputStream * @throws IOException */ public static void writeJpegImage_Sun(Image image, OutputStream outputStream) throws IOException { if (outputStream == null) { return; } try { BufferedImage bufferedImage = null; if (image instanceof BufferedImage) { bufferedImage = (BufferedImage)image; } else { // 30% cpu resource bufferedImage = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_RGB); // 7.5 cpu Graphics2D g = bufferedImage.createGraphics(); // 50% cpu g.drawImage(image, 0, 0, null); g.dispose(); // free resource } // write it to disk // 12% cpu JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(outputStream); encoder.encode(bufferedImage); } finally { try { if (outputStream != null) { outputStream.close(); } } catch (IOException ex1) { } } } public static void writeJpegImage_Sun(Image image, String fileName) throws IOException { OutputStream outputStream = new FileOutputStream(fileName); writeJpegImage_Sun(image, outputStream); } /** * This method write image to output stream, then it close the stream before return * * @param image Image * @param outputStream OutputStream * @throws IOException */ public static void writeJpegImage_nonSun(Image image, OutputStream outputStream) throws IOException { if (outputStream == null) { return; } try { JpegEncoder encoder = new JpegEncoder(image, 80, outputStream); encoder.Compress(); } catch (Exception ex) { } finally { try { if (outputStream != null) { outputStream.close(); } } catch (IOException ex1) { } } } public static void writeJpegImage_nonSun(Image image, String fileName) throws IOException { OutputStream outputStream = new FileOutputStream(fileName); //this method will close the stream before it returns so no need to close writeJpegImage_nonSun(image, outputStream); } public static Image getScaledInstance(Image srcImage, int width, int height) { boolean useSun = false; ImageFilter filter; if (useSun){ //log.trace("use sun scalefilter"); filter = new java.awt.image.AreaAveragingScaleFilter(width, height); } else { //log.trace("use nguoimau scalefilter"); filter = new net.myvietnam.mvncore.util.AreaAveragingScaleFilter(width, height); } ImageProducer prod = new FilteredImageSource(srcImage.getSource(), filter); Image newImage = Toolkit.getDefaultToolkit().createImage(prod); ImageIcon imageIcon = new ImageIcon(newImage); return imageIcon.getImage(); } /* public static void main(String[] args) { try { FileInputStream is = new FileInputStream("c:\\PUTTY.RND"); createThumbnail(is, "c:\\out.jpg", 120, 120); } catch (Exception ex) { ex.printStackTrace(); } log.debug("done"); } */ } /** * This class is taken from Pure Java AWT project * and modified to exclude PJAGraphicsManager class */ // From java.awt.image.AreaAveragingScaleFilter // but without use of float type class AreaAveragingScaleFilter extends ImageFilter { private ColorModel rgbModel; private long [] alphas; private long [] reds; private long [] greens; private long [] blues; protected int srcWidth; protected int srcHeight; private int [] srcPixels; protected int destWidth; protected int destHeight; protected int [] destPixels; { // Test if the class java.awt.image.ColorModel can be loaded //boolean classColorModelAccessible = PJAGraphicsManager.getDefaultGraphicsManager ().isClassAccessible ("java.awt.image.ColorModel"); // modified by minhnn boolean classColorModelAccessible = isClassAccessible ("java.awt.image.ColorModel"); if (classColorModelAccessible) rgbModel = ColorModel.getRGBdefault (); } /** * Constructs an AreaAveragingScaleFilter that scales the pixels from * its source Image as specified by the width and height parameters. * @param width the target width to scale the image * @param height the target height to scale the image */ public AreaAveragingScaleFilter(int width, int height) { destWidth = width; destHeight = height; } public void setDimensions (int w, int h) { srcWidth = w; srcHeight = h; if (destWidth < 0) { if (destHeight < 0) { destWidth = srcWidth; destHeight = srcHeight; } else destWidth = srcWidth * destHeight / srcHeight; } else if (destHeight < 0) destHeight = srcHeight * destWidth / srcWidth; consumer.setDimensions (destWidth, destHeight); } public void setHints (int hints) { // Images are sent entire frame by entire frame consumer.setHints ( (hints & (SINGLEPASS | SINGLEFRAME)) | TOPDOWNLEFTRIGHT); } public void imageComplete (int status) { if ( status == STATICIMAGEDONE || status == SINGLEFRAMEDONE) accumPixels (0, 0, srcWidth, srcHeight, rgbModel, srcPixels, 0, srcWidth); consumer.imageComplete (status); } public void setPixels (int x, int y, int width, int height, ColorModel model, byte pixels [], int offset, int scansize) { // Store pixels in srcPixels array if (srcPixels == null) srcPixels = new int [srcWidth * srcHeight]; for (int row = 0, destRow = y * srcWidth; row < height; row++, destRow += srcWidth) { int rowOff = offset + row * scansize; for (int col = 0; col < width; col++) // v1.2 : Added & 0xFF to disable sign bit srcPixels [destRow + x + col] = model.getRGB (pixels [rowOff + col] & 0xFF); } } public void setPixels (int x, int y, int width, int height, ColorModel model, int pixels[], int offset, int scansize) { // Store pixels in srcPixels array if (srcPixels == null) srcPixels = new int [srcWidth * srcHeight]; for (int row = 0, destRow = y * srcWidth; row < height; row++, destRow += srcWidth) { int rowOff = offset + row * scansize; for (int col = 0; col < width; col++) // If model == null, consider it's the default RGB model srcPixels [destRow + x + col] = model == null ? pixels [rowOff + col] : model.getRGB (pixels [rowOff + col]); } } private int [] calcRow () { long mult = (srcWidth * srcHeight) << 32; if (destPixels == null) destPixels = new int [destWidth]; for (int x = 0; x < destWidth; x++) { int a = (int)roundDiv (alphas [x], mult); int r = (int)roundDiv (reds [x], mult); int g = (int)roundDiv (greens [x], mult); int b = (int)roundDiv (blues [x], mult); a = Math.max (Math.min (a, 255), 0); r = Math.max (Math.min (r, 255), 0); g = Math.max (Math.min (g, 255), 0); b = Math.max (Math.min (b, 255), 0); destPixels [x] = (a << 24 | r << 16 | g << 8 | b); } return destPixels; } private void accumPixels (int x, int y, int w, int h, ColorModel model, int [] pixels, int off, int scansize) { reds = new long [destWidth]; greens = new long [destWidth]; blues = new long [destWidth]; alphas = new long [destWidth]; int sy = y; int syrem = destHeight; int dy = 0; int dyrem = 0; while (sy < y + h) { if (dyrem == 0) { for (int i = 0; i < destWidth; i++) alphas [i] = reds [i] = greens [i] = blues [i] = 0; dyrem = srcHeight; } int amty = Math.min (syrem, dyrem); int sx = 0; int dx = 0; int sxrem = 0; int dxrem = srcWidth; int a = 0, r = 0, g = 0, b = 0; while (sx < w) { if (sxrem == 0) { sxrem = destWidth; int rgb = pixels [off + sx]; a = rgb >>> 24; r = (rgb >> 16) & 0xFF; g = (rgb >> 8) & 0xFF; b = rgb & 0xFF; } int amtx = Math.min (sxrem, dxrem); long mult = (amtx * amty) << 32; alphas [dx] += mult * a; reds [dx] += mult * r; greens [dx] += mult * g; blues [dx] += mult * b; if ((sxrem -= amtx) == 0) sx++; if ((dxrem -= amtx) == 0) { dx++; dxrem = srcWidth; } } if ((dyrem -= amty) == 0) { int outpix [] = calcRow (); do { consumer.setPixels (0, dy, destWidth, 1, rgbModel, outpix, 0, destWidth); dy++; } while ((syrem -= amty) >= amty && amty == srcHeight); } else syrem -= amty; if (syrem == 0) { syrem = destHeight; sy++; off += scansize; } } } //////////////// // util method //////////////// // util method /** * Returns the rounded result of <code>dividend / divisor, avoiding the use of floating * point operation (returns the same as <code>Math.round((float)dividend / divisor)). * @param dividend A <code>int number to divide. * @param divisor A <code>int divisor. * @return dividend / divisor rounded to the closest <code>int integer. */ public static int roundDiv(int dividend, int divisor) { final int remainder = dividend % divisor; if (Math.abs(remainder) * 2 <= Math.abs(divisor)) return dividend / divisor; else if (dividend * divisor < 0) return dividend / divisor - 1; else return dividend / divisor + 1; } /** * Returns the rounded result of <code>dividend / divisor, avoiding the use of floating * point operation (returns the same as <code>Math.round((double)dividend / divisor)). * @param dividend A <code>long number to divide. * @param divisor A <code>long divisor. * @return dividend / divisor rounded to the closest <code>long integer. */ public static long roundDiv (long dividend, long divisor) { final long remainder = dividend % divisor; if (Math.abs (remainder) * 2 <= Math.abs (divisor)) return dividend / divisor; else if (dividend * divisor < 0) return dividend / divisor - 1; else return dividend / divisor + 1; } /** * Returns <code>true if it successes to load the class |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.