webgl.js 18 KB

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