- learning Python
- reading comprehension of raw documentation without any additional help
- training coding structure for big projects
- seemed cool to decode pixels of a PNG file and printing the color in terminal
This project wants to be a rudimentary PNG reader for educational purposes, a PNG decoder so to speak. Everything is implemented by me manually, without additional help from external libraries. I am aware that it is slow being written in Python.
Informations that you can get:
- Resolution of the file (H x W)
- Color type (0, 2, 3, 4, 6)
- Bit depth (1, 2, 4, 8, 16, according to the Color Type)
- Interlace (True / False)
- Chunks that build up the file
- Number of blocks in the ZLIB format
- Number of distinct pixels and the pixels themselves
- Find out if the file is invalid / corrupted via exceptions in the running phase
Pixel format: colored_square #RGB_code Opacity_percent
RGB_code is in the following hex format: hex(R_value)hex(G_value)hex(B_value)
Run main.py:
python main.py [path_to_png]
There are 5 pixel classes, representing each Color Type. Each class has the same format so that they can be casted into a dictionary table in pairs: (color_type : pixel_color_type_class)
The implementation of each class has:
- a constructor that regards bit depth
- a reading generator function, that can facilitate the reading of bits
Supported chunks: IHDR, PLTE, IDAT, IEND
- There is room for scalability. You can further add support for other types of chunks by creating child classes of the
Chunkclass inchunks.py. IHDRis straightforward in implementation, following the format and reading byte by byte the data.PLTEdoes the same, with the mention that it usesPixel_2as palette entry. In fact, the palette is an array of size2^color_depthofPixel_2.IDATis reading byte by byte.
Putting IDAT data chunks together, the decompression starts by decoding each block of data.
- Each block uses 2 tries data structures for Huffman decodings:
- one for literal/length
- another one for distance
The trie class has a constructor that creates a Huffman Trie with the restriction: |huffman(alphabet_i)| = length_i
with a sorted alphabet.
- The trie itself is implemented offline with static memory. Each node of the trie is an index into an array with an upper bound of
sum(length_i). - Huffman codes use numbers in base 2 for representation.
- For dynamic compression blocks, a third trie is created for decoding the lengths arrays.
- For reading bits in LSB → MSB order, a generator function is used to facilitate that, keeping track of the position in the byte.
Lengths and distances information are placed into 2 arrays structured together under the Table class:
extra_bitslengths
- Every interpretation of the code is unique.
- Codes with the same length must be lexicographically the same as their value in the alphabet.
- Codes with shorter length must be lexicographically smaller than the ones with bigger length.
- This guarantees uniqueness.
- It disregards the need of knowing the alphabet in the encoding.
- The tries themselves are well balanced, leading to approximately
log2(n)maximum code length, wheren = |alphabet|.
After the block is decompressed, the tries are deleted from memory and created again in the next run.
Not supported.
Used ANSII Escape Code:
Changing background color: ESC[48;2;R;G;Bm
Changing foreground color: ESC[38;2;R;G;Bm
Reseting: ESC[m
Because of the limited number of bits that are supported in the terminal, the colors are capped in 8 bits. So for 16 bit colors, they will suffer a loss of quality in the terminal











