Skip to content

Commit 89bbd1c

Browse files
committed
Fix making an image that is to be placed at a fractional pixel
The code now specifies the resampling with the foreknowledge that the resampled image will be placed at the nearest display pixel. This fixes situations where the rendered image can appear to be shifted by up to one display pixel.
1 parent 47874b4 commit 89bbd1c

1 file changed

Lines changed: 16 additions & 22 deletions

File tree

lib/matplotlib/image.py

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
operations.
44
"""
55

6-
import math
76
import os
87
import logging
98
from pathlib import Path
@@ -393,10 +392,15 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0,
393392
if clipped_bbox is None:
394393
return None, 0, 0, None
395394

396-
out_width_base = clipped_bbox.width * magnification
397-
out_height_base = clipped_bbox.height * magnification
395+
# Define the magnified bbox after clipping
396+
magnified_extents = clipped_bbox.extents * magnification
397+
if ((not unsampled) and round_to_pixel_border):
398+
# Round to the nearest output pixel
399+
magnified_bbox = Bbox.from_extents((magnified_extents + 0.5).astype(int))
400+
else:
401+
magnified_bbox = Bbox.from_extents(magnified_extents)
398402

399-
if out_width_base == 0 or out_height_base == 0:
403+
if magnified_bbox.width == 0 or magnified_bbox.height == 0:
400404
return None, 0, 0, None
401405

402406
if self.origin == 'upper':
@@ -417,23 +421,10 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0,
417421

418422
t = (t0
419423
+ (Affine2D()
420-
.translate(-clipped_bbox.x0, -clipped_bbox.y0)
421-
.scale(magnification)))
422-
423-
# So that the image is aligned with the edge of the Axes, we want to
424-
# round up the output width to the next integer. This also means
425-
# scaling the transform slightly to account for the extra subpixel.
426-
if ((not unsampled) and t.is_affine and round_to_pixel_border and
427-
(out_width_base % 1.0 != 0.0 or out_height_base % 1.0 != 0.0)):
428-
out_width = math.ceil(out_width_base)
429-
out_height = math.ceil(out_height_base)
430-
extra_width = (out_width - out_width_base) / out_width_base
431-
extra_height = (out_height - out_height_base) / out_height_base
432-
t += Affine2D().scale(1.0 + extra_width, 1.0 + extra_height)
433-
else:
434-
out_width = int(out_width_base)
435-
out_height = int(out_height_base)
436-
out_shape = (out_height, out_width)
424+
.scale(magnification)
425+
.translate(-magnified_bbox.x0, -magnified_bbox.y0)))
426+
427+
out_shape = (int(magnified_bbox.height), int(magnified_bbox.width))
437428

438429
if not unsampled:
439430
if not (A.ndim == 2 or A.ndim == 3 and A.shape[-1] in (3, 4)):
@@ -560,7 +551,10 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0,
560551
t = Affine2D().translate(
561552
int(max(subset.xmin, 0)), int(max(subset.ymin, 0))) + t
562553

563-
return output, clipped_bbox.x0, clipped_bbox.y0, t
554+
return (output,
555+
magnified_bbox.x0 / magnification,
556+
magnified_bbox.y0 / magnification,
557+
t)
564558

565559
def make_image(self, renderer, magnification=1.0, unsampled=False):
566560
"""

0 commit comments

Comments
 (0)