GraphicsMagick gm4java im4java
最近对使用Java对操作这方面问题进行了下调研, 随即写了个图片操作工具类, 包括图片的裁剪, 缩放, 移动, 旋转, 加水印, 后续待追加
- 首先有两个成熟的操作图片的工具库, 分别是GM 和 IM, 在安装了两者其一的基础上, 打开CMD, 执行相关的命令, 就可实现对图片的一些操作
- 然后在Java领域里, 就有封装好了的通过调用对应的类库, 底层再去调用上面两者的命令行执行, 进而实现图片操作, gm4java, im4java和jmagick都是这样的工具
- GM算是IM的分支, 但是与IM相比有几个优势, 其中最大的优点就是它的性能要好
- gm4java本身比较简单, 就是一个执行器帮我们间接的去执行GM提供的一些命令, 但是它也提供了对im4java的支持!
…
现选择GM+gm4java实现图片的裁剪, 缩放, 拼合, 旋转, 移动功能, 然后在实现图片加水印的功能时一直行不通, 最后还是借用了im4java实现了, 后续有待改进
工具类实现:
package org.hinsteny.commons.io.image;
import org.gm4java.engine.GMException;
import org.gm4java.engine.GMService;
import org.gm4java.engine.GMServiceException;
import org.gm4java.engine.support.SimpleGMService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 图片裁剪, 变形, 加水印工具类
* @author Hinsteny
* @date 2017-08-07
* @since 1.0.0
*/
public class ImageUtil {
private static final Logger logger = LoggerFactory.getLogger(ImageUtil.class);
static {
try {
Runtime.getRuntime().exec("gm");
} catch (IOException e) {
logger.info("graphics_magick_path not set, so the ImageUtil may not work properly!");
e.printStackTrace();
}
}
/**
* 对图片进行指定宽高的缩放, 保持原图的宽高比, 对于所给宽高与原图呼应适配
* @param width
* @param height
* @param srcPath
* @param desPath
* @return
*/
public static boolean convertReSizeImage(Integer width, Integer height, String srcPath, String desPath) {
List<String> commonds = Arrays.asList(ActionEnum.CONVERT.getAction(), srcPath, OptionEnum.RESIZE.getOption(), width + "x" +height, desPath );
return doExecute(commonds);
}
/**
* 对图片进行指定宽高的变形, 如果宽高大于原尺寸会做拉伸处理, 但是小于的话不会压缩, 会进行裁剪
* @param width
* @param height
* @param srcPath
* @param desPath
* @return
*/
public static boolean convertReSizeImageTile(Integer width, Integer height, String srcPath, String desPath) {
List<String> commonds = Arrays.asList(ActionEnum.CONVERT.getAction(), OptionEnum.SIZE.getOption(), width + "x" +height, "tile:" + srcPath, desPath );
return doExecute(commonds);
}
/**
* 对两张图片进行叠加结合, 以baseImage为底, changeImage在上, 两张图片左上角对其, 最终outputImage和baseImage等大小
* @param changeImage
* @param baseImage
* @param outputImage
* @return
*/
public static boolean compositeImage(String changeImage, String baseImage, String outputImage) {
List<String> commonds = Arrays.asList(ActionEnum.COMPOSITE.getAction(), changeImage, baseImage, outputImage );
return doExecute(commonds);
}
/**
* 对两张图片进行混合, 比较preImage和sufImage的颜色不同之处, 进行反向颜色混合
* @param preImage
* @param sufImage
* @param maskImage
* @param outputImage
* @return
*/
public static boolean compositeImage(String preImage, String sufImage, String maskImage, String outputImage) {
List<String> commonds = new ArrayList<>();
commonds.addAll(Arrays.asList(ActionEnum.COMPOSITE.getAction(), OptionEnum.COMPOSE.getOption(), OptionEnum.DIFFERENCE.getOption(), preImage, sufImage));
if (null != maskImage && maskImage.length() > 0)
commonds.add(maskImage);
commonds.add(outputImage);
return doExecute(commonds);
}
/**
* 把超过两张的图片进行拼合, 最终呈现从上至下的依次连接
* @param inputImage ...
* @param outputImage
* @return
*/
public static boolean convertAppendImages(String outputImage, String ... inputImage) {
assert inputImage != null && inputImage.length > 1;
List<String> commonds = new ArrayList<>();
commonds.add(ActionEnum.CONVERT.getAction());
commonds.addAll(Arrays.asList(inputImage));
commonds.add(OptionEnum.APPEND.getOption());
commonds.add(outputImage);
return doExecute(commonds);
}
/**
* rotate the image
* 对一系列图片进行旋转
* @param degrees
* @param inputImage ...
* @return
*/
public static boolean mogrifyRotateImages(int degrees, String ... inputImage) {
assert inputImage != null && inputImage.length > 0;
List<String> commonds = new ArrayList<>();
commonds.add(ActionEnum.MOGRIFY.getAction());
commonds.add(OptionEnum.ROTATE.getOption());
commonds.add(String.valueOf(degrees));
commonds.addAll(Arrays.asList(inputImage));
return doExecute(commonds);
}
/**
* roll an image vertically or horizontally
* 对一系列图片基于原图基准位置进行左右或者上下移动
* @param horizontal 水平位移
* @param vertical 垂直位移
* @param inputImage ...
* @return
*/
public static boolean mogrifyRollImages(int horizontal, int vertical, String ... inputImage) {
assert inputImage != null && inputImage.length > 0;
List<String> commonds = new ArrayList<>();
commonds.addAll(Arrays.asList(ActionEnum.MOGRIFY.getAction(), OptionEnum.ROLL.getOption(), (horizontal > 0 ? "+" : "-") + horizontal + (vertical > 0 ? "+" : "-") + vertical));
commonds.addAll(Arrays.asList(inputImage));
return doExecute(commonds);
}
/**
* annotate an image with one text string
* 给一张图片加水印文字
* @param inputImage
* @param outputImage
* @param text
* @param text_color
* @param text_size
* @param x_point
* @param y_point
* @return
*/
public static boolean convertDrawTextToImage(String inputImage, String outputImage, String text, String text_color, int text_size, int x_point, int y_point) {
List<String> commonds = new ArrayList<>();
commonds.addAll(Arrays.asList(ActionEnum.CONVERT.getAction(), OptionEnum.POINTSIZE.getOption(), String.valueOf(text_size), OptionEnum.FILL.getOption(),
text_color, OptionEnum.DRAW.getOption(), "\"text " + String.valueOf(x_point) + "," + String.valueOf(y_point) + " \'" + text + "\'\"", inputImage, outputImage));
return doExecute(commonds);
}
/**
* 执行gm commonds
* @param commonds
* @return
*/
private static boolean doExecute(List<String> commonds) {
assert null != commonds && commonds.size() > 0;
String result = "";
try {
// System.err.println(commonds.stream().collect(Collectors.joining(" ")));
if (logger.isInfoEnabled()) {
// logger.info("Do excute commonds is: [{}]", commonds.stream().collect(Collectors.joining(" ")));
}
result = getGMService().execute(commonds);
} catch (GMException |GMServiceException | IOException e) {
e.printStackTrace();
if (logger.isInfoEnabled()){
logger.info("Result: {}, Exception: {}", result, e.toString());
}
return false;
}
return true;
}
private static GMService getGMService(){
return new SimpleGMService();
}
}
贴点测试代码:
package org.hinsteny.commons.io;
import org.hinsteny.commons.BaseTest;
import org.hinsteny.commons.ImageUitl;
import org.testng.annotations.Test;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
/**
* @Auther Hinsteny
* @Desc
* @Date 2017-08-07
* @copyright: 2017 All rights reserved.
*/
public class ImagesTest extends BaseTest {
private static final String IMAGE_TYPE = ".jpg";
private static final String IMAGE_Folder = "images";
private static final String TEST_IMAGE = "togepi.jpg";
private String getImgSourcePath(String fileName) {
if (null == fileName || fileName.replace(" +", "").length() == 0)
fileName = UUID.randomUUID().toString() + IMAGE_TYPE;
String path = ImagesTest.this.getClass().getResource("").getPath();
fileName = path.concat(IMAGE_Folder).concat(File.separator).concat(fileName);
return System.getProperty("os.name").toLowerCase().indexOf("win") > -1 ? fileName.substring(1) : fileName;
}
/**
* 等比缩放图片用例
* @throws IOException
*/
@Test
public void testReSizeImage() throws IOException {
String sourceFilePath = getImgSourcePath(TEST_IMAGE);
ImageUitl.convertReSizeImage(300, 300, sourceFilePath, getImgSourcePath(null));
}
.....
}
- O(∩_∩)O!*