ImageUtils.java

/*

 * Copyright (C) 2003-2010 eXo Platform SAS.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see<http://www.gnu.org/licenses/>.
 */
package org.exoplatform.social.core.image;

import org.exoplatform.commons.utils.MimeTypeResolver;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.social.core.model.AvatarAttachment;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

/**
 * @author tuan_nguyenxuan Oct 29, 2010
 */
public class ImageUtils {
  public static final String KEY_SEPARATOR           = "_";
  public static final String KEY_DIMENSION_SEPARATOR = "x";

  public static final String GIF_EXTENDSION          = "gif";
  private static final Log LOG = ExoLogger.getLogger(ImageUtils.class);

  /**
   * @param str Make string params not null
   */
  private static void makeNotNull(String... str)
  {
    for (String string : str) {
      if (string == null)
        string = "";
    }
  }

  /**
   * @param oldFileName
   * @param subfix
   * @param postfix
   * @return name of resized image name like from "avatar.jpg" to
   *         "RESIZED_avatar_100x100.jpg"
   */
  public static String buildFileName(String oldFileName, String subfix, String postfix) {
    makeNotNull(oldFileName,subfix,postfix);
    if(oldFileName.equals(""))
      return subfix + postfix;
    int dotIndex = oldFileName.lastIndexOf('.');
    if(dotIndex < 0)
      return subfix + oldFileName + postfix;
    String nameOnly = oldFileName.substring(0, dotIndex);
    String extendtionAndDot = oldFileName.substring(dotIndex);
    return subfix + nameOnly + postfix + extendtionAndDot;
  }

  /**
   * @param width
   * @param height
   * @return postfix for image name like avatar_100x100
   */

  public static String buildImagePostfix(int width, int height) {
    return KEY_SEPARATOR + (width < 0 ? 0 : width) + KEY_DIMENSION_SEPARATOR
        + (height < 0 ? 0 : height);
  }

  /**
   * @param imageStream
   * @param maxWidth
   * @param maxHeight
   * @param avatarId
   * @param avatarFileName
   * @param avatarMimeType
   * @param avatarWorkspace
   * @return new AvatarAtachment that contain parameter values and resized
   *         avatar
   */
  public static AvatarAttachment createResizedAvatarAttachment(InputStream imageStream,
                                                               int maxWidth,
                                                               int maxHeight,
                                                               String avatarId,
                                                               String avatarFileName,
                                                               String avatarMimeType,
                                                               String avatarWorkspace) {
    if (maxHeight <= 0 || maxWidth <= 0) {
      LOG.warn("Fail to resize image to avatar attachment with dimension <= 0");
      return null;
    }

    try {
      MimeTypeResolver mimeTypeResolver = new MimeTypeResolver();
      String extension = mimeTypeResolver.getExtension(avatarMimeType);
      // TODO: Resize gif image. Now we skip gif because we can't resize it now
      if (extension.equalsIgnoreCase(GIF_EXTENDSION)) {
        return null;
      }

      BufferedImage image = ImageIO.read(imageStream);

      int targetHeight = image.getHeight();
      int targetWidth = image.getWidth();

      double maxDimensionsRatio =  (double) maxHeight / (double) maxWidth;
      double imageRatio =  (double) image.getHeight() / (double) image.getWidth();

      if(imageRatio > maxDimensionsRatio && image.getHeight() > maxHeight) {
        targetHeight = maxHeight;
        targetWidth = (maxHeight * image.getWidth()) / image.getHeight();
      } else if(imageRatio < maxDimensionsRatio && image.getWidth() > maxWidth) {
        targetHeight = (maxWidth * image.getHeight()) / image.getWidth();
        targetWidth = maxWidth;
      }

      // Create temp file to store resized image to put to avatar attachment
      File tmp = File.createTempFile("RESIZED", null);
      image = resizeImage(image, targetWidth, targetHeight);

      ImageIO.write(image, extension, tmp);
      
      // Create new avatar attachment
      AvatarAttachment newAvatarAttachment = new AvatarAttachment(avatarId,
                                                                  avatarFileName,
                                                                  avatarMimeType,
                                                                  new FileInputStream(tmp),
                                                                  avatarWorkspace,
                                                                  System.currentTimeMillis());

      // Delete temp file
      tmp.delete();
      return newAvatarAttachment;
    } catch (Exception e) {
      LOG.error("Fail to resize image to avatar attachment: " + e);
      return null;
    }
  }

  private static BufferedImage resizeImage(BufferedImage image, int width, int height) {
    final BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    final Graphics2D graphics2D = bufferedImage.createGraphics();
    graphics2D.setComposite(AlphaComposite.Src);
    //below three lines are for RenderingHints for better image quality
    graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    graphics2D.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY);
    graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
    graphics2D.drawImage(image, 0, 0, width, height, null);
    graphics2D.dispose();
    return bufferedImage;
  }
}