You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
475 lines
18 KiB
475 lines
18 KiB
/** |
|
* @licstart The following is the entire license notice for the |
|
* Javascript code in this page |
|
* |
|
* Copyright 2019 Mozilla Foundation |
|
* |
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
* you may not use this file except in compliance with the License. |
|
* You may obtain a copy of the License at |
|
* |
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* Unless required by applicable law or agreed to in writing, software |
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
* See the License for the specific language governing permissions and |
|
* limitations under the License. |
|
* |
|
* @licend The above is the entire license notice for the |
|
* Javascript code in this page |
|
*/ |
|
"use strict"; |
|
|
|
Object.defineProperty(exports, "__esModule", { |
|
value: true |
|
}); |
|
exports.WebGLContext = void 0; |
|
|
|
var _util = require("../shared/util"); |
|
|
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a 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); } } |
|
|
|
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } |
|
|
|
var WebGLContext = |
|
/*#__PURE__*/ |
|
function () { |
|
function WebGLContext(_ref) { |
|
var _ref$enable = _ref.enable, |
|
enable = _ref$enable === void 0 ? false : _ref$enable; |
|
|
|
_classCallCheck(this, WebGLContext); |
|
|
|
this._enabled = enable === true; |
|
} |
|
|
|
_createClass(WebGLContext, [{ |
|
key: "composeSMask", |
|
value: function composeSMask(_ref2) { |
|
var layer = _ref2.layer, |
|
mask = _ref2.mask, |
|
properties = _ref2.properties; |
|
return WebGLUtils.composeSMask(layer, mask, properties); |
|
} |
|
}, { |
|
key: "drawFigures", |
|
value: function drawFigures(_ref3) { |
|
var width = _ref3.width, |
|
height = _ref3.height, |
|
backgroundColor = _ref3.backgroundColor, |
|
figures = _ref3.figures, |
|
context = _ref3.context; |
|
return WebGLUtils.drawFigures(width, height, backgroundColor, figures, context); |
|
} |
|
}, { |
|
key: "clear", |
|
value: function clear() { |
|
WebGLUtils.cleanup(); |
|
} |
|
}, { |
|
key: "isEnabled", |
|
get: function get() { |
|
var enabled = this._enabled; |
|
|
|
if (enabled) { |
|
enabled = WebGLUtils.tryInitGL(); |
|
} |
|
|
|
return (0, _util.shadow)(this, 'isEnabled', enabled); |
|
} |
|
}]); |
|
|
|
return WebGLContext; |
|
}(); |
|
|
|
exports.WebGLContext = WebGLContext; |
|
|
|
var WebGLUtils = function WebGLUtilsClosure() { |
|
function loadShader(gl, code, shaderType) { |
|
var shader = gl.createShader(shaderType); |
|
gl.shaderSource(shader, code); |
|
gl.compileShader(shader); |
|
var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS); |
|
|
|
if (!compiled) { |
|
var errorMsg = gl.getShaderInfoLog(shader); |
|
throw new Error('Error during shader compilation: ' + errorMsg); |
|
} |
|
|
|
return shader; |
|
} |
|
|
|
function createVertexShader(gl, code) { |
|
return loadShader(gl, code, gl.VERTEX_SHADER); |
|
} |
|
|
|
function createFragmentShader(gl, code) { |
|
return loadShader(gl, code, gl.FRAGMENT_SHADER); |
|
} |
|
|
|
function createProgram(gl, shaders) { |
|
var program = gl.createProgram(); |
|
|
|
for (var i = 0, ii = shaders.length; i < ii; ++i) { |
|
gl.attachShader(program, shaders[i]); |
|
} |
|
|
|
gl.linkProgram(program); |
|
var linked = gl.getProgramParameter(program, gl.LINK_STATUS); |
|
|
|
if (!linked) { |
|
var errorMsg = gl.getProgramInfoLog(program); |
|
throw new Error('Error during program linking: ' + errorMsg); |
|
} |
|
|
|
return program; |
|
} |
|
|
|
function createTexture(gl, image, textureId) { |
|
gl.activeTexture(textureId); |
|
var texture = gl.createTexture(); |
|
gl.bindTexture(gl.TEXTURE_2D, texture); |
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); |
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); |
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); |
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); |
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); |
|
return texture; |
|
} |
|
|
|
var currentGL, currentCanvas; |
|
|
|
function generateGL() { |
|
if (currentGL) { |
|
return; |
|
} |
|
|
|
currentCanvas = document.createElement('canvas'); |
|
currentGL = currentCanvas.getContext('webgl', { |
|
premultipliedalpha: false |
|
}); |
|
} |
|
|
|
var smaskVertexShaderCode = '\ |
|
attribute vec2 a_position; \ |
|
attribute vec2 a_texCoord; \ |
|
\ |
|
uniform vec2 u_resolution; \ |
|
\ |
|
varying vec2 v_texCoord; \ |
|
\ |
|
void main() { \ |
|
vec2 clipSpace = (a_position / u_resolution) * 2.0 - 1.0; \ |
|
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1); \ |
|
\ |
|
v_texCoord = a_texCoord; \ |
|
} '; |
|
var smaskFragmentShaderCode = '\ |
|
precision mediump float; \ |
|
\ |
|
uniform vec4 u_backdrop; \ |
|
uniform int u_subtype; \ |
|
uniform sampler2D u_image; \ |
|
uniform sampler2D u_mask; \ |
|
\ |
|
varying vec2 v_texCoord; \ |
|
\ |
|
void main() { \ |
|
vec4 imageColor = texture2D(u_image, v_texCoord); \ |
|
vec4 maskColor = texture2D(u_mask, v_texCoord); \ |
|
if (u_backdrop.a > 0.0) { \ |
|
maskColor.rgb = maskColor.rgb * maskColor.a + \ |
|
u_backdrop.rgb * (1.0 - maskColor.a); \ |
|
} \ |
|
float lum; \ |
|
if (u_subtype == 0) { \ |
|
lum = maskColor.a; \ |
|
} else { \ |
|
lum = maskColor.r * 0.3 + maskColor.g * 0.59 + \ |
|
maskColor.b * 0.11; \ |
|
} \ |
|
imageColor.a *= lum; \ |
|
imageColor.rgb *= imageColor.a; \ |
|
gl_FragColor = imageColor; \ |
|
} '; |
|
var smaskCache = null; |
|
|
|
function initSmaskGL() { |
|
var canvas, gl; |
|
generateGL(); |
|
canvas = currentCanvas; |
|
currentCanvas = null; |
|
gl = currentGL; |
|
currentGL = null; |
|
var vertexShader = createVertexShader(gl, smaskVertexShaderCode); |
|
var fragmentShader = createFragmentShader(gl, smaskFragmentShaderCode); |
|
var program = createProgram(gl, [vertexShader, fragmentShader]); |
|
gl.useProgram(program); |
|
var cache = {}; |
|
cache.gl = gl; |
|
cache.canvas = canvas; |
|
cache.resolutionLocation = gl.getUniformLocation(program, 'u_resolution'); |
|
cache.positionLocation = gl.getAttribLocation(program, 'a_position'); |
|
cache.backdropLocation = gl.getUniformLocation(program, 'u_backdrop'); |
|
cache.subtypeLocation = gl.getUniformLocation(program, 'u_subtype'); |
|
var texCoordLocation = gl.getAttribLocation(program, 'a_texCoord'); |
|
var texLayerLocation = gl.getUniformLocation(program, 'u_image'); |
|
var texMaskLocation = gl.getUniformLocation(program, 'u_mask'); |
|
var texCoordBuffer = gl.createBuffer(); |
|
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer); |
|
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); |
|
gl.enableVertexAttribArray(texCoordLocation); |
|
gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0); |
|
gl.uniform1i(texLayerLocation, 0); |
|
gl.uniform1i(texMaskLocation, 1); |
|
smaskCache = cache; |
|
} |
|
|
|
function composeSMask(layer, mask, properties) { |
|
var width = layer.width, |
|
height = layer.height; |
|
|
|
if (!smaskCache) { |
|
initSmaskGL(); |
|
} |
|
|
|
var cache = smaskCache, |
|
canvas = cache.canvas, |
|
gl = cache.gl; |
|
canvas.width = width; |
|
canvas.height = height; |
|
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); |
|
gl.uniform2f(cache.resolutionLocation, width, height); |
|
|
|
if (properties.backdrop) { |
|
gl.uniform4f(cache.resolutionLocation, properties.backdrop[0], properties.backdrop[1], properties.backdrop[2], 1); |
|
} else { |
|
gl.uniform4f(cache.resolutionLocation, 0, 0, 0, 0); |
|
} |
|
|
|
gl.uniform1i(cache.subtypeLocation, properties.subtype === 'Luminosity' ? 1 : 0); |
|
var texture = createTexture(gl, layer, gl.TEXTURE0); |
|
var maskTexture = createTexture(gl, mask, gl.TEXTURE1); |
|
var buffer = gl.createBuffer(); |
|
gl.bindBuffer(gl.ARRAY_BUFFER, buffer); |
|
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0, width, 0, 0, height, 0, height, width, 0, width, height]), gl.STATIC_DRAW); |
|
gl.enableVertexAttribArray(cache.positionLocation); |
|
gl.vertexAttribPointer(cache.positionLocation, 2, gl.FLOAT, false, 0, 0); |
|
gl.clearColor(0, 0, 0, 0); |
|
gl.enable(gl.BLEND); |
|
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); |
|
gl.clear(gl.COLOR_BUFFER_BIT); |
|
gl.drawArrays(gl.TRIANGLES, 0, 6); |
|
gl.flush(); |
|
gl.deleteTexture(texture); |
|
gl.deleteTexture(maskTexture); |
|
gl.deleteBuffer(buffer); |
|
return canvas; |
|
} |
|
|
|
var figuresVertexShaderCode = '\ |
|
attribute vec2 a_position; \ |
|
attribute vec3 a_color; \ |
|
\ |
|
uniform vec2 u_resolution; \ |
|
uniform vec2 u_scale; \ |
|
uniform vec2 u_offset; \ |
|
\ |
|
varying vec4 v_color; \ |
|
\ |
|
void main() { \ |
|
vec2 position = (a_position + u_offset) * u_scale; \ |
|
vec2 clipSpace = (position / u_resolution) * 2.0 - 1.0; \ |
|
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1); \ |
|
\ |
|
v_color = vec4(a_color / 255.0, 1.0); \ |
|
} '; |
|
var figuresFragmentShaderCode = '\ |
|
precision mediump float; \ |
|
\ |
|
varying vec4 v_color; \ |
|
\ |
|
void main() { \ |
|
gl_FragColor = v_color; \ |
|
} '; |
|
var figuresCache = null; |
|
|
|
function initFiguresGL() { |
|
var canvas, gl; |
|
generateGL(); |
|
canvas = currentCanvas; |
|
currentCanvas = null; |
|
gl = currentGL; |
|
currentGL = null; |
|
var vertexShader = createVertexShader(gl, figuresVertexShaderCode); |
|
var fragmentShader = createFragmentShader(gl, figuresFragmentShaderCode); |
|
var program = createProgram(gl, [vertexShader, fragmentShader]); |
|
gl.useProgram(program); |
|
var cache = {}; |
|
cache.gl = gl; |
|
cache.canvas = canvas; |
|
cache.resolutionLocation = gl.getUniformLocation(program, 'u_resolution'); |
|
cache.scaleLocation = gl.getUniformLocation(program, 'u_scale'); |
|
cache.offsetLocation = gl.getUniformLocation(program, 'u_offset'); |
|
cache.positionLocation = gl.getAttribLocation(program, 'a_position'); |
|
cache.colorLocation = gl.getAttribLocation(program, 'a_color'); |
|
figuresCache = cache; |
|
} |
|
|
|
function drawFigures(width, height, backgroundColor, figures, context) { |
|
if (!figuresCache) { |
|
initFiguresGL(); |
|
} |
|
|
|
var cache = figuresCache, |
|
canvas = cache.canvas, |
|
gl = cache.gl; |
|
canvas.width = width; |
|
canvas.height = height; |
|
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); |
|
gl.uniform2f(cache.resolutionLocation, width, height); |
|
var count = 0; |
|
var i, ii, rows; |
|
|
|
for (i = 0, ii = figures.length; i < ii; i++) { |
|
switch (figures[i].type) { |
|
case 'lattice': |
|
rows = figures[i].coords.length / figures[i].verticesPerRow | 0; |
|
count += (rows - 1) * (figures[i].verticesPerRow - 1) * 6; |
|
break; |
|
|
|
case 'triangles': |
|
count += figures[i].coords.length; |
|
break; |
|
} |
|
} |
|
|
|
var coords = new Float32Array(count * 2); |
|
var colors = new Uint8Array(count * 3); |
|
var coordsMap = context.coords, |
|
colorsMap = context.colors; |
|
var pIndex = 0, |
|
cIndex = 0; |
|
|
|
for (i = 0, ii = figures.length; i < ii; i++) { |
|
var figure = figures[i], |
|
ps = figure.coords, |
|
cs = figure.colors; |
|
|
|
switch (figure.type) { |
|
case 'lattice': |
|
var cols = figure.verticesPerRow; |
|
rows = ps.length / cols | 0; |
|
|
|
for (var row = 1; row < rows; row++) { |
|
var offset = row * cols + 1; |
|
|
|
for (var col = 1; col < cols; col++, offset++) { |
|
coords[pIndex] = coordsMap[ps[offset - cols - 1]]; |
|
coords[pIndex + 1] = coordsMap[ps[offset - cols - 1] + 1]; |
|
coords[pIndex + 2] = coordsMap[ps[offset - cols]]; |
|
coords[pIndex + 3] = coordsMap[ps[offset - cols] + 1]; |
|
coords[pIndex + 4] = coordsMap[ps[offset - 1]]; |
|
coords[pIndex + 5] = coordsMap[ps[offset - 1] + 1]; |
|
colors[cIndex] = colorsMap[cs[offset - cols - 1]]; |
|
colors[cIndex + 1] = colorsMap[cs[offset - cols - 1] + 1]; |
|
colors[cIndex + 2] = colorsMap[cs[offset - cols - 1] + 2]; |
|
colors[cIndex + 3] = colorsMap[cs[offset - cols]]; |
|
colors[cIndex + 4] = colorsMap[cs[offset - cols] + 1]; |
|
colors[cIndex + 5] = colorsMap[cs[offset - cols] + 2]; |
|
colors[cIndex + 6] = colorsMap[cs[offset - 1]]; |
|
colors[cIndex + 7] = colorsMap[cs[offset - 1] + 1]; |
|
colors[cIndex + 8] = colorsMap[cs[offset - 1] + 2]; |
|
coords[pIndex + 6] = coords[pIndex + 2]; |
|
coords[pIndex + 7] = coords[pIndex + 3]; |
|
coords[pIndex + 8] = coords[pIndex + 4]; |
|
coords[pIndex + 9] = coords[pIndex + 5]; |
|
coords[pIndex + 10] = coordsMap[ps[offset]]; |
|
coords[pIndex + 11] = coordsMap[ps[offset] + 1]; |
|
colors[cIndex + 9] = colors[cIndex + 3]; |
|
colors[cIndex + 10] = colors[cIndex + 4]; |
|
colors[cIndex + 11] = colors[cIndex + 5]; |
|
colors[cIndex + 12] = colors[cIndex + 6]; |
|
colors[cIndex + 13] = colors[cIndex + 7]; |
|
colors[cIndex + 14] = colors[cIndex + 8]; |
|
colors[cIndex + 15] = colorsMap[cs[offset]]; |
|
colors[cIndex + 16] = colorsMap[cs[offset] + 1]; |
|
colors[cIndex + 17] = colorsMap[cs[offset] + 2]; |
|
pIndex += 12; |
|
cIndex += 18; |
|
} |
|
} |
|
|
|
break; |
|
|
|
case 'triangles': |
|
for (var j = 0, jj = ps.length; j < jj; j++) { |
|
coords[pIndex] = coordsMap[ps[j]]; |
|
coords[pIndex + 1] = coordsMap[ps[j] + 1]; |
|
colors[cIndex] = colorsMap[cs[j]]; |
|
colors[cIndex + 1] = colorsMap[cs[j] + 1]; |
|
colors[cIndex + 2] = colorsMap[cs[j] + 2]; |
|
pIndex += 2; |
|
cIndex += 3; |
|
} |
|
|
|
break; |
|
} |
|
} |
|
|
|
if (backgroundColor) { |
|
gl.clearColor(backgroundColor[0] / 255, backgroundColor[1] / 255, backgroundColor[2] / 255, 1.0); |
|
} else { |
|
gl.clearColor(0, 0, 0, 0); |
|
} |
|
|
|
gl.clear(gl.COLOR_BUFFER_BIT); |
|
var coordsBuffer = gl.createBuffer(); |
|
gl.bindBuffer(gl.ARRAY_BUFFER, coordsBuffer); |
|
gl.bufferData(gl.ARRAY_BUFFER, coords, gl.STATIC_DRAW); |
|
gl.enableVertexAttribArray(cache.positionLocation); |
|
gl.vertexAttribPointer(cache.positionLocation, 2, gl.FLOAT, false, 0, 0); |
|
var colorsBuffer = gl.createBuffer(); |
|
gl.bindBuffer(gl.ARRAY_BUFFER, colorsBuffer); |
|
gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW); |
|
gl.enableVertexAttribArray(cache.colorLocation); |
|
gl.vertexAttribPointer(cache.colorLocation, 3, gl.UNSIGNED_BYTE, false, 0, 0); |
|
gl.uniform2f(cache.scaleLocation, context.scaleX, context.scaleY); |
|
gl.uniform2f(cache.offsetLocation, context.offsetX, context.offsetY); |
|
gl.drawArrays(gl.TRIANGLES, 0, count); |
|
gl.flush(); |
|
gl.deleteBuffer(coordsBuffer); |
|
gl.deleteBuffer(colorsBuffer); |
|
return canvas; |
|
} |
|
|
|
return { |
|
tryInitGL: function tryInitGL() { |
|
try { |
|
generateGL(); |
|
return !!currentGL; |
|
} catch (ex) {} |
|
|
|
return false; |
|
}, |
|
composeSMask: composeSMask, |
|
drawFigures: drawFigures, |
|
cleanup: function cleanup() { |
|
if (smaskCache && smaskCache.canvas) { |
|
smaskCache.canvas.width = 0; |
|
smaskCache.canvas.height = 0; |
|
} |
|
|
|
if (figuresCache && figuresCache.canvas) { |
|
figuresCache.canvas.width = 0; |
|
figuresCache.canvas.height = 0; |
|
} |
|
|
|
smaskCache = null; |
|
figuresCache = null; |
|
} |
|
}; |
|
}(); |