001: /*
002: * Copyright (C) 2006 Methodhead Software LLC. All rights reserved.
003: *
004: * This file is part of TransferCM.
005: *
006: * TransferCM is free software; you can redistribute it and/or modify it under the
007: * terms of the GNU General Public License as published by the Free Software
008: * Foundation; either version 2 of the License, or (at your option) any later
009: * version.
010: *
011: * TransferCM is distributed in the hope that it will be useful, but WITHOUT ANY
012: * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
013: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
014: * details.
015: *
016: * You should have received a copy of the GNU General Public License along with
017: * TransferCM; if not, write to the Free Software Foundation, Inc., 51 Franklin St,
018: * Fifth Floor, Boston, MA 02110-1301 USA
019: */
020:
021: package com.methodhead.shim;
022:
023: import com.sun.image.codec.jpeg.JPEGEncodeParam;
024: import com.sun.image.codec.jpeg.JPEGCodec;
025: import com.sun.image.codec.jpeg.JPEGImageEncoder;
026: import java.io.BufferedOutputStream;
027: import java.awt.image.BufferedImage;
028: import java.awt.Image;
029: import java.awt.Container;
030: import java.awt.Graphics2D;
031: import java.awt.RenderingHints;
032: import java.awt.MediaTracker;
033: import java.awt.Toolkit;
034: import java.awt.Color;
035: import java.io.FileOutputStream;
036: import java.io.File;
037: import org.apache.log4j.Logger;
038: import org.apache.commons.lang.exception.ExceptionUtils;
039:
040: /**
041: * Creates thumbnails of popular image formats.
042: */
043: public class Thumbnailer {
044:
045: // constructors /////////////////////////////////////////////////////////////
046:
047: // constants ////////////////////////////////////////////////////////////////
048:
049: /**
050: * Maximum width of the thumbnail.
051: */
052: public static final int THUMB_HEIGHT = 50;
053:
054: /**
055: * Maximum height of the thumbnail.
056: */
057: public static final int THUMB_WIDTH = 50;
058:
059: /**
060: * Thumbnail quality in a range between 0.0 and 1.0.
061: */
062: public static final float THUMB_QUALITY = 1.0f;
063:
064: // classes //////////////////////////////////////////////////////////////////
065:
066: // methods //////////////////////////////////////////////////////////////////
067:
068: /**
069: * Creates a thumbnail of <tt>src</tt> in the file specified by
070: * <tt>dest</tt>. The thumbnail is in JPEG format. It maintains the aspect
071: * ratio of the source image and is contrained by {@link #THUMB_WIDTH
072: * THUMB_WIDTH} and {@link #THUMB_HEIGHT THUMB_HEIGHT}.
073: */
074: public static void thumbnail(File src, File dest) {
075:
076: logger_.debug("thumbnail(): Thumbnailing " + src + " to "
077: + dest);
078:
079: try {
080: if (!src.exists() || !src.isFile())
081: throw new ShimException(
082: "File does not exist or is not a file \"" + src
083: + "\"");
084:
085: //
086: // load image
087: //
088: Image image = Toolkit.getDefaultToolkit().getImage(
089: src.getAbsolutePath());
090: MediaTracker mediaTracker = new MediaTracker(
091: new Container());
092: mediaTracker.addImage(image, 0);
093: mediaTracker.waitForID(0);
094:
095: //
096: // calculate width and height
097: //
098: int thumbWidth = THUMB_HEIGHT;
099: int thumbHeight = THUMB_WIDTH;
100: double thumbRatio = (double) thumbWidth
101: / (double) thumbHeight;
102:
103: int imageWidth = image.getWidth(null);
104: int imageHeight = image.getHeight(null);
105:
106: //
107: // already smaller than the thumb?
108: //
109: if ((imageHeight <= THUMB_HEIGHT)
110: && (imageWidth <= THUMB_WIDTH)) {
111: thumbHeight = imageHeight;
112: thumbWidth = imageWidth;
113: }
114:
115: //
116: // scale the image to right size
117: //
118: else {
119: double imageRatio = (double) imageWidth
120: / (double) imageHeight;
121:
122: if (thumbRatio < imageRatio) {
123: thumbHeight = (int) (thumbWidth / imageRatio);
124: } else {
125: thumbWidth = (int) (thumbHeight * imageRatio);
126: }
127: }
128:
129: //
130: // make sure we're at least a pixel wide or tall
131: //
132: if (thumbHeight == 0)
133: thumbHeight = 1;
134:
135: if (thumbWidth == 0)
136: thumbWidth = 1;
137:
138: //
139: // draw original image to thumbnail image object and
140: // scale it to the new size on-the-fly
141: //
142: BufferedImage thumbImage = new BufferedImage(thumbWidth,
143: thumbHeight, BufferedImage.TYPE_INT_RGB);
144:
145: Graphics2D graphics2D = thumbImage.createGraphics();
146: graphics2D.setRenderingHint(
147: RenderingHints.KEY_INTERPOLATION,
148: RenderingHints.VALUE_INTERPOLATION_BILINEAR);
149: graphics2D.setRenderingHint(
150: RenderingHints.KEY_ALPHA_INTERPOLATION,
151: RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
152: graphics2D.drawImage(image, 0, 0, thumbWidth, thumbHeight,
153: Color.WHITE, null);
154:
155: //
156: // save the thumbnail
157: //
158: BufferedOutputStream out = new BufferedOutputStream(
159: new FileOutputStream(dest));
160: JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
161: JPEGEncodeParam param = encoder
162: .getDefaultJPEGEncodeParam(thumbImage);
163: param.setQuality(THUMB_QUALITY, false);
164: encoder.setJPEGEncodeParam(param);
165: encoder.encode(thumbImage);
166: out.close();
167: }
168:
169: //
170: // these exceptions we know about
171: //
172: catch (java.lang.InterruptedException e) {
173: throw new ShimException("Thumbnailing " + src + " to "
174: + dest + ". " + ExceptionUtils.getStackTrace(e));
175: } catch (java.io.FileNotFoundException e) {
176: throw new ShimException("Thumbnailing " + src + " to "
177: + dest + ". " + ExceptionUtils.getStackTrace(e));
178: } catch (java.io.IOException e) {
179: throw new ShimException("Thumbnailing " + src + " to "
180: + dest + ". " + ExceptionUtils.getStackTrace(e));
181: }
182: }
183:
184: /**
185: * Creates thumbnails for any JPG, GIF, and PNG file in <tt>src</tt> in
186: * <tt>dest</tt>. If a thumbnail already exists in <tt>dest</tt>, it is
187: * updated if the file in <tt>src</tt> is newer. The thumbnails are JPEG's,
188: * so ".jpg" is appended to the filename of each thumbnail. Some images
189: * cannot be processed properly and the resulting IllegalArgumentException is
190: * caught and logged, but otherwise ignored; no thumbnail file is created in
191: * this case.
192: */
193: public void syncDir(File src, File dest) {
194:
195: logger_.debug("syncDir(): Syncing " + src + " to " + dest);
196:
197: if (!src.exists() || !src.isDirectory())
198: throw new ShimException("Invalid source directory \"" + src
199: + "\"");
200:
201: if (!dest.exists() || !dest.isDirectory())
202: throw new ShimException("Invalid dest directory \"" + dest
203: + "\"");
204:
205: File[] files = src.listFiles();
206: for (int i = 0; i < files.length; i++) {
207: String fileName = files[i].getName().toLowerCase();
208:
209: if (!fileName.endsWith(".jpg")
210: && !fileName.endsWith(".jpeg")
211: && !fileName.endsWith(".png")
212: && !fileName.endsWith(".gif"))
213: continue;
214:
215: File f = new File(dest, files[i].getName() + ".jpg");
216:
217: if (f.exists() && f.isFile()
218: && (files[i].lastModified() < f.lastModified()))
219: continue;
220:
221: try {
222: thumbnail(files[i], f);
223: } catch (IllegalArgumentException e) {
224:
225: //
226: // we know through practice that bad images cause
227: // IllegalArgumentExceptions; we ignore them
228: //
229: logger_.warn("Couldn't thumbnail " + files[i] + ". "
230: + e.getMessage());
231: }
232: }
233: }
234:
235: // properties ///////////////////////////////////////////////////////////////
236:
237: // attributes ///////////////////////////////////////////////////////////////
238:
239: //
240: // arbitrarily trying out a more aggresive logging effort
241: //
242: private static Logger logger_ = Logger.getLogger(Thumbnailer.class);
243: }
|