org.encuestame.business.images.ImageThumbnailGeneratorImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.encuestame.business.images.ImageThumbnailGeneratorImpl.java

Source

/*
 ************************************************************************************
 * Copyright (C) 2001-2011 encuestame: system online surveys Copyright (C) 2011
 * encuestame Development Team.
 * Licensed under the Apache Software License version 2.0
 * 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 org.encuestame.business.images;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Generate thumbnails for images.
 * @author Picado, Juan juanATencuestame.org
 * @since Jan 22, 2011 7:48:24 PM
 */
public class ImageThumbnailGeneratorImpl implements ThumbnailGenerator {

    private Log log = LogFactory.getLog(this.getClass());

    /**
     *
     */
    public Object createThumbnail(InputStream inputStream, File fileOut, int largestDimension, Object hint)
            throws IOException {

        // What's the base image that we are starting with?  If there's a hint, that's the scaled image
        // from the last time around, use that... (since we know we always iterate downwards in scale)
        Image imageIn;
        if (hint instanceof Image) {
            imageIn = (Image) hint;
            log.info("createThumbnail(" + fileOut + ") reusing prior result image...");
        } else {
            log.info("createThumbnail(" + fileOut + ") reading image from stream " + inputStream);
            imageIn = ImageIO.read(inputStream);
        }
        if (imageIn == null) {
            log.warn("Could not read image file: " + inputStream);
            return hint;
        }
        BufferedImage imageOut = createThumbnailImage(imageIn, fileOut, largestDimension);
        // Return this image now as the hint for the next scaling iteration
        if (imageOut != null)
            hint = imageOut;

        return hint;
    }

    /**
     * Create a thumbnail image and save it to disk.
     *
     * This algorithm is based on:
     *      http://www.philreeve.com/java_high_quality_thumbnails.php
     *
     * @param imageIn           The image you want to scale.
     * @param fileOut           The output file.
     * @param largestDimension  The largest dimension, so that neither the width nor height
     *                          will exceed this value.
     *
     * @return the image that was created, null if imageIn or fileOut is null.
     * @throws java.io.IOException if something goes wrong when saving as jpeg
     */
    public BufferedImage createThumbnailImage(Image imageIn, File fileOut, int largestDimension)
            throws IOException {
        if ((imageIn == null) || (fileOut == null)) {
            return null;
        }
        //it seems to not return the right size until the methods get called for the first time
        imageIn.getWidth(null);
        imageIn.getHeight(null);

        // Find biggest dimension
        int nImageWidth = imageIn.getWidth(null);
        int nImageHeight = imageIn.getHeight(null);
        int nImageLargestDim = Math.max(nImageWidth, nImageHeight);
        double scale = (double) largestDimension / (double) nImageLargestDim;
        int sizeDifference = nImageLargestDim - largestDimension;

        //create an image buffer to draw to
        BufferedImage imageOut = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); // 8-bit RGB
        Graphics2D g2d;
        AffineTransform tx;

        // Use a few steps if the sizes are drastically different, and only scale
        // if the desired size is smaller than the original.
        int numSteps = 0;
        if (scale < 1.0d) {
            // Make sure we have at least 1 step
            numSteps = Math.max(1, (sizeDifference / 100));
        }

        if (numSteps > 0) {
            int stepSize = sizeDifference / numSteps;
            int stepWeight = stepSize / 2;
            int heavierStepSize = stepSize + stepWeight;
            int lighterStepSize = stepSize - stepWeight;
            int currentStepSize, centerStep;
            double scaledW = imageIn.getWidth(null);
            double scaledH = imageIn.getHeight(null);

            if ((numSteps % 2) == 1) //if there's an odd number of steps
                centerStep = (int) Math.ceil((double) numSteps / 2d); //find the center step
            else
                centerStep = -1; //set it to -1 so it's ignored later

            Integer intermediateSize;
            Integer previousIntermediateSize = nImageLargestDim;

            for (Integer i = 0; i < numSteps; i++) {
                if (i + 1 != centerStep) {
                    //if this isn't the center step

                    if (i == numSteps - 1) {
                        //if this is the last step
                        //fix the stepsize to account for decimal place errors previously
                        currentStepSize = previousIntermediateSize - largestDimension;
                    } else {
                        if (numSteps - i > numSteps / 2) //if we're in the first half of the reductions
                            currentStepSize = heavierStepSize;
                        else
                            currentStepSize = lighterStepSize;
                    }
                } else {
                    //center step, use natural step size
                    currentStepSize = stepSize;
                }

                intermediateSize = previousIntermediateSize - currentStepSize;
                scale = intermediateSize / (double) previousIntermediateSize;
                scaledW = Math.max((int) (scaledW * scale), 1);
                scaledH = Math.max((int) (scaledH * scale), 1);

                log.info("step " + i + ": scaling to " + scaledW + " x " + scaledH);
                imageOut = new BufferedImage((int) scaledW, (int) scaledH, BufferedImage.TYPE_INT_RGB); // 8 bit RGB
                g2d = imageOut.createGraphics();
                g2d.setBackground(Color.WHITE);
                g2d.clearRect(0, 0, imageOut.getWidth(), imageOut.getHeight());
                g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
                tx = new AffineTransform();
                tx.scale(scale, scale);
                g2d.drawImage(imageIn, tx, null);
                g2d.dispose();
                imageIn = new ImageIcon(imageOut).getImage();
                previousIntermediateSize = intermediateSize;
            }
        } else {
            // This enforces a rule that we always have an 8-bit image with white background for the thumbnail.  Plus, for large
            // images, this makes subsequent downscaling really fast because we are working on a large 8-bit image
            // instead of a large 12 or 24 bit image, so the downstream effect is very noticable.
            imageOut = new BufferedImage(imageIn.getWidth(null), imageIn.getHeight(null),
                    BufferedImage.TYPE_INT_RGB);
            g2d = imageOut.createGraphics();
            g2d.setBackground(Color.WHITE);
            g2d.clearRect(0, 0, imageOut.getWidth(), imageOut.getHeight());
            tx = new AffineTransform();
            tx.setToIdentity(); //use identity matrix so image is copied exactly
            g2d.drawImage(imageIn, tx, null);
            g2d.dispose();
        }
        //saveImageAsJPEG(imageOut, fileOut);
        ImageIO.write(imageOut, "jpg", fileOut);
        return imageOut;
    }

}