Skip to content

Commit a08f0cd

Browse files
committed
Use integer coordinates when drawing images (bug 1264608, issue #3351)
- it aims to fix https://bugzilla.mozilla.org/show_bug.cgi?id=1264608; - it's only a partial fix for #3351; - some tiled images have some spurious white lines between the tiles. When the current transform is applyed the corners of an image can have some non-integer coordinates leading to some extra transparency added to handle that. So with this patch the current transform is applied on the point and on the dimensions in order to have at the end only integer values.
1 parent b72a448 commit a08f0cd

File tree

5 files changed

+126
-8
lines changed

5 files changed

+126
-8
lines changed

src/display/canvas.js

+96-8
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,78 @@ class CachedCanvases {
377377
}
378378
}
379379

380+
function drawImageAtIntegerCoords(
381+
ctx,
382+
srcImg,
383+
srcX,
384+
srcY,
385+
srcW,
386+
srcH,
387+
destX,
388+
destY,
389+
destW,
390+
destH
391+
) {
392+
const [a, b, c, d, tx, ty] = ctx.mozCurrentTransform;
393+
if (b === 0 && c === 0) {
394+
// top-left corner is at (X, Y) and
395+
// bottom-right one is at (X + width, Y + height).
396+
397+
// If leftX is 4.321 then it's rounded to 4.
398+
// If width is 10.432 then it's rounded to 11 because
399+
// rightX = leftX + width = 14.753 which is rounded to 15
400+
// so after rounding the total width is 11 (15 - 4).
401+
// It's why we can't just floor/ceil uniformly, it just depends
402+
// on the values we've.
403+
404+
const tlX = destX * a + tx;
405+
const rTlX = Math.round(tlX);
406+
const tlY = destY * d + ty;
407+
const rTlY = Math.round(tlY);
408+
const brX = (destX + destW) * a + tx;
409+
410+
// Some pdf contains images with 1x1 images so in case of 0-width after
411+
// scaling we must fallback on 1 to be sure there is something.
412+
const rWidth = Math.abs(Math.round(brX) - rTlX) || 1;
413+
const brY = (destY + destH) * d + ty;
414+
const rHeight = Math.abs(Math.round(brY) - rTlY) || 1;
415+
416+
// We must apply a transformation in order to apply it on the image itself.
417+
// For example if a == 1 && d == -1, it means that the image itself is
418+
// mirrored w.r.t. the x-axis.
419+
ctx.setTransform(Math.sign(a), 0, 0, Math.sign(d), rTlX, rTlY);
420+
ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, 0, 0, rWidth, rHeight);
421+
ctx.setTransform(a, b, c, d, tx, ty);
422+
423+
return [rWidth, rHeight];
424+
}
425+
426+
if (a === 0 && d === 0) {
427+
// This path is taken in issue9462.pdf (page 3).
428+
const tlX = destY * c + tx;
429+
const rTlX = Math.round(tlX);
430+
const tlY = destX * b + ty;
431+
const rTlY = Math.round(tlY);
432+
const brX = (destY + destH) * c + tx;
433+
const rWidth = Math.abs(Math.round(brX) - rTlX) || 1;
434+
const brY = (destX + destW) * b + ty;
435+
const rHeight = Math.abs(Math.round(brY) - rTlY) || 1;
436+
437+
ctx.setTransform(0, Math.sign(b), Math.sign(c), 0, rTlX, rTlY);
438+
ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, 0, 0, rHeight, rWidth);
439+
ctx.setTransform(a, b, c, d, tx, ty);
440+
441+
return [rHeight, rWidth];
442+
}
443+
444+
// Not a scale matrix so let the render handle the case without rounding.
445+
ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, destX, destY, destW, destH);
446+
447+
const scaleX = Math.hypot(a, b);
448+
const scaleY = Math.hypot(c, d);
449+
return [scaleX * destW, scaleY * destH];
450+
}
451+
380452
function compileType3Glyph(imgData) {
381453
const POINT_TO_PROCESS_LIMIT = 1000;
382454
const POINT_TYPES = new Uint8Array([
@@ -1461,8 +1533,8 @@ class CanvasGraphics {
14611533
const cord1 = Util.applyTransform([0, 0], maskToCanvas);
14621534
const cord2 = Util.applyTransform([width, height], maskToCanvas);
14631535
const rect = Util.normalizeRect([cord1[0], cord1[1], cord2[0], cord2[1]]);
1464-
const drawnWidth = Math.ceil(rect[2] - rect[0]);
1465-
const drawnHeight = Math.ceil(rect[3] - rect[1]);
1536+
const drawnWidth = Math.round(rect[2] - rect[0]);
1537+
const drawnHeight = Math.round(rect[3] - rect[1]);
14661538
const fillCanvas = this.cachedCanvases.getCanvas(
14671539
"fillCanvas",
14681540
drawnWidth,
@@ -1496,7 +1568,9 @@ class CanvasGraphics {
14961568
fillCtx.mozCurrentTransform,
14971569
img.interpolate
14981570
);
1499-
fillCtx.drawImage(
1571+
1572+
drawImageAtIntegerCoords(
1573+
fillCtx,
15001574
scaled,
15011575
0,
15021576
0,
@@ -3005,7 +3079,18 @@ class CanvasGraphics {
30053079
ctx.save();
30063080
ctx.transform.apply(ctx, image.transform);
30073081
ctx.scale(1, -1);
3008-
ctx.drawImage(maskCanvas.canvas, 0, 0, width, height, 0, -1, 1, 1);
3082+
drawImageAtIntegerCoords(
3083+
ctx,
3084+
maskCanvas.canvas,
3085+
0,
3086+
0,
3087+
width,
3088+
height,
3089+
0,
3090+
-1,
3091+
1,
3092+
1
3093+
);
30093094
ctx.restore();
30103095
}
30113096
this.compose();
@@ -3085,7 +3170,9 @@ class CanvasGraphics {
30853170
ctx.mozCurrentTransform,
30863171
imgData.interpolate
30873172
);
3088-
ctx.drawImage(
3173+
3174+
const [rWidth, rHeight] = drawImageAtIntegerCoords(
3175+
ctx,
30893176
scaled.img,
30903177
0,
30913178
0,
@@ -3103,8 +3190,8 @@ class CanvasGraphics {
31033190
imgData,
31043191
left: position[0],
31053192
top: position[1],
3106-
width: width / ctx.mozCurrentTransformInverse[0],
3107-
height: height / ctx.mozCurrentTransformInverse[3],
3193+
width: rWidth,
3194+
height: rHeight,
31083195
});
31093196
}
31103197
this.compose();
@@ -3133,7 +3220,8 @@ class CanvasGraphics {
31333220
ctx.save();
31343221
ctx.transform.apply(ctx, entry.transform);
31353222
ctx.scale(1, -1);
3136-
ctx.drawImage(
3223+
drawImageAtIntegerCoords(
3224+
ctx,
31373225
tmpCanvas.canvas,
31383226
entry.x,
31393227
entry.y,

test/pdfs/issue3351.1.pdf.link

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
https://github.com/mozilla/pdf.js/files/8582209/Doc2.pdf
2+

test/pdfs/issue3351.2.pdf.link

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

test/pdfs/issue3351.3.pdf.link

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

test/test_manifest.json

+24
Original file line numberDiff line numberDiff line change
@@ -6403,5 +6403,29 @@
64036403
"link": true,
64046404
"lastPage": 1,
64056405
"type": "eq"
6406+
},
6407+
{ "id": "issue3351.1",
6408+
"file": "pdfs/issue3351.1.pdf",
6409+
"md5": "4216245a5f18bb3eac80575ccf0b272d",
6410+
"rounds": 1,
6411+
"link": true,
6412+
"lastPage": 1,
6413+
"type": "eq"
6414+
},
6415+
{ "id": "issue3351.2",
6416+
"file": "pdfs/issue3351.2.pdf",
6417+
"md5": "fa3fd1659c409c7824ef8838c3071efc",
6418+
"rounds": 1,
6419+
"link": true,
6420+
"lastPage": 1,
6421+
"type": "eq"
6422+
},
6423+
{ "id": "issue3351.3",
6424+
"file": "pdfs/issue3351.3.pdf",
6425+
"md5": "60e2f2c480b6bc0e7f726743c6896520",
6426+
"rounds": 1,
6427+
"link": true,
6428+
"lastPage": 1,
6429+
"type": "eq"
64066430
}
64076431
]

0 commit comments

Comments
 (0)