image generation added
This commit is contained in:
321
generate_images.py
Normal file
321
generate_images.py
Normal file
@@ -0,0 +1,321 @@
|
||||
"""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")
|
||||
Reference in New Issue
Block a user