The https://i.imgur.com/Vc2eT5I.png image >>124 is small and simple enough to manipulate in the REPL. Here is a demonstration of independent decoding of the second IDAT run, similar to what would happen in partial or parallel decoding. Loading the bytes:
$ python3
>>> with open ("Vc2eT5I.png", "rb") as f:
... b = f.read ()
...
>>> len (b)
34525
The chunk layout is in >>122:
>>> idats = [(0x00000056, 16384), (0x00004062, 1762), (0x00004750, 16245)]
Merging IDAT payloads:
>>> ba = bytearray ()
>>> for pos, len in idats:
... ba.extend (b [pos + 8 : pos + 8 + len])
...
Decompression yields the filtered rows. The size matches >>123:
>>> import zlib
>>> dec = zlib.decompress (ba)
>>> del len
>>> len (dec)
1151538
PNG doesn't use deflate directly but goes through zlib wrapping. The trailer of the wrapping is the adler32 of the uncompressed data. This is just a sanity check.
>>> hex (zlib.adler32 (dec))
'0x24074d18'
This matches the MSB u32 in the final four bytes of the final IDAT payload:
>>> adlpos = idats [-1][0] + 8 + idats [-1][1] - 4
>>> b [adlpos : adlpos + 4]
b'$\x07M\x18'
>>> import struct
>>> hex (struct.unpack (">L", _) [0])
'0x24074d18'
The PNG IHDR is fixed in size and position. We can peek at the height:
>>> hpos = 8 + 8 + 4
>>> struct.unpack (">L", b [hpos : hpos + 4]) [0]
562
The height is edited to half:
>>> ba.clear ()
>>> ba.extend (b [:33])
>>> ba [hpos : hpos + 4] = struct.pack (">L", 562 // 2)
Since the IHDR changed it needs a new chunk checksum:
>>> crc = zlib.crc32 (b'IHDR')
>>> crc = zlib.crc32 (ba [8 + 8 : 8 + 8 + 13], crc)
>>> ba [29 : 33] = struct.pack (">L", crc)
The second IDAT run has a single chunk. Since it will now be the first and only chunk it needs the zlib header, which is two extra bytes:
>>> ba.extend (struct.pack (">L", idats [-1][1] + 2))
>>> ba.extend (b'IDAT')
The zlib header is taken from the first 2 bytes of the first IDAT payload:
>>> ba.extend (b [idats [0][0] + 8 : idats [0][0] + 10])
The raw deflate stream of the second IDAT run is then copied exactly, omitting the old adler32 trailer:
>>> ba.extend (b [idats [-1][0] + 8 : idats [-1][0] + 8 + idats [-1][1] - 4])
The new adler32 trailer is over the second half of the uncompressed data:
>>> adl = zlib.adler32 (dec [len (dec) // 2:])
>>> ba.extend (struct.pack (">L", adl))
The new IDAT needs its chunk checksum:
>>> crc = zlib.crc32 (b'IDAT')
>>> crc = zlib.crc32 (ba [33 + 8:], crc)
>>> ba.extend (struct.pack (">L", crc))
The IEND chunk is a constant PNG trailer:
>>> ba.extend (b [-12:])
The result is a valid PNG image:
>>> with open ("temp/half.png", "wb") as f:
... f.write (ba)
...
16304
The image http://0x0.st/iyn7.png is the lower half of the original. Its chunk layout is:
0 0x00000008 13 b'IHDR' 0xE1499D34
1 0x00000021 16247 b'IDAT' 0xBE6A01A6
2 0x00003FA4 0 b'IEND' 0xAE426082
It is not much to look at, but its raw deflate stream is bit-for-bit identical to the one from the second IDAT run of the original. The checksums are only updated to make a valid PNG file. This decodes independently, without using the first IDAT run, as would happen in partial or parallel decoding. This is possible because the encoder split the image on row boundaries, and the filter condition >>129 and deflate block byte alignment and back reference conditions >>124 are all met.
I think this about covers the function of the iDOT chunk.