1
1
# SPDX-FileCopyrightText: 2019 Dave Astels for Adafruit Industries
2
+ # SPDX-FileCopyrightText: 2022 Matt Land
2
3
#
3
4
# SPDX-License-Identifier: MIT
4
5
10
11
Make a screenshot (the contents of a displayio.Display) and save in a BMP file.
11
12
12
13
13
- * Author(s): Dave Astels
14
+ * Author(s): Dave Astels, Matt Land
14
15
15
16
Implementation Notes
16
17
--------------------
32
33
import board
33
34
from displayio import Bitmap , Palette , Display
34
35
36
+ try :
37
+ from typing import Tuple , Optional , Union
38
+ from io import BufferedWriter
39
+ except ImportError :
40
+ pass
41
+
35
42
__version__ = "0.0.0-auto.0"
36
43
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BitmapSaver.git"
37
44
38
45
39
- def _write_bmp_header (output_file , filesize ) :
46
+ def _write_bmp_header (output_file : BufferedWriter , filesize : int ) -> None :
40
47
output_file .write (bytes ("BM" , "ascii" ))
41
48
output_file .write (struct .pack ("<I" , filesize ))
42
49
output_file .write (b"\00 \x00 " )
43
50
output_file .write (b"\00 \x00 " )
44
51
output_file .write (struct .pack ("<I" , 54 ))
45
52
46
53
47
- def _write_dib_header (output_file , width , height ) :
54
+ def _write_dib_header (output_file : BufferedWriter , width : int , height : int ) -> None :
48
55
output_file .write (struct .pack ("<I" , 40 ))
49
56
output_file .write (struct .pack ("<I" , width ))
50
57
output_file .write (struct .pack ("<I" , height ))
@@ -54,44 +61,50 @@ def _write_dib_header(output_file, width, height):
54
61
output_file .write (b"\x00 " )
55
62
56
63
57
- def _bytes_per_row (source_width ) :
64
+ def _bytes_per_row (source_width : int ) -> int :
58
65
pixel_bytes = 3 * source_width
59
66
padding_bytes = (4 - (pixel_bytes % 4 )) % 4
60
67
return pixel_bytes + padding_bytes
61
68
62
69
63
- def _rotated_height_and_width (pixel_source ) :
70
+ def _rotated_height_and_width (pixel_source : Union [ Bitmap , Display ]) -> Tuple [ int , int ] :
64
71
# flip axis if the display is rotated
65
72
if isinstance (pixel_source , Display ) and (pixel_source .rotation % 180 != 0 ):
66
- return ( pixel_source .height , pixel_source .width )
67
- return ( pixel_source .width , pixel_source .height )
73
+ return pixel_source .height , pixel_source .width
74
+ return pixel_source .width , pixel_source .height
68
75
69
76
70
- def _rgb565_to_bgr_tuple (color ) :
71
- blue = (color << 3 ) & 0x00F8 # extract each of the RGB tripple into it's own byte
77
+ def _rgb565_to_bgr_tuple (color : int ) -> Tuple [ int , int , int ] :
78
+ blue = (color << 3 ) & 0x00F8 # extract each of the RGB triple into it's own byte
72
79
green = (color >> 3 ) & 0x00FC
73
80
red = (color >> 8 ) & 0x00F8
74
- return ( blue , green , red )
81
+ return blue , green , red
75
82
76
83
77
84
# pylint:disable=too-many-locals
78
- def _write_pixels (output_file , pixel_source , palette ):
85
+ def _write_pixels (
86
+ output_file : BufferedWriter ,
87
+ pixel_source : Union [Bitmap , Display ],
88
+ palette : Optional [Palette ],
89
+ ) -> None :
79
90
saving_bitmap = isinstance (pixel_source , Bitmap )
80
91
width , height = _rotated_height_and_width (pixel_source )
81
92
row_buffer = bytearray (_bytes_per_row (width ))
82
93
for y in range (height , 0 , - 1 ):
83
94
buffer_index = 0
84
95
if saving_bitmap :
96
+ # pixel_source: Bitmap
85
97
for x in range (width ):
86
- pixel = pixel_source [x , y - 1 ]
87
- color = palette [pixel ]
98
+ pixel = pixel_source [x , y - 1 ] # type: ignore
99
+ color = palette [pixel ] # type: ignore # handled by save_pixel's guardians
88
100
for _ in range (3 ):
89
101
row_buffer [buffer_index ] = color & 0xFF
90
102
color >>= 8
91
103
buffer_index += 1
92
104
else :
105
+ # pixel_source: Display
93
106
result_buffer = bytearray (2048 )
94
- data = pixel_source .fill_row (y - 1 , result_buffer )
107
+ data = pixel_source .fill_row (y - 1 , result_buffer ) # type: ignore
95
108
for i in range (width ):
96
109
pixel565 = (data [i * 2 ] << 8 ) + data [i * 2 + 1 ]
97
110
for b in _rgb565_to_bgr_tuple (pixel565 ):
@@ -106,7 +119,11 @@ def _write_pixels(output_file, pixel_source, palette):
106
119
# pylint:enable=too-many-locals
107
120
108
121
109
- def save_pixels (file_or_filename , pixel_source = None , palette = None ):
122
+ def save_pixels (
123
+ file_or_filename : Union [str , BufferedWriter ],
124
+ pixel_source : Union [Display , Bitmap ] = None ,
125
+ palette : Palette = None ,
126
+ ) -> None :
110
127
"""Save pixels to a 24 bit per pixel BMP file.
111
128
If pixel_source if a displayio.Bitmap, save it's pixels through palette.
112
129
If it's a displayio.Display, a palette isn't required.
@@ -116,10 +133,10 @@ def save_pixels(file_or_filename, pixel_source=None, palette=None):
116
133
:param palette: the Palette to use for looking up colors in the bitmap
117
134
"""
118
135
if not pixel_source :
119
- if "DISPLAY" in dir (board ):
120
- pixel_source = board .DISPLAY
121
- else :
136
+ if "DISPLAY" not in dir (board ):
122
137
raise ValueError ("Second argument must be a Bitmap or Display" )
138
+ pixel_source = board .DISPLAY
139
+
123
140
if isinstance (pixel_source , Bitmap ):
124
141
if not isinstance (palette , Palette ):
125
142
raise ValueError ("Third argument must be a Palette for a Bitmap save" )
0 commit comments