feat: add app icons
This commit is contained in:
@@ -28,5 +28,5 @@ def main() -> None:
|
||||
|
||||
|
||||
def _build_icons(ctx: AdvancedContext) -> None:
|
||||
build_standard_icons(ctx)
|
||||
build_non_standard_icons(ctx)
|
||||
#build_standard_icons(ctx)
|
||||
|
||||
@@ -5,6 +5,15 @@ import PIL.Image
|
||||
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:
|
||||
"""
|
||||
Load input artifact used for building temporary and output artifact.
|
||||
|
||||
@@ -15,9 +15,7 @@ class VerticalAnchor(enum.IntEnum):
|
||||
Bottom = enum.auto()
|
||||
|
||||
|
||||
def _anchor_offset(
|
||||
oversize: int, anchor: HorizontalAnchor | VerticalAnchor
|
||||
) -> int:
|
||||
def _anchor_offset(oversize: int, anchor: HorizontalAnchor | VerticalAnchor) -> int:
|
||||
match anchor:
|
||||
case HorizontalAnchor.Left | VerticalAnchor.Top:
|
||||
return 0
|
||||
@@ -47,12 +45,69 @@ def aspect_ratio_scale_and_crop(
|
||||
scaled_height = math.ceil(art.height * scale)
|
||||
scaled = art.resize((scaled_width, scaled_height), scale_resample)
|
||||
|
||||
crop_x = _anchor_offset(
|
||||
scaled_width - target_width, crop_horizontal_anchor
|
||||
crop_x = _anchor_offset(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.
|
||||
# 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)
|
||||
|
||||
|
||||
@@ -148,6 +148,16 @@ class AdvancedContext:
|
||||
) -> None:
|
||||
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
|
||||
|
||||
def input_artifact(self, *args: str) -> Path:
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
from ..assemblicon.context import AdvancedContext
|
||||
from .mimetype_icons import build_mimetype_icons
|
||||
from .application_icons import build_application_icons
|
||||
|
||||
|
||||
def build_non_standard_icons(ctx: AdvancedContext) -> None:
|
||||
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
|
||||
# save as temporary for future use
|
||||
ctx.save_temporary_artifact(jpg_image, "image-jpeg.png")
|
||||
# save for FD
|
||||
ctx.save_fd_artifact(jpg_image, FdContext.MimeTypes, "image-jpeg")
|
||||
# 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:
|
||||
art = ctx.render_svg_then_load(ctx.input_artifact("object-flip-horizontal.svg"))
|
||||
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"))
|
||||
with art:
|
||||
ctx.save_fd_artifact(art, FdContext.Actions, "object-flip-vertical")
|
||||
|
||||
|
||||
def _build_object_rotate_icons(ctx: AdvancedContext) -> None:
|
||||
art = ctx.render_svg_then_load(ctx.input_artifact("object-rotate-left.svg"))
|
||||
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"))
|
||||
with art:
|
||||
ctx.save_fd_artifact(art, FdContext.Actions, "object-rotate-right")
|
||||
|
||||
Reference in New Issue
Block a user