"""Erzeugt das Red-Mode-Logo: unterer Bogen 'NACHT-NAVIGATOR' -> 'RED RALF MODE'.""" import math from PIL import Image, ImageDraw, ImageFont SRC = "nacht-navigator.png" OUT_FINAL = "app/public/redmode-logo.png" OUT_PREVIEW = "logo_preview.png" S = 2 CS = 209 CX = CY = 104 img = Image.open(SRC).convert("RGB") big = img.crop((0, 33, 209, 242)).resize((CS * S, CS * S), Image.LANCZOS) cx = cy = CX * S px = big.load() def ang_xy(r, deg): a = math.radians(deg) return cx + r * math.cos(a), cy + r * math.sin(a) def median_patch(x, y, rad=2): vals = [[], [], []] for dx in range(-rad, rad + 1): for dy in range(-rad, rad + 1): c = px[int(round(x)) + dx, int(round(y)) + dy] for k in range(3): vals[k].append(c[k]) return tuple(sorted(v)[len(v) // 2] for v in vals) # ---------------------------------------------------------------- Bogen löschen def radial_profile(deg, r0=104, r1=180, step=2): return [median_patch(*ang_xy(r, deg)) for r in range(r0, r1 + 1, step)] profL = radial_profile(18) profR = radial_profile(162) radii = list(range(104, 181, 2)) A0, A1 = 26, 154 R0, R1 = 104, 180 for y in range(cy - R1 - 8, cy + R1 + 9): for x in range(cx - R1 - 8, cx + R1 + 9): dx, dy = x - cx, y - cy r = math.hypot(dx, dy) if r < R0 or r > R1: continue th = math.degrees(math.atan2(dy, dx)) if th < 0: th += 360 if not (A0 <= th <= A1): continue t = (th - A0) / (A1 - A0) i = min(range(len(radii)), key=lambda k: abs(radii[k] - r)) cL, cR = profL[i], profR[i] px[x, y] = tuple(int(cL[k] + (cR[k] - cL[k]) * t) for k in range(3)) # ------------------------------------------------- Gold-Ringe wieder anmalen def ring_color(r0, r1): """Helles Gold: pro Winkel das hellste Pixel im Radius-Intervall, dann Median.""" cols = [] for deg in list(range(0, 16)) + list(range(164, 181)): best = None for r in range(r0, r1 + 1): x, y = ang_xy(r, deg) c = median_patch(x, y, 1) lum = 0.299 * c[0] + 0.587 * c[1] + 0.114 * c[2] if best is None or lum > best[0]: best = (lum, c) cols.append(best[1]) cols.sort(key=lambda c: sum(c)) return cols[int(len(cols) * 0.75)] gold_inner = ring_color(105, 111) gold_outer = ring_color(166, 176) print("Gold innen:", gold_inner, "aussen:", gold_outer) dr = ImageDraw.Draw(big) for r, col, w in [(108, gold_inner, 7), (172, gold_outer, 9)]: bb = (cx - r, cy - r, cx + r, cy + r) dr.arc(bb, start=A0 - 3, end=A1 + 3, fill=col, width=w) # ------------------------------------------------------- Bogen-Text zeichnen FONT_PATH = "C:/Windows/Fonts/ARIALNB.TTF" TEXT = "RED RALF MODE" R_BASE = 156 SPAN_MAX = 100 SQUEEZE = 0.62 def char_w(fnt, ch): box = fnt.getbbox(ch, stroke_width=0) return box[2] - box[0] size = 72 while size > 10: fnt = ImageFont.truetype(FONT_PATH, size) raw = sum(char_w(fnt, ch) for ch in TEXT) if raw * SQUEEZE <= R_BASE * math.radians(SPAN_MAX): break size -= 2 print("Fontgroesse:", size, "raw width:", raw) fnt = ImageFont.truetype(FONT_PATH, size) items = [] for ch in TEXT: if ch == " ": items.append((None, int(char_w(fnt, "N") * 0.45))) continue box = fnt.getbbox(ch, stroke_width=4) w, h = box[2] - box[0] + 16, box[3] - box[1] + 16 ci = Image.new("RGBA", (w, h), (0, 0, 0, 0)) cd = ImageDraw.Draw(ci) cd.text((8 - box[0], 8 - box[1]), ch, font=fnt, fill=(248, 244, 232, 255), stroke_width=4, stroke_fill=(22, 14, 6, 255)) ci = ci.resize((max(4, int(w * SQUEEZE)), h), Image.LANCZOS) items.append((ci, int(char_w(fnt, ch) * SQUEEZE) + 2)) total_w = sum(w for _, w in items) span_deg = math.degrees(total_w / R_BASE) print("Bogen-Spanne:", round(span_deg, 1), "deg") layer = Image.new("RGBA", big.size, (0, 0, 0, 0)) theta = math.radians(90 + span_deg / 2) # linke Bogenkante, laeuft nach rechts abnehmend for ci, w in items: adv = w / R_BASE # Bogenlaenge/Radius = Winkel in RADIAN theta -= adv / 2 if ci is not None: bx, by = ang_xy(R_BASE, math.degrees(theta)) rot = ci.rotate(90 - math.degrees(theta), expand=True, resample=Image.BICUBIC) # Alpha-BBox: Glyph unten (Baseline) an (bx,by) ausrichten ab = rot.getbbox() ox = bx - (ab[0] + ab[2]) / 2 oy = by - ab[3] layer.alpha_composite(rot, (int(ox), int(oy))) theta -= adv / 2 out = Image.alpha_composite(big.convert("RGBA"), layer) out.save(OUT_PREVIEW) # ------------------------------------------------------- Alpha-Maske + speichern final = out.copy() mask = Image.new("L", final.size, 0) md = ImageDraw.Draw(mask) md.ellipse((10, 18, 408, 400), fill=255) final.putalpha(mask) final.save(OUT_FINAL) print("Gespeichert:", OUT_FINAL, final.size)