"""Generate the app icon: olive-green rounded square with a single elegant note.""" import struct import zlib import math import os BG_R, BG_G, BG_B = 124, 154, 60 def create_icon(): sizes = [16, 32, 48, 64, 128, 256] images = [] for s in sizes: images.append(_render(s)) _write_ico(images, sizes, os.path.join(os.path.dirname(__file__), "app.ico")) print("app.ico created!") def _render(size): """Render an olive-green rounded-square with a single music note.""" pixels = bytearray(size * size * 4) corner = size * 0.22 for y in range(size): for x in range(size): i = (y * size + x) * 4 inside = _rounded_rect(x, y, size, corner) if inside >= 1.0: nr, ng, nb, na = _note_pixel(x, y, size) if na > 0: pixels[i] = nr pixels[i + 1] = ng pixels[i + 2] = nb pixels[i + 3] = na else: pixels[i] = BG_R pixels[i + 1] = BG_G pixels[i + 2] = BG_B pixels[i + 3] = 255 elif inside > 0: a = int(inside * 255) nr, ng, nb, na = _note_pixel(x, y, size) if na > 0: pixels[i] = nr pixels[i + 1] = ng pixels[i + 2] = nb pixels[i + 3] = min(255, int(na * inside)) else: pixels[i] = BG_R pixels[i + 1] = BG_G pixels[i + 2] = BG_B pixels[i + 3] = a else: pixels[i:i + 4] = b'\x00\x00\x00\x00' return bytes(pixels) def _rounded_rect(x, y, size, corner): """Return 0.0-1.0 coverage for anti-aliased rounded rectangle.""" margin = size * 0.06 left, top = margin, margin right, bottom = size - margin - 1, size - margin - 1 if x < left or x > right or y < top or y > bottom: return 0.0 cr = corner corners = [ (left + cr, top + cr), (right - cr, top + cr), (left + cr, bottom - cr), (right - cr, bottom - cr), ] for ccx, ccy in corners: in_corner_x = (x < ccx and ccx == corners[0][0] or x < ccx and ccx == corners[2][0] or x > ccx and ccx == corners[1][0] or x > ccx and ccx == corners[3][0]) in_corner_y = (y < ccy and ccy == corners[0][1] or y < ccy and ccy == corners[1][1] or y > ccy and ccy == corners[2][1] or y > ccy and ccy == corners[3][1]) if in_corner_x and in_corner_y: dist = ((x - ccx) ** 2 + (y - ccy) ** 2) ** 0.5 if dist > cr + 1: return 0.0 elif dist > cr - 1: return max(0.0, min(1.0, cr + 1 - dist)) else: return 1.0 return 1.0 def _note_pixel(x, y, size): """Draw a single elegant eighth note (quaver).""" s = size white = (255, 255, 255, 235) # --- Note head: tilted filled ellipse --- hcx = s * 0.44 hcy = s * 0.71 hrx = s * 0.14 hry = s * 0.09 tilt = -0.45 ca, sa = math.cos(tilt), math.sin(tilt) dx, dy = x - hcx, y - hcy u = (dx * ca + dy * sa) / hrx v = (-dx * sa + dy * ca) / hry d2 = u * u + v * v if d2 <= 1.0: return white if d2 <= 1.25: alpha = max(0, int(235 * (1.25 - d2) / 0.25)) if alpha > 10: return (255, 255, 255, alpha) # --- Stem: thin vertical line from head to top --- stem_x = hcx + hrx * ca stem_hw = max(0.6, s * 0.018) stem_top = s * 0.19 stem_bot = hcy - hry * 0.15 if stem_top <= y <= stem_bot and abs(x - stem_x) <= stem_hw: edge = stem_hw - abs(x - stem_x) if edge < 1.0: return (255, 255, 255, max(0, int(235 * edge))) return white # --- Flag: elegant curved shape at top of stem --- ft = stem_top fh = s * 0.32 fb = ft + fh fw = s * 0.19 if ft <= y <= fb and x >= stem_x - stem_hw: t = (y - ft) / fh bulge = fw * math.sin(t * math.pi * 0.82) * ((1 - t) ** 0.65) right_edge = stem_x + stem_hw + bulge if x <= right_edge: edge_dist = right_edge - x if edge_dist < 1.2 and bulge > stem_hw: alpha = max(0, int(235 * edge_dist / 1.2)) if alpha > 10: return (255, 255, 255, alpha) return (0, 0, 0, 0) return white return (0, 0, 0, 0) def _make_png(rgba_data, width, height): """Create a minimal PNG from RGBA pixel data.""" def chunk(ctype, data): c = ctype + data crc = struct.pack('>I', zlib.crc32(c) & 0xffffffff) return struct.pack('>I', len(data)) + c + crc raw = b'' for row in range(height): raw += b'\x00' off = row * width * 4 raw += rgba_data[off:off + width * 4] ihdr = struct.pack('>IIBBBBB', width, height, 8, 6, 0, 0, 0) compressed = zlib.compress(raw) png = b'\x89PNG\r\n\x1a\n' png += chunk(b'IHDR', ihdr) png += chunk(b'IDAT', compressed) png += chunk(b'IEND', b'') return png def _write_ico(images, sizes, path): """Write a multi-size .ico file.""" count = len(sizes) header = struct.pack('= 256 else s h = 0 if s >= 256 else s entry = struct.pack('