Update profile card generation in default.py by changing default text color to white, refining layout dimensions for better proportions, and enhancing visual elements such as the stats area and progress bar. Implement improved text shadowing and spacing for a more polished appearance.
Some checks are pending
Run pre-commit / Run pre-commit (push) Waiting to run

This commit is contained in:
Valerie 2025-05-25 21:58:36 -04:00
parent 6c5f48fcd7
commit 4c99430d95

View file

@ -190,77 +190,39 @@ def generate_default_profile(
log.debug(f"PFP animated: {pfp_animated}, BG animated: {bg_animated}") log.debug(f"PFP animated: {pfp_animated}, BG animated: {bg_animated}")
# Setup # Setup
default_fill = (0, 0, 0) # Default fill color for text default_fill = (255, 255, 255) # Default fill color for text
stroke_width = 2 # Width of the stroke around text stroke_width = 2 # Width of the stroke around text
if square: if square:
desired_card_size = (450, 450) desired_card_size = (450, 450)
# aspect_ratio = imgtools.calc_aspect_ratio(*desired_card_size)
name_y = 35 # Upper bound of username placement
stats_y = 160 # Upper bound of stats texts
blur_edge = 450 # Left bound of blur edge
bar_width = 550 # Length of level bar
bar_height = 40 # Height of level bar
bar_start = 475 # Left bound of level bar
bar_top = 380 # Top bound of level bar
stat_bottom = bar_top - 10 # Bottom bound of all stats
stat_start = bar_start + 10 # Left bound of all stats
stat_split = stat_start + 210 # Split between left and right stats
stat_end = 990 # Right bound of all stats
stat_offset = 45 # Offset between stats
circle_x = 60 # Left bound of profile circle
circle_y = 60 # Top bound of profile circle
star_text_x = 910 # Left bound of star text
star_text_y = 35 # Top bound of star text
star_icon_x = 850 # Left bound of star icon
star_icon_y = 35 # Top bound of star icon
else: else:
# Ensure the card is the correct size and aspect ratio # Reduce height for better proportions
desired_card_size = (1050, 450) desired_card_size = (1050, 350)
# aspect_ratio = imgtools.calc_aspect_ratio(*desired_card_size)
name_y = 35 # Upper bound of username placement
stats_y = 160 # Upper bound of stats texts
blur_edge = 450 # Left bound of blur edge
bar_width = 550 # Length of level bar
bar_height = 40 # Height of level bar
bar_start = 475 # Left bound of level bar
bar_top = 380 # Top bound of level bar
stat_bottom = bar_top - 10 # Bottom bound of all stats
stat_start = bar_start + 10 # Left bound of all stats
stat_split = stat_start + 210 # Split between left and right stats
stat_end = 990 # Right bound of all stats
stat_offset = 45 # Offset between stats
circle_x = 60 # Left bound of profile circle
circle_y = 60 # Top bound of profile circle
star_text_x = 910 # Left bound of star text
star_text_y = 35 # Top bound of star text
star_icon_x = 850 # Left bound of star icon
star_icon_y = 35 # Top bound of star icon
# Create the stats layer with glass effect
stats_layer = Image.new("RGBA", desired_card_size, (0, 0, 0, 0))
# Define the stats area with a modern glass effect # Define the stats area with a modern glass effect
stats_area = ( stats_area = (
400, # x1 - Start after profile picture 400, # x1 - Start after profile picture
20, # y1 - Start near top 20, # y1 - Start near top
1000 if not square else 430, # x2 - End near right edge 1000 if not square else 430, # x2 - End near right edge
430 # y2 - End near bottom 330 if not square else 430 # y2 - End near bottom
) )
# Create a semi-transparent background for stats # Create the stats layer with glass effect
stats_layer = Image.new("RGBA", desired_card_size, (0, 0, 0, 0))
# Create a semi-transparent background for stats with modern blur effect
glass = Image.new("RGBA", desired_card_size, (0, 0, 0, 0)) glass = Image.new("RGBA", desired_card_size, (0, 0, 0, 0))
glass_draw = ImageDraw.Draw(glass) glass_draw = ImageDraw.Draw(glass)
glass_draw.rounded_rectangle(stats_area, radius=20, fill=(0, 0, 0, 80)) glass_draw.rounded_rectangle(stats_area, radius=25, fill=(0, 0, 0, 95))
# Add a subtle gradient overlay # Add a subtle gradient overlay for depth
gradient = Image.new("RGBA", desired_card_size, (0, 0, 0, 0)) gradient = Image.new("RGBA", desired_card_size, (0, 0, 0, 0))
gradient_draw = ImageDraw.Draw(gradient) gradient_draw = ImageDraw.Draw(gradient)
for i in range(20): for i in range(30):
opacity = int(80 * (1 - i/20)) opacity = int(60 * (1 - i/30))
gradient_draw.rounded_rectangle( gradient_draw.rounded_rectangle(
(stats_area[0], stats_area[1]+i, stats_area[2], stats_area[3]), (stats_area[0], stats_area[1]+i, stats_area[2], stats_area[3]),
radius=20, radius=25,
fill=(255, 255, 255, opacity) fill=(255, 255, 255, opacity)
) )
@ -268,14 +230,15 @@ def generate_default_profile(
stats_layer = Image.alpha_composite(stats_layer, glass) stats_layer = Image.alpha_composite(stats_layer, glass)
stats_layer = Image.alpha_composite(stats_layer, gradient) stats_layer = Image.alpha_composite(stats_layer, gradient)
# Add a subtle border # Add a subtle glow border
border_draw = ImageDraw.Draw(stats_layer) border_draw = ImageDraw.Draw(stats_layer)
border_draw.rounded_rectangle( for i in range(3):
stats_area, border_draw.rounded_rectangle(
radius=20, (stats_area[0]-i, stats_area[1]-i, stats_area[2]+i, stats_area[3]+i),
outline=(255, 255, 255, 100), radius=25,
width=2 outline=(155, 17, 30, 100-i*30),
) width=1
)
# Draw stats with improved styling # Draw stats with improved styling
draw = ImageDraw.Draw(stats_layer) draw = ImageDraw.Draw(stats_layer)
@ -285,54 +248,78 @@ def generate_default_profile(
if prestige > 0: if prestige > 0:
level_text = f"P{prestige}{level_text}" level_text = f"P{prestige}{level_text}"
level_font = ImageFont.truetype(str(font_path or imgtools.DEFAULT_FONT), 48) # Use larger font for level display
level_y = stats_area[1] + 20 level_font = ImageFont.truetype(str(font_path or imgtools.DEFAULT_FONT), 56)
level_y = stats_area[1] + 15
# Add subtle text shadow for depth
shadow_offset = 2
draw.text(
(stats_area[0] + 20 + shadow_offset, level_y + shadow_offset),
level_text,
font=level_font,
fill=(0, 0, 0, 100)
)
draw.text( draw.text(
(stats_area[0] + 20, level_y), (stats_area[0] + 20, level_y),
level_text, level_text,
font=level_font, font=level_font,
fill=user_color, fill=user_color
stroke_width=2,
stroke_fill=(0, 0, 0)
) )
# Stats section # Stats section with improved layout
font_size = 32 title_font_size = 24
font = ImageFont.truetype(str(font_path or imgtools.DEFAULT_FONT), font_size) value_font_size = 28
spacing = 50 # Vertical spacing between stats title_font = ImageFont.truetype(str(font_path or imgtools.DEFAULT_FONT), title_font_size)
value_font = ImageFont.truetype(str(font_path or imgtools.DEFAULT_FONT), value_font_size)
spacing = 45 # Vertical spacing between stats
# Starting positions # Starting positions
start_x = stats_area[0] + 20 start_x = stats_area[0] + 20
start_y = level_y + 80 start_y = level_y + 80
# Messages stat # Helper function for stat rendering
draw.text((start_x, start_y), "💬", font=font, fill=stat_color) def draw_stat(y_pos, icon, title, value, color=stat_color):
draw.text((start_x + 40, start_y), f"{humanize_number(messages)}", font=font, fill=stat_color) # Draw icon
draw.text((start_x, y_pos), icon, font=value_font, fill=color)
# Draw title
draw.text((start_x + 40, y_pos), f"{title}:", font=title_font, fill=(200, 200, 200))
# Draw value with shadow for depth
value_x = start_x + 40
value_y = y_pos + title_font_size + 2
draw.text((value_x + 1, value_y + 1), value, font=value_font, fill=(0, 0, 0, 100))
draw.text((value_x, value_y), value, font=value_font, fill=color)
return spacing + title_font_size + 5
# Voice time stat # Draw each stat with title and value
draw.text((start_x, start_y + spacing), "🎤", font=font, fill=stat_color) current_y = start_y
draw.text((start_x + 40, start_y + spacing), imgtools.abbreviate_time(voicetime), font=font, fill=stat_color) current_y += draw_stat(current_y, "💬", "Messages", f"{humanize_number(messages)} sent")
current_y += draw_stat(current_y, "🎤", "Voice Time", imgtools.abbreviate_time(voicetime))
current_y += draw_stat(current_y, "", "Stars", humanize_number(stars))
# Stars stat # Right column stats
draw.text((start_x, start_y + spacing * 2), "", font=font, fill=stat_color) right_x = stats_area[0] + 300
draw.text((start_x + 40, start_y + spacing * 2), f"{humanize_number(stars)}", font=font, fill=stat_color) current_y = start_y
if balance is not None:
current_y += draw_stat(current_y, "💰", "Balance", f"{humanize_number(balance)} {currency_name}")
current_y += draw_stat(current_y, "🏆", "Rank", f"#{humanize_number(position)}")
# Progress bar # Progress bar with modern design
progress = (current_xp - previous_xp) / (next_xp - previous_xp)
bar_width = stats_area[2] - stats_area[0] - 40 bar_width = stats_area[2] - stats_area[0] - 40
bar_height = 25 bar_height = 30
bar_y = stats_area[3] - 60 bar_y = stats_area[3] - 60
progress = (current_xp - previous_xp) / (next_xp - previous_xp)
# Create progress bar background # Create progress bar background with gradient
bar_bg = Image.new("RGBA", (bar_width, bar_height), (0, 0, 0, 100)) bar_bg = Image.new("RGBA", (bar_width, bar_height), (0, 0, 0, 100))
progress_width = max(1, int(bar_width * progress)) # Ensure at least 1 pixel width progress_width = max(1, int(bar_width * progress))
bar_progress = Image.new("RGBA", (progress_width, bar_height), level_bar_color + (200,)) bar_progress = Image.new("RGBA", (progress_width, bar_height), level_bar_color + (200,))
# Add gradient to progress bar # Add shine effect to progress bar
gradient = Image.new("RGBA", bar_progress.size, (0, 0, 0, 0)) gradient = Image.new("RGBA", bar_progress.size, (0, 0, 0, 0))
gradient_draw = ImageDraw.Draw(gradient) gradient_draw = ImageDraw.Draw(gradient)
for i in range(bar_height): for i in range(bar_height):
opacity = int(100 * (1 - i/bar_height)) opacity = int(150 * (1 - abs(i - bar_height/2)/(bar_height/2)))
gradient_draw.rectangle( gradient_draw.rectangle(
(0, i, bar_progress.width, i+1), (0, i, bar_progress.width, i+1),
fill=(255, 255, 255, opacity) fill=(255, 255, 255, opacity)
@ -354,13 +341,32 @@ def generate_default_profile(
stats_layer.paste(bar_bg, (start_x, bar_y), bar_bg) stats_layer.paste(bar_bg, (start_x, bar_y), bar_bg)
stats_layer.paste(bar_progress_masked, (start_x, bar_y), bar_progress_masked) stats_layer.paste(bar_progress_masked, (start_x, bar_y), bar_progress_masked)
# XP text # XP text with improved styling
xp_font = ImageFont.truetype(str(font_path or imgtools.DEFAULT_FONT), 24) xp_font = ImageFont.truetype(str(font_path or imgtools.DEFAULT_FONT), 24)
xp_text = f"{humanize_number(current_xp)} / {humanize_number(next_xp)} XP" xp_text = f"EXP: {humanize_number(current_xp)} / {humanize_number(next_xp)}"
xp_x = start_x + (bar_width - draw.textlength(xp_text, font=xp_font)) // 2
# Draw XP text with shadow
draw.text( draw.text(
(start_x, bar_y + bar_height + 5), (xp_x + 1, bar_y - 30 + 1),
xp_text, xp_text,
font=xp_font, font=xp_font,
fill=(0, 0, 0, 100)
)
draw.text(
(xp_x, bar_y - 30),
xp_text,
font=xp_font,
fill=(255, 255, 255)
)
# Progress percentage
percent_text = f"{int(progress * 100)}%"
percent_x = start_x + (bar_width - draw.textlength(percent_text, font=xp_font)) // 2
draw.text(
(percent_x, bar_y + bar_height + 5),
percent_text,
font=xp_font,
fill=stat_color, fill=stat_color,
stroke_width=1, stroke_width=1,
stroke_fill=(0, 0, 0) stroke_fill=(0, 0, 0)
@ -377,16 +383,16 @@ def generate_default_profile(
card = imgtools.fit_aspect_ratio(card, desired_card_size) card = imgtools.fit_aspect_ratio(card, desired_card_size)
# Round the card corners # Round the card corners
card = imgtools.round_image_corners(card, 20) card = imgtools.round_image_corners(card, 25)
# Create circular profile picture # Create circular profile picture
pfp_size = (320, 320) # Profile picture size pfp_size = (300, 300) # Slightly smaller profile picture
pfp = pfp.resize(pfp_size, Image.Resampling.LANCZOS) pfp = pfp.resize(pfp_size, Image.Resampling.LANCZOS)
pfp = imgtools.make_profile_circle(pfp) pfp = imgtools.make_profile_circle(pfp)
# Position profile picture on the left # Position profile picture on the left
pfp_x = 40 pfp_x = 50
pfp_y = (card.height - pfp_size[1]) // 2 pfp_y = (desired_card_size[1] - pfp_size[1]) // 2
# Create a new image for final composition # Create a new image for final composition
final_image = Image.new("RGBA", desired_card_size, (0, 0, 0, 0)) final_image = Image.new("RGBA", desired_card_size, (0, 0, 0, 0))
@ -414,9 +420,9 @@ def generate_default_profile(
card = card.convert("RGBA") card = card.convert("RGBA")
card = imgtools.fit_aspect_ratio(card, desired_card_size) card = imgtools.fit_aspect_ratio(card, desired_card_size)
if blur: if blur:
blur_section = imgtools.blur_section(card, (blur_edge, 0, card.width, card.height)) blur_section = imgtools.blur_section(card, (stats_area[0], 0, card.width, card.height))
# Paste onto the stats # Paste onto the stats
card.paste(blur_section, (blur_edge, 0), blur_section) card.paste(blur_section, (stats_area[0], 0), blur_section)
card.paste(stats_layer, (0, 0), stats_layer) card.paste(stats_layer, (0, 0), stats_layer)
@ -474,8 +480,8 @@ def generate_default_profile(
# Paste items onto the card # Paste items onto the card
if blur: if blur:
blur_section = imgtools.blur_section(card_frame, (blur_edge, 0, card_frame.width, card_frame.height)) blur_section = imgtools.blur_section(card_frame, (stats_area[0], 0, card_frame.width, card_frame.height))
card_frame.paste(blur_section, (blur_edge, 0), blur_section) card_frame.paste(blur_section, (stats_area[0], 0), blur_section)
card_frame.paste(pfp, (circle_x, circle_y), pfp) card_frame.paste(pfp, (circle_x, circle_y), pfp)
card_frame.paste(stats_layer, (0, 0), stats_layer) card_frame.paste(stats_layer, (0, 0), stats_layer)
@ -547,9 +553,9 @@ def generate_default_profile(
card_frame = card_frame.convert("RGBA") card_frame = card_frame.convert("RGBA")
if blur: if blur:
blur_section = imgtools.blur_section(card_frame, (blur_edge, 0, card_frame.width, card_frame.height)) blur_section = imgtools.blur_section(card_frame, (stats_area[0], 0, card_frame.width, card_frame.height))
# Paste onto the stats # Paste onto the stats
card_frame.paste(blur_section, (blur_edge, 0), blur_section) card_frame.paste(blur_section, (stats_area[0], 0), blur_section)
if pfp_frame.mode != "RGBA": if pfp_frame.mode != "RGBA":
pfp_frame = pfp_frame.convert("RGBA") pfp_frame = pfp_frame.convert("RGBA")