"""Tab Shooter — Icon Generator (pure stdlib). Erzeugt icons/icon16.png, icon48.png, icon128.png direkt über zlib+struct. Keine externen Abhängigkeiten (PIL nicht nötig). """ import os import struct import zlib OUT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "icons") def write_png(path, width, height, rgba_pixels): """rgba_pixels: bytearray mit width*height*4 Bytes (RGBA).""" def chunk(tag, data): c = tag + data crc = struct.pack(">I", zlib.crc32(c) & 0xFFFFFFFF) return struct.pack(">I", len(data)) + c + crc header = b"\x89PNG\r\n\x1a\n" ihdr = struct.pack(">IIBBBBB", width, height, 8, 6, 0, 0, 0) # 8-bit, RGBA # Filter byte 0 am Anfang jeder Zeile raw = bytearray() stride = width * 4 for y in range(height): raw.append(0) raw.extend(rgba_pixels[y * stride:(y + 1) * stride]) idat = zlib.compress(bytes(raw), 9) png = header + chunk(b"IHDR", ihdr) + chunk(b"IDAT", idat) + chunk(b"IEND", b"" ) with open(path, "wb") as f: f.write(png) def lerp(a, b, t): return int(a + (b - a) * t) def lerp_color(c1, c2, t): return tuple(lerp(c1[i], c2[i], t) for i in range(3)) def render_icon(size): """Zeichnet ein Raumschiff mit Cyan->Magenta Verlauf auf dunklem Grund.""" bg_top = (10, 6, 30) bg_bot = (30, 8, 50) cyan = (0, 240, 255) magenta = (255, 0, 229) yellow = (255, 170, 0) px = bytearray(size * size * 4) cx = (size - 1) / 2.0 nose_y = size * 0.18 base_y = size * 0.80 half_w = size * 0.34 def set_pixel(x, y, r, g, b, a=255): if 0 <= x < size and 0 <= y < size: i = (y * size + x) * 4 # alpha-blend over existing if px[i + 3] == 0: px[i:i + 4] = bytes([r, g, b, a]) else: ia = a / 255.0 px[i] = int(r * ia + px[i] * (1 - ia)) px[i + 1] = int(g * ia + px[i + 1] * (1 - ia)) px[i + 2] = int(b * ia + px[i + 2] * (1 - ia)) px[i + 3] = min(255, px[i + 3] + a) for y in range(size): for x in range(size): # Hintergrund-Verlauf (radial-ish) t = y / max(1, size - 1) r, g, b = lerp_color(bg_top, bg_bot, t) # Sterne if (x * 7 + y * 13) % 37 == 0 and size >= 48: r, g, b = 220, 220, 255 set_pixel(x, y, r, g, b, 255) # Raumschiff: für jede Zeile y berechne ship-x-Bereich for y in range(size): # relative position im Schiff (0 = nose, 1 = base) if y < nose_y or y > base_y: continue t = (y - nose_y) / max(1, (base_y - nose_y)) # Schiffsbreite wächst zur Basis w = half_w * (0.15 + 0.85 * t) # Schiff-Verlauf cyan->magenta cr, cg, cb = lerp_color(cyan, magenta, t) for x in range(size): if abs(x - cx) <= w: # leichte Randabdunklung edge = abs(x - cx) / max(1, w) shade = 1.0 - edge * 0.3 set_pixel(x, y, int(cr * shade), int(cg * shade), int(cb * shade), 255) # Triebwerk-Glühen an der Basis if t > 0.9: for x in range(size): if abs(x - cx) < w * 0.5: set_pixel(x, y, *yellow, 255) # Cockpit (kleiner Magenta-Kreis nahe der Spitze) cockpit_cy = int(nose_y + (base_y - nose_y) * 0.35) cockpit_r = max(1, size // 12) for y in range(size): for x in range(size): dx = x - cx dy = y - cockpit_cy if dx * dx + dy * dy <= cockpit_r * cockpit_r: set_pixel(x, y, *magenta, 255) return px def main(): os.makedirs(OUT_DIR, exist_ok=True) for s in (16, 48, 128): pixels = render_icon(s) path = os.path.join(OUT_DIR, f"icon{s}.png") write_png(path, s, s, pixels) print(f"Wrote {path}") if __name__ == "__main__": main()