import bmp and export file

This commit is contained in:
Klemek
2021-08-25 23:53:35 +02:00
parent 5d13db7a09
commit 9a955b9062
+80 -24
View File
@@ -88,26 +88,45 @@ class Bitmap:
def read_bmp(cls, path: str) -> Tuple[int, int, int, bytes]:
with open(path, mode="rb") as f:
bmp_data = f.read()
width, height, color_depth, data_start, data_size = cls.read_header(
bmp_data
)
# TODO read data
width, height, color_depth, data_start, data_size = cls.__read_header(bmp_data)
content_data = bmp_data[data_start:]
if data_size > 0:
content_data = content_data[:data_size]
output_data = cls.__read_formated_data(width, height, color_depth, content_data)
return width, height, color_depth, output_data
@classmethod
def read_header(cls, bmp_data: bytes) -> Tuple[int, int, int, int, int]:
def __read_header(cls, bmp_data: bytes) -> Tuple[int, int, int, int, int]:
if bmp_data[0:2].decode() != "BM":
raise BitmapError("Not a Bitmap Image")
if int.from_bytes(bmp_data[30:34], byteorder="little") != 0:
raise BitmapError("Cannot read Bitmap: compression")
raise BitmapError("Cannot read Bitmap: need no compression")
if int.from_bytes(bmp_data[26:28], byteorder="little") != 1:
raise BitmapError("Cannot read Bitmap: color panes")
raise BitmapError("Cannot read Bitmap: need 1 color panes")
width = int.from_bytes(bmp_data[18:22], byteorder="little")
height = int.from_bytes(bmp_data[22:28], byteorder="little")
height = int.from_bytes(bmp_data[22:26], byteorder="little")
color_depth = int.from_bytes(bmp_data[28:30], byteorder="little") // 8
if color_depth < 1:
raise BitmapError("Cannot read Bitmap: bits per pixels is < 8")
data_start = int.from_bytes(bmp_data[10:14], byteorder="little")
data_size = int.from_bytes(bmp_data[34:38], byteorder="little")
return width, height, color_depth, data_start, data_size
@classmethod
def __read_formated_data(
cls, width: int, height: int, color_depth: int, bmp_data: bytes
) -> bytes:
line_padding = (width * color_depth) % 4
if line_padding > 0:
real_width = width * color_depth + (4 - line_padding)
else:
real_width = width * color_depth
output_data = bytes()
for y in range(height):
start = (height - y - 1) * real_width
output_data += bmp_data[start : start + width * color_depth]
return output_data
class Image:
def __init__(self, name: str, width: int, height: int, empty: bool = False) -> None:
@@ -129,25 +148,23 @@ class Image:
width -= 1
self.width = width
self.height = pixels // width
print(f"image '{self.name}': {self.width}x{self.height}")
def add_data(self, raw_data: List[str]) -> None:
for v in raw_data:
self.data += [int(v, 16)]
def get_position(self, x: int, y: int) -> int:
def __get_position(self, x: int, y: int) -> int:
real_width = (len(self.data) * 8) // self.height
return y * real_width + x
def get_pixel(self, x: int, y: int) -> bool:
position = self.get_position(x, y)
position = self.__get_position(x, y)
chunk_id = position // 8
return self.data[chunk_id] & (1 << (7 - position % 8)) > 0
def set_pixel(self, x: int, y: int, v: bool) -> None:
position = self.get_position(x, y)
position = self.__get_position(x, y)
chunk_id = position // 8
byte = pow(2, 7 - position % 8)
if v != self.get_pixel(x, y):
if v:
self.data[chunk_id] |= 1 << (7 - position % 8)
@@ -155,7 +172,7 @@ class Image:
self.data[chunk_id] &= ~(1 << (7 - position % 8))
self.modified = True
def get_color_bytes(self) -> bytes:
def __get_color_bytes(self) -> bytes:
output = bytes()
for y in range(self.height):
for x in range(self.width):
@@ -166,11 +183,33 @@ class Image:
return output
def export_bmp(self, path: str) -> None:
Bitmap.write_bmp(path, self.width, 3, self.get_color_bytes())
Bitmap.write_bmp(path, self.width, 3, self.__get_color_bytes())
def __set_color_bytes(self, color_depth: int, data: bytes) -> None:
for y in range(self.height):
for x in range(self.width):
position = (y * self.width + x) * color_depth
colors = data[position : position + color_depth]
mean_color = sum(c for c in colors) / color_depth
if mean_color < 128:
self.set_pixel(x, y, True)
def import_bmp(self, path: str) -> None:
self.width, self.height, color_depth, data = Bitmap.read_bmp(path)
# TODO
self.width, self.height, color_depth, bmp_data = Bitmap.read_bmp(path)
self.data = [0] * ((self.width * self.height) // 8)
self.__set_color_bytes(color_depth, bmp_data)
def export_cpp(self) -> str:
# 16 per line
output = [
f"// '{self.name}', {self.width}x{self.height}px",
f"const unsigned char {self.name} [] PROGMEM = {{",
]
while len(self.data) > 16:
output += ["\t" + ", ".join(f"0x{v:02x}" for v in self.data[0:16]) + ","]
self.data = self.data[16:]
output += ["\t" + ", ".join(f"0x{v:02x}" for v in self.data), "};", ""]
return "\n".join(output)
class File:
@@ -226,6 +265,11 @@ class File:
return images
def export(self, path: str) -> None:
with open(path, mode="w") as f:
for image in self.images:
f.write(image.export_cpp())
class App(ttk.Frame):
def __init__(self, parent) -> None:
@@ -390,7 +434,6 @@ class App(ttk.Frame):
def update_canvas(self) -> None:
image = self.current_image
scale = 3
if image is None:
self.canvas.configure(
width=0,
@@ -475,7 +518,7 @@ class App(ttk.Frame):
def save_file(self, path: Optional[str] = None) -> None:
if path == "":
path = filedialog.asksaveasfilename()
# TODO
self.current_file.export(path)
self.open_file(path)
def open_file(self, path: Optional[str]) -> None:
@@ -501,7 +544,16 @@ class App(ttk.Frame):
pass # TODO
def import_bmp(self) -> None:
pass # TODO
if self.current_image is None:
return
path = filedialog.askopenfilename(
filetypes=Bitmap.FILE_TYPES,
defaultextension=Bitmap.FILE_TYPES,
)
if path is not None:
# TODO error handling
self.current_image.import_bmp(path)
self.update()
def export_bmp(self) -> None:
if self.current_image is None:
@@ -515,8 +567,12 @@ class App(ttk.Frame):
self.current_image.export_bmp(path)
if __name__ == "__main__":
app = App(Tk())
app.pack(fill="both", expand=True)
# if __name__ == "__main__":
# app = App(Tk())
# app.pack(fill="both", expand=True)
app.mainloop()
# app.mainloop()
f = File("tetris.h")
f.images[-1].import_bmp("tetrisbg2.bmp")
f.export("tetris2.h")