2
0

webgl.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. /**
  2. * @licstart The following is the entire license notice for the
  3. * Javascript code in this page
  4. *
  5. * Copyright 2018 Mozilla Foundation
  6. *
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. *
  19. * @licend The above is the entire license notice for the
  20. * Javascript code in this page
  21. */
  22. "use strict";
  23. Object.defineProperty(exports, "__esModule", {
  24. value: true
  25. });
  26. exports.WebGLContext = void 0;
  27. var _util = require("../shared/util");
  28. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  29. function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
  30. function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
  31. var WebGLContext =
  32. /*#__PURE__*/
  33. function () {
  34. function WebGLContext(_ref) {
  35. var _ref$enable = _ref.enable,
  36. enable = _ref$enable === void 0 ? false : _ref$enable;
  37. _classCallCheck(this, WebGLContext);
  38. this._enabled = enable === true;
  39. }
  40. _createClass(WebGLContext, [{
  41. key: "composeSMask",
  42. value: function composeSMask(_ref2) {
  43. var layer = _ref2.layer,
  44. mask = _ref2.mask,
  45. properties = _ref2.properties;
  46. return WebGLUtils.composeSMask(layer, mask, properties);
  47. }
  48. }, {
  49. key: "drawFigures",
  50. value: function drawFigures(_ref3) {
  51. var width = _ref3.width,
  52. height = _ref3.height,
  53. backgroundColor = _ref3.backgroundColor,
  54. figures = _ref3.figures,
  55. context = _ref3.context;
  56. return WebGLUtils.drawFigures(width, height, backgroundColor, figures, context);
  57. }
  58. }, {
  59. key: "clear",
  60. value: function clear() {
  61. WebGLUtils.cleanup();
  62. }
  63. }, {
  64. key: "isEnabled",
  65. get: function get() {
  66. var enabled = this._enabled;
  67. if (enabled) {
  68. enabled = WebGLUtils.tryInitGL();
  69. }
  70. return (0, _util.shadow)(this, 'isEnabled', enabled);
  71. }
  72. }]);
  73. return WebGLContext;
  74. }();
  75. exports.WebGLContext = WebGLContext;
  76. var WebGLUtils = function WebGLUtilsClosure() {
  77. function loadShader(gl, code, shaderType) {
  78. var shader = gl.createShader(shaderType);
  79. gl.shaderSource(shader, code);
  80. gl.compileShader(shader);
  81. var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
  82. if (!compiled) {
  83. var errorMsg = gl.getShaderInfoLog(shader);
  84. throw new Error('Error during shader compilation: ' + errorMsg);
  85. }
  86. return shader;
  87. }
  88. function createVertexShader(gl, code) {
  89. return loadShader(gl, code, gl.VERTEX_SHADER);
  90. }
  91. function createFragmentShader(gl, code) {
  92. return loadShader(gl, code, gl.FRAGMENT_SHADER);
  93. }
  94. function createProgram(gl, shaders) {
  95. var program = gl.createProgram();
  96. for (var i = 0, ii = shaders.length; i < ii; ++i) {
  97. gl.attachShader(program, shaders[i]);
  98. }
  99. gl.linkProgram(program);
  100. var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
  101. if (!linked) {
  102. var errorMsg = gl.getProgramInfoLog(program);
  103. throw new Error('Error during program linking: ' + errorMsg);
  104. }
  105. return program;
  106. }
  107. function createTexture(gl, image, textureId) {
  108. gl.activeTexture(textureId);
  109. var texture = gl.createTexture();
  110. gl.bindTexture(gl.TEXTURE_2D, texture);
  111. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  112. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  113. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  114. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
  115. gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
  116. return texture;
  117. }
  118. var currentGL, currentCanvas;
  119. function generateGL() {
  120. if (currentGL) {
  121. return;
  122. }
  123. currentCanvas = document.createElement('canvas');
  124. currentGL = currentCanvas.getContext('webgl', {
  125. premultipliedalpha: false
  126. });
  127. }
  128. var smaskVertexShaderCode = '\
  129. attribute vec2 a_position; \
  130. attribute vec2 a_texCoord; \
  131. \
  132. uniform vec2 u_resolution; \
  133. \
  134. varying vec2 v_texCoord; \
  135. \
  136. void main() { \
  137. vec2 clipSpace = (a_position / u_resolution) * 2.0 - 1.0; \
  138. gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1); \
  139. \
  140. v_texCoord = a_texCoord; \
  141. } ';
  142. var smaskFragmentShaderCode = '\
  143. precision mediump float; \
  144. \
  145. uniform vec4 u_backdrop; \
  146. uniform int u_subtype; \
  147. uniform sampler2D u_image; \
  148. uniform sampler2D u_mask; \
  149. \
  150. varying vec2 v_texCoord; \
  151. \
  152. void main() { \
  153. vec4 imageColor = texture2D(u_image, v_texCoord); \
  154. vec4 maskColor = texture2D(u_mask, v_texCoord); \
  155. if (u_backdrop.a > 0.0) { \
  156. maskColor.rgb = maskColor.rgb * maskColor.a + \
  157. u_backdrop.rgb * (1.0 - maskColor.a); \
  158. } \
  159. float lum; \
  160. if (u_subtype == 0) { \
  161. lum = maskColor.a; \
  162. } else { \
  163. lum = maskColor.r * 0.3 + maskColor.g * 0.59 + \
  164. maskColor.b * 0.11; \
  165. } \
  166. imageColor.a *= lum; \
  167. imageColor.rgb *= imageColor.a; \
  168. gl_FragColor = imageColor; \
  169. } ';
  170. var smaskCache = null;
  171. function initSmaskGL() {
  172. var canvas, gl;
  173. generateGL();
  174. canvas = currentCanvas;
  175. currentCanvas = null;
  176. gl = currentGL;
  177. currentGL = null;
  178. var vertexShader = createVertexShader(gl, smaskVertexShaderCode);
  179. var fragmentShader = createFragmentShader(gl, smaskFragmentShaderCode);
  180. var program = createProgram(gl, [vertexShader, fragmentShader]);
  181. gl.useProgram(program);
  182. var cache = {};
  183. cache.gl = gl;
  184. cache.canvas = canvas;
  185. cache.resolutionLocation = gl.getUniformLocation(program, 'u_resolution');
  186. cache.positionLocation = gl.getAttribLocation(program, 'a_position');
  187. cache.backdropLocation = gl.getUniformLocation(program, 'u_backdrop');
  188. cache.subtypeLocation = gl.getUniformLocation(program, 'u_subtype');
  189. var texCoordLocation = gl.getAttribLocation(program, 'a_texCoord');
  190. var texLayerLocation = gl.getUniformLocation(program, 'u_image');
  191. var texMaskLocation = gl.getUniformLocation(program, 'u_mask');
  192. var texCoordBuffer = gl.createBuffer();
  193. gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
  194. gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0]), gl.STATIC_DRAW);
  195. gl.enableVertexAttribArray(texCoordLocation);
  196. gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);
  197. gl.uniform1i(texLayerLocation, 0);
  198. gl.uniform1i(texMaskLocation, 1);
  199. smaskCache = cache;
  200. }
  201. function composeSMask(layer, mask, properties) {
  202. var width = layer.width,
  203. height = layer.height;
  204. if (!smaskCache) {
  205. initSmaskGL();
  206. }
  207. var cache = smaskCache,
  208. canvas = cache.canvas,
  209. gl = cache.gl;
  210. canvas.width = width;
  211. canvas.height = height;
  212. gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
  213. gl.uniform2f(cache.resolutionLocation, width, height);
  214. if (properties.backdrop) {
  215. gl.uniform4f(cache.resolutionLocation, properties.backdrop[0], properties.backdrop[1], properties.backdrop[2], 1);
  216. } else {
  217. gl.uniform4f(cache.resolutionLocation, 0, 0, 0, 0);
  218. }
  219. gl.uniform1i(cache.subtypeLocation, properties.subtype === 'Luminosity' ? 1 : 0);
  220. var texture = createTexture(gl, layer, gl.TEXTURE0);
  221. var maskTexture = createTexture(gl, mask, gl.TEXTURE1);
  222. var buffer = gl.createBuffer();
  223. gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  224. gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0, width, 0, 0, height, 0, height, width, 0, width, height]), gl.STATIC_DRAW);
  225. gl.enableVertexAttribArray(cache.positionLocation);
  226. gl.vertexAttribPointer(cache.positionLocation, 2, gl.FLOAT, false, 0, 0);
  227. gl.clearColor(0, 0, 0, 0);
  228. gl.enable(gl.BLEND);
  229. gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
  230. gl.clear(gl.COLOR_BUFFER_BIT);
  231. gl.drawArrays(gl.TRIANGLES, 0, 6);
  232. gl.flush();
  233. gl.deleteTexture(texture);
  234. gl.deleteTexture(maskTexture);
  235. gl.deleteBuffer(buffer);
  236. return canvas;
  237. }
  238. var figuresVertexShaderCode = '\
  239. attribute vec2 a_position; \
  240. attribute vec3 a_color; \
  241. \
  242. uniform vec2 u_resolution; \
  243. uniform vec2 u_scale; \
  244. uniform vec2 u_offset; \
  245. \
  246. varying vec4 v_color; \
  247. \
  248. void main() { \
  249. vec2 position = (a_position + u_offset) * u_scale; \
  250. vec2 clipSpace = (position / u_resolution) * 2.0 - 1.0; \
  251. gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1); \
  252. \
  253. v_color = vec4(a_color / 255.0, 1.0); \
  254. } ';
  255. var figuresFragmentShaderCode = '\
  256. precision mediump float; \
  257. \
  258. varying vec4 v_color; \
  259. \
  260. void main() { \
  261. gl_FragColor = v_color; \
  262. } ';
  263. var figuresCache = null;
  264. function initFiguresGL() {
  265. var canvas, gl;
  266. generateGL();
  267. canvas = currentCanvas;
  268. currentCanvas = null;
  269. gl = currentGL;
  270. currentGL = null;
  271. var vertexShader = createVertexShader(gl, figuresVertexShaderCode);
  272. var fragmentShader = createFragmentShader(gl, figuresFragmentShaderCode);
  273. var program = createProgram(gl, [vertexShader, fragmentShader]);
  274. gl.useProgram(program);
  275. var cache = {};
  276. cache.gl = gl;
  277. cache.canvas = canvas;
  278. cache.resolutionLocation = gl.getUniformLocation(program, 'u_resolution');
  279. cache.scaleLocation = gl.getUniformLocation(program, 'u_scale');
  280. cache.offsetLocation = gl.getUniformLocation(program, 'u_offset');
  281. cache.positionLocation = gl.getAttribLocation(program, 'a_position');
  282. cache.colorLocation = gl.getAttribLocation(program, 'a_color');
  283. figuresCache = cache;
  284. }
  285. function drawFigures(width, height, backgroundColor, figures, context) {
  286. if (!figuresCache) {
  287. initFiguresGL();
  288. }
  289. var cache = figuresCache,
  290. canvas = cache.canvas,
  291. gl = cache.gl;
  292. canvas.width = width;
  293. canvas.height = height;
  294. gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
  295. gl.uniform2f(cache.resolutionLocation, width, height);
  296. var count = 0;
  297. var i, ii, rows;
  298. for (i = 0, ii = figures.length; i < ii; i++) {
  299. switch (figures[i].type) {
  300. case 'lattice':
  301. rows = figures[i].coords.length / figures[i].verticesPerRow | 0;
  302. count += (rows - 1) * (figures[i].verticesPerRow - 1) * 6;
  303. break;
  304. case 'triangles':
  305. count += figures[i].coords.length;
  306. break;
  307. }
  308. }
  309. var coords = new Float32Array(count * 2);
  310. var colors = new Uint8Array(count * 3);
  311. var coordsMap = context.coords,
  312. colorsMap = context.colors;
  313. var pIndex = 0,
  314. cIndex = 0;
  315. for (i = 0, ii = figures.length; i < ii; i++) {
  316. var figure = figures[i],
  317. ps = figure.coords,
  318. cs = figure.colors;
  319. switch (figure.type) {
  320. case 'lattice':
  321. var cols = figure.verticesPerRow;
  322. rows = ps.length / cols | 0;
  323. for (var row = 1; row < rows; row++) {
  324. var offset = row * cols + 1;
  325. for (var col = 1; col < cols; col++, offset++) {
  326. coords[pIndex] = coordsMap[ps[offset - cols - 1]];
  327. coords[pIndex + 1] = coordsMap[ps[offset - cols - 1] + 1];
  328. coords[pIndex + 2] = coordsMap[ps[offset - cols]];
  329. coords[pIndex + 3] = coordsMap[ps[offset - cols] + 1];
  330. coords[pIndex + 4] = coordsMap[ps[offset - 1]];
  331. coords[pIndex + 5] = coordsMap[ps[offset - 1] + 1];
  332. colors[cIndex] = colorsMap[cs[offset - cols - 1]];
  333. colors[cIndex + 1] = colorsMap[cs[offset - cols - 1] + 1];
  334. colors[cIndex + 2] = colorsMap[cs[offset - cols - 1] + 2];
  335. colors[cIndex + 3] = colorsMap[cs[offset - cols]];
  336. colors[cIndex + 4] = colorsMap[cs[offset - cols] + 1];
  337. colors[cIndex + 5] = colorsMap[cs[offset - cols] + 2];
  338. colors[cIndex + 6] = colorsMap[cs[offset - 1]];
  339. colors[cIndex + 7] = colorsMap[cs[offset - 1] + 1];
  340. colors[cIndex + 8] = colorsMap[cs[offset - 1] + 2];
  341. coords[pIndex + 6] = coords[pIndex + 2];
  342. coords[pIndex + 7] = coords[pIndex + 3];
  343. coords[pIndex + 8] = coords[pIndex + 4];
  344. coords[pIndex + 9] = coords[pIndex + 5];
  345. coords[pIndex + 10] = coordsMap[ps[offset]];
  346. coords[pIndex + 11] = coordsMap[ps[offset] + 1];
  347. colors[cIndex + 9] = colors[cIndex + 3];
  348. colors[cIndex + 10] = colors[cIndex + 4];
  349. colors[cIndex + 11] = colors[cIndex + 5];
  350. colors[cIndex + 12] = colors[cIndex + 6];
  351. colors[cIndex + 13] = colors[cIndex + 7];
  352. colors[cIndex + 14] = colors[cIndex + 8];
  353. colors[cIndex + 15] = colorsMap[cs[offset]];
  354. colors[cIndex + 16] = colorsMap[cs[offset] + 1];
  355. colors[cIndex + 17] = colorsMap[cs[offset] + 2];
  356. pIndex += 12;
  357. cIndex += 18;
  358. }
  359. }
  360. break;
  361. case 'triangles':
  362. for (var j = 0, jj = ps.length; j < jj; j++) {
  363. coords[pIndex] = coordsMap[ps[j]];
  364. coords[pIndex + 1] = coordsMap[ps[j] + 1];
  365. colors[cIndex] = colorsMap[cs[j]];
  366. colors[cIndex + 1] = colorsMap[cs[j] + 1];
  367. colors[cIndex + 2] = colorsMap[cs[j] + 2];
  368. pIndex += 2;
  369. cIndex += 3;
  370. }
  371. break;
  372. }
  373. }
  374. if (backgroundColor) {
  375. gl.clearColor(backgroundColor[0] / 255, backgroundColor[1] / 255, backgroundColor[2] / 255, 1.0);
  376. } else {
  377. gl.clearColor(0, 0, 0, 0);
  378. }
  379. gl.clear(gl.COLOR_BUFFER_BIT);
  380. var coordsBuffer = gl.createBuffer();
  381. gl.bindBuffer(gl.ARRAY_BUFFER, coordsBuffer);
  382. gl.bufferData(gl.ARRAY_BUFFER, coords, gl.STATIC_DRAW);
  383. gl.enableVertexAttribArray(cache.positionLocation);
  384. gl.vertexAttribPointer(cache.positionLocation, 2, gl.FLOAT, false, 0, 0);
  385. var colorsBuffer = gl.createBuffer();
  386. gl.bindBuffer(gl.ARRAY_BUFFER, colorsBuffer);
  387. gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);
  388. gl.enableVertexAttribArray(cache.colorLocation);
  389. gl.vertexAttribPointer(cache.colorLocation, 3, gl.UNSIGNED_BYTE, false, 0, 0);
  390. gl.uniform2f(cache.scaleLocation, context.scaleX, context.scaleY);
  391. gl.uniform2f(cache.offsetLocation, context.offsetX, context.offsetY);
  392. gl.drawArrays(gl.TRIANGLES, 0, count);
  393. gl.flush();
  394. gl.deleteBuffer(coordsBuffer);
  395. gl.deleteBuffer(colorsBuffer);
  396. return canvas;
  397. }
  398. return {
  399. tryInitGL: function tryInitGL() {
  400. try {
  401. generateGL();
  402. return !!currentGL;
  403. } catch (ex) {}
  404. return false;
  405. },
  406. composeSMask: composeSMask,
  407. drawFigures: drawFigures,
  408. cleanup: function cleanup() {
  409. if (smaskCache && smaskCache.canvas) {
  410. smaskCache.canvas.width = 0;
  411. smaskCache.canvas.height = 0;
  412. }
  413. if (figuresCache && figuresCache.canvas) {
  414. figuresCache.canvas.width = 0;
  415. figuresCache.canvas.height = 0;
  416. }
  417. smaskCache = null;
  418. figuresCache = null;
  419. }
  420. };
  421. }();