Bilibili Anime4K滤镜

通过Anime4K滤镜让Bilibili和ACFun上的2D番剧更加清晰

目前为 2021-01-20 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Bilibili_Anime4K
  3. // @name:zh-CN Bilibili Anime4K滤镜
  4. // @description Bring Anime4K to Bilibili and ACFun's HTML5 player to clearify 2D anime.
  5. // @description:zh-CN 通过Anime4K滤镜让Bilibili和ACFun上的2D番剧更加清晰
  6. // @namespace http://net2cn.tk/
  7. // @homepageURL https://github.com/net2cn/Bilibili_Anime4K/
  8. // @supportURL https://github.com/net2cn/Bilibili_Anime4K/issues
  9. // @version 0.4.6
  10. // @author net2cn
  11. // @copyright bloc97, DextroseRe, NeuroWhAI, and all contributors of Anime4K
  12. // @match *://www.bilibili.com/video/av*
  13. // @match *://www.bilibili.com/bangumi/play/ep*
  14. // @match *://www.bilibili.com/bangumi/play/ss*
  15. // @match *://www.bilibili.com/video/BV*
  16. // @match *://www.acfun.cn/bangumi/aa*
  17. // @grant none
  18. // @license MIT License
  19. // @run-at document-idle
  20. // ==/UserScript==
  21.  
  22. // WebGL implementation by NeuroWhAI.
  23. // https://github.com/bloc97/Anime4K/blob/master/web/main.js
  24.  
  25. function createShader(gl, type, source) {
  26. var shader = gl.createShader(type);
  27. gl.shaderSource(shader, source);
  28.  
  29. gl.compileShader(shader);
  30. if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
  31. throw new Error(gl.getShaderInfoLog(shader));
  32. }
  33.  
  34. return shader;
  35. }
  36.  
  37. function createProgram(gl, vertexSource, fragmentSource) {
  38. var program = gl.createProgram();
  39.  
  40. //console.log(fragmentSource)
  41.  
  42. var vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexSource);
  43. var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentSource);
  44.  
  45. gl.attachShader(program, vertexShader);
  46. gl.attachShader(program, fragmentShader);
  47.  
  48. gl.linkProgram(program);
  49. if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
  50. throw new Error(gl.getProgramInfoLog(program));
  51. }
  52.  
  53. var wrapper = { program: program };
  54.  
  55. var numAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
  56. for (var i = 0; i < numAttributes; i++) {
  57. var attribute = gl.getActiveAttrib(program, i);
  58. wrapper[attribute.name] = gl.getAttribLocation(program, attribute.name);
  59. }
  60. var numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
  61. for (var i$1 = 0; i$1 < numUniforms; i$1++) {
  62. var uniform = gl.getActiveUniform(program, i$1);
  63. wrapper[uniform.name] = gl.getUniformLocation(program, uniform.name);
  64. }
  65.  
  66. return wrapper;
  67. }
  68.  
  69. function createTexture(gl, filter, data, width, height) {
  70. var texture = gl.createTexture();
  71. gl.bindTexture(gl.TEXTURE_2D, texture);
  72. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  73. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  74. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter);
  75. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filter);
  76. if (data instanceof Uint8Array) {
  77. gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
  78. } else {
  79. gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, data);
  80. }
  81. gl.bindTexture(gl.TEXTURE_2D, null);
  82. return texture;
  83. }
  84.  
  85. function bindTexture(gl, texture, unit) {
  86. gl.activeTexture(gl.TEXTURE0 + unit);
  87. gl.bindTexture(gl.TEXTURE_2D, texture);
  88. }
  89.  
  90. function updateTexture(gl, texture, src) {
  91. gl.bindTexture(gl.TEXTURE_2D, texture);
  92. gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, src);
  93. }
  94.  
  95. function createBuffer(gl, data) {
  96. var buffer = gl.createBuffer();
  97. gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  98. gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
  99. return buffer;
  100. }
  101.  
  102. function bindAttribute(gl, buffer, attribute, numComponents) {
  103. gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  104. gl.enableVertexAttribArray(attribute);
  105. gl.vertexAttribPointer(attribute, numComponents, gl.FLOAT, false, 0, 0);
  106. }
  107.  
  108. function bindFramebuffer(gl, framebuffer, texture) {
  109. gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
  110. if (texture) {
  111. gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
  112. }
  113. }
  114.  
  115.  
  116. const quadVert = `
  117. precision mediump float;
  118.  
  119. attribute vec2 a_pos;
  120. varying vec2 v_tex_pos;
  121.  
  122. void main() {
  123. v_tex_pos = a_pos;
  124. gl_Position = vec4(1.0 - 2.0 * a_pos, 0, 1);
  125. }
  126. `;
  127.  
  128. const scaleFrag = `
  129. precision mediump float;
  130.  
  131. uniform sampler2D u_texture;
  132. uniform vec2 u_size;
  133. varying vec2 v_tex_pos;
  134.  
  135. vec4 interp(const vec2 uv) {
  136. vec2 px = 1.0 / u_size;
  137. vec2 vc = (floor(uv * u_size)) * px;
  138. vec2 f = fract(uv * u_size);
  139. vec4 tl = texture2D(u_texture, vc);
  140. vec4 tr = texture2D(u_texture, vc + vec2(px.x, 0));
  141. vec4 bl = texture2D(u_texture, vc + vec2(0, px.y));
  142. vec4 br = texture2D(u_texture, vc + px);
  143. return mix(mix(tl, tr, f.x), mix(bl, br, f.x), f.y);
  144. }
  145.  
  146. void main() {
  147. gl_FragColor = interp(1.0 - v_tex_pos);
  148. //gl_FragColor = texture2D(u_texture, 1.0 - v_tex_pos);
  149. }
  150. `;
  151.  
  152. const thinLinesFrag = `
  153. precision mediump float;
  154.  
  155. uniform sampler2D scaled_texture;
  156. uniform float u_scale;
  157. uniform vec2 u_pt;
  158. varying vec2 v_tex_pos;
  159.  
  160. #define strength (min(u_scale / 6.0, 1.0))
  161.  
  162. vec4 HOOKED_tex(vec2 pos) {
  163. return texture2D(scaled_texture, pos);
  164. }
  165.  
  166. float getLum(vec4 rgb) {
  167. return (rgb.r + rgb.r + rgb.g + rgb.g + rgb.g + rgb.b) / 6.0;
  168. }
  169.  
  170. vec4 getLargest(vec4 cc, vec4 lightestColor, vec4 a, vec4 b, vec4 c) {
  171. vec4 newColor = cc * (1.0 - strength) + ((a + b + c) / 3.0) * strength;
  172. if (newColor.a > lightestColor.a) {
  173. return newColor;
  174. }
  175. return lightestColor;
  176. }
  177.  
  178. vec4 getRGBL(vec2 pos) {
  179. return vec4(HOOKED_tex(pos).rgb, getLum(HOOKED_tex(pos)));
  180. }
  181.  
  182. float min3v(vec4 a, vec4 b, vec4 c) {
  183. return min(min(a.a, b.a), c.a);
  184. }
  185. float max3v(vec4 a, vec4 b, vec4 c) {
  186. return max(max(a.a, b.a), c.a);
  187. }
  188.  
  189. void main() {
  190. vec2 HOOKED_pos = v_tex_pos;
  191.  
  192. vec2 d = u_pt;
  193.  
  194. vec4 cc = getRGBL(HOOKED_pos);
  195. vec4 t = getRGBL(HOOKED_pos + vec2(0.0, -d.y));
  196. vec4 tl = getRGBL(HOOKED_pos + vec2(-d.x, -d.y));
  197. vec4 tr = getRGBL(HOOKED_pos + vec2(d.x, -d.y));
  198.  
  199. vec4 l = getRGBL(HOOKED_pos + vec2(-d.x, 0.0));
  200. vec4 r = getRGBL(HOOKED_pos + vec2(d.x, 0.0));
  201.  
  202. vec4 b = getRGBL(HOOKED_pos + vec2(0.0, d.y));
  203. vec4 bl = getRGBL(HOOKED_pos + vec2(-d.x, d.y));
  204. vec4 br = getRGBL(HOOKED_pos + vec2(d.x, d.y));
  205.  
  206. vec4 lightestColor = cc;
  207.  
  208. //Kernel 0 and 4
  209. float maxDark = max3v(br, b, bl);
  210. float minLight = min3v(tl, t, tr);
  211.  
  212. if (minLight > cc.a && minLight > maxDark) {
  213. lightestColor = getLargest(cc, lightestColor, tl, t, tr);
  214. } else {
  215. maxDark = max3v(tl, t, tr);
  216. minLight = min3v(br, b, bl);
  217. if (minLight > cc.a && minLight > maxDark) {
  218. lightestColor = getLargest(cc, lightestColor, br, b, bl);
  219. }
  220. }
  221.  
  222. //Kernel 1 and 5
  223. maxDark = max3v(cc, l, b);
  224. minLight = min3v(r, t, tr);
  225.  
  226. if (minLight > maxDark) {
  227. lightestColor = getLargest(cc, lightestColor, r, t, tr);
  228. } else {
  229. maxDark = max3v(cc, r, t);
  230. minLight = min3v(bl, l, b);
  231. if (minLight > maxDark) {
  232. lightestColor = getLargest(cc, lightestColor, bl, l, b);
  233. }
  234. }
  235.  
  236. //Kernel 2 and 6
  237. maxDark = max3v(l, tl, bl);
  238. minLight = min3v(r, br, tr);
  239.  
  240. if (minLight > cc.a && minLight > maxDark) {
  241. lightestColor = getLargest(cc, lightestColor, r, br, tr);
  242. } else {
  243. maxDark = max3v(r, br, tr);
  244. minLight = min3v(l, tl, bl);
  245. if (minLight > cc.a && minLight > maxDark) {
  246. lightestColor = getLargest(cc, lightestColor, l, tl, bl);
  247. }
  248. }
  249.  
  250. //Kernel 3 and 7
  251. maxDark = max3v(cc, l, t);
  252. minLight = min3v(r, br, b);
  253.  
  254. if (minLight > maxDark) {
  255. lightestColor = getLargest(cc, lightestColor, r, br, b);
  256. } else {
  257. maxDark = max3v(cc, r, b);
  258. minLight = min3v(t, l, tl);
  259. if (minLight > maxDark) {
  260. lightestColor = getLargest(cc, lightestColor, t, l, tl);
  261. }
  262. }
  263.  
  264. gl_FragColor = lightestColor;
  265. }
  266. `;
  267.  
  268. const lumaFrag = `
  269. precision mediump float;
  270.  
  271. uniform sampler2D scaled_texuture;
  272. uniform sampler2D post_kernel_texture;
  273. uniform vec2 u_pt;
  274. varying vec2 v_tex_pos;
  275.  
  276. vec4 HOOKED_tex(vec2 pos) {
  277. return texture2D(scaled_texuture, pos);
  278. }
  279.  
  280. float getLum(vec4 rgb) {
  281. return (rgb.r + rgb.r + rgb.g + rgb.g + rgb.g + rgb.b) / 6.0;
  282. }
  283.  
  284. void main() { //Save lum on OUTPUT
  285. vec2 HOOKED_pos = v_tex_pos;
  286.  
  287. vec4 rgb = HOOKED_tex(HOOKED_pos);
  288. float lum = getLum(rgb);
  289. gl_FragColor = vec4(lum);
  290. }
  291. `;
  292.  
  293. const lumaGausXFrag = `
  294. precision mediump float;
  295.  
  296. uniform sampler2D scaled_texture;
  297. uniform sampler2D post_kernel_texture;
  298. uniform vec2 u_pt;
  299. varying vec2 v_tex_pos;
  300.  
  301. vec4 HOOKED_tex(vec2 pos) {
  302. return texture2D(scaled_texture, pos);
  303. }
  304.  
  305. vec4 LUMA_tex(vec2 pos){
  306. return texture2D(post_kernel_texture, pos);
  307. }
  308.  
  309. float lumGaussian5(vec2 pos, vec2 d) {
  310. float g = LUMA_tex(pos - (d * 2.0)).x * 0.187691;
  311. g = g + LUMA_tex(pos - d).x * 0.206038;
  312. g = g + LUMA_tex(pos).x * 0.212543;
  313. g = g + LUMA_tex(pos + d).x * 0.206038;
  314. g = g + LUMA_tex(pos + (d * 2.0)).x * 0.187691;
  315.  
  316. return clamp(g, 0.0, 1.0); //Clamp for sanity check
  317. }
  318.  
  319.  
  320. void main() {
  321. vec2 HOOKED_pos = v_tex_pos;
  322. vec2 HOOKED_pt = u_pt;
  323.  
  324. float g = lumGaussian5(HOOKED_pos, vec2(HOOKED_pt.x, 0));
  325. gl_FragColor = vec4(LUMA_tex(HOOKED_pos).x, g, LUMA_tex(HOOKED_pos).zw);
  326. }
  327. `;
  328.  
  329. const lumaGausYFrag = `
  330. precision mediump float;
  331.  
  332. uniform sampler2D scaled_texture;
  333. uniform sampler2D post_kernel_texture;
  334. uniform vec2 u_pt;
  335. varying vec2 v_tex_pos;
  336.  
  337. vec4 HOOKED_tex(vec2 pos) {
  338. return texture2D(scaled_texture, pos);
  339. }
  340.  
  341. vec4 LUMAG_tex(vec2 pos){
  342. return texture2D(post_kernel_texture, pos);
  343. }
  344.  
  345. float lumGaussian5(vec2 pos, vec2 d) {
  346. float g = LUMAG_tex(pos - (d * 2.0)).x * 0.187691;
  347. g = g + LUMAG_tex(pos - d).x * 0.206038;
  348. g = g + LUMAG_tex(pos).x * 0.212543;
  349. g = g + LUMAG_tex(pos + d).x * 0.206038;
  350. g = g + LUMAG_tex(pos + (d * 2.0)).x * 0.187691;
  351.  
  352. return clamp(g, 0.0, 1.0); //Clamp for sanity check
  353. }
  354.  
  355. void main() {
  356. vec2 HOOKED_pos = v_tex_pos;
  357. vec2 HOOKED_pt = u_pt;
  358.  
  359. float g = lumGaussian5(HOOKED_pos, vec2(0, HOOKED_pt.y));
  360. gl_FragColor = vec4(LUMAG_tex(HOOKED_pos).x, g, LUMAG_tex(HOOKED_pos).zw);
  361. }
  362. `;
  363.  
  364. const lineDetectFrag = `
  365. precision mediump float;
  366.  
  367. uniform sampler2D scaled_texture;
  368. uniform sampler2D post_kernel_texture;
  369. uniform vec2 u_pt;
  370. varying vec2 v_tex_pos;
  371.  
  372. #define BlendColorDodgef(base, blend) (((blend) == 1.0) ? (blend) : min((base) / (1.0 - (blend)), 1.0))
  373. #define BlendColorDividef(top, bottom) (((bottom) == 1.0) ? (bottom) : min((top) / (bottom), 1.0))
  374.  
  375. // Component wise blending
  376. #define Blend(base, blend, funcf) vec3(funcf(base.r, blend.r), funcf(base.g, blend.g), funcf(base.b, blend.b))
  377. #define BlendColorDodge(base, blend) Blend(base, blend, BlendColorDodgef)
  378.  
  379. vec4 HOOKED_tex(vec2 pos) {
  380. return texture2D(scaled_texture, pos);
  381. }
  382.  
  383. vec4 POSTKERNEL_tex(vec2 pos){
  384. return texture2D(post_kernel_texture, pos);
  385. }
  386.  
  387. void main() {
  388. vec2 HOOKED_pos = v_tex_pos;
  389.  
  390. float lum = clamp(POSTKERNEL_tex(HOOKED_pos).x, 0.001, 0.999);
  391. float lumg = clamp(POSTKERNEL_tex(HOOKED_pos).y, 0.001, 0.999);
  392.  
  393. float pseudolines = BlendColorDividef(lum, lumg);
  394. pseudolines = 1.0 - clamp(pseudolines - 0.05, 0.0, 1.0);
  395.  
  396. gl_FragColor = vec4(pseudolines, 0, 0, 0);
  397. }
  398. `;
  399.  
  400. const lineGausXFrag = `
  401. precision mediump float;
  402.  
  403. uniform sampler2D scaled_texture;
  404. uniform sampler2D post_kernel_texture;
  405. uniform vec2 u_pt;
  406. varying vec2 v_tex_pos;
  407.  
  408. vec4 HOOKED_tex(vec2 pos) {
  409. return texture2D(scaled_texture, pos);
  410. }
  411.  
  412. vec4 LUMAG_tex(vec2 pos){
  413. return texture2D(post_kernel_texture, pos);
  414. }
  415.  
  416. float lumGaussian5(vec2 pos, vec2 d) {
  417. float g = LUMAG_tex(pos - (d * 2.0)).x * 0.187691;
  418. g = g + LUMAG_tex(pos - d).x * 0.206038;
  419. g = g + LUMAG_tex(pos).x * 0.212543;
  420. g = g + LUMAG_tex(pos + d).x * 0.206038;
  421. g = g + LUMAG_tex(pos + (d * 2.0)).x * 0.187691;
  422.  
  423. return clamp(g, 0.0, 1.0); //Clamp for sanity check
  424. }
  425.  
  426.  
  427. void main() {
  428. vec2 HOOKED_pos = v_tex_pos;
  429. vec2 HOOKED_pt = u_pt;
  430.  
  431. float g = lumGaussian5(HOOKED_pos, vec2(HOOKED_pt.x, 0.0));
  432. gl_FragColor = vec4(g, 0, 0, 0);
  433. }
  434. `;
  435.  
  436. const lineGausYFrag = `
  437. precision mediump float;
  438.  
  439. uniform sampler2D scaled_texture;
  440. uniform sampler2D post_kernel_texture;
  441. uniform vec2 u_pt;
  442. varying vec2 v_tex_pos;
  443.  
  444. vec4 HOOKED_tex(vec2 pos) {
  445. return texture2D(scaled_texture, pos);
  446. }
  447.  
  448. vec4 LUMAG_tex(vec2 pos){
  449. return texture2D(post_kernel_texture, pos);
  450. }
  451.  
  452. float lumGaussian5(vec2 pos, vec2 d) {
  453. float g = LUMAG_tex(pos - (d * 2.0)).x * 0.187691;
  454. g = g + LUMAG_tex(pos - d).x * 0.206038;
  455. g = g + LUMAG_tex(pos).x * 0.212543;
  456. g = g + LUMAG_tex(pos + d).x * 0.206038;
  457. g = g + LUMAG_tex(pos + (d * 2.0)).x * 0.187691;
  458.  
  459. return clamp(g, 0.0, 1.0); //Clamp for sanity check
  460. }
  461.  
  462. void main() {
  463. vec2 HOOKED_pos = v_tex_pos;
  464. vec2 HOOKED_pt = u_pt;
  465.  
  466. float g = lumGaussian5(HOOKED_pos, vec2(0.0, HOOKED_pt.y));
  467. gl_FragColor = vec4(g, 0, 0, 0);
  468. }
  469. `;
  470.  
  471. const gradFrag = `
  472. precision mediump float;
  473.  
  474. uniform sampler2D u_texture;
  475. uniform sampler2D u_textureTemp;
  476. uniform vec2 u_pt;
  477. varying vec2 v_tex_pos;
  478.  
  479. vec4 HOOKED_tex(vec2 pos) {
  480. return texture2D(u_texture, 1.0 - pos);
  481. }
  482.  
  483. vec4 POSTKERNEL_tex(vec2 pos) {
  484. return texture2D(u_textureTemp, 1.0 - pos);
  485. }
  486.  
  487. vec4 getRGBL(vec2 pos) {
  488. return vec4(HOOKED_tex(pos).rgb, POSTKERNEL_tex(pos).x);
  489. }
  490.  
  491. void main() {
  492. vec2 HOOKED_pos = v_tex_pos;
  493.  
  494. vec2 d = u_pt;
  495.  
  496. //[tl t tr]
  497. //[ l cc r]
  498. //[bl b br]
  499. vec4 cc = getRGBL(HOOKED_pos);
  500. vec4 t = getRGBL(HOOKED_pos + vec2(0.0, -d.y));
  501. vec4 tl = getRGBL(HOOKED_pos + vec2(-d.x, -d.y));
  502. vec4 tr = getRGBL(HOOKED_pos + vec2(d.x, -d.y));
  503.  
  504. vec4 l = getRGBL(HOOKED_pos + vec2(-d.x, 0.0));
  505. vec4 r = getRGBL(HOOKED_pos + vec2(d.x, 0.0));
  506.  
  507. vec4 b = getRGBL(HOOKED_pos + vec2(0.0, d.y));
  508. vec4 bl = getRGBL(HOOKED_pos + vec2(-d.x, d.y));
  509. vec4 br = getRGBL(HOOKED_pos + vec2(d.x, d.y));
  510.  
  511. //Horizontal Gradient
  512. //[-1 0 1]
  513. //[-2 0 2]
  514. //[-1 0 1]
  515. float xgrad = (-tl.a + tr.a - l.a - l.a + r.a + r.a - bl.a + br.a);
  516.  
  517. //Vertical Gradient
  518. //[-1 -2 -1]
  519. //[ 0 0 0]
  520. //[ 1 2 1]
  521. float ygrad = (-tl.a - t.a - t.a - tr.a + bl.a + b.a + b.a + br.a);
  522.  
  523. gl_FragColor = vec4(1.0 - clamp(sqrt(xgrad * xgrad + ygrad * ygrad), 0.0, 1.0));
  524. }
  525. `;
  526.  
  527. const refineFrag = `
  528. precision mediump float;
  529.  
  530. uniform sampler2D u_texture;
  531. uniform sampler2D u_textureTemp;
  532. uniform vec2 u_pt;
  533. uniform float u_scale;
  534. varying vec2 v_tex_pos;
  535.  
  536. vec4 HOOKED_tex(vec2 pos) {
  537. return texture2D(u_texture, vec2(pos.x, 1.0 - pos.y));
  538. }
  539.  
  540. vec4 POSTKERNEL_tex(vec2 pos) {
  541. return texture2D(u_textureTemp, vec2(pos.x, 1.0 - pos.y));
  542. }
  543.  
  544. #define LINE_DETECT_MUL 8.0
  545. #define LINE_DETECT_THRESHOLD 0.2
  546.  
  547. #define strength (min(u_scale, 1.0))
  548. #define lineprob (POSTKERNEL_tex(v_tex_pos).y)
  549.  
  550. vec4 getAverage(vec4 cc, vec4 a, vec4 b, vec4 c) {
  551. float prob = clamp(lineprob * LINE_DETECT_MUL, 0.0, 1.0);
  552. if (prob < LINE_DETECT_THRESHOLD) {
  553. prob = 0.0;
  554. }
  555. float realstrength = clamp(strength * prob, 0.0, 1.0);
  556. return cc * (1.0 - realstrength) + ((a + b + c) / 3.0) * realstrength;
  557. }
  558.  
  559. vec4 getRGBL(vec2 pos) {
  560. return vec4(HOOKED_tex(pos).rgb, POSTKERNEL_tex(pos).z);
  561. }
  562.  
  563. float min3v(vec4 a, vec4 b, vec4 c) {
  564. return min(min(a.a, b.a), c.a);
  565. }
  566. float max3v(vec4 a, vec4 b, vec4 c) {
  567. return max(max(a.a, b.a), c.a);
  568. }
  569.  
  570. void main() {
  571. vec2 HOOKED_pos = v_tex_pos;
  572.  
  573. vec2 d = u_pt;
  574.  
  575. vec4 cc = getRGBL(HOOKED_pos);
  576. vec4 t = getRGBL(HOOKED_pos + vec2(0.0, -d.y));
  577. vec4 tl = getRGBL(HOOKED_pos + vec2(-d.x, -d.y));
  578. vec4 tr = getRGBL(HOOKED_pos + vec2(d.x, -d.y));
  579.  
  580. vec4 l = getRGBL(HOOKED_pos + vec2(-d.x, 0.0));
  581. vec4 r = getRGBL(HOOKED_pos + vec2(d.x, 0.0));
  582.  
  583. vec4 b = getRGBL(HOOKED_pos + vec2(0.0, d.y));
  584. vec4 bl = getRGBL(HOOKED_pos + vec2(-d.x, d.y));
  585. vec4 br = getRGBL(HOOKED_pos + vec2(d.x, d.y));
  586.  
  587. //Kernel 0 and 4
  588. float maxDark = max3v(br, b, bl);
  589. float minLight = min3v(tl, t, tr);
  590.  
  591. if (minLight > cc.a && minLight > maxDark) {
  592. gl_FragColor = getAverage(cc, tl, t, tr);
  593. return;
  594. } else {
  595. maxDark = max3v(tl, t, tr);
  596. minLight = min3v(br, b, bl);
  597. if (minLight > cc.a && minLight > maxDark) {
  598. gl_FragColor = getAverage(cc, br, b, bl);
  599. return;
  600. }
  601. }
  602.  
  603. //Kernel 1 and 5
  604. maxDark = max3v(cc, l, b);
  605. minLight = min3v(r, t, tr);
  606.  
  607. if (minLight > maxDark) {
  608. gl_FragColor = getAverage(cc, r, t, tr);
  609. return;
  610. } else {
  611. maxDark = max3v(cc, r, t);
  612. minLight = min3v(bl, l, b);
  613. if (minLight > maxDark) {
  614. gl_FragColor = getAverage(cc, bl, l, b);
  615. return;
  616. }
  617. }
  618.  
  619. //Kernel 2 and 6
  620. maxDark = max3v(l, tl, bl);
  621. minLight = min3v(r, br, tr);
  622.  
  623. if (minLight > cc.a && minLight > maxDark) {
  624. gl_FragColor = getAverage(cc, r, br, tr);
  625. return;
  626. } else {
  627. maxDark = max3v(r, br, tr);
  628. minLight = min3v(l, tl, bl);
  629. if (minLight > cc.a && minLight > maxDark) {
  630. gl_FragColor = getAverage(cc, l, tl, bl);
  631. return;
  632. }
  633. }
  634.  
  635. //Kernel 3 and 7
  636. maxDark = max3v(cc, l, t);
  637. minLight = min3v(r, br, b);
  638.  
  639. if (minLight > maxDark) {
  640. gl_FragColor = getAverage(cc, r, br, b);
  641. return;
  642. } else {
  643. maxDark = max3v(cc, r, b);
  644. minLight = min3v(t, l, tl);
  645. if (minLight > maxDark) {
  646. gl_FragColor = getAverage(cc, t, l, tl);
  647. return;
  648. }
  649. }
  650.  
  651.  
  652. gl_FragColor = cc;
  653. }
  654. `;
  655.  
  656. const fxaaFrag = `
  657. precision mediump float;
  658.  
  659. uniform sampler2D u_texture;
  660. uniform sampler2D u_textureTemp;
  661. uniform vec2 u_pt;
  662. uniform float u_scale;
  663. varying vec2 v_tex_pos;
  664.  
  665. vec4 HOOKED_tex(vec2 pos) {
  666. return texture2D(u_texture, vec2(pos.x, 1.0 - pos.y));
  667. }
  668.  
  669. vec4 POSTKERNEL_tex(vec2 pos) {
  670. return texture2D(u_textureTemp, vec2(pos.x, 1.0 - pos.y));
  671. }
  672.  
  673. #define FXAA_MIN (1.0 / 128.0)
  674. #define FXAA_MUL (1.0 / 8.0)
  675. #define FXAA_SPAN 8.0
  676.  
  677. #define LINE_DETECT_MUL 4.0
  678. #define LINE_DETECT_THRESHOLD 0.2
  679.  
  680. #define strength (min(u_scale, 1.0))
  681. #define lineprob (POSTKERNEL_tex(v_tex_pos).y)
  682.  
  683. vec4 getAverage(vec4 cc, vec4 xc) {
  684. float prob = clamp(lineprob * LINE_DETECT_MUL, 0.0, 1.0);
  685. if (prob < LINE_DETECT_THRESHOLD) {
  686. prob = 0.0;
  687. }
  688. float realstrength = clamp(strength * prob, 0.0, 1.0);
  689. return cc * (1.0 - realstrength) + xc * realstrength;
  690. }
  691.  
  692. float getLum(vec4 rgb) {
  693. return (rgb.r + rgb.r + rgb.g + rgb.g + rgb.g + rgb.b) / 6.0;
  694. }
  695.  
  696. void main() {
  697. vec2 HOOKED_pos = v_tex_pos;
  698.  
  699. vec2 d = u_pt;
  700.  
  701. vec4 cc = HOOKED_tex(HOOKED_pos);
  702. vec4 xc = cc;
  703.  
  704. float t = POSTKERNEL_tex(HOOKED_pos + vec2(0, -d.y)).x;
  705. float l = POSTKERNEL_tex(HOOKED_pos + vec2(-d.x, 0)).x;
  706. float r = POSTKERNEL_tex(HOOKED_pos + vec2(d.x, 0)).x;
  707. float b = POSTKERNEL_tex(HOOKED_pos + vec2(0, d.y)).x;
  708.  
  709. float tl = POSTKERNEL_tex(HOOKED_pos + vec2(-d.x, -d.y)).x;
  710. float tr = POSTKERNEL_tex(HOOKED_pos + vec2(d.x, -d.y)).x;
  711. float bl = POSTKERNEL_tex(HOOKED_pos + vec2(-d.x, d.y)).x;
  712. float br = POSTKERNEL_tex(HOOKED_pos + vec2(d.x, d.y)).x;
  713. float cl = POSTKERNEL_tex(HOOKED_pos).x;
  714.  
  715. float minl = min(cl, min(min(tl, tr), min(bl, br)));
  716. float maxl = max(cl, max(max(tl, tr), max(bl, br)));
  717.  
  718. vec2 dir = vec2(- tl - tr + bl + br, tl - tr + bl - br);
  719.  
  720. float dirReduce = max((tl + tr + bl + br) *
  721. (0.25 * FXAA_MUL), FXAA_MIN);
  722.  
  723. float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);
  724. dir = min(vec2(FXAA_SPAN, FXAA_SPAN),
  725. max(vec2(-FXAA_SPAN, -FXAA_SPAN),
  726. dir * rcpDirMin)) * d;
  727.  
  728. vec4 rgbA = 0.5 * (
  729. HOOKED_tex(HOOKED_pos + dir * -(1.0/6.0)) +
  730. HOOKED_tex(HOOKED_pos + dir * (1.0/6.0)));
  731. vec4 rgbB = rgbA * 0.5 + 0.25 * (
  732. HOOKED_tex(HOOKED_pos + dir * -0.5) +
  733. HOOKED_tex(HOOKED_pos + dir * 0.5));
  734.  
  735. //vec4 luma = vec4(0.299, 0.587, 0.114, 0.0);
  736. //float lumb = dot(rgbB, luma);
  737. float lumb = getLum(rgbB);
  738.  
  739. if ((lumb < minl) || (lumb > maxl)) {
  740. xc = rgbA;
  741. } else {
  742. xc = rgbB;
  743. }
  744. gl_FragColor = getAverage(cc, xc);
  745. }
  746. `;
  747.  
  748. const drawFrag = `
  749. precision mediump float;
  750.  
  751. uniform sampler2D u_texture;
  752. varying vec2 v_tex_pos;
  753.  
  754. void main() {
  755. vec4 color = texture2D(u_texture, vec2(v_tex_pos.x, 1.0 - v_tex_pos.y));
  756. gl_FragColor = color;
  757. }
  758. `;
  759.  
  760.  
  761. function Scaler(gl) {
  762. this.gl = gl;
  763.  
  764. this.inputTex = null;
  765. this.inputMov = null;
  766. this.inputWidth = 0;
  767. this.inputHeight = 0;
  768.  
  769. this.loggedPaused = false;
  770.  
  771. this.quadBuffer = createBuffer(gl, new Float32Array([0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1]));
  772. this.framebuffer = gl.createFramebuffer();
  773.  
  774. console.log('Compiling shaders...')
  775. this.scaleProgram = createProgram(gl, quadVert, scaleFrag);
  776. this.thinLinesProgram = createProgram(gl, quadVert, thinLinesFrag);
  777. this.lumaProgram = createProgram(gl, quadVert, lumaFrag);
  778. this.lumaGausXProgram = createProgram(gl, quadVert, lumaGausXFrag);
  779. this.lumaGausYProgram = createProgram(gl, quadVert, lumaGausYFrag);
  780. this.lineDetectProgram = createProgram(gl, quadVert, lineDetectFrag);
  781. this.lineGausXProgram = createProgram(gl, quadVert, lineGausXFrag);
  782. this.lineGausYProgram = createProgram(gl, quadVert, lineGausYFrag);
  783. this.gradProgram = createProgram(gl, quadVert, gradFrag);
  784. this.refineProgram = createProgram(gl, quadVert, refineFrag);
  785. this.fxaaProgram = createProgram(gl, quadVert, fxaaFrag);
  786. this.drawProgram = createProgram(gl, quadVert, drawFrag);
  787.  
  788. this.postKernelTexture = null;
  789. this.postKernelTexture2 = null;
  790.  
  791. this.scale = 1.0;
  792. }
  793.  
  794. Scaler.prototype.inputImage = function (img) {
  795. const gl = this.gl;
  796.  
  797. this.inputWidth = img.width;
  798. this.inputHeight = img.height;
  799.  
  800. this.inputTex = createTexture(gl, gl.LINEAR, img);
  801. this.inputMov = null;
  802. }
  803.  
  804. Scaler.prototype.inputVideo = function (mov) {
  805. const gl = this.gl;
  806.  
  807. const width = mov.videoWidth;
  808. const height = mov.videoHeight;
  809.  
  810. this.inputWidth = width;
  811. this.inputHeight = height;
  812.  
  813. let emptyPixels = new Uint8Array(width * height * 4);
  814. this.inputTex = createTexture(gl, gl.LINEAR, emptyPixels, width, height);
  815. this.inputMov = mov;
  816. }
  817.  
  818. Scaler.prototype.resize = function (scale) {
  819. const gl = this.gl;
  820.  
  821. const width = Math.round(this.inputWidth * scale);
  822. const height = Math.round(this.inputHeight * scale);
  823.  
  824. gl.canvas.width = width;
  825. gl.canvas.height = height;
  826.  
  827. let emptyPixels = new Uint8Array(width * height * 4);
  828. this.scaleTexture = createTexture(gl, gl.LINEAR, emptyPixels, width, height);
  829. this.scaleTexture2 = createTexture(gl, gl.LINEAR, emptyPixels, width, height);
  830. this.postKernelTexture = createTexture(gl, gl.LINEAR, emptyPixels, width, height);
  831. this.postKernelTexture2 = createTexture(gl, gl.LINEAR, emptyPixels, width, height);
  832. }
  833.  
  834. Scaler.prototype.render = function () {
  835. if (!this.inputTex) {
  836. return;
  837. }
  838.  
  839. const gl = this.gl;
  840. const scalePgm = this.scaleProgram;
  841. const thinLinesPgm = this.thinLinesProgram;
  842. const lumaPgm = this.lumaProgram;
  843. const lumaGausXPgm = this.lumaGausXProgram;
  844. const lumaGausYPgm = this.lumaGausYProgram;
  845. const lineDetectPgm = this.lineDetectProgram;
  846. const lineGausXPgm = this.lineGausXProgram;
  847. const lineGausYPgm = this.lineGausYProgram;
  848. const gradPgm = this.gradProgram;
  849. const refinePgm = this.refineProgram;
  850. const fxaaPgm = this.fxaaProgram;
  851. const drawPgm = this.drawProgram;
  852. const defaultRatio = 16/9
  853.  
  854. // Nasty trick to fix video quailty changing bug.
  855. if (gl.getError() == gl.INVALID_VALUE) {
  856. console.log('glError detected! Fetching new viedo tag... (This may happen due to resolution change)')
  857. let newMov = getNewVideoTag()
  858. console.log("Video width: " + newMov.videoWidth)
  859. console.log("Video height: " + newMov.videoHeight)
  860. let newRatio = newMov.videoWidth/newMov.videoHeight
  861. console.log("Video Ratio: " + newRatio)
  862. if ((newRatio/defaultRatio - 1) < 0.001) { // To prevent float precision caused problem.
  863. let w = newRatio/defaultRatio*100
  864. console.log("Setting new width ratio: " + w + "%")
  865. globalBoard.style.width = w + "%"
  866. }
  867. this.inputVideo(newMov)
  868. }
  869.  
  870. // Check if video is paused.
  871. if (this.inputMov.paused){
  872. // If paused we stop rendering new frames.
  873. if(!this.loggedPaused){
  874. console.log("Video paused.")
  875. this.loggedPaused = true
  876. }
  877. return
  878. } else {
  879. // Else we continue rendering new frames.
  880. if(this.loggedPaused){
  881. console.log("Video continued.")
  882. this.loggedPaused = false
  883. }
  884. }
  885.  
  886. if (this.inputMov) {
  887. updateTexture(gl, this.inputTex, this.inputMov);
  888. }
  889.  
  890. // Automatic change scale according to original video resolution.
  891. // Upscaled to 1440p.
  892. let newScale = 1440 / this.inputMov.videoHeight;
  893. if (this.scale != newScale){
  894. this.scale = newScale;
  895. console.log('Setting scale to ' + this.scale);
  896. }
  897.  
  898. gl.disable(gl.DEPTH_TEST);
  899. gl.disable(gl.STENCIL_TEST);
  900.  
  901. gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
  902.  
  903.  
  904. // First upscaling with Bicubic interpolation.
  905. // Upscaling
  906. bindFramebuffer(gl, this.framebuffer, this.scaleTexture);
  907.  
  908. gl.useProgram(scalePgm.program);
  909.  
  910. bindAttribute(gl, this.quadBuffer, scalePgm.a_pos, 2);
  911. bindTexture(gl, this.inputTex, 0);
  912. gl.uniform1i(scalePgm.u_texture, 0);
  913. gl.uniform2f(scalePgm.u_size, this.inputWidth, this.inputHeight);
  914.  
  915. gl.drawArrays(gl.TRIANGLES, 0, 6);
  916.  
  917.  
  918. // Scaled: scaleTexture
  919.  
  920. // Thin Lines
  921. bindFramebuffer(gl, this.framebuffer, this.scaleTexture2);
  922.  
  923. gl.useProgram(thinLinesPgm.program);
  924.  
  925. bindAttribute(gl, this.quadBuffer, thinLinesPgm.a_pos, 2);
  926. bindTexture(gl, this.scaleTexture, 0);
  927. gl.uniform1i(thinLinesPgm.scaled_texture, 0);
  928. gl.uniform1f(thinLinesPgm.u_scale, this.scale);
  929. gl.uniform2f(thinLinesPgm.u_pt, 1.0 / gl.canvas.width, 1.0 / gl.canvas.height);
  930.  
  931. gl.drawArrays(gl.TRIANGLES, 0, 6);
  932.  
  933. // Scaled: scaleTexture2
  934.  
  935. // Compute Luminance
  936. bindFramebuffer(gl, this.framebuffer, this.postKernelTexture);
  937.  
  938. gl.useProgram(lumaPgm.program);
  939.  
  940. bindAttribute(gl, this.quadBuffer, lumaPgm.a_pos, 2);
  941. bindTexture(gl, this.scaleTexture2, 0);
  942. gl.uniform1i(lumaPgm.scaled_texture, 0);
  943. gl.uniform2f(lumaPgm.u_pt, 1.0 / gl.canvas.width, 1.0 / gl.canvas.height);
  944.  
  945. gl.drawArrays(gl.TRIANGLES, 0, 6);
  946.  
  947. // Scaled: scaleTexture2 (unchanged)
  948. // PostKernel: postKernelTexture (luminance)
  949.  
  950. // Compute Luminance Gaussian X
  951. bindFramebuffer(gl, this.framebuffer, this.postKernelTexture2);
  952.  
  953. gl.useProgram(lumaGausXPgm.program);
  954.  
  955. bindAttribute(gl, this.quadBuffer, lumaGausXPgm.a_pos, 2);
  956. bindTexture(gl, this.scaleTexture2, 0);
  957. bindTexture(gl, this.postKernelTexture, 1);
  958. gl.uniform1i(lumaGausXPgm.scaled_texture, 0);
  959. gl.uniform1i(lumaGausXPgm.post_kernel_texture, 1);
  960. gl.uniform2f(lumaGausXPgm.u_pt, 1.0 / gl.canvas.width, 1.0 / gl.canvas.height);
  961.  
  962. gl.drawArrays(gl.TRIANGLES, 0, 6);
  963.  
  964. // PostKernel: postKernelTexture2
  965.  
  966. // Compute Luminance Gaussian Y
  967. bindFramebuffer(gl, this.framebuffer, this.postKernelTexture);
  968.  
  969. gl.useProgram(lumaGausYPgm.program);
  970.  
  971. bindAttribute(gl, this.quadBuffer, lumaGausYPgm.a_pos, 2);
  972. bindTexture(gl, this.scaleTexture2, 0);
  973. bindTexture(gl, this.postKernelTexture2, 1);
  974. gl.uniform1i(lumaGausYPgm.scaled_texture, 0);
  975. gl.uniform1i(lumaGausYPgm.post_kernel_texture, 1);
  976. gl.uniform2f(lumaGausYPgm.u_pt, 1.0 / gl.canvas.width, 1.0 / gl.canvas.height);
  977.  
  978. gl.drawArrays(gl.TRIANGLES, 0, 6);
  979.  
  980. // PostKernel: postKernelTexture
  981.  
  982. // Line detect
  983. bindFramebuffer(gl, this.framebuffer, this.postKernelTexture2);
  984.  
  985. gl.useProgram(lineDetectPgm.program);
  986.  
  987. bindAttribute(gl, this.quadBuffer, lumaGausYPgm.a_pos, 2);
  988. bindTexture(gl, this.scaleTexture2, 0);
  989. bindTexture(gl, this.postKernelTexture, 1);
  990. gl.uniform1i(lineDetectPgm.scaled_texture, 0);
  991. gl.uniform1i(lineDetectPgm.post_kernel_texture, 1);
  992. gl.uniform2f(lineDetectPgm.u_pt, 1.0 / gl.canvas.width, 1.0 / gl.canvas.height);
  993.  
  994. gl.drawArrays(gl.TRIANGLES, 0, 6);
  995.  
  996. // PostKernel: postKernelTexture2
  997.  
  998. // Compute Line Gaussian X
  999. bindFramebuffer(gl, this.framebuffer, this.postKernelTexture);
  1000.  
  1001. gl.useProgram(lineGausXPgm.program);
  1002.  
  1003. bindAttribute(gl, this.quadBuffer, lineGausXPgm.a_pos, 2);
  1004. bindTexture(gl, this.scaleTexture2, 0);
  1005. bindTexture(gl, this.postKernelTexture2, 1);
  1006. gl.uniform1i(lineGausXPgm.scaled_texture, 0);
  1007. gl.uniform1i(lineGausXPgm.post_kernel_texture, 1);
  1008. gl.uniform2f(lineGausXPgm.u_pt, 1.0 / gl.canvas.width, 1.0 / gl.canvas.height);
  1009.  
  1010. gl.drawArrays(gl.TRIANGLES, 0, 6);
  1011.  
  1012. // PostKernel: postKernelTexture
  1013.  
  1014. // Compute Line Gaussian Y
  1015. bindFramebuffer(gl, this.framebuffer, this.postKernelTexture2);
  1016.  
  1017. gl.useProgram(lineGausYPgm.program);
  1018.  
  1019. bindAttribute(gl, this.quadBuffer, lineGausYPgm.a_pos, 2);
  1020. bindTexture(gl, this.scaleTexture2, 0);
  1021. bindTexture(gl, this.postKernelTexture, 1);
  1022. gl.uniform1i(lineGausYPgm.scaled_texture, 0);
  1023. gl.uniform1i(lineGausYPgm.post_kernel_texture, 1);
  1024. gl.uniform2f(lineGausYPgm.u_pt, 1.0 / gl.canvas.width, 1.0 / gl.canvas.height);
  1025.  
  1026. gl.drawArrays(gl.TRIANGLES, 0, 6);
  1027.  
  1028. // PostKernel: postKernelTexture2
  1029.  
  1030. // Compute Gradient
  1031. bindFramebuffer(gl, this.framebuffer, this.postKernelTexture);
  1032.  
  1033. gl.useProgram(gradPgm.program);
  1034.  
  1035. bindAttribute(gl, this.quadBuffer, gradPgm.a_pos, 2);
  1036. bindTexture(gl, this.scaleTexture2, 0);
  1037. bindTexture(gl, this.postKernelTexture2, 1);
  1038. gl.uniform1i(gradPgm.scaleFrag, 0);
  1039. gl.uniform1i(gradPgm.post_kernel_texture, 1);
  1040. gl.uniform2f(gradPgm.u_pt, 1.0 / gl.canvas.width, 1.0 / gl.canvas.height);
  1041.  
  1042. gl.drawArrays(gl.TRIANGLES, 0, 6);
  1043.  
  1044. // PostKernel: postKernelTexture
  1045.  
  1046. // Refine
  1047. bindFramebuffer(gl, this.framebuffer, this.scaleTexture);
  1048.  
  1049. gl.useProgram(refinePgm.program);
  1050.  
  1051. bindAttribute(gl, this.quadBuffer, refinePgm.a_pos, 2);
  1052. bindTexture(gl, this.scaleTexture2, 0);
  1053. bindTexture(gl, this.postKernelTexture, 1);
  1054. gl.uniform1i(refinePgm.u_texture, 0);
  1055. gl.uniform1i(refinePgm.u_textureTemp, 1);
  1056. gl.uniform1f(refinePgm.u_scale, this.scale);
  1057. gl.uniform2f(refinePgm.u_pt, 1.0 / gl.canvas.width, 1.0 / gl.canvas.height);
  1058.  
  1059. gl.drawArrays(gl.TRIANGLES, 0, 6);
  1060.  
  1061. // PostKernel: scaleTexture
  1062.  
  1063. // FXAA
  1064. bindFramebuffer(gl, this.framebuffer, this.scaleTexture2);
  1065.  
  1066. gl.useProgram(fxaaPgm.program);
  1067.  
  1068. bindAttribute(gl, this.quadBuffer, fxaaPgm.a_pos, 2);
  1069. bindTexture(gl, this.scaleTexture, 0);
  1070. bindTexture(gl, this.postKernelTexture, 1);
  1071. gl.uniform1i(fxaaPgm.u_texture, 0);
  1072. gl.uniform1i(fxaaPgm.u_textureTemp, 1);
  1073. gl.uniform1f(fxaaPgm.u_scale, this.scale);
  1074. gl.uniform2f(fxaaPgm.u_pt, 1.0 / gl.canvas.width, 1.0 / gl.canvas.height);
  1075.  
  1076. gl.drawArrays(gl.TRIANGLES, 0, 6);
  1077.  
  1078. // PostKernel: scaleTexture2
  1079.  
  1080. // Draw
  1081. bindFramebuffer(gl, null);
  1082.  
  1083. gl.useProgram(drawPgm.program);
  1084.  
  1085. bindAttribute(gl, this.quadBuffer, drawPgm.a_pos, 2);
  1086. bindTexture(gl, this.scaleTexture2, 0);
  1087. gl.uniform1i(drawPgm.u_texture, 0);
  1088.  
  1089. gl.drawArrays(gl.TRIANGLES, 0, 6);
  1090. }
  1091.  
  1092. // Parameters.
  1093. let globalScaler = null;
  1094. let globalMovOrig = null;
  1095. let globalBoard = null;
  1096. let globalScale = 2.0;
  1097.  
  1098. let globalUpdateId, globalPreviousDelta = 0;
  1099. let globalFpsLimit = 30; // Limit fps to 30 fps. Change here if you want more frames to be rendered. (But usually 30 fps is pretty enough for most anime as they are mostly done on threes.)
  1100.  
  1101. async function injectCanvas() {
  1102. console.log('Injecting canvas...')
  1103.  
  1104. // Create a canvas (since video tag do not support WebGL).
  1105. globalMovOrig = await getVideoTag()
  1106. console.log(globalMovOrig)
  1107.  
  1108. let div = globalMovOrig.parentElement
  1109. div.style.backgroundColor = "black" // Patch for ACFun.
  1110.  
  1111. globalBoard = document.createElement('canvas');
  1112. // Make it visually fill the positioned parent
  1113. globalBoard.style.width = '100%';
  1114. globalBoard.style.height = '100%';
  1115. // ...then set the internal size to match
  1116. globalBoard.width = globalBoard.offsetWidth;
  1117. globalBoard.height = globalBoard.offsetHeight;
  1118. // Add it back to the div where contains the video tag we use as input.
  1119. div.appendChild(globalBoard)
  1120.  
  1121. // Hide original video tag, we don't need it to be displayed.
  1122. globalMovOrig.style.display = 'none'
  1123. }
  1124.  
  1125. async function getVideoTag() {
  1126. while(document.getElementsByTagName("video").length <= 0) {
  1127. await new Promise(r => setTimeout(r, 500));
  1128. }
  1129.  
  1130. return document.getElementsByTagName("video")[0]
  1131. }
  1132.  
  1133. function getNewVideoTag() {
  1134. // Get video tag.
  1135. globalMovOrig = document.getElementsByTagName("video")[0]
  1136.  
  1137. // Hide it, we don't need it to be displayed.
  1138. globalMovOrig.style.display = 'none'
  1139.  
  1140. globalScaler.scale = globalScale;
  1141.  
  1142. return globalMovOrig
  1143. }
  1144.  
  1145. function doFilter() {
  1146. // Setting our parameters for filtering.
  1147. // scale: multipliers that we need to zoom in.
  1148. // Here's the fun part. We create a pixel shader for our canvas
  1149. console.log('Enabling filter...')
  1150.  
  1151. const gl = globalBoard.getContext('webgl');
  1152.  
  1153. globalMovOrig.addEventListener('loadedmetadata', function () {
  1154. globalScaler = new Scaler(gl);
  1155. globalScaler.inputVideo(globalMovOrig);
  1156. globalScaler.resize(globalScale);
  1157. globalScaler.scale = globalScale;
  1158. }, true);
  1159. globalMovOrig.addEventListener('error', function () {
  1160. alert("Can't get video, sorry.");
  1161. }, true);
  1162.  
  1163. console.log("Framerate limit is set to " + globalFpsLimit + " FPS.")
  1164. // Do it! Filter it! Profit!
  1165. function render(currentDelta) {
  1166. // Notice that limiting the framerate here did increase performance.
  1167. globalUpdateId = requestAnimationFrame(render);
  1168. let delta = currentDelta - globalPreviousDelta;
  1169.  
  1170. if (globalFpsLimit && delta < 1000/globalFpsLimit){
  1171. return;
  1172. }
  1173.  
  1174. if (globalScaler) {
  1175. globalScaler.render();
  1176. }
  1177. globalPreviousDelta = currentDelta
  1178. }
  1179.  
  1180. globalUpdateId = requestAnimationFrame(render);
  1181. }
  1182.  
  1183. (async function () {
  1184. console.log('Bilibili_Anime4K starting...')
  1185. await injectCanvas()
  1186. doFilter()
  1187. })();