package com.java.app.image;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

import javax.media.jai.JAI;
import javax.media.jai.OpImage;
import javax.media.jai.RenderedOp;
import javax.media.jai.operator.SubsampleAverageDescriptor;

import com.sun.image.codec.jpeg.ImageFormatException;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGEncodeParam;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
import com.sun.media.jai.codec.SeekableStream;

/**
 * Utility to resize image or create image thumbnail
 * @author [email protected]
 * @required: Java Advanced Imaging API (JAI) library
 */
public class ImageThumbnail {
 
  public static void main(String[] args) throws Exception {
    ImageThumbnail it = new ImageThumbnail();
    JPanel p = new JPanel(new GridLayout(1,1,1,1));
    SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss.S");
    String fileName = "view.jpg";
    System.out.println("Begin "+sdf.format(new Date()));       
    InputStream inputStream = new FileInputStream(fileName);
    BufferedImage bimg = it.getThumbnail(inputStream, 128);
    //Image image = ImageIO.read(new File(fileName));
    //BufferedImage bimg = it.getThumbnail((BufferedImage)image, 128, 96);
    it.saveJpeg("temp.jpg", bimg, 0.7f);
    System.out.println("Finish "+sdf.format(new Date()));       
    ImageIcon icon = new ImageIcon(bimg);
    p.add(new JLabel(icon));
    JFrame f = new JFrame("Image File");
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.getContentPane().add(new JScrollPane(p));
    f.pack();
    f.setLocationRelativeTo(null);
    f.setVisible(true);
  }
 
  public ImageThumbnail() {
  }
 
  /**
   * Create the image thumbnail, resize the image
   * @param imageInputStream as InputStream
   * @param targetWidth as int
   * @return BufferedImage
   * @throws IOException
   */
  public BufferedImage getThumbnail(InputStream imageInputStream, int targetWidth) throws IOException {
    SeekableStream seekableImageStream;
    RenderedOp originalImage, resizedImage;
    RenderedImage image;
    RenderingHints hints;
    BufferedImage bufferedImage;
   
    System.setProperty("com.sun.media.jai.disableMediaLib", "true"); //get rid of exception for not using native acceleration
    seekableImageStream = SeekableStream.wrapInputStream(imageInputStream, true);
    originalImage = JAI.create("stream", seekableImageStream);
    ((OpImage) originalImage.getRendering()).setTileCache(null);
    image = originalImage;
    int imageWidth = originalImage.getWidth();
    double scale = (targetWidth > 0 && imageWidth > targetWidth) ? (double) targetWidth / imageWidth : 1.0;
    hints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
    resizedImage = SubsampleAverageDescriptor.create(image, new Double(scale), new Double(scale), hints);
    bufferedImage = resizedImage.getAsBufferedImage();
    return bufferedImage;     
  }
 
  /**
   * Here is another method but seems to be slower than above for some JPG image file
   * Create and get the image thumbnail
   * @param image as BufferedImage
   * @param thWidth
   * @param thHeight
   */
  public BufferedImage getThumbnail(BufferedImage image, int thWidth, int thHeight) throws Exception {
 
    int w = image.getWidth();
    int h = image.getHeight();
   
    BufferedImage thumbnail = new BufferedImage(thWidth, thHeight, BufferedImage.TYPE_INT_RGB);
 
    float scalex = ((float)w) / ((float) thWidth);
    float scaley = ((float)h) / ((float) thHeight);
   
    float scale = Math.min(scalex, scaley);
    if (scale < 1) scale = 1;
   
    BufferedImage scaled = createResizedImage(image, Math.round(w/scale), Math.round(h/scale));
   
    Graphics2D g = thumbnail.createGraphics();
    g.setColor(Color.white);
    g.fillRect(0, 0, thWidth, thHeight);
    g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
    g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g.drawImage(scaled, (thWidth - scaled.getWidth()) / 2, (thHeight - scaled.getHeight()) / 2, null);
    g.dispose();
   
    return thumbnail;
 
  }
 
  /**
   * Resize the image
   * @param img as BufferedImage
   * @param targetWidth
   * @param targetHeight
   */
  private BufferedImage createResizedImage(BufferedImage img, int targetWidth, int targetHeight) {
 
    if (img.getWidth() == targetHeight && img.getHeight() == targetHeight) return img;
   
    boolean higherQuality = true;
    boolean isIndexColorModel = img.getColorModel() instanceof IndexColorModel;
    int type;
   
    if (!isIndexColorModel){
      type = (img.getColorModel().hasAlpha()) ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB;
    }
    else {type = img.getType();}
   
    BufferedImage ret = (BufferedImage) img;
   
    int w, h;
    if (higherQuality) {
      w = img.getWidth();
      h = img.getHeight();
    } else {
      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 = isIndexColorModel ?
      new BufferedImage(w, h, type, (IndexColorModel)img.getColorModel()) :
      new BufferedImage(w, h, type);
     
      Graphics2D g2 = tmp.createGraphics();
      g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
      g2.drawImage(ret, 0, 0, w, h, null);
      g2.dispose();
     
      ret = tmp;
    } while (w != targetWidth || h != targetHeight);
   
    return ret;
  }
 
  /**
   * Write the jpeg file
   * @param outFile
   * @param bimg
   * @param quality
   */
  public void saveJpeg(String outFile, BufferedImage bimg, float quality) throws ImageFormatException, IOException {
 
    FileOutputStream out = new FileOutputStream(outFile);
   
    // Encodes image as a JPEG data stream
    JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);   
    JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(bimg);
    param.setQuality(quality, true);
   
    encoder.setJPEGEncodeParam(param);
    encoder.encode(bimg);   
  }
 
}