最近写了一个java的图片合成相关的项目,真的是一踩一个坑,下面博主说说遇见的一些坑和怎么解决的这些问题。 本文主要讲图片合成,加文字还是比较简单的。
先讲下我的需求,把一张图片盖到另外一张图片上面,然后在图片上加上字
第一个版本
最开始的时候我用的是thumbnailator工具包,如上图效果
但是问题来了,这张图有很大的问题,两个图都是透明度发生了变化。
第二个版本
使用java自带的Graphics2D
代码如下
String blackPath = "";
String fontPath = "";
// 水印图
URL frontUrl = new URL(fontPath);
// 地图
URL blackUrl = new URL(blackPath);
BufferedImage blackImage = ImageIO.read(blackUrl );
BufferedImage frontImage = ImageIO.read(frontUrl );
// 拉伸下
Thumbnails.Builder<BufferedImage> of = Thumbnails.of(blackImage).size(750, 750);
blackImage = of.asBufferedImage();
Graphics2D g2d = blackImage.createGraphics();
// 在地图上绘制水印
g2d.drawImage(frontImage, 0, 0, 750, 750, null);
g2d.dispose();
这个代码可以合成图片,但是有两个问题
- 图片失真
- 图片合成后在叠加的部分颜色发生了变化
第三个版本
使用Toolkit.getDefaultToolkit() 读取image
图片image转化为bufferImage方法
public static BufferedImage toBufferedImage(Image image) {
if (image instanceof BufferedImage) {
return (BufferedImage)image;
}
// This code ensures that all the pixels in the image are loaded
image = new ImageIcon(image).getImage();
// Determine if the image has transparent pixels; for this method's
// implementation, see e661 Determining If an Image Has Transparent Pixels
//boolean hasAlpha = hasAlpha(image);
// Create a buffered image with a format that's compatible with the screen
BufferedImage bimage = null;
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
try {
// Determine the type of transparency of the new buffered image
int transparency = Transparency.OPAQUE;
/* if (hasAlpha) {
transparency = Transparency.BITMASK;
}*/
// Create the buffered image
GraphicsDevice gs = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gs.getDefaultConfiguration();
bimage = gc.createCompatibleImage(
image.getWidth(null), image.getHeight(null), transparency);
} catch (HeadlessException e) {
// The system does not have a screen
}
if (bimage == null) {
// Create a buffered image using the default color model
int type = BufferedImage.TYPE_INT_RGB;
//int type = BufferedImage.TYPE_3BYTE_BGR;//by wang
/*if (hasAlpha) {
type = BufferedImage.TYPE_INT_ARGB;
}*/
bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type);
}
// Copy image to buffered image
Graphics g = bimage.createGraphics();
// Paint the image onto the buffered image
g.drawImage(image, 0, 0, null);
g.dispose();
return bimage;
}
图片合成方法
String blackPath = "";
String fontPath = "";
// 水印图
URL frontUrl = new URL(fontPath);
// 地图
URL blackUrl = new URL(blackPath);
// 读取背景图
Image blackImage = Toolkit.getDefaultToolkit().getImage(blackUrl);
// 读取水印图
Image frontImage = Toolkit.getDefaultToolkit().getImage(frontUrl);
BufferedImage image = toBufferedImage(blackImage);
// 拉伸下,
Thumbnails.Builder<BufferedImage> of = Thumbnails.of(image).size(750, 750);
image = of.asBufferedImage();
Graphics2D g2d = image.createGraphics();
// 在地图上绘制水印
g2d.drawImage(frontImage, 0, 0, 750, 750, null);
g2d.dispose();
改进之后图片的失真问题解决了, 图片的颜色变化问题也解决了。
但是新问题又来了,合成的时候有时候成功有时候失败
上面这个问题真的是日了狗了。
在这里抱怨一下,程序员不怕不成功的代码,就怕这代码有时候成功,有时候失败,最后我找到问题所在,这个读取image的方法它不是同步的,也就是说必须把它变成同步。
第四个版本
这个方法是把图片读取从异步变成同步
@Slf4j
public class MediaTrackerRunner extends Applet implements Runnable
{
int speed = 5;
private String path;
public MediaTrackerRunner(String path) throws HeadlessException
{
this.path = path;
}
@Override
public void init()
{
super.init();
}
@Override
public void run()
{
while (true)
{
repaint(); // 重绘
try
{
// 休眠,休眠时间根据图像显示速度计算
Thread.sleep(1000 / speed);
}
catch (InterruptedException e)
{
throw new RuntimeException(e);
}
}
}
/**
* [简要描述]:<br/> 根据图片路径读取图片, 同步
* [详细描述]:<br/>
* @return java.awt.Image
* 2020/12/9 - 19:05
**/
public Image execute()
{
MediaTracker tracker = new MediaTracker(this);
URL fontUrl = null;
try
{
fontUrl = new URL(path);
}
catch (MalformedURLException e)
{
e.printStackTrace();
}
// 此方法为异步
Image fotImage = Toolkit.getDefaultToolkit().getImage(fontUrl);
tracker.addImage(fotImage, 1);
try
{
tracker.waitForAll();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
return fotImage;
}
}
楼主在本地添加参数-Djava.awt.headless=false调试并且运行成功,但是一上linux服务,fuck又不行
最后报错
Can’t connect to X11 window server using ‘:0.0′ as the value of the DISPLAY variable
楼主去研究了下,最后得出结论,使用这个api必须系统要支持图形化操作界面
当到这里的时候,楼主想死的心都有了
最后改进
不能使用同步的方法了,经过我观察,总是水印图加载部分,或者加载失败。最后试了试水印图使用ImageIo.read方法来读取,代码如下
这个toBufferedImage方法在上面
String blackPath = "";
String fontPath = "";
// 水印图
URL frontUrl = new URL(fontPath);
// 地图
URL blackUrl = new URL(blackPath);
// 读取水印图
Image frontImage = ImageIO.read(frontUrl);
// 读取背景图
Image blackImage = Toolkit.getDefaultToolkit().getImage(blackUrl);
BufferedImage image = toBufferedImage(blackImage);
// 拉伸下,
Thumbnails.Builder<BufferedImage> of = Thumbnails.of(image).size(750, 750);
image = of.asBufferedImage();
Graphics2D g2d = image.createGraphics();
// 在地图上绘制水印
g2d.drawImage(frontImage, 0, 0, 750, 750, null);
g2d.dispose();
最后附图
最后还真的让楼主成功了,这些问题记录下,顺便述说下楼主的辛酸。如果哪位大佬有更好的方法,或者更好的工具包出来,欢迎指点。