ImageTools.java

package sk.iway.iwcm.common;

import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Iterator;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
import javax.imageio.stream.ImageOutputStream;

import org.apache.commons.lang.ArrayUtils;

import sk.iway.iwcm.Constants;
import sk.iway.iwcm.FileTools;
import sk.iway.iwcm.Logger;
import sk.iway.iwcm.Tools;
import sk.iway.iwcm.gallery.GalleryDB;
import sk.iway.iwcm.io.IwcmFile;
import sk.iway.iwcm.io.IwcmFsDB;
import sk.iway.iwcm.io.IwcmInputStream;
import sk.iway.iwcm.io.IwcmOutputStream;

/**
 *  Manipulacia s obrazkami
 *
 *@Title        WebJET
 *@Company      Interway s.r.o. (www.interway.sk)
 *@Copyright    Interway s.r.o. (c) 2001-2002
 *@author       $Author: jeeff $
 *@version      $Revision$
 *@created      Utorok, 2003, marec 30
 *@modified     $Date$
 */
public class ImageTools
{
	private ImageTools() {

	}

	/**
	 * Returns true if fileName is image file (ends with .jpg, png...)
	 * @param fileName
	 * @return
	 */
	public static boolean isImage(String fileName)
	{
		String ext = FileTools.getFileExtension(fileName);
		return ext.equalsIgnoreCase("jpg") || ext.equalsIgnoreCase("jpeg") || ext.equalsIgnoreCase("gif") || ext.equalsIgnoreCase("png") || ext.equalsIgnoreCase("svg");
	}

	public static int resizeImage(IwcmFile imageFile, int width, int height)
	{
		try
		{
			String srcUrlLC  = imageFile.getName().toLowerCase();

			if (imageFile.isFile() && (srcUrlLC.endsWith(".jpg") || srcUrlLC.endsWith(".jpeg") || srcUrlLC.endsWith(".gif") || srcUrlLC.endsWith(".png")))
			{
				if (GalleryDB.existsImageMagickConvertCommand()){
					return resizeImageImageMagick(imageFile,width,height);
				}
				else{
					return resizeImageGraphics2D(imageFile, width, height);
				}
			}
		}
		catch (Exception ex)
		{
			sk.iway.iwcm.Logger.error(ex);
		}
		return(3);

	}

	/**
	 * @param imageFile
	 * @param width
	 * @param height
	 * @return
	 */
	private static int resizeImageImageMagick(IwcmFile imageFile, int width, int height)
	{
		GalleryDB.stripExif(imageFile.getAbsolutePath());

		String[] args = new String[]{"imagemagick","from","-resize",width+"x"+height+"!","to"};
		return executeImageMagick(imageFile, args);
	}

	/**
	 * @param imageFile
	 * @param width
	 * @param height
	 * @return
	 * @throws IOException
	 * @throws FileNotFoundException
	 */
	private static int resizeImageGraphics2D(IwcmFile imageFile, int width, int height) throws IOException, FileNotFoundException
	{
		//opener = new Opener();
		//imp = opener.openImage(realPath);
		BufferedImage originalImage = ImageIO.read(new IwcmInputStream(imageFile));

		double scaleFactor = (width * 1.0) / (originalImage.getWidth() * 1.0);
		double scaleFactor2 = (height * 1.0) / (originalImage.getHeight() * 1.0);

		int w = (int) (originalImage.getWidth() * scaleFactor);
		int h = (int) (originalImage.getHeight() * scaleFactor2);

		Image resizedImage = originalImage.getScaledInstance(w, h, Image.SCALE_AREA_AVERAGING);
		BufferedImage bufSmall = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
		bufSmall.getGraphics().drawImage(resizedImage, 0, 0, null);
		bufSmall.getGraphics().dispose();


		try
		{
			ImageWriteParam iwparam = new JPEGImageWriteParam(null);
			iwparam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT) ;
			iwparam.setCompressionQuality(0.85F);

			//ImageIO.write(bufSmall, format, iwparam, fSmallImg);
			//Jimi.putImage("image/" + type, image, realPathSmall);

			ImageWriter writer = null;
			@SuppressWarnings("rawtypes")
			Iterator iter = ImageIO.getImageWritersByFormatName("jpg");
			if (iter.hasNext()) {
				 writer = (ImageWriter)iter.next();
			}

			if (writer != null) {
				if (imageFile.exists())
				{
					imageFile.delete();
				}

				// Prepare output file
				IwcmOutputStream iwos = new IwcmOutputStream(imageFile);
				ImageOutputStream ios = ImageIO.createImageOutputStream(iwos);
				writer.setOutput(ios);

				// Write the image
				writer.write(null, new IIOImage(bufSmall, null, null), iwparam);

				// Cleanup
				ios.flush();
				writer.dispose();
				ios.close();
				iwos.close();
			}

			return(0);
		}
		catch (Exception ex)
		{
			sk.iway.iwcm.Logger.error(ex);
			return(2);
		}
	}

	/**
	 * Resizne obrazok, vrati:
	 * 0-vsetko je OK
	 * 1-obrazok je uz teraz mensi
	 * 2-nepodarilo sa ulozit obrazok
	 * 3-nastala neznama chyba
	 * 4-obrazok je v nepodporovanom formate
	 *
	 * @param srcUrl - url adresa obrazku
	 * @param width  - sirka
	 * @param height - vyska
	 * @return
	 */
	public static int resizeImage(String srcUrl, int width, int height)
	{
		IwcmFile imageFile = new IwcmFile(Tools.getRealPath(srcUrl));
		return resizeImage(imageFile, width, height);
	}

	public static int cropImage(IwcmFile imageFile, int width, int height, int startX, int startY)
	{
		try
		{
			String srcUrlLC = imageFile.getName().toLowerCase();

			if (imageFile.isFile() && (srcUrlLC.endsWith(".jpg") || srcUrlLC.endsWith(".jpeg") || srcUrlLC.endsWith(".gif") || srcUrlLC.endsWith(".png")))
			{
				if (GalleryDB.existsImageMagickConvertCommand()){
					return cropImageImageMagick(imageFile, width, height, startX, startY);
				}
				else{
					return cropImageGraphics2D(imageFile, width, height, startX, startY);
				}
			}
		}
		catch (Exception ex)
		{
			sk.iway.iwcm.Logger.error(ex);
		}
		return(3);

	}

	/**
	 * @param imageFile
	 * @param width
	 * @param height
	 * @param startX
	 * @param startY
	 * @return
	 */
	private static int cropImageImageMagick(IwcmFile imageFile, int width, int height, int startX, int startY)
	{
		GalleryDB.stripExif(imageFile.getAbsolutePath());

		String[] args = new String[]{"imagemagick", "from", "-crop", width+"x"+height+"+"+startX+"+"+startY, "to"};
		return executeImageMagick(imageFile,args);
	}

	/**
	 * @param imageFile
	 * @param width
	 * @param height
	 * @param startX
	 * @param startY
	 * @return
	 * @throws IOException
	 * @throws FileNotFoundException
	 */
	private static int cropImageGraphics2D(IwcmFile imageFile, int width, int height, int startX, int startY) throws IOException,
				FileNotFoundException
	{
		//double scaleFactor = 0.3;
		//double scaleFactor2 = 0.3;

		BufferedImage originalImage = ImageIO.read(new IwcmInputStream(imageFile));

		Image cropedImage = originalImage.getSubimage(startX, startY, width, height);

		BufferedImage bufSmall = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
		bufSmall.getGraphics().drawImage(cropedImage, 0, 0, null);
		bufSmall.getGraphics().dispose();

		try
		{
			ImageWriteParam iwparam = new JPEGImageWriteParam(null);
			iwparam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT) ;
			iwparam.setCompressionQuality(0.85F);

			ImageWriter writer = null;
			@SuppressWarnings("rawtypes")
			Iterator iter = ImageIO.getImageWritersByFormatName("jpg");
			if (iter.hasNext()) {
				 writer = (ImageWriter)iter.next();
			}
			if (writer != null) {
				// Prepare output file
				IwcmOutputStream iwos = new IwcmOutputStream(imageFile);
				ImageOutputStream ios = ImageIO.createImageOutputStream(iwos);
				writer.setOutput(ios);

				// Write the image
				writer.write(null, new IIOImage(bufSmall, null, null), iwparam);

				// Cleanup
				ios.flush();
				writer.dispose();
				ios.close();
				iwos.close();
			}

			return(0);
		}
		catch (Exception ex)
		{
			sk.iway.iwcm.Logger.error(ex);
			return(2);
		}
	}
	public static int cropImage(String srcUrl, int width, int height, int startX, int startY)
	{
		IwcmFile imageFile = new IwcmFile(Tools.getRealPath(srcUrl));
		return  cropImage(imageFile, width, height, startX, startY);
	}

	public static int rotateImage(IwcmFile imageFile, double angle)
	{

		try
		{
			String srcUrlLC = imageFile.toString().toLowerCase();

			if (imageFile.isFile() && (srcUrlLC.endsWith(".jpg") || srcUrlLC.endsWith(".jpeg") || srcUrlLC.endsWith(".gif") || srcUrlLC.endsWith(".png")))
			{
				if (GalleryDB.existsImageMagickConvertCommand()){
					return rotateImageImageMagick(imageFile, angle);
				}else{
					return rotateImageGraphics2D(imageFile, angle);
				}

			}
		}
		catch (Exception ex)
		{
			sk.iway.iwcm.Logger.error(ex);
		}
		return(3);

	}


	/**
	 * @param imageFile
	 * @param angle
	 * @return
	 */
	private static int rotateImageImageMagick(IwcmFile imageFile, double angle)
	{
		GalleryDB.stripExif(imageFile.getAbsolutePath());

		String[] args = new String[]{"imagemagick", "from", "-rotate", Double.toString(angle), "to"};
		return executeImageMagick(imageFile,args);
	}

	/**
	 * @param imageFile
	 * @param angle
	 * @param args
	 */
	private static int executeImageMagick(IwcmFile imageFile, String[] args)
	{
		int fromIndex = ArrayUtils.indexOf(args, "from");
		int toIndex = ArrayUtils.indexOf(args, "to");
		if (fromIndex < 0 || toIndex < 0)
		{
			throw new IllegalArgumentException("String argument array parameter must contain 'from' and 'to' ");
		}
		if (IwcmFsDB.useDBStorage(imageFile.getVirtualPath()))
		{
			try
			{
				String temporaryOriginal = IwcmFsDB.getTempFilePath(imageFile.getPath());
				String temporaryProcessed = IwcmFsDB.getTempFilePath(imageFile.getPath() + ".new");
				File temporaryProcessedFile = new File(temporaryProcessed);
				File imageFileFile = new File(imageFile.getAbsolutePath());
				IwcmFsDB.writeFileToDisk(imageFileFile, temporaryProcessedFile);
				args[fromIndex] = temporaryOriginal;
				args[toIndex] = temporaryProcessed;
				int result = GalleryDB.executeImageMagickCommand(args);
				if (result == 0)
				{
					IwcmFsDB.writeFileToDB(temporaryProcessedFile, imageFileFile);
					if (new File(temporaryOriginal).delete() && temporaryProcessedFile.delete())
					{
						return 0;
					}
				}
				else
				{
					return result;
				}
			}
			catch (IOException e)
			{
				sk.iway.iwcm.Logger.error(e);
			}
		}
		else
		{
			IwcmFile processed = new IwcmFile(imageFile.getAbsolutePath() + ".new");
			args[fromIndex] = imageFile.getAbsolutePath();
			args[toIndex] = processed.getAbsolutePath();
			int result = GalleryDB.executeImageMagickCommand(args);
			if (result == 0)
			{
				FileTools.copyFile(processed, imageFile);
				return processed.delete() ? 0 : -1;
			}
		}
		return -1;
	}

	/**
	 * @param imageFile
	 * @param angle
	 * @return
	 * @throws IOException
	 * @throws FileNotFoundException
	 */
	private static int rotateImageGraphics2D(IwcmFile imageFile, double angle) throws IOException, FileNotFoundException
	{
		BufferedImage originalImage = ImageIO.read(new IwcmInputStream(imageFile));

		int w = originalImage.getWidth();
		int h = originalImage.getHeight();

		//prepocet na radiany
		double angleRad = angle*(Math.PI*2)/360;

		double sin = Math.abs(Math.sin(angleRad));
		double cos = Math.abs(Math.cos(angleRad));

		int neww = (int)Math.floor(w*cos+h*sin);
		int newh = (int)Math.floor(h*cos+w*sin);


		//debug
/*	Logger.println(this,"*****************************************************");
		Logger.println(this,"image width= " +w+ "\t image height= " +h);
		Logger.println(this,"angle= "+angle+ "\t\t angleRad= " +angleRad);
		Logger.println(this,"sin= " +sin+ "\t\t cos= " +cos);
		Logger.println(this,"new width= " +neww+ "\t\t new height= " +newh);
		Logger.println(this,"*****************************************************");
*/


		//int transparency = originalImage.getColorModel().getTransparency();

		/*GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
		GraphicsDevice gd = ge.getDefaultScreenDevice();
		GraphicsConfiguration gc = gd.getDefaultConfiguration();

		BufferedImage result = gc.createCompatibleImage(neww, newh); //, transparency);*/

		//BufferedImage result = originalImage.getScaledInstance(neww, newh, )
		BufferedImage result = new BufferedImage(neww, newh, BufferedImage.TYPE_INT_RGB);

		Graphics2D g = result.createGraphics();
		g.translate((neww-w)/(double)2, (newh-h)/(double)2);
		g.rotate(angleRad, w/(double)2, h/(double)2);
		g.drawRenderedImage(originalImage, null);

		Image rotatedImage = result.getSubimage(0, 0, result.getWidth(), result.getHeight());

		BufferedImage bufSmall = new BufferedImage(neww, newh, BufferedImage.TYPE_INT_RGB);
		bufSmall.getGraphics().drawImage(rotatedImage, 0, 0, null);
		bufSmall.getGraphics().dispose();

		try
		{
			ImageWriteParam iwparam = new JPEGImageWriteParam(null);
			iwparam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT) ;
			iwparam.setCompressionQuality(0.85F);

			//ImageIO.write(bufSmall, format, iwparam, fSmallImg);
			//Jimi.putImage("image/" + type, image, realPathSmall);

			ImageWriter writer = null;
			@SuppressWarnings("rawtypes")
			Iterator iter = ImageIO.getImageWritersByFormatName("jpg");
			if (iter.hasNext()) {
				 writer = (ImageWriter)iter.next();
			}
			if (writer != null) {
				// Prepare output file
				IwcmOutputStream iwos = new IwcmOutputStream(imageFile);
				ImageOutputStream ios = ImageIO.createImageOutputStream(iwos);
				writer.setOutput(ios);

				// Write the image
				writer.write(null, new IIOImage(bufSmall, null, null), iwparam);

				// Cleanup
				ios.flush();
				writer.dispose();
				ios.close();
				iwos.close();
			}

			return(0);
		}
		catch (Exception ex)
		{
			sk.iway.iwcm.Logger.error(ex);
			return(2);
		}
	}

	public static int rotateImage(String srcUrl, double angle)
	{
		return rotateImage(new IwcmFile(Tools.getRealPath(srcUrl)), angle);
	}

	/**
	 * Skonvertuje fotku do RGB ak je v CMYK, ak je konverzia povolena.
	 *
	 * @param filePath
	 */
	public static void convertCmykToRgb(String filePath)
	{
		if (Constants.getBoolean("galleryConvertCmykToRgb") == false)
			return;

		String imageMagickDir = GalleryDB.getImageMagicDir();
		boolean isCmyk = false;
		boolean identifyExist = false;
		boolean convertExists = false;
		String identifyFile = getIdentifyRuntimeFile();
		String runtimeFile = getMogrifyRuntimeFile();

		try
		{
			if (Tools.isNotEmpty(imageMagickDir))
			{
				//overime si ci existuje convert a identify
				File f = new File(imageMagickDir + File.separatorChar + identifyFile);
				if (f.exists() && f.canRead())
					identifyExist = true;
				else
					Logger.debug(ImageTools.class, "identify command not found");
				File fi = new File(imageMagickDir + File.separatorChar + runtimeFile);
				if (fi.exists() && fi.canRead())
					convertExists = true;
				else
					Logger.debug(ImageTools.class, "converter command not found");
			}

			if (Tools.isNotEmpty(imageMagickDir) && convertExists && identifyExist && Tools.isNotEmpty(Constants.getString("galleryConvertCmykToRgbInputProfilePath")) && Tools.isNotEmpty(Constants.getString("galleryConvertCmykToRgbOutputProfilePath")))
			{
				Logger.debug(GalleryDB.class, "executing image magick: " + imageMagickDir + File.separatorChar + identifyFile);
				Runtime rt = Runtime.getRuntime();

				String[] args = new String[3];




				args[0] = imageMagickDir + File.separatorChar + identifyFile;
				args[1] = "-verbose";
				args[2] = filePath;

				Process proc = rt.exec(args);
				InputStream stderr = proc.getInputStream();//getErrorStream();
				BufferedReader br = new BufferedReader(new InputStreamReader(stderr, Constants.FILE_ENCODING));
				String line = null;
				while ((line = br.readLine()) != null)
				{
					if (line.indexOf("CMYK")>-1)
					{
						isCmyk = true;
						break;
					}
				}
				br.close();

				//ak uploadovany obrazok je v CMYKu, skonvertujeme ho...
				if (isCmyk)
				{
					args = new String[11];

					if (IwcmFsDB.useDBStorage(IwcmFsDB.getVirtualPath(filePath)))
					{
						IwcmFsDB.writeFileToDisk(new File(filePath),new File(IwcmFsDB.getTempFilePath(filePath)));
						args[0] = imageMagickDir + File.separatorChar + runtimeFile;
						args[1] = "-profile";
						args[2] = Constants.getString("galleryConvertCmykToRgbInputProfilePath");
						args[3] = "-profile";
						args[4] = Constants.getString("galleryConvertCmykToRgbOutputProfilePath");
						args[5]	= "-set";
						args[6] = "colorspace";
						args[7] = "sRGB";
						args[8] = "-quality";
						args[9] = "85";
						args[10] = IwcmFsDB.getTempFilePath(filePath);
					}
					else
					{
						args[0] = imageMagickDir + File.separatorChar + runtimeFile;
						args[1] = "-profile";
						args[2] = Constants.getString("galleryConvertCmykToRgbInputProfilePath");
						args[3] = "-profile";
						args[4] = Constants.getString("galleryConvertCmykToRgbOutputProfilePath");
						args[5]	= "-set";
						args[6] = "colorspace";
						args[7] = "sRGB";
						args[8] = "-quality";
						args[9] = "85";
						args[10] = filePath;
					}

					StringBuilder params = new StringBuilder();
					for (int i = 0; i < args.length; i++)
					{
						params.append(' ').append(args[i]);
					}
					Logger.debug(GalleryDB.class, "LONGCMD:\n" + params);
					proc = rt.exec(args);
					stderr = proc.getInputStream();
					br = new BufferedReader(new InputStreamReader(stderr, Constants.FILE_ENCODING));
					while ((line = br.readLine()) != null)
					{
						Logger.debug(GalleryDB.class, line);
					}
					br.close();
					int exitValue = proc.waitFor();
					if (IwcmFsDB.useDBStorage(IwcmFsDB.getVirtualPath(filePath)))
					{
						IwcmFsDB.writeFileToDB(new File(IwcmFsDB.getTempFilePath(IwcmFsDB.getVirtualPath(filePath))), new File(IwcmFsDB.getVirtualPath(filePath)));
						new File(IwcmFsDB.getTempFilePath(IwcmFsDB.getVirtualPath(filePath))).delete();
					}
					Logger.debug(GalleryDB.class, "ExitValue: " + exitValue);
				}
			}

		}
		catch (Exception ex)
		{
			sk.iway.iwcm.Logger.error(ex);
		}
	}

	/**
	 * ziska prikaz programu Identify (sucast Imagemagick)
	 *
	 * @return
	 */
	public static String getIdentifyRuntimeFile()
	{
		String result = "identify";
		if (System.getProperty("os.name").indexOf("Windows") != -1)
		{
			result  = "identify.exe";
		}
		return result ;
	}

	/**
	 * prikaz mogrify funguje rovnako ako convert ale ma ako parameter iba jednu cestu, prepisuje povodny vstupny subor
	 * http://www.imagemagick.org/www/mogrify.html
	 *
	 * @return
	 */
	public static String getMogrifyRuntimeFile()
	{
		String result = "mogrify";
		if (System.getProperty("os.name").indexOf("Windows") != -1)
		{
			result  = "mogrify.exe";
		}
		return result ;
	}
}