diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index ddb4fd610b12..bd1254c27fe1 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -496,37 +496,40 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0, out_alpha *= _resample(self, alpha, out_shape, t, resample=True) # mask and run through the norm resampled_masked = np.ma.masked_array(A_resampled, out_mask) - output = self.norm(resampled_masked) + res = self.norm(resampled_masked) else: if A.ndim == 2: # interpolation_stage = 'rgba' self.norm.autoscale_None(A) A = self.to_rgba(A) + if A.dtype == np.uint8: + # uint8 is too imprecise for premultiplied alpha roundtrips. + A = np.divide(A, 0xff, dtype=np.float32) alpha = self.get_alpha() + post_apply_alpha = False if alpha is None: # alpha parameter not specified if A.shape[2] == 3: # image has no alpha channel - output_alpha = 255 if A.dtype == np.uint8 else 1.0 - else: - output_alpha = _resample( # resample alpha channel - self, A[..., 3], out_shape, t) - output = _resample( # resample rgb channels - self, _rgb_to_rgba(A[..., :3]), out_shape, t) + A = np.dstack([A, np.ones(A.shape[:2])]) elif np.ndim(alpha) > 0: # Array alpha # user-specified array alpha overrides the existing alpha channel - output_alpha = _resample(self, alpha, out_shape, t) - output = _resample( - self, _rgb_to_rgba(A[..., :3]), out_shape, t) + A = np.dstack([A[..., :3], alpha]) else: # Scalar alpha if A.shape[2] == 3: # broadcast scalar alpha - output_alpha = (255 * alpha) if A.dtype == np.uint8 else alpha + A = np.dstack([A, np.full(A.shape[:2], alpha, np.float32)]) else: # or apply scalar alpha to existing alpha channel - output_alpha = _resample(self, A[..., 3], out_shape, t) * alpha - output = _resample( - self, _rgb_to_rgba(A[..., :3]), out_shape, t) - output[..., 3] = output_alpha # recombine rgb and alpha - - # output is now either a 2D array of normed (int or float) data + post_apply_alpha = True + # Resample in premultiplied alpha space. (TODO: Consider + # implementing premultiplied-space resampling in + # span_image_resample_rgba_affine::generate?) + A[..., :3] *= A[..., 3:] + res = _resample(self, A, out_shape, t) + np.divide(res[..., :3], res[..., 3:], out=res[..., :3], + where=res[..., 3:] != 0) + if post_apply_alpha: + res[..., 3] *= alpha + + # res is now either a 2D array of normed (int or float) data # or an RGBA array of re-sampled input - output = self.to_rgba(output, bytes=True, norm=False) + output = self.to_rgba(res, bytes=True, norm=False) # output is now a correctly sized RGBA array of uint8 # Apply alpha *after* if the input was greyscale without a mask diff --git a/lib/matplotlib/tests/baseline_images/test_image/downsampling_speckle.png b/lib/matplotlib/tests/baseline_images/test_image/downsampling_speckle.png index eb8b3ce13013..7c18bc89a9af 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/downsampling_speckle.png and b/lib/matplotlib/tests/baseline_images/test_image/downsampling_speckle.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_image/rgba_antialias.png b/lib/matplotlib/tests/baseline_images/test_image/rgba_antialias.png index 65476dc9a595..62cae15ccc91 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/rgba_antialias.png and b/lib/matplotlib/tests/baseline_images/test_image/rgba_antialias.png differ diff --git a/lib/matplotlib/tests/test_image.py b/lib/matplotlib/tests/test_image.py index 7437651d2b86..9e387701bf38 100644 --- a/lib/matplotlib/tests/test_image.py +++ b/lib/matplotlib/tests/test_image.py @@ -260,15 +260,15 @@ def test_image_alpha(): def test_imshow_alpha(fig_test, fig_ref): np.random.seed(19680801) - rgbf = np.random.rand(6, 6, 3) + rgbf = np.random.rand(6, 6, 3).astype(np.float32) rgbu = np.uint8(rgbf * 255) ((ax0, ax1), (ax2, ax3)) = fig_test.subplots(2, 2) ax0.imshow(rgbf, alpha=0.5) ax1.imshow(rgbf, alpha=0.75) - ax2.imshow(rgbu, alpha=0.5) - ax3.imshow(rgbu, alpha=0.75) + ax2.imshow(rgbu, alpha=127/255) + ax3.imshow(rgbu, alpha=191/255) - rgbaf = np.concatenate((rgbf, np.ones((6, 6, 1))), axis=2) + rgbaf = np.concatenate((rgbf, np.ones((6, 6, 1))), axis=2).astype(np.float32) rgbau = np.concatenate((rgbu, np.full((6, 6, 1), 255, np.uint8)), axis=2) ((ax0, ax1), (ax2, ax3)) = fig_ref.subplots(2, 2) rgbaf[:, :, 3] = 0.5 @@ -514,7 +514,7 @@ def test_image_composite_background(): ax.set_xlim([0, 12]) -@image_comparison(['image_composite_alpha'], remove_text=True) +@image_comparison(['image_composite_alpha'], remove_text=True, tol=0.07) def test_image_composite_alpha(): """ Tests that the alpha value is recognized and correctly applied in the diff --git a/lib/matplotlib/tests/test_png.py b/lib/matplotlib/tests/test_png.py index 9208c31df2bf..a7677b0d05ac 100644 --- a/lib/matplotlib/tests/test_png.py +++ b/lib/matplotlib/tests/test_png.py @@ -7,7 +7,7 @@ from matplotlib import cm, pyplot as plt -@image_comparison(['pngsuite.png'], tol=0.04) +@image_comparison(['pngsuite.png'], tol=0.09) def test_pngsuite(): files = sorted( (Path(__file__).parent / "baseline_images/pngsuite").glob("basn*.png"))