|
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-2024 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.