14 changed files with 604 additions and 81 deletions
@ -0,0 +1,428 @@
@@ -0,0 +1,428 @@
|
||||
/* -*- 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 generageGL() { |
||||
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; |
||||
|
||||
generageGL(); |
||||
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; |
||||
|
||||
generageGL(); |
||||
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; |
||||
for (var i = 0, ii = figures.length; i < ii; i++) { |
||||
switch (figures[i].type) { |
||||
case 'lattice': |
||||
var 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 (var 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; |
||||
var 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() { |
||||
smaskCache = null; |
||||
figuresCache = null; |
||||
} |
||||
|
||||
return { |
||||
get isEnabled() { |
||||
if (PDFJS.disableWebGL) { |
||||
return false; |
||||
} |
||||
var enabled = false; |
||||
try { |
||||
generageGL(); |
||||
enabled = !!currentGL; |
||||
} catch (e) { } |
||||
return shadow(this, 'isEnabled', enabled); |
||||
}, |
||||
composeSMask: composeSMask, |
||||
drawFigures: drawFigures, |
||||
clear: cleanup |
||||
}; |
||||
})(); |
Loading…
Reference in new issue