feat: serve platform logos as 80 px webp

Shown at 40 px, the 96 px PNG logos were 136 KiB of the home page. WebP at twice the display size is 27 KiB for the twelve raster marks. SVG stays as each project publishes it.
This commit is contained in:
Abdessamad Derraz committed 2026-10-23 10:46:58 +02:00
1 parent 3d0b013588
commit 046c376448
26 files changed
+24 -19

No files matched your search

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

+12 -12
View File
@@ -2,7 +2,7 @@
"note": "Each platform's own logo, owned by its project and shown only to name that platform. Copied once by scripts/fetch_site_images.py.",
"images": [
{
"file": "amberelec.png",
"file": "amberelec.webp",
"platform": "amberelec",
"source": "https://avatars.githubusercontent.com/u/102820909",
"owner": "AmberELEC",
@@ -16,28 +16,28 @@
"licence": "repository licence MIT"
},
{
"file": "bizhawk.png",
"file": "bizhawk.webp",
"platform": "bizhawk",
"source": "https://avatars.githubusercontent.com/u/11743303",
"owner": "TASVideos",
"licence": "GitHub organisation avatar of TASEmulators"
},
{
"file": "emudeck.png",
"file": "emudeck.webp",
"platform": "emudeck",
"source": "https://raw.githubusercontent.com/dragoonDorise/EmuDeck/main/icons/EmuDeck.png",
"owner": "EmuDeck",
"licence": "repository licence GPL-3.0"
},
{
"file": "emuelec.png",
"file": "emuelec.webp",
"platform": "emuelec",
"source": "https://avatars.githubusercontent.com/u/57574835",
"owner": "EmuELEC",
"licence": "GitHub organisation avatar of EmuELEC"
},
{
"file": "knulli.png",
"file": "knulli.webp",
"platform": "knulli",
"source": "https://avatars.githubusercontent.com/u/166268572",
"owner": "KNULLI",
@@ -51,21 +51,21 @@
"licence": "file from libretro/retroarch-assets, CC-BY-4.0"
},
{
"file": "minui.png",
"file": "minui.webp",
"platform": "minui",
"source": "https://avatars.githubusercontent.com/u/225589",
"owner": "Shaun Inman",
"licence": "GitHub avatar of shauninman, the author of MinUI"
},
{
"file": "misterfpga.png",
"file": "misterfpga.webp",
"platform": "misterfpga",
"source": "https://avatars.githubusercontent.com/u/29345272",
"owner": "MiSTer FPGA",
"licence": "GitHub organisation avatar of MiSTer-devel"
},
{
"file": "nextui.png",
"file": "nextui.webp",
"platform": "nextui",
"source": "https://avatars.githubusercontent.com/u/202386615",
"owner": "LoveRetro",
@@ -86,7 +86,7 @@
"licence": "repository licence GPL-3.0"
},
{
"file": "retrobat.png",
"file": "retrobat.webp",
"platform": "retrobat",
"source": "https://raw.githubusercontent.com/RetroBat-Official/retrobat/main/system/resources/retrobat_logo_notext.png",
"owner": "RetroBat",
@@ -100,21 +100,21 @@
"licence": "repository licence GPL-3.0"
},
{
"file": "retropie.png",
"file": "retropie.webp",
"platform": "retropie",
"source": "https://avatars.githubusercontent.com/u/11378204",
"owner": "RetroPie",
"licence": "GitHub organisation avatar of RetroPie"
},
{
"file": "rocknix.png",
"file": "rocknix.webp",
"platform": "rocknix",
"source": "https://avatars.githubusercontent.com/u/163176886",
"owner": "ROCKNIX",
"licence": "GitHub organisation avatar of ROCKNIX"
},
{
"file": "romm.png",
"file": "romm.webp",
"platform": "romm",
"source": "https://avatars.githubusercontent.com/u/164058018",
"owner": "RomM",
+12 -7
View File
@@ -8,8 +8,9 @@ into ``docs_assets/logos/`` and ``docs_assets/system-icons/``, which are
committed, and records where each came from in a ``sources.json`` beside
them. The site build only reads those copies (``sitediagrams.localize_images``).
Raster images are normalised to a square PNG (96 px for logos, 48 px for
system icons) so every tile has the same weight, and SVG is kept as published.
Raster logos are normalised to an 80 px square WebP, twice the 40 px they
are shown at, and system icons to a 48 px PNG, so every tile has the same
weight. SVG is kept as published.
Normalising needs Pillow. This is a maintenance tool run by hand when a
logo changes, not a build step: the build stays on the standard library
and PyYAML.
@@ -36,7 +37,7 @@ sys.path.insert(0, str(ROOT / "scripts"))
LOGO_DIR = ROOT / "docs_assets" / "logos"
ICON_DIR = ROOT / "docs_assets" / "system-icons"
LOGO_SIZE = 96
LOGO_SIZE = 80
ICON_SIZE = 48
AVATAR_HOST = "avatars.githubusercontent.com"
USER_AGENT = "retrobios-site-images"
@@ -47,7 +48,7 @@ def fetch(url: str) -> bytes:
return response.read()
def square_png(data: bytes, size: int) -> bytes:
def square_image(data: bytes, size: int, kind: str = "PNG") -> bytes:
try:
from PIL import Image
except ImportError as exc:
@@ -57,7 +58,11 @@ def square_png(data: bytes, size: int) -> bytes:
tile = Image.new("RGBA", (size, size), (0, 0, 0, 0))
tile.paste(image, ((size - image.width) // 2, (size - image.height) // 2), image)
out = io.BytesIO()
tile.save(out, "PNG", optimize=True)
if kind == "WEBP":
# Quality 90: half the bytes of lossless, edges still clean at 40 px.
tile.save(out, "WEBP", quality=90, method=6)
else:
tile.save(out, "PNG", optimize=True)
return out.getvalue()
@@ -98,7 +103,7 @@ def fetch_logos() -> None:
if is_svg(data):
file_name, payload = f"{name}.svg", safe_svg(data, url)
else:
file_name, payload = f"{name}.png", square_png(data, LOGO_SIZE)
file_name, payload = f"{name}.webp", square_image(data, LOGO_SIZE, "WEBP")
for stale in LOGO_DIR.glob(f"{name}.*"):
if stale.name != file_name:
stale.unlink()
@@ -141,7 +146,7 @@ def fetch_icons() -> None:
continue
raise
file_name = f"{name}.png"
(ICON_DIR / file_name).write_bytes(square_png(data, ICON_SIZE))
(ICON_DIR / file_name).write_bytes(square_image(data, ICON_SIZE))
entries.append({"file": file_name, "source": url})
print(f" {file_name}")
write_manifest(