通过 JAVA 实现 Windows 屏幕水印
不是图片加水印,是 Windows 电脑屏幕上加水印,防拍照。图片加水印网上很多实用文章。Windows 电脑屏幕上加水印更多是一些企业信息安全工具的广告,或者弹出图片加水印的文章,或者使用非JAVA语言实现的屏幕加水印文章
效果
- 支持多屏幕水印添加
- 水印运行在所有程序顶层,不会被遮住
- 运行窗口隐藏
- 水印上的鼠标事件会透传到水印下的程序
- 水印透明的可调整
- 可关闭水印
效果图:可参考 Windows 未激活或预览版的水印
原理
通过 JAVA 的 JFrame 在 windows 屏幕增加一个不影响点击透传的顶层透明遮罩层,配合 Graphics 在遮罩层上添加文字水印,但加上的水印会与鼠标产生交互,无法禁用或透传给水印下的其它 Windows 程序,所以再通过 jna 包调用 Windows 相关 api 对窗口做了处理,使窗口内的文字水印不与鼠标交互。
源码
环境:JAVA JDK1.8
[pom.xml]{.label .info}
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.5.0</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna-platform</artifactId>
<version>5.5.0</version>
</dependency>
[FullScreenWatermark.java]{.label .primary}
import com.sun.jna.Native;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinUser;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
public class FullScreenWatermark extends JFrame {
private static final int WHITE_COLOR_KEY = 0xFFFFFF; // White color RGB value
private Rectangle screenBounds;
public FullScreenWatermark(GraphicsDevice screen) {
setUndecorated(true); // 无边框
setAlwaysOnTop(true); // 始终在最前面
setType(Window.Type.UTILITY); // 不在任务栏展示窗口
// 获取屏幕的大小
screenBounds = screen.getDefaultConfiguration().getBounds();
setSize(screenBounds.width, screenBounds.height); // 设置窗口宽高
setLocation(screenBounds.x, screenBounds.y);// 将窗口定位到屏幕的左上角
setBackground(new Color(0, 0, 0, 0)); // 白色透明背景,最后一个参数是透明度
// 添加窗口事件
addComponentListener(new ComponentAdapter() {
// 窗口展示后触发事件
@Override
public void componentShown(ComponentEvent e) {
// 为窗口透传鼠标交互事件,用的是JNI调用 Windows 的API
makeClickThrough(FullScreenWatermark.this);
}
});
// 设置窗口可见
setVisible(true);
}
@Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g; // 用于水印文字倾斜
// 水印颜色与透明度
g2d.setColor(new Color(1f, 1f, 1f, 0.5f));
Font watermarkFont = new Font("SimSun", Font.PLAIN, 36);// 字体,注意部分字体不支持中文
g2d.setFont(watermarkFont);// 设置字体
// 计算水印文本的位置长宽度
int frontWidth = g.getFontMetrics().stringWidth("昔日长廊");
int frontHeight = g.getFontMetrics().getHeight();
int x = (screenBounds.width - frontWidth) / 2;
int y = (screenBounds.height - frontHeight) / 2;
// 保存当前的变换矩阵,这个是倾斜的核心
g2d.translate(0, y);
g2d.shear(0, -0.5); // 沿着Y轴进行倾斜变化
// 这块就是循环画水印了
boolean flag = true;
for (int iy=screenBounds.width;iy>0;iy-=100){
flag = !flag;
for (int ix=0;ix<screenBounds.width;ix+=200){
int twox = ix+(flag?80:0);
// 画单个水印函数
g2d.drawString("昔日长廊", twox, twox/2+y-iy);
}
}
}
public static void makeClickThrough(Window window) {
WinDef.HWND hwnd = new WinDef.HWND(Native.getWindowPointer(window));
User32.INSTANCE.SetWindowLong(hwnd, WinUser.GWL_EXSTYLE,
User32.INSTANCE.GetWindowLong(hwnd, WinUser.GWL_EXSTYLE) | WinUser.WS_EX_LAYERED | WinUser.WS_EX_TRANSPARENT);
// 这段 WinUser.LWA_COLORKEY 代表指定颜色透明、WHITE_COLOR_KEY 就是前面定义的白色、 0 就是透明度,表示完全透明
User32.INSTANCE.SetLayeredWindowAttributes(hwnd, WHITE_COLOR_KEY, (byte) 0, WinUser.LWA_COLORKEY);
}
public static void main(String[] args) {
// 1、获取所有屏幕设备
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] screens = ge.getScreenDevices();
ArrayList<FullScreenWatermark> fullScreenWatermarks = new ArrayList<>();
// 遍历每个屏幕设备并创建全屏水印窗口
for (GraphicsDevice screen : screens) {
fullScreenWatermarks.add(new FullScreenWatermark(screen));
}
try {
// 过渡10秒
Thread.sleep(10000);
} catch (InterruptedException e) {}
for (FullScreenWatermark fullScreenWatermark : fullScreenWatermarks) {
// 7、关闭水印
//fullScreenWatermark.dispose();
}
}
}
Windows 水印颜色设计
可通过加两层水印,一层白色水印、一层黑色的水印,为了避免覆盖,核心亮点是透明度。这样不管用户桌面软件背景是什么颜色,都能很好的展示出来
结语
欢迎交流,可以说说更好的实现方式