使用Thumbnails处理图片(自定义文本水印位置)

图片水印
placeholder image
admin 发布于:2015-12-15 16:57:52
阅读:loading

背景介绍

记得以前弄java生成gif动画图片的时候,了解过java操作图片的一个库Thumbnails,发现它并不能创建gif图片,故并没有深入研究,但发现网上对它的评价很高,是一款处理图片很好的解决方案。

最近有要给图片加上一个动态水印的需求,也就是一张图片,谁看就显示谁的工号水印,让我想起来这个并未深入了解的组件,于是才终于有了想法去了解它。

通过一系列的网上搜索发现它能解决的主流功能有:图片放大、缩小(按指定宽高、比例)、图片旋转、图片格式转换、图片裁切、图片水印等几项,看了下网上示例也大多围绕这几项而来的,那么作为学习了解我也照搬这几项吧。主要做了以下几个示例:

功能点

1)缩放图片

      按照设置的宽高去缩小、放大图片,同时可以设置是否保持原图比例;另外支持按图片的比例缩放。示例(以下示例图片为同一个)中原图采用600x600的大小,按宽高比例将图片缩小3倍,大小为200x200的比例大小;按宽高放大1.5倍,大小为900x900的比例大小;按比例缩小至30%,则大小为180x180比例大小;按原图大小放大3倍,则大小为1800x1800比例大小;

2)旋转图片

      可按弧(角)度进行旋转,示例中采用旋转45度,90度,135度来实现;

3)裁切图片

      裁切图片主要分为两种,一种是按它预置好的9个位置再按坐标进行四周扩散,9个位置分别为:image.png也就是可以从以上9个点中以任何一个部分作为0,0坐标的位置 开始向其他坐标点位置发散,根据两个点的位置和宽和高而截取一个矩形(没有研究是不是可以按其他图形进行裁切)。示例中从正中间裁切400像素再缩小为200像素大小的图片;从顶部中间裁切400像素再缩小为200像素大小的图片;按自定义坐标裁切图片,此处这个自定义坐标我觉得需要画图说明一下,参考如下图:

image.png

4)图片水印

      由于本次捡起来这个组件是从给图片添加水印而来的,故对这个添加水印的功能坐的更加详细的实现,其中主要的功能涉及有:按9个方向位置进行设置水印、自定义坐标位置、加长版本图片的多个水印、图片水印、文本水印、文本水印字体、字体大小、颜色、旋转角度、透明度、水印是否为透明图等等细节功能。至于显示位置的坐标就得去精确找位置了。

5)图片格式转换

      此组件可将图片的格式比如png转换为jpg格式等,感觉项目中此功能作用不是很大,故跳过。

6)其他

a、很多时候都是直接读取一个图片文件,将一些水印或者其他的处理直接在内存中进行cao作,cao作完成后直接返回OutputStream输出至浏览器;

b、以上一些图片的处理中,发现添加水印的动作是比较消耗性能的;

c、不知道其它一些cao作图片的库使用起来若何?

附上几个操作的原图和效果图,如下:

image.png


(原图)

image.png

(按比例缩小3倍)
image.png
(90度旋转)

长图-含水印.png

(长图多个水印)

image.png


参考代码

package com;

import java.awt.Color;

import java.awt.Font;

import java.awt.Graphics2D;

import java.awt.Transparency;

import java.awt.image.BufferedImage;

import java.io.File;

import java.io.IOException;

import javax.imageio.ImageIO;

import net.coobird.thumbnailator.Thumbnails;

import net.coobird.thumbnailator.Thumbnails.Builder;

import net.coobird.thumbnailator.geometry.Coordinate;

import net.coobird.thumbnailator.geometry.Position;

import net.coobird.thumbnailator.geometry.Positions;

public class ImageHandle {

    /**

     * 按宽高进行缩放,此时有几个图片宽度与高度大小的逻辑处理

     */

    public static void zoomSize(File srcFile , File outFile , int width , int height){

        try {

            Thumbnails.of(srcFile).size(width, height).toFile(outFile);

            //也可将图片输出至OutputStream

            //是否使用等比缩放,keepAspectRatio(false)

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

    /**

     * 将图片进行等比例缩放,0~~1之间的为缩小,大于1的为放大

     */

    public static void zoomScaling(File srcFile , File outFile , double scale){

        try {

            Thumbnails.of(srcFile).scale(scale).toFile(outFile);

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

    /**

     * 按角度旋转

     */

    public static void rotateAngle(File srcFile , File outFile , double rotate){

        try {

            Thumbnails.of(srcFile).scale(1).rotate(rotate).toFile(outFile);

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

    /**

     * 按坐标位置裁切图片,先按位置获取指定的图片范围,再按指定宽高存储

     */

    public static void corpRegion(File srcFile , File outFile , Positions postition , int width , int height , int x , int y){

        try {

            Thumbnails.of(srcFile).sourceRegion(postition, x, y).size(width, height).toFile(outFile);

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

    /**

     * 按坐标位置裁切图片,先按位置获取指定的图片范围,再按指定宽高存储

     * x1,x2为左上角的坐标点

     * y1,y2为以左上角为0,0坐标位置的相对坐标(或者x1+要裁切图片宽度=y1,y1+要裁切图片高度=y2的坐标)

     */

    public static void corpRegion(File srcFile , File outFile , int width , int height , int x1 , int y1 , int x2 , int y2){

        try {

            Thumbnails.of(srcFile).sourceRegion(x1, y1, x2, y2).size(width, height).keepAspectRatio(false).toFile(outFile);

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

    /**

     * 添加文本水印,也可添加图片水印

     */

    public static void watermark(File srcFile , File outFile , Position position , String text , double rotate , float opacity){

        //从原图中找出300x300的大小来显示水印文本

        BufferedImage bi = new BufferedImage(300, 300 ,BufferedImage.TYPE_INT_RGB);

        Graphics2D g = bi.createGraphics();

        //设置绘图区域透明

        bi = g.getDeviceConfiguration().createCompatibleImage(300, 300,Transparency.TRANSLUCENT);

        g.dispose();

        //字体、字体大小,透明度,旋转角度

        g = bi.createGraphics();

        g.setFont(new Font("微软雅黑", Font.BOLD, 26));

        char[] data = text.toCharArray();

        g.rotate(rotate);

        g.setColor(Color.RED);

        //设置文本显示坐标,以上述中的300x300位置为0,0点

        g.drawChars(data, 0, data.length, -70, 200);

        try {

            Thumbnails.of(srcFile).scale(1).watermark(position, bi, opacity).toFile(outFile);

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

    public static void main(String[] args) throws IOException {

        String imgPath = "D:\\demo\\源宝宝.jpg";//原图

        File srcFile = new File(imgPath);

        BufferedImage image = ImageIO.read(srcFile);

        int width = image.getWidth();

        int height = image.getHeight();

        //原图大小为:600,600

        System.out.println("原图宽度为:" + width + " ,高度为 " + height);

        System.out.println("=========缩放图片=========");

        //按宽高缩小3倍,200x200

        zoomSize(srcFile, new File(srcFile.getParent() , "按宽高缩小3倍.jpg"), width/3, height/3);

        //按宽高放大1.5倍,900x900

        zoomSize(srcFile, new File(srcFile.getParent() , "按宽高放大1.5倍.jpg"), (int)(width*1.5), (int)(height*1.5));

        //按比例缩小3倍,600*0.3=180

        zoomScaling(srcFile, new File(srcFile.getParent() , "按比例缩小3倍.jpg"), 0.3);

        //按比例放大3倍,600*3=1800

        zoomScaling(srcFile, new File(srcFile.getParent() , "按比例放大3倍.jpg"), 3);

        System.out.println("=========旋转图片=========");

        rotateAngle(srcFile , new File(srcFile.getParent() , "旋转45度.jpg") , 45);

        rotateAngle(srcFile , new File(srcFile.getParent() , "旋转90度.jpg") , 90);

        rotateAngle(srcFile , new File(srcFile.getParent() , "旋转135度.jpg") , 135);

        System.out.println("=========裁切图片=========");

        corpRegion(srcFile , new File(srcFile.getParent() , "从正中间裁切400像素再缩小为200像素大小.jpg") , Positions.CENTER , 200 , 200 , 400 , 400);

        corpRegion(srcFile , new File(srcFile.getParent() , "从顶部中间裁切400像素再缩小为200像素大小.jpg") , Positions.TOP_CENTER , 200 , 200 , 400 , 400);

        corpRegion(srcFile, new File(srcFile.getParent() , "按自定义坐标裁切图片.jpg"), 350, 300, 150, 200, 350, 300);

        System.out.println("=========水印图片=========");

        //此功能由于项目中使用了此种方式,故代码比较多,功能相对比较多,但具体的代码,参数应该自行设置

        //水印可以同时添加多个,添加方式为:.watermark(顶部).watermark(中部).watermark(底部)

        watermark(srcFile, new File(srcFile.getParent() , "图片正中间水印旋转45度.jpg") , Positions.CENTER , "http://www.chendd.cn" , -0.5 , 0.5f);

        watermark(srcFile, new File(srcFile.getParent() , "自定义位置添加水印.jpg") , new Coordinate(100 , 300) , "http://www.chendd.cn" , -0.5 , 0.5f);

        //给较长图添加一屏一个水印

        File longImg = new File(srcFile.getParent() , "加长版.jpg");

        watermarkLongImage(longImg, new File(longImg.getParent() , "加长版图片水印.jpg"));

        System.out.println("=========图片格式转换貌似使用较少,此处省略=========");

    }

    private static void watermarkLongImage(File srcFile , File outFile) {

        //从原图中找出300x300的大小来显示水印文本

        BufferedImage bi = new BufferedImage(300, 100 ,BufferedImage.TYPE_INT_RGB);

        Graphics2D g = bi.createGraphics();

        //设置绘图区域透明

        bi = g.getDeviceConfiguration().createCompatibleImage(300, 100,Transparency.TRANSLUCENT);

        g.dispose();

        //字体、字体大小,透明度,旋转角度

        g = bi.createGraphics();

        g.setFont(new Font("微软雅黑", Font.BOLD, 26));

        char[] data = "http://www.chendd.cn".toCharArray();

        g.setColor(Color.RED);

        //设置文本显示坐标,以上述中的300x300位置为0,0点

        g.drawChars(data, 0, data.length, 10, 20);

        try {

            BufferedImage image = ImageIO.read(srcFile);

            int height = image.getHeight();

            //假设此图在手机端访问,手机的高度为400,则每超过400部分显示一个水印

            Builder<File> builder = Thumbnails.of(srcFile).scale(1);

            int mod = (int) Math.ceil(height / 400);

            for(int i=1 ; i < mod ; i++){

                int x = 200;

                int y = (i) * 400;

                System.out.println(x + "," + y);

                builder.watermark(new Coordinate(x , y), bi, 1);

            }

            builder.toFile(outFile);

        } catch (IOException e) {

            e.printStackTrace();

        }  

    }

}
===========================================================

<dependency>

      <groupId>net.coobird</groupId>

      <artifactId>thumbnailator</artifactId>

      <version>0.4.8</version>

</dependency>

下载与补充

所有的图片下载地址为:http://yunpan.cn/c3bc2IkYHvMhj (提取码:956d)(时至今日该链接肯定已经失效)

@2020-08-16 补充

① thumbnailator 组件有了2020的最新版本,0.4.11;

② 该组件对于图片水印支持的非常好,但是对于文本水印的方式则没有提供(或许是我不知道),本文特补充一下关于图片增加文本水印时的位置支持,分别是:左上、中上、右上、中左、中、中右、左下、下中、右下,共计9个位置,具体实现是使用java.awt.FontMetrics类提供的API实现,它支持根据字体样式获取该文本所占宽度大小,代码及生成图片样例参考如下:

9个位置的文本水印.zip


上述许多内容已经过时和过期了,留存本篇文章仅为方便个人查看,原始文章的信息参考:

原始链接:https://www.chendd.cn/information/viewInformation/workUsed/158.a

最后更新:2015-12-15 16:57:52

访问次数:2565

评论次数:0

点赞个数:3,[相当给力:3]

 点赞


 发表评论

当前回复:作者

 评论列表


留言区