[api-minor] Remove the WebGL implementation
Reasons for the removal include: - This functionality was always somewhat experimental and has never been enabled by default, partly because of worries about rendering bugs caused by e.g. bad/outdated graphics drivers. - After the initial implementation, in PR 4286 (back in 2014), no additional functionality has been added to the WebGL implementation. - The vast majority of all documents do not benefit from WebGL rendering, since only a couple of *specific* features are supported (e.g. some Soft Masks and Patterns). - There is, and has always been, *zero* test-coverage for the WebGL implementation. - Overall performance, in the PDF.js library, has improved since the experimental WebGL implementation was added. Rather than shipping unused *and* untested code, it seems reasonable to simply remove the WebGL implementation for now; thanks to version control it's always possible to bring back the code should the need ever arise.
This commit is contained in:
parent
99eac86478
commit
2ba4b65ca8
@ -52,12 +52,6 @@
|
||||
],
|
||||
"default": 0
|
||||
},
|
||||
"enableWebGL": {
|
||||
"title": "Enable WebGL",
|
||||
"description": "Whether to enable WebGL.",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"pdfBugEnabled": {
|
||||
"title": "Enable debugging tools",
|
||||
"description": "Whether to enable debugging tools.",
|
||||
|
@ -57,7 +57,6 @@ import { MessageHandler } from "../shared/message_handler.js";
|
||||
import { Metadata } from "./metadata.js";
|
||||
import { OptionalContentConfig } from "./optional_content_config.js";
|
||||
import { PDFDataTransportStream } from "./transport_stream.js";
|
||||
import { WebGLContext } from "./webgl.js";
|
||||
|
||||
const DEFAULT_RANGE_CHUNK_SIZE = 65536; // 2^16 = 65536
|
||||
const RENDERING_CANCELLED_TIMEOUT = 100; // ms
|
||||
@ -1092,8 +1091,6 @@ class PDFDocumentProxy {
|
||||
* the `PDFPageProxy.getViewport` method.
|
||||
* @property {string} [intent] - Rendering intent, can be 'display' or 'print'.
|
||||
* The default value is 'display'.
|
||||
* @property {boolean} [enableWebGL] - Enables WebGL accelerated rendering for
|
||||
* some operations. The default value is `false`.
|
||||
* @property {boolean} [renderInteractiveForms] - Whether or not interactive
|
||||
* form elements are rendered in the display layer. If so, we do not render
|
||||
* them on the canvas as well. The default value is `false`.
|
||||
@ -1272,7 +1269,6 @@ class PDFPageProxy {
|
||||
canvasContext,
|
||||
viewport,
|
||||
intent = "display",
|
||||
enableWebGL = false,
|
||||
renderInteractiveForms = false,
|
||||
transform = null,
|
||||
imageLayer = null,
|
||||
@ -1319,9 +1315,6 @@ class PDFPageProxy {
|
||||
const canvasFactoryInstance =
|
||||
canvasFactory ||
|
||||
new DefaultCanvasFactory({ ownerDocument: this._ownerDocument });
|
||||
const webGLContext = new WebGLContext({
|
||||
enable: enableWebGL,
|
||||
});
|
||||
const annotationStorage = includeAnnotationStorage
|
||||
? this._transport.annotationStorage.serializable
|
||||
: null;
|
||||
@ -1388,7 +1381,6 @@ class PDFPageProxy {
|
||||
operatorList: intentState.operatorList,
|
||||
pageIndex: this._pageIndex,
|
||||
canvasFactory: canvasFactoryInstance,
|
||||
webGLContext,
|
||||
useRequestAnimationFrame: renderingIntent !== "print",
|
||||
pdfBug: this._pdfBug,
|
||||
});
|
||||
@ -3022,7 +3014,6 @@ const InternalRenderTask = (function InternalRenderTaskClosure() {
|
||||
operatorList,
|
||||
pageIndex,
|
||||
canvasFactory,
|
||||
webGLContext,
|
||||
useRequestAnimationFrame = false,
|
||||
pdfBug = false,
|
||||
}) {
|
||||
@ -3034,7 +3025,6 @@ const InternalRenderTask = (function InternalRenderTaskClosure() {
|
||||
this.operatorList = operatorList;
|
||||
this._pageIndex = pageIndex;
|
||||
this.canvasFactory = canvasFactory;
|
||||
this.webGLContext = webGLContext;
|
||||
this._pdfBug = pdfBug;
|
||||
|
||||
this.running = false;
|
||||
@ -3093,7 +3083,6 @@ const InternalRenderTask = (function InternalRenderTaskClosure() {
|
||||
this.commonObjs,
|
||||
this.objs,
|
||||
this.canvasFactory,
|
||||
this.webGLContext,
|
||||
imageLayer,
|
||||
optionalContentConfig
|
||||
);
|
||||
|
@ -808,7 +808,7 @@ const CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
}
|
||||
}
|
||||
|
||||
function composeSMask(ctx, smask, layerCtx, webGLContext) {
|
||||
function composeSMask(ctx, smask, layerCtx) {
|
||||
const mask = smask.canvas;
|
||||
const maskCtx = smask.context;
|
||||
|
||||
@ -821,27 +821,13 @@ const CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
smask.offsetY
|
||||
);
|
||||
|
||||
const backdrop = smask.backdrop || null;
|
||||
if (!smask.transferMap && webGLContext.isEnabled) {
|
||||
const composed = webGLContext.composeSMask({
|
||||
layer: layerCtx.canvas,
|
||||
mask,
|
||||
properties: {
|
||||
subtype: smask.subtype,
|
||||
backdrop,
|
||||
},
|
||||
});
|
||||
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||
ctx.drawImage(composed, smask.offsetX, smask.offsetY);
|
||||
return;
|
||||
}
|
||||
genericComposeSMask(
|
||||
maskCtx,
|
||||
layerCtx,
|
||||
mask.width,
|
||||
mask.height,
|
||||
smask.subtype,
|
||||
backdrop,
|
||||
smask.backdrop,
|
||||
smask.transferMap
|
||||
);
|
||||
ctx.drawImage(mask, 0, 0);
|
||||
@ -859,7 +845,6 @@ const CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
commonObjs,
|
||||
objs,
|
||||
canvasFactory,
|
||||
webGLContext,
|
||||
imageLayer,
|
||||
optionalContentConfig
|
||||
) {
|
||||
@ -873,7 +858,6 @@ const CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
this.commonObjs = commonObjs;
|
||||
this.objs = objs;
|
||||
this.canvasFactory = canvasFactory;
|
||||
this.webGLContext = webGLContext;
|
||||
this.imageLayer = imageLayer;
|
||||
this.groupStack = [];
|
||||
this.processingType3 = null;
|
||||
@ -1042,7 +1026,6 @@ const CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
}
|
||||
|
||||
this.cachedCanvases.clear();
|
||||
this.webGLContext.clear();
|
||||
|
||||
if (this.imageLayer) {
|
||||
this.imageLayer.endLayout();
|
||||
@ -1192,12 +1175,7 @@ const CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
this.groupLevel--;
|
||||
this.ctx = this.groupStack.pop();
|
||||
|
||||
composeSMask(
|
||||
this.ctx,
|
||||
this.current.activeSMask,
|
||||
groupCtx,
|
||||
this.webGLContext
|
||||
);
|
||||
composeSMask(this.ctx, this.current.activeSMask, groupCtx);
|
||||
this.ctx.restore();
|
||||
this.ctx.save(); // save is needed since SMask will be resumed.
|
||||
copyCtxState(groupCtx, this.ctx);
|
||||
@ -1235,12 +1213,7 @@ const CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
this.groupLevel--;
|
||||
this.ctx = this.groupStack.pop();
|
||||
|
||||
composeSMask(
|
||||
this.ctx,
|
||||
this.current.activeSMask,
|
||||
groupCtx,
|
||||
this.webGLContext
|
||||
);
|
||||
composeSMask(this.ctx, this.current.activeSMask, groupCtx);
|
||||
this.ctx.restore();
|
||||
copyCtxState(groupCtx, this.ctx);
|
||||
// Transform was changed in the SMask canvas, reflecting this change on
|
||||
@ -2004,8 +1977,7 @@ const CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
ctx,
|
||||
this.commonObjs,
|
||||
this.objs,
|
||||
this.canvasFactory,
|
||||
this.webGLContext
|
||||
this.canvasFactory
|
||||
);
|
||||
},
|
||||
};
|
||||
|
@ -233,8 +233,7 @@ const createMeshCanvas = (function createMeshCanvasClosure() {
|
||||
colors,
|
||||
figures,
|
||||
backgroundColor,
|
||||
cachedCanvases,
|
||||
webGLContext
|
||||
cachedCanvases
|
||||
) {
|
||||
// we will increase scale on some weird factor to let antialiasing take
|
||||
// care of "rough" edges
|
||||
@ -273,49 +272,29 @@ const createMeshCanvas = (function createMeshCanvasClosure() {
|
||||
const paddedWidth = width + BORDER_SIZE * 2;
|
||||
const paddedHeight = height + BORDER_SIZE * 2;
|
||||
|
||||
let canvas, tmpCanvas, i, ii;
|
||||
if (webGLContext.isEnabled) {
|
||||
canvas = webGLContext.drawFigures({
|
||||
width,
|
||||
height,
|
||||
backgroundColor,
|
||||
figures,
|
||||
context,
|
||||
});
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=972126
|
||||
tmpCanvas = cachedCanvases.getCanvas(
|
||||
"mesh",
|
||||
paddedWidth,
|
||||
paddedHeight,
|
||||
false
|
||||
);
|
||||
tmpCanvas.context.drawImage(canvas, BORDER_SIZE, BORDER_SIZE);
|
||||
canvas = tmpCanvas.canvas;
|
||||
} else {
|
||||
tmpCanvas = cachedCanvases.getCanvas(
|
||||
"mesh",
|
||||
paddedWidth,
|
||||
paddedHeight,
|
||||
false
|
||||
);
|
||||
const tmpCtx = tmpCanvas.context;
|
||||
const tmpCanvas = cachedCanvases.getCanvas(
|
||||
"mesh",
|
||||
paddedWidth,
|
||||
paddedHeight,
|
||||
false
|
||||
);
|
||||
const tmpCtx = tmpCanvas.context;
|
||||
|
||||
const data = tmpCtx.createImageData(width, height);
|
||||
if (backgroundColor) {
|
||||
const bytes = data.data;
|
||||
for (i = 0, ii = bytes.length; i < ii; i += 4) {
|
||||
bytes[i] = backgroundColor[0];
|
||||
bytes[i + 1] = backgroundColor[1];
|
||||
bytes[i + 2] = backgroundColor[2];
|
||||
bytes[i + 3] = 255;
|
||||
}
|
||||
const data = tmpCtx.createImageData(width, height);
|
||||
if (backgroundColor) {
|
||||
const bytes = data.data;
|
||||
for (let i = 0, ii = bytes.length; i < ii; i += 4) {
|
||||
bytes[i] = backgroundColor[0];
|
||||
bytes[i + 1] = backgroundColor[1];
|
||||
bytes[i + 2] = backgroundColor[2];
|
||||
bytes[i + 3] = 255;
|
||||
}
|
||||
for (i = 0; i < figures.length; i++) {
|
||||
drawFigure(data, figures[i], context);
|
||||
}
|
||||
tmpCtx.putImageData(data, BORDER_SIZE, BORDER_SIZE);
|
||||
canvas = tmpCanvas.canvas;
|
||||
}
|
||||
for (let i = 0, ii = figures.length; i < ii; i++) {
|
||||
drawFigure(data, figures[i], context);
|
||||
}
|
||||
tmpCtx.putImageData(data, BORDER_SIZE, BORDER_SIZE);
|
||||
const canvas = tmpCanvas.canvas;
|
||||
|
||||
return {
|
||||
canvas,
|
||||
@ -362,8 +341,7 @@ ShadingIRs.Mesh = {
|
||||
colors,
|
||||
figures,
|
||||
shadingFill ? null : background,
|
||||
owner.cachedCanvases,
|
||||
owner.webGLContext
|
||||
owner.cachedCanvases
|
||||
);
|
||||
|
||||
if (!shadingFill) {
|
||||
|
@ -1,495 +0,0 @@
|
||||
/* 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.
|
||||
*/
|
||||
/* eslint-disable no-multi-str */
|
||||
|
||||
import { shadow } from "../shared/util.js";
|
||||
|
||||
class WebGLContext {
|
||||
constructor({ enable = false }) {
|
||||
this._enabled = enable === true;
|
||||
}
|
||||
|
||||
get isEnabled() {
|
||||
let enabled = this._enabled;
|
||||
if (enabled) {
|
||||
enabled = WebGLUtils.tryInitGL();
|
||||
}
|
||||
return shadow(this, "isEnabled", enabled);
|
||||
}
|
||||
|
||||
composeSMask({ layer, mask, properties }) {
|
||||
return WebGLUtils.composeSMask(layer, mask, properties);
|
||||
}
|
||||
|
||||
drawFigures({ width, height, backgroundColor, figures, context }) {
|
||||
return WebGLUtils.drawFigures(
|
||||
width,
|
||||
height,
|
||||
backgroundColor,
|
||||
figures,
|
||||
context
|
||||
);
|
||||
}
|
||||
|
||||
clear() {
|
||||
WebGLUtils.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
const WebGLUtils = (function WebGLUtilsClosure() {
|
||||
function loadShader(gl, code, shaderType) {
|
||||
const shader = gl.createShader(shaderType);
|
||||
gl.shaderSource(shader, code);
|
||||
gl.compileShader(shader);
|
||||
const compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
|
||||
if (!compiled) {
|
||||
const 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) {
|
||||
const program = gl.createProgram();
|
||||
for (let i = 0, ii = shaders.length; i < ii; ++i) {
|
||||
gl.attachShader(program, shaders[i]);
|
||||
}
|
||||
gl.linkProgram(program);
|
||||
const linked = gl.getProgramParameter(program, gl.LINK_STATUS);
|
||||
if (!linked) {
|
||||
const errorMsg = gl.getProgramInfoLog(program);
|
||||
throw new Error("Error during program linking: " + errorMsg);
|
||||
}
|
||||
return program;
|
||||
}
|
||||
function createTexture(gl, image, textureId) {
|
||||
gl.activeTexture(textureId);
|
||||
const 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;
|
||||
}
|
||||
|
||||
let currentGL, currentCanvas;
|
||||
function generateGL() {
|
||||
if (currentGL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The temporary canvas is used in the WebGL context.
|
||||
currentCanvas = document.createElement("canvas");
|
||||
currentGL = currentCanvas.getContext("webgl", {
|
||||
premultipliedalpha: false,
|
||||
});
|
||||
}
|
||||
|
||||
const 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; \
|
||||
} ";
|
||||
|
||||
const 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; \
|
||||
} ";
|
||||
|
||||
let smaskCache = null;
|
||||
|
||||
function initSmaskGL() {
|
||||
generateGL();
|
||||
const canvas = currentCanvas;
|
||||
currentCanvas = null;
|
||||
const gl = currentGL;
|
||||
currentGL = null;
|
||||
|
||||
// setup a GLSL program
|
||||
const vertexShader = createVertexShader(gl, smaskVertexShaderCode);
|
||||
const fragmentShader = createFragmentShader(gl, smaskFragmentShaderCode);
|
||||
const program = createProgram(gl, [vertexShader, fragmentShader]);
|
||||
gl.useProgram(program);
|
||||
|
||||
const 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");
|
||||
|
||||
const texCoordLocation = gl.getAttribLocation(program, "a_texCoord");
|
||||
const texLayerLocation = gl.getUniformLocation(program, "u_image");
|
||||
const texMaskLocation = gl.getUniformLocation(program, "u_mask");
|
||||
|
||||
// provide texture coordinates for the rectangle.
|
||||
const texCoordBuffer = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
|
||||
// prettier-ignore
|
||||
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) {
|
||||
const width = layer.width,
|
||||
height = layer.height;
|
||||
|
||||
if (!smaskCache) {
|
||||
initSmaskGL();
|
||||
}
|
||||
const 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
|
||||
const texture = createTexture(gl, layer, gl.TEXTURE0);
|
||||
const maskTexture = createTexture(gl, mask, gl.TEXTURE1);
|
||||
|
||||
// Create a buffer and put a single clipspace rectangle in
|
||||
// it (2 triangles)
|
||||
const buffer = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
||||
// prettier-ignore
|
||||
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;
|
||||
}
|
||||
|
||||
const 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); \
|
||||
} ";
|
||||
|
||||
const figuresFragmentShaderCode =
|
||||
"\
|
||||
precision mediump float; \
|
||||
\
|
||||
varying vec4 v_color; \
|
||||
\
|
||||
void main() { \
|
||||
gl_FragColor = v_color; \
|
||||
} ";
|
||||
|
||||
let figuresCache = null;
|
||||
|
||||
function initFiguresGL() {
|
||||
generateGL();
|
||||
const canvas = currentCanvas;
|
||||
currentCanvas = null;
|
||||
const gl = currentGL;
|
||||
currentGL = null;
|
||||
|
||||
// setup a GLSL program
|
||||
const vertexShader = createVertexShader(gl, figuresVertexShaderCode);
|
||||
const fragmentShader = createFragmentShader(gl, figuresFragmentShaderCode);
|
||||
const program = createProgram(gl, [vertexShader, fragmentShader]);
|
||||
gl.useProgram(program);
|
||||
|
||||
const 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();
|
||||
}
|
||||
const 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
|
||||
let count = 0;
|
||||
for (let i = 0, ii = figures.length; i < ii; i++) {
|
||||
switch (figures[i].type) {
|
||||
case "lattice":
|
||||
const 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
|
||||
const coords = new Float32Array(count * 2);
|
||||
const colors = new Uint8Array(count * 3);
|
||||
const coordsMap = context.coords,
|
||||
colorsMap = context.colors;
|
||||
let pIndex = 0,
|
||||
cIndex = 0;
|
||||
for (let i = 0, ii = figures.length; i < ii; i++) {
|
||||
const figure = figures[i],
|
||||
ps = figure.coords,
|
||||
cs = figure.colors;
|
||||
switch (figure.type) {
|
||||
case "lattice":
|
||||
const cols = figure.verticesPerRow;
|
||||
const rows = (ps.length / cols) | 0;
|
||||
for (let row = 1; row < rows; row++) {
|
||||
let offset = row * cols + 1;
|
||||
for (let 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 (let 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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
const 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);
|
||||
|
||||
const 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() {
|
||||
try {
|
||||
generateGL();
|
||||
return !!currentGL;
|
||||
} catch (ex) {}
|
||||
return false;
|
||||
},
|
||||
|
||||
composeSMask,
|
||||
|
||||
drawFigures,
|
||||
|
||||
cleanup() {
|
||||
if (smaskCache?.canvas) {
|
||||
smaskCache.canvas.width = 0;
|
||||
smaskCache.canvas.height = 0;
|
||||
}
|
||||
if (figuresCache?.canvas) {
|
||||
figuresCache.canvas.width = 0;
|
||||
figuresCache.canvas.height = 0;
|
||||
}
|
||||
smaskCache = null;
|
||||
figuresCache = null;
|
||||
},
|
||||
};
|
||||
})();
|
||||
|
||||
export { WebGLContext };
|
@ -354,9 +354,6 @@ const PDFViewerApplication = {
|
||||
if ("disablehistory" in hashParams) {
|
||||
AppOptions.set("disableHistory", hashParams.disablehistory === "true");
|
||||
}
|
||||
if ("webgl" in hashParams) {
|
||||
AppOptions.set("enableWebGL", hashParams.webgl === "true");
|
||||
}
|
||||
if ("verbosity" in hashParams) {
|
||||
AppOptions.set("verbosity", hashParams.verbosity | 0);
|
||||
}
|
||||
@ -511,7 +508,6 @@ const PDFViewerApplication = {
|
||||
findController,
|
||||
scriptingManager: pdfScriptingManager,
|
||||
renderer: AppOptions.get("renderer"),
|
||||
enableWebGL: AppOptions.get("enableWebGL"),
|
||||
l10n: this.l10n,
|
||||
textLayerMode: AppOptions.get("textLayerMode"),
|
||||
imageResourcesPath: AppOptions.get("imageResourcesPath"),
|
||||
@ -1520,8 +1516,7 @@ const PDFViewerApplication = {
|
||||
console.log(
|
||||
`PDF ${pdfDocument.fingerprint} [${info.PDFFormatVersion} ` +
|
||||
`${(info.Producer || "-").trim()} / ${(info.Creator || "-").trim()}] ` +
|
||||
`(PDF.js: ${version || "-"}` +
|
||||
`${this.pdfViewer.enableWebGL ? " [WebGL]" : ""})`
|
||||
`(PDF.js: ${version || "-"})`
|
||||
);
|
||||
|
||||
let pdfTitle = info?.Title;
|
||||
|
@ -70,11 +70,6 @@ const defaultOptions = {
|
||||
value: true,
|
||||
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
|
||||
},
|
||||
enableWebGL: {
|
||||
/** @type {boolean} */
|
||||
value: false,
|
||||
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
|
||||
},
|
||||
externalLinkRel: {
|
||||
/** @type {string} */
|
||||
value: "noopener noreferrer nofollow",
|
||||
|
@ -73,8 +73,6 @@ const DEFAULT_CACHE_SIZE = 10;
|
||||
* @property {boolean} [enablePrintAutoRotate] - Enables automatic rotation of
|
||||
* landscape pages upon printing. The default is `false`.
|
||||
* @property {string} renderer - 'canvas' or 'svg'. The default is 'canvas'.
|
||||
* @property {boolean} [enableWebGL] - Enables WebGL accelerated rendering for
|
||||
* some operations. The default value is `false`.
|
||||
* @property {boolean} [useOnlyCssZoom] - Enables CSS only zooming. The default
|
||||
* value is `false`.
|
||||
* @property {number} [maxCanvasPixels] - The maximum supported canvas size in
|
||||
@ -194,7 +192,6 @@ class BaseViewer {
|
||||
this.renderInteractiveForms = options.renderInteractiveForms !== false;
|
||||
this.enablePrintAutoRotate = options.enablePrintAutoRotate || false;
|
||||
this.renderer = options.renderer || RendererType.CANVAS;
|
||||
this.enableWebGL = options.enableWebGL || false;
|
||||
this.useOnlyCssZoom = options.useOnlyCssZoom || false;
|
||||
this.maxCanvasPixels = options.maxCanvasPixels;
|
||||
this.l10n = options.l10n || NullL10n;
|
||||
@ -550,7 +547,6 @@ class BaseViewer {
|
||||
imageResourcesPath: this.imageResourcesPath,
|
||||
renderInteractiveForms: this.renderInteractiveForms,
|
||||
renderer: this.renderer,
|
||||
enableWebGL: this.enableWebGL,
|
||||
useOnlyCssZoom: this.useOnlyCssZoom,
|
||||
maxCanvasPixels: this.maxCanvasPixels,
|
||||
l10n: this.l10n,
|
||||
|
@ -55,8 +55,6 @@ import { viewerCompatibilityParams } from "./viewer_compatibility.js";
|
||||
* @property {boolean} renderInteractiveForms - Turns on rendering of
|
||||
* interactive form elements. The default value is `true`.
|
||||
* @property {string} renderer - 'canvas' or 'svg'. The default is 'canvas'.
|
||||
* @property {boolean} [enableWebGL] - Enables WebGL accelerated rendering for
|
||||
* some operations. The default value is `false`.
|
||||
* @property {boolean} [useOnlyCssZoom] - Enables CSS only zooming. The default
|
||||
* value is `false`.
|
||||
* @property {number} [maxCanvasPixels] - The maximum supported canvas size in
|
||||
@ -105,7 +103,6 @@ class PDFPageView {
|
||||
this.xfaLayerFactory = options.xfaLayerFactory;
|
||||
this.structTreeLayerFactory = options.structTreeLayerFactory;
|
||||
this.renderer = options.renderer || RendererType.CANVAS;
|
||||
this.enableWebGL = options.enableWebGL || false;
|
||||
this.l10n = options.l10n || NullL10n;
|
||||
|
||||
this.paintTask = null;
|
||||
@ -728,7 +725,6 @@ class PDFPageView {
|
||||
canvasContext: ctx,
|
||||
transform,
|
||||
viewport: this.viewport,
|
||||
enableWebGL: this.enableWebGL,
|
||||
renderInteractiveForms: this.renderInteractiveForms,
|
||||
optionalContentConfigPromise: this._optionalContentConfigPromise,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user