package at.favre.tools.dconvert.converters.scaling; import com.mortennobel.imagescaling.*; import org.imgscalr.Scalr; import java.awt.*; import java.awt.image.BufferedImage; /** * A best of of progressive scaling algorithms from different libs */ public class ProgressiveAlgorithm implements ScaleAlgorithm { public enum Type { /** * Algorithms from https://github.com/mortennobel/java-image-scaling */ NOBEL_BILINEAR, NOBEL_BICUBUC, NOBEL_NEAREST_NEIGHBOR, NOBEL_LANCZOS3, /** * Algorithms from https://github.com/coobird/thumbnailator */ THUMBNAILATOR_BILINEAR, THUMBNAILATOR_BICUBUC, /** * Algorithms from https://github.com/thebuzzmedia/imgscalr */ IMGSCALR_SEVENTH_STEP, IMGSCALR_HALF_STEP, /** * Combination of bilinear with lanczos3, uses bilinear if target is at least half of src */ PROGRESSIVE_BILINEAR_AND_LANCZOS2, PROGRESSIVE_BILINEAR_AND_LANCZOS3; } public Type type; public ProgressiveAlgorithm(Type type) { this.type = type; } @Override public BufferedImage scale(BufferedImage imageToScale, int dWidth, int dHeight) { switch (type) { case NOBEL_BILINEAR: return new MultiStepRescaleOp(dWidth, dHeight, RenderingHints.VALUE_INTERPOLATION_BILINEAR) .filter(imageToScale, null); case NOBEL_BICUBUC: return new MultiStepRescaleOp(dWidth, dHeight, RenderingHints.VALUE_INTERPOLATION_BICUBIC) .filter(imageToScale, null); case NOBEL_NEAREST_NEIGHBOR: return new MultiStepRescaleOp(dWidth, dHeight, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR) .filter(imageToScale, null); case NOBEL_LANCZOS3: return new MultiStepLanczos3RescaleOp(dWidth, dHeight).filter(imageToScale, null); case PROGRESSIVE_BILINEAR_AND_LANCZOS2: return scaleProgressiveLanczos(imageToScale, dWidth, dHeight, 2); case PROGRESSIVE_BILINEAR_AND_LANCZOS3: return scaleProgressiveLanczos(imageToScale, dWidth, dHeight, 3); case THUMBNAILATOR_BILINEAR: return new ThumbnailnatorProgressiveAlgorithm(RenderingHints.VALUE_INTERPOLATION_BILINEAR).scale(imageToScale, dWidth, dHeight); case THUMBNAILATOR_BICUBUC: return new ThumbnailnatorProgressiveAlgorithm(RenderingHints.VALUE_INTERPOLATION_BICUBIC).scale(imageToScale, dWidth, dHeight); case IMGSCALR_SEVENTH_STEP: return Scalr.resize(imageToScale, Scalr.Method.ULTRA_QUALITY, Scalr.Mode.FIT_EXACT, dWidth, dHeight, null); case IMGSCALR_HALF_STEP: return Scalr.resize(imageToScale, Scalr.Method.QUALITY, Scalr.Mode.FIT_EXACT, dWidth, dHeight, null); default: throw new IllegalArgumentException("unknown algorithm"); } } private BufferedImage scaleProgressiveLanczos(BufferedImage imageToScale, int dstWidth, int dstHeight, float radius) { if (dstWidth < (imageToScale.getWidth() / 2) && dstHeight < (imageToScale.getHeight() / 2)) { return new ThumbnailnatorProgressiveAlgorithm(RenderingHints.VALUE_INTERPOLATION_BILINEAR).scale(imageToScale, dstWidth, dstHeight); } else { return new ResampleAlgorithm(new ResampleAlgorithm.LanczosFilter(radius)).scale(imageToScale, dstWidth, dstHeight); } } private class MultiStepLanczos3RescaleOp extends AdvancedResizeOp { private MultiStepLanczos3RescaleOp(int dstWidth, int dstHeight) { super(DimensionConstrain.createAbsolutionDimension(dstWidth, dstHeight)); } public BufferedImage doFilter(BufferedImage img, BufferedImage dest, int dstWidth, int dstHeight) { BufferedImage ret = img; int w, h; w = img.getWidth(); h = img.getHeight(); do { if (w > dstWidth) { w /= 2; if (w < dstWidth) { w = dstWidth; } } else { w = dstWidth; } if (h > dstHeight) { h /= 2; if (h < dstHeight) { h = dstHeight; } } else { h = dstHeight; } ResampleOp resizeOp = new ResampleOp(w, h); resizeOp.setFilter(ResampleFilters.getLanczos3Filter()); ret = resizeOp.filter(ret, null); } while (w != dstWidth || h != dstHeight); return ret; } } @Override public String toString() { return "ProgressiveAlgorithm[" + type + ']'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ProgressiveAlgorithm that = (ProgressiveAlgorithm) o; return type == that.type; } @Override public int hashCode() { return type != null ? type.hashCode() : 0; } }