#!/usr/bin/python3
# ===============================================================
# read a BMP file and paint it into a graphics window
#
# The graphics.py module is slow. That is OK because
# this is an exercise in learning "stuff".
# ===============================================================
import graphics as gr
import user_interface as ui
# ---------------------------------------------------------------
# ---- create graphics window
# ---------------------------------------------------------------
def create_graphics_window(width,height):
win = gr.GraphWin('BMP Image',width,height)
win.setBackground('white')
return win
# ---------------------------------------------------------------
# ---- open BMP file and read it directly into a byte array
# ---------------------------------------------------------------
def load_bmp_to_array(bmp_file_path:str) -> bytearray:
with open(bmp_file_path,'rb') as file:
bmp_bytes = file.read()
return bmp_bytes
# ---------------------------------------------------------------
# ---- calculate row padding
# ----
# ---- Note: The pixel format is defined by the DIB header or
# ---- extra bit masks. Each row in the Pixel array is
# ---- padded to a multiple of 4 bytes in size.
# ---------------------------------------------------------------
def calculate_row_padding(width):
match (width * 3) % 4:
case 3:
return 1
case 2:
return 2
case 1:
return 3
return 0
# ---------------------------------------------------------------
# ---- copy pixels to the graphics window
# ---------------------------------------------------------------
def copy_image_to_window(win,width,height,offset,bary):
# ---- byte array index padding for the end of each row
padding = calculate_row_padding(width)
print(f'padding = {padding}')
# ---- byte array index
byt_idx = offset
# ---- image is upside down and should be reversed
# ---- thus the -1 increment
for y in range(height-1,0,-1):
for x in range(width):
# ---- RGB colors
b = int.from_bytes(bary[byt_idx :byt_idx+1],'little')
g = int.from_bytes(bary[byt_idx+1:byt_idx+2],'little')
r = int.from_bytes(bary[byt_idx+2:byt_idx+3],'little')
# ---- plot (draw) pixel
color = gr.color_rgb(r,g,b)
win.plotPixel(x,y,color)
# ---- next pixel
byt_idx += 3
# ---- end of row - add padding to get
# ---- the start of next row
byt_idx += padding
return
# ---------------------------------------------------------------
# ---- main
# ---------------------------------------------------------------
if __name__ == '__main__':
##file_path = 'bmp_all_red.bmp' # 25 x 27
file_path = 'mona_lisa_small_a.bmp' # 100 x 149
##file_path = 'mona_lisa_small_b.bmp' # 149 x 100
bmp_bytes = load_bmp_to_array(file_path)
image_height = int.from_bytes(bmp_bytes[22:26],'little')
image_size = int.from_bytes(bmp_bytes[34:38],'little')
image_offset = int.from_bytes(bmp_bytes[10:14],'little')
image_width = int.from_bytes(bmp_bytes[18:22],'little')
print()
print(f'{len(bmp_bytes)} from file {file_path}')
print()
print(f'image offset = {image_offset}')
print(f'image width = {image_width}')
print(f'image height = {image_height}')
print(f'image size = {image_size}')
win = create_graphics_window(image_width,image_height)
copy_image_to_window(win,image_width,image_height,
image_offset,bmp_bytes)
ui.pause()
print()