Package com.alibaba.simpleimage.util

Source Code of com.alibaba.simpleimage.util.ImageScaleHelper

/*
* Copyright 1999-2101 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.simpleimage.util;

import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;
import java.awt.image.renderable.ParameterBlock;

import javax.media.jai.BorderExtender;
import javax.media.jai.Interpolation;
import javax.media.jai.JAI;
import javax.media.jai.PlanarImage;
import javax.media.jai.RenderedImageAdapter;

import org.w3c.dom.Node;

import com.alibaba.simpleimage.ImageWrapper;
import com.alibaba.simpleimage.SimpleImageException;
import com.alibaba.simpleimage.jai.scale.LanczosScaleOp;
import com.alibaba.simpleimage.render.ScaleParameter;
import com.sun.media.jai.opimage.SubsampleAverageCRIF;

public class ImageScaleHelper {

    private static ImageLog log = ImageLog.getLog(ImageScaleHelper.class);

    public static ImageWrapper scaleGIF(ImageWrapper imgWrapper, ScaleParameter zoom) throws SimpleImageException {
        Node streamMetadata = imgWrapper.getStreamMetadata();

        int width = 0, height = 0;
        Node screenDescNode = NodeUtils.getChild(streamMetadata, "LogicalScreenDescriptor");
        if (screenDescNode != null) {
            width = NodeUtils.getIntAttr(screenDescNode, "logicalScreenWidth");
            height = NodeUtils.getIntAttr(screenDescNode, "logicalScreenHeight");
        }
        if (width <= 0 || height <= 0) {
            width = imgWrapper.getAsBufferedImage().getWidth();
            height = imgWrapper.getAsBufferedImage().getHeight();
        }

        // do not need scale the image
        if (zoom.getMaxWidth() >= width && zoom.getMaxHeight() >= height) {
            return imgWrapper;
        }

        double scale = computeDoubleScale(width, height, zoom);
        int newWidth = (int) (width * scale);
        int newHeight = (int) (height * scale);
        NodeUtils.setAttrValue(screenDescNode, "logicalScreenWidth", newWidth);
        NodeUtils.setAttrValue(screenDescNode, "logicalScreenHeight", newHeight);
        NodeUtils.removeChild(streamMetadata, "GlobalColorTable");

        for (int i = 0; i < imgWrapper.getNumOfImages(); i++) {
            PlanarImage img = imgWrapper.getAsPlanarImage(i);
            Node imgMetadata = imgWrapper.getMetadata(i);

            if (img.getColorModel() instanceof IndexColorModel) {
                throw new SimpleImageException(
                                               "Unsupported scale image with IndexColorModel, please convert to RGB color model first");
            }
            // No more need, index color model will triger throws exception first
            // NodeUtils.removeChild(imgMetadata, "LocalColorTable");
            Node imgDescNode = NodeUtils.getChild(imgMetadata, "ImageDescriptor");

            int x = NodeUtils.getIntAttr(imgDescNode, "imageLeftPosition");
            int y = NodeUtils.getIntAttr(imgDescNode, "imageTopPosition");
            int newX = (int) (x * scale), newY = (int) (y * scale);
            newX = newX > 0 ? newX : 0;
            newY = newY > 0 ? newY : 0;
            NodeUtils.setAttrValue(imgDescNode, "imageLeftPosition", newX);
            NodeUtils.setAttrValue(imgDescNode, "imageTopPosition", newY);

            int imgWidth = NodeUtils.getIntAttr(imgDescNode, "imageWidth");
            int imgHeight = NodeUtils.getIntAttr(imgDescNode, "imageHeight");
            int newImgWidth = (int) (imgWidth * scale), newImgHeight = (int) (imgHeight * scale);
            newImgWidth = newImgWidth > 1 ? newImgWidth : 1;
            newImgHeight = newImgHeight > 1 ? newImgHeight : 1;
            NodeUtils.setAttrValue(imgDescNode, "imageWidth", newImgWidth);
            NodeUtils.setAttrValue(imgDescNode, "imageHeight", newImgHeight);

            if (zoom.getAlgorithm() == ScaleParameter.Algorithm.INTERP_BICUBIC) {
                img = bicubicScaleImage(img, (float) scale, Interpolation.INTERP_BICUBIC);
            } else if (zoom.getAlgorithm() == ScaleParameter.Algorithm.INTERP_BICUBIC_2) {
                img = bicubicScaleImage(img, (float) scale, Interpolation.INTERP_BICUBIC_2);
            } else if (zoom.getAlgorithm() == ScaleParameter.Algorithm.SUBSAMPLE_AVG) {
                img = subsampleavgScaleImage(img, scale);
            } else if (zoom.getAlgorithm() == ScaleParameter.Algorithm.LANCZOS) {
                img = lanczosScaleImage(img, scale);
            } else if (zoom.getAlgorithm() == ScaleParameter.Algorithm.AUTO) {
                img = autoScaleImage(img, scale);
            } else {
                throw new IllegalArgumentException("Unknow algorithm");
            }

            imgWrapper.setImage(i, img);
        }

        return imgWrapper;
    }

    public static PlanarImage scale(PlanarImage input, ScaleParameter zoom) {
        int w = input.getWidth();
        int h = input.getHeight();

        // 如果不超过最大限制则不做任何处理
        if (zoom.getMaxWidth() >= w && zoom.getMaxHeight() >= h) {
            return input;
        }

        if (zoom.getAlgorithm() == ScaleParameter.Algorithm.INTERP_BICUBIC) {
            float scale = computeFloatScale(w, h, zoom);

            return bicubicScaleImage(input, scale, Interpolation.INTERP_BICUBIC);
        } else if (zoom.getAlgorithm() == ScaleParameter.Algorithm.INTERP_BICUBIC_2) {
            float scale = computeFloatScale(w, h, zoom);

            return bicubicScaleImage(input, scale, Interpolation.INTERP_BICUBIC_2);
        } else if (zoom.getAlgorithm() == ScaleParameter.Algorithm.SUBSAMPLE_AVG) {
            double scale = computeDoubleScale(w, h, zoom);

            return subsampleavgScaleImage(input, scale);
        } else if (zoom.getAlgorithm() == ScaleParameter.Algorithm.LANCZOS) {
            double scale = computeDoubleScale(w, h, zoom);

            return lanczosScaleImage(input, scale);
        } else if (zoom.getAlgorithm() == ScaleParameter.Algorithm.AUTO) {
            double scale = computeDoubleScale(w, h, zoom);

            return autoScaleImage(input, scale);
        } else if (zoom.getAlgorithm() == ScaleParameter.Algorithm.PROGRESSIVE) {
            throw new UnsupportedOperationException("Deprecated method");
        } else {
            throw new IllegalArgumentException("Unknow algorithm");
        }
    }

    public static float computeFloatScale(int w, int h, ScaleParameter zoom) {
        int maxWidth = zoom.getMaxWidth();
        int maxHeight = zoom.getMaxHeight();
        float scale = 0.0f;

        scale = Math.min(((float) maxWidth) / w, ((float) maxHeight) / h);

        return scale;
    }

    public static double computeDoubleScale(int w, int h, ScaleParameter zoom) {
        int maxWidth = zoom.getMaxWidth();
        int maxHeight = zoom.getMaxHeight();
        double scale = 0.0;

        scale = Math.min(((double) maxWidth) / w, ((double) maxHeight) / h);

        return scale;
    }

    public static PlanarImage bicubicScaleImage(PlanarImage input, float scale, int alg) {
        RenderingHints qualityHints = new RenderingHints(RenderingHints.KEY_RENDERING,
                                                         RenderingHints.VALUE_RENDER_QUALITY);
        // 必须使用该hint,否则会出现边框变黑到情况
        qualityHints.put(JAI.KEY_BORDER_EXTENDER, BorderExtender.createInstance(BorderExtender.BORDER_COPY));

        ParameterBlock pb = new ParameterBlock();
        pb.addSource(input);
        pb.add(scale);
        pb.add(scale);
        pb.add(0.0F);
        pb.add(0.0F);
        pb.add(Interpolation.getInstance(alg));

        return JAI.create("scale", pb, qualityHints);
    }

    public static PlanarImage subsampleavgScaleImage(PlanarImage input, double scale) {
        RenderingHints qualityHints = new RenderingHints(RenderingHints.KEY_RENDERING,
                                                         RenderingHints.VALUE_RENDER_QUALITY);

        ParameterBlock pb = new ParameterBlock();
        pb.addSource(input);
        pb.add(scale);
        pb.add(scale);

        // Because the mlib subsampleaverage has bug, use pure java version
        SubsampleAverageCRIF factory = new SubsampleAverageCRIF();
        PlanarImage zoomOp = (PlanarImage) factory.create(pb, qualityHints);

        return zoomOp;
    }

    public static PlanarImage lanczosScaleImage(PlanarImage input, double scale) {
        LanczosScaleOp lanczosOp = new LanczosScaleOp(scale, scale);
        BufferedImage dest = lanczosOp.compute(input.getAsBufferedImage());

        return PlanarImage.wrapRenderedImage(dest);
    }

    public static PlanarImage autoScaleImage(PlanarImage input, double scale) {
        if (input.getWidth() > 3000 || input.getHeight() > 3000) {
            return subsampleavgScaleImage(input, scale);
        }

        try {
            return lanczosScaleImage(input, scale);
        } catch (Exception e) {
            log.warn("LanczosScale fail : " + e.getMessage(), input);

            return subsampleavgScaleImage(input, scale);
        }
    }

    /**
     * 折半渐进压缩图片方法 测试后觉得效果不太理想,不鼓励使用
     *
     * @param img
     * @param targetWidth
     * @param targetHeight
     * @param hint
     * @param progressiveBilinear
     * @return
     */
    @Deprecated
    public static PlanarImage progressiveScaleImage(PlanarImage input, int targetWidth, int targetHeight, Object hint,
                                                    boolean progressive) {
        BufferedImage img = input.getAsBufferedImage();
        int type = (img.getTransparency() == Transparency.OPAQUE) ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
        BufferedImage ret = img;
        BufferedImage scratchImage = null;
        Graphics2D g2 = null;
        int w, h;
        int prevW = ret.getWidth();
        int prevH = ret.getHeight();
        boolean isTranslucent = img.getTransparency() != Transparency.OPAQUE;

        if (progressive) {
            // 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 (progressive && w > targetWidth) {
                w /= 2;
                if (w < targetWidth) {
                    w = targetWidth;
                }
            }

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

            if (scratchImage == null || isTranslucent) {
                // Use a single scratch buffer for all iterations
                // and then copy to the final, correctly-sized image
                // before returning
                scratchImage = new BufferedImage(w, h, type);
                g2 = scratchImage.createGraphics();
            }
            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
            g2.drawImage(ret, 0, 0, w, h, 0, 0, prevW, prevH, null);
            prevW = w;
            prevH = h;

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

        if (g2 != null) {
            g2.dispose();
        }

        // If we used a scratch buffer that is larger than our target size,
        // create an image of the right size and copy the results into it
        if (targetWidth != ret.getWidth() || targetHeight != ret.getHeight()) {
            scratchImage = new BufferedImage(targetWidth, targetHeight, type);
            g2 = scratchImage.createGraphics();
            g2.drawImage(ret, 0, 0, null);
            g2.dispose();
            ret = scratchImage;
        }

        return new RenderedImageAdapter(ret);
    }
}
TOP

Related Classes of com.alibaba.simpleimage.util.ImageScaleHelper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.