Skip to content

Commit

Permalink
Fix alpha channels in PDF images
Browse files Browse the repository at this point in the history
The code was conflating the presence of an alpha channel with whether
the image is grayscale or RGB.

Fixes matplotlib#4331
  • Loading branch information
jkseppan committed Jul 6, 2015
1 parent d58a2a5 commit f2d59a4
Showing 1 changed file with 34 additions and 29 deletions.
63 changes: 34 additions & 29 deletions lib/matplotlib/backends/backend_pdf.py
Expand Up @@ -92,7 +92,6 @@

# TODOs:
#
# * the alpha channel of images
# * image compression could be improved (PDF supports png-like compression)
# * encoding of fonts, including mathtext fonts and unicode support
# * TTF support has lots of small TODOs, e.g., how do you know if a font
Expand Down Expand Up @@ -1262,18 +1261,19 @@ def imageObject(self, image):
self.images[image] = (name, ob)
return name

# These two from backend_ps.py
# TODO: alpha (SMask, p. 518 of pdf spec)

def _rgb(self, im):
h, w, s = im.as_rgba_str()

rgba = np.fromstring(s, np.uint8)
rgba.shape = (h, w, 4)
rgba = rgba[::-1]
rgb = rgba[:, :, :3]
a = rgba[:, :, 3:]
return h, w, rgb.tostring(), a.tostring()
rgb = rgba[:, :, :3].tostring()
a = rgba[:, :, 3]
if np.all(a == 255):
alpha = None
else:
alpha = a.tostring()
return h, w, rgb, alpha

def _gray(self, im, rc=0.3, gc=0.59, bc=0.11):
rgbat = im.as_rgba_str()
Expand All @@ -1284,24 +1284,30 @@ def _gray(self, im, rc=0.3, gc=0.59, bc=0.11):
r = rgba_f[:, :, 0]
g = rgba_f[:, :, 1]
b = rgba_f[:, :, 2]
gray = (r*rc + g*gc + b*bc).astype(np.uint8)
return rgbat[0], rgbat[1], gray.tostring()
a = rgba[:, :, 3]
if np.all(a == 255):
alpha = None
else:
alpha = a.tostring()
gray = (r*rc + g*gc + b*bc).astype(np.uint8).tostring()
return rgbat[0], rgbat[1], gray, alpha

def writeImages(self):
for img, pair in six.iteritems(self.images):
if img.is_grayscale:
height, width, data = self._gray(img)
self.beginStream(
pair[1].id,
self.reserveObject('length of image stream'),
{'Type': Name('XObject'), 'Subtype': Name('Image'),
'Width': width, 'Height': height,
'ColorSpace': Name('DeviceGray'), 'BitsPerComponent': 8})
# TODO: predictors (i.e., output png)
self.currentstream.write(data)
self.endStream()
height, width, data, adata = self._gray(img)
else:
height, width, data, adata = self._rgb(img)

colorspace = 'DeviceGray' if img.is_grayscale else 'DeviceRGB'
obj = {'Type': Name('XObject'),
'Subtype': Name('Image'),
'Width': width,
'Height': height,
'ColorSpace': Name(colorspace),
'BitsPerComponent': 8}

if adata is not None:
smaskObject = self.reserveObject("smask")
self.beginStream(
smaskObject.id,
Expand All @@ -1312,17 +1318,16 @@ def writeImages(self):
# TODO: predictors (i.e., output png)
self.currentstream.write(adata)
self.endStream()
obj['SMask'] = smaskObject

self.beginStream(
pair[1].id,
self.reserveObject('length of image stream'),
{'Type': Name('XObject'), 'Subtype': Name('Image'),
'Width': width, 'Height': height,
'ColorSpace': Name('DeviceRGB'), 'BitsPerComponent': 8,
'SMask': smaskObject})
# TODO: predictors (i.e., output png)
self.currentstream.write(data)
self.endStream()
self.beginStream(
pair[1].id,
self.reserveObject('length of image stream'),
obj
)
# TODO: predictors (i.e., output png)
self.currentstream.write(data)
self.endStream()

def markerObject(self, path, trans, fillp, strokep, lw, joinstyle,
capstyle):
Expand Down

0 comments on commit f2d59a4

Please sign in to comment.