mirror of
https://github.com/zen-browser/pdf.js.git
synced 2025-07-10 02:05:37 +02:00
Improve performance with image masks (bug 857031)
- it aims to partially fix performance issue reported: https://bugzilla.mozilla.org/show_bug.cgi?id=857031; - the idea is too avoid to use byte arrays but use ImageBitmap which are a way faster to draw: * an ImageBitmap is Transferable which means that it can be built in the worker instead of in the main thread: - this is achieved in using an OffscreenCanvas when it's available, there is a bug to enable them for pdf.js: https://bugzilla.mozilla.org/show_bug.cgi?id=1763330; - or in using createImageBitmap: in Firefox a task is sent to the main thread to build the bitmap so it's slightly slower than using an OffscreenCanvas. * it's transfered from the worker to the main thread by "reference"; * the byte buffers used to create the image data have a very short lifetime and ergo the memory used is globally less than before. - Use the localImageCache for the mask; - Fix the pdf issue4436r.pdf: it was expected to have a binary stream for the image; - Move the singlePixel trick from operator_list to image: this way we can use this trick even if it isn't in a set as defined in operator_list.
This commit is contained in:
parent
2b673a6941
commit
040fcae5ab
11 changed files with 256 additions and 65 deletions
|
@ -31,6 +31,7 @@ import {
|
|||
PathType,
|
||||
TilingPattern,
|
||||
} from "./pattern_helper.js";
|
||||
import { applyMaskImageData } from "../shared/image_utils.js";
|
||||
import { PixelsPerInch } from "./display_utils.js";
|
||||
|
||||
// <canvas> contexts store most of the state we need natively.
|
||||
|
@ -845,6 +846,13 @@ function putBinaryImageData(ctx, imgData, transferMaps = null) {
|
|||
}
|
||||
|
||||
function putBinaryImageMask(ctx, imgData) {
|
||||
if (imgData.bitmap) {
|
||||
// The bitmap has been created in the worker.
|
||||
ctx.drawImage(imgData.bitmap, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Slow path: OffscreenCanvas isn't available in the worker.
|
||||
const height = imgData.height,
|
||||
width = imgData.width;
|
||||
const partialChunkHeight = height % FULL_CHUNK_HEIGHT;
|
||||
|
@ -862,20 +870,15 @@ function putBinaryImageMask(ctx, imgData) {
|
|||
|
||||
// Expand the mask so it can be used by the canvas. Any required
|
||||
// inversion has already been handled.
|
||||
let destPos = 3; // alpha component offset
|
||||
for (let j = 0; j < thisChunkHeight; j++) {
|
||||
let elem,
|
||||
mask = 0;
|
||||
for (let k = 0; k < width; k++) {
|
||||
if (!mask) {
|
||||
elem = src[srcPos++];
|
||||
mask = 128;
|
||||
}
|
||||
dest[destPos] = elem & mask ? 0 : 255;
|
||||
destPos += 4;
|
||||
mask >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
({ srcPos } = applyMaskImageData({
|
||||
src,
|
||||
srcPos,
|
||||
dest,
|
||||
width,
|
||||
height: thisChunkHeight,
|
||||
}));
|
||||
|
||||
ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT);
|
||||
}
|
||||
}
|
||||
|
@ -1120,6 +1123,15 @@ class CanvasGraphics {
|
|||
this._cachedGetSinglePixelWidth = null;
|
||||
}
|
||||
|
||||
getObject(data, fallback = null) {
|
||||
if (typeof data === "string") {
|
||||
return data.startsWith("g_")
|
||||
? this.commonObjs.get(data)
|
||||
: this.objs.get(data);
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
beginDrawing({
|
||||
transform,
|
||||
viewport,
|
||||
|
@ -2754,6 +2766,9 @@ class CanvasGraphics {
|
|||
if (!this.contentVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
img = this.getObject(img.data, img);
|
||||
|
||||
const ctx = this.ctx;
|
||||
const width = img.width,
|
||||
height = img.height;
|
||||
|
@ -2785,7 +2800,7 @@ class CanvasGraphics {
|
|||
}
|
||||
|
||||
paintImageMaskXObjectRepeat(
|
||||
imgData,
|
||||
img,
|
||||
scaleX,
|
||||
skewX = 0,
|
||||
skewY = 0,
|
||||
|
@ -2795,11 +2810,14 @@ class CanvasGraphics {
|
|||
if (!this.contentVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
img = this.getObject(img.data, img);
|
||||
|
||||
const ctx = this.ctx;
|
||||
ctx.save();
|
||||
const currentTransform = ctx.mozCurrentTransform;
|
||||
ctx.transform(scaleX, skewX, skewY, scaleY, 0, 0);
|
||||
const mask = this._createMaskCanvas(imgData);
|
||||
const mask = this._createMaskCanvas(img);
|
||||
|
||||
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||
for (let i = 0, ii = positions.length; i < ii; i += 2) {
|
||||
|
@ -2869,9 +2887,7 @@ class CanvasGraphics {
|
|||
if (!this.contentVisible) {
|
||||
return;
|
||||
}
|
||||
const imgData = objId.startsWith("g_")
|
||||
? this.commonObjs.get(objId)
|
||||
: this.objs.get(objId);
|
||||
const imgData = this.getObject(objId);
|
||||
if (!imgData) {
|
||||
warn("Dependent image isn't ready yet");
|
||||
return;
|
||||
|
@ -2884,9 +2900,7 @@ class CanvasGraphics {
|
|||
if (!this.contentVisible) {
|
||||
return;
|
||||
}
|
||||
const imgData = objId.startsWith("g_")
|
||||
? this.commonObjs.get(objId)
|
||||
: this.objs.get(objId);
|
||||
const imgData = this.getObject(objId);
|
||||
if (!imgData) {
|
||||
warn("Dependent image isn't ready yet");
|
||||
return;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue