Add nerd glasses and the ...because he was bored... subtitle to the loading screen
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 159 KiB After Width: | Height: | Size: 171 KiB |
+19
-13
@@ -9,10 +9,11 @@ The stock Godot boot splash (logo plus "GODOT Game engine") is replaced by the
|
|||||||
|
|
||||||
## Asset
|
## Asset
|
||||||
|
|
||||||
`assets/branding/boot_splash.png`, 1024x1185, palette PNG with alpha
|
`assets/branding/boot_splash.png`, 1024x1315, palette PNG with alpha
|
||||||
(256 colors, 95 alpha levels), about 159 KB. Subject: cartoon zombie head and bust,
|
(256 colors, 95 alpha levels), about 171 KB. Subject: cartoon zombie head and bust,
|
||||||
bald scalp with a sparse hair fringe on the sides, unsettling grin, thumbs up, with
|
bald scalp with a sparse hair fringe on the sides, thick nerd glasses, unsettling grin,
|
||||||
"MatGames" below (one word, no space) in a phosphor-terminal typeface.
|
thumbs up, with "MatGames" (one word, no space) in a phosphor-terminal typeface and
|
||||||
|
the subtitle `"...because he was bored..."` beneath it.
|
||||||
|
|
||||||
Provenance is recorded in `tools/branding/manifest.json`:
|
Provenance is recorded in `tools/branding/manifest.json`:
|
||||||
|
|
||||||
@@ -20,8 +21,10 @@ Provenance is recorded in `tools/branding/manifest.json`:
|
|||||||
`gemini-3.1-flash-image`) on 2026-10-11 from no third-party source material; the
|
`gemini-3.1-flash-image`) on 2026-10-11 from no third-party source material; the
|
||||||
provider terms do not claim ownership of the output. A second AI pass (`agy_edit`)
|
provider terms do not claim ownership of the output. A second AI pass (`agy_edit`)
|
||||||
kept the face, grin, thumbs-up hand, shirt, pose and flat background and changed
|
kept the face, grin, thumbs-up hand, shirt, pose and flat background and changed
|
||||||
only the hair to a bald scalp with a side fringe; both files and their SHA256 are
|
only the hair to a bald scalp with a side fringe, then a third pass added thick nerd
|
||||||
kept in `tools/branding/raw/` and the edit is recorded under `mascot_derived_from`.
|
glasses while keeping the face, grin, hand, shirt, pose and flat background. All
|
||||||
|
files and their SHA256 are kept in `tools/branding/raw/` and the chain is recorded
|
||||||
|
under `mascot_provenance`.
|
||||||
Note that an image generator does not emit real alpha, so the flat magenta
|
Note that an image generator does not emit real alpha, so the flat magenta
|
||||||
background is keyed out locally;
|
background is keyed out locally;
|
||||||
- lettering: drawn by script, not by the generator, because generated text is
|
- lettering: drawn by script, not by the generator, because generated text is
|
||||||
@@ -45,8 +48,11 @@ packed into the export (`tools/*` is already excluded by the export preset).
|
|||||||
pink tongue or the blue shirt because their red and blue are not both above green;
|
pink tongue or the blue shirt because their red and blue are not both above green;
|
||||||
4. crops to the subject, applies a 50 px fade at the bottom to soften the frame cut
|
4. crops to the subject, applies a 50 px fade at the bottom to soften the frame cut
|
||||||
of the generated bust, and scales to 1024 px wide with 40 px side padding;
|
of the generated bust, and scales to 1024 px wide with 40 px side padding;
|
||||||
5. draws the title with VT323 in amber over a blurred glow layer. The title defaults
|
5. draws the title with VT323 in amber over a blurred glow layer, plus an optional
|
||||||
to the one-word form `MatGames`; pass `--title` to change it;
|
second line. Defaults are the one-word `MatGames` and the subtitle
|
||||||
|
`"...because he was bored..."`; `--title`, `--subtitle` (empty to omit) and
|
||||||
|
`--subtitle-height` change them. A 30 px `--bottom-padding` band is left under the
|
||||||
|
text so the shell progress bar does not cross the lettering;
|
||||||
6. quantizes to 256 colors (no dither) and writes the PNG plus the manifest.
|
6. quantizes to 256 colors (no dither) and writes the PNG plus the manifest.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -71,11 +77,11 @@ ratio; the exported shell confirms the result with the classes
|
|||||||
|
|
||||||
## Verification
|
## Verification
|
||||||
|
|
||||||
- Exported `index.png` has the same 1024x1185 dimensions as the master. It is
|
- Exported `index.png` has the same 1024x1315 dimensions as the master. It is
|
||||||
re-encoded by the exporter to 32-bit RGBA (about 496 KB), so the bytes are not
|
re-encoded by the exporter to 32-bit RGBA (about 528 KB), so the bytes are not
|
||||||
identical; composited on black the two images differ by at most 6.4/255 on
|
identical; composited on black the two images differ by at most 5.8/255 on
|
||||||
0.13% of pixels, all with alpha below 20, i.e. invisible. The release keeps the
|
0.11% of pixels, all with alpha below 20, i.e. invisible. The release keeps the
|
||||||
159 KB master in the repository and ships a gzip sidecar.
|
171 KB master in the repository and ships a gzip sidecar.
|
||||||
- The exported shell contains no "Game engine" text and the splash image has an
|
- The exported shell contains no "Game engine" text and the splash image has an
|
||||||
empty `alt`.
|
empty `alt`.
|
||||||
- `html/export_icon=false`, so no favicon is exported: the browser tab still shows
|
- `html/export_icon=false`, so no favicon is exported: the browser tab still shows
|
||||||
|
|||||||
@@ -1,5 +1,19 @@
|
|||||||
# Web build and compressed deployment
|
# Web build and compressed deployment
|
||||||
|
|
||||||
|
## Nerd glasses and subtitle release: f77b7ba7b89364dd
|
||||||
|
|
||||||
|
The mascot now wears thick nerd glasses (third AI edit pass) and carries the subtitle
|
||||||
|
`"...because he was bored..."` under the title, see `docs/BRANDING.md`. A 30 px band
|
||||||
|
was left under the lettering because the shell progress bar crossed the subtitle in the
|
||||||
|
first attempt, deployed minutes earlier as cd86c85387c1c9f3 and superseded by this one.
|
||||||
|
PCK 4,373,568 bytes; gzip payload total 12,821,256 bytes. Backup
|
||||||
|
`index-before-nerd-bar.html` (the earlier `index-before-nerd.html` holds the
|
||||||
|
cd86c85387c1c9f3 shell).
|
||||||
|
|
||||||
|
Obsolete releases after this deploy: 097e6c64b42640ad and 1194f239fcbd91d6, both
|
||||||
|
deletable under the retention policy once approved.
|
||||||
|
|
||||||
|
|
||||||
## Bald mascot release: 1194f239fcbd91d6
|
## Bald mascot release: 1194f239fcbd91d6
|
||||||
|
|
||||||
Title is now the one-word `MatGames` and the mascot has a bald scalp with a side
|
Title is now the one-word `MatGames` and the mascot has a bald scalp with a side
|
||||||
|
|||||||
@@ -3,43 +3,52 @@
|
|||||||
"file": "assets/branding/boot_splash.png",
|
"file": "assets/branding/boot_splash.png",
|
||||||
"size": [
|
"size": [
|
||||||
1024,
|
1024,
|
||||||
1185
|
1315
|
||||||
],
|
],
|
||||||
"mode": "P",
|
"mode": "P",
|
||||||
"palette_colors": 256,
|
"palette_colors": 256,
|
||||||
"sha256": "4b4bab87b5038915d5a7493c6ee134cd654293036d093e71d210cbad6359bdb2"
|
"sha256": "715bd5f1dd3a651b1b5f6ee0560db232ddfdad676091f07ea34696787e27fbee"
|
||||||
},
|
},
|
||||||
"mascot_source": {
|
"mascot_source": {
|
||||||
"file": "tools/branding/raw/agy_edit_20261010235510_0.jpg",
|
"file": "tools/branding/raw/agy_edit_20261011000309_0.jpg",
|
||||||
"sha256": "913184c2e2e3dd5357f7729988859c8e038e4561ba18af7cb60f12644f7f6994"
|
"sha256": "57bc1e46eb0876c02008038981c9b01a6bf4e02da78418de7b44f4fcbd93895e"
|
||||||
},
|
},
|
||||||
"generator": {
|
"generator": {
|
||||||
"tool": "agy_generate (Antigravity direct API)",
|
"tool": "agy_generate (Antigravity direct API)",
|
||||||
"model": "gemini-3.1-flash-image",
|
"model": "gemini-3.1-flash-image",
|
||||||
"date": "2026-10-11",
|
"date": "2026-10-11",
|
||||||
"prompt": "single cartoon zombie head-and-bust mascot, unsettling wide grin, thumbs up, 2D cartoon style with bold outlines, flat magenta chroma-key background, no text and no logo; then edited to a bald scalp with a sparse hair fringe on the sides only",
|
"prompt": "single cartoon zombie head-and-bust mascot, unsettling wide grin, thumbs up, 2D cartoon style with bold outlines, flat magenta chroma-key background, no text and no logo; then edited to a bald scalp with a sparse hair fringe on the sides only, then edited again to wear thick nerd glasses",
|
||||||
"terms_note": "generated from no third-party source material; the provider terms do not claim ownership of the output. The generator emits no real alpha channel, so the background is keyed out locally by this script."
|
"terms_note": "generated from no third-party source material; the provider terms do not claim ownership of the output. The generator emits no real alpha channel, so the background is keyed out locally by this script."
|
||||||
},
|
},
|
||||||
"chroma_key": {
|
"chroma_key": {
|
||||||
"background_rgb": [
|
"background_rgb": [
|
||||||
246,
|
244,
|
||||||
1,
|
0,
|
||||||
237
|
233
|
||||||
],
|
],
|
||||||
"transparent_below": 55,
|
"transparent_below": 55,
|
||||||
"opaque_above": 130
|
"opaque_above": 130
|
||||||
},
|
},
|
||||||
"title": {
|
"title": {
|
||||||
"text": "MatGames",
|
"text": "MatGames",
|
||||||
|
"subtitle": "\"...because he was bored...\"",
|
||||||
"font": "tools/branding/fonts/VT323-Regular.ttf",
|
"font": "tools/branding/fonts/VT323-Regular.ttf",
|
||||||
"license": "SIL Open Font License 1.1 (tools/branding/fonts/OFL.txt)"
|
"license": "SIL Open Font License 1.1 (tools/branding/fonts/OFL.txt)"
|
||||||
},
|
},
|
||||||
"bottom_fade_px": 50,
|
"bottom_fade_px": 50,
|
||||||
"mascot_derived_from": [
|
"mascot_provenance": [
|
||||||
{
|
{
|
||||||
"file": "tools/branding/raw/agy_generate_20261010234423_0.jpg",
|
"file": "tools/branding/raw/agy_generate_20261010234423_0.jpg",
|
||||||
"sha256": "5929495ff606fbcdd74f52a37304f3b569315bcd1fc0fef812b5fd7689dc1396",
|
"sha256": "5929495ff606fbcdd74f52a37304f3b569315bcd1fc0fef812b5fd7689dc1396"
|
||||||
"edit": "AI edit (agy_edit): keep face, grin, thumbs up, shirt, pose and flat background; change the hair to a bald scalp with a sparse fringe on the sides only"
|
},
|
||||||
|
{
|
||||||
|
"file": "tools/branding/raw/agy_edit_20261010235510_0.jpg",
|
||||||
|
"sha256": "913184c2e2e3dd5357f7729988859c8e038e4561ba18af7cb60f12644f7f6994"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "tools/branding/raw/agy_edit_20261011000309_0.jpg",
|
||||||
|
"sha256": "57bc1e46eb0876c02008038981c9b01a6bf4e02da78418de7b44f4fcbd93895e"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"bottom_padding_px": 30
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 529 KiB |
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://b5pj4ipovpbo"
|
||||||
|
path="res://.godot/imported/agy_edit_20261011000309_0.jpg-c4ed4776ca2aca40794a7d66a2e8befc.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://tools/branding/raw/agy_edit_20261011000309_0.jpg"
|
||||||
|
dest_files=["res://.godot/imported/agy_edit_20261011000309_0.jpg-c4ed4776ca2aca40794a7d66a2e8befc.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
+37
-26
@@ -14,14 +14,19 @@ from PIL import Image, ImageDraw, ImageFilter, ImageFont
|
|||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument('raw', type=pathlib.Path, help='raw generated image (magenta background)')
|
parser.add_argument('raw', type=pathlib.Path, help='raw generated image (magenta background)')
|
||||||
parser.add_argument('--title', default='MatGames')
|
parser.add_argument('--title', default='MatGames')
|
||||||
|
parser.add_argument('--subtitle', default='"...because he was bored..."',
|
||||||
|
help='second line under the title; empty string for none')
|
||||||
|
parser.add_argument('--subtitle-height', type=int, default=100)
|
||||||
|
parser.add_argument('--bottom-padding', type=int, default=30,
|
||||||
|
help='empty band under the text: the shell progress bar sits there')
|
||||||
parser.add_argument('--canvas', type=int, default=1024)
|
parser.add_argument('--canvas', type=int, default=1024)
|
||||||
parser.add_argument('--title-height', type=int, default=190)
|
parser.add_argument('--title-height', type=int, default=190)
|
||||||
parser.add_argument('--bottom-fade', type=int, default=50,
|
parser.add_argument('--bottom-fade', type=int, default=50,
|
||||||
help='soften the generator frame cut at the bottom of the bust')
|
help='soften the generator frame cut at the bottom of the bust')
|
||||||
parser.add_argument('--colors', type=int, default=256,
|
parser.add_argument('--colors', type=int, default=256,
|
||||||
help='palette colors for the shipped PNG; 0 keeps 32-bit RGBA')
|
help='palette colors for the shipped PNG; 0 keeps 32-bit RGBA')
|
||||||
parser.add_argument('--generated-from', type=pathlib.Path, default=None,
|
parser.add_argument('--provenance', action='append', type=pathlib.Path, default=[],
|
||||||
help='pre-edit generation this file was derived from, for provenance')
|
help='earlier files of the generation/edit chain, in order; repeatable')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
args.raw = args.raw.resolve()
|
args.raw = args.raw.resolve()
|
||||||
root = pathlib.Path(__file__).resolve().parents[1]
|
root = pathlib.Path(__file__).resolve().parents[1]
|
||||||
@@ -70,27 +75,34 @@ if args.bottom_fade > 0:
|
|||||||
|
|
||||||
# ---------------------------------------------------------------- composition
|
# ---------------------------------------------------------------- composition
|
||||||
canvas_width = args.canvas
|
canvas_width = args.canvas
|
||||||
title_area = args.title_height
|
title_area = args.title_height + (args.subtitle_height if args.subtitle else 0)
|
||||||
scale = (canvas_width - 2 * 40) / mascot.width
|
scale = (canvas_width - 2 * 40) / mascot.width
|
||||||
mascot = mascot.resize((round(mascot.width * scale), round(mascot.height * scale)), Image.LANCZOS)
|
mascot = mascot.resize((round(mascot.width * scale), round(mascot.height * scale)), Image.LANCZOS)
|
||||||
height = mascot.height + title_area
|
height = mascot.height + title_area + args.bottom_padding
|
||||||
canvas = Image.new('RGBA', (canvas_width, height), (0, 0, 0, 0))
|
canvas = Image.new('RGBA', (canvas_width, height), (0, 0, 0, 0))
|
||||||
canvas.alpha_composite(mascot, ((canvas_width - mascot.width) // 2, 0))
|
canvas.alpha_composite(mascot, ((canvas_width - mascot.width) // 2, 0))
|
||||||
|
|
||||||
font_size = int(title_area * 0.95)
|
|
||||||
font = ImageFont.truetype(str(font_path), font_size)
|
def _draw_line(text: str, block_top: int, block_height: int, size: int,
|
||||||
text = args.title
|
glow_color: tuple, text_color: tuple, blur: int) -> None:
|
||||||
while font.getbbox(text)[2] - font.getbbox(text)[0] > canvas_width - 60 and font_size > 8:
|
font = ImageFont.truetype(str(font_path), size)
|
||||||
font_size -= 2
|
while font.getbbox(text)[2] - font.getbbox(text)[0] > canvas_width - 60 and size > 8:
|
||||||
font = ImageFont.truetype(str(font_path), font_size)
|
size -= 2
|
||||||
|
font = ImageFont.truetype(str(font_path), size)
|
||||||
box = font.getbbox(text)
|
box = font.getbbox(text)
|
||||||
text_width = box[2] - box[0]
|
position = ((canvas_width - (box[2] - box[0])) // 2 - box[0],
|
||||||
position = ((canvas_width - text_width) // 2 - box[0], mascot.height + (title_area - box[3]) // 2 - box[1])
|
block_top + (block_height - box[3]) // 2 - box[1])
|
||||||
glow = Image.new('RGBA', canvas.size, (0, 0, 0, 0))
|
glow = Image.new('RGBA', canvas.size, (0, 0, 0, 0))
|
||||||
ImageDraw.Draw(glow).text(position, text, font=font, fill=(255, 150, 20, 190))
|
ImageDraw.Draw(glow).text(position, text, font=font, fill=glow_color)
|
||||||
glow = glow.filter(ImageFilter.GaussianBlur(9))
|
canvas.alpha_composite(glow.filter(ImageFilter.GaussianBlur(blur)))
|
||||||
canvas.alpha_composite(glow)
|
ImageDraw.Draw(canvas).text(position, text, font=font, fill=text_color)
|
||||||
ImageDraw.Draw(canvas).text(position, text, font=font, fill=(255, 196, 64, 255))
|
|
||||||
|
|
||||||
|
_draw_line(args.title, mascot.height, args.title_height, int(args.title_height * 0.95),
|
||||||
|
(255, 150, 20, 190), (255, 196, 64, 255), 9)
|
||||||
|
if args.subtitle:
|
||||||
|
_draw_line(args.subtitle, mascot.height + args.title_height, args.subtitle_height,
|
||||||
|
int(args.subtitle_height * 0.8), (255, 150, 20, 150), (255, 176, 58, 240), 6)
|
||||||
|
|
||||||
out = branding / 'boot_splash.png'
|
out = branding / 'boot_splash.png'
|
||||||
if args.colors > 0:
|
if args.colors > 0:
|
||||||
@@ -111,23 +123,22 @@ manifest.update({
|
|||||||
'prompt': ('single cartoon zombie head-and-bust mascot, unsettling wide grin, thumbs up, '
|
'prompt': ('single cartoon zombie head-and-bust mascot, unsettling wide grin, thumbs up, '
|
||||||
'2D cartoon style with bold outlines, flat magenta chroma-key background, '
|
'2D cartoon style with bold outlines, flat magenta chroma-key background, '
|
||||||
'no text and no logo; then edited to a bald scalp with a sparse hair fringe '
|
'no text and no logo; then edited to a bald scalp with a sparse hair fringe '
|
||||||
'on the sides only'),
|
'on the sides only, then edited again to wear thick nerd glasses'),
|
||||||
'terms_note': ('generated from no third-party source material; the provider terms do not claim '
|
'terms_note': ('generated from no third-party source material; the provider terms do not claim '
|
||||||
'ownership of the output. The generator emits no real alpha channel, so the '
|
'ownership of the output. The generator emits no real alpha channel, so the '
|
||||||
'background is keyed out locally by this script.'),
|
'background is keyed out locally by this script.'),
|
||||||
},
|
},
|
||||||
'chroma_key': {'background_rgb': list(background), 'transparent_below': T0, 'opaque_above': T1},
|
'chroma_key': {'background_rgb': list(background), 'transparent_below': T0, 'opaque_above': T1},
|
||||||
'title': {'text': args.title, 'font': 'tools/branding/fonts/VT323-Regular.ttf',
|
'title': {'text': args.title, 'subtitle': args.subtitle,
|
||||||
|
'font': 'tools/branding/fonts/VT323-Regular.ttf',
|
||||||
'license': 'SIL Open Font License 1.1 (tools/branding/fonts/OFL.txt)'},
|
'license': 'SIL Open Font License 1.1 (tools/branding/fonts/OFL.txt)'},
|
||||||
'bottom_fade_px': args.bottom_fade,
|
'bottom_fade_px': args.bottom_fade,
|
||||||
|
'bottom_padding_px': args.bottom_padding,
|
||||||
})
|
})
|
||||||
if args.generated_from is not None:
|
chain = list(args.provenance) + [args.raw]
|
||||||
chain = manifest.get('mascot_derived_from', [])
|
manifest['mascot_provenance'] = [
|
||||||
manifest['mascot_derived_from'] = chain + [{
|
{'file': str(path.resolve().relative_to(root)),
|
||||||
'file': str(args.generated_from.resolve().relative_to(root)),
|
'sha256': hashlib.sha256(path.read_bytes()).hexdigest()} for path in chain]
|
||||||
'sha256': hashlib.sha256(args.generated_from.read_bytes()).hexdigest(),
|
manifest.pop('mascot_derived_from', None)
|
||||||
'edit': 'AI edit (agy_edit): keep face, grin, thumbs up, shirt, pose and flat background; '
|
|
||||||
'change the hair to a bald scalp with a sparse fringe on the sides only',
|
|
||||||
}]
|
|
||||||
(source_tools / 'manifest.json').write_text(json.dumps(manifest, indent=2) + '\n')
|
(source_tools / 'manifest.json').write_text(json.dumps(manifest, indent=2) + '\n')
|
||||||
print(out, canvas.size, canvas.mode, 'mascot', mascot.size)
|
print(out, canvas.size, canvas.mode, 'mascot', mascot.size)
|
||||||
|
|||||||
Reference in New Issue
Block a user