KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > myvietnam > mvncore > util > ImageUtil


1 /*
2  * $Header: /cvsroot/mvnforum/myvietnam/src/net/myvietnam/mvncore/util/ImageUtil.java,v 1.17 2006/04/15 02:59:20 minhnn Exp $
3  * $Author: minhnn $
4  * $Revision: 1.17 $
5  * $Date: 2006/04/15 02:59:20 $
6  *
7  * ====================================================================
8  *
9  * Copyright (C) 2002-2006 by MyVietnam.net
10  *
11  * All copyright notices regarding MyVietnam and MyVietnam CoreLib
12  * MUST remain intact in the scripts and source code.
13  *
14  * This library is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU Lesser General Public
16  * License as published by the Free Software Foundation; either
17  * version 2.1 of the License, or (at your option) any later version.
18  *
19  * This library is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22  * Lesser General Public License for more details.
23  *
24  * You should have received a copy of the GNU Lesser General Public
25  * License along with this library; if not, write to the Free Software
26  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27  *
28  * Correspondence and Marketing Questions can be sent to:
29  * info at MyVietnam net
30  *
31  * @author: Minh Nguyen
32  * @author: Mai Nguyen
33  */

34 package net.myvietnam.mvncore.util;
35
36 import java.io.*;
37 import java.awt.image.BufferedImage JavaDoc;
38 import java.awt.geom.AffineTransform JavaDoc;
39 import java.awt.*;
40 import java.awt.image.*;
41 import javax.swing.ImageIcon JavaDoc;
42 import com.sun.image.codec.jpeg.*;
43 import org.apache.commons.logging.Log;
44 import org.apache.commons.logging.LogFactory;
45 import net.myvietnam.mvncore.thirdparty.JpegEncoder;
46 import net.myvietnam.mvncore.exception.*;
47 import net.myvietnam.mvncore.util.FileUtil;
48
49 public final class ImageUtil {
50
51     private static Log log = LogFactory.getLog(ImageUtil.class);
52
53     private ImageUtil() {// prevent instantiation
54
}
55
56     /**
57      * @todo: xem lai ham nay, neu kich thuoc nho hon max thi ta luu truc tiep
58      * inputStream xuong thumbnailFile luon
59      *
60      * This method create a thumbnail and reserve the ratio of the output image
61      * NOTE: This method closes the inputStream after it have done its work.
62      *
63      * @param inputStream the stream of a jpeg file
64      * @param thumbnailFile the output file, must have the ".jpg" extension
65      * @param maxWidth the maximun width of the image
66      * @param maxHeight the maximun height of the image
67      * @throws IOException
68      * @throws BadInputException
69      * @throws AssertionException
70      */

71     public static void createThumbnail(InputStream inputStream, String JavaDoc thumbnailFile, int maxWidth, int maxHeight)
72         throws IOException, AssertionException {
73         
74         //boolean useSun = false;
75
if (thumbnailFile.toLowerCase().endsWith(".jpg") == false) {
76             throw new IllegalArgumentException JavaDoc("Cannot create a thumbnail with the extension other than '.jpg'.");
77         }
78         if (maxWidth <= 0) {
79             throw new IllegalArgumentException JavaDoc("maxWidth must >= 0");
80         }
81         if (maxHeight <= 0) {
82             throw new IllegalArgumentException JavaDoc("maxHeight must >= 0");
83         }
84
85         OutputStream outputStream = null;
86         try {
87             //JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(inputStream);
88
//BufferedImage srcImage = decoder.decodeAsBufferedImage();
89
byte[] srcByte = FileUtil.getBytes(inputStream);
90             ImageIcon JavaDoc imageIcon = new ImageIcon JavaDoc(srcByte);
91             Image JavaDoc srcImage = imageIcon.getImage();
92
93             int imgWidth = srcImage.getWidth(null);
94             int imgHeight = srcImage.getHeight(null);
95             //log.trace("width = " + imgWidth + " height = " + imgHeight);
96
if ( (imgWidth <= 0) || (imgHeight <= 0) ) {
97                 // imgWidth or imgHeight could be -1, which is considered as an assertion
98
throw new AssertionException("Assertion: ImageUtil: cannot get the image size.");
99             }
100
101             // Set the scale.
102
AffineTransform JavaDoc tx = new AffineTransform JavaDoc();
103             if ((imgWidth > maxWidth) || (imgHeight > maxHeight)) {
104                 double scaleX = (double)maxWidth/imgWidth;
105                 double scaleY = (double)maxHeight/imgHeight;
106                 double scaleRatio = (scaleX < scaleY) ? scaleX : scaleY;
107                 imgWidth = (int)(imgWidth * scaleRatio);
108                 imgHeight = (int)(imgHeight * scaleRatio);
109                 // scale as needed
110
tx.scale(scaleRatio, scaleRatio);
111             } else {// we dont need any transform here, just save it to file and return
112
outputStream = new FileOutputStream(thumbnailFile);
113                 outputStream.write(srcByte);
114                 return;
115             }
116
117             // create thumbnail image
118
BufferedImage JavaDoc bufferedImage = new BufferedImage JavaDoc(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);
119
120             Graphics2D g = bufferedImage.createGraphics();
121             boolean useTransform = false;
122             if (useTransform) {// use transfrom to draw
123
//log.trace("use transform");
124
g.drawImage(srcImage, tx, null);
125             } else {// use java filter
126
//log.trace("use filter");
127
Image JavaDoc scaleImage = getScaledInstance(srcImage, imgWidth, imgHeight);
128                 g.drawImage(scaleImage, 0, 0, null);
129             }
130             g.dispose();// free resource
131

132             // write it to disk
133
outputStream = new FileOutputStream(thumbnailFile);
134             JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(outputStream);
135             encoder.encode(bufferedImage);
136         } catch (IOException e) {
137             log.error("Error", e);
138             throw e;
139         } finally {// this finally is very important
140
try {
141                 inputStream.close();
142             } catch (IOException e) {/* ignore */}
143             if (outputStream != null) {
144                 try {
145                     outputStream.close();
146                 } catch (IOException e) {/* ignore */}
147             }
148         }
149     }
150
151     /**
152      * This method returns a fit-sized image for a source image,
153      * this method retains the ratio of the source image
154      */

155     public static Image JavaDoc getFitSizeImage(Image JavaDoc srcImage, int fitWidth, int fitHeight)
156         throws IOException {
157         if ((fitWidth < 100) || (fitHeight < 100)) throw new IllegalArgumentException JavaDoc("Cannot accept values < 100");
158
159         int srcWidth = srcImage.getWidth(null);//xem lai cho nay vi neu dung BufferedImage thi khong can null
160
int srcHeight = srcImage.getHeight(null);
161         //log.trace("src w = " + srcWidth + " h = " + srcHeight);
162

163         // dont need any transforms
164
if ((srcWidth == fitWidth) && (srcHeight == fitHeight)) return srcImage;
165
166         int newWidth = srcWidth;
167         int newHeight = srcHeight;
168
169         double fitRatio = (double)fitWidth / fitHeight;
170         double srcRatio = (double)srcWidth / srcHeight;
171         if (srcRatio > fitRatio) {// must cut the width of the source image
172
newWidth = (int)(srcHeight * fitRatio);
173         } else {// must cut the height of the source image
174
newHeight = (int)(srcWidth / fitRatio);
175         }
176         //log.trace("new w = " + newWidth + " h = " + newHeight);
177

178         ImageFilter cropFilter = new CropImageFilter((srcWidth-newWidth)/2, (srcHeight-newHeight)/2, newWidth, newHeight);
179         ImageProducer cropProd = new FilteredImageSource(srcImage.getSource(), cropFilter);
180         Image JavaDoc cropImage = Toolkit.getDefaultToolkit().createImage(cropProd);
181
182         Image JavaDoc retImage = new ImageIcon JavaDoc(getScaledInstance(cropImage, fitWidth, fitHeight)).getImage();
183
184         return retImage;
185     }
186
187     /**
188      * This method returns a fit-sized image for a source image,
189      * this method retains the ratio of the source image
190      */

191     public static Image JavaDoc getFitSizeImage(InputStream inputStream, int fitWidth, int fitHeight)
192         throws IOException {
193         try {
194             JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(inputStream);
195             BufferedImage JavaDoc srcImage = decoder.decodeAsBufferedImage();
196
197             return getFitSizeImage(srcImage, fitWidth, fitHeight);
198         } catch (IOException e) {
199             log.error("Cannot run getFitSizeImage", e);
200             throw e;
201         } finally {// this finally is very important
202
inputStream.close();
203         }
204     }
205
206     /**
207      * This method write the image to a stream.
208      * It auto detect the image is Image or BufferedImage.
209      * This method close the output stream before it return.
210      *
211      * @param image Image
212      * @param outputStream OutputStream
213      * @throws IOException
214      */

215     public static void writeJpegImage_Sun(Image JavaDoc image, OutputStream outputStream) throws IOException {
216
217         if (outputStream == null) {
218             return;
219         }
220
221         try {
222             BufferedImage JavaDoc bufferedImage = null;
223             if (image instanceof BufferedImage JavaDoc) {
224                 bufferedImage = (BufferedImage JavaDoc)image;
225             } else {
226                 // 30% cpu resource
227
bufferedImage = new BufferedImage JavaDoc(image.getWidth(null), image.getHeight(null),
228                                                   BufferedImage.TYPE_INT_RGB);
229
230                 // 7.5 cpu
231
Graphics2D g = bufferedImage.createGraphics();
232
233                 // 50% cpu
234
g.drawImage(image, 0, 0, null);
235                 g.dispose(); // free resource
236
}
237
238             // write it to disk
239
// 12% cpu
240
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(outputStream);
241             encoder.encode(bufferedImage);
242         } finally {
243             try {
244                 if (outputStream != null) {
245                     outputStream.close();
246                 }
247             } catch (IOException ex1) {
248             }
249         }
250     }
251
252     public static void writeJpegImage_Sun(Image JavaDoc image, String JavaDoc fileName) throws IOException {
253         OutputStream outputStream = new FileOutputStream(fileName);
254         writeJpegImage_Sun(image, outputStream);
255     }
256
257     /**
258      * This method write image to output stream, then it close the stream before return
259      *
260      * @param image Image
261      * @param outputStream OutputStream
262      * @throws IOException
263      */

264     public static void writeJpegImage_nonSun(Image JavaDoc image, OutputStream outputStream) throws IOException {
265         if (outputStream == null) {
266             return;
267         }
268         try {
269             JpegEncoder encoder = new JpegEncoder(image, 80, outputStream);
270             encoder.Compress();
271         } catch (Exception JavaDoc ex) {
272         } finally {
273             try {
274                 if (outputStream != null) {
275                     outputStream.close();
276                 }
277             } catch (IOException ex1) {
278             }
279         }
280     }
281
282     public static void writeJpegImage_nonSun(Image JavaDoc image, String JavaDoc fileName) throws IOException {
283         OutputStream outputStream = new FileOutputStream(fileName);
284
285         //this method will close the stream before it returns so no need to close
286
writeJpegImage_nonSun(image, outputStream);
287     }
288
289     public static Image JavaDoc getScaledInstance(Image JavaDoc srcImage, int width, int height) {
290         boolean useSun = false;
291         ImageFilter filter;
292         if (useSun){
293             //log.trace("use sun scalefilter");
294
filter = new java.awt.image.AreaAveragingScaleFilter JavaDoc(width, height);
295         } else {
296             //log.trace("use nguoimau scalefilter");
297
filter = new net.myvietnam.mvncore.util.AreaAveragingScaleFilter(width, height);
298         }
299         ImageProducer prod = new FilteredImageSource(srcImage.getSource(), filter);
300         Image JavaDoc newImage = Toolkit.getDefaultToolkit().createImage(prod);
301         ImageIcon JavaDoc imageIcon = new ImageIcon JavaDoc(newImage);
302         return imageIcon.getImage();
303     }
304 /*
305     public static void main(String[] args) {
306         try {
307             FileInputStream is = new FileInputStream("c:\\PUTTY.RND");
308             createThumbnail(is, "c:\\out.jpg", 120, 120);
309         } catch (Exception ex) {
310             ex.printStackTrace();
311         }
312         log.debug("done");
313     }
314 */

315 }
316
317
318
319
320
321     /**
322      * This class is taken from Pure Java AWT project
323      * and modified to exclude PJAGraphicsManager class
324      */

325   // From java.awt.image.AreaAveragingScaleFilter
326
// but without use of float type
327
class AreaAveragingScaleFilter extends ImageFilter {
328     private ColorModel rgbModel;
329     private long [] alphas;
330     private long [] reds;
331     private long [] greens;
332     private long [] blues;
333
334     protected int srcWidth;
335     protected int srcHeight;
336     private int [] srcPixels;
337     protected int destWidth;
338     protected int destHeight;
339     protected int [] destPixels;
340
341     {
342       // Test if the class java.awt.image.ColorModel can be loaded
343
//boolean classColorModelAccessible = PJAGraphicsManager.getDefaultGraphicsManager ().isClassAccessible ("java.awt.image.ColorModel");
344
// modified by minhnn
345
boolean classColorModelAccessible = isClassAccessible ("java.awt.image.ColorModel");
346       if (classColorModelAccessible)
347         rgbModel = ColorModel.getRGBdefault ();
348     }
349
350     /**
351      * Constructs an AreaAveragingScaleFilter that scales the pixels from
352      * its source Image as specified by the width and height parameters.
353      * @param width the target width to scale the image
354      * @param height the target height to scale the image
355      */

356     public AreaAveragingScaleFilter(int width, int height) {
357         destWidth = width;
358         destHeight = height;
359     }
360
361     public void setDimensions (int w, int h)
362     {
363       srcWidth = w;
364       srcHeight = h;
365       if (destWidth < 0)
366       {
367         if (destHeight < 0)
368         {
369           destWidth = srcWidth;
370           destHeight = srcHeight;
371         }
372         else
373           destWidth = srcWidth * destHeight / srcHeight;
374       }
375       else if (destHeight < 0)
376         destHeight = srcHeight * destWidth / srcWidth;
377
378       consumer.setDimensions (destWidth, destHeight);
379     }
380
381     public void setHints (int hints)
382     {
383       // Images are sent entire frame by entire frame
384
consumer.setHints ( (hints & (SINGLEPASS | SINGLEFRAME))
385                          | TOPDOWNLEFTRIGHT);
386     }
387
388     public void imageComplete (int status)
389     {
390       if ( status == STATICIMAGEDONE
391           || status == SINGLEFRAMEDONE)
392         accumPixels (0, 0, srcWidth, srcHeight, rgbModel, srcPixels, 0, srcWidth);
393       consumer.imageComplete (status);
394     }
395
396     public void setPixels (int x, int y, int width, int height,
397                            ColorModel model, byte pixels [], int offset, int scansize)
398     {
399       // Store pixels in srcPixels array
400
if (srcPixels == null)
401         srcPixels = new int [srcWidth * srcHeight];
402       for (int row = 0, destRow = y * srcWidth;
403            row < height;
404            row++, destRow += srcWidth)
405       {
406         int rowOff = offset + row * scansize;
407         for (int col = 0; col < width; col++)
408           // v1.2 : Added & 0xFF to disable sign bit
409
srcPixels [destRow + x + col] = model.getRGB (pixels [rowOff + col] & 0xFF);
410       }
411     }
412
413     public void setPixels (int x, int y, int width, int height,
414                    ColorModel model, int pixels[], int offset, int scansize)
415     {
416       // Store pixels in srcPixels array
417
if (srcPixels == null)
418         srcPixels = new int [srcWidth * srcHeight];
419       for (int row = 0, destRow = y * srcWidth;
420            row < height;
421            row++, destRow += srcWidth)
422       {
423         int rowOff = offset + row * scansize;
424         for (int col = 0; col < width; col++)
425           // If model == null, consider it's the default RGB model
426
srcPixels [destRow + x + col] = model == null
427                                                      ? pixels [rowOff + col]
428                                                      : model.getRGB (pixels [rowOff + col]);
429       }
430     }
431
432     private int [] calcRow ()
433     {
434       long mult = (srcWidth * srcHeight) << 32;
435       if (destPixels == null)
436         destPixels = new int [destWidth];
437
438       for (int x = 0; x < destWidth; x++)
439       {
440         int a = (int)roundDiv (alphas [x], mult);
441         int r = (int)roundDiv (reds [x], mult);
442         int g = (int)roundDiv (greens [x], mult);
443         int b = (int)roundDiv (blues [x], mult);
444         a = Math.max (Math.min (a, 255), 0);
445         r = Math.max (Math.min (r, 255), 0);
446         g = Math.max (Math.min (g, 255), 0);
447         b = Math.max (Math.min (b, 255), 0);
448         destPixels [x] = (a << 24 | r << 16 | g << 8 | b);
449       }
450
451       return destPixels;
452     }
453
454     private void accumPixels (int x, int y, int w, int h,
455                               ColorModel model, int [] pixels, int off,
456                               int scansize)
457     {
458       reds = new long [destWidth];
459       greens = new long [destWidth];
460       blues = new long [destWidth];
461       alphas = new long [destWidth];
462
463       int sy = y;
464       int syrem = destHeight;
465       int dy = 0;
466       int dyrem = 0;
467       while (sy < y + h)
468       {
469         if (dyrem == 0)
470         {
471           for (int i = 0; i < destWidth; i++)
472             alphas [i] =
473             reds [i] =
474             greens [i] =
475             blues [i] = 0;
476
477           dyrem = srcHeight;
478         }
479
480         int amty = Math.min (syrem, dyrem);
481         int sx = 0;
482         int dx = 0;
483         int sxrem = 0;
484         int dxrem = srcWidth;
485         int a = 0,
486             r = 0,
487             g = 0,
488             b = 0;
489         while (sx < w)
490         {
491           if (sxrem == 0)
492           {
493             sxrem = destWidth;
494             int rgb = pixels [off + sx];
495             a = rgb >>> 24;
496             r = (rgb >> 16) & 0xFF;
497             g = (rgb >> 8) & 0xFF;
498             b = rgb & 0xFF;
499           }
500
501           int amtx = Math.min (sxrem, dxrem);
502           long mult = (amtx * amty) << 32;
503           alphas [dx] += mult * a;
504           reds [dx] += mult * r;
505           greens [dx] += mult * g;
506           blues [dx] += mult * b;
507
508           if ((sxrem -= amtx) == 0)
509             sx++;
510
511           if ((dxrem -= amtx) == 0)
512           {
513             dx++;
514             dxrem = srcWidth;
515           }
516         }
517
518         if ((dyrem -= amty) == 0)
519         {
520           int outpix [] = calcRow ();
521           do
522           {
523             consumer.setPixels (0, dy, destWidth, 1,
524                                 rgbModel, outpix, 0, destWidth);
525             dy++;
526           }
527           while ((syrem -= amty) >= amty && amty == srcHeight);
528         }
529         else
530           syrem -= amty;
531
532         if (syrem == 0)
533         {
534           syrem = destHeight;
535           sy++;
536           off += scansize;
537         }
538       }
539     }
540     ////////////////
541
// util method
542
////////////////
543
// util method
544
/**
545      * Returns the rounded result of <code>dividend / divisor</code>, avoiding the use of floating
546      * point operation (returns the same as <code>Math.round((float)dividend / divisor)</code>).
547      * @param dividend A <code>int</code> number to divide.
548      * @param divisor A <code>int</code> divisor.
549      * @return dividend / divisor rounded to the closest <code>int</code> integer.
550      */

551     public static int roundDiv(int dividend, int divisor) {
552         final int remainder = dividend % divisor;
553         if (Math.abs(remainder) * 2 <= Math.abs(divisor))
554             return dividend / divisor;
555         else
556         if (dividend * divisor < 0)
557             return dividend / divisor - 1;
558         else
559             return dividend / divisor + 1;
560     }
561
562     /**
563      * Returns the rounded result of <code>dividend / divisor</code>, avoiding the use of floating
564      * point operation (returns the same as <code>Math.round((double)dividend / divisor)</code>).
565      * @param dividend A <code>long</code> number to divide.
566      * @param divisor A <code>long</code> divisor.
567      * @return dividend / divisor rounded to the closest <code>long</code> integer.
568      */

569     public static long roundDiv (long dividend, long divisor) {
570         final long remainder = dividend % divisor;
571         if (Math.abs (remainder) * 2 <= Math.abs (divisor))
572           return dividend / divisor;
573         else
574           if (dividend * divisor < 0)
575             return dividend / divisor - 1;
576           else
577             return dividend / divisor + 1;
578     }
579
580     /**
581      * Returns <code>true</code> if it successes to load the class <code>className</code>.
582      * If security manager is too restictive, it is possible that the classes <code>java.awt.Color</code>,
583      * <code>java.awt.Rectangle</code>, <code>java.awt.Font</code>, <code>java.awt.FontMetrics</code>
584      * and <code>java.awt.image.ColorModel</code> (and also <code>java.awt.Dimension</code> and other classes
585      * not required by PJA classes) can't be loaded because they need either the class <code>java.awt.Toolkit</code>
586      * or the library awt to be accessible to call their <code>initIDs ()</code> native method.
587      * @param className the fully qualified class name.
588      * @return <code>true</code> if <code>java.awt.Toolkit</code> class could be loaded.
589      */

590    public static boolean isClassAccessible(String JavaDoc className) {
591        // Test if the class className can be loaded
592
try {
593            Class.forName(className);
594            // Class can be loaded
595
return true;
596        } catch (ClassNotFoundException JavaDoc e) {} catch (LinkageError JavaDoc error) {} // Thrown by some AWT classes which require awt library in static initializer.
597

598        return false;
599    }
600 }
601
Popular Tags