43 lines
1.6 KiB
Python
43 lines
1.6 KiB
Python
"""Pre-scale the carousel imagery so browsers never upscale it.
|
|
|
|
The refined fabric shots top out at 1024-1376px, but the full-bleed hero paints
|
|
them across up to ~1920 CSS px (3840 device px on retina). Browsers enlarge them
|
|
at render time and the knit stitches smear. Resampling once with Lanczos plus a
|
|
light unsharp pass keeps the texture readable and, at these sizes, costs about
|
|
the same bytes as the originals. Requires Pillow. Generated assets are committed;
|
|
runtime needs nothing.
|
|
"""
|
|
from pathlib import Path
|
|
import sys
|
|
from PIL import Image, ImageFilter
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
TARGETS = [ROOT / "public" / "images" / "refined" / "hero",
|
|
ROOT / "public" / "images" / "refined" / "products"]
|
|
LONG_EDGE = 2048
|
|
QUALITY = 82
|
|
|
|
|
|
def optimize(path):
|
|
with Image.open(path) as source:
|
|
image = source.convert("RGB")
|
|
scale = LONG_EDGE / max(image.size)
|
|
if scale > 1:
|
|
image = image.resize(
|
|
(round(image.width * scale), round(image.height * scale)),
|
|
Image.Resampling.LANCZOS,
|
|
)
|
|
image = image.filter(ImageFilter.UnsharpMask(radius=1.0, percent=60, threshold=3))
|
|
before = path.stat().st_size
|
|
image.save(path, quality=QUALITY, optimize=True, progressive=True)
|
|
after = path.stat().st_size
|
|
print(f"{path.relative_to(ROOT)} {image.size} {before // 1024}KB -> {after // 1024}KB")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
files = sorted(p for target in TARGETS for p in target.rglob("*.jpeg"))
|
|
if not files:
|
|
sys.exit("no carousel images found")
|
|
for file in files:
|
|
optimize(file)
|