一、介绍

在实际的系统开发中,某些业务场景下,经常需要给原始图片添加水印,以防止图片信息在互联网上随意传播!
也有的基于当下的业务需求,需要给相机照片加水印、地理位置、时间等信息,以方便记录自己的生活!
有的人可能很容易想到,通过 PS 技术就可以很轻松的完成!
的确,对于单个图像而言很容易,但是对于成千上万的图像,采用人工处理,显然不可取!
问题来了,面对大批量的图像加水印需求,应当如何处理呢?
试想一下,如果采用人工方式来给图像添加水印,大概的步骤离不开以下几步:
1、先获取需要处理的图像
2、然后将图像摆放整齐,用尺子计算出需要加水印的位置
3、采用画笔准确无误的在对应的位置上画上水印
4、最后,水印添加之后!
如果采用程序来实现,思路也是一样的,废话也不多说了,代码直接撸上!

二、程序实践

下面以java程序为例,给以下图添加一段复印无效的文字水印,并居中!
Java-图片添加水印 - 图1
程序实践如下:

  1. import org.apache.commons.lang3.StringUtils;
  2. import javax.imageio.ImageIO;
  3. import java.awt.*;
  4. import java.awt.image.BufferedImage;
  5. import java.io.File;
  6. /**
  7. * 给图像添加水印
  8. * @since 2021-10-29
  9. */
  10. public class ImageWaterMarkUtil {
  11. /**
  12. * 给图像添加文字水印
  13. * @param srcImgPath 原始文件地址
  14. * @param targetImgPath 目标文件地址
  15. * @param text 水印内容
  16. * @param color 水印文字颜色
  17. * @param font 水印文字字体
  18. * @param alpha 水印透明度
  19. * @param positionWidth 水印横向位置
  20. * @param positionHeight 水印纵向位置
  21. * @param degree 水印图片旋转角度
  22. * @param location 水印的位置,左上角、右上角、左下角、右下角、居中
  23. */
  24. public static void markImage(String srcImgPath,
  25. String targetImgPath,
  26. String text,
  27. Color color,
  28. Font font,
  29. float alpha,
  30. int positionWidth,
  31. int positionHeight,
  32. Integer degree,
  33. String location) {
  34. try {
  35. // 1、读取源图片
  36. Image srcImg = ImageIO.read(new File(srcImgPath));
  37. int srcImgWidth = srcImg.getWidth(null);
  38. int srcImgHeight = srcImg.getHeight(null);
  39. BufferedImage buffImg = new BufferedImage(srcImgWidth, srcImgHeight, BufferedImage.TYPE_INT_RGB);
  40. // 2、得到画笔对象
  41. Graphics2D g = buffImg.createGraphics();
  42. // 3、设置对线段的锯齿状边缘处理
  43. g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
  44. g.drawImage(srcImg.getScaledInstance(srcImgWidth, srcImgHeight, Image.SCALE_SMOOTH), 0, 0, null);
  45. // 4、设置水印旋转
  46. if (null != degree) {
  47. g.rotate(Math.toRadians(degree), (double) buffImg.getWidth() / 2, (double) buffImg.getHeight() / 2);
  48. }
  49. // 5、设置水印文字颜色
  50. g.setColor(color);
  51. // 6、设置水印文字Font
  52. g.setFont(font);
  53. // 7、设置水印文字透明度
  54. g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
  55. // 8、水印图片的位置
  56. int x = 0, y = 0;
  57. if (StringUtils.equals(location, "left-top")) {
  58. x = 30;
  59. y = font.getSize();
  60. } else if (StringUtils.equals(location, "right-top")) {
  61. x = srcImgWidth - getWatermarkLength(text, g) - 30;
  62. y = font.getSize();
  63. } else if (StringUtils.equals(location, "left-bottom")) {
  64. x += 30;
  65. y = buffImg.getHeight() - font.getSize();
  66. } else if (StringUtils.equals(location, "right-bottom")) {
  67. x = srcImgWidth - getWatermarkLength(text, g) - 30;
  68. y = srcImgHeight - font.getSize();
  69. } else if (StringUtils.equals(location, "center")) {
  70. x = (srcImgWidth - getWatermarkLength(text, g)) / 2;
  71. y = srcImgHeight / 2;
  72. } else {
  73. //自定义位置
  74. x = positionWidth;
  75. y = positionHeight;
  76. }
  77. // 9、第一参数->设置的内容,后面两个参数->文字在图片上的坐标位置(x,y)
  78. g.drawString(text, x, y);
  79. // 10、释放资源
  80. g.dispose();
  81. // 11、生成图片
  82. ImageIO.write(buffImg, "png", new File(targetImgPath));
  83. System.out.println("图片完成添加水印文字");
  84. } catch (Exception e) {
  85. e.printStackTrace();
  86. }
  87. }
  88. /**
  89. * 计算填充的水印长度
  90. * @param text
  91. * @param g
  92. * @return
  93. */
  94. private static int getWatermarkLength(String text, Graphics2D g) {
  95. return g.getFontMetrics(g.getFont()).charsWidth(text.toCharArray(), 0, text.length());
  96. }
  97. public static void main(String[] args) {
  98. String srcImgPath = "/Users/pzblog/Desktop/Jietu.jpg"; //原始文件地址
  99. String targetImgPath = "/Users/pzblog/Desktop/Jietu-copy.jpg"; //目标文件地址
  100. String text = "复 印 无 效"; //水印文字内容
  101. Color color = Color.red; //水印文字颜色
  102. Font font = new Font("宋体", Font.BOLD, 60); //水印文字字体
  103. float alpha = 0.4f; //水印透明度
  104. int positionWidth = 320; //水印横向位置坐标
  105. int positionHeight = 450; //水印纵向位置坐标
  106. Integer degree = -30; //水印旋转角度
  107. String location = "center"; //水印的位置
  108. //给图片添加文字水印
  109. markImage(srcImgPath, targetImgPath, text, color, font, alpha, positionWidth, positionHeight, degree, location);
  110. }
  111. }

运行结果如下:
Java-图片添加水印 - 图2
水印添加成功!

2.1、给图像添加多处文字

有的需求会要求给图像添加多处文字水印,例如下图!
Java-图片添加水印 - 图3
处理过程也很简单!

  1. import javax.imageio.ImageIO;
  2. import java.awt.*;
  3. import java.awt.image.BufferedImage;
  4. import java.io.File;
  5. /**
  6. * 给图像添加水印
  7. * @since 2021-10-29
  8. */
  9. public class ImageFullWaterMarkUtil {
  10. /**
  11. * 给图像添加多处文字水印
  12. * @param srcImgPath 原始文件地址
  13. * @param targetImgPath 目标文件地址
  14. * @param text 水印内容
  15. * @param color 水印文字颜色
  16. * @param font 水印文字字体
  17. * @param alpha 水印透明度
  18. * @param startWidth 水印横向起始位置
  19. * @param degree 水印图片旋转角度
  20. * @param interval 高度间隔
  21. */
  22. public static void fullMarkImage(String srcImgPath,
  23. String targetImgPath,
  24. String text,
  25. Color color,
  26. Font font,
  27. float alpha,
  28. int startWidth,
  29. Integer degree,
  30. Integer interval) {
  31. try {
  32. // 1、读取源图片
  33. Image srcImg = ImageIO.read(new File(srcImgPath));
  34. int srcImgWidth = srcImg.getWidth(null);
  35. int srcImgHeight = srcImg.getHeight(null);
  36. BufferedImage buffImg = new BufferedImage(srcImgWidth, srcImgHeight, BufferedImage.TYPE_INT_RGB);
  37. // 2、得到画笔对象
  38. Graphics2D g = buffImg.createGraphics();
  39. // 3、设置对线段的锯齿状边缘处理
  40. g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
  41. g.drawImage(srcImg.getScaledInstance(srcImgWidth, srcImgHeight, Image.SCALE_SMOOTH), 0, 0, null);
  42. // 4、设置水印旋转
  43. if (null != degree) {
  44. g.rotate(Math.toRadians(degree), (double) buffImg.getWidth() / 2, (double) buffImg.getHeight() / 2);
  45. }
  46. // 5、设置水印文字颜色
  47. g.setColor(color);
  48. // 6、设置水印文字Font
  49. g.setFont(font);
  50. // 7、设置水印文字透明度
  51. g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
  52. // 8、水印图片的位置
  53. int x = startWidth;
  54. int y = font.getSize();
  55. int space = srcImgHeight / interval;
  56. for (int i = 0; i < space; i++) {
  57. //如果最后一个坐标的y轴比height高,直接退出
  58. if (((y + font.getSize()) > srcImgHeight) || ((x + getWatermarkLength(text,g)) > srcImgWidth)) {
  59. break;
  60. }
  61. //9、进行绘制
  62. g.drawString(text, x, y);
  63. x += getWatermarkLength(text,g);
  64. y += font.getSize() + interval;
  65. }
  66. // 10、释放资源
  67. g.dispose();
  68. // 11、生成图片
  69. ImageIO.write(buffImg, "png", new File(targetImgPath));
  70. System.out.println("图片完成添加水印文字");
  71. } catch (Exception e) {
  72. e.printStackTrace();
  73. }
  74. }
  75. /**
  76. * 计算填充的水印长度
  77. * @param text
  78. * @param g
  79. * @return
  80. */
  81. private static int getWatermarkLength(String text, Graphics2D g) {
  82. return g.getFontMetrics(g.getFont()).charsWidth(text.toCharArray(), 0, text.length());
  83. }
  84. public static void main(String[] args) {
  85. String srcImgPath = "/Users/pzblog/Desktop/Jietu.jpg"; //原始文件地址
  86. String targetImgPath = "/Users/pzblog/Desktop/Jietu-copy-full.jpg"; //目标文件地址
  87. String text = "复 印 无 效"; //水印文字内容
  88. Color color = Color.red; //水印文字颜色
  89. Font font = new Font("宋体", Font.BOLD, 30); //水印文字字体
  90. float alpha = 0.4f; //水印透明度
  91. int startWidth = 30; //水印横向位置坐标
  92. Integer degree = -0; //水印旋转角度
  93. Integer interval = 100; //水印的位置
  94. //给图片添加文字水印
  95. fullMarkImage(srcImgPath, targetImgPath, text, color, font, alpha, startWidth, degree, interval);
  96. }
  97. }

2.2、给图像添加图片水印

某些情况下,还需要给图像添加图片水印,例如下图效果!
Java-图片添加水印 - 图4
处理过程也很简单!

  1. import org.apache.commons.lang3.StringUtils;
  2. import javax.imageio.ImageIO;
  3. import javax.swing.*;
  4. import java.awt.*;
  5. import java.awt.image.BufferedImage;
  6. import java.io.File;
  7. /**
  8. * 给图像添加水印
  9. * @since 2021-10-29
  10. */
  11. public class ImageIconWaterMarkUtil {
  12. /**
  13. * 给图像添加多处文字水印
  14. * @param srcImgPath 原始文件地址
  15. * @param targetImgPath 目标文件地址
  16. * @param iconImgPath 水印icon
  17. * @param alpha 水印透明度
  18. * @param positionWidth 水印横向位置
  19. * @param positionHeight 水印纵向位置
  20. * @param degree 水印图片旋转角度
  21. * @param location 水印的位置,左上角、右上角、左下角、右下角、居中
  22. */
  23. public static void fullMarkImage(String srcImgPath,
  24. String targetImgPath,
  25. String iconImgPath,
  26. float alpha,
  27. int positionWidth,
  28. int positionHeight,
  29. Integer degree,
  30. String location) {
  31. try {
  32. // 1、读取源图片
  33. Image srcImg = ImageIO.read(new File(srcImgPath));
  34. int srcImgWidth = srcImg.getWidth(null);
  35. int srcImgHeight = srcImg.getHeight(null);
  36. BufferedImage buffImg = new BufferedImage(srcImgWidth, srcImgHeight, BufferedImage.TYPE_INT_RGB);
  37. // 2、得到画笔对象
  38. Graphics2D g = buffImg.createGraphics();
  39. // 3、设置对线段的锯齿状边缘处理
  40. g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
  41. g.drawImage(srcImg.getScaledInstance(srcImgWidth, srcImgHeight, Image.SCALE_SMOOTH), 0, 0, null);
  42. // 4、设置水印旋转
  43. if (null != degree) {
  44. g.rotate(Math.toRadians(degree), (double) buffImg.getWidth() / 2, (double) buffImg.getHeight() / 2);
  45. }
  46. // 5、设置水印文字透明度
  47. g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
  48. // 6、水印图片的路径 水印图片一般为gif或者png的,这样可设置透明度
  49. ImageIcon imgIcon = new ImageIcon(iconImgPath);
  50. // 7、得到Image对象。
  51. Image iconImg = imgIcon.getImage();
  52. int iconImgWidth = iconImg.getWidth(null);
  53. int iconImgHeight = iconImg.getHeight(null);
  54. int x = 0, y = 0;
  55. if (StringUtils.equals(location, "left-top")) {
  56. x = iconImgWidth;
  57. y = iconImgHeight;
  58. } else if (StringUtils.equals(location, "right-top")) {
  59. x = srcImgWidth - iconImgWidth - 30;
  60. y = iconImgHeight;
  61. } else if (StringUtils.equals(location, "left-bottom")) {
  62. x += iconImgWidth;
  63. y = buffImg.getHeight() - iconImgHeight;
  64. } else if (StringUtils.equals(location, "right-bottom")) {
  65. x = srcImgWidth - iconImgWidth - 30;
  66. y = srcImgHeight - iconImgHeight;
  67. } else if (StringUtils.equals(location, "center")) {
  68. x = (srcImgWidth - iconImgWidth) / 2;
  69. y = (srcImgHeight - iconImgHeight) / 2;
  70. } else {
  71. //自定义位置
  72. x = positionWidth;
  73. y = positionHeight;
  74. }
  75. g.drawImage(iconImg, x, y, null);
  76. g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
  77. // 10、释放资源
  78. g.dispose();
  79. // 11、生成图片
  80. ImageIO.write(buffImg, "jpg", new File(targetImgPath));
  81. System.out.println("图片完成添加图片水印文字");
  82. } catch (Exception e) {
  83. e.printStackTrace();
  84. }
  85. }
  86. /**
  87. * 计算填充的水印长度
  88. * @param text
  89. * @param g
  90. * @return
  91. */
  92. private static int getWatermarkLength(String text, Graphics2D g) {
  93. return g.getFontMetrics(g.getFont()).charsWidth(text.toCharArray(), 0, text.length());
  94. }
  95. public static void main(String[] args) {
  96. String srcImgPath = "/Users/pzblog/Desktop/Jietu.jpg"; //原始文件地址
  97. String targetImgPath = "/Users/pzblog/Desktop/Jietu-copy-img.jpg"; //目标文件地址
  98. String iconImgPath = "/Users/pzblog/Desktop/1.png"; //图片水印地址
  99. float alpha = 0.6f; //水印透明度
  100. int positionWidth = 320; //水印横向位置坐标
  101. int positionHeight = 450; //水印纵向位置坐标
  102. Integer degree = 0; //水印旋转角度
  103. String location = "center"; //水印的位置
  104. //给图片添加文字水印
  105. fullMarkImage(srcImgPath, targetImgPath, iconImgPath, alpha, positionWidth, positionHeight, degree, location);
  106. }
  107. }

三、踩坑点

以上实现都很简单,但是在实际的实现过程中,却发现了一个巨大的坑,如果用的iphone手机拍摄的,按照以上代码进行添加水印,会发现图像突然变横了!
例如下图是原图:
Java-图片添加水印 - 图5
按照上面添加水印的处理,得到的图像结果如下:
Java-图片添加水印 - 图6
很明显,图像旋转了90度!
通过不同拍摄角度的反复测试,发现拍摄角度正常,但是经过程序处理之后,有些是需要旋转 90/180/270 度才能回正。
如果想要在正确的位置加上水印,就必须先对图像进行旋转回到原有的角度,然后再添加水印!
那问题来了,如何获取其旋转的角度呢?
经过查阅资料,对于图像的拍摄角度信息,有一个专业的名词:EXIF,EXIF是 Exchangeable Image File的缩写,这是一种专门为数码相机照片设定的格式。
这种格式可以用来记录数字照片的属性信息,例如相机的品牌及型号、相片的拍摄时间、拍摄时所设置的光圈大小、快门速度、ISO等等信息。除此之外它还能够记录拍摄数据,以及照片格式化方式。
通过它,可以得知图像的旋转角度信息!
下面一起来了解下采用 Java 语言如何读取图像的 EXIF 信息,包括如何根据 EXIF 信息对图像进行调整以适合用户浏览。

  • 首先添加 EXIF 依赖包

    1. <dependency>
    2. <groupId>com.drewnoakes</groupId>
    3. <artifactId>metadata-extractor</artifactId>
    4. <version>2.16.0</version>
    5. </dependency>
  • 然后读取图像的 EXIF 信息 ```java import com.drew.imaging.ImageMetadataReader; import com.drew.imaging.ImageProcessingException; import com.drew.metadata.Directory; import com.drew.metadata.Metadata; import com.drew.metadata.Tag;

import java.io.File; import java.io.IOException;

public class EXIFTest {

  1. public static void main(String[] args) throws ImageProcessingException, IOException {
  2. Metadata metadata = ImageMetadataReader.readMetadata(new File("/Users/pzblog/Desktop/11.jpeg"));
  3. for (Directory directory : metadata.getDirectories()) {
  4. for (Tag tag : directory.getTags()) {
  5. System.out.println(String.format("[%s] - %s = %s",
  6. directory.getName(), tag.getTagName(), tag.getDescription()));
  7. }
  8. if (directory.hasErrors()) {
  9. for (String error : directory.getErrors()) {
  10. System.err.format("ERROR: %s", error);
  11. }
  12. }
  13. }
  14. }

}

  1. 输出结果:

[JPEG] - Compression Type = Baseline [JPEG] - Data Precision = 8 bits [JPEG] - Image Height = 1080 pixels [JPEG] - Image Width = 1440 pixels [JPEG] - Number of Components = 3 [JPEG] - Component 1 = Y component: Quantization table 0, Sampling factors 2 horiz/2 vert [JPEG] - Component 2 = Cb component: Quantization table 1, Sampling factors 1 horiz/1 vert [JPEG] - Component 3 = Cr component: Quantization table 1, Sampling factors 1 horiz/1 vert [JFIF] - Version = 1.1 [JFIF] - Resolution Units = none [JFIF] - X Resolution = 72 dots [JFIF] - Y Resolution = 72 dots [JFIF] - Thumbnail Width Pixels = 0 [JFIF] - Thumbnail Height Pixels = 0 [Exif IFD0] - Orientation = Right side, top (Rotate 90 CW) [Exif SubIFD] - Exif Image Width = 1440 pixels [Exif SubIFD] - Exif Image Height = 1080 pixels [ICC Profile] - Profile Size = 548 [ICC Profile] - CMM Type = appl [ICC Profile] - Version = 4.0.0 [ICC Profile] - Class = Display Device [ICC Profile] - Color space = RGB [ICC Profile] - Profile Connection Space = XYZ [ICC Profile] - Profile Date/Time = 2017:07:07 13:22:32 [ICC Profile] - Signature = acsp [ICC Profile] - Primary Platform = Apple Computer, Inc. [ICC Profile] - Device manufacturer = APPL [ICC Profile] - XYZ values = 0.964 1 0.825 [ICC Profile] - Tag Count = 10 [ICC Profile] - Profile Description = Display P3 [ICC Profile] - Profile Copyright = Copyright Apple Inc., 2017 [ICC Profile] - Media White Point = (0.9505, 1, 1.0891) [ICC Profile] - Red Colorant = (0.5151, 0.2412, 65536) [ICC Profile] - Green Colorant = (0.292, 0.6922, 0.0419) [ICC Profile] - Blue Colorant = (0.1571, 0.0666, 0.7841) [ICC Profile] - Red TRC = para (0x70617261): 32 bytes [ICC Profile] - Chromatic Adaptation = sf32 (0x73663332): 44 bytes [ICC Profile] - Blue TRC = para (0x70617261): 32 bytes [ICC Profile] - Green TRC = para (0x70617261): 32 bytes [Photoshop] - Caption Digest = 212 29 140 217 143 0 178 4 233 128 9 152 236 248 66 126 [Huffman] - Number of Tables = 4 Huffman tables [File Type] - Detected File Type Name = JPEG [File Type] - Detected File Type Long Name = Joint Photographic Experts Group [File Type] - Detected MIME Type = image/jpeg [File Type] - Expected File Name Extension = jpg [File] - File Name = 11.jpeg [File] - File Size = 234344 bytes [File] - File Modified Date = 星期日 十一月 07 20:05:52 +08:00 2021

  1. 其中`Orientation`标签描述的就是图像旋转的角度。

[Exif IFD0] - Orientation = Right side, top (Rotate 90 CW)

  1. 最后,可以通过`Orientation`信息计算出图像对应的旋转角度。
  2. ```java
  3. import com.alibaba.fastjson.JSON;
  4. import com.drew.imaging.jpeg.JpegMetadataReader;
  5. import com.drew.metadata.Directory;
  6. import com.drew.metadata.Metadata;
  7. import com.drew.metadata.Tag;
  8. import java.io.FileInputStream;
  9. import java.io.IOException;
  10. import java.io.InputStream;
  11. public class TransferImage {
  12. public static void main(String[] args) throws IOException {
  13. String path = "/Users/pzblog/Desktop/11.jpeg";
  14. int result = getImgRotateAngle(new FileInputStream(path));
  15. System.out.println(result);
  16. }
  17. public static int getImgRotateAngle(InputStream inputStream) {
  18. int rotateAngle = 0;
  19. try {
  20. Metadata metadata = JpegMetadataReader.readMetadata(inputStream);
  21. Iterable<Directory> directories = metadata.getDirectories();
  22. for (Directory directory : directories) {
  23. for (Tag tag : directory.getTags()) {
  24. System.out.println(JSON.toJSONString(tag));
  25. int tagType = tag.getTagType();
  26. //照片拍摄角度信息
  27. if (274 == tagType) {
  28. String description = tag.getDescription();
  29. //Left side, bottom (Rotate 270 CW)
  30. switch (description) {
  31. //顺时针旋转90度
  32. case "Right side, top (Rotate 90 CW)":
  33. rotateAngle = 90;
  34. break;
  35. case "Left side, bottom (Rotate 270 CW)":
  36. rotateAngle = 270;
  37. break;
  38. case "Bottom, right side (Rotate 180)":
  39. rotateAngle = 180;
  40. break;
  41. default:
  42. rotateAngle = 0;
  43. break;
  44. }
  45. }
  46. }
  47. }
  48. return rotateAngle;
  49. } catch (Exception e) {
  50. return 0;
  51. }
  52. }
  53. }

输出的旋转角度结果:

  1. 90
  • 接着通过旋转角度参数,对图像进行回正 ```java import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException;

public class RotateImage {

  1. public static BufferedImage rotate(Image src, int angel) {
  2. int src_width = src.getWidth(null);
  3. int src_height = src.getHeight(null);
  4. // calculate the new image size
  5. Rectangle rect_des = calcRotatedSize(new Rectangle(new Dimension(
  6. src_width, src_height)), angel);
  7. BufferedImage res = null;
  8. res = new BufferedImage(rect_des.width, rect_des.height,
  9. BufferedImage.TYPE_INT_RGB);
  10. Graphics2D g2 = res.createGraphics();
  11. // transform
  12. g2.translate((rect_des.width - src_width) / 2,
  13. (rect_des.height - src_height) / 2);
  14. g2.rotate(Math.toRadians(angel), src_width / 2, src_height / 2);
  15. g2.drawImage(src, null, null);
  16. return res;
  17. }
  18. public static Rectangle calcRotatedSize(Rectangle src, int angel) {
  19. // if angel is greater than 90 degree, we need to do some conversion
  20. if (angel >= 90) {
  21. if(angel / 90 % 2 == 1){
  22. int temp = src.height;
  23. src.height = src.width;
  24. src.width = temp;
  25. }
  26. angel = angel % 90;
  27. }
  28. double r = Math.sqrt(src.height * src.height + src.width * src.width) / 2;
  29. double len = 2 * Math.sin(Math.toRadians(angel) / 2) * r;
  30. double angel_alpha = (Math.PI - Math.toRadians(angel)) / 2;
  31. double angel_dalta_width = Math.atan((double) src.height / src.width);
  32. double angel_dalta_height = Math.atan((double) src.width / src.height);
  33. int len_dalta_width = (int) (len * Math.cos(Math.PI - angel_alpha
  34. - angel_dalta_width));
  35. int len_dalta_height = (int) (len * Math.cos(Math.PI - angel_alpha
  36. - angel_dalta_height));
  37. int des_width = src.width + len_dalta_width * 2;
  38. int des_height = src.height + len_dalta_height * 2;
  39. return new java.awt.Rectangle(new Dimension(des_width, des_height));
  40. }
  41. public static void main(String[] args) throws IOException {
  42. BufferedImage src = ImageIO.read(new File("/Users/pzblog/Desktop/11.jpeg"));
  43. BufferedImage des = RotateImage.rotate(src, 90);
  44. ImageIO.write(des, "jpg", new File("/Users/pzblog/Desktop/11-rotate.jpeg"));
  45. }

}

  1. - 最后给回正后的图像添加水印
  2. ```java
  3. public static void main(String[] args) {
  4. String srcImgPath = "/Users/pzblog/Desktop/11-rotate.jpeg"; //原始文件地址
  5. String targetImgPath = "/Users/pzblog/Desktop/1-rotate-copy.jpg"; //目标文件地址
  6. String text = "复 印 无 效"; //水印文字内容
  7. Color color = Color.red; //水印文字颜色
  8. Font font = new Font("宋体", Font.BOLD, 60); //水印文字字体
  9. float alpha = 0.4f; //水印透明度
  10. int positionWidth = 320; //水印横向位置坐标
  11. int positionHeight = 450; //水印纵向位置坐标
  12. Integer degree = -30; //水印旋转角度
  13. String location = "center"; //水印的位置
  14. //给图片添加文字水印
  15. markImage(srcImgPath, targetImgPath, text, color, font, alpha, positionWidth, positionHeight, degree, location);
  16. }

输入结果:
Java-图片添加水印 - 图7
添加水印的结果与预期一致!