Skip to content

Commit e4ea2e0

Browse files
Store ops bboxes in a linear Uint8Array
This PR changes the way we store bounding boxes so that they use less memory and can be more easily shared across threads in the future. Instead of storing the bounding box and list of dependencies for each operation that renders _something_, we now only store the bounding box of _every_ operation and no dependencies list. The bounding box of each operation covers the bounding box of all the operations affected by it that render something. For example, the bounding box of a `setFont` operation will be the bounding box of all the `showText` operations that use that font. This affects the debugging experience in pdfBug, since now the bounding box of an operation may be larger than what it renders itself. To help with this, now when hovering on an operation we also highlight (in red) all its dependents. We highlight with white stripes operations that do not affect any part of the page (i.e. with an empty bbox). To save memory, we now save bounding box x/y coordinates as uint8 rather than float64. This effectively gives us a 256x256 uniform grid that covers the page, which is high enough resolution for the usecase.
1 parent e37a58f commit e4ea2e0

10 files changed

Lines changed: 477 additions & 274 deletions

src/display/api.js

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1242,8 +1242,8 @@ class PDFDocumentProxy {
12421242
* @property {boolean} [isEditing] - Render the page in editing mode.
12431243
* @property {boolean} [recordOperations] - Record the dependencies and bounding
12441244
* 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.
1245+
* @property {(index: number) => boolean} [operationsFilter] - If provided, only
1246+
* run for which this function returns `true`.
12471247
*/
12481248

12491249
/**
@@ -1314,7 +1314,7 @@ class PDFPageProxy {
13141314

13151315
this._intentStates = new Map();
13161316
this.destroyed = false;
1317-
this.recordedGroups = null;
1317+
this.recordedBBoxes = null;
13181318
}
13191319

13201320
/**
@@ -1440,7 +1440,7 @@ class PDFPageProxy {
14401440
printAnnotationStorage = null,
14411441
isEditing = false,
14421442
recordOperations = false,
1443-
filteredOperationIndexes = null,
1443+
operationsFilter = null,
14441444
}) {
14451445
this._stats?.time("Overall");
14461446

@@ -1487,23 +1487,28 @@ class PDFPageProxy {
14871487
this._pumpOperatorList(intentArgs);
14881488
}
14891489

1490+
const recordForDebugger = Boolean(
1491+
this._pdfBug && globalThis.StepperManager?.enabled
1492+
);
1493+
14901494
const shouldRecordOperations =
1491-
!this.recordedGroups &&
1492-
(recordOperations ||
1493-
(this._pdfBug && globalThis.StepperManager?.enabled));
1495+
!this.recordedBBoxes && (recordOperations || recordForDebugger);
14941496

14951497
const complete = error => {
14961498
intentState.renderTasks.delete(internalRenderTask);
14971499

14981500
if (shouldRecordOperations) {
1499-
const recordedGroups = internalRenderTask.gfx?.dependencyTracker.take();
1500-
if (recordedGroups) {
1501-
internalRenderTask.stepper?.setOperatorGroups(recordedGroups);
1501+
const recordedBBoxes = internalRenderTask.gfx?.dependencyTracker.take();
1502+
if (recordedBBoxes) {
1503+
if (internalRenderTask.stepper) {
1504+
internalRenderTask.stepper.setOperatorBBoxes(
1505+
recordedBBoxes,
1506+
internalRenderTask.gfx.dependencyTracker.takeDebugMetadata()
1507+
);
1508+
}
15021509
if (recordOperations) {
1503-
this.recordedGroups = recordedGroups;
1510+
this.recordedBBoxes = recordedBBoxes;
15041511
}
1505-
} else if (recordOperations) {
1506-
this.recordedGroups = [];
15071512
}
15081513
}
15091514

@@ -1542,7 +1547,11 @@ class PDFPageProxy {
15421547
canvas,
15431548
canvasContext,
15441549
dependencyTracker: shouldRecordOperations
1545-
? new CanvasDependencyTracker(canvas)
1550+
? new CanvasDependencyTracker(
1551+
canvas,
1552+
intentState.operatorList.length,
1553+
recordForDebugger
1554+
)
15461555
: null,
15471556
viewport,
15481557
transform,
@@ -1559,7 +1568,7 @@ class PDFPageProxy {
15591568
pdfBug: this._pdfBug,
15601569
pageColors,
15611570
enableHWA: this._transport.enableHWA,
1562-
filteredOperationIndexes,
1571+
operationsFilter,
15631572
});
15641573

15651574
(intentState.renderTasks ||= new Set()).add(internalRenderTask);
@@ -3169,7 +3178,7 @@ class InternalRenderTask {
31693178
pdfBug = false,
31703179
pageColors = null,
31713180
enableHWA = false,
3172-
filteredOperationIndexes = null,
3181+
operationsFilter = null,
31733182
}) {
31743183
this.callback = callback;
31753184
this.params = params;
@@ -3201,7 +3210,7 @@ class InternalRenderTask {
32013210
this._canvasContext = params.canvas ? null : params.canvasContext;
32023211
this._enableHWA = enableHWA;
32033212
this._dependencyTracker = params.dependencyTracker;
3204-
this._filteredOperationIndexes = filteredOperationIndexes;
3213+
this._operationsFilter = operationsFilter;
32053214
}
32063215

32073216
get completed() {
@@ -3288,6 +3297,9 @@ class InternalRenderTask {
32883297
this.graphicsReadyCallback ||= this._continueBound;
32893298
return;
32903299
}
3300+
this.gfx.dependencyTracker?.growOperationsCount(
3301+
this.operatorList.fnArray.length
3302+
);
32913303
this.stepper?.updateOperatorList(this.operatorList);
32923304

32933305
if (this.running) {
@@ -3328,7 +3340,7 @@ class InternalRenderTask {
33283340
this.operatorListIdx,
33293341
this._continueBound,
33303342
this.stepper,
3331-
this._filteredOperationIndexes
3343+
this._operationsFilter
33323344
);
33333345
if (this.operatorListIdx === this.operatorList.argsArray.length) {
33343346
this.running = false;

src/display/canvas.js

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -763,7 +763,7 @@ class CanvasGraphics {
763763
executionStartIdx,
764764
continueCallback,
765765
stepper,
766-
filteredOperationIndexes
766+
operationsFilter
767767
) {
768768
const argsArray = operatorList.argsArray;
769769
const fnArray = operatorList.fnArray;
@@ -791,7 +791,7 @@ class CanvasGraphics {
791791
return i;
792792
}
793793

794-
if (!filteredOperationIndexes || filteredOperationIndexes.has(i)) {
794+
if (!operationsFilter || operationsFilter(i)) {
795795
fnId = fnArray[i];
796796
// TODO: There is a `undefined` coming from somewhere.
797797
fnArgs = argsArray[i] ?? null;
@@ -1100,7 +1100,7 @@ class CanvasGraphics {
11001100
-offsetY,
11011101
]);
11021102
fillCtx.fillStyle = isPatternFill
1103-
? fillColor.getPattern(ctx, this, inverse, PathType.FILL)
1103+
? fillColor.getPattern(ctx, this, inverse, PathType.FILL, opIdx)
11041104
: fillColor;
11051105

11061106
fillCtx.fillRect(0, 0, width, height);
@@ -1549,7 +1549,8 @@ class CanvasGraphics {
15491549
ctx,
15501550
this,
15511551
getCurrentTransformInverse(ctx),
1552-
PathType.STROKE
1552+
PathType.STROKE,
1553+
opIdx
15531554
);
15541555
if (baseTransform) {
15551556
const newPath = new Path2D();
@@ -1603,7 +1604,8 @@ class CanvasGraphics {
16031604
ctx,
16041605
this,
16051606
getCurrentTransformInverse(ctx),
1606-
PathType.FILL
1607+
PathType.FILL,
1608+
opIdx
16071609
);
16081610
if (baseTransform) {
16091611
const newPath = new Path2D();
@@ -1759,7 +1761,7 @@ class CanvasGraphics {
17591761
setFont(opIdx, fontRefName, size) {
17601762
this.dependencyTracker
17611763
?.recordSimpleData("font", opIdx)
1762-
.recordNamedDependency(opIdx, fontRefName);
1764+
.recordSimpleDataFromNamed("fontObj", fontRefName, opIdx);
17631765
const fontObj = this.commonObjs.get(fontRefName);
17641766
const current = this.current;
17651767

@@ -2034,7 +2036,6 @@ class CanvasGraphics {
20342036
if (this.dependencyTracker) {
20352037
this.dependencyTracker
20362038
.recordDependencies(opIdx, Dependencies.showText)
2037-
.copyDependenciesFromIncrementalOperation(opIdx, "sameLineText")
20382039
.resetBBox(opIdx);
20392040
if (this.current.textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG) {
20402041
this.dependencyTracker
@@ -2047,9 +2048,7 @@ class CanvasGraphics {
20472048
const font = current.font;
20482049
if (font.isType3Font) {
20492050
this.showType3Text(opIdx, glyphs);
2050-
this.dependencyTracker
2051-
?.recordOperation(opIdx)
2052-
.recordIncrementalData("sameLineText", opIdx);
2051+
this.dependencyTracker?.recordShowTextOperation(opIdx);
20532052
return undefined;
20542053
}
20552054

@@ -2095,7 +2094,8 @@ class CanvasGraphics {
20952094
ctx,
20962095
this,
20972096
getCurrentTransformInverse(ctx),
2098-
PathType.FILL
2097+
PathType.FILL,
2098+
opIdx
20992099
);
21002100
patternFillTransform = getCurrentTransform(ctx);
21012101
ctx.restore();
@@ -2108,7 +2108,8 @@ class CanvasGraphics {
21082108
ctx,
21092109
this,
21102110
getCurrentTransformInverse(ctx),
2111-
PathType.STROKE
2111+
PathType.STROKE,
2112+
opIdx
21122113
);
21132114
patternStrokeTransform = getCurrentTransform(ctx);
21142115
ctx.restore();
@@ -2157,8 +2158,7 @@ class CanvasGraphics {
21572158
-measure.actualBoundingBoxAscent,
21582159
measure.actualBoundingBoxDescent
21592160
)
2160-
.recordOperation(opIdx)
2161-
.recordIncrementalData("sameLineText", opIdx);
2161+
.recordShowTextOperation(opIdx);
21622162
}
21632163
current.x += width * widthAdvanceScale * textHScale;
21642164
ctx.restore();
@@ -2277,9 +2277,7 @@ class CanvasGraphics {
22772277
ctx.restore();
22782278
this.compose();
22792279

2280-
this.dependencyTracker
2281-
?.recordOperation(opIdx)
2282-
.recordIncrementalData("sameLineText", opIdx);
2280+
this.dependencyTracker?.recordShowTextOperation(opIdx);
22832281
return undefined;
22842282
}
22852283

@@ -2351,7 +2349,6 @@ class CanvasGraphics {
23512349
}
23522350
ctx.restore();
23532351
if (dependencyTracker) {
2354-
this.dependencyTracker.recordNestedDependencies();
23552352
this.dependencyTracker = dependencyTracker;
23562353
}
23572354
}
@@ -2378,7 +2375,7 @@ class CanvasGraphics {
23782375
if (IR[0] === "TilingPattern") {
23792376
const baseTransform = this.baseTransform || getCurrentTransform(this.ctx);
23802377
const canvasGraphicsFactory = {
2381-
createCanvasGraphics: ctx =>
2378+
createCanvasGraphics: (ctx, renderingOpIdx) =>
23822379
new CanvasGraphics(
23832380
ctx,
23842381
this.commonObjs,
@@ -2392,7 +2389,11 @@ class CanvasGraphics {
23922389
undefined,
23932390
undefined,
23942391
this.dependencyTracker
2395-
? new CanvasNestedDependencyTracker(this.dependencyTracker, opIdx)
2392+
? new CanvasNestedDependencyTracker(
2393+
this.dependencyTracker,
2394+
renderingOpIdx,
2395+
/* ignoreBBoxes */ true
2396+
)
23962397
: null
23972398
),
23982399
};
@@ -2470,7 +2471,8 @@ class CanvasGraphics {
24702471
ctx,
24712472
this,
24722473
getCurrentTransformInverse(ctx),
2473-
PathType.SHADING
2474+
PathType.SHADING,
2475+
opIdx
24742476
);
24752477

24762478
const inv = getCurrentTransformInverse(ctx);
@@ -2937,7 +2939,8 @@ class CanvasGraphics {
29372939
maskCtx,
29382940
this,
29392941
getCurrentTransformInverse(ctx),
2940-
PathType.FILL
2942+
PathType.FILL,
2943+
opIdx
29412944
)
29422945
: fillColor;
29432946
maskCtx.fillRect(0, 0, width, height);

0 commit comments

Comments
 (0)