Merge pull request #14853 from calixteman/white_lines

Use integer coordinates when drawing images (bug 1264608, issue #3351)
This commit is contained in:
calixteman 2022-04-29 18:15:03 +02:00 committed by GitHub
commit b10b8dad7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 126 additions and 8 deletions

View File

@ -377,6 +377,78 @@ class CachedCanvases {
} }
} }
function drawImageAtIntegerCoords(
ctx,
srcImg,
srcX,
srcY,
srcW,
srcH,
destX,
destY,
destW,
destH
) {
const [a, b, c, d, tx, ty] = ctx.mozCurrentTransform;
if (b === 0 && c === 0) {
// top-left corner is at (X, Y) and
// bottom-right one is at (X + width, Y + height).
// If leftX is 4.321 then it's rounded to 4.
// If width is 10.432 then it's rounded to 11 because
// rightX = leftX + width = 14.753 which is rounded to 15
// so after rounding the total width is 11 (15 - 4).
// It's why we can't just floor/ceil uniformly, it just depends
// on the values we've.
const tlX = destX * a + tx;
const rTlX = Math.round(tlX);
const tlY = destY * d + ty;
const rTlY = Math.round(tlY);
const brX = (destX + destW) * a + tx;
// Some pdf contains images with 1x1 images so in case of 0-width after
// scaling we must fallback on 1 to be sure there is something.
const rWidth = Math.abs(Math.round(brX) - rTlX) || 1;
const brY = (destY + destH) * d + ty;
const rHeight = Math.abs(Math.round(brY) - rTlY) || 1;
// We must apply a transformation in order to apply it on the image itself.
// For example if a == 1 && d == -1, it means that the image itself is
// mirrored w.r.t. the x-axis.
ctx.setTransform(Math.sign(a), 0, 0, Math.sign(d), rTlX, rTlY);
ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, 0, 0, rWidth, rHeight);
ctx.setTransform(a, b, c, d, tx, ty);
return [rWidth, rHeight];
}
if (a === 0 && d === 0) {
// This path is taken in issue9462.pdf (page 3).
const tlX = destY * c + tx;
const rTlX = Math.round(tlX);
const tlY = destX * b + ty;
const rTlY = Math.round(tlY);
const brX = (destY + destH) * c + tx;
const rWidth = Math.abs(Math.round(brX) - rTlX) || 1;
const brY = (destX + destW) * b + ty;
const rHeight = Math.abs(Math.round(brY) - rTlY) || 1;
ctx.setTransform(0, Math.sign(b), Math.sign(c), 0, rTlX, rTlY);
ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, 0, 0, rHeight, rWidth);
ctx.setTransform(a, b, c, d, tx, ty);
return [rHeight, rWidth];
}
// Not a scale matrix so let the render handle the case without rounding.
ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, destX, destY, destW, destH);
const scaleX = Math.hypot(a, b);
const scaleY = Math.hypot(c, d);
return [scaleX * destW, scaleY * destH];
}
function compileType3Glyph(imgData) { function compileType3Glyph(imgData) {
const POINT_TO_PROCESS_LIMIT = 1000; const POINT_TO_PROCESS_LIMIT = 1000;
const POINT_TYPES = new Uint8Array([ const POINT_TYPES = new Uint8Array([
@ -1461,8 +1533,8 @@ class CanvasGraphics {
const cord1 = Util.applyTransform([0, 0], maskToCanvas); const cord1 = Util.applyTransform([0, 0], maskToCanvas);
const cord2 = Util.applyTransform([width, height], maskToCanvas); const cord2 = Util.applyTransform([width, height], maskToCanvas);
const rect = Util.normalizeRect([cord1[0], cord1[1], cord2[0], cord2[1]]); const rect = Util.normalizeRect([cord1[0], cord1[1], cord2[0], cord2[1]]);
const drawnWidth = Math.ceil(rect[2] - rect[0]); const drawnWidth = Math.round(rect[2] - rect[0]) || 1;
const drawnHeight = Math.ceil(rect[3] - rect[1]); const drawnHeight = Math.round(rect[3] - rect[1]) || 1;
const fillCanvas = this.cachedCanvases.getCanvas( const fillCanvas = this.cachedCanvases.getCanvas(
"fillCanvas", "fillCanvas",
drawnWidth, drawnWidth,
@ -1496,7 +1568,9 @@ class CanvasGraphics {
fillCtx.mozCurrentTransform, fillCtx.mozCurrentTransform,
img.interpolate img.interpolate
); );
fillCtx.drawImage(
drawImageAtIntegerCoords(
fillCtx,
scaled, scaled,
0, 0,
0, 0,
@ -3005,7 +3079,18 @@ class CanvasGraphics {
ctx.save(); ctx.save();
ctx.transform.apply(ctx, image.transform); ctx.transform.apply(ctx, image.transform);
ctx.scale(1, -1); ctx.scale(1, -1);
ctx.drawImage(maskCanvas.canvas, 0, 0, width, height, 0, -1, 1, 1); drawImageAtIntegerCoords(
ctx,
maskCanvas.canvas,
0,
0,
width,
height,
0,
-1,
1,
1
);
ctx.restore(); ctx.restore();
} }
this.compose(); this.compose();
@ -3085,7 +3170,9 @@ class CanvasGraphics {
ctx.mozCurrentTransform, ctx.mozCurrentTransform,
imgData.interpolate imgData.interpolate
); );
ctx.drawImage(
const [rWidth, rHeight] = drawImageAtIntegerCoords(
ctx,
scaled.img, scaled.img,
0, 0,
0, 0,
@ -3103,8 +3190,8 @@ class CanvasGraphics {
imgData, imgData,
left: position[0], left: position[0],
top: position[1], top: position[1],
width: width / ctx.mozCurrentTransformInverse[0], width: rWidth,
height: height / ctx.mozCurrentTransformInverse[3], height: rHeight,
}); });
} }
this.compose(); this.compose();
@ -3133,7 +3220,8 @@ class CanvasGraphics {
ctx.save(); ctx.save();
ctx.transform.apply(ctx, entry.transform); ctx.transform.apply(ctx, entry.transform);
ctx.scale(1, -1); ctx.scale(1, -1);
ctx.drawImage( drawImageAtIntegerCoords(
ctx,
tmpCanvas.canvas, tmpCanvas.canvas,
entry.x, entry.x,
entry.y, entry.y,

View File

@ -0,0 +1,2 @@
https://github.com/mozilla/pdf.js/files/8582209/Doc2.pdf

View File

@ -0,0 +1,2 @@
https://bugzilla.mozilla.org/attachment.cgi?id=8741330

View File

@ -0,0 +1,2 @@
https://bugzilla.mozilla.org/attachment.cgi?id=8741334

View File

@ -6403,5 +6403,29 @@
"link": true, "link": true,
"lastPage": 1, "lastPage": 1,
"type": "eq" "type": "eq"
},
{ "id": "issue3351.1",
"file": "pdfs/issue3351.1.pdf",
"md5": "4216245a5f18bb3eac80575ccf0b272d",
"rounds": 1,
"link": true,
"lastPage": 1,
"type": "eq"
},
{ "id": "issue3351.2",
"file": "pdfs/issue3351.2.pdf",
"md5": "fa3fd1659c409c7824ef8838c3071efc",
"rounds": 1,
"link": true,
"lastPage": 1,
"type": "eq"
},
{ "id": "issue3351.3",
"file": "pdfs/issue3351.3.pdf",
"md5": "60e2f2c480b6bc0e7f726743c6896520",
"rounds": 1,
"link": true,
"lastPage": 1,
"type": "eq"
} }
] ]