videos, infographics. Some kind of slideshare clone, to summarize. Featuring : - channel management: classify slides (presentations, videos, documents, ...) and tune their access rights for visibility, uploading and downloading - full branding new shiny website module ! - automatic fetch of youtube and google docs information based on URL - statistics: views, likes, interfaced with messaging, ... - embed mechanism to embed slides in other websites Courtesy of RD India, notably Mantavya Gajjar, Ravi Gadhia, Vijay Patel, Pariket Trivedi Courtesy of Use Merge Team, notably Jerome Maes and Richard Mathot Let us not forget our usability testing teams that tested it quite a lot of time. Special thanks to Martin Trigaux that did nothing.
438 lines
17 KiB
JavaScript
438 lines
17 KiB
JavaScript
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
|
/* Copyright 2014 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.
|
|
*/
|
|
/* globals PDFJS, shadow */
|
|
/* jshint -W043 */
|
|
|
|
'use strict';
|
|
|
|
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);
|
|
|
|
// Set the parameters so we can render any size image.
|
|
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);
|
|
|
|
// Upload the image into the texture.
|
|
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;
|
|
|
|
// setup a GLSL program
|
|
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');
|
|
|
|
// provide texture coordinates for the rectangle.
|
|
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);
|
|
|
|
// Create a textures
|
|
var texture = createTexture(gl, layer, gl.TEXTURE0);
|
|
var maskTexture = createTexture(gl, mask, gl.TEXTURE1);
|
|
|
|
|
|
// Create a buffer and put a single clipspace rectangle in
|
|
// it (2 triangles)
|
|
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);
|
|
|
|
// draw
|
|
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;
|
|
|
|
// setup a GLSL program
|
|
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);
|
|
|
|
// count triangle points
|
|
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;
|
|
}
|
|
}
|
|
// transfer data
|
|
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[i]];
|
|
colors[cIndex + 1] = colorsMap[cs[j] + 1];
|
|
colors[cIndex + 2] = colorsMap[cs[j] + 2];
|
|
pIndex += 2;
|
|
cIndex += 3;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// draw
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
return {
|
|
get isEnabled() {
|
|
if (PDFJS.disableWebGL) {
|
|
return false;
|
|
}
|
|
var enabled = false;
|
|
try {
|
|
generateGL();
|
|
enabled = !!currentGL;
|
|
} catch (e) { }
|
|
return shadow(this, 'isEnabled', enabled);
|
|
},
|
|
composeSMask: composeSMask,
|
|
drawFigures: drawFigures,
|
|
clear: cleanup
|
|
};
|
|
})();
|