322 lines
10 KiB
Python
322 lines
10 KiB
Python
"""Generate required images for SEO and branding."""
|
|
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
import os
|
|
|
|
# Create images directory
|
|
os.makedirs('web/static/images', exist_ok=True)
|
|
|
|
# Neo-Brutalism color palette
|
|
COLORS = {
|
|
'yellow': '#FFE500',
|
|
'pink': '#FF10F0',
|
|
'cyan': '#00F0FF',
|
|
'green': '#39FF14',
|
|
'black': '#000000',
|
|
'white': '#FFFFFF',
|
|
'gray': '#F5F5F5'
|
|
}
|
|
|
|
def create_favicon(size, filename):
|
|
"""Create a favicon with Neo-Brutalism design."""
|
|
img = Image.new('RGB', (size, size), COLORS['yellow'])
|
|
draw = ImageDraw.Draw(img)
|
|
|
|
# Draw thick border
|
|
border_width = max(2, size // 8)
|
|
draw.rectangle([0, 0, size-1, size-1], outline=COLORS['black'], width=border_width)
|
|
|
|
# Draw maze-like pattern
|
|
cell_size = size // 4
|
|
for i in range(1, 4):
|
|
# Vertical lines
|
|
x = i * cell_size
|
|
draw.line([(x, border_width), (x, size - border_width)],
|
|
fill=COLORS['black'], width=max(1, size // 16))
|
|
# Horizontal lines
|
|
y = i * cell_size
|
|
draw.line([(border_width, y), (size - border_width, y)],
|
|
fill=COLORS['black'], width=max(1, size // 16))
|
|
|
|
img.save(f'web/static/images/{filename}')
|
|
print(f"Created {filename} ({size}x{size})")
|
|
|
|
def create_twitter_image():
|
|
"""Create Twitter card image (1200x675)."""
|
|
width, height = 1200, 675
|
|
img = Image.new('RGB', (width, height), COLORS['gray'])
|
|
draw = ImageDraw.Draw(img)
|
|
|
|
# Background with gradient effect (using rectangles)
|
|
for i in range(0, height, 20):
|
|
color_mix = int(245 - (i / height) * 20)
|
|
draw.rectangle([0, i, width, i+20],
|
|
fill=f'#{color_mix:02x}{color_mix:02x}{color_mix:02x}')
|
|
|
|
# Title box
|
|
title_height = 150
|
|
title_y = 50
|
|
draw.rectangle([50, title_y, width-50, title_y + title_height],
|
|
fill=COLORS['yellow'])
|
|
draw.rectangle([50, title_y, width-50, title_y + title_height],
|
|
outline=COLORS['black'], width=8)
|
|
|
|
# Shadow for title box
|
|
shadow_offset = 12
|
|
draw.rectangle([50 + shadow_offset, title_y + shadow_offset,
|
|
width-50 + shadow_offset, title_y + title_height + shadow_offset],
|
|
outline=COLORS['black'], width=8)
|
|
|
|
# Try to use a bold font, fallback to default
|
|
try:
|
|
font_large = ImageFont.truetype("arial.ttf", 70)
|
|
font_medium = ImageFont.truetype("arial.ttf", 50)
|
|
font_small = ImageFont.truetype("arial.ttf", 35)
|
|
except:
|
|
font_large = ImageFont.load_default()
|
|
font_medium = ImageFont.load_default()
|
|
font_small = ImageFont.load_default()
|
|
|
|
# Title text
|
|
title = "MAZE GENERATOR"
|
|
bbox = draw.textbbox((0, 0), title, font=font_large)
|
|
text_width = bbox[2] - bbox[0]
|
|
text_x = (width - text_width) // 2
|
|
draw.text((text_x, title_y + 40), title, fill=COLORS['black'], font=font_large)
|
|
|
|
# Features boxes
|
|
features = [
|
|
"8 ALGORITHMS",
|
|
"ANIMATED SOLVING",
|
|
"NEO-BRUTALISM"
|
|
]
|
|
|
|
feature_y = title_y + title_height + 60
|
|
box_width = 320
|
|
box_height = 100
|
|
spacing = 40
|
|
total_width = len(features) * box_width + (len(features) - 1) * spacing
|
|
start_x = (width - total_width) // 2
|
|
|
|
colors_cycle = [COLORS['pink'], COLORS['cyan'], COLORS['green']]
|
|
|
|
for i, feature in enumerate(features):
|
|
x = start_x + i * (box_width + spacing)
|
|
|
|
# Shadow
|
|
draw.rectangle([x + 8, feature_y + 8, x + box_width + 8, feature_y + box_height + 8],
|
|
fill=COLORS['black'])
|
|
|
|
# Box
|
|
draw.rectangle([x, feature_y, x + box_width, feature_y + box_height],
|
|
fill=colors_cycle[i])
|
|
draw.rectangle([x, feature_y, x + box_width, feature_y + box_height],
|
|
outline=COLORS['black'], width=6)
|
|
|
|
# Text
|
|
bbox = draw.textbbox((0, 0), feature, font=font_small)
|
|
text_w = bbox[2] - bbox[0]
|
|
text_h = bbox[3] - bbox[1]
|
|
draw.text((x + (box_width - text_w) // 2,
|
|
feature_y + (box_height - text_h) // 2),
|
|
feature, fill=COLORS['black'], font=font_small)
|
|
|
|
img.save('web/static/images/twitter-image.png')
|
|
print("Created twitter-image.png (1200x675)")
|
|
|
|
def create_og_image():
|
|
"""Create Open Graph image (1200x630)."""
|
|
width, height = 1200, 630
|
|
img = Image.new('RGB', (width, height), COLORS['yellow'])
|
|
draw = ImageDraw.Draw(img)
|
|
|
|
# Border
|
|
draw.rectangle([0, 0, width-1, height-1], outline=COLORS['black'], width=10)
|
|
|
|
# Try to use a bold font, fallback to default
|
|
try:
|
|
font_title = ImageFont.truetype("arial.ttf", 90)
|
|
font_subtitle = ImageFont.truetype("arial.ttf", 45)
|
|
except:
|
|
font_title = ImageFont.load_default()
|
|
font_subtitle = ImageFont.load_default()
|
|
|
|
# Title
|
|
title = "MAZE GENERATOR"
|
|
bbox = draw.textbbox((0, 0), title, font=font_title)
|
|
text_width = bbox[2] - bbox[0]
|
|
draw.text(((width - text_width) // 2, 150), title,
|
|
fill=COLORS['black'], font=font_title)
|
|
|
|
# Subtitle
|
|
subtitle = "8 ALGORITHMS • ANIMATED SOLVING"
|
|
bbox = draw.textbbox((0, 0), subtitle, font=font_subtitle)
|
|
text_width = bbox[2] - bbox[0]
|
|
draw.text(((width - text_width) // 2, 280), subtitle,
|
|
fill=COLORS['black'], font=font_subtitle)
|
|
|
|
# Draw simple maze pattern
|
|
maze_size = 300
|
|
maze_x = (width - maze_size) // 2
|
|
maze_y = 380
|
|
cell_size = maze_size // 6
|
|
|
|
# Background for maze
|
|
draw.rectangle([maze_x - 10, maze_y - 10,
|
|
maze_x + maze_size + 10, maze_y + maze_size + 10],
|
|
fill=COLORS['white'])
|
|
draw.rectangle([maze_x - 10, maze_y - 10,
|
|
maze_x + maze_size + 10, maze_y + maze_size + 10],
|
|
outline=COLORS['black'], width=6)
|
|
|
|
# Draw grid
|
|
for i in range(7):
|
|
# Vertical
|
|
x = maze_x + i * cell_size
|
|
draw.line([(x, maze_y), (x, maze_y + maze_size)],
|
|
fill=COLORS['black'], width=3)
|
|
# Horizontal
|
|
y = maze_y + i * cell_size
|
|
draw.line([(maze_x, y), (maze_x + maze_size, y)],
|
|
fill=COLORS['black'], width=3)
|
|
|
|
img.save('web/static/images/og-image.png')
|
|
print("Created og-image.png (1200x630)")
|
|
|
|
def create_apple_touch_icon():
|
|
"""Create Apple touch icon (180x180)."""
|
|
size = 180
|
|
img = Image.new('RGB', (size, size), COLORS['yellow'])
|
|
draw = ImageDraw.Draw(img)
|
|
|
|
# Border
|
|
border = 8
|
|
draw.rectangle([0, 0, size-1, size-1], outline=COLORS['black'], width=border)
|
|
|
|
# Simple maze M letter
|
|
try:
|
|
font = ImageFont.truetype("arial.ttf", 120)
|
|
except:
|
|
font = ImageFont.load_default()
|
|
|
|
text = "M"
|
|
bbox = draw.textbbox((0, 0), text, font=font)
|
|
text_width = bbox[2] - bbox[0]
|
|
text_height = bbox[3] - bbox[1]
|
|
draw.text(((size - text_width) // 2, (size - text_height) // 2 - 10),
|
|
text, fill=COLORS['black'], font=font)
|
|
|
|
img.save('web/static/images/apple-touch-icon.png')
|
|
print("Created apple-touch-icon.png (180x180)")
|
|
|
|
def create_pwa_icons():
|
|
"""Create PWA icons."""
|
|
for size in [192, 512]:
|
|
img = Image.new('RGB', (size, size), COLORS['yellow'])
|
|
draw = ImageDraw.Draw(img)
|
|
|
|
# Border
|
|
border = max(8, size // 24)
|
|
draw.rectangle([0, 0, size-1, size-1], outline=COLORS['black'], width=border)
|
|
|
|
# Draw maze pattern
|
|
cell_size = size // 8
|
|
padding = border * 2
|
|
|
|
for i in range(1, 8):
|
|
x = padding + i * cell_size
|
|
y = padding + i * cell_size
|
|
|
|
# Some vertical lines
|
|
if i % 2 == 0:
|
|
draw.line([(x, padding), (x, size - padding)],
|
|
fill=COLORS['black'], width=max(2, size // 64))
|
|
|
|
# Some horizontal lines
|
|
if i % 3 == 1:
|
|
draw.line([(padding, y), (size - padding, y)],
|
|
fill=COLORS['black'], width=max(2, size // 64))
|
|
|
|
filename = f'icon-{size}x{size}.png'
|
|
img.save(f'web/static/images/{filename}')
|
|
print(f"Created {filename} ({size}x{size})")
|
|
|
|
def create_screenshot():
|
|
"""Create a generic screenshot placeholder."""
|
|
width, height = 1280, 720
|
|
img = Image.new('RGB', (width, height), COLORS['gray'])
|
|
draw = ImageDraw.Draw(img)
|
|
|
|
# Header
|
|
header_height = 120
|
|
draw.rectangle([0, 0, width, header_height], fill=COLORS['yellow'])
|
|
draw.rectangle([0, 0, width, header_height], outline=COLORS['black'], width=8)
|
|
|
|
try:
|
|
font = ImageFont.truetype("arial.ttf", 60)
|
|
except:
|
|
font = ImageFont.load_default()
|
|
|
|
text = "MAZE GENERATOR - SCREENSHOT"
|
|
bbox = draw.textbbox((0, 0), text, font=font)
|
|
text_width = bbox[2] - bbox[0]
|
|
draw.text(((width - text_width) // 2, 30), text, fill=COLORS['black'], font=font)
|
|
|
|
# Draw sample maze area
|
|
maze_area_x = 50
|
|
maze_area_y = header_height + 50
|
|
maze_area_w = 600
|
|
maze_area_h = 500
|
|
|
|
draw.rectangle([maze_area_x, maze_area_y,
|
|
maze_area_x + maze_area_w, maze_area_y + maze_area_h],
|
|
fill=COLORS['white'])
|
|
draw.rectangle([maze_area_x, maze_area_y,
|
|
maze_area_x + maze_area_w, maze_area_y + maze_area_h],
|
|
outline=COLORS['black'], width=6)
|
|
|
|
# Controls panel
|
|
panel_x = maze_area_x + maze_area_w + 50
|
|
panel_y = maze_area_y
|
|
panel_w = width - panel_x - 50
|
|
panel_h = maze_area_h
|
|
|
|
draw.rectangle([panel_x, panel_y, panel_x + panel_w, panel_y + panel_h],
|
|
fill=COLORS['cyan'])
|
|
draw.rectangle([panel_x, panel_y, panel_x + panel_w, panel_y + panel_h],
|
|
outline=COLORS['black'], width=6)
|
|
|
|
img.save('web/static/images/screenshot.png')
|
|
print("Created screenshot.png (1280x720)")
|
|
|
|
# Generate all images
|
|
print("Generating images...")
|
|
print("-" * 50)
|
|
|
|
# Favicons
|
|
create_favicon(16, 'favicon-16x16.png')
|
|
create_favicon(32, 'favicon-32x32.png')
|
|
|
|
# Social media
|
|
create_twitter_image()
|
|
create_og_image()
|
|
|
|
# Mobile
|
|
create_apple_touch_icon()
|
|
create_pwa_icons()
|
|
|
|
# Screenshot
|
|
create_screenshot()
|
|
|
|
print("-" * 50)
|
|
print("All images generated successfully!")
|
|
print("\nGenerated files:")
|
|
print(" web/static/images/favicon-16x16.png")
|
|
print(" web/static/images/favicon-32x32.png")
|
|
print(" web/static/images/apple-touch-icon.png")
|
|
print(" web/static/images/icon-192x192.png")
|
|
print(" web/static/images/icon-512x512.png")
|
|
print(" web/static/images/twitter-image.png")
|
|
print(" web/static/images/og-image.png")
|
|
print(" web/static/images/screenshot.png")
|