generate_icon.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. """Generate MixBoard iOS app icon — a stylized mixing board fader knob
  2. with a music note, on a dark gradient background."""
  3. from PIL import Image, ImageDraw, ImageFont
  4. import math
  5. SIZE = 1024
  6. CENTER = SIZE // 2
  7. img = Image.new("RGBA", (SIZE, SIZE), (0, 0, 0, 0))
  8. draw = ImageDraw.Draw(img)
  9. # Background — dark gradient with subtle green tint (Winamp vibes)
  10. for y in range(SIZE):
  11. t = y / SIZE
  12. r = int(10 + 15 * t)
  13. g = int(15 + 25 * t)
  14. b = int(12 + 18 * t)
  15. draw.line([(0, y), (SIZE, y)], fill=(r, g, b, 255))
  16. # Rounded corners mask
  17. mask = Image.new("L", (SIZE, SIZE), 0)
  18. mask_draw = ImageDraw.Draw(mask)
  19. radius = int(SIZE * 0.22)
  20. mask_draw.rounded_rectangle([(0, 0), (SIZE - 1, SIZE - 1)], radius=radius, fill=255)
  21. # Apply rounded corners
  22. img.putalpha(mask)
  23. # Outer glow ring
  24. ring_cx, ring_cy = CENTER, CENTER + 20
  25. ring_r_outer = 380
  26. ring_r_inner = 310
  27. for angle_deg in range(360):
  28. angle = math.radians(angle_deg)
  29. # Green glow gradient around the ring
  30. intensity = 0.6 + 0.4 * math.sin(angle * 2 + 0.5)
  31. g_val = int(200 * intensity)
  32. for r_offset in range(ring_r_inner, ring_r_outer):
  33. alpha_factor = 1.0 - abs(r_offset - (ring_r_inner + ring_r_outer) / 2) / ((ring_r_outer - ring_r_inner) / 2)
  34. alpha_factor = max(0, alpha_factor) ** 1.5
  35. x = int(ring_cx + r_offset * math.cos(angle))
  36. y = int(ring_cy + r_offset * math.sin(angle))
  37. if 0 <= x < SIZE and 0 <= y < SIZE:
  38. existing = img.getpixel((x, y))
  39. new_g = min(255, existing[1] + int(g_val * alpha_factor * 0.5))
  40. img.putpixel((x, y), (existing[0], new_g, existing[2], existing[3]))
  41. # Central knob (fader/mixing board aesthetic)
  42. knob_r = 260
  43. # Dark circle
  44. for y_off in range(-knob_r, knob_r + 1):
  45. for x_off in range(-knob_r, knob_r + 1):
  46. dist = math.sqrt(x_off ** 2 + y_off ** 2)
  47. if dist <= knob_r:
  48. x = ring_cx + x_off
  49. y = ring_cy + y_off
  50. if 0 <= x < SIZE and 0 <= y < SIZE:
  51. # Metallic gradient
  52. t = (y_off + knob_r) / (2 * knob_r)
  53. base = int(30 + 35 * (1 - t))
  54. # Edge highlight
  55. edge = max(0, 1 - (knob_r - dist) / 15) if dist > knob_r - 15 else 0
  56. highlight = int(80 * edge)
  57. r_val = min(255, base + highlight)
  58. g_val = min(255, base + highlight + int(20 * edge))
  59. b_val = min(255, base + highlight)
  60. img.putpixel((int(x), int(y)), (r_val, g_val, b_val, 255))
  61. # Knob indicator line (green, pointing up — like a fader position)
  62. line_w = 8
  63. for y_off in range(-knob_r + 30, -60):
  64. for x_off in range(-line_w, line_w + 1):
  65. x = ring_cx + x_off
  66. y = ring_cy + y_off
  67. if 0 <= x < SIZE and 0 <= y < SIZE:
  68. dist_from_center = abs(x_off)
  69. alpha = max(0, 1 - dist_from_center / line_w)
  70. g_val = int(255 * alpha)
  71. existing = img.getpixel((x, y))
  72. img.putpixel((x, y), (0, g_val, 0, 255))
  73. # Music note symbol in center
  74. # Draw a simple eighth note
  75. note_cx, note_cy = ring_cx + 10, ring_cy + 30
  76. note_color = (0, 220, 0, 255)
  77. # Note head (filled ellipse)
  78. head_rx, head_ry = 45, 35
  79. for y_off in range(-head_ry, head_ry + 1):
  80. for x_off in range(-head_rx, head_rx + 1):
  81. # Rotated ellipse
  82. angle = -0.4
  83. rx = x_off * math.cos(angle) - y_off * math.sin(angle)
  84. ry = x_off * math.sin(angle) + y_off * math.cos(angle)
  85. if (rx / head_rx) ** 2 + (ry / head_ry) ** 2 <= 1:
  86. x = note_cx + x_off
  87. y = note_cy + y_off
  88. if 0 <= x < SIZE and 0 <= y < SIZE:
  89. img.putpixel((int(x), int(y)), note_color)
  90. # Note stem
  91. stem_x = note_cx + 40
  92. for y_off in range(-160, 10):
  93. for x_off in range(-4, 5):
  94. x = stem_x + x_off
  95. y = note_cy + y_off
  96. if 0 <= x < SIZE and 0 <= y < SIZE:
  97. img.putpixel((int(x), int(y)), note_color)
  98. # Flag (curved)
  99. for i in range(60):
  100. t = i / 60
  101. fx = stem_x + int(35 * math.sin(t * math.pi * 0.8))
  102. fy = note_cy - 160 + int(60 * t)
  103. for dx in range(-3, 4):
  104. for dy in range(-2, 3):
  105. x, y = fx + dx, fy + dy
  106. if 0 <= x < SIZE and 0 <= y < SIZE:
  107. img.putpixel((x, y), note_color)
  108. # "MB" text at bottom
  109. # Use a simple approach — draw M and B with rectangles
  110. text_y = SIZE - 130
  111. text_color = (0, 180, 0, 200)
  112. # Just add a subtle "MixBoard" text feel with dots
  113. for i, char_x in enumerate(range(CENTER - 100, CENTER + 120, 22)):
  114. dot_r = 4
  115. for dy in range(-dot_r, dot_r + 1):
  116. for dx in range(-dot_r, dot_r + 1):
  117. if dx ** 2 + dy ** 2 <= dot_r ** 2:
  118. x, y = char_x + dx, text_y + dy
  119. if 0 <= x < SIZE and 0 <= y < SIZE:
  120. alpha = int(150 * (1 - (dx ** 2 + dy ** 2) / dot_r ** 2))
  121. existing = img.getpixel((x, y))
  122. img.putpixel((x, y), (0, min(255, existing[1] + alpha), 0, existing[3]))
  123. # Save
  124. output_path = "/Users/vdrobkov/Misc/Documents/Copilot/Mixboard iOS/Sources/Resources/Assets.xcassets/AppIcon.appiconset/icon_1024.png"
  125. img.save(output_path, "PNG")
  126. print(f"Icon saved to {output_path}")
  127. print(f"Size: {img.size}")