mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
107 lines
4.8 KiB
Python
107 lines
4.8 KiB
Python
"""Cover / thumbnail for the MobileGL intro: one 16:9 image whose centre 4:3 crop keeps everything
|
|
important; the side bands only carry decoration. MGL_LANG=zh for the Chinese cover."""
|
|
from video import *
|
|
import paper
|
|
|
|
paper.TR.update({
|
|
'Desktop OpenGL, anywhere': '桌面 OpenGL,处处可用',
|
|
'How it works, in 5 minutes': '5 分钟看懂它如何工作',
|
|
} if LANG == 'zh' else {})
|
|
|
|
SAFE_W = H * 4 // 3 # 1440: width kept by a centred 4:3 crop
|
|
OX = (W - SAFE_W) // 2 # 240: left edge of the safe area
|
|
|
|
|
|
@cache
|
|
def spr_big_tile(ch, color, k=1.22):
|
|
w, h = int(160 * k), int(180 * k)
|
|
a = Art(w, h, f'bigtile|{ch}|{color}|{k}')
|
|
c = 46 * k
|
|
a.poly([(0, 14 * k), (14 * k, 0), (w - c, 0), (w, c), (w, h - 14 * k), (w - 14 * k, h), (14 * k, h),
|
|
(0, h - 14 * k)], color, rough=0.9)
|
|
a.poly([(w - c, 0), (w - c, c), (w, c)], shade(color, 1.3), rough=0.4)
|
|
a.text(w / 2 - 2, h / 2 + 4, ch, ('fredoka', int(122 * k), 'Bold'), WHITE, shade=True)
|
|
return a.build()
|
|
|
|
|
|
def put(c, spr, x, y, rot=0.0, s=1.0, elev=1.2):
|
|
c.put(spr, x, y, rot, s, None, 1, elev, boil=0)
|
|
|
|
|
|
def side_decor(c):
|
|
"""Decoration confined to the two bands outside the 4:3 crop (x < 240, x > 1680)."""
|
|
put(c, spr_plane(MUSTARD), 125, 330, 14, 0.8, 3.0)
|
|
put(c, spr_plane(CORAL), 110, 790, -10, 0.7, 3.0)
|
|
put(c, spr_crane(CORAL), 1800, 300, -6, 0.5, 3.0)
|
|
put(c, spr_plane(TEAL), 1805, 770, -16, 0.75, 3.0)
|
|
rng = random.Random(7)
|
|
for band in ((25, OX - 25), (OX + SAFE_W + 25, W - 25)):
|
|
n = 0
|
|
while n < 7:
|
|
x, y = rng.uniform(*band), rng.uniform(60, H - 60)
|
|
size = rng.randrange(24, 46)
|
|
if not (band[0] + size / 2 < x < band[1] - size / 2):
|
|
continue
|
|
if any(abs(y - yy) < 150 for yy in (330, 790, 300, 770)) and abs(x - (band[0] + band[1]) / 2) < 90:
|
|
continue # keep clear of the planes / crane
|
|
put(c, spr_confetti(rng.randrange(4), rng.choice((CORAL, MUSTARD, TEAL, BLUE, PLUM, ORANGE)), size),
|
|
x, y, rng.uniform(-40, 40), 1, 0.6)
|
|
n += 1
|
|
|
|
|
|
def safe_content(c):
|
|
"""Everything that matters, laid out inside the centre 1440x1080."""
|
|
cx = W / 2
|
|
rng = random.Random(43)
|
|
n = 0
|
|
while n < 12: # a few confetti pieces inside the safe area's own edges
|
|
x, y = rng.uniform(OX + 40, OX + SAFE_W - 40), rng.uniform(40, H - 40)
|
|
if OX + 90 < x < OX + SAFE_W - 90 and 90 < y < H - 60:
|
|
continue
|
|
put(c, spr_confetti(rng.randrange(4), rng.choice((CORAL, MUSTARD, TEAL, BLUE, PLUM, ORANGE)),
|
|
rng.randrange(24, 46)), x, y, rng.uniform(-40, 40), 1, 0.6)
|
|
n += 1
|
|
step = 166
|
|
for i, ch in enumerate('MobileGL'):
|
|
put(c, spr_big_tile(ch, TITLE_COLS[i], 0.98), cx - step * 3.5 + step * i, 240 + (5 if i % 2 else -5),
|
|
(3.5 if i % 2 else -3.5), 1, 1.3)
|
|
put(c, spr_label('Desktop OpenGL, anywhere', ('fredoka', 64, 'SemiBold'), WHITE, INK, padx=44, pady=20),
|
|
cx, 465, -1.5, 1, 1.4)
|
|
put(c, spr_label('How it works, in 5 minutes', ('hand', 42), CORAL, WHITE, padx=28, pady=10),
|
|
cx, 565, 2, 1, 1.4)
|
|
y = 835
|
|
put(c, spr_appwin(), OX + 225, y, -4, 0.5)
|
|
put(c, spr_arrow(90, INK, 18, 40), OX + 445, y, 0, 1, 0.6)
|
|
put(c, spr_crane(BRAND), OX + 655, y - 45, -4, 0.85, 3.5)
|
|
put(c, spr_slab('MobileGL', 230, BRAND, h=64, size=36), OX + 655, y + 75, 2)
|
|
put(c, spr_arrow(90, INK, 18, 40), OX + 870, y, 0, 1, 0.6)
|
|
put(c, spr_phone(BG_PEACH), OX + 1035, y + 10, 4, 0.5)
|
|
put(c, spr_tablet(BG_MINT), OX + 1250, y + 25, -3, 0.52)
|
|
put(c, spr_bubble('OpenGL ES', ('fredoka', 36, 'SemiBold'), TEAL, WHITE), OX + 1020, y - 140, -3, 1, 1.5)
|
|
put(c, spr_bubble('Vulkan', ('fredoka', 36, 'SemiBold'), PLUM, WHITE, 'br'), OX + 1285, y - 115, 3, 1, 1.5)
|
|
put(c, spr_sticker('OpenGL\n4.6 Core', MUSTARD, r=80, size=29, fg=INK), OX + 1310, 462, 12, 1, 1.6)
|
|
|
|
|
|
def cover():
|
|
frame = background(BG_CREAM).copy()
|
|
c = Ctx(frame, 100.0, 0, 1)
|
|
side_decor(c)
|
|
safe_content(c)
|
|
return frame.convert('RGB')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
suffix = '' if LANG == 'en' else '_' + LANG
|
|
img = cover()
|
|
img.save(os.path.join(OUT, f'cover{suffix}.png'))
|
|
img.resize((1280, 720), Image.LANCZOS).save(os.path.join(OUT, f'cover{suffix}_1280x720.jpg'), quality=92)
|
|
if '--guides' in sys.argv: # preview with the 4:3 crop lines drawn in
|
|
g = img.copy()
|
|
d = ImageDraw.Draw(g)
|
|
for x in (OX, OX + SAFE_W):
|
|
d.line([(x, 0), (x, H)], fill=(220, 0, 0), width=4)
|
|
g.resize((1280, 720), Image.LANCZOS).save(os.path.join(OUT, f'cover{suffix}_guides.jpg'), quality=88)
|
|
img.crop((OX, 0, OX + SAFE_W, H)).resize((960, 720), Image.LANCZOS).save(
|
|
os.path.join(OUT, f'cover{suffix}_crop43.jpg'), quality=88)
|
|
print('ok')
|