微博图片加解密工具

此插件用于微博图片上传的加密以及查看时的解密,加入了中值滤波以消除微博水印和压缩带来的噪声。原作者是@B1llyHerrington,

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         微博图片加解密工具
// @namespace    http://tampermonkey.net/
// @version      0.2.0
// @description  此插件用于微博图片上传的加密以及查看时的解密,加入了中值滤波以消除微博水印和压缩带来的噪声。原作者是@B1llyHerrington,
// @author       丸户
// @match        https://weibo.com/*
// @match        http://photo.weibo.com/*
// @grant        none
// ==/UserScript==
//原作者 @B1llyHerrington  ||  Github:https://github.com/xfgryujk/weibo-img-crypto
(function() {
    'use strict';

window.isWbImgCryptoLoaded || (function () {
  window.isWbImgCryptoLoaded = true

  // 从谷歌V8引擎抄来的 https://github.com/v8/v8/blob/dae6dfe08ba9810abbe7eee81f7c58e999ae8525/src/math.js#L144
  class Random {
    constructor (seed) {
      if (seed === undefined) {
        seed = new Date().getTime()
      }
      this._rngstate = [seed & 0x0000FFFF, seed >>> 16]
    }

    // 返回[0, 1)
    random () {
      let r0 = (Math.imul(18030, this._rngstate[0] & 0xFFFF) + (this._rngstate[0] >>> 16)) | 0
      this._rngstate[0] = r0
      let r1 = (Math.imul(36969, this._rngstate[1] & 0xFFFF) + (this._rngstate[1] >>> 16)) | 0
      this._rngstate[1] = r1
      let x = ((r0 << 16) + (r1 & 0xFFFF)) | 0
      // Division by 0x100000000 through multiplication by reciprocal.
      return (x < 0 ? (x + 0x100000000) : x) * 2.3283064365386962890625e-10
    }

    // 返回[min, max]的整数
    randint (min, max) {
      return Math.floor(min + this.random() * (max - min + 1))
    }
  }

  // 生成[0, length)的随机序列,每次调用next()返回和之前不重复的值,直到[0, length)用完
  class RandomSequence {
    constructor (length, seed) {
      this._rng = new Random(seed)
      this._list = new Array(length)
      for (let i = 0; i < length; i++) {
        this._list[i] = i
      }
      this._nextMin = 0
    }

    next () {
      if (this._nextMin >= this._list.length) {
        this._nextMin = 0
      }
      let index = this._rng.randint(this._nextMin, this._list.length - 1)
      let result = this._list[index]
      this._list[index] = this._list[this._nextMin]
      this._list[this._nextMin] = result
      this._nextMin++
      return result
    }
  }

  const DEFAULT_SEED = 114514 //加解密种子

  function encrypt (data) {
    let nRgbs = data.length / 4 * 3
    let seq = new RandomSequence(nRgbs, window.randomSeed || DEFAULT_SEED)
    let buffer = new Uint8ClampedArray(nRgbs)
    // 每一个RGB值放到新的位置
    for (let i = 0; i < data.length; i += 4) {
      buffer[seq.next()] = data[i]
      buffer[seq.next()] = data[i + 1]
      buffer[seq.next()] = data[i + 2]
    }
    for (let i = 0, j = 0; i < data.length; i += 4, j += 3) {
      data[i] = buffer[j]
      data[i + 1] = buffer[j + 1]
      data[i + 2] = buffer[j + 2]
    }
  }

  function decrypt (data) {
    let nRgbs = data.length / 4 * 3
    let buffer = new Uint8ClampedArray(nRgbs)
    for (let i = 0, j = 0; i < data.length; i += 4, j += 3) {
      buffer[j] = data[i]
      buffer[j + 1] = data[i + 1]
      buffer[j + 2] = data[i + 2]
    }
    let seq = new RandomSequence(nRgbs, window.randomSeed || DEFAULT_SEED)
    // 取新的位置,放回原来的位置
    for (let i = 0; i < data.length; i += 4) {
      data[i] = buffer[seq.next()]
      data[i + 1] = buffer[seq.next()]
      data[i + 2] = buffer[seq.next()]
    }
  }

    //中值滤波
function median(filterWidth, filterHeight, dataArray, width, height) {
    var temp = [];
    for(var i=0; i<dataArray.length; i++) {
        temp.push(dataArray[i]);
    }
    for(var x=(filterWidth-1)/2; x<width-(filterWidth-1)/2; x++) {
        for(var y=(filterHeight-1)/2; y<width-(filterHeight-1)/2; y++) {
            var tempArray = [];
            for(var m=-(filterWidth-1)/2; m<=(filterWidth-1)/2; m++) {
                for(var j=-(filterHeight-1)/2; j<=(filterHeight-1)/2; j++) {
                    tempArray.push(temp[(j+y)*width+m+x]);
                }
            }
            // 泡沫排序,找出中值
            do {
                var loop = 0;
                for(var n=0; n<tempArray.length-1; n++) {
                    if(tempArray[n]>tempArray[n+1]) {
                        var tempChange = tempArray[n];
                        tempArray[n] = tempArray[n+1];
                        tempArray[n+1] = tempChange;
                        loop = 1;
                    }
                }
            }while(loop);
            dataArray[y*width+x] = tempArray[Math.round(tempArray.length/2)];
        }
    }
    return dataArray;
}

  let canvas = document.createElement('canvas')
  let ctx = canvas.getContext('2d')

  // Hook FileReader.readAsDataURL
  let originalReadAsDataURL = window.FileReader.prototype.readAsDataURL
  window.FileReader.prototype.readAsDataURL = function (file) {
    if (file.type.startsWith('image/') && file.type !== 'image/gif') { // 暂时不支持GIF
      // Hook onloadend
      let originalOnloadend = this.onloadend
      this.onloadend = () => {
        let img = new window.Image()
        img.onload = () => {
          [canvas.width, canvas.height] = [img.width, img.height]
          ctx.drawImage(img, 0, 0)

          // 加密
          let imgData = ctx.getImageData(0, 0, img.width, img.height)
          encrypt(imgData.data)
          ctx.putImageData(imgData, 0, 0)

          // 替换上传的图片
          originalOnloadend({target: {result: canvas.toDataURL()}})
        }
        img.src = this.result
      }
    }
    originalReadAsDataURL.call(this, file)
  }

  // 监听右键菜单
  document.addEventListener('contextmenu', event => {
    if (event.target instanceof window.Image) {
      // event.preventDefault() // 为了右键保存图片这里先注释掉了
      let originImg = event.target
      if (!(originImg instanceof window.Image)) {
        return
      }

      // 跨域
      let img = new window.Image()
      img.crossOrigin = 'anonymous'
      img.onerror = () => window.alert('载入图片失败,可能是跨域问题?')
      img.onload = () => {
        [canvas.width, canvas.height] = [img.width, img.height]
        ctx.drawImage(img, 0, 0)

        // 解密
        let imgData = ctx.getImageData(0, 0, canvas.width, canvas.height)
        decrypt(imgData.data)
          var r=[];
          var g=[];
          var b=[];

        for(let i=0, len=imgData.data.length; i<len; i+=4) {
                r[Math.floor(i/4)] = imgData.data[i]
                g[Math.floor(i/4)] = imgData.data[i+1]
                b[Math.floor(i/4)] = imgData.data[i+2]
        // RGB分离
        }

          r=median(3, 3, r, canvas.width, canvas.height)
          g=median(3, 3, g, canvas.width, canvas.height)
          b=median(3, 3, b, canvas.width, canvas.height)
          //3*3中值滤波
          for(let i=0, len2=imgData.data.length; i<len2; i+=4) {
             imgData.data[i]=r[Math.floor(i/4)]
             imgData.data[i+1]=g[Math.floor(i/4)]
             imgData.data[i+2]=b[Math.floor(i/4)]
        // 合成
        }
        ctx.putImageData(imgData, 0, 0)
        originImg.src = canvas.toDataURL()
      }

      if (!originImg.src.startsWith('data:')) { // 如果是'data:'开头说明已经解密过了
        // 防缓存
        img.src = originImg.src + (originImg.src.indexOf('?') === -1 ? '?_t=' : '&_t=') + new Date().getTime()
      }
    }
  })
})()
    // Your code here...
})();