Clover Coverage Report - Atlassian Core
Coverage timestamp: Sun Nov 30 2008 18:33:35 CST
97   374   39   4.62
16   275   0.4   10.5
21     1.86  
2    
 
 
  Thumber       Line # 35 91 34 0% 0.0
  Thumber.WidthHeightHelper       Line # 343 6 5 0% 0.0
 
No Tests
 
1    package com.atlassian.core.util.thumbnail;
2   
3    import com.atlassian.core.util.ImageInfo;
4    import com.sun.image.codec.jpeg.JPEGCodec;
5    import com.sun.image.codec.jpeg.JPEGEncodeParam;
6    import com.sun.image.codec.jpeg.JPEGImageEncoder;
7   
8    import org.apache.log4j.Category;
9    import org.apache.commons.io.IOUtils;
10   
11    import java.awt.Graphics;
12    import java.awt.Image;
13    import java.awt.Toolkit;
14    import java.awt.image.AreaAveragingScaleFilter;
15    import java.awt.image.BufferedImage;
16    import java.awt.image.FilteredImageSource;
17    import java.awt.image.ImageProducer;
18    import java.io.BufferedOutputStream;
19    import java.io.File;
20    import java.io.FileInputStream;
21    import java.io.FileNotFoundException;
22    import java.io.FileOutputStream;
23    import java.io.IOException;
24    import java.io.InputStream;
25    import java.io.OutputStream;
26    import java.net.MalformedURLException;
27    import java.util.ArrayList;
28    import java.util.List;
29   
30    import javax.imageio.ImageIO;
31   
32    /**
33    * A class to create and retrieve thumbnail of images.
34    */
 
35    public class Thumber
36    {
37    private static final Category log = Category.getInstance(Thumber.class);
38    private ImageInfo imageInfo = new ImageInfo();
39   
40    public static final String JPEG_MIME_TYPE = "image/jpeg";
41    public static final String PNG_MIME_TYPE = "image/png";
42    public static final String GIF_MIME_TYPE = "image/gif";
43   
44    public static final List THUMBNAIL_MIME_TYPES = new ArrayList(3);
45   
46    public static final String JPEG_FORMAT = "JPEG";
47    public static final String PNG_FORMAT = "PNG";
48    public static final String GIF_FORMAT = "GIF";
49   
50    public static final List THUMBNAIL_FORMATS = new ArrayList(3);
51   
 
52  0 toggle static
53    {
54  0 THUMBNAIL_MIME_TYPES.add(JPEG_MIME_TYPE);
55  0 THUMBNAIL_MIME_TYPES.add(PNG_MIME_TYPE);
56  0 THUMBNAIL_MIME_TYPES.add(GIF_MIME_TYPE);
57  0 THUMBNAIL_FORMATS.add(JPEG_FORMAT);
58  0 THUMBNAIL_FORMATS.add(PNG_FORMAT);
59  0 THUMBNAIL_FORMATS.add(GIF_FORMAT);
60    }
61   
62    private float encodingQuality = 0.80f; // default to 0.80f, seems reasonable enough, and still provides good result.
63   
64    /**
65    * @return True if the AWT default toolkit exists, false (with an error logged) if it does not.
66    */
 
67  0 toggle public boolean checkToolkit()
68    {
69  0 try
70    {
71  0 Toolkit.getDefaultToolkit();
72    }
73    catch (Throwable e)
74    {
75  0 log.error("Unable to acquire AWT default toolkit - thumbnails will not be displayed. Check DISPLAY variable or use setting -Djava.awt.headless=true.", e);
76  0 return false;
77    }
78  0 return true;
79    }
80   
81   
82    /**
83    * Retrieves an existing thumbnail, or creates a new one.
84    *
85    * @param originalFile The file which is being thumbnailed.
86    * @param thumbnailFile The location of the existing thumbnail (if it exists), or the location to create a new thumbnail.
87    * @param maxWidth The max width of the thumbnail.
88    * @param maxHeight The max height of the thumbnail.
89    * @param thumbnailId
90    * @return
91    * @throws MalformedURLException
92    */
 
93  0 toggle public Thumbnail retrieveOrCreateThumbNail(File originalFile, File thumbnailFile, int maxWidth, int maxHeight, long thumbnailId) throws MalformedURLException
94    {
95  0 FileInputStream originalFileStream = null;
96  0 try
97    {
98  0 originalFileStream = new FileInputStream(originalFile);
99  0 return retrieveOrCreateThumbNail(originalFileStream, originalFile.getName(), thumbnailFile, maxWidth, maxHeight, thumbnailId);
100    }
101    catch (FileNotFoundException e)
102    {
103  0 log.error("Unable to create thumbnail: file not found: " + originalFile.getAbsolutePath());
104    }
105    finally
106    {
107  0 try
108    {
109  0 originalFileStream.close();
110    }
111    catch (IOException e)
112    {
113  0 log.warn(e, e);
114    }
115    }
116   
117  0 return null;
118    }
119   
120    // All thumbnail images are stored in JPEG format on disk.
 
121  0 toggle public void storeImage(BufferedImage scaledImage, File file) throws FileNotFoundException
122    {
123  0 OutputStream fout = null;
124   
125  0 try
126    {
127  0 fout = new BufferedOutputStream(new FileOutputStream(file));
128  0 JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(fout);
129  0 JPEGEncodeParam encodeParams = encoder.getDefaultJPEGEncodeParam(scaledImage);
130  0 encodeParams.setQuality(encodingQuality, false);
131  0 encoder.setJPEGEncodeParam(encodeParams);
132  0 encoder.encode(scaledImage);
133    }
134    catch (IOException e)
135    {
136  0 log.error("Error encoding the thumbnail image", e);
137    }
138    finally
139    {
140  0 IOUtils.closeQuietly(fout);
141    }
142    }
143   
 
144  0 toggle public BufferedImage scaleImage(Image imageToScale, WidthHeightHelper newDimensions)
145    {
146    // If the original image is an instance of BufferedImage, we need to make sure
147    // that it is an sRGB image. If it is not, we need to convert it before scaling it
148    // as we run into these issue otherwise:
149    // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4886071
150    // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4705399
151   
152  0 if (imageToScale instanceof BufferedImage)
153    {
154  0 BufferedImage bufferedImage = (BufferedImage) imageToScale;
155  0 if (!bufferedImage.getColorModel().getColorSpace().isCS_sRGB()) {
156  0 BufferedImage sRGBImage = new BufferedImage(bufferedImage.getWidth(), bufferedImage.getHeight(), BufferedImage.TYPE_INT_RGB);
157  0 Graphics g = sRGBImage.getGraphics();
158  0 g.drawImage(bufferedImage, 0, 0, null);
159  0 g.dispose();
160  0 imageToScale = sRGBImage;
161    }
162    }
163   
164  0 AreaAveragingScaleFilter scaleFilter =
165    new AreaAveragingScaleFilter(newDimensions.getWidth(), newDimensions.getHeight());
166  0 ImageProducer producer = new FilteredImageSource(imageToScale.getSource(),
167    scaleFilter);
168   
169  0 SimpleImageConsumer generator = new SimpleImageConsumer();
170  0 producer.startProduction(generator);
171  0 BufferedImage scaled = generator.getImage();
172  0 scaled.flush();
173  0 return scaled;
174    }
175   
176    /**
177    * Need to pass filename in as we cannot get the filename from the stream that is passed in
178    * @param originalFileStream
179    * @param fileName
180    * @param thumbnailFile
181    * @param maxWidth
182    * @param maxHeight
183    * @param thumbnailId
184    * @return a Thumbnail instance or null if an error occured
185    * @throws MalformedURLException
186    */
 
187  0 toggle public Thumbnail retrieveOrCreateThumbNail(InputStream originalFileStream, String fileName, File thumbnailFile, int maxWidth, int maxHeight, long thumbnailId) throws MalformedURLException
188    {
189  0 Thumbnail thumbnail = null;
190  0 try
191    {
192  0 thumbnail = getThumbnail(thumbnailFile, fileName, thumbnailId);
193    }
194    catch (IOException e)
195    {
196  0 log.error("Unable to get thumbnail image for id " + thumbnailId, e);
197  0 return null;
198    }
199   
200  0 if (thumbnail == null)
201    {
202  0 try
203    {
204  0 thumbnail = createThumbnail(originalFileStream, thumbnailFile, maxWidth, maxHeight, thumbnailId, fileName);
205    }
206    catch (IOException e)
207    {
208  0 log.error("Unable to create thumbnail image for id " + thumbnailId, e);
209  0 return null;
210    }
211    }
212   
213  0 return thumbnail;
214    }
215   
216    // PRIVATE METHODS -------------------------------------------------------------------------------------------
 
217  0 toggle private BufferedImage scaleImage(Image originalImage, int maxWidth, int maxHeight)
218    {
219  0 return scaleImage(originalImage, determineScaleSize(maxWidth, maxHeight, originalImage));
220    }
221   
 
222  0 toggle private WidthHeightHelper determineScaleSize(int maxWidth, int maxHeight, Image image)
223    {
224  0 return determineScaleSize(maxWidth, maxHeight, image.getWidth(null), image.getHeight(null));
225    }
226   
 
227  0 toggle private Thumbnail createThumbnail(InputStream originalFile, File thumbnailFile, int maxWidth, int maxHeight, long thumbId, String fileName) throws IOException, FileNotFoundException
228    {
229    // Load original image.
230  0 Image originalImage = getImage(originalFile);
231    // Create scaled buffered image from original image.
232  0 BufferedImage scaledImage = scaleImage(originalImage, maxWidth, maxHeight);
233   
234  0 int height = scaledImage.getHeight();
235  0 int width = scaledImage.getWidth();
236   
237  0 storeImage(scaledImage, thumbnailFile);
238   
239  0 return new Thumbnail(height, width, fileName, thumbId);
240    }
241   
 
242  0 toggle private Thumbnail getThumbnail(File thumbnailFile, String filename, long thumbId) throws IOException
243    {
244  0 if (thumbnailFile.exists())
245    {
246  0 final Image thumbImage = getImage(thumbnailFile);
247  0 return new Thumbnail(thumbImage.getHeight(null), thumbImage.getWidth(null), filename, thumbId);
248    }
249  0 return null;
250    }
251   
252    /**
253    * @param file
254    * @return An Image object or null if there was no suitable ImageReader for the given data
255    * @throws IOException
256    */
 
257  0 toggle public Image getImage(File file) throws IOException
258    {
259  0 return ImageIO.read(file);
260    }
261   
262    /**
263    *
264    * @param is
265    * @return An Image object or null if there was no suitable ImageReader for the given data
266    * @throws IOException
267    */
 
268  0 toggle public Image getImage(InputStream is) throws IOException
269    {
270  0 return ImageIO.read(is);
271    }
272   
273    /**
274    * Set the default encoding quality used by the thumber to encode jpegs.
275    * @param f
276    */
 
277  0 toggle public void setEncodingQuality(float f)
278    {
279  0 if (f > 1.0f || f < 0.0f)
280    {
281  0 throw new IllegalArgumentException("Invalid quality setting '"+f+"', value must be between 0 and 1. ");
282    }
283  0 encodingQuality = f;
284    }
285   
 
286  0 toggle public WidthHeightHelper determineScaleSize(int maxWidth, int maxHeight, int imageWidth, int imageHeight)
287    {
288  0 if (maxHeight > imageHeight && maxWidth > imageWidth)
289    {
290  0 return new Thumber.WidthHeightHelper(imageWidth, imageHeight);
291    }
292    // Determine scale size.
293    // Retain original image proportions with scaled image.
294  0 double thumbRatio = (double) maxWidth / (double) maxHeight;
295   
296  0 double imageRatio = (double) imageWidth / (double) imageHeight;
297   
298  0 if (thumbRatio < imageRatio)
299    {
300  0 return new Thumber.WidthHeightHelper(maxWidth, (int) (maxWidth / imageRatio));
301    }
302    else
303    {
304  0 return new Thumber.WidthHeightHelper((int) (maxHeight * imageRatio), maxHeight);
305    }
306    }
307   
 
308  0 toggle public boolean isFileSupportedImage(File file)
309    {
310  0 try
311    {
312  0 return isFileSupportedImage(new FileInputStream(file));
313    }
314    catch (FileNotFoundException e)
315    {
316  0 return false;
317    }
318    }
319   
 
320  0 toggle public boolean isFileSupportedImage(InputStream inputStream)
321    {
322  0 try
323    {
324  0 imageInfo.setInput(inputStream);
325  0 imageInfo.check();
326  0 return THUMBNAIL_FORMATS.contains(imageInfo.getFormatName());
327    }
328    finally
329    {
330  0 try
331    {
332  0 if (inputStream != null)
333    {
334  0 inputStream.close();
335    }
336    } catch (Exception e)
337    {
338  0 log.error(e, e);
339    }
340    }
341    }
342   
 
343    public static class WidthHeightHelper
344    {
345    private int width;
346    private int height;
347   
 
348  0 toggle public WidthHeightHelper(int width, int height)
349    {
350  0 this.width = width;
351  0 this.height = height;
352    }
353   
 
354  0 toggle public int getWidth()
355    {
356  0 return width;
357    }
358   
 
359  0 toggle public void setWidth(int width)
360    {
361  0 this.width = width;
362    }
363   
 
364  0 toggle public int getHeight()
365    {
366  0 return height;
367    }
368   
 
369  0 toggle public void setHeight(int height)
370    {
371  0 this.height = height;
372    }
373    }
374    }