mirror of
https://github.com/mozilla/pdf.js.git
synced 2026-04-09 23:04:02 +02:00
Merge pull request #20966 from calixteman/rm_canvas_cache
Remove the canvases cache
This commit is contained in:
commit
ae84c662b8
@ -204,36 +204,6 @@ function mirrorContextOperations(ctx, destCtx) {
|
||||
};
|
||||
}
|
||||
|
||||
class CachedCanvases {
|
||||
#cache = new Map();
|
||||
|
||||
constructor(canvasFactory) {
|
||||
this.canvasFactory = canvasFactory;
|
||||
}
|
||||
|
||||
getCanvas(id, width, height) {
|
||||
let canvasEntry = this.#cache.get(id);
|
||||
if (canvasEntry) {
|
||||
this.canvasFactory.reset(canvasEntry, width, height);
|
||||
} else {
|
||||
canvasEntry = this.canvasFactory.create(width, height);
|
||||
this.#cache.set(id, canvasEntry);
|
||||
}
|
||||
return canvasEntry;
|
||||
}
|
||||
|
||||
delete(id) {
|
||||
this.#cache.delete(id);
|
||||
}
|
||||
|
||||
clear() {
|
||||
for (const canvasEntry of this.#cache.values()) {
|
||||
this.canvasFactory.destroy(canvasEntry);
|
||||
}
|
||||
this.#cache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
function drawImageAtIntegerCoords(
|
||||
ctx,
|
||||
srcImg,
|
||||
@ -681,11 +651,11 @@ class CanvasGraphics {
|
||||
this.smaskStack = [];
|
||||
this.smaskCounter = 0;
|
||||
this.tempSMask = null;
|
||||
this.smaskGroupCanvases = [];
|
||||
this.suspendedCtx = null;
|
||||
this.contentVisible = true;
|
||||
this.markedContentStack = markedContentStack || [];
|
||||
this.optionalContentConfig = optionalContentConfig;
|
||||
this.cachedCanvases = new CachedCanvases(this.canvasFactory);
|
||||
this.cachedPatterns = new Map();
|
||||
this.annotationCanvasMap = annotationCanvasMap;
|
||||
this.viewportScale = 1;
|
||||
@ -731,14 +701,11 @@ class CanvasGraphics {
|
||||
this.ctx.fillStyle = savedFillStyle;
|
||||
|
||||
if (transparency) {
|
||||
const transparentCanvas = this.cachedCanvases.getCanvas(
|
||||
"transparent",
|
||||
width,
|
||||
height
|
||||
);
|
||||
const transparentCanvas = (this.transparentCanvasEntry =
|
||||
this.canvasFactory.create(width, height));
|
||||
this.compositeCtx = this.ctx;
|
||||
this.transparentCanvas = transparentCanvas.canvas;
|
||||
this.ctx = transparentCanvas.context;
|
||||
({ canvas: this.transparentCanvas, context: this.ctx } =
|
||||
transparentCanvas);
|
||||
this.ctx.save();
|
||||
// The transform can be applied before rendering, transferring it to
|
||||
// the new canvas.
|
||||
@ -862,14 +829,26 @@ class CanvasGraphics {
|
||||
this.ctx.setTransform(1, 0, 0, 1, 0, 0); // Avoid apply transform twice
|
||||
this.ctx.drawImage(this.transparentCanvas, 0, 0);
|
||||
this.ctx.restore();
|
||||
this.canvasFactory.destroy(this.transparentCanvasEntry);
|
||||
this.transparentCanvas = null;
|
||||
this.transparentCanvasEntry = null;
|
||||
}
|
||||
}
|
||||
|
||||
endDrawing() {
|
||||
this.#restoreInitialState();
|
||||
|
||||
this.cachedCanvases.clear();
|
||||
// Destroy all smask group canvases now that rendering is complete.
|
||||
// These cannot be destroyed eagerly because activeSMask is part of
|
||||
// CanvasExtraState and is shared (via Object.create prototype chain) across
|
||||
// save/restore state copies.
|
||||
for (const canvas of this.smaskGroupCanvases) {
|
||||
this.canvasFactory.destroy(canvas);
|
||||
}
|
||||
this.smaskGroupCanvases.length = 0;
|
||||
this.tempSMask = null;
|
||||
this.smaskStack.length = 0;
|
||||
|
||||
this.cachedPatterns.clear();
|
||||
|
||||
for (const cache of this._cachedBitmapsMap.values()) {
|
||||
@ -910,52 +889,80 @@ class CanvasGraphics {
|
||||
// displayWidth and displayHeight are used for VideoFrame.
|
||||
const width = img.width ?? img.displayWidth;
|
||||
const height = img.height ?? img.displayHeight;
|
||||
let widthScale = Math.max(
|
||||
const widthScale = Math.max(
|
||||
Math.hypot(inverseTransform[0], inverseTransform[1]),
|
||||
1
|
||||
);
|
||||
let heightScale = Math.max(
|
||||
const heightScale = Math.max(
|
||||
Math.hypot(inverseTransform[2], inverseTransform[3]),
|
||||
1
|
||||
);
|
||||
|
||||
let paintWidth = width,
|
||||
paintHeight = height;
|
||||
let tmpCanvasId = "prescale1";
|
||||
let tmpCanvas, tmpCtx;
|
||||
while (
|
||||
(widthScale > 2 && paintWidth > 1) ||
|
||||
(heightScale > 2 && paintHeight > 1)
|
||||
) {
|
||||
let newWidth = paintWidth,
|
||||
newHeight = paintHeight;
|
||||
if (widthScale > 2 && paintWidth > 1) {
|
||||
// Pre-compute each step's output dimensions.
|
||||
const scaleSteps = [];
|
||||
let ws = widthScale,
|
||||
hs = heightScale,
|
||||
pw = width,
|
||||
ph = height;
|
||||
while ((ws > 2 && pw > 1) || (hs > 2 && ph > 1)) {
|
||||
let nw = pw,
|
||||
nh = ph;
|
||||
if (ws > 2 && pw > 1) {
|
||||
// See bug 1820511 (Windows specific bug).
|
||||
// TODO: once the above bug is fixed we could revert to:
|
||||
// newWidth = Math.ceil(paintWidth / 2);
|
||||
newWidth =
|
||||
paintWidth >= 16384
|
||||
? Math.floor(paintWidth / 2) - 1 || 1
|
||||
: Math.ceil(paintWidth / 2);
|
||||
widthScale /= paintWidth / newWidth;
|
||||
// nw = Math.ceil(pw / 2);
|
||||
nw = pw >= 16384 ? Math.floor(pw / 2) - 1 || 1 : Math.ceil(pw / 2);
|
||||
ws /= pw / nw;
|
||||
}
|
||||
if (heightScale > 2 && paintHeight > 1) {
|
||||
if (hs > 2 && ph > 1) {
|
||||
// TODO: see the comment above.
|
||||
newHeight =
|
||||
paintHeight >= 16384
|
||||
? Math.floor(paintHeight / 2) - 1 || 1
|
||||
: Math.ceil(paintHeight) / 2;
|
||||
heightScale /= paintHeight / newHeight;
|
||||
nh = ph >= 16384 ? Math.floor(ph / 2) - 1 || 1 : Math.ceil(ph) / 2;
|
||||
hs /= ph / nh;
|
||||
}
|
||||
tmpCanvas = this.cachedCanvases.getCanvas(
|
||||
tmpCanvasId,
|
||||
scaleSteps.push({ newWidth: nw, newHeight: nh });
|
||||
pw = nw;
|
||||
ph = nh;
|
||||
}
|
||||
|
||||
if (scaleSteps.length === 0) {
|
||||
return { img, paintWidth: width, paintHeight: height, tmpCanvas: null };
|
||||
}
|
||||
|
||||
if (scaleSteps.length === 1) {
|
||||
const { newWidth, newHeight } = scaleSteps[0];
|
||||
const tmpCanvas = this.canvasFactory.create(newWidth, newHeight);
|
||||
tmpCanvas.context.drawImage(
|
||||
img,
|
||||
0,
|
||||
0,
|
||||
width,
|
||||
height,
|
||||
0,
|
||||
0,
|
||||
newWidth,
|
||||
newHeight
|
||||
);
|
||||
tmpCtx = tmpCanvas.context;
|
||||
tmpCtx.clearRect(0, 0, newWidth, newHeight);
|
||||
tmpCtx.drawImage(
|
||||
img,
|
||||
return {
|
||||
img: tmpCanvas.canvas,
|
||||
paintWidth: newWidth,
|
||||
paintHeight: newHeight,
|
||||
tmpCanvas,
|
||||
};
|
||||
}
|
||||
|
||||
// More than 2 steps: ping-pong between two reused canvas entries.
|
||||
// canvasFactory.reset() resizes (and implicitly clears) a canvas without
|
||||
// creating a new JS object or calling getContext() again.
|
||||
let readEntry = this.canvasFactory.create(1, 1);
|
||||
let writeEntry = this.canvasFactory.create(1, 1);
|
||||
let paintWidth = width,
|
||||
paintHeight = height;
|
||||
let source = img;
|
||||
|
||||
for (const { newWidth, newHeight } of scaleSteps) {
|
||||
this.canvasFactory.reset(writeEntry, newWidth, newHeight);
|
||||
writeEntry.context.drawImage(
|
||||
source,
|
||||
0,
|
||||
0,
|
||||
paintWidth,
|
||||
@ -965,15 +972,19 @@ class CanvasGraphics {
|
||||
newWidth,
|
||||
newHeight
|
||||
);
|
||||
img = tmpCanvas.canvas;
|
||||
[readEntry, writeEntry] = [writeEntry, readEntry];
|
||||
source = readEntry.canvas;
|
||||
paintWidth = newWidth;
|
||||
paintHeight = newHeight;
|
||||
tmpCanvasId = tmpCanvasId === "prescale1" ? "prescale2" : "prescale1";
|
||||
}
|
||||
|
||||
// writeEntry is now the stale buffer — destroy it.
|
||||
this.canvasFactory.destroy(writeEntry);
|
||||
return {
|
||||
img,
|
||||
img: readEntry.canvas,
|
||||
paintWidth,
|
||||
paintHeight,
|
||||
tmpCanvas: readEntry,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1025,7 +1036,7 @@ class CanvasGraphics {
|
||||
}
|
||||
|
||||
if (!scaled) {
|
||||
maskCanvas = this.cachedCanvases.getCanvas("maskCanvas", width, height);
|
||||
maskCanvas = this.canvasFactory.create(width, height);
|
||||
putBinaryImageMask(maskCanvas.context, img);
|
||||
}
|
||||
|
||||
@ -1048,11 +1059,7 @@ class CanvasGraphics {
|
||||
const [minX, minY, maxX, maxY] = minMax;
|
||||
const drawnWidth = Math.round(maxX - minX) || 1;
|
||||
const drawnHeight = Math.round(maxY - minY) || 1;
|
||||
const fillCanvas = this.cachedCanvases.getCanvas(
|
||||
"fillCanvas",
|
||||
drawnWidth,
|
||||
drawnHeight
|
||||
);
|
||||
const fillCanvas = this.canvasFactory.create(drawnWidth, drawnHeight);
|
||||
const fillCtx = fillCanvas.context;
|
||||
|
||||
// The offset will be the top-left coordinate mask.
|
||||
@ -1064,15 +1071,24 @@ class CanvasGraphics {
|
||||
fillCtx.translate(-offsetX, -offsetY);
|
||||
fillCtx.transform(...maskToCanvas);
|
||||
|
||||
let scaledEntry = null;
|
||||
if (!scaled) {
|
||||
// Pre-scale if needed to improve image smoothing.
|
||||
scaled = this._scaleImage(
|
||||
const scaleResult = this._scaleImage(
|
||||
maskCanvas.canvas,
|
||||
getCurrentTransformInverse(fillCtx)
|
||||
);
|
||||
scaled = scaled.img;
|
||||
scaled = scaleResult.img;
|
||||
scaledEntry = scaleResult.tmpCanvas;
|
||||
if (scaled !== maskCanvas.canvas) {
|
||||
// _scaleImage created a new canvas; maskCanvas is no longer needed.
|
||||
this.canvasFactory.destroy(maskCanvas);
|
||||
maskCanvas = null;
|
||||
}
|
||||
if (cache && isPatternFill) {
|
||||
cache.set(cacheKey, scaled);
|
||||
scaledEntry = null; // bitmap cache owns the canvas now
|
||||
maskCanvas = null; // bitmap cache may own maskCanvas.canvas (= scaled)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1093,6 +1109,13 @@ class CanvasGraphics {
|
||||
width,
|
||||
height
|
||||
);
|
||||
if (scaledEntry) {
|
||||
this.canvasFactory.destroy(scaledEntry);
|
||||
}
|
||||
if (maskCanvas) {
|
||||
// scaled === maskCanvas.canvas and not owned by the bitmap cache.
|
||||
this.canvasFactory.destroy(maskCanvas);
|
||||
}
|
||||
fillCtx.globalCompositeOperation = "source-in";
|
||||
|
||||
const inverse = Util.transform(getCurrentTransformInverse(fillCtx), [
|
||||
@ -1111,8 +1134,7 @@ class CanvasGraphics {
|
||||
|
||||
if (cache && !isPatternFill) {
|
||||
// The fill canvas is put in the cache associated to the mask image
|
||||
// so we must remove from the cached canvas: it mustn't be used again.
|
||||
this.cachedCanvases.delete("fillCanvas");
|
||||
// so it mustn't be used again.
|
||||
cache.set(cacheKey, fillCanvas.canvas);
|
||||
}
|
||||
|
||||
@ -1124,6 +1146,8 @@ class CanvasGraphics {
|
||||
// Round the offsets to avoid drawing fractional pixels.
|
||||
return {
|
||||
canvas: fillCanvas.canvas,
|
||||
// canvasEntry is null when the bitmap cache owns the canvas.
|
||||
canvasEntry: cache && !isPatternFill ? null : fillCanvas,
|
||||
offsetX: Math.round(offsetX),
|
||||
offsetY: Math.round(offsetY),
|
||||
};
|
||||
@ -1257,12 +1281,8 @@ class CanvasGraphics {
|
||||
}
|
||||
const drawnWidth = this.ctx.canvas.width;
|
||||
const drawnHeight = this.ctx.canvas.height;
|
||||
const cacheId = "smaskGroupAt" + this.groupLevel;
|
||||
const scratchCanvas = this.cachedCanvases.getCanvas(
|
||||
cacheId,
|
||||
drawnWidth,
|
||||
drawnHeight
|
||||
);
|
||||
const scratchCanvas = this.canvasFactory.create(drawnWidth, drawnHeight);
|
||||
this.smaskScratchCanvas = scratchCanvas;
|
||||
this.suspendedCtx = this.ctx;
|
||||
const ctx = (this.ctx = scratchCanvas.context);
|
||||
ctx.setTransform(this.suspendedCtx.getTransform());
|
||||
@ -1283,6 +1303,8 @@ class CanvasGraphics {
|
||||
this.ctx = this.suspendedCtx;
|
||||
|
||||
this.suspendedCtx = null;
|
||||
this.canvasFactory.destroy(this.smaskScratchCanvas);
|
||||
this.smaskScratchCanvas = null;
|
||||
}
|
||||
|
||||
compose(dirtyBox) {
|
||||
@ -1356,6 +1378,7 @@ class CanvasGraphics {
|
||||
let maskX = layerOffsetX - maskOffsetX;
|
||||
let maskY = layerOffsetY - maskOffsetY;
|
||||
|
||||
let maskExtensionEntry = null;
|
||||
if (backdrop) {
|
||||
if (
|
||||
maskX < 0 ||
|
||||
@ -1363,19 +1386,15 @@ class CanvasGraphics {
|
||||
maskX + width > maskCanvas.width ||
|
||||
maskY + height > maskCanvas.height
|
||||
) {
|
||||
const canvas = this.cachedCanvases.getCanvas(
|
||||
"maskExtension",
|
||||
width,
|
||||
height
|
||||
);
|
||||
const ctx = canvas.context;
|
||||
maskExtensionEntry = this.canvasFactory.create(width, height);
|
||||
const ctx = maskExtensionEntry.context;
|
||||
ctx.drawImage(maskCanvas, -maskX, -maskY);
|
||||
ctx.globalCompositeOperation = "destination-atop";
|
||||
ctx.fillStyle = backdrop;
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
ctx.globalCompositeOperation = "source-over";
|
||||
|
||||
maskCanvas = canvas.canvas;
|
||||
maskCanvas = maskExtensionEntry.canvas;
|
||||
maskX = maskY = 0;
|
||||
} else {
|
||||
maskCtx.save();
|
||||
@ -1417,6 +1436,9 @@ class CanvasGraphics {
|
||||
height
|
||||
);
|
||||
layerCtx.restore();
|
||||
if (maskExtensionEntry) {
|
||||
this.canvasFactory.destroy(maskExtensionEntry);
|
||||
}
|
||||
}
|
||||
|
||||
save(opIdx) {
|
||||
@ -1992,14 +2014,12 @@ class CanvasGraphics {
|
||||
get isFontSubpixelAAEnabled() {
|
||||
// Checks if anti-aliasing is enabled when scaled text is painted.
|
||||
// On Windows GDI scaled fonts looks bad.
|
||||
const { context: ctx } = this.cachedCanvases.getCanvas(
|
||||
"isFontSubpixelAAEnabled",
|
||||
10,
|
||||
10
|
||||
);
|
||||
const tmpCanvas = this.canvasFactory.create(10, 10);
|
||||
const ctx = tmpCanvas.context;
|
||||
ctx.scale(1.5, 1);
|
||||
ctx.fillText("I", 0, 10);
|
||||
const data = ctx.getImageData(0, 0, 10, 10).data;
|
||||
this.canvasFactory.destroy(tmpCanvas);
|
||||
let enabled = false;
|
||||
for (let i = 3; i < data.length; i += 4) {
|
||||
if (data[i] > 0 && data[i] < 255) {
|
||||
@ -2610,16 +2630,13 @@ class CanvasGraphics {
|
||||
|
||||
this.current.startNewPathAndClipBox([0, 0, drawnWidth, drawnHeight]);
|
||||
|
||||
let cacheId = "groupAt" + this.groupLevel;
|
||||
if (group.smask) {
|
||||
// Using two cache entries is case if masks are used one after another.
|
||||
cacheId += "_smask_" + (this.smaskCounter++ % 2);
|
||||
this.smaskCounter++;
|
||||
}
|
||||
const scratchCanvas = this.canvasFactory.create(drawnWidth, drawnHeight);
|
||||
if (group.smask) {
|
||||
this.smaskGroupCanvases.push(scratchCanvas);
|
||||
}
|
||||
const scratchCanvas = this.cachedCanvases.getCanvas(
|
||||
cacheId,
|
||||
drawnWidth,
|
||||
drawnHeight
|
||||
);
|
||||
const groupCtx = scratchCanvas.context;
|
||||
|
||||
// Since we created a new canvas that is just the size of the bounding box
|
||||
@ -2721,6 +2738,10 @@ class CanvasGraphics {
|
||||
);
|
||||
this.ctx.drawImage(groupCtx.canvas, 0, 0);
|
||||
this.ctx.restore();
|
||||
this.canvasFactory.destroy({
|
||||
canvas: groupCtx.canvas,
|
||||
context: groupCtx,
|
||||
});
|
||||
this.compose(dirtyBox);
|
||||
}
|
||||
}
|
||||
@ -2837,6 +2858,9 @@ class CanvasGraphics {
|
||||
)
|
||||
.recordOperation(opIdx);
|
||||
ctx.restore();
|
||||
if (mask.canvasEntry) {
|
||||
this.canvasFactory.destroy(mask.canvasEntry);
|
||||
}
|
||||
this.compose();
|
||||
}
|
||||
|
||||
@ -2893,6 +2917,9 @@ class CanvasGraphics {
|
||||
);
|
||||
}
|
||||
ctx.restore();
|
||||
if (mask.canvasEntry) {
|
||||
this.canvasFactory.destroy(mask.canvasEntry);
|
||||
}
|
||||
this.compose();
|
||||
|
||||
this.dependencyTracker?.recordOperation(opIdx);
|
||||
@ -2914,11 +2941,7 @@ class CanvasGraphics {
|
||||
for (const image of images) {
|
||||
const { data, width, height, transform } = image;
|
||||
|
||||
const maskCanvas = this.cachedCanvases.getCanvas(
|
||||
"maskCanvas",
|
||||
width,
|
||||
height
|
||||
);
|
||||
const maskCanvas = this.canvasFactory.create(width, height);
|
||||
const maskCtx = maskCanvas.context;
|
||||
maskCtx.save();
|
||||
|
||||
@ -2955,6 +2978,7 @@ class CanvasGraphics {
|
||||
1,
|
||||
1
|
||||
);
|
||||
this.canvasFactory.destroy(maskCanvas);
|
||||
|
||||
this.dependencyTracker?.recordBBox(opIdx, ctx, 0, width, 0, height);
|
||||
ctx.restore();
|
||||
@ -3012,20 +3036,16 @@ class CanvasGraphics {
|
||||
|
||||
applyTransferMapsToBitmap(imgData) {
|
||||
if (this.current.transferMaps === "none") {
|
||||
return imgData.bitmap;
|
||||
return { img: imgData.bitmap, canvasEntry: null };
|
||||
}
|
||||
const { bitmap, width, height } = imgData;
|
||||
const tmpCanvas = this.cachedCanvases.getCanvas(
|
||||
"inlineImage",
|
||||
width,
|
||||
height
|
||||
);
|
||||
const tmpCanvas = this.canvasFactory.create(width, height);
|
||||
const tmpCtx = tmpCanvas.context;
|
||||
tmpCtx.filter = this.current.transferMaps;
|
||||
tmpCtx.drawImage(bitmap, 0, 0);
|
||||
tmpCtx.filter = "none";
|
||||
|
||||
return tmpCanvas.canvas;
|
||||
return { img: tmpCanvas.canvas, canvasEntry: tmpCanvas };
|
||||
}
|
||||
|
||||
paintInlineImageXObject(opIdx, imgData) {
|
||||
@ -3051,8 +3071,11 @@ class CanvasGraphics {
|
||||
ctx.scale(1 / width, -1 / height);
|
||||
|
||||
let imgToPaint;
|
||||
let inlineImgCanvas = null;
|
||||
if (imgData.bitmap) {
|
||||
imgToPaint = this.applyTransferMapsToBitmap(imgData);
|
||||
const result = this.applyTransferMapsToBitmap(imgData);
|
||||
imgToPaint = result.img;
|
||||
inlineImgCanvas = result.canvasEntry;
|
||||
} else if (
|
||||
(typeof HTMLElement === "function" && imgData instanceof HTMLElement) ||
|
||||
!imgData.data
|
||||
@ -3060,14 +3083,10 @@ class CanvasGraphics {
|
||||
// typeof check is needed due to node.js support, see issue #8489
|
||||
imgToPaint = imgData;
|
||||
} else {
|
||||
const tmpCanvas = this.cachedCanvases.getCanvas(
|
||||
"inlineImage",
|
||||
width,
|
||||
height
|
||||
);
|
||||
const tmpCtx = tmpCanvas.context;
|
||||
putBinaryImageData(tmpCtx, imgData);
|
||||
imgToPaint = this.applyTransferMapsToCanvas(tmpCtx);
|
||||
const tmpCanvas = this.canvasFactory.create(width, height);
|
||||
putBinaryImageData(tmpCanvas.context, imgData);
|
||||
imgToPaint = this.applyTransferMapsToCanvas(tmpCanvas.context);
|
||||
inlineImgCanvas = tmpCanvas;
|
||||
}
|
||||
|
||||
const scaled = this._scaleImage(
|
||||
@ -3105,6 +3124,12 @@ class CanvasGraphics {
|
||||
width,
|
||||
height
|
||||
);
|
||||
if (scaled.tmpCanvas) {
|
||||
this.canvasFactory.destroy(scaled.tmpCanvas);
|
||||
}
|
||||
if (inlineImgCanvas) {
|
||||
this.canvasFactory.destroy(inlineImgCanvas);
|
||||
}
|
||||
this.compose();
|
||||
this.restore(opIdx);
|
||||
}
|
||||
@ -3115,16 +3140,17 @@ class CanvasGraphics {
|
||||
}
|
||||
const ctx = this.ctx;
|
||||
let imgToPaint;
|
||||
let inlineImgCanvas = null;
|
||||
if (imgData.bitmap) {
|
||||
imgToPaint = imgData.bitmap;
|
||||
} else {
|
||||
const w = imgData.width;
|
||||
const h = imgData.height;
|
||||
|
||||
const tmpCanvas = this.cachedCanvases.getCanvas("inlineImage", w, h);
|
||||
const tmpCtx = tmpCanvas.context;
|
||||
putBinaryImageData(tmpCtx, imgData);
|
||||
imgToPaint = this.applyTransferMapsToCanvas(tmpCtx);
|
||||
const tmpCanvas = this.canvasFactory.create(w, h);
|
||||
putBinaryImageData(tmpCanvas.context, imgData);
|
||||
imgToPaint = this.applyTransferMapsToCanvas(tmpCanvas.context);
|
||||
inlineImgCanvas = tmpCanvas;
|
||||
}
|
||||
|
||||
this.dependencyTracker?.resetBBox(opIdx);
|
||||
@ -3148,6 +3174,9 @@ class CanvasGraphics {
|
||||
this.dependencyTracker?.recordBBox(opIdx, ctx, 0, 1, -1, 0);
|
||||
ctx.restore();
|
||||
}
|
||||
if (inlineImgCanvas) {
|
||||
this.canvasFactory.destroy(inlineImgCanvas);
|
||||
}
|
||||
this.dependencyTracker?.recordOperation(opIdx);
|
||||
this.compose();
|
||||
}
|
||||
|
||||
@ -41,25 +41,25 @@ class BaseCanvasFactory {
|
||||
};
|
||||
}
|
||||
|
||||
reset(canvasAndContext, width, height) {
|
||||
if (!canvasAndContext.canvas) {
|
||||
reset({ canvas }, width, height) {
|
||||
if (!canvas) {
|
||||
throw new Error("Canvas is not specified");
|
||||
}
|
||||
if (width <= 0 || height <= 0) {
|
||||
throw new Error("Invalid canvas size");
|
||||
}
|
||||
canvasAndContext.canvas.width = width;
|
||||
canvasAndContext.canvas.height = height;
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
}
|
||||
|
||||
destroy(canvasAndContext) {
|
||||
if (!canvasAndContext.canvas) {
|
||||
const { canvas } = canvasAndContext;
|
||||
if (!canvas) {
|
||||
throw new Error("Canvas is not specified");
|
||||
}
|
||||
// Zeroing the width and height cause Firefox to release graphics
|
||||
// resources immediately, which can greatly reduce memory consumption.
|
||||
canvasAndContext.canvas.width = 0;
|
||||
canvasAndContext.canvas.height = 0;
|
||||
canvas.width = canvas.height = 0;
|
||||
canvasAndContext.canvas = null;
|
||||
canvasAndContext.context = null;
|
||||
}
|
||||
|
||||
@ -217,11 +217,7 @@ class RadialAxialShadingPattern extends BaseShadingPattern {
|
||||
const width = Math.ceil(ownerBBox[2] - ownerBBox[0]) || 1;
|
||||
const height = Math.ceil(ownerBBox[3] - ownerBBox[1]) || 1;
|
||||
|
||||
const tmpCanvas = owner.cachedCanvases.getCanvas(
|
||||
"pattern",
|
||||
width,
|
||||
height
|
||||
);
|
||||
const tmpCanvas = owner.canvasFactory.create(width, height);
|
||||
|
||||
const tmpCtx = tmpCanvas.context;
|
||||
tmpCtx.clearRect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height);
|
||||
@ -254,6 +250,7 @@ class RadialAxialShadingPattern extends BaseShadingPattern {
|
||||
tmpCtx.fill();
|
||||
|
||||
pattern = ctx.createPattern(tmpCanvas.canvas, "no-repeat");
|
||||
owner.canvasFactory.destroy(tmpCanvas);
|
||||
const domMatrix = new DOMMatrix(inverse);
|
||||
pattern.setTransform(domMatrix);
|
||||
} else {
|
||||
@ -448,7 +445,7 @@ class MeshShadingPattern extends BaseShadingPattern {
|
||||
this.matrix = null;
|
||||
}
|
||||
|
||||
_createMeshCanvas(combinedScale, backgroundColor, cachedCanvases) {
|
||||
_createMeshCanvas(combinedScale, backgroundColor, canvasFactory) {
|
||||
// we will increase scale on some weird factor to let antialiasing take
|
||||
// care of "rough" edges
|
||||
const EXPECTED_SCALE = 1.1;
|
||||
@ -489,12 +486,7 @@ class MeshShadingPattern extends BaseShadingPattern {
|
||||
|
||||
const paddedWidth = width + BORDER_SIZE * 2;
|
||||
const paddedHeight = height + BORDER_SIZE * 2;
|
||||
|
||||
const tmpCanvas = cachedCanvases.getCanvas(
|
||||
"mesh",
|
||||
paddedWidth,
|
||||
paddedHeight
|
||||
);
|
||||
const tmpCanvas = canvasFactory.create(paddedWidth, paddedHeight);
|
||||
|
||||
if (isWebGPUMeshReady()) {
|
||||
tmpCanvas.context.drawImage(
|
||||
@ -560,7 +552,7 @@ class MeshShadingPattern extends BaseShadingPattern {
|
||||
const temporaryPatternCanvas = this._createMeshCanvas(
|
||||
scale,
|
||||
pathType === PathType.SHADING ? null : this._background,
|
||||
owner.cachedCanvases
|
||||
owner.canvasFactory
|
||||
);
|
||||
|
||||
if (pathType !== PathType.SHADING) {
|
||||
@ -576,7 +568,12 @@ class MeshShadingPattern extends BaseShadingPattern {
|
||||
);
|
||||
ctx.scale(temporaryPatternCanvas.scaleX, temporaryPatternCanvas.scaleY);
|
||||
|
||||
return ctx.createPattern(temporaryPatternCanvas.canvas, "no-repeat");
|
||||
const pattern = ctx.createPattern(
|
||||
temporaryPatternCanvas.canvas,
|
||||
"no-repeat"
|
||||
);
|
||||
owner.canvasFactory.destroy(temporaryPatternCanvas);
|
||||
return pattern;
|
||||
}
|
||||
}
|
||||
|
||||
@ -704,11 +701,7 @@ class TilingPattern {
|
||||
combinedScaleY
|
||||
);
|
||||
|
||||
const tmpCanvas = owner.cachedCanvases.getCanvas(
|
||||
"pattern",
|
||||
dimx.size,
|
||||
dimy.size
|
||||
);
|
||||
const tmpCanvas = owner.canvasFactory.create(dimx.size, dimy.size);
|
||||
const tmpCtx = tmpCanvas.context;
|
||||
const graphics = canvasGraphicsFactory.createCanvasGraphics(tmpCtx, opIdx);
|
||||
graphics.groupLevel = owner.groupLevel;
|
||||
@ -775,11 +768,7 @@ class TilingPattern {
|
||||
|
||||
const xSize = dimx2.size;
|
||||
const ySize = dimy2.size;
|
||||
const tmpCanvas2 = owner.cachedCanvases.getCanvas(
|
||||
"pattern-workaround",
|
||||
xSize,
|
||||
ySize
|
||||
);
|
||||
const tmpCanvas2 = owner.canvasFactory.create(xSize, ySize);
|
||||
const tmpCtx2 = tmpCanvas2.context;
|
||||
const ii = redrawHorizontally ? Math.floor(width / xstep) : 0;
|
||||
const jj = redrawVertically ? Math.floor(height / ystep) : 0;
|
||||
@ -800,8 +789,10 @@ class TilingPattern {
|
||||
);
|
||||
}
|
||||
}
|
||||
owner.canvasFactory.destroy(tmpCanvas);
|
||||
return {
|
||||
canvas: tmpCanvas2.canvas,
|
||||
canvasEntry: tmpCanvas2,
|
||||
scaleX: dimx2.scale,
|
||||
scaleY: dimy2.scale,
|
||||
offsetX: x0,
|
||||
@ -811,6 +802,7 @@ class TilingPattern {
|
||||
|
||||
return {
|
||||
canvas: tmpCanvas.canvas,
|
||||
canvasEntry: tmpCanvas,
|
||||
scaleX: dimx.scale,
|
||||
scaleY: dimy.scale,
|
||||
offsetX: x0,
|
||||
@ -894,6 +886,7 @@ class TilingPattern {
|
||||
);
|
||||
|
||||
const pattern = ctx.createPattern(temporaryPatternCanvas.canvas, "repeat");
|
||||
owner.canvasFactory.destroy(temporaryPatternCanvas.canvasEntry);
|
||||
pattern.setTransform(domMatrix);
|
||||
|
||||
return pattern;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user