gen_stickers.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. #!/usr/bin/env python3
  2. """Generate placeholder sticker PNGs for the default packs."""
  3. import math
  4. import os
  5. import struct
  6. import zlib
  7. ROOT = os.path.join(os.path.dirname(__file__), "..", "data", "stickers")
  8. SIZE = 96
  9. def chunk(tag, data):
  10. crc = zlib.crc32(tag + data) & 0xFFFFFFFF
  11. return struct.pack(">I", len(data)) + tag + data + struct.pack(">I", crc)
  12. def encode_png(pixels):
  13. raw = b""
  14. for y in range(SIZE):
  15. raw += b"\x00"
  16. for x in range(SIZE):
  17. raw += bytes(pixels[y][x])
  18. return (
  19. b"\x89PNG\r\n\x1a\n"
  20. + chunk(b"IHDR", struct.pack(">IIBBBBB", SIZE, SIZE, 8, 6, 0, 0, 0))
  21. + chunk(b"IDAT", zlib.compress(raw, 9))
  22. + chunk(b"IEND", b"")
  23. )
  24. def blank(fill=(0, 0, 0, 0)):
  25. return [[fill for _ in range(SIZE)] for _ in range(SIZE)]
  26. def setp(px, x, y, c):
  27. if 0 <= x < SIZE and 0 <= y < SIZE:
  28. px[y][x] = c
  29. def disk(px, cx, cy, r, color):
  30. r2 = r * r
  31. for y in range(int(cy - r), int(cy + r) + 1):
  32. for x in range(int(cx - r), int(cx + r) + 1):
  33. if (x - cx) ** 2 + (y - cy) ** 2 <= r2:
  34. setp(px, x, y, color)
  35. def ring(px, cx, cy, r, w, color):
  36. r2 = r * r
  37. inner = (r - w) ** 2
  38. for y in range(int(cy - r), int(cy + r) + 1):
  39. for x in range(int(cx - r), int(cx + r) + 1):
  40. d = (x - cx) ** 2 + (y - cy) ** 2
  41. if inner <= d <= r2:
  42. setp(px, x, y, color)
  43. def line(px, x0, y0, x1, y1, w, color):
  44. steps = max(abs(x1 - x0), abs(y1 - y0), 1)
  45. for i in range(steps + 1):
  46. t = i / steps
  47. x = x0 + (x1 - x0) * t
  48. y = y0 + (y1 - y0) * t
  49. disk(px, x, y, w, color)
  50. def save(pack, code, px):
  51. d = os.path.join(ROOT, str(pack))
  52. os.makedirs(d, exist_ok=True)
  53. path = os.path.join(d, code + ".png")
  54. with open(path, "wb") as f:
  55. f.write(encode_png(px))
  56. print("wrote", path)
  57. def face_base(color=(255, 214, 64, 255)):
  58. px = blank()
  59. disk(px, 48, 48, 40, color)
  60. return px
  61. def smile():
  62. px = face_base()
  63. disk(px, 34, 40, 5, (60, 40, 20, 255))
  64. disk(px, 62, 40, 5, (60, 40, 20, 255))
  65. line(px, 32, 62, 48, 70, 3, (60, 40, 20, 255))
  66. line(px, 48, 70, 64, 62, 3, (60, 40, 20, 255))
  67. save(1, "smile", px)
  68. def laugh():
  69. px = face_base()
  70. disk(px, 34, 38, 5, (60, 40, 20, 255))
  71. disk(px, 62, 38, 5, (60, 40, 20, 255))
  72. disk(px, 48, 64, 14, (60, 40, 20, 255))
  73. disk(px, 48, 60, 12, (255, 214, 64, 255))
  74. save(1, "laugh", px)
  75. def sad():
  76. px = face_base((130, 180, 255, 255))
  77. disk(px, 34, 42, 5, (40, 50, 90, 255))
  78. disk(px, 62, 42, 5, (40, 50, 90, 255))
  79. line(px, 32, 70, 48, 62, 3, (40, 50, 90, 255))
  80. line(px, 48, 62, 64, 70, 3, (40, 50, 90, 255))
  81. disk(px, 70, 58, 5, (80, 140, 255, 255))
  82. save(1, "sad", px)
  83. def angry():
  84. px = face_base((255, 120, 90, 255))
  85. disk(px, 34, 44, 5, (80, 20, 20, 255))
  86. disk(px, 62, 44, 5, (80, 20, 20, 255))
  87. line(px, 24, 32, 42, 40, 3, (80, 20, 20, 255))
  88. line(px, 72, 32, 54, 40, 3, (80, 20, 20, 255))
  89. line(px, 34, 68, 62, 68, 3, (80, 20, 20, 255))
  90. save(1, "angry", px)
  91. def cool():
  92. px = face_base()
  93. disk(px, 34, 42, 10, (30, 30, 30, 255))
  94. disk(px, 62, 42, 10, (30, 30, 30, 255))
  95. line(px, 44, 42, 52, 42, 3, (30, 30, 30, 255))
  96. line(px, 32, 66, 64, 66, 3, (60, 40, 20, 255))
  97. save(1, "cool", px)
  98. def wow():
  99. px = face_base((255, 230, 120, 255))
  100. ring(px, 34, 40, 8, 3, (60, 40, 20, 255))
  101. ring(px, 62, 40, 8, 3, (60, 40, 20, 255))
  102. disk(px, 48, 66, 9, (60, 40, 20, 255))
  103. save(1, "wow", px)
  104. def sleep():
  105. px = face_base((200, 210, 255, 255))
  106. line(px, 26, 42, 42, 42, 3, (60, 40, 80, 255))
  107. line(px, 54, 42, 70, 42, 3, (60, 40, 80, 255))
  108. line(px, 34, 66, 62, 66, 3, (60, 40, 80, 255))
  109. disk(px, 74, 24, 6, (140, 120, 255, 255))
  110. save(1, "sleep", px)
  111. def shy():
  112. px = face_base((255, 200, 210, 255))
  113. disk(px, 34, 42, 4, (80, 40, 50, 255))
  114. disk(px, 62, 42, 4, (80, 40, 50, 255))
  115. disk(px, 26, 56, 7, (255, 140, 150, 180))
  116. disk(px, 70, 56, 7, (255, 140, 150, 180))
  117. line(px, 38, 66, 58, 66, 2, (80, 40, 50, 255))
  118. save(1, "shy", px)
  119. def like():
  120. px = blank()
  121. disk(px, 48, 52, 28, (7, 193, 96, 255))
  122. line(px, 40, 58, 40, 30, 6, (255, 255, 255, 255))
  123. disk(px, 54, 36, 10, (255, 255, 255, 255))
  124. save(2, "like", px)
  125. def ok():
  126. px = blank()
  127. disk(px, 48, 48, 30, (64, 158, 255, 255))
  128. ring(px, 48, 48, 14, 5, (255, 255, 255, 255))
  129. save(2, "ok", px)
  130. def clap():
  131. px = blank()
  132. disk(px, 36, 52, 18, (255, 180, 70, 255))
  133. disk(px, 60, 44, 18, (255, 200, 90, 255))
  134. save(2, "clap", px)
  135. def wave():
  136. px = blank()
  137. disk(px, 48, 58, 22, (255, 210, 140, 255))
  138. disk(px, 62, 30, 12, (255, 210, 140, 255))
  139. save(2, "wave", px)
  140. if __name__ == "__main__":
  141. smile()
  142. laugh()
  143. sad()
  144. angry()
  145. cool()
  146. wow()
  147. sleep()
  148. shy()
  149. like()
  150. ok()
  151. clap()
  152. wave()