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
|
.. figure:: res/GalleryApp.png
|
||||||
:width: 179
|
:width: 179
|
||||||
|
|
||||||
Only 240x240 images are supported for now.
|
The images have to be uploaded in the "gallery" directory.
|
||||||
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.
|
||||||
The images have to be encoded as raw RGB565 data in big endian byte order.
|
To encode them, you can use GIMP (File → Export, select the BMP format,
|
||||||
To encode them, you can use ffmpeg:
|
set "R5 G6 B5" in "Advanced Options"), or ImageMagick:
|
||||||
|
|
||||||
.. code-block:: sh
|
.. 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:
|
And to upload:
|
||||||
|
|
||||||
.. code-block:: sh
|
.. 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 wasp
|
||||||
import icons
|
import icons
|
||||||
|
from apps.pager import PagerApp
|
||||||
|
|
||||||
class GalleryApp():
|
class GalleryApp():
|
||||||
NAME = 'Gallery'
|
NAME = 'Gallery'
|
||||||
|
@ -84,6 +85,12 @@ class GalleryApp():
|
||||||
self.index = (self.index + increment) % len(self.files)
|
self.index = (self.index + increment) % len(self.files)
|
||||||
self._draw()
|
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):
|
def _draw(self):
|
||||||
draw = wasp.watch.drawable
|
draw = wasp.watch.drawable
|
||||||
draw.fill()
|
draw.fill()
|
||||||
|
@ -97,17 +104,56 @@ class GalleryApp():
|
||||||
draw.string(filename[:(draw.wrap(filename, 240)[1])], 0, 200)
|
draw.string(filename[:(draw.wrap(filename, 240)[1])], 0, 200)
|
||||||
file = open("gallery/{}".format(filename), "rb")
|
file = open("gallery/{}".format(filename), "rb")
|
||||||
display = wasp.watch.display
|
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 don't have enough memory to load the entire image at once, so
|
||||||
# we stream it from flash memory to the display
|
# we stream it from flash memory to the display
|
||||||
#TODO: why can't we do it in a single quick write session?
|
buf = display.linebuffer[:2*width]
|
||||||
#display.quick_start()
|
for y in reversed(range(0, height)):
|
||||||
buf = display.linebuffer[:2*240]
|
if bottom_up: file.seek(data_offset + y * width * 2)
|
||||||
read = 1
|
file.readinto(buf)
|
||||||
while read > 0:
|
for x in range(0, width):
|
||||||
read = file.readinto(buf)
|
buf[x*2], buf[x*2+1] = buf[x*2+1], buf[x*2]
|
||||||
#display.quick_write(buf)
|
|
||||||
display.write_data(buf)
|
display.write_data(buf)
|
||||||
#display.quick_end()
|
|
||||||
file.close()
|
file.close()
|
||||||
|
|
Loading…
Reference in a new issue