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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  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. from splash import SplashScreen
  137. splash = SplashScreen(t)
  138. t.loop_start()
  139. splash.draw()
  140. t.loop_end()
  141. from manager import Manager
  142. m = Manager(t)
  143. m.add(ScrollText(t, "tom-thumb Abcdefgh tom-thumb", "tom-thumb",
  144. 1, 75, (0, 255, 0), (0, 0, 25)))
  145. m.add(ScrollText(t, "antidote Abcdefgh antidote", "antidote",
  146. 1, 75, (0, 255, 0), (0, 0, 25)))
  147. m.add(ScrollText(t, "uushi Abcdefgh uushi", "uushi",
  148. 1, 75, (0, 255, 0), (0, 0, 25)))
  149. m.add(ScrollText(t, "lemon Abcdefgh lemon", "lemon",
  150. 1, 75, (0, 255, 0), (0, 0, 25)))
  151. m.add(ScrollText(t, "ib8x8u Abcdefgh ib8x8u", "ib8x8u",
  152. 1, 75, (0, 255, 0), (0, 0, 25)))
  153. m.add(ScrollText(t, "iv18x16u Abcdefgh iv18x16u", "iv18x16u",
  154. 1, 75, (0, 255, 0), (0, 0, 25)))
  155. m.restart()
  156. t.loop(m.draw)