Merge pull request #16448 from calixteman/improve_rescaleandstroke

Improve performance of canvas::rescaleAndStroke by around 30% (bug 1135277)
This commit is contained in:
calixteman 2023-05-20 21:06:40 +02:00 committed by GitHub
commit 65e23432cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -986,7 +986,7 @@ class CanvasGraphics {
this.outputScaleY = 1; this.outputScaleY = 1;
this.pageColors = pageColors; this.pageColors = pageColors;
this._cachedScaleForStroking = null; this._cachedScaleForStroking = [-1, 0];
this._cachedGetSinglePixelWidth = null; this._cachedGetSinglePixelWidth = null;
this._cachedBitmapsMap = new Map(); this._cachedBitmapsMap = new Map();
} }
@ -1395,7 +1395,7 @@ class CanvasGraphics {
// Graphics state // Graphics state
setLineWidth(width) { setLineWidth(width) {
if (width !== this.current.lineWidth) { if (width !== this.current.lineWidth) {
this._cachedScaleForStroking = null; this._cachedScaleForStroking[0] = -1;
} }
this.current.lineWidth = width; this.current.lineWidth = width;
this.ctx.lineWidth = width; this.ctx.lineWidth = width;
@ -1602,7 +1602,7 @@ class CanvasGraphics {
// Ensure that the clipping path is reset (fixes issue6413.pdf). // Ensure that the clipping path is reset (fixes issue6413.pdf).
this.pendingClip = null; this.pendingClip = null;
this._cachedScaleForStroking = null; this._cachedScaleForStroking[0] = -1;
this._cachedGetSinglePixelWidth = null; this._cachedGetSinglePixelWidth = null;
} }
} }
@ -1610,7 +1610,7 @@ class CanvasGraphics {
transform(a, b, c, d, e, f) { transform(a, b, c, d, e, f) {
this.ctx.transform(a, b, c, d, e, f); this.ctx.transform(a, b, c, d, e, f);
this._cachedScaleForStroking = null; this._cachedScaleForStroking[0] = -1;
this._cachedGetSinglePixelWidth = null; this._cachedGetSinglePixelWidth = null;
} }
@ -2297,7 +2297,7 @@ class CanvasGraphics {
if (isTextInvisible || fontSize === 0) { if (isTextInvisible || fontSize === 0) {
return; return;
} }
this._cachedScaleForStroking = null; this._cachedScaleForStroking[0] = -1;
this._cachedGetSinglePixelWidth = null; this._cachedGetSinglePixelWidth = null;
ctx.save(); ctx.save();
@ -3160,16 +3160,23 @@ class CanvasGraphics {
// The goal of this function is to rescale before setting the // The goal of this function is to rescale before setting the
// lineWidth in order to have both thicknesses greater or equal // lineWidth in order to have both thicknesses greater or equal
// to 1 after transform. // to 1 after transform.
if (!this._cachedScaleForStroking) { if (this._cachedScaleForStroking[0] === -1) {
const { lineWidth } = this.current; const { lineWidth } = this.current;
const m = getCurrentTransform(this.ctx); const { a, b, c, d } = this.ctx.getTransform();
let scaleX, scaleY; let scaleX, scaleY;
if (m[1] === 0 && m[2] === 0) { if (b === 0 && c === 0) {
// Fast path // Fast path
const normX = Math.abs(m[0]); const normX = Math.abs(a);
const normY = Math.abs(m[3]); const normY = Math.abs(d);
if (lineWidth === 0) { if (normX === normY) {
if (lineWidth === 0) {
scaleX = scaleY = 1 / normX;
} else {
const scaledLineWidth = normX * lineWidth;
scaleX = scaleY = scaledLineWidth < 1 ? 1 / scaledLineWidth : 1;
}
} else if (lineWidth === 0) {
scaleX = 1 / normX; scaleX = 1 / normX;
scaleY = 1 / normY; scaleY = 1 / normY;
} else { } else {
@ -3185,9 +3192,9 @@ class CanvasGraphics {
// - heightX (orthogonal to My) has a length: |det(M)| / norm(My). // - heightX (orthogonal to My) has a length: |det(M)| / norm(My).
// heightX and heightY are the thicknesses of the transformed pixel // heightX and heightY are the thicknesses of the transformed pixel
// and they must be both greater or equal to 1. // and they must be both greater or equal to 1.
const absDet = Math.abs(m[0] * m[3] - m[2] * m[1]); const absDet = Math.abs(a * d - b * c);
const normX = Math.hypot(m[0], m[1]); const normX = Math.hypot(a, b);
const normY = Math.hypot(m[2], m[3]); const normY = Math.hypot(c, d);
if (lineWidth === 0) { if (lineWidth === 0) {
scaleX = normY / absDet; scaleX = normY / absDet;
scaleY = normX / absDet; scaleY = normX / absDet;
@ -3197,7 +3204,8 @@ class CanvasGraphics {
scaleY = normX > baseArea ? normX / baseArea : 1; scaleY = normX > baseArea ? normX / baseArea : 1;
} }
} }
this._cachedScaleForStroking = [scaleX, scaleY]; this._cachedScaleForStroking[0] = scaleX;
this._cachedScaleForStroking[1] = scaleY;
} }
return this._cachedScaleForStroking; return this._cachedScaleForStroking;
} }
@ -3216,11 +3224,9 @@ class CanvasGraphics {
return; return;
} }
let savedMatrix, savedDashes, savedDashOffset; const dashes = ctx.getLineDash();
if (saveRestore) { if (saveRestore) {
savedMatrix = getCurrentTransform(ctx); ctx.save();
savedDashes = ctx.getLineDash().slice();
savedDashOffset = ctx.lineDashOffset;
} }
ctx.scale(scaleX, scaleY); ctx.scale(scaleX, scaleY);
@ -3232,16 +3238,16 @@ class CanvasGraphics {
// else we'll have some bugs (but only with too thin lines). // else we'll have some bugs (but only with too thin lines).
// Here we take the max... why not taking the min... or something else. // Here we take the max... why not taking the min... or something else.
// Anyway, as said it's buggy when scaleX !== scaleY. // Anyway, as said it's buggy when scaleX !== scaleY.
const scale = Math.max(scaleX, scaleY); if (dashes.length > 0) {
ctx.setLineDash(ctx.getLineDash().map(x => x / scale)); const scale = Math.max(scaleX, scaleY);
ctx.lineDashOffset /= scale; ctx.setLineDash(dashes.map(x => x / scale));
ctx.lineDashOffset /= scale;
}
ctx.stroke(); ctx.stroke();
if (saveRestore) { if (saveRestore) {
ctx.setTransform(...savedMatrix); ctx.restore();
ctx.setLineDash(savedDashes);
ctx.lineDashOffset = savedDashOffset;
} }
} }