feat: initialize Tab Shooter Chrome extension game
This commit is contained in:
commit
0f566b02ac
11 changed files with 1614 additions and 0 deletions
124
generate_icons.py
Normal file
124
generate_icons.py
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
"""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()
|
||||
Loading…
Add table
Add a link
Reference in a new issue