Skip to content

Commit

Permalink
Merge pull request #2898 from wiredfool/pr_2790
Browse files Browse the repository at this point in the history
Add support for sRGB and cHRM chunks, permit sRGB when no iCCP chunk
  • Loading branch information
wiredfool authored Dec 27, 2017
2 parents 3d8035b + 27bc13d commit bd5f99f
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 14 deletions.
25 changes: 23 additions & 2 deletions PIL/PngImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -387,12 +387,31 @@ def chunk_tRNS(self, pos, length):
return s

def chunk_gAMA(self, pos, length):

# gamma setting
s = ImageFile._safe_read(self.fp, length)
self.im_info["gamma"] = i32(s) / 100000.0
return s

def chunk_cHRM(self, pos, length):
# chromaticity, 8 unsigned ints, actual value is scaled by 100,000
# WP x,y, Red x,y, Green x,y Blue x,y

s = ImageFile._safe_read(self.fp, length)
raw_vals = struct.unpack('>%dI' % (len(s) // 4), s)
self.im_info['chromaticity'] = tuple(elt/100000.0 for elt in raw_vals)
return s

def chunk_sRGB(self, pos, length):
# srgb rendering intent, 1 byte
# 0 perceptual
# 1 relative colorimetric
# 2 saturation
# 3 absolute colorimetric

s = ImageFile._safe_read(self.fp, length)
self.im_info['srgb'] = i8(s)
return s

def chunk_pHYs(self, pos, length):

# pixels per unit
Expand Down Expand Up @@ -731,7 +750,9 @@ def _save(im, fp, filename, chunk=putchunk):
name = b"ICC Profile"
data = name + b"\0\0" + zlib.compress(icc)
chunk(fp, b"iCCP", data)
else:

# You must either have sRGB or iCCP.
# Disallow sRGB chunks when an iCCP-chunk has been emitted.
chunks.remove(b"sRGB")

info = im.encoderinfo.get("pnginfo")
Expand Down
28 changes: 16 additions & 12 deletions Tests/test_file_png.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,21 @@ def setUp(self):
if "zip_encoder" not in codecs or "zip_decoder" not in codecs:
self.skipTest("zip/deflate support not available")

def get_chunks(self, filename):
chunks = []
with open(filename, "rb") as fp:
fp.read(8)
with PngImagePlugin.PngStream(fp) as png:
while True:
cid, pos, length = png.read()
chunks.append(cid)
try:
s = png.call(cid, pos, length)
except EOFError:
break
png.crc(cid, s)
return chunks

def test_sanity(self):

# internal version number
Expand Down Expand Up @@ -501,18 +516,7 @@ def test_chunk_order(self):
test_file = self.tempfile("temp.png")
im.convert("P").save(test_file, dpi=(100, 100))

chunks = []
with open(test_file, "rb") as fp:
fp.read(8)
with PngImagePlugin.PngStream(fp) as png:
while True:
cid, pos, length = png.read()
chunks.append(cid)
try:
s = png.call(cid, pos, length)
except EOFError:
break
png.crc(cid, s)
chunks = self.get_chunks(test_file)

# https://www.w3.org/TR/PNG/#5ChunkOrdering
# IHDR - shall be first
Expand Down
13 changes: 13 additions & 0 deletions docs/handbook/image-file-formats.rst
Original file line number Diff line number Diff line change
Expand Up @@ -408,9 +408,22 @@ PIL identifies, reads, and writes PNG files containing ``1``, ``L``, ``P``,
The :py:meth:`~PIL.Image.Image.open` method sets the following
:py:attr:`~PIL.Image.Image.info` properties, when appropriate:

**chromaticity**
The chromaticity points, as an 8 tuple of floats. (``White Point
X``, ``White Point Y``, ``Red X``, ``Red Y``, ``Green X``, ``Green
Y``, ``Blue X``, ``Blue Y``)

**gamma**
Gamma, given as a floating point number.

**srgb**
The sRGB rendering intent as an integer.

* 0 Perceptual
* 1 Relative Colorimetric
* 2 Saturation
* 3 Absolute Colorimetric

**transparency**
For ``P`` images: Either the palette index for full transparent pixels,
or a byte string with alpha values for each palette entry.
Expand Down

0 comments on commit bd5f99f

Please sign in to comment.