Abstract Adam7 coordinate generation into its own generator
@@ -179,6 +179,7 @@ __all__ = ['Image', 'Reader', 'Writer', 'write_chunks', 'from_array']
# http://www.w3.org/TR/PNG/#5PNG-file-signature
_signature = struct.pack('8B', 137, 80, 78, 71, 13, 10, 26, 10)
+# The xstart, ystart, xstep, ystep for the Adam7 interlace passes.
_adam7 = ((0, 0, 8, 8),
(4, 0, 8, 8),
(0, 4, 4, 8),
@@ -188,6 +189,24 @@ _adam7 = ((0, 0, 8, 8),
(0, 1, 1, 2))
+def adam7generate(width, height):
+ """
+ Generate the coordinates for the reduced scanlines
+ of an Adam7 interlaced image
+ of size `width` by `height` pixels.
+
+ Yields a series of (x, y, xstep) triples,
+ each one identifying a reduced scanline consisting of
+ pixels starting at (x, y) and taking every xstep pixel to the right.
+ """
+
+ for xstart, ystart, xstep, ystep in _adam7:
+ if xstart >= width:
+ continue
+ for y in range(ystart, height, ystep):
+ yield xstart, y, xstep
+
+
# Holds information about the 'pHYs' chunk (used by the Reader, only)
_Resolution = collections.namedtuple('_Resolution', 'x y unit_is_meter')
@@ -950,22 +969,26 @@ class Writer:
fmt = 'BH'[self.bitdepth > 8]
# Value per row
vpr = self.width * self.planes
- for xstart, ystart, xstep, ystep in _adam7:
- if xstart >= self.width:
- continue
+
+ # Each iteration generates a scanline starting at (x, y)
+ # and consisting of every xstep pixels.
+ for x, y, xstep in adam7generate(self.width, self.height):
# Pixels per row (of reduced image)
- ppr = int(math.ceil((self.width - xstart) / float(xstep)))
- # number of values in reduced image row.
- row_len = ppr * self.planes
- for y in range(ystart, self.height, ystep):
+ ppr = int(math.ceil((self.width - x) / float(xstep)))
+ # Values per row (of reduced image)
+ reduced_row_len = ppr * self.planes
if xstep == 1:
+ # Easy case: line is a simple slice.
offset = y * vpr
yield pixels[offset: offset + vpr]
- else:
+ continue
+ # We have to step by xstep,
+ # which we can do one plane at a time
+ # using the step in Python slices.
row = array(fmt)
# There's no easier way to set the length of an array
- row.extend(pixels[0:row_len])
- offset = y * vpr + xstart * self.planes
+ row.extend(pixels[0:reduced_row_len])
+ offset = y * vpr + x * self.planes
end_offset = (y + 1) * vpr
skip = self.planes * xstep
for i in range(self.planes):
