docs: def idat(self, rows, packed=False):
          """Generator that produce uncompressed IDAT data from rows"""
          # http://www.w3.org/TR/PNG/#11IDAT
          filt = Filter(self.bitdepth * self.planes,
                        self.interlace, self.height)
          data = bytearray()
  
          def byteextend(rowbytes):
              """Default extending data with bytes. Applying filter"""
              data.extend(filt.do_filter(self.filter_type, rowbytes))
  
          # Choose an extend function based on the bitdepth.  The extend
          # function packs/decomposes the pixel values into bytes and
          # stuffs them onto the data array.
          if self.bitdepth == 8 or packed:
              extend = byteextend
          elif self.bitdepth == 16:
              def extend(sl):
                  """Decompose into bytes before byteextend"""
                  fmt = '!%dH' % len(sl)
                  byteextend(bytearray(struct.pack(fmt, *sl)))
          else:
              # Pack into bytes
              assert self.bitdepth < 8
              # samples per byte
              spb = 8 // self.bitdepth
  
              def extend(sl):
                  """Pack into bytes before byteextend"""
                  a = bytearray(sl)
                  # Adding padding bytes so we can group into a whole
                  # number of spb-tuples.
                  l = float(len(a))
                  extra = math.ceil(l / float(spb)) * spb - l
                  a.extend([0] * int(extra))
                  # Pack into bytes
                  l = group(a, spb)
                  l = [reduce(lambda x, y: (x << self.bitdepth) + y, e)
                       for e in l]
                  byteextend(l)
  
          # Build the first row, testing mostly to see if we need to
          # changed the extend function to cope with NumPy integer types
          # (they cause our ordinary definition of extend to fail, so we
          # wrap it).  See
          # http://code.google.com/p/pypng/issues/detail?id=44
          enumrows = enumerate(rows)
          del rows
  
          # :todo: Certain exceptions in the call to ``.next()`` or the
          # following try would indicate no row data supplied.
          # Should catch.
          i, row = next(enumrows)
          try:
              # If this fails...
              extend(row)
          except:
              # ... try a version that converts the values to int first.
              # Not only does this work for the (slightly broken) NumPy
              # types, there are probably lots of other, unknown, "nearly"
              # int types it works for.
              def wrapmapint(f):
                  return lambda sl: f([int(x) for x in sl])
              extend = wrapmapint(extend)
              del wrapmapint
              extend(row)
  
          for i, row in enumrows:
              extend(row)
              if len(data) > self.chunk_limit:
                  yield bytearray_to_bytes(data)
                  # Because of our very witty definition of ``extend``,
                  # above, we must re-use the same ``data`` object.  Hence
                  # we use ``del`` to empty this one, rather than create a
                  # fresh one (which would be my natural FP instinct).
                  del data[:]
          if len(data):
              yield bytearray_to_bytes(data)
          self.irows = i + 1