Labmagic 1.0: control de la Blackmagic Pocket 4K por Bluetooth, panel para OBS y licencia MIT
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Este commit está contenido en:
140
make_icon.py
Archivo normal
140
make_icon.py
Archivo normal
@@ -0,0 +1,140 @@
|
||||
"""
|
||||
Genera el icono de Labmagic a partir del wordmark blanco real de Labstream.
|
||||
|
||||
- icono.ico / icono.png: la opción elegida (OPCION_POR_DEFECTO).
|
||||
- iconos/opcion-N.ico|png: todas las opciones, por si se quiere cambiar (basta copiar una sobre icono.ico).
|
||||
- iconos/opciones.png: hoja de muestra con las opciones a 256 px y a tamaño pequeño.
|
||||
"""
|
||||
import os
|
||||
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
LOGO_BLANCO = r"C:\Users\Munka\OneDrive\Labstream\Propuestas\0_Modelo Propuestas\Logos\Logo Blanco.png"
|
||||
OPCION_POR_DEFECTO = 2
|
||||
|
||||
ROJO = (227, 38, 44, 255)
|
||||
ROJO_OSCURO = (170, 22, 28, 255)
|
||||
NEGRO = (16, 16, 16, 255)
|
||||
BLANCO = (255, 255, 255, 255)
|
||||
SS = 4 # supermuestreo
|
||||
|
||||
|
||||
def cargar_wordmark():
|
||||
"""Devuelve (wordmark 'labstream', 'lab' solo, logo completo con STUDIO), todos RGBA blancos recortados."""
|
||||
img = Image.open(LOGO_BLANCO).convert("RGBA")
|
||||
w, h = img.size
|
||||
full = img.crop(img.getbbox())
|
||||
word = img.crop((0, 0, w, int(h * 0.66)))
|
||||
word = word.crop(word.getbbox())
|
||||
lab = word.crop((0, 0, int(word.width * 0.295), word.height))
|
||||
lab = lab.crop(lab.getbbox())
|
||||
return word, lab, full
|
||||
|
||||
|
||||
def fondo(size, color, radio=0.22, borde=None, grosor=0.035, inset=0.065):
|
||||
s = size * SS
|
||||
img = Image.new("RGBA", (s, s), (0, 0, 0, 0))
|
||||
d = ImageDraw.Draw(img)
|
||||
d.rounded_rectangle((0, 0, s - 1, s - 1), radius=int(s * radio), fill=color)
|
||||
if borde:
|
||||
i = int(s * inset)
|
||||
d.rounded_rectangle((i, i, s - 1 - i, s - 1 - i), radius=int(s * (radio - inset * 0.8)),
|
||||
outline=borde, width=max(1, int(s * grosor)))
|
||||
return img
|
||||
|
||||
|
||||
def pegar_centrado(base, art, ancho_rel, dy_rel=0.0):
|
||||
s = base.width
|
||||
ratio = art.height / art.width
|
||||
w = int(s * ancho_rel)
|
||||
h = int(w * ratio)
|
||||
art = art.resize((w, h), Image.LANCZOS)
|
||||
x = (s - w) // 2
|
||||
y = int((s - h) / 2 + s * dy_rel)
|
||||
base.alpha_composite(art, (x, y))
|
||||
return base
|
||||
|
||||
|
||||
def punto(base, cx_rel, cy_rel, r_rel, color):
|
||||
s = base.width
|
||||
d = ImageDraw.Draw(base)
|
||||
cx, cy, r = s * cx_rel, s * cy_rel, s * r_rel
|
||||
d.ellipse((cx - r, cy - r, cx + r, cy + r), fill=color)
|
||||
return base
|
||||
|
||||
|
||||
def opcion(n, size):
|
||||
word, lab, full = cargar_wordmark()
|
||||
if n == 1: # Rojo, borde blanco fino, wordmark blanco
|
||||
base = fondo(size, ROJO, borde=BLANCO)
|
||||
return pegar_centrado(base, word, 0.74)
|
||||
if n == 2: # Rojo, wordmark grande "lab" + punto REC blanco
|
||||
base = fondo(size, ROJO, borde=BLANCO)
|
||||
pegar_centrado(base, lab, 0.56, dy_rel=-0.03)
|
||||
return punto(base, 0.5, 0.78, 0.055, BLANCO)
|
||||
if n == 3: # Negro con borde rojo, wordmark blanco y punto rojo
|
||||
base = fondo(size, NEGRO, borde=ROJO, grosor=0.05)
|
||||
pegar_centrado(base, word, 0.74, dy_rel=-0.04)
|
||||
return punto(base, 0.5, 0.74, 0.05, ROJO)
|
||||
if n == 4: # Rojo oscuro degradado, logo completo (labstream + STUDIO)
|
||||
s = size * SS
|
||||
grad = Image.new("RGBA", (s, s), ROJO)
|
||||
gd = ImageDraw.Draw(grad)
|
||||
for y in range(s):
|
||||
t = y / s
|
||||
c = tuple(int(ROJO[i] * (1 - t) + ROJO_OSCURO[i] * t) for i in range(3)) + (255,)
|
||||
gd.line((0, y, s, y), fill=c)
|
||||
mask = fondo(size, BLANCO)
|
||||
base = Image.new("RGBA", (s, s), (0, 0, 0, 0))
|
||||
base.paste(grad, (0, 0), mask)
|
||||
ImageDraw.Draw(base).rounded_rectangle((int(s * 0.065), int(s * 0.065), s - 1 - int(s * 0.065), s - 1 - int(s * 0.065)),
|
||||
radius=int(s * 0.17), outline=BLANCO, width=max(1, int(s * 0.03)))
|
||||
return pegar_centrado(base, full, 0.72)
|
||||
raise ValueError(n)
|
||||
|
||||
|
||||
def render(n, size):
|
||||
return opcion(n, size).resize((size, size), Image.LANCZOS)
|
||||
|
||||
|
||||
def guardar_ico(n, ruta_ico, ruta_png):
|
||||
sizes = [16, 24, 32, 48, 64, 128, 256]
|
||||
imgs = [render(n, s) for s in sizes]
|
||||
imgs[-1].save(ruta_ico, format="ICO", sizes=[(s, s) for s in sizes], append_images=imgs[:-1])
|
||||
render(n, 64).save(ruta_png)
|
||||
|
||||
|
||||
def hoja(ruta):
|
||||
"""Hoja de muestra: cada opción a 256 px, y a 48/32/16 px para ver cómo se lee en pequeño."""
|
||||
try:
|
||||
font = ImageFont.truetype("C:/Windows/Fonts/segoeui.ttf", 22)
|
||||
fontb = ImageFont.truetype("C:/Windows/Fonts/segoeuib.ttf", 26)
|
||||
except Exception: # noqa: BLE001
|
||||
font = fontb = ImageFont.load_default()
|
||||
nombres = {1: "1 · Rojo, borde blanco, wordmark", 2: "2 · Rojo, 'lab' grande + punto",
|
||||
3: "3 · Negro, borde rojo, wordmark", 4: "4 · Rojo degradado, logo completo"}
|
||||
col_w, pad = 360, 40
|
||||
sheet = Image.new("RGBA", (col_w * 4 + pad, 470), (24, 24, 24, 255))
|
||||
d = ImageDraw.Draw(sheet)
|
||||
d.text((pad, 14), "Opciones de icono para Labmagic", fill=(240, 240, 240, 255), font=fontb)
|
||||
for i, n in enumerate((1, 2, 3, 4)):
|
||||
x0 = pad + i * col_w
|
||||
sheet.alpha_composite(render(n, 256), (x0, 60))
|
||||
y = 340
|
||||
xx = x0
|
||||
for s in (48, 32, 16):
|
||||
sheet.alpha_composite(render(n, s), (xx, y + (48 - s) // 2))
|
||||
xx += s + 14
|
||||
d.text((x0, 405), nombres[n], fill=(200, 200, 200, 255), font=font)
|
||||
sheet.save(ruta)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
out_dir = os.path.join(HERE, "iconos")
|
||||
os.makedirs(out_dir, exist_ok=True)
|
||||
for n in (1, 2, 3, 4):
|
||||
guardar_ico(n, os.path.join(out_dir, f"opcion-{n}.ico"), os.path.join(out_dir, f"opcion-{n}.png"))
|
||||
guardar_ico(OPCION_POR_DEFECTO, os.path.join(HERE, "icono.ico"), os.path.join(HERE, "icono.png"))
|
||||
hoja(os.path.join(out_dir, "opciones.png"))
|
||||
print("icono por defecto: opción", OPCION_POR_DEFECTO, "->", os.path.join(HERE, "icono.ico"))
|
||||
Referencia en una nueva incidencia
Block a user