Python RGB Matrix games and animations https://www.xythobuz.de/ledmatrix_v2.html
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. #!/usr/bin/env python3
  2. # Uses the Python BDF format bitmap font parser:
  3. # https://github.com/tomchen/bdfparser
  4. #
  5. # And the pillow Python Imaging Library:
  6. # https://github.com/python-pillow/Pillow
  7. #
  8. # ----------------------------------------------------------------------------
  9. # "THE BEER-WARE LICENSE" (Revision 42):
  10. # <xythobuz@xythobuz.de> wrote this file. As long as you retain this notice
  11. # you can do whatever you want with this stuff. If we meet some day, and you
  12. # think this stuff is worth it, you can buy me a beer in return. Thomas Buck
  13. # ----------------------------------------------------------------------------
  14. from bdfparser import Font
  15. from PIL import Image
  16. import os
  17. import time
  18. class DrawText:
  19. def __init__(self, g, fg = (255, 255, 255), bg = (0, 0, 0), c = (0, 255, 0)):
  20. self.gui = g
  21. self.fg = fg
  22. self.bg = bg
  23. self.color = c
  24. scriptDir = os.path.dirname(os.path.realpath(__file__))
  25. fontDir = os.path.join(scriptDir, "fonts")
  26. self.fonts = {}
  27. for f in os.listdir(os.fsencode(fontDir)):
  28. filename = os.fsdecode(f)
  29. if not filename.lower().endswith(".bdf"):
  30. continue
  31. # TODO hard-coded per-font offsets and spacing adjustment
  32. offset = 0
  33. spacing = 0
  34. if filename == "iv18x16u.bdf":
  35. offset = 6
  36. elif filename == "ib8x8u.bdf":
  37. offset = 10
  38. elif filename == "lemon.bdf":
  39. offset = 8
  40. spacing = -3
  41. elif filename == "antidote.bdf":
  42. offset = 9
  43. spacing = -1
  44. elif filename == "uushi.bdf":
  45. offset = 8
  46. spacing = -2
  47. elif filename == "tom-thumb.bdf":
  48. offset = 12
  49. spacing = 2
  50. # center vertically, in case of
  51. # multiple stacked panels
  52. # TODO hard-coded offsets assume 32x32 panels
  53. if self.gui.height > 32:
  54. offset += 16
  55. font = Font(os.path.join(fontDir, filename))
  56. data = (font, offset, {}, spacing)
  57. self.fonts[filename[:-4]] = data
  58. def getGlyph(self, c, font):
  59. if not isinstance(font, str):
  60. # fall-back to first available font
  61. f, offset, cache, spacing = next(iter(self.fonts))
  62. else:
  63. # users font choice
  64. f, offset, cache, spacing = self.fonts[font]
  65. # only render glyphs once, cache resulting image data
  66. if not c in cache:
  67. g = f.glyph(c).draw()
  68. # invert color
  69. g = g.replace(1, 2).replace(0, 1).replace(2, 0)
  70. # render to pixel data
  71. bytesdict = {
  72. 0: int(self.fg[2] << 16 | self.fg[1] << 8 | self.fg[0]).to_bytes(4, byteorder = "little"),
  73. 1: int(self.bg[2] << 16 | self.bg[1] << 8 | self.bg[0]).to_bytes(4, byteorder = "little"),
  74. 2: int(self.color[2] << 16 | self.color[1] << 8 | self.color[0]).to_bytes(4, byteorder = "little"),
  75. }
  76. img = Image.frombytes('RGBA',
  77. (g.width(), g.height()),
  78. g.tobytes('RGBA', bytesdict))
  79. cache[c] = img
  80. return (cache[c], offset, spacing)
  81. def drawGlyph(self, g, xOff, yOff, spacing):
  82. if xOff >= self.gui.width:
  83. return
  84. for x in range(0, g.width):
  85. for y in range(0, g.height):
  86. xTarget = xOff + x
  87. if (xTarget < 0) or (xTarget >= self.gui.width):
  88. continue
  89. p = g.getpixel((x, y))
  90. self.gui.set_pixel(xTarget, yOff + y, p)
  91. for x in range(0, spacing):
  92. for y in range(0, g.height):
  93. self.gui.set_pixel(xOff + x + g.width, yOff + y, self.bg)
  94. def text(self, s, f, offset = 0, earlyAbort = True, yOff = 0):
  95. w = 0
  96. for c in s:
  97. xOff = -offset + w
  98. if earlyAbort:
  99. if xOff >= self.gui.width:
  100. break
  101. g, y, spacing = self.getGlyph(c, f)
  102. w += g.width + spacing
  103. if xOff >= -16: # some wiggle room so chars dont disappear
  104. self.drawGlyph(g, xOff, y + yOff, spacing)
  105. return w
  106. class ScrollText:
  107. def __init__(self, g, t, f, i = 1, s = 75, fg = (255, 255, 255), bg = (0, 0, 0)):
  108. self.gui = g
  109. self.drawer = DrawText(self.gui, fg, bg)
  110. self.iterations = i
  111. self.speed = 1.0 / s
  112. self.setText(t, f)
  113. self.restart()
  114. def setText(self, t, f):
  115. self.text = t
  116. self.font = f
  117. self.width = self.drawer.text(self.text, self.font, 0, False)
  118. def restart(self):
  119. self.offset = -self.gui.width
  120. self.last = time.time()
  121. self.count = 0
  122. def finished(self):
  123. return (self.count >= self.iterations)
  124. def draw(self):
  125. if (time.time() - self.last) > self.speed:
  126. off = (time.time() - self.last) / self.speed
  127. self.offset += int(off)
  128. self.last = time.time()
  129. if self.offset >= self.width:
  130. self.offset = -self.gui.width
  131. self.count += 1
  132. self.drawer.text(self.text, self.font, self.offset, True)
  133. if __name__ == "__main__":
  134. import util
  135. t = util.getTarget()
  136. # show splash screen while initializing
  137. from splash import SplashScreen
  138. splash = SplashScreen(t)
  139. t.loop_start()
  140. splash.draw()
  141. t.loop_end()
  142. from manager import Manager
  143. m = Manager(t)
  144. scriptDir = os.path.dirname(os.path.realpath(__file__))
  145. fontDir = os.path.join(scriptDir, "fonts")
  146. for f in os.listdir(os.fsencode(fontDir)):
  147. filename = os.fsdecode(f)
  148. if not filename.endswith(".bdf"):
  149. continue
  150. fontName = filename[:-4]
  151. s = fontName + " Abcdefgh " + fontName
  152. m.add(ScrollText(t, s, fontName, 1, 75, (0, 255, 0), (0, 0, 25)))
  153. m.restart()
  154. t.loop(m.draw)