×
文章路径: Java

如何给图片生成水印和缩略图

发表于1年前(Mar 9, 2017 4:53:38 PM)  阅读 698  评论 0

分类: Java

标签: thumbnailator waterpress 水印 缩略图 剪裁

水印和缩略图是一般内容网站很常见的一个功能,而thumbnailator则是java在这方面一个很强大的工具。

首先,在笔者没有使用thumbnailator之前,在网上找了一段生成水印的方法:

    public static void marker2(String srcImgPath, String iconPath, String targerPath, String position, 
			Integer positionX, Integer positionY, Double alpha, Integer degree, Integer minWidth, Integer minHeight) {
        OutputStream os = null;
        try {
        	if(targerPath==null) {
        		targerPath = srcImgPath;
        	}
            Image srcImg = ImageIO.read(new File(srcImgPath));
            if(minWidth!=null) {
            	if(srcImg.getWidth(null)<minWidth) {
            		return;
            	}
            }
            if(minHeight!=null) {
            	if(srcImg.getHeight(null)<minHeight) {
            		return;
            	}
            }
            BufferedImage buffImg = new BufferedImage(srcImg.getWidth(null),
                    srcImg.getHeight(null), BufferedImage.TYPE_INT_RGB);
            // 1、得到画笔对象
            Graphics2D g = buffImg.createGraphics();
            // 2、设置对线段的锯齿状边缘处理
            g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                    RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            g.drawImage(
                    srcImg.getScaledInstance(srcImg.getWidth(null),
                            srcImg.getHeight(null), Image.SCALE_SMOOTH), 0, 0,
                    null);
            // 3、设置水印旋转
            if (null != degree&&degree!=0) {
                g.rotate(Math.toRadians(degree),
                        (double) buffImg.getWidth() / 2,
                        (double) buffImg.getHeight() / 2);
            }
            // 4、水印图片的路径 水印图片一般为gif或者png的,这样可设置透明度
            ImageIcon imgIcon = new ImageIcon(iconPath);
            // 5、得到Image对象。
            Image img = imgIcon.getImage();
            if(alpha!=null) {
            	g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP,
                        alpha.floatValue()));
            }
            // 6、水印图片的位置
            Point point = new Point(0, 0);
            if("right-bottom".equals(position)) {
            	point.x = buffImg.getWidth()-img.getWidth(null);
            	point.y = buffImg.getHeight()-img.getHeight(null);
            }
            if(positionX!=null) {
            	point.x += positionX;
            }
            if(positionY!=null) {
            	point.y += positionY;
            }
            g.drawImage(img, point.x, point.y, null);
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
            // 7、释放资源
            g.dispose();
            // 8、生成图片
            os = new FileOutputStream(targerPath);
            ImageIO.write(buffImg, "JPG", os);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != os)
                    os.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

上面的方法可以正常生成水印,笔者在原来代码的基础上扩展添加了position,positionX,positionY几个参数,position表示水印生成的位置,当初设想值为left-top,left-center,left-bottom,center-top,center-center,center-bottom,right-top,right-center,right-bottom九个值,而postionX,positionY则是在水印位置的基础上的修正量。笔者暂时只添加了right-bottom的实现。

这是原始生成图片的方法,当时笔者测试时没发现,添加水印后的图片,文件大小比原图要大了很多,因为上面的方法没有控制图片的输出质量,我们平时在用制图软件时,导出图片的时候经常会有一个图片质量的设置。笔者暂时未找到上面代码如何控制图片生成的质量。所幸,笔者后来找到了thumbnailator这个工具。

    public static void marker(String srcImgPath, String iconPath, String targetPath, String position, 
			Double alpha, Integer minWidth, Integer minHeight) {
        try {
        	if(targetPath==null) {
        		targetPath = srcImgPath;
        	}
            Image srcImg = ImageIO.read(new File(srcImgPath));
            if(minWidth!=null) {
            	if(srcImg.getWidth(null)<minWidth) {
            		return;
            	}
            }
            if(minHeight!=null) {
            	if(srcImg.getHeight(null)<minHeight) {
            		return;
            	}
            }
            Positions positions = Positions.BOTTOM_RIGHT;
        	positions = Positions.valueOf(position);
        	if(positions==null) {
        		positions = Positions.BOTTOM_RIGHT;
        	}
            Thumbnails.of(srcImgPath).size(srcImg.getWidth(null), srcImg.getHeight(null)).watermark(positions, ImageIO.read(new File(iconPath)), alpha.floatValue()).toFile(targetPath);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
        }
	}

可以看到,使用thumbnailator生成水印,核心代码就一句,方便了很多,而且生成的文件大小不会像之前的方法那样出现大幅增大的情况。

接下来,继续使用thumbnailator生成缩略图,其实thumbnailator生成缩略图也很简单:

Thumbnails.of(file.getInputStream()).size(w, h).outputQuality(1).toFile(targetFile);

网上很多人都是这样生成缩略图的。这种方法有个问题,如果源图的比例和你要生成的缩略图的比例不一致,那么生成出来的缩略图比例也是会不一致的。打个比方,如果你采用上面的办法将一个800x400的图片,生成100x100的缩略图,那么最后生成的图片将是100x50的,如果这正是你想要的,那没问题,但笔者认为不管怎么,最后生成的图片应该还是100x100,为了生成指定比例,那么势必就得有所剪裁了。

笔者的思路是,先将源图按原始比例缩小,缩小到能正好包住目标图片的大小,然后剪裁出指定大小的图片,代码如下:

                int w = 0;
                int h = 0;
                Image image = ImageIO.read(file.getInputStream()); 
                int imageWidth = image.getWidth(null);
                int imageHeight = image.getHeight(null);
                if(width==null) {
                	w = imageWidth;
                } else {
                	w = Integer.parseInt(width);
                }
                if(height==null) {
                	h = imageHeight;
                } else {
                	h = Integer.parseInt(height);
                }
                if(Math.round(w*10000/h)==Math.round(imageWidth*10000/imageHeight)) {
                	Thumbnails.of(file.getInputStream()).size(w, h).outputQuality(1).toFile(targetFile);
                } else {
                	if(Math.round(w*10000/imageWidth)>Math.round(h*10000/imageHeight)) {
                		Thumbnails.of(
                				Thumbnails.of(file.getInputStream())
                            	.scale(((double)w/imageWidth))
                            	.outputQuality(1)
                            	.asBufferedImage()
                         )
                		.sourceRegion(Positions.TOP_CENTER, w, h)
                		.size(w, h)
                		.outputQuality(1)
                		.toFile(targetFile);
                	} else {
                		Thumbnails.of(
                				Thumbnails.of(file.getInputStream())
                				.scale(((double)h/imageHeight))
                            	.outputQuality(1)
                            	.asBufferedImage()
                         )
                		.sourceRegion(Positions.CENTER, w, h)
                		.size(w, h)
                		.outputQuality(1)
                		.toFile(targetFile);
                	}
                }

其中file变量是前台上传上来的源文件,MultipartFile类型,width,height是要生成的目标缩略图的大小,上几张效果图:

源文件,原始大小1024x768,下面显示大小600x450:

生成缩略图280x180:

生成缩略图200x150:

相比按原始比例剪裁,笔者更满意上面这种效果

发表评论