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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. #!/usr/bin/env python3
  2. # ----------------------------------------------------------------------------
  3. # "THE BEER-WARE LICENSE" (Revision 42):
  4. # <xythobuz@xythobuz.de> wrote this file. As long as you retain this notice
  5. # you can do whatever you want with this stuff. If we meet some day, and you
  6. # think this stuff is worth it, you can buy me a beer in return. Thomas Buck
  7. # ----------------------------------------------------------------------------
  8. from bdfparser import Font
  9. from PIL import Image
  10. import os
  11. import time
  12. class DrawText:
  13. def __init__(self, g):
  14. self.gui = g
  15. scriptDir = os.path.dirname(os.path.realpath(__file__))
  16. fontDir = os.path.join(scriptDir, "fonts")
  17. self.fonts = []
  18. for f in os.listdir(os.fsencode(fontDir)):
  19. filename = os.fsdecode(f)
  20. if not filename.lower().endswith(".bdf"):
  21. continue
  22. font = Font(os.path.join(fontDir, filename))
  23. #print(f"{filename} global size is "
  24. # f"{font.headers['fbbx']} x {font.headers['fbby']} (pixel), "
  25. # f"it contains {len(font)} glyphs.")
  26. # TODO hard-coded per-font offsets
  27. offset = 0
  28. if filename == "iv18x16u.bdf":
  29. offset = 6
  30. data = (font, offset, {})
  31. self.fonts.append(data)
  32. def getGlyph(self, c):
  33. f, o, cache = self.fonts[0] # TODO selection of fonts
  34. # only render glyphs once, cache resulting image data
  35. if not c in cache:
  36. g = f.glyph(c).draw()
  37. # invert color
  38. g = g.replace(1, 2).replace(0, 1).replace(2, 0)
  39. # render to pixel data
  40. img = Image.frombytes('RGBA',
  41. (g.width(), g.height()),
  42. g.tobytes('RGBA'))
  43. cache[c] = img
  44. return (cache[c], o)
  45. def drawGlyph(self, g, xOff, yOff):
  46. if xOff >= self.gui.width:
  47. return
  48. for x in range(0, g.width):
  49. for y in range(0, g.height):
  50. xTarget = xOff + x
  51. if (xTarget < 0) or (xTarget >= self.gui.width):
  52. continue
  53. p = g.getpixel((x, y))
  54. self.gui.set_pixel(xTarget, yOff + y, p)
  55. def text(self, s, offset = 0, earlyAbort = True):
  56. w = 0
  57. for c in s:
  58. xOff = -offset + w
  59. if earlyAbort:
  60. if xOff >= self.gui.width:
  61. break
  62. g, y = self.getGlyph(c)
  63. w += g.width
  64. if xOff >= -10: # some wiggle room so chars dont disappear
  65. self.drawGlyph(g, xOff, y)
  66. return w
  67. import sys
  68. class ScrollText:
  69. def __init__(self, g, t, i = 1, s = 75):
  70. self.gui = g
  71. self.drawer = DrawText(self.gui)
  72. self.text = t
  73. self.iterations = i
  74. self.speed = 1.0 / s
  75. self.width = self.drawer.text(self.text, 0, False)
  76. self.restart()
  77. def restart(self):
  78. self.offset = -self.gui.width
  79. self.last = time.time()
  80. self.count = 0
  81. def finished(self):
  82. return (self.count >= self.iterations)
  83. def draw(self):
  84. if (time.time() - self.last) > self.speed:
  85. off = (time.time() - self.last) / self.speed
  86. self.offset += int(off)
  87. self.last = time.time()
  88. if self.offset >= self.width:
  89. self.offset = -self.gui.width
  90. self.count += 1
  91. self.drawer.text(self.text, self.offset, True)
  92. if __name__ == "__main__":
  93. import util
  94. t = util.getTarget()
  95. d = ScrollText(t, "This is a long scrolling text. Is it too fast or maybe too slow?")
  96. t.debug_loop(d.draw)