Skip to content

Commit 782f54f

Browse files
committed
Integrate saving bitmaps and taking screenshots
1 parent e1dc0f2 commit 782f54f

File tree

3 files changed

+115
-30
lines changed

3 files changed

+115
-30
lines changed

adafruit_bitmapsaver.py

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
`adafruit_bitmapsaver`
2424
================================================================================
2525
26-
Save a displayio.Bitmap (and associated displayio.Palette) into a BMP file.
26+
Save a displayio.Bitmap (and associated displayio.Palette) in a BMP file.
27+
Make a screenshot (the contents of a displayio.Display) and save in a BMP file.
2728
2829
2930
* Author(s): Dave Astels
@@ -43,8 +44,10 @@
4344

4445
# imports
4546

47+
import gc
4648
import struct
47-
from displayio import Bitmap, Palette
49+
import board
50+
from displayio import Bitmap, Palette, Display
4851

4952
__version__ = "0.0.0-auto.0"
5053
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BitmapSaver.git"
@@ -58,53 +61,72 @@ def _write_bmp_header(output_file, filesize):
5861
output_file.write(b'\00\x00')
5962
output_file.write(struct.pack('<I', 54))
6063

61-
def _write_dib_header(output_file, bitmap):
64+
def _write_dib_header(output_file, pixel_source):
6265
output_file.write(struct.pack('<I', 40))
63-
output_file.write(struct.pack('<I', bitmap.width))
64-
output_file.write(struct.pack('<I', bitmap.height))
66+
output_file.write(struct.pack('<I', pixel_source.width))
67+
output_file.write(struct.pack('<I', pixel_source.height))
6568
output_file.write(struct.pack('<H', 1))
6669
output_file.write(struct.pack('<H', 24))
6770
output_file.write(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
6871

69-
def _bytes_per_row(bitmap):
70-
pixel_bytes = 3 * bitmap.width
72+
def _bytes_per_row(pixel_source):
73+
pixel_bytes = 3 * pixel_source.width
7174
padding_bytes = (4 - (pixel_bytes % 4)) % 4
7275
return pixel_bytes + padding_bytes
7376

74-
def _write_pixels(output_file, bitmap, palette):
75-
row_buffer = bytearray(_bytes_per_row(bitmap))
77+
def rgb565_to_bgr_tuple(color):
78+
blue = (color << 3) & 0x00F8 # extract each of the RGB tripple into it's own byte
79+
green = (color >> 3) & 0x00FC
80+
red = (color >> 8) & 0x00F8
81+
return (blue, green, red)
7682

77-
for y in range(bitmap.height, 0, -1):
83+
def _write_pixels(output_file, pixel_source, palette):
84+
row_buffer = bytearray(_bytes_per_row(pixel_source))
85+
saving_bitmap = isinstance(pixel_source, Bitmap)
86+
for y in range(pixel_source.height, 0, -1):
7887
buffer_index = 0
79-
for x in range(bitmap.width):
80-
pixel = bitmap[x, y-1]
81-
color = palette[pixel]
82-
for _ in range(3):
83-
row_buffer[buffer_index] = color & 0xFF
84-
color >>= 8
85-
buffer_index += 1
88+
if saving_bitmap:
89+
for x in range(pixel_source.width):
90+
pixel = pixel_source[x, y-1]
91+
color = palette[pixel]
92+
for _ in range(3):
93+
row_buffer[buffer_index] = color & 0xFF
94+
color >>= 8
95+
buffer_index += 1
96+
else:
97+
data = pixel_source.fill_area(x=0, y=y-1, width=pixel_source.width, height=1)
98+
for i in range(pixel_source.width):
99+
pixel565 = (data[i * 2] << 8) + data[i * 2 + 1]
100+
for b in rgb565_to_bgr_tuple(pixel565):
101+
row_buffer[buffer_index] = b & 0xFF
102+
buffer_index += 1
86103
output_file.write(row_buffer)
104+
gc.collect()
87105

88-
def save_bitmap(bitmap, palette, file_or_filename):
89-
"""Save a bitmap (using an associated palette) to a 24 bit per pixel BMP file.
106+
def save_pixels(file_or_filename, pixel_source=board.DISPLAY, palette=None):
107+
"""Save pixels to a 24 bit per pixel BMP file.
108+
If pixel_source if a displayio.Bitmap, save it's pixels through palette.
109+
If it's a displayio.Display, a palette isn't required.
90110
91-
:param bitmap: the displayio.Bitmap to save
92-
:param palette: the displayio.Palette to use for looking up colors in the bitmap
111+
:param file_or_filename: either the file to save to, or it's absolute name
112+
:param pixel_source: the Bitmap or Display to save
113+
:param palette: the Palette to use for looking up colors in the bitmap
93114
"""
94-
if not isinstance(bitmap, Bitmap):
95-
raise ValueError('First argument must be a Bitmap')
96-
if not isinstance(palette, Palette):
97-
raise ValueError('Second argument must be a Palette')
115+
if isinstance(pixel_source, Bitmap):
116+
if not isinstance(palette, Palette):
117+
raise ValueError('Third argument must be a Palette for a Bitmap save')
118+
elif not isinstance(pixel_source, Display):
119+
raise ValueError('Second argument must be a Bitmap or Display')
98120
try:
99121
if isinstance(file_or_filename, str):
100122
output_file = open(file_or_filename, 'wb')
101123
else:
102124
output_file = file_or_filename
103125

104-
filesize = 54 + bitmap.height * _bytes_per_row(bitmap)
126+
filesize = 54 + pixel_source.height * _bytes_per_row(pixel_source)
105127
_write_bmp_header(output_file, filesize)
106-
_write_dib_header(output_file, bitmap)
107-
_write_pixels(output_file, bitmap, palette)
128+
_write_dib_header(output_file, pixel_source)
129+
_write_pixels(output_file, pixel_source, palette)
108130
except Exception:
109131
raise
110132
else:

examples/bitmapsaver_simpletest.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,25 @@
1+
# The MIT License (MIT)
2+
#
3+
# Copyright (c) 2019 Dave Astels for Adafruit Industries
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in
13+
# all copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
# THE SOFTWARE.
22+
123
"""Example of using save_bitmap"""
224

325
import board
@@ -6,7 +28,7 @@
628
from displayio import Bitmap, Palette
729
import adafruit_sdcard
830
import storage
9-
from adafruit_bitmapsaver import save_bitmap
31+
from adafruit_bitmapsaver import save_pixels
1032

1133
#pylint:disable=invalid-name
1234

@@ -47,4 +69,4 @@
4769
bitmap[x, y] = 0
4870

4971
print('Saving bitmap')
50-
save_bitmap(bitmap, palette, '/sd/test.bmp')
72+
save_pixels('/sd/test.bmp', bitmap, palette)

examples/screenshot_simpletest.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# The MIT License (MIT)
2+
#
3+
# Copyright (c) 2019 Dave Astels for Adafruit Industries
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in
13+
# all copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
# THE SOFTWARE.
22+
23+
"""Example of taking a screenshot."""
24+
25+
import board
26+
import digitalio
27+
import busio
28+
import adafruit_sdcard
29+
import storage
30+
from adafruit_bitmapsaver import save_pixels
31+
32+
33+
spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO)
34+
cs = digitalio.DigitalInOut(board.SD_CS)
35+
sdcard = adafruit_sdcard.SDCard(spi, cs)
36+
vfs = storage.VfsFat(sdcard)
37+
storage.mount(vfs, "/sd")
38+
39+
print('Taking Screenshot...')
40+
save_pixels('/sd/screenshot.bmp')
41+
print('Screenshot taken')

0 commit comments

Comments
 (0)