View Javadoc

1   package com.atlassian.core.util.thumbnail;
2   
3   import com.atlassian.core.util.ReusableBufferedInputStream;
4   import com.atlassian.core.util.thumbnail.loader.ImageFactory;
5   
6   import java.awt.*;
7   import java.awt.image.BufferedImage;
8   import java.io.IOException;
9   
10  public class MemoryImageScale implements ImageScaler
11  {
12      private final DimensionsHelper dimensionsHelper;
13      private final ImageFactory imageFactory;
14  
15      public MemoryImageScale(DimensionsHelper dimensionsHelper, ImageFactory imageFactory)
16      {
17          this.dimensionsHelper = dimensionsHelper;
18          this.imageFactory = imageFactory;
19      }
20  
21      @Override
22      public BufferedImage scaleImage(int maxWidth, int maxHeight, ReusableBufferedInputStream imageStream) throws IOException
23      {
24          final BufferedImage imageToScale = imageFactory.loadImage(imageStream);
25          final Dimensions newDimensions = dimensionsHelper.determineScaledDimensions(maxWidth, maxHeight, imageToScale);
26          return scaleImage(imageToScale, newDimensions);
27      }
28  
29      public static BufferedImage scaleImage(final BufferedImage imageToScale, final Thumber.WidthHeightHelper newDimensions)
30      {
31          return scaleImage(imageToScale, new Dimensions(newDimensions.getWidth(), newDimensions.getHeight()));
32      }
33  
34      public static BufferedImage scaleImage(final BufferedImage imageToScale, final Dimensions newDimensions)
35      {
36          if (newDimensions.getWidth() > imageToScale.getWidth() || newDimensions.getHeight() > imageToScale.getHeight())
37          {
38              return getScaledInstance(imageToScale, newDimensions.getWidth(), newDimensions.getHeight(),
39                      RenderingHints.VALUE_INTERPOLATION_BICUBIC, false);
40          }
41          else
42          {
43              return getScaledInstance(imageToScale, newDimensions.getWidth(), newDimensions.getHeight(),
44                      RenderingHints.VALUE_INTERPOLATION_BILINEAR, true);
45          }
46      }
47  
48      /**
49       * Convenience method that returns a scaled instance of the provided {@code BufferedImage}.
50       * <p/>
51       * Borrowed from http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html
52       * Warning: this algorith enters endless loop, when target size is bigger than image size and higherQuality is true
53       *
54       * @param image         the original image to be scaled
55       * @param targetWidth   the desired width of the scaled instance, in pixels
56       * @param targetHeight  the desired height of the scaled instance, in pixels
57       * @param hint          one of the rendering hints that corresponds to {@code RenderingHints.KEY_INTERPOLATION} (e.g. {@code
58       *                      RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR}, {@code RenderingHints.VALUE_INTERPOLATION_BILINEAR}, {@code
59       *                      RenderingHints.VALUE_INTERPOLATION_BICUBIC})
60       * @param higherQuality if true, this method will use a multi-step scaling technique that provides higher quality
61       *                      than the usual one-step technique (only useful in downscaling cases, where {@code targetWidth} or {@code
62       *                      targetHeight} is smaller than the original dimensions, and generally only when the {@code BILINEAR} hint is
63       *                      specified)
64       * @return a scaled version of the original {@code BufferedImage}
65       */
66      public static BufferedImage getScaledInstance(BufferedImage image,
67                                              int targetWidth,
68                                              int targetHeight,
69                                              Object hint,
70                                              boolean higherQuality)
71      {
72          int type = (image.getTransparency() == Transparency.OPAQUE) ?
73                  BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
74          int w, h;
75          if (higherQuality)
76          {
77              // Use multi-step technique: start with original size, then
78              // scale down in multiple passes with drawImage()
79              // until the target size is reached
80              w = image.getWidth();
81              h = image.getHeight();
82          }
83          else
84          {
85              // Use one-step technique: scale directly from original
86              // size to target size with a single drawImage() callscaleImage
87              w = targetWidth;
88              h = targetHeight;
89          }
90  
91          do
92          {
93              if (higherQuality && w > targetWidth)
94              {
95                  w /= 2;
96                  if (w < targetWidth)
97                  {
98                      w = targetWidth;
99                  }
100             }
101 
102             if (higherQuality && h > targetHeight)
103             {
104                 h /= 2;
105                 if (h < targetHeight)
106                 {
107                     h = targetHeight;
108                 }
109             }
110 
111             BufferedImage tmp = new BufferedImage(w, h, type);
112             Graphics2D g2 = tmp.createGraphics();
113             g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
114             g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
115             g2.setComposite(AlphaComposite.SrcOver);
116             g2.drawImage(image, 0, 0, w, h, null);
117             g2.dispose();
118 
119             image = tmp;
120         }
121         while (w != targetWidth || h != targetHeight);
122 
123         return image;
124     }
125 }