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 pathlib import Path
from .assemblicon.context import Context from .assemblicon.context import Context
from .assemblicon.utils import setup_logging from .assemblicon.utils import setup_logging
from .image_icons import build_image_icons
from .std_icons import build_standard_icons from .std_icons import build_standard_icons
from .ext_icons import build_ext_icons
def main() -> None: def main() -> None:
@@ -22,4 +22,4 @@ def main() -> None:
def _build_icons(ctx: Context) -> None: def _build_icons(ctx: Context) -> None:
build_standard_icons(ctx) build_standard_icons(ctx)
#build_image_icons(ctx) #build_ext_icons(ctx)
@@ -1,10 +1,19 @@
import logging
from pathlib import Path from pathlib import Path
import PIL.Image 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"]) return PIL.Image.open(p, "r", ["PNG"])
def save_artifact(art: PIL.Image.Image, p: Path) -> None: def save_artifact(art: PIL.Image.Image, p: Path) -> None:
logging.info("Saving artifact: %s", p)
art.save(p, format="PNG") art.save(p, format="PNG")
@@ -3,11 +3,11 @@ import os
import subprocess import subprocess
from pathlib import Path from pathlib import Path
from dataclasses import dataclass from dataclasses import dataclass
import PIL.Image import PIL.Image
import PIL.ImageFile
from .context import Context from .context import Context
from .utils import Artifact from .utils import Artifact
from .artio import load_artifact
@dataclass @dataclass
@@ -25,8 +25,12 @@ class InkscapeConfig:
f"The image height in Inkscape config must be a positive integer" 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) logging.info("Rendering Inkscape artifact: %s -> %s", src, dst)
inkscape_bin = os.getenv("ASSEMBLICON_INKSCAPE", "inkscape") 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}" f"Fail to execute Inkscape. Return code is {proc.returncode}"
) )
return load_artifact(dst)
@dataclass @dataclass
class BlenderConfig: class BlenderConfig:
@@ -96,7 +102,9 @@ 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) logging.info("Rendering Blender artifact: %s -> %s", src, dst)
script = _BLENDER_RENDER_SCRIPT.format( script = _BLENDER_RENDER_SCRIPT.format(
@@ -115,14 +123,14 @@ def blender_render(ctx: Context, src: Path, dst: Path, cfg: BlenderConfig) -> No
] ]
proc = subprocess.run(cmd, capture_output=False) proc = subprocess.run(cmd, capture_output=False)
if proc.returncode != 0: if proc.returncode != 0:
raise RuntimeError( raise RuntimeError(f"Fail to execute Blender. Return code is {proc.returncode}")
f"Fail to execute Blender. Return code is {proc.returncode}"
)
if not dst.exists(): if not dst.exists():
raise RuntimeError( raise RuntimeError(
f"Blender exited successfully but output file {dst} is missing" f"Blender exited successfully but output file {dst} is missing"
) )
return load_artifact(dst)
_ICON_ART_SIZE = 1024 _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.Image
import PIL.ImageEnhance import PIL.ImageEnhance
from .assemblicon.render import inkscape_render, ico_render, InkscapeConfig from ..assemblicon.render import inkscape_render, ico_render, InkscapeConfig
from .assemblicon.context import Context from ..assemblicon.context import Context
from .assemblicon.utils import Artifact from ..assemblicon.utils import Artifact
from .assemblicon.geometry import MinMaxRect, PosSizeRect from ..assemblicon.geometry import MinMaxRect, PosSizeRect
from .assemblicon.artproc import ( from ..assemblicon.artio import load_asset, save_artifact
from ..assemblicon.artproc import (
aspect_ratio_scale_and_crop, aspect_ratio_scale_and_crop,
VerticalAnchor, VerticalAnchor,
HorizontalAnchor, HorizontalAnchor,
@@ -16,24 +17,23 @@ IMAGE_INNER_RECT_POSSIZE = IMAGE_INNER_RECT_MINMAX.to_pos_size_rect()
def build_image_icons(ctx: Context) -> None: def build_image_icons(ctx: Context) -> None:
# build image base # build image base
inkscape_render( image_base = inkscape_render(
ctx.input_artifact("component", "image-base.svg"), ctx.input_artifact("component", "image-base.svg"),
ctx.temporary_artifact("component", "image-base.png"), ctx.temporary_artifact("component", "image-base.png"),
InkscapeConfig(1024, 1024), InkscapeConfig(1024, 1024),
) )
# load image base # load image base
with PIL.Image.open( with image_base:
ctx.temporary_artifact("component", "image-base.png")
) as image_base:
_build_jpg_icon(ctx, image_base) _build_jpg_icon(ctx, image_base)
def _build_jpg_icon(ctx: Context, base: Artifact) -> None: def _build_jpg_icon(ctx: Context, base: Artifact) -> None:
jpg_image = base.copy() jpg_image = base.copy()
with PIL.Image.open( jpg_image_inner = load_asset(
ctx.input_artifact("ext", "bitmap", "Niinsaare_järv.jpg") 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 # crop the area which is interested by ourselves
interest_area = PosSizeRect(2700, 0, 5016, 3888).to_min_max_rect() interest_area = PosSizeRect(2700, 0, 5016, 3888).to_min_max_rect()
crop_jpg_image_inner = jpg_image_inner.crop(interest_area.to_pillow_tuple()) 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() bestfit_jpg_image_inner, IMAGE_INNER_RECT_POSSIZE.to_pillow_tuple()
) )
# build artifacts # 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( ico_render(
ctx, ctx,
jpg_image, jpg_image,
@@ -1,14 +1,9 @@
from pathlib import Path from pathlib import Path
import PIL.Image 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.context import Context
from ..assemblicon.utils import Artifact from ..assemblicon.utils import Artifact
from ..assemblicon.geometry import MinMaxRect, PosSizeRect from ..assemblicon.artio import save_artifact
from ..assemblicon.artproc import (
aspect_ratio_scale_and_crop,
VerticalAnchor,
HorizontalAnchor,
)
def build_action_icons(ctx: Context) -> None: 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: def _build_go_icons(ctx: Context) -> None:
# build go-* base # build go-* base
inkscape_render( go_base = inkscape_render(
ctx.input_artifact("component", "go-base.svg"), ctx.input_artifact("component", "go-base.svg"),
ctx.temporary_artifact("component", "go-base.png"), ctx.temporary_artifact("component", "go-base.png"),
InkscapeConfig(1024, 1024), InkscapeConfig(1024, 1024),
) )
# build go-* shining # build go-* shining
inkscape_render( go_shining = inkscape_render(
ctx.input_artifact("component", "go-inner-shining.svg"), ctx.input_artifact("component", "go-inner-shining.svg"),
ctx.temporary_artifact("component", "go-inner-shining.png"), ctx.temporary_artifact("component", "go-inner-shining.png"),
InkscapeConfig(1024, 1024), InkscapeConfig(1024, 1024),
) )
with PIL.Image.open(ctx.temporary_artifact("component", "go-base.png")) as go_base: with go_base, go_shining:
with PIL.Image.open( _build_go_icon(
ctx.temporary_artifact("component", "go-inner-shining.png") ctx,
) as go_shining: go_base,
_build_go_icon(ctx, go_base, go_shining, ctx.input_artifact("go-previous.svg")) go_shining,
_build_go_icon(ctx, go_base, go_shining, ctx.input_artifact("go-next.svg")) 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() 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)