feat: add app icons
This commit is contained in:
@@ -28,5 +28,5 @@ def main() -> None:
|
|||||||
|
|
||||||
|
|
||||||
def _build_icons(ctx: AdvancedContext) -> None:
|
def _build_icons(ctx: AdvancedContext) -> None:
|
||||||
build_standard_icons(ctx)
|
|
||||||
build_non_standard_icons(ctx)
|
build_non_standard_icons(ctx)
|
||||||
|
#build_standard_icons(ctx)
|
||||||
|
|||||||
@@ -5,6 +5,15 @@ import PIL.Image
|
|||||||
from .utils import Artifact, ArtifactFile
|
from .utils import Artifact, ArtifactFile
|
||||||
|
|
||||||
|
|
||||||
|
def new_artifact(size: tuple[int, int]) -> Artifact:
|
||||||
|
"""
|
||||||
|
Create new RGBA empty (#ffffff00) artifact with given size.
|
||||||
|
"""
|
||||||
|
return PIL.Image.new(
|
||||||
|
"RGBA", size, "#ffffff00"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def load_input_artifact(p: Path) -> ArtifactFile:
|
def load_input_artifact(p: Path) -> ArtifactFile:
|
||||||
"""
|
"""
|
||||||
Load input artifact used for building temporary and output artifact.
|
Load input artifact used for building temporary and output artifact.
|
||||||
|
|||||||
@@ -15,9 +15,7 @@ class VerticalAnchor(enum.IntEnum):
|
|||||||
Bottom = enum.auto()
|
Bottom = enum.auto()
|
||||||
|
|
||||||
|
|
||||||
def _anchor_offset(
|
def _anchor_offset(oversize: int, anchor: HorizontalAnchor | VerticalAnchor) -> int:
|
||||||
oversize: int, anchor: HorizontalAnchor | VerticalAnchor
|
|
||||||
) -> int:
|
|
||||||
match anchor:
|
match anchor:
|
||||||
case HorizontalAnchor.Left | VerticalAnchor.Top:
|
case HorizontalAnchor.Left | VerticalAnchor.Top:
|
||||||
return 0
|
return 0
|
||||||
@@ -47,12 +45,69 @@ def aspect_ratio_scale_and_crop(
|
|||||||
scaled_height = math.ceil(art.height * scale)
|
scaled_height = math.ceil(art.height * scale)
|
||||||
scaled = art.resize((scaled_width, scaled_height), scale_resample)
|
scaled = art.resize((scaled_width, scaled_height), scale_resample)
|
||||||
|
|
||||||
crop_x = _anchor_offset(
|
crop_x = _anchor_offset(scaled_width - target_width, crop_horizontal_anchor)
|
||||||
scaled_width - target_width, crop_horizontal_anchor
|
crop_y = _anchor_offset(scaled_height - target_height, crop_vertical_anchor)
|
||||||
|
return scaled.crop((crop_x, crop_y, crop_x + target_width, crop_y + target_height))
|
||||||
|
|
||||||
|
|
||||||
|
def anchor_to_pos(
|
||||||
|
art: Artifact,
|
||||||
|
horizontal_anchor: HorizontalAnchor,
|
||||||
|
vertical_anchor: VerticalAnchor,
|
||||||
|
) -> tuple[int, int]:
|
||||||
|
"""
|
||||||
|
Resolve the anchor point position inside given artifact.
|
||||||
|
|
||||||
|
:param art: The artifact whose anchor point should be resolved.
|
||||||
|
:param horizontal_anchor: The anchor selection at horizontal direction.
|
||||||
|
:param vertical_anchor: The anchor selection at vertical direction.
|
||||||
|
:return: The (x, y) coordinate of the anchor point. For Left/Top
|
||||||
|
anchors this is the corresponding edge; for Right/Bottom anchors
|
||||||
|
this is just outside the artifact (the exact corner coordinate).
|
||||||
|
"""
|
||||||
|
return (
|
||||||
|
_anchor_offset(art.width, horizontal_anchor),
|
||||||
|
_anchor_offset(art.height, vertical_anchor),
|
||||||
)
|
)
|
||||||
crop_y = _anchor_offset(
|
|
||||||
scaled_height - target_height, crop_vertical_anchor
|
|
||||||
|
def align_paste(
|
||||||
|
art: Artifact,
|
||||||
|
clipboard: Artifact,
|
||||||
|
art_pos: tuple[int, int],
|
||||||
|
clipboard_pos: tuple[int, int],
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Paste clipboard onto given artifact with two anchor points aligned.
|
||||||
|
|
||||||
|
The clipboard is pasted in the way that the point indicated by
|
||||||
|
clipboard_pos on the clipboard exactly overlaps the point indicated
|
||||||
|
by art_pos on the artifact. The art is modified in place. Positions
|
||||||
|
resolving to negative paste origins are allowed and clipped by PIL
|
||||||
|
just like a normal paste call.
|
||||||
|
|
||||||
|
:param art: The artifact pasted onto. Modified in place.
|
||||||
|
:param clipboard: The artifact pasted as the clipboard.
|
||||||
|
:param art_pos: The (x, y) point on the art to be aligned, usually
|
||||||
|
resolved by anchor_to_pos.
|
||||||
|
:param clipboard_pos: The (x, y) point on the clipboard to be
|
||||||
|
aligned, usually resolved by anchor_to_pos.
|
||||||
|
"""
|
||||||
|
art.paste(
|
||||||
|
clipboard,
|
||||||
|
(art_pos[0] - clipboard_pos[0], art_pos[1] - clipboard_pos[1]),
|
||||||
)
|
)
|
||||||
return scaled.crop(
|
|
||||||
(crop_x, crop_y, crop_x + target_width, crop_y + target_height)
|
|
||||||
|
def align_alpha_composite(
|
||||||
|
art: Artifact,
|
||||||
|
clipboard: Artifact,
|
||||||
|
art_pos: tuple[int, int],
|
||||||
|
clipboard_pos: tuple[int, int],
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Alpha-supported ``align_paste``.
|
||||||
|
"""
|
||||||
|
art.alpha_composite(
|
||||||
|
clipboard, (art_pos[0] - clipboard_pos[0], art_pos[1] - clipboard_pos[1])
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ _DEFAULT_BLD_CONFIG = artrender.BlenderConfig(VANILLA_ARTIFACT_HW, VANILLA_ARTIF
|
|||||||
|
|
||||||
# TODO: For easy testing, we only build 32x32 and 48x48.
|
# TODO: For easy testing, we only build 32x32 and 48x48.
|
||||||
# Enable full generation after testing.
|
# Enable full generation after testing.
|
||||||
_FD_THUMBNAIL_HWS: tuple[int, ...] = (32, 48)
|
_FD_THUMBNAIL_HWS: tuple[int, ...] = (32, 48, 1024)
|
||||||
# _FD_THUMBNAIL_HWS: tuple[int, ...] = (16, 32, 48, 64, 128, 256)
|
# _FD_THUMBNAIL_HWS: tuple[int, ...] = (16, 32, 48, 64, 128, 256)
|
||||||
|
|
||||||
|
|
||||||
@@ -148,6 +148,16 @@ class AdvancedContext:
|
|||||||
) -> None:
|
) -> None:
|
||||||
self.__context.__exit__(exc_type, exc_value, traceback)
|
self.__context.__exit__(exc_type, exc_value, traceback)
|
||||||
|
|
||||||
|
# region: New Artifact
|
||||||
|
|
||||||
|
def new_artifact(self) -> Artifact:
|
||||||
|
"""
|
||||||
|
Create new RGBA vanilla size (1024x1024) empty (#ffffff00) artifact.
|
||||||
|
"""
|
||||||
|
return artio.new_artifact((VANILLA_ARTIFACT_HW, VANILLA_ARTIFACT_HW))
|
||||||
|
|
||||||
|
# endregion
|
||||||
|
|
||||||
# region: Input Artifact
|
# region: Input Artifact
|
||||||
|
|
||||||
def input_artifact(self, *args: str) -> Path:
|
def input_artifact(self, *args: str) -> Path:
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
from ..assemblicon.context import AdvancedContext
|
from ..assemblicon.context import AdvancedContext
|
||||||
from .mimetype_icons import build_mimetype_icons
|
from .mimetype_icons import build_mimetype_icons
|
||||||
|
from .application_icons import build_application_icons
|
||||||
|
|
||||||
|
|
||||||
def build_non_standard_icons(ctx: AdvancedContext) -> None:
|
def build_non_standard_icons(ctx: AdvancedContext) -> None:
|
||||||
build_mimetype_icons(ctx)
|
build_mimetype_icons(ctx)
|
||||||
|
build_application_icons(ctx)
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import PIL.Image
|
||||||
|
from ..assemblicon.context import AdvancedContext, FdContext, WinCategory, MacCategory
|
||||||
|
from ..assemblicon.artproc import (
|
||||||
|
anchor_to_pos,
|
||||||
|
align_paste,
|
||||||
|
VerticalAnchor,
|
||||||
|
HorizontalAnchor,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def build_application_icons(ctx: AdvancedContext) -> None:
|
||||||
|
_build_sarasacw_picture_icons(ctx)
|
||||||
|
|
||||||
|
|
||||||
|
def _build_sarasacw_picture_icons(ctx: AdvancedContext) -> None:
|
||||||
|
app = ctx.new_artifact()
|
||||||
|
frame = ctx.render_svg_then_load(
|
||||||
|
ctx.input_artifact("app", "sarasacw-picture", "app-frame.svg")
|
||||||
|
)
|
||||||
|
|
||||||
|
inner = ctx.load_temporary_artifact("image-jpeg.png")
|
||||||
|
resized_inner = inner.resize((990, 990), PIL.Image.Resampling.LANCZOS)
|
||||||
|
|
||||||
|
align_paste(
|
||||||
|
app,
|
||||||
|
resized_inner,
|
||||||
|
anchor_to_pos(app, HorizontalAnchor.Center, VerticalAnchor.Center),
|
||||||
|
anchor_to_pos(resized_inner, HorizontalAnchor.Center, VerticalAnchor.Center),
|
||||||
|
)
|
||||||
|
app.alpha_composite(frame, (0, 0))
|
||||||
|
|
||||||
|
# save artifact
|
||||||
|
ctx.save_fd_artifact(app, FdContext.Applications, "org.sarasachipworkshop.picture")
|
||||||
|
ctx.save_win_artifact(app, WinCategory.Application, "sarasacw-picture")
|
||||||
|
ctx.save_mac_artifact(app, MacCategory.Application, "sarasacw-picture")
|
||||||
@@ -62,6 +62,8 @@ def _build_jpg_icon(ctx: AdvancedContext, base: Artifact) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# build artifacts
|
# build artifacts
|
||||||
|
# save as temporary for future use
|
||||||
|
ctx.save_temporary_artifact(jpg_image, "image-jpeg.png")
|
||||||
# save for FD
|
# save for FD
|
||||||
ctx.save_fd_artifact(jpg_image, FdContext.MimeTypes, "image-jpeg")
|
ctx.save_fd_artifact(jpg_image, FdContext.MimeTypes, "image-jpeg")
|
||||||
# use image/jpeg as the default image icon
|
# use image/jpeg as the default image icon
|
||||||
|
|||||||
@@ -36,13 +36,17 @@ def _build_go_icons(ctx: AdvancedContext) -> None:
|
|||||||
|
|
||||||
def _build_object_flip_icons(ctx: AdvancedContext) -> None:
|
def _build_object_flip_icons(ctx: AdvancedContext) -> None:
|
||||||
art = ctx.render_svg_then_load(ctx.input_artifact("object-flip-horizontal.svg"))
|
art = ctx.render_svg_then_load(ctx.input_artifact("object-flip-horizontal.svg"))
|
||||||
ctx.save_fd_artifact(art, FdContext.Actions, "object-flip-horizontal")
|
with art:
|
||||||
|
ctx.save_fd_artifact(art, FdContext.Actions, "object-flip-horizontal")
|
||||||
art = ctx.render_svg_then_load(ctx.input_artifact("object-flip-vertical.svg"))
|
art = ctx.render_svg_then_load(ctx.input_artifact("object-flip-vertical.svg"))
|
||||||
ctx.save_fd_artifact(art, FdContext.Actions, "object-flip-vertical")
|
with art:
|
||||||
|
ctx.save_fd_artifact(art, FdContext.Actions, "object-flip-vertical")
|
||||||
|
|
||||||
|
|
||||||
def _build_object_rotate_icons(ctx: AdvancedContext) -> None:
|
def _build_object_rotate_icons(ctx: AdvancedContext) -> None:
|
||||||
art = ctx.render_svg_then_load(ctx.input_artifact("object-rotate-left.svg"))
|
art = ctx.render_svg_then_load(ctx.input_artifact("object-rotate-left.svg"))
|
||||||
ctx.save_fd_artifact(art, FdContext.Actions, "object-rotate-left")
|
with art:
|
||||||
|
ctx.save_fd_artifact(art, FdContext.Actions, "object-rotate-left")
|
||||||
art = ctx.render_svg_then_load(ctx.input_artifact("object-rotate-right.svg"))
|
art = ctx.render_svg_then_load(ctx.input_artifact("object-rotate-right.svg"))
|
||||||
ctx.save_fd_artifact(art, FdContext.Actions, "object-rotate-right")
|
with art:
|
||||||
|
ctx.save_fd_artifact(art, FdContext.Actions, "object-rotate-right")
|
||||||
|
|||||||
Reference in New Issue
Block a user