package net.coobird.thumbnailator.resizers; import java.awt.AlphaComposite; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.util.Collections; import java.util.Map; /** * A {@link Resizer} which performs resizing operations by using * progressive bilinear scaling. *

* The resizing technique used in this class is based on the technique * discussed in Chapter 4: Images of * Filthy Rich Clients * by Chet Haase and Romain Guy. *

* The actual implemenation of the technique is independent of the code which * is provided in the book. * * @author coobird * */ public class ProgressiveBilinearResizer extends AbstractResizer { /** * Instantiates a {@link ProgressiveBilinearResizer} with default * rendering hints. */ public ProgressiveBilinearResizer() { this(Collections.emptyMap()); } /** * Instantiates a {@link ProgressiveBilinearResizer} with the specified * rendering hints. * * @param hints Additional rendering hints to apply. */ public ProgressiveBilinearResizer(Map hints) { super(RenderingHints.VALUE_INTERPOLATION_BILINEAR, hints); } /** * Resizes an image using the progressive bilinear scaling technique. *

* If the source and/or destination image is {@code null}, then a * {@link NullPointerException} will be thrown. * * @param srcImage The source image. * @param destImage The destination image. * * @throws NullPointerException When the source and/or the destination * image is {@code null}. */ @Override public void resize(BufferedImage srcImage, BufferedImage destImage) throws NullPointerException { super.performChecks(srcImage, destImage); int currentWidth = srcImage.getWidth(); int currentHeight = srcImage.getHeight(); final int targetWidth = destImage.getWidth(); final int targetHeight = destImage.getHeight(); // If multi-step downscaling is not required, perform one-step. if ((targetWidth * 2 >= currentWidth) && (targetHeight * 2 >= currentHeight)) { Graphics2D g = destImage.createGraphics(); g.drawImage(srcImage, 0, 0, targetWidth, targetHeight, null); g.dispose(); return; } // Temporary image used for in-place resizing of image. BufferedImage tempImage = new BufferedImage( currentWidth, currentHeight, destImage.getType() ); Graphics2D g = tempImage.createGraphics(); g.setRenderingHints(RENDERING_HINTS); g.setComposite(AlphaComposite.Src); /* * Determine the size of the first resize step should be. * 1) Beginning from the target size * 2) Increase each dimension by 2 * 3) Until reaching the original size */ int startWidth = targetWidth; int startHeight = targetHeight; while (startWidth < currentWidth && startHeight < currentHeight) { startWidth *= 2; startHeight *= 2; } currentWidth = startWidth / 2; currentHeight = startHeight / 2; // Perform first resize step. g.drawImage(srcImage, 0, 0, currentWidth, currentHeight, null); // Perform an in-place progressive bilinear resize. while ( (currentWidth >= targetWidth * 2) && (currentHeight >= targetHeight * 2) ) { currentWidth /= 2; currentHeight /= 2; if (currentWidth < targetWidth) { currentWidth = targetWidth; } if (currentHeight < targetHeight) { currentHeight = targetHeight; } g.drawImage( tempImage, 0, 0, currentWidth, currentHeight, 0, 0, currentWidth * 2, currentHeight * 2, null ); } g.dispose(); // Draw the resized image onto the destination image. Graphics2D destg = destImage.createGraphics(); destg.drawImage(tempImage, 0, 0, targetWidth, targetHeight, 0, 0, currentWidth, currentHeight, null); destg.dispose(); } }