Package org.fcrepo.localservices.imagemanip

Source Code of org.fcrepo.localservices.imagemanip.ImageManipulation

/* The contents of this file are subject to the license and copyright terms
* detailed in the license directory at the root of the source tree (also
* available online at http://fedora-commons.org/license/).
*/

/**
* Image manipulations (with the exception of the watermarking function)
* are handled by ImageJ, a Java API written for image processing.
*
* Image encoding and decoding is handled by JAI, the Java Advanced Imaging
* API, with the exception of GIF encoding, which is handled by ImageJ.
*
*  ImageJ Information:
*
*  Rasband, W.S., ImageJ, National Institutes of Health, Bethesda,
*  Maryland, USA, http://rsb.info.nih.gov/ij/, 1997-2003.
*
*    The GifEncoder portion of ImageJ is copyrighted below:
*
*  Transparency handling and variable bit size courtesy of Jack Palevich.
*
*  Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>.  All rights reserved.
*
*  Redistribution and use in source and binary forms, with or without
*  modification, are permitted provided that the following conditions
*  are met:
*  1. Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*  2. Redistributions in binary form must reproduce the above copyright
*     notice, this list of conditions and the following disclaimer in the
*     documentation and/or other materials provided with the distribution.
*
*  THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
*  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
*  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
*  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
*  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
*  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
*  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
*  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
*  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
*  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
*  SUCH DAMAGE.
*
*  Visit the ACME Labs Java page for up-to-date versions of this and other
*  fine Java utilities: http://www.acme.com/java/
*
*/
package org.fcrepo.localservices.imagemanip;

import ij.ImagePlus;
import ij.io.FileInfo;
import ij.io.GifEncoder;
import ij.process.ImageProcessor;
import ij.process.MedianCut;

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.media.jai.JAI;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.PoolingClientConnectionManager;
import org.apache.http.params.CoreConnectionPNames;

import com.sun.media.jai.codec.BMPEncodeParam;
import com.sun.media.jai.codec.ImageCodec;
import com.sun.media.jai.codec.ImageEncodeParam;
import com.sun.media.jai.codec.JPEGEncodeParam;
import com.sun.media.jai.codec.MemoryCacheSeekableStream;
import com.sun.media.jai.codec.PNGEncodeParam;
import com.sun.media.jai.codec.TIFFEncodeParam;

/**
* ImageManipulation is a Java servlet that takes a URL of an image as a param
* and based on other given parameters, can perform a variety of image related
* manipulations on the object.
*
* <p>After the image is manipulated, it is then sent back as an image/type
* object to the calling parent, most often a browser or an HTML img tag.
*
* @author Theodore Serbinski
*/
public class ImageManipulation
        extends HttpServlet {

    private static final long serialVersionUID = 1L;
   
    private static final Logger LOGGER =
            Logger.getLogger(
                "org.apache.catalina.core.ContainerBase.[Catalina].[localhost]");

    private String inputMimeType;

    private boolean alreadyConvertedToRGB = false;

    private final PoolingClientConnectionManager cManager =
            getConnectionManager();

    private PoolingClientConnectionManager getConnectionManager() {
        PoolingClientConnectionManager cm =
            new PoolingClientConnectionManager();
        cm.getSchemeRegistry().register(
                new Scheme("https", 443, SSLSocketFactory.getSocketFactory()));
        cm.getSchemeRegistry().register(
                new Scheme("https-tomcat", 8443, SSLSocketFactory.getSocketFactory()));
        cm.getSchemeRegistry().register(
                new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
        cm.getSchemeRegistry().register(
                new Scheme("http-tomcat", 8080, PlainSocketFactory.getSocketFactory()));
        return cm;
    }

    @Override
    public void destroy() {
        cManager.shutdown();
    }

    /**
     * Method automatically called by browser to handle image manipulations.
     *
     * @param req
     *        Browser request to servlet res Response sent back to browser after
     *        image manipulation
     * @throws IOException
     *         If an input or output exception occurred ServletException If a
     *         servlet exception occurred
     */
    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse res)
            throws ServletException, IOException {
      System.setProperty("java.awt.headless", "true");
        // collect all possible parameters for servlet
        String url = req.getParameter("url");
        String op = req.getParameter("op");
        String newWidth = req.getParameter("newWidth");
        String brightAmt = req.getParameter("brightAmt");
        String zoomAmt = req.getParameter("zoomAmt");
        String wmText = req.getParameter("wmText");
        String cropX = req.getParameter("cropX");
        String cropY = req.getParameter("cropY");
        String cropWidth = req.getParameter("cropWidth");
        String cropHeight = req.getParameter("cropHeight");
        String convertTo = req.getParameter("convertTo");
        if (convertTo != null) {
            convertTo = convertTo.toLowerCase();
        }
        try {
            if (op == null) {
                throw new ServletException("op parameter not specified.");
            }
            String outputMimeType;
            // get the image via url and put it into the ImagePlus processor.
            BufferedImage img = getImage(url);
            // do watermarking stuff
            if (op.equals("watermark")) {
                if (wmText == null) {
                    throw new ServletException("Must specify wmText.");
                }
                Graphics g = img.getGraphics();
                int fontSize = img.getWidth() * 3 / 100;
                if (fontSize < 10) {
                    fontSize = 10;
                }
                g.setFont(new Font("Lucida Sans", Font.BOLD, fontSize));
                FontMetrics fm = g.getFontMetrics();
                int stringWidth =
                        (int) fm.getStringBounds(wmText, g).getWidth();
                int x = img.getWidth() / 2 - stringWidth / 2;
                int y = img.getHeight() - fm.getHeight();
                g.setColor(new Color(180, 180, 180));
                g.fill3DRect(x - 10,
                             y - fm.getHeight() - 4,
                             stringWidth + 20,
                             fm.getHeight() + 12,
                             true);
                g.setColor(new Color(100, 100, 100));
                g.drawString(wmText, x + 2, y + 2);
                g.setColor(new Color(240, 240, 240));
                g.drawString(wmText, x, y);
            }
            ImageProcessor ip = new ImagePlus("temp", img).getProcessor();
            // if the inputMimeType is image/gif, need to convert to RGB in any case
            if (inputMimeType.equals("image/gif")) {
                ip = ip.convertToRGB();
                alreadyConvertedToRGB = true;
            }
            // causes scale() and resize() to do bilinear interpolation
            ip.setInterpolate(true);
            if (!op.equals("convert")) {
                if (op.equals("resize")) {
                    ip = resize(ip, newWidth);
                } else if (op.equals("zoom")) {
                    ip = zoom(ip, zoomAmt);
                } else if (op.equals("brightness")) {
                    ip = brightness(ip, brightAmt);
                } else if (op.equals("watermark")) {
                    // this is now taken care of beforehand (see above)
                } else if (op.equals("grayscale")) {
                    ip = grayscale(ip);
                } else if (op.equals("crop")) {
                    ip = crop(ip, cropX, cropY, cropWidth, cropHeight);
                } else {
                    throw new ServletException("Invalid operation: " + op);
                }
                outputMimeType = inputMimeType;
            } else {
                if (convertTo == null) {
                    throw new ServletException("Neither op nor convertTo was specified.");
                }
                if (convertTo.equals("jpg") || convertTo.equals("jpeg")) {
                    outputMimeType = "image/jpeg";
                } else if (convertTo.equals("gif")) {
                    outputMimeType = "image/gif";
                } else if (convertTo.equals("tiff")) {
                    outputMimeType = "image/tiff";
                } else if (convertTo.equals("bmp")) {
                    outputMimeType = "image/bmp";
                } else if (convertTo.equals("png")) {
                    outputMimeType = "image/png";
                } else {
                    throw new ServletException("Invalid format: " + convertTo);
                }
            }
            res.setContentType(outputMimeType);
            BufferedOutputStream out =
                    new BufferedOutputStream(res.getOutputStream());
            outputImage(ip, out, outputMimeType);
            out.flush();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
            res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e
                    .getClass().getName()
                    + ": " + e.getMessage());
        }
    }

    /**
     * Gets and deserializes the image at the given URL into an Image object.
     * This method also sets the inputMimeType based on the HTTP Content-Type
     * header so that, if the image needs to be returned in it's original
     * format, the correct mime type can be sent in the response header. If the
     * input image is not a gif, jpg, tiff, bmp, or png (according to the http
     * response header), or some other kind of error occurs while reading the
     * stream from the remote host, a ServletException is thrown.
     *
     * @param url
     *        The location of the input image.
     * @return Image The image object, if successful.
     * @throws Exception
     *         If any of the aforementioned problems occurs.
     */
    private BufferedImage getImage(String url) throws Exception {
        HttpGet get = null;
        LOGGER.info("ImageManipulation: GET " + url);
        try {
            DefaultHttpClient client = new DefaultHttpClient(cManager);
            client.getParams().setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 20000);
            get = new HttpGet(url);
            client.getParams().setBooleanParameter(ClientPNames.HANDLE_REDIRECTS, true);
            HttpResponse response = client.execute(get);
            int resultCode = response.getStatusLine().getStatusCode();
            if (resultCode != 200) {
                throw new ServletException("Could not load image: " + url
                        + ".  Errorcode " + resultCode + " from remote server.");
            }
            LOGGER.info("ImageManipulation: STATUS " + resultCode);
            inputMimeType = response.getFirstHeader(HttpHeaders.CONTENT_TYPE).getValue();
            LOGGER.info("ImageManipulation: Content-Type " + inputMimeType);
            if (inputMimeType.equals("image/gif")
                    || inputMimeType.equals("image/jpeg")
                    || inputMimeType.equals("image/tiff")
                    || inputMimeType.equals("image/bmp")
                    || inputMimeType.equals("image/x-ms-bmp")
                    || inputMimeType.equals("image/x-bitmap")
                    || inputMimeType.equals("image/png")) {
                if (inputMimeType.endsWith("p")) {
                    inputMimeType = "image/bmp"; // windows bitmaps are most
                }
                // commonly supported with this
                // mime type, even though it's not
                // an IANA-registered image type
                return JAI.create("stream",
                                  new MemoryCacheSeekableStream(response
                                          .getEntity().getContent()))
                        .getAsBufferedImage();
            } else {
                throw new ServletException("Source image was not a gif, png, "
                        + "bmp, tiff, or jpg.");
            }
        } catch (Exception e) {
            LOGGER.log(Level.SEVERE, e.getMessage(), e);
            throw e;
        } finally {
            if (get != null) {
                get.releaseConnection();
            }
        }

    }

    private void outputImage(ImageProcessor ip,
                             OutputStream out,
                             String outputMimeType) throws Exception {
        if (outputMimeType.equals("image/gif")) {
            if (!alreadyConvertedToRGB) {
                ip = ip.convertToRGB();
            }
            MedianCut mc =
                    new MedianCut((int[]) ip.getPixels(), ip.getWidth(), ip
                            .getHeight());
            ip = mc.convertToByte(256);
            ImagePlus imp = new ImagePlus("temp", ip);
            FileInfo fi = imp.getFileInfo();
            byte pixels[] = (byte[]) imp.getProcessor().getPixels();
            GifEncoder ge =
                    new GifEncoder(fi.width,
                                   fi.height,
                                   pixels,
                                   fi.reds,
                                   fi.greens,
                                   fi.blues);
            ge.write(out);
        } else {
            ImageEncodeParam param = null;
            String format = null;
            if (outputMimeType.equals("image/jpeg")) {
                param = new JPEGEncodeParam();
                format = "JPEG";
            } else if (outputMimeType.equals("image/tiff")) {
                param = new TIFFEncodeParam();
                format = "TIFF";
            } else if (outputMimeType.equals("image/bmp")) {
                param = new BMPEncodeParam();
                format = "BMP";
            } else if (outputMimeType.equals("image/png")) {
                param = new PNGEncodeParam.RGB();
                format = "PNG";
            }
            ImageCodec.createImageEncoder(format, out, param).encode(JAI
                    .create("AWTImage", ip.createImage()));
        }
    }

    /**
     * Resizes an image to the supplied new width in pixels. The height is
     * reduced proportionally to the new width.
     *
     * @param ip
     *        The image to resize newWidth The width in pixels to resize the
     *        image to
     * @return The image resized
     */
    private ImageProcessor resize(ImageProcessor ip, String newWidth) {
        if (newWidth != null) {
            try {
                int width = Integer.parseInt(newWidth);

                if (width < 0) {
                    return ip;
                }

                int imgWidth = ip.getWidth();
                int imgHeight = ip.getHeight();

                ip = ip.resize(width, width * imgHeight / imgWidth);
            }
            // no need to do anything with number format exception since the servlet
            // returns only images; just return the original image
            catch (NumberFormatException e) {
            }
        }

        return ip;
    }

    /**
     * Zooms either in or out of an image by a supplied amount. The zooming
     * occurs from the center of the image.
     *
     * @param ip
     *        The image to zoom zoomAmt The amount to zoom the image. 0 <
     *        zoomAmt < 1 : zoom out 1 = zoomAmt : original image 1 < zoomAmt :
     *        zoom in
     * @return The image zoomed
     */
    private ImageProcessor zoom(ImageProcessor ip, String zoomAmt) {
        if (zoomAmt != null) {
            try {
                float zoom = Float.parseFloat(zoomAmt);

                if (zoom < 0) {
                    return ip;
                }

                ip.scale(zoom, zoom);

                // if the image is being zoomed out, trim the extra whitespace around the image
                if (zoom < 1) {
                    int imgWidth = ip.getWidth();
                    int imgHeight = ip.getHeight();

                    // set a ROI around the image, minus the extra whitespace
                    ip.setRoi(Math.round(imgWidth / 2 - imgWidth * zoom / 2),
                              Math.round(imgHeight / 2 - imgHeight * zoom / 2),
                              Math.round(imgWidth * zoom),
                              Math.round(imgHeight * zoom));
                    ip = ip.crop();
                }

            }

            // no need to do anything with number format exception since the servlet
            // returns only images; just return the original image
            catch (NumberFormatException e) {
            }
        }

        return ip;
    }

    /**
     * Adjusts the brightness of an image by a supplied amount.
     *
     * @param ip
     *        The image to adjust the brightness of brightAmt The amount to
     *        adjust the brightness of the image by 0 <= brightAmt < 1 : darkens
     *        image 1 = brightAmt : original image 1 < brightAmt : brightens
     *        image
     * @return The image with brightness levels adjusted
     */
    private ImageProcessor brightness(ImageProcessor ip, String brightAmt) {
        if (brightAmt != null) {
            try {
                float bright = Float.parseFloat(brightAmt);

                if (bright < 0) {
                    return ip;
                }

                ip.multiply(bright);

            }

            // no need to do anything with number format exception since the servlet
            // returns only images; just return the original image
            catch (NumberFormatException e) {
            }
        }

        return ip;
    }

    /**
     * Converts an image to gray scale.
     *
     * @param ip
     *        The image to convert to grayscale
     * @return The image converted to grayscale
     */
    private ImageProcessor grayscale(ImageProcessor ip) {
        ip = ip.convertToByte(true);

        return ip;
    }

    /**
     * Crops an image with supplied starting point and ending point.
     *
     * @param ip
     *        The image to crop cropX The starting x position; x=0 corresponds
     *        to left side of image cropY The starting y position; y=0
     *        corresponds to top of image cropWidth The width of the crop,
     *        starting from the above x cropHeight The height of the crop,
     *        starting from the above y
     * @return The image cropped
     */
    public ImageProcessor crop(ImageProcessor ip,
                               String cropX,
                               String cropY,
                               String cropWidth,
                               String cropHeight) {
        if (cropX != null && cropY != null) {
            try {
                int x = Integer.parseInt(cropX);
                int y = Integer.parseInt(cropY);
                int width;
                int height;

                // if value for cropWidth is not given, just use the width of the image
                if (cropWidth != null) {
                    width = Integer.parseInt(cropWidth);
                } else {
                    width = ip.getWidth();
                }

                // if value for cropHeight is not given, just use the height of the image
                if (cropHeight != null) {
                    height = Integer.parseInt(cropHeight);
                } else {
                    height = ip.getHeight();
                }

                // if any value is negative, this causes ImageJ to explode, so just return
                if (x < 0 || y < 0 || width < 0 || height < 0) {
                    return ip;
                }

                ip.setRoi(x, y, width, height);
                ip = ip.crop();
            }

            // no need to do anything with number format exception since the servlet
            // returns only images; just return the original image
            catch (NumberFormatException e) {
            }
        }

        return ip;
    }
}
TOP

Related Classes of org.fcrepo.localservices.imagemanip.ImageManipulation

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.