Size of interlaced against non-interlaced PNG uncompressed data

Goal

I would like to know by advance the length of the uncompressed data within a PNG image whether it is encoded interlaced or not. Knowing and comparing the size of interlaced against non-interlaced PNG uncompressed (inflated) data can allow a wiser choice when encoding a PNG image and can also help while decoding. This can also explain why PNG images are usually greater when interlaced. All the following explanations are based on the PNG RFC. I focus on the cases when the pixel size is >= 8 bits.

Interlaced and non-interlaced: what changes in the PNG file?

Let’s assume 2 identical PNG images, one non-interlaced and one interlaced.
The differences between the files describing those images will be:
– In IHDR chunk: Interlace method (0 for non-interlaced, 1 (Adam7) for interlaced). This doesn’t change the file size by itself.
– In IDAT chunks: Compressed (deflated) scanlines starting with filter type containing the encoded image data. This usually changes the file size as explained below.

Uncompressed data size

The uncompressed (inflated) data length can be calculated as follows for the cases when the pixel size is >= 8 bits.

Non-interlaced case

uncompressed_data_length = height * width * nb_bits_per_pixel / 8 + height
Where nb_bits_per_pixel = nb_sample_per_pixel * bit_depth
Where nb_sample_per_pixel depends on the color_type:
color_type to nb_sample_per_pixel:
0 (grayscale) -> 1
2 (RGB) -> 3
3 (palette based) -> 1
4 (grayscale with alpha) -> 2
6 (RGB with alpha) -> 4

So, for example, if we assume a non-interlaced image with the following characteristics, we get an uncompressed data size of 1695885:
height: 677
width: 626
color_type: 6
=> nb_sample_per_pixel: 4
bit_depth: 8
=> nb_bits_per_pixel: 32

Note the “+ height” at the end of the formula giving the uncompressed_data_length. This is due to the fact that for each scanline, a leading filter type byte is necessary and there are as many scanlines as there are lines in a non-interlaced image.

Interlaced case

uncompressed_data_length = height * width * nb_bits_per_pixel / 8 + sum_lines_per_pass
Where sum_lines_per_pass = nb_pass1_lines + nb_pass2_lines + nb_pass3_lines + nb_pass4_lines + nb_pass5_lines + nb_pass6_lines + nb_pass7_lines
Where (calculation due to Adam7 interlace method):
nb_pass1_lines = CEIL(height/8)
nb_pass2_lines = (width>4?CEIL(height/8):0)
nb_pass3_lines = CEIL((height-4)/8)
nb_pass4_lines = (width>2?CEIL(height/4):0)
nb_pass5_lines = CEIL((height-2)/4)
nb_pass6_lines = (width>1?CEIL(height/2):0)
nb_pass7_lines = FLOOR(height/2)

So, for example, if we assume an interlaced image with the following characteristics, we get an uncompressed data size of 1696479:
height: 677
width: 626
color_type: 6
=> nb_sample_per_pixel: 4
bit_depth: 8
=> nb_bits_per_pixel: 32
nb_pass1_lines = 85
nb_pass2_lines = 85
nb_pass3_lines = 85
nb_pass4_lines = 170
nb_pass5_lines = 169
nb_pass6_lines = 339
nb_pass7_lines = 338
=> sum_lines_per_pass = 1271 (which is greater than height 677 for the non-interlaced case)

Note the “+ sum_lines_per_pass” at the end of the formula giving the uncompressed_data_length. This is because the number of filter type needs to be added. Each scanline has a leading filter type. Having passes for the interlaced case leads to a different number of filter types.

Comparison of uncompressed data length formula whether interlaced or not

Is the number of filter types for the interlaced case always going to be greater than the one for the non-interlaced case because of the passes?
It is easy to show that the number of lines for passes 1, 2, 4 and 6 contribute already for at least a number equivalent to height when width > 4. Indeed:
CEIL(height/8) + CEIL(height/8) + CEIL(height/4) + CEIL(height/2) >= height
The number of lines for passes 3, 5 and 7 can contribute to increase the number of filters for the interlaced case above the number of filters for the non-interlaced case.
Thus, the number of filters for the interlaced case will almost always be greater than the one for the non-interlaced case.

Why is this only “almost” always greater?

Let’s take the following particular example:
height: 1
width: 1
color_type: 6
=> nb_sample_per_pixel: 4
bit_depth: 8
=> nb_bits_per_pixel: 32
nb_pass1_lines = 1
nb_pass2_lines = 0
nb_pass3_lines = 0
nb_pass4_lines = 0
nb_pass5_lines = 0
nb_pass6_lines = 0
nb_pass7_lines = 0
=> sum_lines_per_pass = 1 (which is equal to height 1)
This means that the uncompressed data length should be the same for a 1 pixel PNG image whether interlaced or not (particular case).

Conclusion

The conclusion is that the uncompressed data length of identical PNG images (with pixel size >= 8) will almost always be greater in the interlaced case because more filter types must be described in the file. Though, the compression (deflate) applied to the PNG data can lead to some particular cases if we compare the resulting PNG file sizes.

Leave a comment

Your email address will not be published. Required fields are marked *