g13gui: Add LPBM image conversion and tests

This is the first step to actually rendering to the G13's display by way of the
g13d daemon. The bitmap format is kinda weird: it's actually 860 bytes of
useable pixel data, but padded out to 960. Each byte corresponds with one
vertical column of 8-pixels, going from top to bottom.

The tests for this effectively check to make sure the length is correct, and
also dumps it out to a running g13d (if there is one). This should probably be
automated a bit more by checking the bytes directly, but given that this is a
visual task anyway, I'm being a bit lazy.
This commit is contained in:
June Tate-Gans 2021-04-29 20:20:15 -05:00
parent a38048ea9f
commit aeced13908
5 changed files with 106 additions and 23 deletions

View File

@ -1,3 +1,4 @@
from g13gui.bitwidgets.display import Fonts from g13gui.bitwidgets.fonts import Fonts
from g13gui.bitwidgets.display import FontManager from g13gui.bitwidgets.fonts import FontManager
from g13gui.bitwidgets.display import Display from g13gui.bitwidgets.display import Display
from g13gui.bitwidgets.display import DisplayMetrics

View File

@ -1,12 +1,51 @@
import struct
import PIL.ImageDraw
import PIL.PyAccess
import sys
from io import BytesIO
from PIL import Image from PIL import Image
from PIL.ImageDraw import ImageDraw
from g13gui.observer import Subject from g13gui.observer import Subject
from g13gui.observer import ChangeType from g13gui.observer import ChangeType
class DisplayMetrics(object): class DisplayMetrics(object):
WIDTH_PIXELS = 160 WIDTH_PIXELS = 160
HEIGHT_PIXELS = 48 HEIGHT_PIXELS = 43
LPBM_LENGTH = 960
def ImageToLPBM(image):
i = PIL.PyAccess.new(image, readonly=True)
bio = BytesIO()
maxBytes = (DisplayMetrics.WIDTH_PIXELS * DisplayMetrics.HEIGHT_PIXELS // 8)
row = 0
col = 0
for byteNum in range(0, maxBytes):
b = int()
if row == 40:
maxSubrow = 3
else:
maxSubrow = 8
for subrow in range(0, maxSubrow):
b |= i[col, row + subrow] << subrow
bio.write(struct.pack('<B', b))
col += 1
if (col % 160) == 0:
col = 0
row += 8
# padding? lpbm files are always 960 bytes, the last bytes are filled with garbage.
bio.write(bytes(LPBM_LENGTH - maxBytes))
return bio.getvalue()
class Display(Subject): class Display(Subject):
@ -17,7 +56,7 @@ class Display(Subject):
DisplayMetrics.HEIGHT_PIXELS)) DisplayMetrics.HEIGHT_PIXELS))
def getContext(self): def getContext(self):
return ImageDraw.Draw(self._bitmap) return PIL.ImageDraw.Draw(self._bitmap)
def commit(self): def commit(self):
# convert to LPBM # convert to LPBM

View File

@ -1,20 +1,29 @@
import unittest
import PIL.Image
from g13gui.bitwidgets.display import LPBM_LENGTH
from g13gui.bitwidgets.display import ImageToLPBM
from g13gui.bitwidgets import Fonts from g13gui.bitwidgets import Fonts
from g13gui.bitwidgets import FontManager from g13gui.bitwidgets import FontManager
from g13gui.bitwidgets import Display from g13gui.bitwidgets import Display
from g13gui.bitwidgets import DisplayMetrics
d = Display() class DisplayTests(unittest.TestCase):
ctx = d.getContext() def setUp(self):
ctx.text((0, 0), "Hello world!", self.d = Display()
font=FontManager.getFont(Fonts.TINY),
fill=(1)) def testConversion(self):
ctx.text((0, 6), "Hello world!", ctx = self.d.getContext()
font=FontManager.getFont(Fonts.MEDIUM), ctx.text((0, 0), "Hello world!",
fill=(1)) font=FontManager.getFont(Fonts.HUGE), fill=1)
ctx.text((0, 11), "Hello world!", result = ImageToLPBM(self.d._bitmap)
font=FontManager.getFont(Fonts.LARGE),
fill=(1)) self.assertEqual(len(result), LPBM_LENGTH)
ctx.text((0, 19), "Hello world!",
font=FontManager.getFont(Fonts.HUGE), with open('/run/g13d/in', 'wb') as fp:
fill=(1)) fp.write(result)
d.debug()
if __name__ == '__main__':
unittest.main()

View File

@ -26,9 +26,10 @@ X11_FONT_PATH = pathlib.Path('/usr/share/fonts/X11/misc')
class Fonts(enum.Enum): class Fonts(enum.Enum):
TINY = X11_FONT_PATH / '4x6.pcf.gz' TINY = X11_FONT_PATH / '4x6.pcf.gz'
MEDIUM = X11_FONT_PATH / '5x7.pcf.gz' SMALL = X11_FONT_PATH / '5x7.pcf.gz'
LARGE = X11_FONT_PATH / '8x13.pcf.gz' MEDIUM = X11_FONT_PATH / '8x13.pcf.gz'
HUGE = X11_FONT_PATH / '9x18.pcf.gz' LARGE = X11_FONT_PATH / '9x18.pcf.gz'
HUGE = X11_FONT_PATH / '10x20.pcf.gz'
class PcfFontConverter(PcfFontFile): class PcfFontConverter(PcfFontFile):

View File

@ -0,0 +1,33 @@
#!/usr/bin/python
import unittest
from g13gui.bitwidgets import Fonts
from g13gui.bitwidgets import FontManager
from g13gui.bitwidgets import Display
class FontsTests(unittest.TestCase):
def testFontDrawing(self):
d = Display()
ctx = d.getContext()
ctx.text((0, 0), "Hello world!",
font=FontManager.getFont(Fonts.TINY),
fill=(1))
ctx.text((0, 6), "Hello world!",
font=FontManager.getFont(Fonts.SMALL),
fill=(1))
ctx.text((0, 11), "Hello world!",
font=FontManager.getFont(Fonts.MEDIUM),
fill=(1))
ctx.text((0, 19), "Hello world!",
font=FontManager.getFont(Fonts.LARGE),
fill=(1))
ctx.text((0, 31), "Hello world!",
font=FontManager.getFont(Fonts.HUGE),
fill=(1))
d.debug()
if __name__ == '__main__':
unittest.main()