/* UtilLib_Image - Copyright (c) 2010-2022 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package net.mapeadores.util.images;

import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import net.mapeadores.util.awt.ResizeInfo;
import net.mapeadores.util.logging.ErrorMessageException;


/**
 *
 * @author Vincent Calame
 */
public final class ImageResizing {

    private ImageResizing() {

    }

    public static void resize(File srcFile, File destFile, ResizeInfo resizeInfo, String format) throws IOException, ErrorMessageException {
        BufferedImage srcImg = ImagesUtils.read(srcFile);
        BufferedImage destImg = getScaledInstance(srcImg, resizeInfo);
        ImagesUtils.write(destImg, format, destFile);
    }

    public static void resize(InputStream inputStream, OutputStream outputStream, ResizeInfo resizeInfo, String format) throws IOException, ErrorMessageException {
        BufferedImage srcImg = ImagesUtils.read(inputStream);
        BufferedImage destImg = getScaledInstance(srcImg, resizeInfo);
        ImagesUtils.write(destImg, format, outputStream);
    }

    public static void resize(InputStream inputStream, File destFile, ResizeInfo resizeInfo, String format) throws IOException, ErrorMessageException {
        BufferedImage srcImg = ImagesUtils.read(inputStream);
        BufferedImage destImg = getScaledInstance(srcImg, resizeInfo);
        ImagesUtils.write(destImg, format, destFile);
    }

    public static BufferedImage getScaledInstance(BufferedImage img, ResizeInfo resizeInfo) {
        int width = img.getWidth();
        int height = img.getHeight();
        short type = resizeInfo.getType();
        int targetWidth = resizeInfo.getWidth();
        int targetHeight = resizeInfo.getHeight();
        if (resizeInfo.isOnlyMax()) {
            switch (type) {
                case ResizeInfo.WIDTH_RESIZING:
                    if (width <= targetWidth) {
                        return img;
                    }
                    break;
                case ResizeInfo.HEIGHT_RESIZING:
                    if (height <= targetHeight) {
                        return img;
                    }
                    break;
                case ResizeInfo.BOTH_RESIZING:
                    if ((width <= targetWidth) && (height <= targetHeight)) {
                        return img;
                    }
                    break;
            }
        }
        float ratio = 0;
        switch (type) {
            case ResizeInfo.WIDTH_RESIZING:
                if (targetWidth == width) {
                    return img;
                }
                ratio = ((float) targetWidth) / width;
                targetHeight = (int) (ratio * height);
                break;
            case ResizeInfo.HEIGHT_RESIZING:
                if (targetHeight == height) {
                    return img;
                }
                ratio = ((float) targetHeight) / height;
                targetWidth = (int) (ratio * width);
                break;
            case ResizeInfo.BOTH_RESIZING:
                if (resizeInfo.isOnlyMax()) {
                    float widthRatio = ((float) targetWidth) / width;
                    float heightRatio = ((float) targetHeight) / height;
                    if (widthRatio < heightRatio) {
                        ratio = widthRatio;
                        targetHeight = (int) (ratio * height);
                    } else {
                        ratio = heightRatio;
                        targetWidth = (int) (ratio * width);
                    }
                } else {
                    ratio = Math.max(((float) targetWidth) / width, ((float) targetHeight) / height);
                }
                break;
        }
        if (ratio > 1) {
            return getScaledInstance(img, targetWidth, targetHeight, RenderingHints.VALUE_INTERPOLATION_BICUBIC, false);
        } else {
            return getScaledInstance(img, targetWidth, targetHeight, RenderingHints.VALUE_INTERPOLATION_BILINEAR, true);
        }
    }

    /**
     *
     * Convenience method that returns a scaled instance of the provided
     * {@code BufferedImage}. From
     * <a href="http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html">The
     * Perils of Image.getScaledInstance()</a>
     * by Chris Campbell
     *
     * @param img the original image to be scaled
     * @param targetWidth the desired width of the scaled instance, in pixels
     * @param targetHeight the desired height of the scaled instance, in pixels
     * @param hint one of the rendering hints that corresponds to
     * {@code RenderingHints.KEY_INTERPOLATION} (e.g.
     * {@code RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR},
     * {@code RenderingHints.VALUE_INTERPOLATION_BILINEAR},
     * {@code RenderingHints.VALUE_INTERPOLATION_BICUBIC})
     * @param higherQuality if true, this method will use a multi-step scaling
     * technique that provides higher quality than the usual one-step technique
     * (only useful in downscaling cases, where {@code targetWidth} or
     * {@code targetHeight} is smaller than the original dimensions, and
     * generally only when the {@code BILINEAR} hint is specified)
     * @return a scaled version of the original {@code BufferedImage}
     */
    public static BufferedImage getScaledInstance(BufferedImage img,
            int targetWidth,
            int targetHeight,
            Object hint,
            boolean higherQuality) {
        int type = (img.getTransparency() == Transparency.OPAQUE)
                ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
        BufferedImage ret = (BufferedImage) img;
        int w, h;
        if (higherQuality) {
            // Use multi-step technique: start with original size, then
            // scale down in multiple passes with drawImage()
            // until the target size is reached
            w = img.getWidth();
            h = img.getHeight();
        } else {
            // Use one-step technique: scale directly from original
            // size to target size with a single drawImage() call
            w = targetWidth;
            h = targetHeight;
        }

        do {
            if (higherQuality && w > targetWidth) {
                w /= 2;
                if (w < targetWidth) {
                    w = targetWidth;
                }
            }

            if (higherQuality && h > targetHeight) {
                h /= 2;
                if (h < targetHeight) {
                    h = targetHeight;
                }
            }

            BufferedImage tmp = new BufferedImage(w, h, type);
            Graphics2D g2 = tmp.createGraphics();
            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
            g2.drawImage(ret, 0, 0, w, h, null);
            g2.dispose();

            ret = tmp;
        } while (w != targetWidth || h != targetHeight);

        return ret;
    }

}
