Spaces:
Runtime error
Runtime error
File size: 5,252 Bytes
732ca45 865cb1a 732ca45 865cb1a 732ca45 865cb1a 732ca45 865cb1a 732ca45 865cb1a 732ca45 865cb1a 732ca45 865cb1a 732ca45 865cb1a 732ca45 865cb1a 732ca45 865cb1a 732ca45 865cb1a 732ca45 865cb1a 732ca45 865cb1a 732ca45 865cb1a 732ca45 865cb1a 732ca45 865cb1a 732ca45 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | # utils/generators/base.py
import io
import os
from PIL import Image, ImageDraw, ImageFont
class BaseGenerator:
CANVAS_W, CANVAS_H = (660, 900)
CARD_W, CARD_H = (580, 820)
CORNER_RAD = 16
BANNER_H = 160
OFFSET_X, OFFSET_Y = (40, 40)
ELEMENT_COLORS = {
"Solstice": (239, 68, 68), "Equinox": (59, 130, 246), "Eclipse": (147, 197, 253),
"Zenith": (168, 85, 247), "Aether": (16, 185, 129), "Nadir": (250, 204, 21),
"Genesis": (34, 197, 94), "Light": (255, 255, 224), "Dark": (30, 27, 75), "None": (156, 163, 175),
"Pyro": (239, 68, 68), "Fire": (239, 68, 68), "Hydro": (59, 130, 246), "Water": (59, 130, 246),
"Cryo": (147, 197, 253), "Ice": (147, 197, 253), "Electro": (168, 85, 247),
"Anemo": (16, 185, 129), "Wind": (16, 185, 129), "Geo": (250, 204, 21), "Earth": (250, 204, 21),
"Dendro": (34, 197, 94)
}
@staticmethod
def get_font(size: int):
base_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
font_path = os.path.join(base_dir, "assets", "font.ttf")
try:
if os.path.exists(font_path): return ImageFont.truetype(font_path, size)
except: pass
return ImageFont.load_default()
@staticmethod
def create_rounded_mask(size, radius):
scale = 3
mask = Image.new('L', (size[0]*scale, size[1]*scale), 0)
draw = ImageDraw.Draw(mask)
draw.rounded_rectangle((0, 0, size[0]*scale, size[1]*scale), radius=radius*scale, fill=255)
return mask.resize(size, Image.Resampling.LANCZOS)
@staticmethod
def create_bottom_rounded_mask(size, radius):
scale = 3
mask = Image.new('L', (size[0]*scale, size[1]*scale), 0)
draw = ImageDraw.Draw(mask)
draw.rounded_rectangle((0, 0, size[0]*scale, size[1]*scale), radius=radius*scale, fill=255)
draw.rectangle((0, 0, size[0]*scale, radius*scale), fill=255)
return mask.resize(size, Image.Resampling.LANCZOS)
@staticmethod
def draw_text_with_glow(uid, position, text, font, core_color="white", glow_color=(0,0,0)):
uid.text(position, text, font=font, fill=core_color, stroke_width=3, stroke_fill=glow_color)
def apply_text(self, blank_template: Image.Image, char_name: str, print_num: int, element: str, rarity: str) -> io.BytesIO:
canvas = blank_template.copy()
uid = ImageDraw.Draw(canvas)
r_color = self.ELEMENT_COLORS.get(element, (250, 204, 21) if rarity in ["Legendary", "Mythic", "SSR", "UR"] else (156, 163, 175))
light_r = tuple(min(255, c + 150) for c in r_color)
glow_solid = r_color[:3]
base_sz = int(self.CARD_W * 0.085)
num_font = self.get_font(base_sz)
num_text = str(print_num)
# ==========================================
# 1. Standard Number Box (100% UNTOUCHED LOGIC)
# ==========================================
try:
num_w = num_font.getlength(num_text)
except:
num_w = 20
box_w = num_w + 24
box_h = 46
box_x2 = self.OFFSET_X + self.CARD_W - 30
box_x1 = box_x2 - box_w
box_y1 = self.OFFSET_Y + self.CARD_H - self.BANNER_H + (self.BANNER_H - box_h) // 2
box_y2 = box_y1 + box_h
uid.rounded_rectangle((box_x1, box_y1, box_x2, box_y2), radius=10, fill=(10, 10, 10, 180), outline=light_r, width=2)
num_x = box_x1 + (box_w - num_w) / 2
num_y = box_y1 - 6
self.draw_text_with_glow(uid, (num_x, num_y), num_text, num_font, core_color="white", glow_color=glow_solid)
# ==========================================
# 2. Dynamic Centered Character Name
# ==========================================
final_char_name = char_name.title()
available_name_w = (box_x1 - self.OFFSET_X) - 20
name_sz = base_sz
min_sz = 20
name_font = self.get_font(name_sz)
# ⚡ THE FIX: Dynamic Scaler Loop. Shrink font until it fits the available width.
while True:
try:
n_w = name_font.getlength(final_char_name)
except AttributeError:
n_w = 20
if n_w <= available_name_w or name_sz <= min_sz:
break
name_sz -= 2
name_font = self.get_font(name_sz)
try:
n_bbox = uid.textbbox((0, 0), final_char_name, font=name_font, stroke_width=3)
except:
n_bbox = (0, 0, n_w, name_sz)
n_w_final = n_bbox[2] - n_bbox[0]
n_h_final = n_bbox[3] - n_bbox[1]
# Center horizontally in the available space, and perfectly vertically in the banner
name_x = self.OFFSET_X + 10 + (available_name_w - n_w_final) // 2 - n_bbox[0]
name_y = self.OFFSET_Y + self.CARD_H - self.BANNER_H + (self.BANNER_H - n_h_final) // 2 - n_bbox[1]
self.draw_text_with_glow(uid, (name_x, name_y), final_char_name, name_font, core_color="white", glow_color=glow_solid)
buf = io.BytesIO()
canvas.save(buf, format="WEBP", quality=85, method=0)
buf.seek(0)
return buf
|