package at.favre.tools.dconvert.converters.scaling; import net.coobird.thumbnailator.makers.FixedSizeThumbnailMaker; import net.coobird.thumbnailator.resizers.AbstractResizer; import java.awt.*; import java.awt.image.BufferedImage; import java.util.Collections; import java.util.Map; /** * Algorithms from Thumbnailnator */ public class ThumbnailnatorProgressiveAlgorithm implements ScaleAlgorithm { private Object interpolationValue; public ThumbnailnatorProgressiveAlgorithm(Object interpolationValue) { this.interpolationValue = interpolationValue; } @Override public BufferedImage scale(BufferedImage imageToScale, int dWidth, int dHeight) { return new FixedSizeThumbnailMaker(dWidth, dHeight, false, true) .resizer(new ProgressiveResizer(interpolationValue)).make(imageToScale); } public static class ProgressiveResizer extends AbstractResizer { public ProgressiveResizer(Object interpolationValue) { this(interpolationValue, Collections.emptyMap()); } public ProgressiveResizer(Object interpolationValue, Map<RenderingHints.Key, Object> hints) { super(interpolationValue, hints); checkArg(interpolationValue); } private void checkArg(Object interpolationValue) { if (interpolationValue != RenderingHints.VALUE_INTERPOLATION_BICUBIC && interpolationValue != RenderingHints.VALUE_INTERPOLATION_BILINEAR && interpolationValue != RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR ) throw new IllegalArgumentException("wrong argument passed muts be one of RenderingHints.VALUE_INTERPOLATION_BICUBIC, " + "RenderingHints.VALUE_INTERPOLATION_BILINEAR or RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR"); } @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)) { super.resize(srcImage, destImage); 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(); } } @Override public String toString() { return "ThumbnailnatorProgressiveAlgorithm{" + "interpolationValue=" + interpolationValue + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ThumbnailnatorProgressiveAlgorithm that = (ThumbnailnatorProgressiveAlgorithm) o; return interpolationValue != null ? interpolationValue.equals(that.interpolationValue) : that.interpolationValue == null; } @Override public int hashCode() { return interpolationValue != null ? interpolationValue.hashCode() : 0; } }