Skip to content

Commit c24487e

Browse files
Add logic to track rendering area of various PDF ops
This commit is a first step towards #6419, and it can also help with first compute which ops can affect what is visible in that part of the page. This commit adds logic to track operations with their respective bounding boxes. Only operations that actually cause something to be rendered have a bounding box and dependencies. Consider the following example: ``` 0. setFillRGBColor 1. beginText 2. showText "Hello" 3. endText 4. constructPath [...] -> eoFill ``` here we have three rendering operations: the showText op (2) and the path (4). (2) depends on (0), (1) and (3), while (4) only depends on (0). Both (2) and (4) have a bounding box. This tracking happens when first rendering a PDF: we then use the recorded information to optimize future partial renderings of a PDF, so that we can skip operations that do not affected the PDF area on the canvas. All this logic only runs when the new `enableOptimizedPartialRendering` preference, disabled by default, is enabled. The bounding boxes and dependencies are also shown in the pdfBug stepper. When hovering over a step now: - it highlights the steps that they depend on - it highlights on the PDF itself the bounding box
1 parent e20ee99 commit c24487e

16 files changed

+2553
-212
lines changed

src/display/api.js

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ import {
6060
NodeStandardFontDataFactory,
6161
NodeWasmFactory,
6262
} from "display-node_utils";
63+
import { CanvasDependencyTracker } from "./canvas_dependency_tracker.js";
6364
import { CanvasGraphics } from "./canvas.js";
6465
import { DOMCanvasFactory } from "./canvas_factory.js";
6566
import { DOMCMapReaderFactory } from "display-cmap_reader_factory";
@@ -1239,6 +1240,10 @@ class PDFDocumentProxy {
12391240
* annotation ids with canvases used to render them.
12401241
* @property {PrintAnnotationStorage} [printAnnotationStorage]
12411242
* @property {boolean} [isEditing] - Render the page in editing mode.
1243+
* @property {boolean} [recordOperations] - Record the dependencies and bounding
1244+
* boxes of all PDF operations that render onto the canvas.
1245+
* @property {Set<number>} [filteredOperationIndexes] - If provided, only run
1246+
* the PDF operations that are included in this set.
12421247
*/
12431248

12441249
/**
@@ -1309,6 +1314,7 @@ class PDFPageProxy {
13091314

13101315
this._intentStates = new Map();
13111316
this.destroyed = false;
1317+
this.recordedGroups = null;
13121318
}
13131319

13141320
/**
@@ -1433,6 +1439,8 @@ class PDFPageProxy {
14331439
pageColors = null,
14341440
printAnnotationStorage = null,
14351441
isEditing = false,
1442+
recordOperations = false,
1443+
filteredOperationIndexes = null,
14361444
}) {
14371445
this._stats?.time("Overall");
14381446

@@ -1479,9 +1487,26 @@ class PDFPageProxy {
14791487
this._pumpOperatorList(intentArgs);
14801488
}
14811489

1490+
const shouldRecordOperations =
1491+
!this.recordedGroups &&
1492+
(recordOperations ||
1493+
(this._pdfBug && globalThis.StepperManager?.enabled));
1494+
14821495
const complete = error => {
14831496
intentState.renderTasks.delete(internalRenderTask);
14841497

1498+
if (shouldRecordOperations) {
1499+
const recordedGroups = internalRenderTask.gfx?.dependencyTracker.take();
1500+
if (recordedGroups) {
1501+
internalRenderTask.stepper?.setOperatorGroups(recordedGroups);
1502+
if (recordOperations) {
1503+
this.recordedGroups = recordedGroups;
1504+
}
1505+
} else if (recordOperations) {
1506+
this.recordedGroups = [];
1507+
}
1508+
}
1509+
14851510
// Attempt to reduce memory usage during *printing*, by always running
14861511
// cleanup immediately once rendering has finished.
14871512
if (intentPrint) {
@@ -1516,6 +1541,9 @@ class PDFPageProxy {
15161541
params: {
15171542
canvas,
15181543
canvasContext,
1544+
dependencyTracker: shouldRecordOperations
1545+
? new CanvasDependencyTracker(canvas)
1546+
: null,
15191547
viewport,
15201548
transform,
15211549
background,
@@ -1531,6 +1559,7 @@ class PDFPageProxy {
15311559
pdfBug: this._pdfBug,
15321560
pageColors,
15331561
enableHWA: this._transport.enableHWA,
1562+
filteredOperationIndexes,
15341563
});
15351564

15361565
(intentState.renderTasks ||= new Set()).add(internalRenderTask);
@@ -3140,6 +3169,7 @@ class InternalRenderTask {
31403169
pdfBug = false,
31413170
pageColors = null,
31423171
enableHWA = false,
3172+
filteredOperationIndexes = null,
31433173
}) {
31443174
this.callback = callback;
31453175
this.params = params;
@@ -3170,6 +3200,8 @@ class InternalRenderTask {
31703200
this._canvas = params.canvas;
31713201
this._canvasContext = params.canvas ? null : params.canvasContext;
31723202
this._enableHWA = enableHWA;
3203+
this._dependencyTracker = params.dependencyTracker;
3204+
this._filteredOperationIndexes = filteredOperationIndexes;
31733205
}
31743206

31753207
get completed() {
@@ -3199,7 +3231,7 @@ class InternalRenderTask {
31993231
this.stepper.init(this.operatorList);
32003232
this.stepper.nextBreakPoint = this.stepper.getNextBreakPoint();
32013233
}
3202-
const { viewport, transform, background } = this.params;
3234+
const { viewport, transform, background, dependencyTracker } = this.params;
32033235

32043236
// When printing in Firefox, we get a specific context in mozPrintCallback
32053237
// which cannot be created from the canvas itself.
@@ -3218,7 +3250,8 @@ class InternalRenderTask {
32183250
this.filterFactory,
32193251
{ optionalContentConfig },
32203252
this.annotationCanvasMap,
3221-
this.pageColors
3253+
this.pageColors,
3254+
dependencyTracker
32223255
);
32233256
this.gfx.beginDrawing({
32243257
transform,
@@ -3294,7 +3327,8 @@ class InternalRenderTask {
32943327
this.operatorList,
32953328
this.operatorListIdx,
32963329
this._continueBound,
3297-
this.stepper
3330+
this.stepper,
3331+
this._filteredOperationIndexes
32983332
);
32993333
if (this.operatorListIdx === this.operatorList.argsArray.length) {
33003334
this.running = false;

0 commit comments

Comments
 (0)