Skip to content

Commit 1e437d3

Browse files
tacaswellQuLogic
authored andcommitted
Merge pull request #5324 from mdboom/transparent-jpg
Fix #5302: Proper alpha-blending for jpeg Conflicts: lib/matplotlib/tests/test_image.py
1 parent 70b851f commit 1e437d3

File tree

2 files changed

+38
-4
lines changed

2 files changed

+38
-4
lines changed

lib/matplotlib/backends/backend_agg.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
from matplotlib.mathtext import MathTextParser
3939
from matplotlib.path import Path
4040
from matplotlib.transforms import Bbox, BboxBase
41+
from matplotlib import colors as mcolors
4142

4243
from matplotlib.backends._backend_agg import RendererAgg as _RendererAgg
4344
from matplotlib import _png
@@ -571,7 +572,6 @@ def print_to_buffer(self):
571572
return result
572573

573574
if _has_pil:
574-
575575
# add JPEG support
576576
def print_jpg(self, filename_or_obj, *args, **kwargs):
577577
"""
@@ -593,14 +593,21 @@ def print_jpg(self, filename_or_obj, *args, **kwargs):
593593
buf, size = self.print_to_buffer()
594594
if kwargs.pop("dryrun", False):
595595
return
596+
# The image is "pasted" onto a white background image to safely
597+
# handle any transparency
596598
image = Image.frombuffer('RGBA', size, buf, 'raw', 'RGBA', 0, 1)
599+
color = mcolors.colorConverter.to_rgb(
600+
rcParams.get('savefig.facecolor', 'white'))
601+
color = tuple([int(x * 255.0) for x in color])
602+
background = Image.new('RGB', size, color)
603+
background.paste(image, image)
597604
options = restrict_dict(kwargs, ['quality', 'optimize',
598605
'progressive'])
599606

600607
if 'quality' not in options:
601608
options['quality'] = rcParams['savefig.jpeg_quality']
602609

603-
return image.save(filename_or_obj, format='jpeg', **options)
610+
return background.save(filename_or_obj, format='jpeg', **options)
604611
print_jpeg = print_jpg
605612

606613
# add TIFF support

lib/matplotlib/tests/test_image.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from matplotlib.image import (BboxImage, imread, NonUniformImage,
1616
AxesImage, FigureImage, PcolorImage)
1717
from matplotlib.transforms import Bbox, Affine2D, TransformedBbox
18-
from matplotlib import rcParams
18+
from matplotlib import rcParams, rc_context
1919
from matplotlib import patches
2020
import matplotlib.pyplot as plt
2121

@@ -35,7 +35,6 @@
3535

3636
try:
3737
from PIL import Image
38-
del Image
3938
HAS_PIL = True
4039
except ImportError:
4140
HAS_PIL = False
@@ -494,6 +493,34 @@ def test_nonuniformimage_setnorm():
494493
im.set_norm(plt.Normalize())
495494

496495

496+
@knownfailureif(not HAS_PIL)
497+
@cleanup
498+
def test_jpeg_alpha():
499+
plt.figure(figsize=(1, 1), dpi=300)
500+
# Create an image that is all black, with a gradient from 0-1 in
501+
# the alpha channel from left to right.
502+
im = np.zeros((300, 300, 4), dtype=float)
503+
im[..., 3] = np.linspace(0.0, 1.0, 300)
504+
505+
plt.figimage(im)
506+
507+
buff = io.BytesIO()
508+
with rc_context({'savefig.facecolor': 'red'}):
509+
plt.savefig(buff, transparent=True, format='jpg', dpi=300)
510+
511+
buff.seek(0)
512+
image = Image.open(buff)
513+
514+
# If this fails, there will be only one color (all black). If this
515+
# is working, we should have all 256 shades of grey represented.
516+
num_colors = len(image.getcolors(256))
517+
assert 175 <= num_colors <= 185, 'num colors: %d' % (num_colors, )
518+
# The fully transparent part should be red, not white or black
519+
# or anything else
520+
corner_pixel = image.getpixel((0, 0))
521+
assert corner_pixel == (254, 0, 0), "corner pixel: %r" % (corner_pixel, )
522+
523+
497524
@cleanup
498525
def test_nonuniformimage_setdata():
499526
ax = plt.gca()

0 commit comments

Comments
 (0)