com.ms.commons.file.service.ImageUtil.java Source code

Java tutorial

Introduction

Here is the source code for com.ms.commons.file.service.ImageUtil.java

Source

/*
 * Copyright 2011-2016 ZXC.com All right reserved. This software is the confidential and proprietary information of
 * ZXC.com ("Confidential Information"). You shall not disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into with ZXC.com.
 */
package com.ms.commons.file.service;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.im4java.core.CompositeCmd;
import org.im4java.core.ConvertCmd;
import org.im4java.core.IMOperation;
import org.im4java.core.IdentifyCmd;
import org.im4java.core.ImageCommand;
import org.im4java.core.Info;
import org.im4java.core.InfoException;
import org.im4java.process.ArrayListOutputConsumer;

import com.ms.commons.cons.ItemImageCons;
import com.ms.commons.file.image.GravityEnum;
import com.ms.commons.file.image.ImageFormat;
import com.ms.commons.lang.Assert;

/**
 * @author zxc Apr 12, 2013 1:18:30 PM
 */
public class ImageUtil implements ItemImageCons {

    /**
     * ?
     */
    public static final String IMAGE_QUALITY = "Quality";
    /**
     * 
     */
    public static final String IMAGE_HEIGHT = "Height";
    /**
     * 
     */
    public static final String IMAGE_WIDTH = "Width";
    /**
     * ?
     */
    public static final String IMAGE_FORMAT = "Format";

    /**
     * 
     */
    public static final boolean isDebug = false;

    // ???
    static {
        try {
            new File(TEMP_IMAGE_PATH).mkdirs();
        } catch (Exception e) {
        }
    }

    // ??
    public static boolean isDebugMode() {
        return isDebug;
    }

    /**
     * @param imgFile
     * @return
     * @throws InfoException
     */
    public static Info getImageInfo(String imgFile) throws InfoException {
        Info imageInfo = new Info(imgFile);
        return imageInfo;
    }

    /**
     * ?
     * 
     * <pre>
     * identify -verbose ??
     * </pre>
     * 
     * @param imageInfo
     * @param propertyName
     * @return
     */
    public static String getProperty(Info imageInfo, String propertyName) {
        String propertyValue = imageInfo.getProperty(propertyName);
        if (propertyValue == null) {
            Enumeration<String> propertyNames = imageInfo.getPropertyNames();
            String value = null;
            while (propertyNames.hasMoreElements()) {
                value = propertyNames.nextElement();
                if (value.indexOf(propertyName) != -1) {
                    propertyValue = imageInfo.getProperty(value);
                    break;
                }
            }
        }
        return propertyValue;
    }

    // /////////////////////////////////////////////////////////////////////
    //
    // ?
    //
    // /////////////////////////////////////////////////////////////////////
    /**
     * ?:?,?
     * 
     * <pre>
     *    %b   file size of image read in
     *    %c   comment property
     *    %d   directory component of path
     *    %e   filename extension or suffix
     *    %f   filename (including suffix)
     *    %g   layer canvas page geometry   ( = %Wx%H%X%Y )
     *    %h   current image height in pixels
     *    %i   image filename (note: becomes output filename for "info:")
     *    %k   number of unique colors
     *    %l   label property
     *    %m   image file format (file magic)
     *    %n   exact number of images in current image sequence
     *    %o   output filename  (used for delegates)
     *    %p   index of image in current image list
     *    %q   quantum depth (compile-time constant)
     *    %r   image class and colorspace
     *    %s   scene number (from input unless re-assigned)
     *    %t   filename without directory or extension (suffix)
     *    %u   unique temporary filename (used for delegates)
     *    %w   current width in pixels
     *    %x   x resolution (density)
     *    %y   y resolution (density)
     *    %z   image depth (as read in unless modified, image save depth)
     *    %A   image transparency channel enabled (true/false)
     *    %C   image compression type
     *    %D   image dispose method
     *    %G   image size ( = %wx%h )
     *    %H   page (canvas) height
     *    %M   Magick filename (original file exactly as given,  including read mods)
     *    %O   page (canvas) offset ( = %X%Y )
     *    %P   page (canvas) size ( = %Wx%H )
     *    %Q   image compression quality ( 0 = default )
     *    %S   ?? scenes ??
     *    %T   image time delay
     *    %W   page (canvas) width
     *    %X   page (canvas) x offset (including sign)
     *    %Y   page (canvas) y offset (including sign)
     *    %Z   unique filename (used for delegates)
     *    %@   bounding box
     *    %#   signature
     *    %%   a percent sign
     *    \n   newline
     *    \r   carriage return
     * </pre>
     * 
     * @param imageFilePath 
     * @return Map?,Map
     */
    public static Map<String, String> getImageBasicInfo(String imageFilePath) {
        // create operation
        IMOperation op = new IMOperation();
        op.ping();
        // op.format("%m\n%w\n%h\n%g\n%W\n%H\n%G\n%z\n%r\n%Q");
        op.format("%m\n%w\n%h\n%Q");
        op.addImage(imageFilePath);

        try {
            // execute ...
            IdentifyCmd identify = new IdentifyCmd();
            ArrayListOutputConsumer output = new ArrayListOutputConsumer();
            identify.setOutputConsumer(output);
            identify.run(op);

            // ... and parse result
            ArrayList<String> cmdOutput = output.getOutput();
            Iterator<String> iter = cmdOutput.iterator();
            Map<String, String> map = new Hashtable<String, String>();
            map.put(IMAGE_FORMAT, iter.next());
            map.put(IMAGE_WIDTH, iter.next());
            map.put(IMAGE_HEIGHT, iter.next());
            map.put(IMAGE_QUALITY, iter.next());
            return map;
        } catch (Exception ex) {
            return Collections.emptyMap();
        }
    }

    /**
     * 
     */
    public static Map<String, Integer> getImageWH(String imageFilePath) {
        // create operation
        IMOperation op = new IMOperation();
        op.ping();
        // op.format("%m\n%w\n%h\n%g\n%W\n%H\n%G\n%z\n%r\n%Q");
        op.format("%w\n%h");
        op.addImage(imageFilePath);

        try {
            // execute ...
            IdentifyCmd identify = new IdentifyCmd();
            ArrayListOutputConsumer output = new ArrayListOutputConsumer();
            identify.setOutputConsumer(output);
            identify.run(op);

            // ... and parse result
            ArrayList<String> cmdOutput = output.getOutput();
            Iterator<String> iter = cmdOutput.iterator();
            Map<String, Integer> map = new Hashtable<String, Integer>();

            map.put(IMAGE_WIDTH, parseInt(iter.next()));
            map.put(IMAGE_HEIGHT, parseInt(iter.next()));
            return map;
        } catch (Exception ex) {
            return Collections.emptyMap();
        }
    }

    private static int parseInt(String value) {
        try {
            return Integer.parseInt(value);
        } catch (Exception e) {
            return -1;
        }
    }

    /**
     * 
     * 
     * @param srcFile ?
     * @param expectedWidth , -1??x?
     * @param expectedHeight -1??y?
     * @return ????
     */
    public static List<String> splitImage(File srcFile, Double quality, Integer expectedWidth,
            Integer expectedHeight) {
        try {
            String srcFileName = srcFile.getPath();
            int imgWidth;
            int imgHeight;
            try {
                Map<String, String> imageBasicInfo = getImageBasicInfo(srcFileName);
                imgWidth = Integer.parseInt(imageBasicInfo.get(IMAGE_WIDTH));
                imgHeight = Integer.parseInt(imageBasicInfo.get(IMAGE_HEIGHT));
            } catch (Exception e) {
                return Collections.emptyList();
            }

            // /////////////////////////////////////////////////////
            // debug
            // /////////////////////////////////////////////////////
            // String quality = imageBasicInfo.get(IMAGE_QUALITY);
            // String fileSize = getProperty(imageInfo, FILESIZE);
            // System.out.println(fileSize);
            // /////////////////////////////////////////////////////
            // debug
            // /////////////////////////////////////////////////////

            // 
            int col = 1;
            if (!(expectedWidth == null || expectedWidth == -1)) {
                col = imgWidth / expectedWidth;
                // 
                col = (col == 0 ? 1 : col);
            }

            // 
            int row = 1;
            if (!(expectedHeight == null || expectedHeight == -1)) {
                row = (imgHeight / expectedHeight);
                int left = imgHeight % expectedHeight;
                if (left > (expectedHeight / 2) && row > 0) {
                    row++;
                }
                // 
                row = (row == 0 ? 1 : row);
            }

            // 
            ConvertCmd convert = new ConvertCmd();
            IMOperation op = new IMOperation();
            // ??80
            if (quality != null && quality > 0) {
                op.quality(quality);
            }
            op.addImage(srcFile.getPath());
            op.crop(null, null, null, null, col + "x" + row + "@");

            int index = srcFileName.lastIndexOf('.');
            String prefix = srcFileName.substring(0, index);
            String suffix = srcFileName.substring(index);
            String destFileName = prefix + "_%d" + suffix;
            op.addImage(destFileName);
            convert.run(op);
            List<String> list = new ArrayList<String>();
            for (int i = 0; i < row; i++) {
                list.add(prefix + "_" + i + suffix);
            }
            return list;
        } catch (Exception e) {
            return Collections.emptyList();
        }

    }

    /**
     * ?+
     * 
     * @param sourceFilePath ?
     * @param destFilePath 
     * @param xOffset ?x??
     * @param yOffset ?y??
     * @param width ?
     * @param height ?
     * @param scaleWidth 
     * @param scaleHeight 
     * @return
     */
    public static boolean cutAndScaleImage(String sourceFilePath, String destFilePath, Double quality, int xOffset,
            int yOffset, int width, int height, int scaleWidth, int scaleHeight) {
        Assert.assertPositive(width, "???0");
        Assert.assertPositive(height, "???0");
        ConvertCmd convert = new ConvertCmd();
        IMOperation op = new IMOperation();
        // 
        String tmpDest = destFilePath /* + "_ross_tmp" */;
        // ??80
        if (quality != null && quality > 0) {
            op.quality(quality);
        }
        op.addImage(sourceFilePath);
        op.crop(width, height, xOffset, yOffset);
        op.addImage(tmpDest);
        try {
            // 
            convert.run(op);
        } catch (Exception e) {
            return false;
        }

        // 
        op = new IMOperation();
        op.addImage(tmpDest);
        op.scale(scaleWidth, scaleHeight);
        op.addImage(destFilePath);
        try {
            convert.run(op);
            // 
            if (!StringUtils.equals(tmpDest, destFilePath)) {
                new File(tmpDest).delete();
            }
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    /**
     * 
     * 
     * @param xOffset ?? ()
     * @param yOffset ?? ()
     * @param width ?
     * @param height ?
     * @param sourceFilePath ????
     * @param destFilePath ?????
     * @return ?true?false
     */
    public static boolean cutImage(String sourceFilePath, String destFilePath, Double quality, int xOffset,
            int yOffset, int width, int height) {
        Assert.assertPositive(width, "???0");
        Assert.assertPositive(height, "???0");
        ConvertCmd convert = new ConvertCmd();
        IMOperation op = new IMOperation();
        // ??80
        if (quality != null && quality > 0) {
            op.quality(quality);
        }
        op.addImage(sourceFilePath);
        op.crop(width, height, xOffset, yOffset);
        op.addImage(destFilePath);
        try {
            convert.run(op);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 
     * 
     * @param sourceImage 
     * @param width  
     * @param height  ?
     * @param destFilePath ?--???
     * @return ?true?false
     */
    public static boolean scaleImage(BufferedImage sourceImage, int width, int height, String destFilePath,
            Double quality) {
        Assert.assertNotNull(sourceImage, "??");
        Assert.assertNotBlank(destFilePath, "??");
        Assert.assertPositive(width, "??0");
        Assert.assertPositive(height, "??0");
        ConvertCmd convert = new ConvertCmd();
        IMOperation op = new IMOperation();
        if (quality != null && quality > 0) {
            op.quality(quality);
        }
        op.addImage();
        op.resize(width, height);
        op.addImage();
        try {
            convert.run(op, sourceImage, destFilePath);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 
     * 
     * @param sourceFilePath ??
     * @param width  
     * @param height  ?
     * @param destFilePath ?--???
     * @return ?true?false
     */
    public static boolean scaleImage(String sourceFilePath, int width, int height, String destFilePath,
            Double quality) {
        Assert.assertNotBlank(sourceFilePath, "??");
        Assert.assertNotBlank(destFilePath, "??");
        Assert.assertPositive(width, "??0");
        Assert.assertPositive(height, "??0");
        ConvertCmd convert = new ConvertCmd();
        IMOperation op = new IMOperation();
        if (quality != null && quality > 0) {
            op.quality(quality);
        }
        op.addImage(sourceFilePath);
        op.resize(width, height);
        op.addImage(destFilePath);
        try {
            convert.run(op);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    // ////////////////////////////////////////////////////////////////////////////////////////////////
    // ////////////////////////////////////////////////////////////////////////////////////////////////
    //
    // ?
    //
    // ////////////////////////////////////////////////////////////////////////////////////////////////
    // ////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * ?--?????
     * 
     * @param bgImage 
     * @param iconImage 
     * @param ? ??
     * @param x x??
     * @param y y??
     * @param width 
     * @param height 
     */
    public static String compositImage(String imageSavePath, ImageFormat format, String bgImage, String iconImage,
            GravityEnum position, Integer x, Integer y, Integer width, Integer height, Double rotation) {
        return compositImage(imageSavePath, format, bgImage, iconImage, position, x, y, width, height, rotation,
                false, null);
    }

    // ?
    public static String compositImage(String imageSavePath, ImageFormat format, String bgImage, String iconImage,
            GravityEnum position, Integer x, Integer y, Integer width, Integer height, Double rotation,
            boolean deleteIconFile, Double quality) {
        ImageCommand cmd = new CompositeCmd();
        IMOperation op = new IMOperation();
        // ?
        if (quality != null) {
            op.quality(quality);
        } else {
            op.quality(85d);
        }
        // ///////////////////////////////
        // NorthWest NorthEast
        //
        // SouthWest SouthEast
        // ///////////////////////////////
        // 
        if (rotation != null) {
            op.rotate(rotation);
            op.background("none");
        }
        // ?
        if (position != null) {
            op.gravity(position.getValue());
        }
        // 
        x = (x == null ? 0 : x);
        y = (y == null ? 0 : y);
        // ?
        op.geometry(width, height, x, y);
        // 
        op.addImage();
        // 
        op.addImage();
        // ??
        op.addImage();
        String dsrc = imageSavePath + System.currentTimeMillis() + format.getImageSuffix();
        try {
            cmd.run(op, iconImage, bgImage, dsrc);
            // ?
            long size = new File(dsrc).length();
            boolean isTooBig = size > MAX_IMAGE_SIZE_512000;
            if (isTooBig) {
                // ?
                try {
                    ImageCommand cvt = new ConvertCmd();
                    op = new IMOperation();
                    // ?
                    op.quality(Math.min((100d * MAX_IMAGE_SIZE_512000) / size % 100, 80d));
                    String dsrc2 = imageSavePath + System.currentTimeMillis() + format.getImageSuffix();
                    op.addImage();
                    op.addImage();
                    cvt.run(op, dsrc, dsrc2);
                    return dsrc2;
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return dsrc;
        } catch (Exception e) {
            e.printStackTrace();
            return StringUtils.EMPTY;
        } finally {
            if (deleteIconFile) {
                deleteFileQuitely(iconImage);
            }
        }

    }

    /**
     * 
     * 
     * @param text 
     * @param textColor 
     * @param fontName ??
     * @param circleFillColor 
     * @param circleStrokeColor 
     * @param x x?
     * @param y y?
     * @param w 
     * @return
     */
    public static String drawCircleWithText(String imageSavePath, String text, String textColor, String fontName,
            Integer fontSize, String circleFillColor, String circleStrokeColor, Integer circleStrokeWidth,
            Integer x, Integer y, Integer w, Double rotation) {
        // 
        String circleImagePath = drawCircle(w, w, circleFillColor, circleStrokeColor, circleStrokeWidth, x, y, w,
                0d);
        // 
        if (fontSize == null) {
            fontSize = 24;
        }
        String textImagePath = createTextImage(text, false, fontName, fontSize, textColor, 0d);
        // ?
        String destImagePath = compositImage(imageSavePath, ImageFormat.PNG, circleImagePath, textImagePath,
                GravityEnum.Center, x, y, null, null, null);
        destImagePath = rotateImageWisely(destImagePath, rotation);
        // 
        if (!isDebugMode()) {
            deleteFileQuitely(circleImagePath);
            deleteFileQuitely(textImagePath);
        }
        return destImagePath;
    }

    /**
     * 
     * 
     * @param text
     * @param textColor
     * @param fontName
     * @param fontSize
     * @param rectFillColor
     * @param rectStrokeColor
     * @param rectStrokeWidth
     * @param x
     * @param y
     * @param w
     * @param h
     * @return
     */
    public static String drawRectangleWithText(String imageSavePath, String text, String textColor, String fontName,
            Integer fontSize, String rectFillColor, String rectStrokeColor, Integer rectStrokeWidth, Integer x,
            Integer y, Integer w, Integer h, Double rotation) {
        // 
        String rectImagePath = drawRectangle(x, y, w, h, 0, rectFillColor, rectStrokeColor, rectStrokeWidth, 0d);
        // 
        if (fontSize == null) {
            fontSize = 24;
        }
        String textImagePath = createTextImage(text, false, fontName, fontSize, textColor, 0d);
        // String textImagePath = drawText(text, PositionEnum.Center, fontName, fontSize, textColor, "none", w, h);
        int gap = 2;
        x += gap;
        // ?
        String destImagePath = compositImage(imageSavePath, ImageFormat.PNG, rectImagePath, textImagePath,
                GravityEnum.Center, x, y, null, null, 0d);
        destImagePath = rotateImageWisely(destImagePath, rotation);
        // 
        if (!isDebugMode()) {
            deleteFileQuitely(rectImagePath);
            deleteFileQuitely(textImagePath);
        }
        return destImagePath;

    }

    /**
     * 
     * 
     * @param text 
     * @param autoWrapper ??
     * @param fontName ??
     * @param fontSize ?
     * @param textColor 
     * @return
     */
    public static String createTextImage(String text, boolean autoWrapper, String fontName, Integer fontSize,
            String textColor, Double rotation) {
        ConvertCmd convert = new ConvertCmd();
        IMOperation op = new IMOperation();
        op.background("none");
        op.antialias();
        if (StringUtils.isNotBlank(textColor)) {
            op.fill(textColor);
        }
        if (StringUtils.isNotBlank(fontName)) {
            op.font(fontName);
        }
        if (fontSize != null) {
            op.pointsize(fontSize);
        }
        if (autoWrapper) {
            op.addRawArgs("caption:" + text);
        } else {
            op.addRawArgs("label:" + text);
        }
        String destImage = TEMP_IMAGE_PATH + System.currentTimeMillis() + ImageFormat.PNG.getImageSuffix();
        op.addImage(destImage);
        try {
            convert.run(op);
            destImage = rotateImageWisely(destImage, rotation);
            return destImage;
        } catch (Exception e) {
            e.printStackTrace();
            return StringUtils.EMPTY;
        }
    }

    /**
     * 
     * 
     * @param x
     * @param y
     * @param w
     * @param h
     * @param fillColor
     * @param strokeColor
     * @param strokeWidth
     * @return
     */
    public static String drawRectangle(Integer x, Integer y, Integer w, Integer h, Integer round, String fillColor,
            String strokeColor, Integer strokeWidth, Double rotation) {
        // 
        x = (x == null ? 0 : x);
        y = (y == null ? 0 : y);
        ConvertCmd convert = new ConvertCmd();
        IMOperation op = new IMOperation();
        op.size(w, h);
        // op.addRawArgs("canvas:none");
        op.addRawArgs("xc:none");
        op.antialias();
        if (StringUtils.isNotBlank(fillColor)) {
            op.fill(fillColor);
        }

        boolean isValidStrokeWidth = strokeWidth != null && strokeWidth > 0;
        if (StringUtils.isNotBlank(strokeColor)) {
            op.stroke(strokeColor);
            if (!isValidStrokeWidth) {
                strokeWidth = 1;
                isValidStrokeWidth = true;
            }
        }
        if (isValidStrokeWidth) {
            op.strokewidth(strokeWidth);
            int gap = strokeWidth;
            if (w != null) {
                w -= gap;
            }
            if (h != null) {
                h -= gap;
            }
        }
        if (round == null) {
            round = 0;
        }
        // roundRectangle
        // String cmd = "rectangle " + x + "," + y + " " + w + "," + h;
        String cmd = "roundRectangle " + x + "," + y + " " + w + "," + h + " " + round + "," + round;
        op.draw(cmd);
        // FIXMEpng??
        String destImage = TEMP_IMAGE_PATH + System.currentTimeMillis() + ImageFormat.PNG.getImageSuffix();
        op.addImage(destImage);
        try {
            convert.run(op);
            destImage = rotateImageWisely(destImage, rotation);
            return destImage;
        } catch (Exception e) {
            e.printStackTrace();
            return StringUtils.EMPTY;
        }
    }

    /**
     * 
     * 
     * <pre>
     * circleellipse???????circle???
     * ????ellipse????
     * ?0~3600?????1/4
     * ellipsecircle
     * </pre>
     * 
     * @param width 
     * @param height 
     * @param fillColor 
     * @param bgColor 
     * @param strokeColor
     * @param x
     * @param y
     * @param w
     * @return
     */
    public static String drawCircle(Integer width, Integer height, String fillColor, String strokeColor,
            Integer strokeWidth, Integer x, Integer y, Integer w, Double rotation) {

        int radias = w / 2;
        Integer centerX = x + radias;
        Integer centerY = y + radias;
        ConvertCmd convert = new ConvertCmd();
        IMOperation op = new IMOperation();
        op.size(width, height);
        op.addRawArgs("canvas:none");
        op.antialias();
        op.fill(fillColor);
        if (StringUtils.isNotBlank(strokeColor)) {
            op.stroke(strokeColor);
        }
        if (strokeWidth != null) {
            op.strokewidth(strokeWidth);
        }
        String cmd = "ellipse " + centerX + "," + centerY + " " + radias + "," + radias + " 0,360";
        op.draw(cmd);
        // FIXMEpng??
        String destImage = TEMP_IMAGE_PATH + System.currentTimeMillis() + ".png";
        op.addImage(destImage);
        try {
            convert.run(op);
            destImage = rotateImageWisely(destImage, rotation);
            return destImage;
        } catch (Exception e) {
            e.printStackTrace();
            return StringUtils.EMPTY;
        }
    }

    // ?
    public static String rotateImage(String originalImagePath, Double rotation) {
        // FIXME?Java?
        // JavaRuntime?
        // convert 1372771154717.jpg -virtual-pixel none +distort SRT '20' rotate_normal2.png
        String destImage = TEMP_IMAGE_PATH + System.currentTimeMillis() + ".png";
        try {
            String command = null;
            command = "convert " + originalImagePath + " -virtual-pixel none +distort SRT '" + rotation + "' "
                    + destImage;
            Process process = Runtime.getRuntime().exec(command);
            process.waitFor();
            int exitValue = process.exitValue();
            return exitValue == 0 ? destImage : StringUtils.EMPTY;
        } catch (IOException e1) {
            e1.printStackTrace();
            return StringUtils.EMPTY;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return StringUtils.EMPTY;
        }
        // ConvertCmd convert = new ConvertCmd();
        // IMOperation op = new IMOperation();
        // op.addImage(originalImagePath);
        // op.addRawArgs(" -virtual-pixel none +distort SRT '" + rotation + "' ");
        // op.addImage(destImage);
        // try {
        // convert.run(op);
        // return destImage;
        // } catch (Exception e) {
        // e.printStackTrace();
        // return StringUtils.EMPTY;
        // }

    }

    // FIXME: ??
    public static String _rotateImage(String originalImagePath, Double rotation) {
        // convert 1372771154717.jpg -virtual-pixel none +distort SRT '20' rotate_normal2.png
        ConvertCmd convert = new ConvertCmd();
        IMOperation op = new IMOperation();
        // op.virtualPixel(" none ");
        // op.p_distort(" STR " + rotation);
        op.addRawArgs(" -virtual-pixel none +distort SRT '" + rotation + "' ");
        String destImage = TEMP_IMAGE_PATH + System.currentTimeMillis() + ".png";
        op.addImage();
        op.addImage();

        // String destImage = IMAGE_PATH + System.currentTimeMillis() + ".png";
        // op.addRawArgs(" " + originalImagePath + " -virtual-pixel none +distort SRT '20' " + destImage);
        String command = null;
        try {
            convert.run(op, originalImagePath, destImage);
            return destImage;
        } catch (Exception e) {
            e.printStackTrace();
            command = "convert " + originalImagePath + " -virtual-pixel none +distort SRT " + rotation + " "
                    + destImage;
            try {
                Runtime.getRuntime().exec(command);
                return destImage;
            } catch (IOException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
            return StringUtils.EMPTY;
        }

    }

    /**
     * 
     * 
     * @param fillColor
     * @param strokeColor
     * @param strokeWidth
     * @param x
     * @param y
     * @param w
     * @param h
     * @return
     */
    public static String drawEclipse(String fillColor, String strokeColor, Integer strokeWidth, Integer x,
            Integer y, Integer w, Integer h, Double rotation) {
        int halfW = w / 2;
        int halfH = h / 2;

        Integer centerX = x + halfW;
        Integer centerY = y + halfH;
        ConvertCmd convert = new ConvertCmd();
        IMOperation op = new IMOperation();
        op.antialias();
        op.size(w, h);
        op.addRawArgs("canvas:none");
        op.fill(fillColor);
        if (StringUtils.isNotBlank(strokeColor)) {
            op.stroke(strokeColor);
        }
        if (strokeWidth != null) {
            op.strokewidth(strokeWidth);
        }
        String cmd = "ellipse " + centerX + "," + centerY + " " + halfW + "," + halfH + " 0,360";
        op.draw(cmd);
        // FIXME png??
        String destImage = TEMP_IMAGE_PATH + System.currentTimeMillis() + ".png";
        op.addImage(destImage);
        try {
            convert.run(op);
            destImage = rotateImageWisely(destImage, rotation);
            return destImage;
        } catch (Exception e) {
            e.printStackTrace();
            return StringUtils.EMPTY;
        }
    }

    /**
     * 
     * 
     * @param srcImage 
     * @param borderWidth 
     * @param color #00ff00?
     * @return
     */
    public static String borderImage(String srcImage, int borderWidth, String color) {
        ConvertCmd convert = new ConvertCmd();
        IMOperation op = new IMOperation();
        op.border(borderWidth);
        if (StringUtils.isNotBlank(color)) {
            op.bordercolor(color);
        }
        String destImage = TEMP_IMAGE_PATH + System.currentTimeMillis() + ImageFormat.PNG.getImageSuffix();
        op.addImage(srcImage);
        op.addImage(destImage);
        try {
            convert.run(op);
            return destImage;
        } catch (Exception e) {
            e.printStackTrace();
            return StringUtils.EMPTY;
        }
    }

    // 
    private static boolean deleteFileQuitely(String fileName) {
        return FileUtils.deleteQuietly(new File(fileName));
    }

    // ???
    private static String rotateImageWisely(String destImagePath, Double rotation) {
        if (rotation != null && rotation % 360 != 0) {
            // try {
            // Thread.sleep(100);
            // } catch (InterruptedException e) {
            // }
            String rotateImagePath = rotateImage(destImagePath, rotation);
            if (!StringUtils.isBlank(rotateImagePath)) {
                destImagePath = rotateImagePath;
            } else {
                // TODO: 
            }
        }
        return destImagePath;
    }

    public static String drawText(String text, GravityEnum gravity, String fontName, Integer fontSize,
            String textColor, String bgColor, Integer width, Integer height, Double rotation) {
        ConvertCmd convert = new ConvertCmd();
        IMOperation op = new IMOperation();
        op.antialias();
        op.size(width, height);
        if (StringUtils.isBlank(bgColor)) {
            bgColor = "none";
        }
        op.addRawArgs("xc:" + bgColor);
        op.fill(textColor);
        if (gravity != null) {
            op.gravity(gravity.getValue());
        }
        op.font(fontName);
        if (fontSize == null) {
            fontSize = DEFAULT_FONT_SIZE;
        }
        op.pointsize(fontSize);
        op.draw("text 0,0 '" + text + "'");

        String destImage = TEMP_IMAGE_PATH + System.currentTimeMillis() + ImageFormat.PNG.getImageSuffix();
        op.addImage(destImage);
        try {
            convert.run(op);// 
            destImage = rotateImageWisely(destImage, rotation);
            return destImage;
        } catch (Exception e) {
            e.printStackTrace();
            return StringUtils.EMPTY;
        }
    }

    /**
     * ? -- ?
     * 
     * @param srcImagePath
     * @return
     */
    public static String autoResize(String srcImagePath) {
        return autoResize(srcImagePath, null);
    }

    public static String autoResize(String srcImagePath, String destImagePath) {
        ConvertCmd convert = new ConvertCmd();
        IMOperation op = new IMOperation();
        op.addImage(srcImagePath);

        op.trim();
        op.p_repage();
        if (StringUtils.isBlank(destImagePath)) {
            destImagePath = srcImagePath;
        }
        op.addImage(destImagePath);
        try {
            convert.run(op); // 
            return destImagePath;
        } catch (Exception e) {
            e.printStackTrace();
            return StringUtils.EMPTY;
        }
    }
}