Gallery: BMP support
Signed-off-by: Francesco Gazzetta <fgaz@fgaz.me>
This commit is contained in:
parent
eba94cd4f6
commit
61d9dbce7c
1 changed files with 61 additions and 15 deletions
|
@ -9,24 +9,25 @@ An application that shows images stored in the filesystem.
|
|||
.. figure:: res/GalleryApp.png
|
||||
:width: 179
|
||||
|
||||
Only 240x240 images are supported for now.
|
||||
The images have to be uploaded in the gallery directory.
|
||||
The images have to be encoded as raw RGB565 data in big endian byte order.
|
||||
To encode them, you can use ffmpeg:
|
||||
The images have to be uploaded in the "gallery" directory.
|
||||
The images have to be encoded as BMP RGB565 data in big endian byte order.
|
||||
To encode them, you can use GIMP (File → Export, select the BMP format,
|
||||
set "R5 G6 B5" in "Advanced Options"), or ImageMagick:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
ffmpeg -vcodec png -i my_image.png -vcodec rawvideo -f rawvideo -pix_fmt rgb565be my_image.rgb565
|
||||
convert -define bmp:subtype=RGB565 my_image.png my_image.bmp
|
||||
|
||||
And to upload:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
./tools/wasptool --binary --upload my_image.rgb565 --as gallery/my_image
|
||||
./tools/wasptool --binary --upload my_image.bmp --as gallery/my_image
|
||||
"""
|
||||
|
||||
import wasp
|
||||
import icons
|
||||
from apps.pager import PagerApp
|
||||
|
||||
class GalleryApp():
|
||||
NAME = 'Gallery'
|
||||
|
@ -84,6 +85,12 @@ class GalleryApp():
|
|||
self.index = (self.index + increment) % len(self.files)
|
||||
self._draw()
|
||||
|
||||
def _invalid_file(self, filename):
|
||||
draw = wasp.watch.drawable
|
||||
draw.string('Invalid BMP file', 0, 10, width=240)
|
||||
draw.blit(self.ICON, 72, 72)
|
||||
draw.line(72,52, 168,148, 3, 0xf800)
|
||||
|
||||
def _draw(self):
|
||||
draw = wasp.watch.drawable
|
||||
draw.fill()
|
||||
|
@ -97,17 +104,56 @@ class GalleryApp():
|
|||
draw.string(filename[:(draw.wrap(filename, 240)[1])], 0, 200)
|
||||
file = open("gallery/{}".format(filename), "rb")
|
||||
display = wasp.watch.display
|
||||
display.set_window(0, 0, 240, 240)
|
||||
|
||||
# check that we are reading a RGB565 BMP
|
||||
magic = file.read(2)
|
||||
if magic != b'BM': # check BMP magic number
|
||||
self._invalid_file(filename)
|
||||
return
|
||||
file.seek(0x0A)
|
||||
data_offset = int.from_bytes(file.read(4), 'little')
|
||||
file.seek(0x0E)
|
||||
dib_len = int.from_bytes(file.read(4), 'little')
|
||||
if dib_len != 124: # check header V5
|
||||
self._invalid_file(filename)
|
||||
return
|
||||
width = int.from_bytes(file.read(4), 'little')
|
||||
height = int.from_bytes(file.read(4), 'little')
|
||||
# width and height are signed, but only height can actually be negative
|
||||
if height >= 2147483648:
|
||||
height = 4294967296 - height
|
||||
bottom_up = False
|
||||
else: bottom_up = True
|
||||
if width > 240 or height > 240: # check size <= 240x240
|
||||
self._invalid_file(filename)
|
||||
return
|
||||
file.seek(0x1C)
|
||||
bit_count = int.from_bytes(file.read(2), 'little')
|
||||
if bit_count != 16: # check 16 bpp
|
||||
self._invalid_file(filename)
|
||||
return
|
||||
compression = int.from_bytes(file.read(4), 'little')
|
||||
if compression != 3: # check bitmask mode
|
||||
self._invalid_file(filename)
|
||||
return
|
||||
file.seek(0x36)
|
||||
bitmask = file.read(4), file.read(4), file.read(4)
|
||||
if bitmask != (b'\x00\xF8\x00\x00', b'\xE0\x07\x00\x00', b'\x1F\x00\x00\x00'): # check bitmask RGB565
|
||||
self._invalid_file(filename)
|
||||
return
|
||||
|
||||
display.set_window((240 - width) // 2, 0, width, height)
|
||||
|
||||
file.seek(data_offset)
|
||||
|
||||
# We don't have enough memory to load the entire image at once, so
|
||||
# we stream it from flash memory to the display
|
||||
#TODO: why can't we do it in a single quick write session?
|
||||
#display.quick_start()
|
||||
buf = display.linebuffer[:2*240]
|
||||
read = 1
|
||||
while read > 0:
|
||||
read = file.readinto(buf)
|
||||
#display.quick_write(buf)
|
||||
buf = display.linebuffer[:2*width]
|
||||
for y in reversed(range(0, height)):
|
||||
if bottom_up: file.seek(data_offset + y * width * 2)
|
||||
file.readinto(buf)
|
||||
for x in range(0, width):
|
||||
buf[x*2], buf[x*2+1] = buf[x*2+1], buf[x*2]
|
||||
display.write_data(buf)
|
||||
#display.quick_end()
|
||||
|
||||
file.close()
|
||||
|
|
Loading…
Reference in a new issue