前端给页面添加暗水印的办法
上一篇文章讲到了在页面上添加明水印的方法,但是明水印比较好清除,而且对于一些没做处理的图片,当用户直接保存的时候,是没有水印的,这时候信息泄露问题依然存在。为了解决这样的问题,我们需要用到暗水印。
实现思路
我们知道图片是由多个像素点组成的,通过canvas的getImageData方法,我们可以得到画布指定矩形的像素数据
getImageData() 方法返回 ImageData 对象,该对象拷贝了画布指定矩形的像素数据。
对于 ImageData 对象中的每个像素,都存在着四方面的信息,即 RGBA 值:
R - 红色 (0-255)G - 绿色 (0-255)B - 蓝色 (0-255)A - alpha 通道 (0-255; 0 是透明的,255 是完全可见的)
值得注意的是:RGB 分量值的小量变动,是肉眼无法分辨的,不影响对图片的识别。这是我们在图片上添加暗水印的基石
color/alpha 以数组形式存在,并存储于 ImageData 对象的data属性中。
以下代码可获得被返回的 ImageData 对象中第一个像素的 color/alpha 信息:
red=imgData.data[0];green=imgData.data[1];blue=imgData.data[2];alpha=imgData.data[3];
感兴趣的同学可以打印看一下效果
function createBackgroundImage(content, proportion, tiltAngle) {const can = document.createElement('canvas')can.width = document.body.clientWidth / proportioncan.height = document.body.clientHeight / proportionconst context = can.getContext('2d')context.rotate(-25 * Math.PI / 180);context.font = "800 30px Microsoft JhengHei";context.fillStyle = "#000";context.textAlign = 'center';context.textBaseline = 'Middle';context.fillText(content, 100, 100)console.log(context.getImageData(0, 0, can.width, can.height))return can.toDataURL("image/png")}const div = document.getElementById('content')console.log('div', div)div.style.backgroundImage = `url(${createBackgroundImage('伯约同学', 6, 10)})`
如下所示,给出了对应图片的ImageData结果,它有以下几个属性
data: Uint8ClampedArray(52752) [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …]colorSpace: “srgb”height: 42width: 314[[Prototype]]: ImageData
Uint8ClampedArray则是对应的像素点,每四个表征一个像素点,然后从左往右,从上往下的顺序进行排列。
下面进行一个简单的加密:把加密图案放到原图中,如果原图中对应的重叠像素点有内容,则最低位为1,否则为0
function mergeData(rawImageSrc, watermarkImageSrc) {return new Promise((resolve, reject) => {const img = new Image()img.onload = function () {const myCanvas = document.createElement("canvas");myCanvas.width = img.width;myCanvas.height = img.height;const ctx = myCanvas.getContext("2d")const bit = 0const offset = 3const oImageData = getImageData(rawImageSrc)const oData = oImageData.dataconst newData = getImageData(watermarkImageSrc).datafor (let i = 0; i < oData.length; i++) {if (i % 4 === bit) {// 只修改目标通道if (newData[i + offset] === 0 && (oData[i] % 2 === 1)) {// 没有信息的像素,将目标通道的奇数像素改为偶数if (oData[i] === 255) {oData[i]--} else {oData[i]++}} else if (newData[i + offset] !== 0 && (oData[i] % 2 === 0)) {// 有信息的像素oData[i]++}}}ctx.putImageData(oImageData, 0, 0)resolve(myCanvas.toDataURL("image/png"))}img.src = rawImageSrc})function getImageData(image) {const img = new Image()img.src = imageconst myCanvas = document.createElement("canvas");myCanvas.width = img.width;myCanvas.height = img.height;const myContext = myCanvas.getContext("2d")myContext.drawImage(img, 0, 0);return myContext.getImageData(0, 0, myCanvas.width, myCanvas.height)}
对应的解密方法
function decrypt(watermarkImage) {return new Promise((resolve, reject) => {const img = new Image()img.onload = function () {const myCanvas = document.createElement("canvas");myCanvas.width = img.width;myCanvas.height = img.height;const ctx = myCanvas.getContext("2d")const imageData = getImageData(watermarkImage)var data = imageData.data;for (var i = 0; i < data.length; i++) {if (i % 4 == 0) {// 红色分量if (data[i] % 2 == 0) {data[i] = 0;} else {data[i] = 255;}} else if (i % 4 == 3) {// alpha通道不做处理continue;} else {// 关闭其他分量,不关闭也不影响答案,甚至更美观 o(^▽^)odata[i] = 0;}}ctx.putImageData(imageData, 0, 0)resolve(myCanvas.toDataURL("image/png"))}img.src = watermarkImage})}
核心示例代码
const image1 = createBackgroundImage('伯约', 3, 10)const image2 = createBackgroundImage('学', 3, 10)mergeData(image1, image2).then(res => {console.log('res', res)decrypt(image2).then(res => {console.log('finalImage', res)})})
展示上面的res和finallImage并进行对比大仙,一张是两个字:伯约 另一张只有一个字:学
第一张:
第二张:
诚然,上面这个方法并不普适,毕竟加密解密方法都写成固定的了,不过思路是统一的,那就是都在原图的基础上修改像素点。
关于更多内容
姊妹篇
明水印:
https://jinbitao./article/details/122902264
具体可以看一下参考文章
1、/developer/article/1841652
2、/deeproom/p/14212568.html
3、/qq_44197554/article/details/122648423