feat: add go icon support

This commit is contained in:
2026-08-24 13:25:03 +08:00
parent 93b48f2264
commit c9740206d4
6 changed files with 75 additions and 40 deletions
+2 -2
View File
@@ -2,8 +2,8 @@ import logging
from pathlib import Path
from .assemblicon.context import Context
from .assemblicon.utils import setup_logging
from .image_icons import build_image_icons
from .std_icons import build_standard_icons
from .ext_icons import build_ext_icons
def main() -> None:
@@ -22,4 +22,4 @@ def main() -> None:
def _build_icons(ctx: Context) -> None:
build_standard_icons(ctx)
#build_image_icons(ctx)
#build_ext_icons(ctx)
@@ -1,10 +1,19 @@
import logging
from pathlib import Path
import PIL.Image
import PIL.ImageFile
def load_artifact(p: Path) -> PIL.Image.ImageFile.ImageFile:
def load_asset(p: Path) -> PIL.ImageFile.ImageFile:
logging.info("Loading asset: %s", p)
return PIL.Image.open(p, "r", ["PNG", "JPEG", "BMP"])
def load_artifact(p: Path) -> PIL.ImageFile.ImageFile:
logging.info("Loading artifact: %s", p)
return PIL.Image.open(p, "r", ["PNG"])
def save_artifact(art: PIL.Image.Image, p: Path) -> None:
logging.info("Saving artifact: %s", p)
art.save(p, format="PNG")
@@ -3,11 +3,11 @@ import os
import subprocess
from pathlib import Path
from dataclasses import dataclass
import PIL.Image
import PIL.ImageFile
from .context import Context
from .utils import Artifact
from .artio import load_artifact
@dataclass
@@ -25,8 +25,12 @@ class InkscapeConfig:
f"The image height in Inkscape config must be a positive integer"
)
# TODO: Add "resvg" alternative for Inkscape
# because Inkscape is too slow.
def inkscape_render(src: Path, dst: Path, cfg: InkscapeConfig) -> None:
def inkscape_render(
src: Path, dst: Path, cfg: InkscapeConfig
) -> PIL.ImageFile.ImageFile:
logging.info("Rendering Inkscape artifact: %s -> %s", src, dst)
inkscape_bin = os.getenv("ASSEMBLICON_INKSCAPE", "inkscape")
@@ -55,6 +59,8 @@ def inkscape_render(src: Path, dst: Path, cfg: InkscapeConfig) -> None:
f"Fail to execute Inkscape. Return code is {proc.returncode}"
)
return load_artifact(dst)
@dataclass
class BlenderConfig:
@@ -96,9 +102,11 @@ except BaseException:
"""
def blender_render(ctx: Context, src: Path, dst: Path, cfg: BlenderConfig) -> None:
def blender_render(
ctx: Context, src: Path, dst: Path, cfg: BlenderConfig
) -> PIL.ImageFile.ImageFile:
logging.info("Rendering Blender artifact: %s -> %s", src, dst)
script = _BLENDER_RENDER_SCRIPT.format(
width=cfg.width, height=cfg.height, dst=repr(str(dst))
)
@@ -115,14 +123,14 @@ def blender_render(ctx: Context, src: Path, dst: Path, cfg: BlenderConfig) -> No
]
proc = subprocess.run(cmd, capture_output=False)
if proc.returncode != 0:
raise RuntimeError(
f"Fail to execute Blender. Return code is {proc.returncode}"
)
raise RuntimeError(f"Fail to execute Blender. Return code is {proc.returncode}")
if not dst.exists():
raise RuntimeError(
f"Blender exited successfully but output file {dst} is missing"
)
return load_artifact(dst)
_ICON_ART_SIZE = 1024
@@ -0,0 +1,6 @@
from ..assemblicon.context import Context
from .image_icons import build_image_icons
def build_ext_icons(ctx: Context) -> None:
build_image_icons(ctx)
@@ -1,10 +1,11 @@
import PIL.Image
import PIL.ImageEnhance
from .assemblicon.render import inkscape_render, ico_render, InkscapeConfig
from .assemblicon.context import Context
from .assemblicon.utils import Artifact
from .assemblicon.geometry import MinMaxRect, PosSizeRect
from .assemblicon.artproc import (
from ..assemblicon.render import inkscape_render, ico_render, InkscapeConfig
from ..assemblicon.context import Context
from ..assemblicon.utils import Artifact
from ..assemblicon.geometry import MinMaxRect, PosSizeRect
from ..assemblicon.artio import load_asset, save_artifact
from ..assemblicon.artproc import (
aspect_ratio_scale_and_crop,
VerticalAnchor,
HorizontalAnchor,
@@ -16,24 +17,23 @@ IMAGE_INNER_RECT_POSSIZE = IMAGE_INNER_RECT_MINMAX.to_pos_size_rect()
def build_image_icons(ctx: Context) -> None:
# build image base
inkscape_render(
image_base = inkscape_render(
ctx.input_artifact("component", "image-base.svg"),
ctx.temporary_artifact("component", "image-base.png"),
InkscapeConfig(1024, 1024),
)
# load image base
with PIL.Image.open(
ctx.temporary_artifact("component", "image-base.png")
) as image_base:
with image_base:
_build_jpg_icon(ctx, image_base)
def _build_jpg_icon(ctx: Context, base: Artifact) -> None:
jpg_image = base.copy()
with PIL.Image.open(
jpg_image_inner = load_asset(
ctx.input_artifact("ext", "bitmap", "Niinsaare_järv.jpg")
) as jpg_image_inner:
)
with jpg_image_inner:
# crop the area which is interested by ourselves
interest_area = PosSizeRect(2700, 0, 5016, 3888).to_min_max_rect()
crop_jpg_image_inner = jpg_image_inner.crop(interest_area.to_pillow_tuple())
@@ -59,7 +59,7 @@ def _build_jpg_icon(ctx: Context, base: Artifact) -> None:
bestfit_jpg_image_inner, IMAGE_INNER_RECT_POSSIZE.to_pillow_tuple()
)
# build artifacts
jpg_image.save(ctx.temporary_artifact("ext", "jpg-image.png"))
save_artifact(jpg_image, ctx.temporary_artifact("ext", "jpg-image.png"))
ico_render(
ctx,
jpg_image,
@@ -1,14 +1,9 @@
from pathlib import Path
import PIL.Image
from ..assemblicon.render import inkscape_render, ico_render, InkscapeConfig
from ..assemblicon.render import inkscape_render, InkscapeConfig
from ..assemblicon.context import Context
from ..assemblicon.utils import Artifact
from ..assemblicon.geometry import MinMaxRect, PosSizeRect
from ..assemblicon.artproc import (
aspect_ratio_scale_and_crop,
VerticalAnchor,
HorizontalAnchor,
)
from ..assemblicon.artio import save_artifact
def build_action_icons(ctx: Context) -> None:
@@ -17,27 +12,44 @@ def build_action_icons(ctx: Context) -> None:
def _build_go_icons(ctx: Context) -> None:
# build go-* base
inkscape_render(
go_base = inkscape_render(
ctx.input_artifact("component", "go-base.svg"),
ctx.temporary_artifact("component", "go-base.png"),
InkscapeConfig(1024, 1024),
)
# build go-* shining
inkscape_render(
go_shining = inkscape_render(
ctx.input_artifact("component", "go-inner-shining.svg"),
ctx.temporary_artifact("component", "go-inner-shining.png"),
InkscapeConfig(1024, 1024),
)
with PIL.Image.open(ctx.temporary_artifact("component", "go-base.png")) as go_base:
with PIL.Image.open(
ctx.temporary_artifact("component", "go-inner-shining.png")
) as go_shining:
_build_go_icon(ctx, go_base, go_shining, ctx.input_artifact("go-previous.svg"))
_build_go_icon(ctx, go_base, go_shining, ctx.input_artifact("go-next.svg"))
with go_base, go_shining:
_build_go_icon(
ctx,
go_base,
go_shining,
ctx.input_artifact("go-previous.svg"),
ctx.temporary_artifact("go-previous.png"),
ctx.output_artifact("go-previous.png")
)
_build_go_icon(
ctx,
go_base,
go_shining,
ctx.input_artifact("go-next.svg"),
ctx.temporary_artifact("go-next.png"),
ctx.output_artifact("go-next.png")
)
def _build_go_icon(ctx: Context, base: Artifact, shining: Artifact, mask: Path) -> None:
def _build_go_icon(
ctx: Context, base: Artifact, shining: Artifact, mask: Path, mask_dst: Path, dst: Path
) -> None:
go = base.copy()
# render shining with mask
go_mask = inkscape_render(mask, mask_dst, InkscapeConfig(1024, 1024))
go.paste(shining, (0, 0), go_mask)
# save as image
save_artifact(go, dst)