Compare commits

..
31 Commits
Author SHA1 Message Date
yyc12345 6a951c6d11 fix: fix sarasacw-picture name error 2026-09-22 16:38:01 +08:00
yyc12345 890019bc54 feat: adapt to new prologue with custom FreeDesktop thumbnail sizes 2026-09-10 09:28:53 +08:00
yyc12345 0d43fe3962 chore: use REUSE instead of non-standard license declaration 2026-09-09 22:02:14 +08:00
yyc12345 a33c3538f5 refactor: peel assemblicon off as indenpendent project
peel assemblicon off as indenpendent project
in SarasasChipWorkshop/assemblicon
2026-09-09 20:40:21 +08:00
yyc12345 72edad6edc doc: update README and LICENSE 2026-09-09 19:56:09 +08:00
yyc12345 2015ebf925 feat: cleanup bitmaps in references 2026-09-09 14:08:57 +08:00
yyc12345 1e15fb4783 feat: support monitor zoom fit best 2026-09-09 14:02:58 +08:00
yyc12345 42a2b08abf feat: support image-png/bmp/gif 2026-09-09 12:09:33 +08:00
yyc12345 61e003c741 fix: fix fatal source link issue 2026-09-08 22:29:50 +08:00
yyc12345 7b216b8865 doc: update TODO 2026-09-08 22:15:13 +08:00
yyc12345 36adf8029a feat: change src layout and write license infos 2026-09-08 22:13:59 +08:00
yyc12345 e0d6aa63e6 feat: add magnifying glass, zoom in, out, notepad icons 2026-09-08 14:31:15 +08:00
yyc12345 765375f04c feat: add plus/minus icons 2026-09-07 21:02:14 +08:00
yyc12345 a563917aa8 feat: split rotation and flip icon build functions 2026-09-07 20:52:24 +08:00
yyc12345 bb5032d502 feat: refactor (3/3) finish user side adaption 2026-09-06 14:38:04 +08:00
yyc12345 db04b9c809 fix: fix refactor issues again 2026-09-06 14:30:00 +08:00
yyc12345 44ba5fbbee fix: fix refactor issues 2026-09-06 13:59:32 +08:00
yyc12345 f650d3f98a feat: refactor assemblicon (2/3) context job topology 2026-09-06 13:35:47 +08:00
yyc12345 43e3bd39ef feat: refactor assemblicon (1/3) sink and source establishment 2026-09-05 22:56:14 +08:00
yyc12345 49edad7d33 feat: add todo 2026-09-03 19:33:07 +08:00
yyc12345 28fa01992e feat: bring source and sink back for future development 2026-09-03 19:13:35 +08:00
yyc12345 d13f18cc90 feat: finish image-zoom-original 2026-09-03 14:59:06 +08:00
yyc12345 da52ab5028 refactor: rename some job names 2026-09-02 17:08:35 +08:00
yyc12345 988a9fc04f refactor: fix aurora icon build error 2026-09-02 14:07:57 +08:00
yyc12345 63ba6d44f7 refactor: basically finish core refactor 2026-09-02 13:36:41 +08:00
yyc12345 bcac59ceb1 refactor: refactor assemblicon 2026-09-01 22:32:00 +08:00
yyc12345 54af04a48f feat: add zoom-original 2026-09-01 17:09:20 +08:00
yyc12345 e43029ea47 feat: add dialog icons and image properties 2026-09-01 15:27:11 +08:00
yyc12345 3a9a6d99cb feat: add app icons 2026-08-28 20:20:55 +08:00
yyc12345 892a946998 feat: add license info for all assets 2026-08-27 15:09:50 +08:00
yyc12345 9d20e60adb feat: add some action icons 2026-08-27 14:57:09 +08:00
33 changed files with 1270 additions and 916 deletions
+14
View File
@@ -0,0 +1,14 @@
# License
This project has mixed licencing.
You are free to copy, redistribute and/or modify aspects of this work under the terms of each licence accordingly.
## Icon Set Builder
The builder of this icon set is under **MIT License**.
The content of these part is under `tool` directory.
## Icon Set Assets
The assets for generating this icon set is under different licenses.
This project follows the [REUSE Specification](https://reuse.software/). You can collect the licensing information using the REUSE tool, or browse the `REUSE.toml` files located in their directory manually.
+9 -1
View File
@@ -5,4 +5,12 @@ Yet Another Aero Icon Set.
## Repository Notes ## Repository Notes
The content submitted in Git repository doesn't include any binary assets. The content submitted in Git repository doesn't include any binary assets.
It only includes code part of this icon set for tracing the change of build code. It only includes the code part of this repository for tracing the changes of code building this icon set.
## Status
Work in progress...
## License
See [LICENSES](LICENSE.md) for more details.
-15
View File
@@ -1,15 +0,0 @@
Ivar Leidus, CC BY-SA 3.0 <https://creativecommons.org/licenses/by-sa/3.0>, via Wikimedia Commons
https://commons.wikimedia.org/wiki/File:Niinsaare_j%C3%A4rv.jpg
Pascalou petit, CC BY-SA 3.0 <https://creativecommons.org/licenses/by-sa/3.0>, via Wikimedia Commons
https://commons.wikimedia.org/wiki/File:Aster_Tataricus.JPG
https://commons.wikimedia.org/wiki/File:Great_pershing_balloon_derby_2005_09_04.jpg
Balloon launch at the Great Pershing Balloon Derby near Brookfield, Missouri on September 4, 2005. Photo taken by Joe DeShon.
This file is licensed under the Creative Commons Attribution 2.5 Generic license.
https://commons.wikimedia.org/wiki/File:Wild_Petunia_Blue_Flower_1.JPG
I, Jonathan Zander, CC BY-SA 3.0 <http://creativecommons.org/licenses/by-sa/3.0/>, via Wikimedia Commons
https://commons.wikimedia.org/wiki/File:Daisy_(Argyranthemum_frutescens).jpg
Fir0002, CC BY-SA 3.0 <http://creativecommons.org/licenses/by-sa/3.0/>, via Wikimedia Commons
+8
View File
@@ -0,0 +1,8 @@
version = 1
[[annotations]]
path = "Nycticorax_nycticorax12.jpg"
precedence = "override"
SPDX-FileComment = "Source: https://commons.wikimedia.org/wiki/File:Nycticorax_nycticorax12.jpg"
SPDX-FileCopyrightText = "Calibas"
SPDX-License-Identifier = "CC-BY-SA-3.0"
+75
View File
@@ -0,0 +1,75 @@
version = 1
[[annotations]]
path = "go-base.svg"
precedence = "override"
SPDX-FileCopyrightText = "2026 yyc12345"
SPDX-License-Identifier = "CC-BY-SA-4.0"
[[annotations]]
path = "go-inner-shining.svg"
precedence = "override"
SPDX-FileCopyrightText = "2026 yyc12345"
SPDX-License-Identifier = "CC-BY-SA-4.0"
[[annotations]]
path = "go-inner.svg"
precedence = "override"
SPDX-FileComment = "Source: https://github.com/google/material-design-icon"
SPDX-FileCopyrightText = ["2014-2026 Google", "2026 yyc12345"]
SPDX-License-Identifier = "Apache-2.0"
[[annotations]]
path = "object-flip-horizontal.svg"
precedence = "override"
SPDX-FileComment = "Source: https://invent.kde.org/frameworks/oxygen-icons"
SPDX-FileCopyrightText = ["2015-2026 KDE Oxygen Icon Theme Team", "2026 yyc12345"]
SPDX-License-Identifier = "LGPL-2.1-only"
[[annotations]]
path = "object-flip-vertical.svg"
precedence = "override"
SPDX-FileComment = "Source: https://invent.kde.org/frameworks/oxygen-icons"
SPDX-FileCopyrightText = ["2015-2026 KDE Oxygen Icon Theme Team", "2026 yyc12345"]
SPDX-License-Identifier = "LGPL-2.1-only"
[[annotations]]
path = "object-rotate-left.svg"
precedence = "override"
SPDX-FileComment = "Source: https://invent.kde.org/frameworks/oxygen-icons"
SPDX-FileCopyrightText = ["2015-2026 KDE Oxygen Icon Theme Team", "2026 yyc12345"]
SPDX-License-Identifier = "LGPL-2.1-only"
[[annotations]]
path = "object-rotate-right.svg"
precedence = "override"
SPDX-FileComment = "Source: https://invent.kde.org/frameworks/oxygen-icons"
SPDX-FileCopyrightText = ["2015-2026 KDE Oxygen Icon Theme Team", "2026 yyc12345"]
SPDX-License-Identifier = "LGPL-2.1-only"
[[annotations]]
path = "image-png-checkerboard.svg"
precedence = "override"
SPDX-FileCopyrightText = "2026 yyc12345"
SPDX-License-Identifier = "CC-BY-SA-4.0"
[[annotations]]
path = "dialog-infomation.svg"
precedence = "override"
SPDX-FileComment = "Source: https://invent.kde.org/frameworks/oxygen-icons"
SPDX-FileCopyrightText = ["2015-2026 KDE Oxygen Icon Theme Team", "2026 yyc12345"]
SPDX-License-Identifier = "LGPL-2.1-only"
[[annotations]]
path = "dialog-warning.svg"
precedence = "override"
SPDX-FileComment = "Source: https://invent.kde.org/frameworks/oxygen-icons"
SPDX-FileCopyrightText = ["2015-2026 KDE Oxygen Icon Theme Team", "2026 yyc12345"]
SPDX-License-Identifier = "LGPL-2.1-only"
[[annotations]]
path = "dialog-error.svg"
precedence = "override"
SPDX-FileComment = "Source: https://invent.kde.org/frameworks/oxygen-icons"
SPDX-FileCopyrightText = "2015-2026 KDE Oxygen Icon Theme Team"
SPDX-License-Identifier = "LGPL-2.1-only"
+13
View File
@@ -0,0 +1,13 @@
version = 1
[[annotations]]
path = "sarasacw-picture-frame.svg"
precedence = "override"
SPDX-FileCopyrightText = "2026 yyc12345"
SPDX-License-Identifier = "CC-BY-SA-4.0"
[[annotations]]
path = "notepad.blend"
precedence = "override"
SPDX-FileCopyrightText = "2026 yyc12345"
SPDX-License-Identifier = "CC-BY-SA-4.0"
+29
View File
@@ -0,0 +1,29 @@
version = 1
[[annotations]]
path = "Niinsaare_järv.jpg"
precedence = "override"
SPDX-FileComment = "Source: https://commons.wikimedia.org/wiki/File:Niinsaare_j%C3%A4rv.jpg"
SPDX-FileCopyrightText = "2013 Ivar Leidus"
SPDX-License-Identifier = "CC-BY-SA-3.0"
[[annotations]]
path = "Tulipa_suaveolens_floriade_to_Canberra.jpg"
precedence = "override"
SPDX-FileComment = "Source: https://commons.wikimedia.org/wiki/File:Tulipa_suaveolens_floriade_to_Canberra.jpg"
SPDX-FileCopyrightText = "2005 John O'Neill"
SPDX-License-Identifier = "CC-BY-SA-3.0"
[[annotations]]
path = "Hot-air_balloon_in_Tambov_-_02.jpg"
precedence = "override"
SPDX-FileComment = "Source: https://commons.wikimedia.org/wiki/File:Hot-air_balloon_in_Tambov_-_02.jpg"
SPDX-FileCopyrightText = "2022 Alexander Novikov"
SPDX-License-Identifier = "CC-BY-SA-4.0"
[[annotations]]
path = "Van_Gogh_-_Starry_Night_-_Google_Art_Project.jpg"
precedence = "override"
SPDX-FileComment = "Source: https://commons.wikimedia.org/wiki/File:Van_Gogh_-_Starry_Night_-_Google_Art_Project.jpg"
SPDX-FileCopyrightText = ["1889 Vincent van Gogh", "Google Arts & Culture"]
SPDX-License-Identifier = "CC0-1.0" # Public Domain actually.
Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

+5
View File
@@ -0,0 +1,5 @@
Blue Background Node 1,#001fbe
Blue Background Node 2,#1439ff
Paper Background Node 1,#ffffff
Paper Backgroundr Node 2,#e3e3e3
1 Blue Background Node 1 #001fbe
2 Blue Background Node 2 #1439ff
3 Paper Background Node 1 #ffffff
4 Paper Backgroundr Node 2 #e3e3e3
+40
View File
@@ -0,0 +1,40 @@
version = 1
[[annotations]]
path = "image-base.svg"
precedence = "override"
SPDX-FileCopyrightText = "2026 yyc12345"
SPDX-License-Identifier = "CC-BY-SA-4.0"
[[annotations]]
path = "generic-background.svg"
precedence = "override"
SPDX-FileCopyrightText = "2026 yyc12345"
SPDX-License-Identifier = "CC-BY-SA-4.0"
[[annotations]]
path = "triangle.svg"
precedence = "override"
SPDX-FileComment = "Source: https://github.com/Templarian/MaterialDesign"
SPDX-FileCopyrightText = ["2014-2025 Austin Andrews", "2026 yyc12345"]
SPDX-License-Identifier = "Apache-2.0"
[[annotations]]
path = "magnifying-glass.blend"
precedence = "override"
SPDX-FileCopyrightText = "2026 yyc12345"
SPDX-License-Identifier = "CC-BY-SA-4.0"
[[annotations]]
path = "minus.svg"
precedence = "override"
SPDX-FileComment = "Source: https://github.com/google/material-design-icon"
SPDX-FileCopyrightText = ["2014-2026 Google", "2026 yyc12345"]
SPDX-License-Identifier = "Apache-2.0"
[[annotations]]
path = "plus.svg"
precedence = "override"
SPDX-FileComment = "Source: https://github.com/google/material-design-icon"
SPDX-FileCopyrightText = ["2014-2026 Google", "2026 yyc12345"]
SPDX-License-Identifier = "Apache-2.0"
-5
View File
@@ -1,5 +0,0 @@
[[licenses]]
filename = 'Niinsaare_järv.jpg'
license = "CC-BY-SA-3.0"
author = ["Ivar Leidus", "Wikimedia Commons"]
source = 'https://commons.wikimedia.org/wiki/File:Niinsaare_j%C3%A4rv.jpg'
+7
View File
@@ -0,0 +1,7 @@
version = 1
[[annotations]]
path = "**"
precedence = "override"
SPDX-FileCopyrightText = "2026 Sarasa's Chip Workshop"
SPDX-License-Identifier = "MIT"
+4 -1
View File
@@ -8,7 +8,7 @@ authors = [
] ]
requires-python = ">=3.13" requires-python = ">=3.13"
dependencies = [ dependencies = [
"pillow>=12.3.0", "assemblicon",
] ]
[project.scripts] [project.scripts]
@@ -17,3 +17,6 @@ aurora-iconset-builder = "aurora_iconset_builder:main"
[build-system] [build-system]
requires = ["uv_build>=0.8.0,<0.9"] requires = ["uv_build>=0.8.0,<0.9"]
build-backend = "uv_build" build-backend = "uv_build"
[tool.uv.sources]
assemblicon = { git = "https://github.com/SarasasChipWorkshop/assemblicon" }
+27 -15
View File
@@ -1,32 +1,44 @@
import logging
from pathlib import Path from pathlib import Path
from .assemblicon.context import Prerequisite, Context, AdvancedContext from assemblicon.highlevel.utils import DEFAULT_FD_THUMBNAIL_HWS
from .assemblicon.utils import setup_logging from assemblicon.highlevel.environment import Environment
from .std_icons import build_standard_icons from assemblicon.highlevel.context import Context
from .nostd_icons import build_non_standard_icons from assemblicon.logger import APP_LOGGER as LOGGER
from .cli import parse from .cli import parse
from .types import AuroraPrologue, AuroraContext
from . import std_icons, nostd_icons
def main() -> None: def main() -> None:
opts = parse() opts = parse()
setup_logging() def tempdir_initializer(p: Path) -> None:
# create our special temporary subdirectory
comp = p / "component"
comp.mkdir(parents=True, exist_ok=True)
repository_root = Path(__file__).resolve().parent.parent.parent.parent repository_root = Path(__file__).resolve().parent.parent.parent.parent
iconset_input = repository_root / "src" iconset_input = repository_root / "src"
iconset_output = repository_root / "artifact" iconset_output = repository_root / "artifact"
prereq = Prerequisite( prologue = AuroraPrologue(
iconset_input, iconset_output, opts.svg_render.to_assemblicon_type() iconset_input,
iconset_output,
opts.svg_render,
# TODO: For easy testing, we only build 32x32 and 48x48.
# Enable full generation after testing.
# DEFAULT_FD_THUMBNAIL_HWS,
(32, 48, 256),
tempdir_initializer,
) )
ctx = Context(prereq) env = Environment(prologue)
advctx = AdvancedContext(ctx) ctx = Context(env)
try: try:
_build_icons(advctx) register(ctx)
ctx.run(opts.selection)
except BaseException as e: except BaseException as e:
logging.fatal("Runtime error: %s", e) LOGGER.fatal("Runtime error: %s", e)
def _build_icons(ctx: AdvancedContext) -> None: def register(ctx: AuroraContext) -> None:
build_standard_icons(ctx) std_icons.register(ctx)
build_non_standard_icons(ctx) nostd_icons.register(ctx)
@@ -1,30 +0,0 @@
import logging
import shutil
from pathlib import Path
import PIL.Image
from .utils import Artifact, ArtifactFile
def load_input_artifact(p: Path) -> ArtifactFile:
"""
Load input artifact used for building temporary and output artifact.
"""
logging.info("Loading asset: %s", p)
return PIL.Image.open(p, "r", ["PNG", "JPEG", "BMP"])
def load_artifact(p: Path) -> ArtifactFile:
"""
Load artifact saved previously as the intermediary.
"""
logging.info("Loading artifact: %s", p)
return PIL.Image.open(p, "r", ["PNG"])
def save_artifact(art: Artifact, p: Path) -> None:
logging.info("Saving artifact: %s", p)
art.save(p, format="PNG")
def dup_artifact(src: Path, dst: Path) -> None:
shutil.copyfile(src, dst)
@@ -1,58 +0,0 @@
import enum
import math
from .utils import Artifact
class HorizontalAnchor(enum.IntEnum):
Left = enum.auto()
Center = enum.auto()
Right = enum.auto()
class VerticalAnchor(enum.IntEnum):
Top = enum.auto()
Center = enum.auto()
Bottom = enum.auto()
def _anchor_offset(
oversize: int, anchor: HorizontalAnchor | VerticalAnchor
) -> int:
match anchor:
case HorizontalAnchor.Left | VerticalAnchor.Top:
return 0
case HorizontalAnchor.Center | VerticalAnchor.Center:
return oversize // 2
case HorizontalAnchor.Right | VerticalAnchor.Bottom:
return oversize
def aspect_ratio_scale_and_crop(
art: Artifact,
target_width: int,
target_height: int,
scale_resample: int,
crop_horizontal_anchor: HorizontalAnchor,
crop_vertical_anchor: VerticalAnchor,
) -> Artifact:
if target_width <= 0:
raise ValueError("Target width must be a positive integer")
if target_height <= 0:
raise ValueError("Target height must be positive integer")
scale = max(target_width / art.width, target_height / art.height)
# use math.ceil to ensure that resized image at least has required size
# (floating point value error may cause this)
scaled_width = math.ceil(art.width * scale)
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_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)
)
@@ -1,211 +0,0 @@
import logging
import os
import enum
import subprocess
from typing import Iterable, Iterator
from pathlib import Path
from dataclasses import dataclass
import PIL.Image
from .utils import Artifact, check_vanilla_artifact
class SvgRenderKind(enum.IntEnum):
Inkscape = enum.auto()
ReSvg = enum.auto()
@dataclass
class SvgConfig:
width: int
height: int
def __post_init__(self) -> None:
if self.width <= 0:
raise ValueError(
f"The image width in Inkscape config must be a positive integer"
)
if self.height <= 0:
raise ValueError(
f"The image height in Inkscape config must be a positive integer"
)
def render_svg(src: Path, dst: Path, kind: SvgRenderKind, cfg: SvgConfig) -> None:
"""
Render given SVG file to PNG file.
:param src: The path to SVG file to be rendered
:param dst: The path to rendered PNG file.
:param kind: The selected SVG render kind.
:param cfg: The configuration when rendering.
"""
logging.info("Rendering SVG artifact with render {kind.name}: %s -> %s", src, dst)
match kind:
case SvgRenderKind.Inkscape:
inkscape_bin = os.getenv("ASSEMBLICON_INKSCAPE", "inkscape")
cmd = [
inkscape_bin,
"--export-area-page",
"--export-width",
str(cfg.width),
"--export-height",
str(cfg.height),
"--export-type",
"png",
"--export-png-color-mode",
"RGBA_8",
"--export-background-opacity",
"0.0",
"--export-png-use-dithering",
"false",
"--export-filename",
str(dst),
str(src),
]
case SvgRenderKind.ReSvg:
resvg_bin = os.getenv("ASSEMBLICON_RESVG", "resvg")
cmd = [
resvg_bin,
"--width",
str(cfg.width),
"--height",
str(cfg.height),
str(src),
str(dst),
]
proc = subprocess.run(cmd, capture_output=False)
if proc.returncode != 0:
raise RuntimeError(
f"Fail to execute SVG render {kind.name}. "
f"Return code is {proc.returncode}"
)
@dataclass
class BlenderConfig:
width: int
height: int
def __post_init__(self) -> None:
if self.width <= 0:
raise ValueError(
f"The image width in Blender config must be a positive integer"
)
if self.height <= 0:
raise ValueError(
f"The image height in Blender config must be a positive integer"
)
_BLENDER_RENDER_SCRIPT = """\
import sys
import traceback
try:
import bpy
render = bpy.context.scene.render
render.resolution_x = {width}
render.resolution_y = {height}
render.resolution_percentage = 100
render.image_settings.file_format = "PNG"
render.image_settings.color_mode = "RGBA"
render.image_settings.color_depth = "8"
render.film_transparent = True
render.use_file_extension = False
render.filepath = {dst}
bpy.ops.render.render(write_still=True)
except BaseException:
traceback.print_exc()
sys.exit(1)
"""
def render_blender(src: Path, dst: Path, temp_script: Path, cfg: BlenderConfig) -> None:
"""
Render given Blender composition into PNG file.
:param src: The path to Blender composition file to be rendered
:param dst: The path to rendered PNG file.
:param temp_script: The path to temporary Python script instructing Blender rendering.
The caller must make sure that this file is immutable during calling this function.
:param cfg: The configuration when rendering.
"""
logging.info("Rendering Blender artifact: %s -> %s", src, dst)
script = _BLENDER_RENDER_SCRIPT.format(
width=cfg.width, height=cfg.height, dst=repr(str(dst))
)
temp_script.write_text(script, encoding="utf-8")
blender_bin = os.getenv("ASSEMBLICON_BLENDER", "blender")
cmd = [
blender_bin,
"--background",
str(src),
"--python",
str(temp_script),
]
proc = subprocess.run(cmd, capture_output=False)
if proc.returncode != 0:
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"
)
def render_thumbnail(art: Artifact, hws: Iterable[int]) -> Iterator[Artifact]:
"""
Render given artifact as multiple resolution thumbnails.
:param art: The artifact to be rendered.
:param hws: The int iterable holding all resolution of thumbnails (square thumbnail only)
:return: A generator outputing thumbnail one by one with the order of given resolution.
"""
check_vanilla_artifact(art)
for hw in hws:
logging.info("Rendering thumbnail %dx%d.", hw, hw)
thumbnail = art.copy()
thumbnail.thumbnail((hw, hw), PIL.Image.Resampling.LANCZOS)
yield thumbnail
def render_ico(art: Artifact, dst: Path) -> None:
"""
Render given artifact as Windows ICO in given path.
:param art: The artifact to be rendered.
:param dst: The path to rendered result.
"""
logging.info("Rendering .ICO artifact: -> %s", dst)
check_vanilla_artifact(art)
sizes = [16, 32, 48, 64, 128, 256]
# provide every frame explicitly so that we never depend on
# undocumented plugin-side resizing behavior
frames = list(render_thumbnail(art, sizes))
art.save(
dst,
format="ICO",
sizes=[(size, size) for size in sizes],
append_images=frames,
)
def render_icns(art: Artifact, dst: Path) -> None:
"""
Render given artifact as macOS ICNS in given path.
:param art: The artifact to be rendered.
:param dst: The path to rendered result.
"""
logging.info("Rendering .ICNS artifact: -> %s", dst)
check_vanilla_artifact(art)
# provide every smaller frame explicitly; the 1024x1024 entry is
# covered by the art itself
frames = list(render_thumbnail(art, (512, 256, 128, 64, 32)))
art.save(dst, format="ICNS", append_images=frames)
@@ -1,368 +0,0 @@
import logging
import tempfile
import os
import enum
from dataclasses import dataclass
from pathlib import Path
from typing import Optional
from types import TracebackType
from .utils import Artifact, ArtifactFile, VANILLA_ARTIFACT_HW
from .artrender import SvgRenderKind, SvgConfig, BlenderConfig
from . import artio, artrender
@dataclass(frozen=True)
class Prerequisite:
input_directory: Path
output_directory: Path
svg_render: SvgRenderKind
def __post_init__(self) -> None:
if not self.input_directory.is_dir():
raise ValueError(
f"Given input path {self.input_directory} is not existing directory."
)
if not self.output_directory.is_dir():
raise ValueError(
f"Given output path {self.output_directory} is not existing directory."
)
class Context:
__prerequisite: Prerequisite
__temp_dir: tempfile.TemporaryDirectory[str]
def __init__(self, prerequisite: Prerequisite) -> None:
self.__prerequisite = prerequisite
self.__temp_dir = tempfile.TemporaryDirectory()
@property
def prerequisite(self) -> Prerequisite:
return self.__prerequisite
def input_artifact(self, *args: str) -> Path:
return self.__prerequisite.input_directory / Path(*args)
def output_artifact(self, *args: str) -> Path:
return self.__prerequisite.output_directory / Path(*args)
def temporary_artifact(self, *args: str) -> Path:
return Path(self.__temp_dir.name) / Path(*args)
def __enter__(self) -> "Context":
return self
def __exit__(
self,
exc_type: type[BaseException] | None,
exc_value: BaseException | None,
traceback: TracebackType | None,
) -> None:
self.__temp_dir.cleanup()
class FdContext(enum.StrEnum):
Actions = "actions"
Animations = "animations"
Applications = "apps"
Categories = "categories"
Devices = "devices"
Emblems = "emblems"
Emotes = "emotes"
International = "intl"
MimeTypes = "mimetypes"
Places = "places"
Status = "status"
class WinCategory(enum.StrEnum):
Application = "apps"
"""Icons for applications"""
Extension = "exts"
"""Icons for file extensions"""
class MacCategory(enum.StrEnum):
Application = "apps"
"""Icons for applications"""
Extension = "exts"
"""Icons for file extensions"""
_TEMP_USER_DIR: str = "user"
_TEMP_LIB_DIR: str = "assemblicon"
_TEMP_LIB_ALLOC_DIR: str = "alloc"
_TEMP_LIB_STABLE_DIR: str = "stable"
_ART_WIN: str = "windows"
_ART_MAC: str = "macos"
_DEFAULT_SVG_CONFIG = artrender.SvgConfig(VANILLA_ARTIFACT_HW, VANILLA_ARTIFACT_HW)
_DEFAULT_BLD_CONFIG = artrender.BlenderConfig(VANILLA_ARTIFACT_HW, VANILLA_ARTIFACT_HW)
# 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, ...] = (16, 32, 48, 64, 128, 256)
class AdvancedContext:
__context: Context
def __init__(self, ctx: Context) -> None:
self.__context = ctx
# construct basic layout of temporary directory
self.__context.temporary_artifact(_TEMP_USER_DIR).mkdir(
parents=True, exist_ok=True
)
self.__context.temporary_artifact(_TEMP_LIB_DIR, _TEMP_LIB_ALLOC_DIR).mkdir(
parents=True, exist_ok=True
)
self.__context.temporary_artifact(_TEMP_LIB_DIR, _TEMP_LIB_STABLE_DIR).mkdir(
parents=True, exist_ok=True
)
# and output directory
for hw in _FD_THUMBNAIL_HWS:
for kind in FdContext:
self.__context.output_artifact(f"{hw}x{hw}", str(kind)).mkdir(
parents=True, exist_ok=True
)
for kind in WinCategory:
self.__context.output_artifact(_ART_WIN, str(kind)).mkdir(
parents=True, exist_ok=True
)
for kind in MacCategory:
self.__context.output_artifact(_ART_MAC, str(kind)).mkdir(
parents=True, exist_ok=True
)
def __enter__(self) -> "AdvancedContext":
return self
def __exit__(
self,
exc_type: type[BaseException] | None,
exc_value: BaseException | None,
traceback: TracebackType | None,
) -> None:
self.__context.__exit__(exc_type, exc_value, traceback)
# region: Input Artifact
def input_artifact(self, *args: str) -> Path:
"""
Get the path to input artifact.
"""
return self.__context.input_artifact(*args)
def load_input_bitmap_artifact(self, *args: str) -> ArtifactFile:
"""
Load given input bitmap asset.
"""
return artio.load_input_artifact(self.input_artifact(*args))
# endregion
# region: Temporary Artifact
def __user_temporary_artifact(self, *args: str) -> Path:
return self.__context.temporary_artifact(_TEMP_USER_DIR, *args)
def __lib_temporary_artifact(self, *args: str) -> Path:
return self.__context.temporary_artifact(_TEMP_LIB_DIR, *args)
def temporary_artifact(self, *args: str) -> Path:
"""
Get the path to temporary artifact.
All existence of sub-directory should be ensured on your own.
"""
return self.__user_temporary_artifact(*args)
def load_temporary_artifact(self, *args: str) -> ArtifactFile:
return artio.load_artifact(self.temporary_artifact(*args))
def save_temporary_artifact(self, art: Artifact, *args: str) -> None:
"""
Save given artifact as temporary artifact.
All existence of sub-directory should be ensured on your own.
"""
artio.save_artifact(art, self.temporary_artifact(*args))
def __allocate_lib_temporary_artifact(self, suffix: Optional[str] = None) -> Path:
p = self.__lib_temporary_artifact(_TEMP_LIB_ALLOC_DIR)
(fd, pf) = tempfile.mkstemp(suffix=suffix, prefix=None, dir=str(p), text=False)
os.close(fd)
return Path(pf)
def __fetch_const_lib_temporary_artifact(self, name: str) -> Path:
return self.__lib_temporary_artifact(_TEMP_LIB_STABLE_DIR, name)
# endregion
# region: Render Wrapper
def __intern_render_svg(
self, src: Path, dst: Optional[Path], cfg: SvgConfig
) -> Path:
"""
Internal used function for rendering SVG.
:return: The path to destination storing render result.
"""
if dst is None:
dst = self.__allocate_lib_temporary_artifact(".png")
artrender.render_svg(src, dst, self.__context.prerequisite.svg_render, cfg)
return dst
def render_svg(
self, src: Path, dst: Path, cfg: SvgConfig = _DEFAULT_SVG_CONFIG
) -> None:
"""
Just render given SVG to PNG file.
:param src: The path to SVG file to be rendered.
:param dst: The path to rendered PNG file.
:param cfg: The configuration when rendering SVG file. Default for vanilla artifact.
"""
self.__intern_render_svg(src, dst, cfg)
def render_svg_then_load(
self,
src: Path,
dst: Optional[Path] = None,
cfg: SvgConfig = _DEFAULT_SVG_CONFIG,
) -> Artifact:
"""
Render given SVG to PNG file, then load it directly.
:param src: The path to SVG file to be rendered.
:param dst: The path to rendered PNG file.
``None`` if you don't need to store it in temporary or output directory.
:param cfg: The configuration when rendering SVG file. Default for vanilla artifact.
:return: The loaded built PNG file.
"""
dst = self.__intern_render_svg(src, dst, cfg)
return artio.load_artifact(dst)
def __intern_render_blender(
self, src: Path, dst: Optional[Path], cfg: BlenderConfig
) -> Path:
"""
Internal used function for rendering Blender composition.
:return: The path to destination storing render result.
"""
temp_script = self.__fetch_const_lib_temporary_artifact("blender.py")
if dst is None:
dst = self.__allocate_lib_temporary_artifact(".png")
artrender.render_blender(src, dst, temp_script, cfg)
return dst
def render_blender(
self, src: Path, dst: Path, cfg: BlenderConfig = _DEFAULT_BLD_CONFIG
) -> None:
"""
Just render given Blender composition to PNG file.
:param src: The path to Blender composition file to be rendered.
:param dst: The path to rendered PNG file.
:param cfg: The configuration when rendering Blender composition file. Default for vanilla artifact.
"""
self.__intern_render_blender(src, dst, cfg)
def render_blender_then_load(
self,
src: Path,
dst: Optional[Path] = None,
cfg: BlenderConfig = _DEFAULT_BLD_CONFIG,
) -> Artifact:
"""
Render given Blender composition to PNG file, then load it directly.
:param src: The path to Blender composition file to be rendered.
:param dst: The path to rendered PNG file.
``None`` if you don't need to store it in temporary or output directory.
:param cfg: The configuration when rendering Blender composition file. Default for vanilla artifact.
:return: The loaded built PNG file.
"""
dst = self.__intern_render_blender(src, dst, cfg)
return artio.load_artifact(dst)
# endregion
# region: Advanced Saver
def __fd_artifact(self, hw: int, kind: FdContext, name: str) -> Path:
return self.__context.output_artifact(f"{hw}x{hw}", str(kind), f"{name}.png")
def save_fd_artifact(self, art: Artifact, kind: FdContext, name: str) -> None:
"""
Save icon following FreeDesktop icon theme specification.
:param name: The name of saved artifact. The file extension ".png" is not required.
"""
logging.info(
"Saving FreeDesktop artifact with kind %s and name %s.", kind, name
)
for hw, thumbnail in zip(
_FD_THUMBNAIL_HWS, artrender.render_thumbnail(art, _FD_THUMBNAIL_HWS)
):
dst = self.__fd_artifact(hw, kind, name)
artio.save_artifact(thumbnail, dst)
def dup_fd_artifact(
self, src_kind: FdContext, src_name: str, dst_kind: FdContext, dst_name: str
) -> None:
for hw in _FD_THUMBNAIL_HWS:
artio.dup_artifact(
self.__fd_artifact(hw, src_kind, src_name),
self.__fd_artifact(hw, dst_kind, dst_name),
)
def __win_artifact(self, kind: WinCategory, name: str) -> Path:
return self.__context.output_artifact(_ART_WIN, str(kind), f"{name}.ico")
def save_win_artifact(self, art: Artifact, kind: WinCategory, name: str) -> None:
"""
Save icon for Windows-only
:param name: The name of saved artifact. The file extension ".ico" is not required.
"""
logging.info(
"Saving Windows-only artifact with kind %s and name %s.", kind, name
)
artrender.render_ico(art, self.__win_artifact(kind, name))
def dup_win_artifact(
self, src_kind: WinCategory, src_name: str, dst_kind: WinCategory, dst_name: str
) -> None:
artio.dup_artifact(
self.__win_artifact(src_kind, src_name),
self.__win_artifact(dst_kind, dst_name),
)
def __mac_artifact(self, kind: MacCategory, name: str) -> Path:
return self.__context.output_artifact(_ART_MAC, str(kind), f"{name}.icns")
def save_mac_artifact(self, art: Artifact, kind: MacCategory, name: str) -> None:
"""
Save icon for macOS-only
:param name: The name of saved artifact. The file extension ".icns" is not required.
"""
logging.info("Saving macOS-only artifact with kind %s and name %s.", kind, name)
artrender.render_icns(art, self.__mac_artifact(kind, name))
def dup_mac_artifact(
self, src_kind: MacCategory, src_name: str, dst_kind: MacCategory, dst_name: str
) -> None:
artio.dup_artifact(
self.__mac_artifact(src_kind, src_name),
self.__mac_artifact(dst_kind, dst_name),
)
# endregion
@@ -1,60 +0,0 @@
from dataclasses import dataclass
@dataclass
class MinMaxRect:
x1: int
y1: int
x2: int
y2: int
def to_pos_size_rect(self) -> "PosSizeRect":
return PosSizeRect(
x=self.x1, y=self.y1, width=self.x2 - self.x1, height=self.y2 - self.y1
)
def to_pillow_tuple(self) -> tuple[int, int, int, int]:
return (self.x1, self.y1, self.x2, self.y2)
@dataclass
class PosSizeRect:
x: int
y: int
width: int
height: int
def to_min_max_rect(self) -> MinMaxRect:
return MinMaxRect(
x1=self.x, y1=self.y, x2=self.x + self.width, y2=self.y + self.height
)
def to_pillow_tuple(self) -> tuple[int, int]:
return (self.x, self.y)
@dataclass
class MarginRect:
top: int
right: int
bottom: int
left: int
def to_min_max_rect(self, width: int, height: int) -> MinMaxRect:
return MinMaxRect(
x1=self.left,
y1=self.top,
x2=width - self.right,
y2=height - self.bottom,
)
@dataclass
class PaddingRect:
top: int
right: int
bottom: int
left: int
def to_min_max_rect(self, x: int, y: int) -> MinMaxRect:
return MinMaxRect(
x1=x - self.left, y1=y - self.top, x2=x + self.right, y2=y + self.bottom
)
@@ -1,20 +0,0 @@
import logging
from PIL.Image import Image
from PIL.ImageFile import ImageFile
type Artifact = Image
type ArtifactFile = ImageFile
def setup_logging() -> None:
logging.basicConfig(format="[%(levelname)s] %(message)s", level=logging.INFO)
VANILLA_ARTIFACT_HW: int = 1024
def check_vanilla_artifact(art: Artifact) -> None:
if art.size != (VANILLA_ARTIFACT_HW, VANILLA_ARTIFACT_HW):
raise ValueError(
f"Icon art must be {VANILLA_ARTIFACT_HW}x{VANILLA_ARTIFACT_HW}, got {art.width}x{art.height} instead"
)
+24 -35
View File
@@ -1,42 +1,31 @@
import argparse
import enum
from dataclasses import dataclass from dataclasses import dataclass
from .assemblicon import artrender from typing import Optional
from assemblicon.cli import (
SvgRenderKind,
class SvgRender(enum.StrEnum): StdOptsSetup,
Inkscape = "inkscape" create_argparser,
ReSvg = "resvg" parse_standard_opts,
)
def to_assemblicon_type(self) -> artrender.SvgRenderKind:
match self:
case SvgRender.Inkscape:
return artrender.SvgRenderKind.Inkscape
case SvgRender.ReSvg:
return artrender.SvgRenderKind.ReSvg
@dataclass(frozen=True) @dataclass(frozen=True)
class Cli: class AuroraOpts:
svg_render: SvgRender selection: Optional[list[str]]
"""The list holding all name of jobs for building."""
svg_render: SvgRenderKind
"""The render kind for SVG file."""
def parse() -> Cli: def parse() -> AuroraOpts:
parser = argparse.ArgumentParser( setup = StdOptsSetup(require_input=False, require_output=False)
prog="Aurora Icon Set Builder",
description="The Programmatic Icon Assembly Workflow for Aurora Icon Set.", parser = create_argparser(setup)
) parser.prog = "Aurora Icon Set Builder"
parser.add_argument( parser.description = "The Programmatic Icon Assembly Workflow for Aurora Icon Set."
"-s",
"--svg-render", ns = parser.parse_args()
dest="svg_render", opts = parse_standard_opts(setup, ns)
action="store",
type=SvgRender, return AuroraOpts(
default=SvgRender.Inkscape, selection=opts.selection, svg_render=opts.svg_render.to_assemblicon_type()
help="The render used for rendering SVG into PNG.",
metavar="RENDER",
)
args = parser.parse_args()
return Cli(
svg_render=args.svg_render,
) )
@@ -1,6 +1,8 @@
from ..assemblicon.context import AdvancedContext from ..types import AuroraContext
from .mimetype_icons import build_mimetype_icons from . import action_icons, application_icons, mimetype_icons
def build_non_standard_icons(ctx: AdvancedContext) -> None: def register(ctx: AuroraContext) -> None:
build_mimetype_icons(ctx) action_icons.register(ctx)
application_icons.register(ctx)
mimetype_icons.register(ctx)
@@ -0,0 +1,243 @@
from ..types import AuroraContext, AuroraEnvironment
from assemblicon.highlevel.utils import FdContext, VART_HW
from assemblicon.highlevel.source import TempArtSource, NewArtSource
from assemblicon.highlevel.sink import FdArtSink
from assemblicon.geometry import Rectangle
from assemblicon.lowlevel.artproc import (
art_anchor_to_offset,
anchor_to_offset,
default_art_resize,
align_paste,
align_alpha_composite,
VerticalAnchor,
HorizontalAnchor,
)
def register(ctx: AuroraContext) -> None:
ctx.register(
"nostd/action/image-properties",
(
TempArtSource("image-jpeg.png"),
TempArtSource("dialog-infomation.png"),
FdArtSink(FdContext.Actions, "image-properties"),
),
build_image_properties_icon,
)
ctx.register(
"nostd/action/image-zoom-original",
(
TempArtSource("component", "triangle-left.png"),
TempArtSource("component", "triangle-down.png"),
TempArtSource("component", "triangle-right.png"),
TempArtSource("component", "triangle-up.png"),
TempArtSource("image-jpeg.png"),
NewArtSource(),
FdArtSink(FdContext.Actions, "image-zoom-original"),
),
build_image_zoom_original_icon,
)
ctx.register(
"nostd/action/monitor-zoom-fit-best",
(
TempArtSource("component", "triangle-left.png"),
TempArtSource("component", "triangle-down.png"),
TempArtSource("component", "triangle-right.png"),
TempArtSource("component", "triangle-up.png"),
TempArtSource("component", "monitor.png"),
NewArtSource(),
FdArtSink(FdContext.Actions, "monitor-zoom-fit-best"),
),
build_monitor_zoom_fit_best_icon,
)
def build_image_properties_icon(
env: AuroraEnvironment,
jpeg_src: TempArtSource,
infomation_src: TempArtSource,
fd_sink: FdArtSink,
) -> None:
art = jpeg_src.pull(env)
info_mark = infomation_src.pull(env)
resized_info_mark = default_art_resize(info_mark, 500, 500)
align_alpha_composite(
art,
resized_info_mark,
art_anchor_to_offset(art, HorizontalAnchor.Right, VerticalAnchor.Bottom),
art_anchor_to_offset(
resized_info_mark, HorizontalAnchor.Right, VerticalAnchor.Bottom
),
)
# save artifact
fd_sink.push(env, art)
def build_image_zoom_original_icon(
env: AuroraEnvironment,
tri_left_src: TempArtSource,
tri_down_src: TempArtSource,
tri_right_src: TempArtSource,
tri_up_src: TempArtSource,
inner_src: TempArtSource,
new_art: NewArtSource,
fd_sink: FdArtSink,
) -> None:
# load triangle assets
TRI_SIZE = (200, 200)
tri_left = default_art_resize(tri_left_src.pull(env), *TRI_SIZE)
tri_down = default_art_resize(tri_down_src.pull(env), *TRI_SIZE)
tri_right = default_art_resize(tri_right_src.pull(env), *TRI_SIZE)
tri_up = default_art_resize(tri_up_src.pull(env), *TRI_SIZE)
# load inner image
inner = inner_src.pull(env)
resized_inner = default_art_resize(inner, 680, 680)
# composite this icon
icon = new_art.pull()
align_paste(
icon,
resized_inner,
art_anchor_to_offset(icon, HorizontalAnchor.Center, VerticalAnchor.Center),
art_anchor_to_offset(
resized_inner, HorizontalAnchor.Center, VerticalAnchor.Center
),
)
tri_rect = Rectangle.from_margin_rect(VART_HW, VART_HW, 100, 0, 100, 0)
align_alpha_composite(
icon,
tri_left,
anchor_to_offset(
tri_rect.width,
tri_rect.height,
HorizontalAnchor.Left,
VerticalAnchor.Center,
)
+ tri_rect.pos1,
art_anchor_to_offset(tri_left, HorizontalAnchor.Left, VerticalAnchor.Center),
)
align_alpha_composite(
icon,
tri_right,
anchor_to_offset(
tri_rect.width,
tri_rect.height,
HorizontalAnchor.Right,
VerticalAnchor.Center,
)
+ tri_rect.pos1,
art_anchor_to_offset(tri_right, HorizontalAnchor.Right, VerticalAnchor.Center),
)
align_alpha_composite(
icon,
tri_up,
anchor_to_offset(
tri_rect.width, tri_rect.height, HorizontalAnchor.Center, VerticalAnchor.Top
)
+ tri_rect.pos1,
art_anchor_to_offset(tri_up, HorizontalAnchor.Center, VerticalAnchor.Top),
)
align_alpha_composite(
icon,
tri_down,
anchor_to_offset(
tri_rect.width,
tri_rect.height,
HorizontalAnchor.Center,
VerticalAnchor.Bottom,
)
+ tri_rect.pos1,
art_anchor_to_offset(tri_down, HorizontalAnchor.Center, VerticalAnchor.Bottom),
)
# save it
fd_sink.push(env, icon)
def build_monitor_zoom_fit_best_icon(
env: AuroraEnvironment,
tri_left_src: TempArtSource,
tri_down_src: TempArtSource,
tri_right_src: TempArtSource,
tri_up_src: TempArtSource,
inner_src: TempArtSource,
new_art: NewArtSource,
fd_sink: FdArtSink,
) -> None:
# load triangle assets
TRI_SIZE = (200, 200)
tri_left = default_art_resize(tri_left_src.pull(env), *TRI_SIZE)
tri_down = default_art_resize(tri_down_src.pull(env), *TRI_SIZE)
tri_right = default_art_resize(tri_right_src.pull(env), *TRI_SIZE)
tri_up = default_art_resize(tri_up_src.pull(env), *TRI_SIZE)
# load inner image
inner = inner_src.pull(env)
resized_inner = default_art_resize(inner, 620, 620)
# composite this icon
icon = new_art.pull()
align_paste(
icon,
resized_inner,
art_anchor_to_offset(icon, HorizontalAnchor.Center, VerticalAnchor.Center),
art_anchor_to_offset(
resized_inner, HorizontalAnchor.Center, VerticalAnchor.Center
),
)
tri_rect = Rectangle.from_margin_rect(VART_HW, VART_HW, 100, 0, 100, 0)
align_alpha_composite(
icon,
tri_right,
anchor_to_offset(
tri_rect.width,
tri_rect.height,
HorizontalAnchor.Left,
VerticalAnchor.Center,
)
+ tri_rect.pos1,
art_anchor_to_offset(tri_right, HorizontalAnchor.Left, VerticalAnchor.Center),
)
align_alpha_composite(
icon,
tri_left,
anchor_to_offset(
tri_rect.width,
tri_rect.height,
HorizontalAnchor.Right,
VerticalAnchor.Center,
)
+ tri_rect.pos1,
art_anchor_to_offset(tri_left, HorizontalAnchor.Right, VerticalAnchor.Center),
)
align_alpha_composite(
icon,
tri_down,
anchor_to_offset(
tri_rect.width, tri_rect.height, HorizontalAnchor.Center, VerticalAnchor.Top
)
+ tri_rect.pos1,
art_anchor_to_offset(tri_down, HorizontalAnchor.Center, VerticalAnchor.Top),
)
align_alpha_composite(
icon,
tri_up,
anchor_to_offset(
tri_rect.width,
tri_rect.height,
HorizontalAnchor.Center,
VerticalAnchor.Bottom,
)
+ tri_rect.pos1,
art_anchor_to_offset(tri_up, HorizontalAnchor.Center, VerticalAnchor.Bottom),
)
# save it
fd_sink.push(env, icon)
@@ -0,0 +1,82 @@
from ..types import AuroraContext, AuroraEnvironment
from assemblicon.highlevel.utils import FdContext, WinCategory, MacCategory
from assemblicon.highlevel.source import (
SvgSource,
TempArtSource,
NewArtSource,
BlenderSource,
)
from assemblicon.highlevel.sink import FdArtSink, WinArtSink, MacArtSink, TempArtSink
from assemblicon.lowlevel.artproc import (
art_anchor_to_offset,
default_art_resize,
align_paste,
VerticalAnchor,
HorizontalAnchor,
)
def register(ctx: AuroraContext) -> None:
ctx.register(
"nostd/application/org.sarasaschipworkshop.picture",
(
SvgSource("apps", "sarasacw-picture-frame.svg"),
TempArtSource("image-jpeg.png"),
NewArtSource(),
FdArtSink(FdContext.Applications, "org.sarasaschipworkshop.picture"),
WinArtSink(WinCategory.Application, "sarasacw-picture"),
MacArtSink(MacCategory.Application, "sarasacw-picture"),
),
build_sarasacw_picture_icon,
)
ctx.register(
"nostd/application/notepad",
(
BlenderSource("apps", "notepad.blend"),
TempArtSink("notepad.png"),
WinArtSink(WinCategory.Application, "notepad"),
),
build_notepad_icon,
)
def build_sarasacw_picture_icon(
env: AuroraEnvironment,
frame_src: SvgSource,
inner_src: TempArtSource,
new_art: NewArtSource,
fd_sink: FdArtSink,
win_sink: WinArtSink,
mac_sink: MacArtSink,
) -> None:
app = new_art.pull()
frame = frame_src.pull(env)
inner = inner_src.pull(env)
resized_inner = default_art_resize(inner, 990, 990)
align_paste(
app,
resized_inner,
art_anchor_to_offset(app, HorizontalAnchor.Center, VerticalAnchor.Center),
art_anchor_to_offset(
resized_inner, HorizontalAnchor.Center, VerticalAnchor.Center
),
)
app.alpha_composite(frame, (0, 0))
# save artifact
fd_sink.push(env, app)
win_sink.push(env, app)
mac_sink.push(env, app)
def build_notepad_icon(
env: AuroraEnvironment,
src: BlenderSource,
temp_sink: TempArtSink,
win_sink: WinArtSink,
) -> None:
src.pull_to_temp(env, temp_sink)
with temp_sink.repull(env) as art:
win_sink.push(env, art)
@@ -1,71 +1,8 @@
import PIL.Image from ..types import AuroraContext, AuroraEnvironment
import PIL.ImageEnhance from assemblicon.highlevel.utils import FdContext, WinCategory, MacCategory
from ..assemblicon.context import AdvancedContext, FdContext, WinCategory, MacCategory from assemblicon.highlevel.source import SvgSource, TempArtSource
from ..assemblicon.utils import Artifact from assemblicon.highlevel.sink import TempArtSink, FdArtSink, WinArtSink, MacArtSink
from ..assemblicon.geometry import MinMaxRect, PosSizeRect
from ..assemblicon.artio import load_input_artifact, save_artifact
from ..assemblicon.artproc import (
aspect_ratio_scale_and_crop,
VerticalAnchor,
HorizontalAnchor,
)
def build_mimetype_icons(ctx: AdvancedContext) -> None: def register(ctx: AuroraContext) -> None:
_build_image_icons(ctx) pass
IMAGE_INNER_RECT_MINMAX = MinMaxRect(20 * 4, 52 * 4, 236 * 4, 204 * 4)
IMAGE_INNER_RECT_POSSIZE = IMAGE_INNER_RECT_MINMAX.to_pos_size_rect()
def _build_image_icons(ctx: AdvancedContext) -> None:
# build image base
image_base = ctx.render_svg_then_load(
ctx.input_artifact("component", "image-base.svg"),
)
# load image base
with image_base:
_build_jpg_icon(ctx, image_base)
def _build_jpg_icon(ctx: AdvancedContext, base: Artifact) -> None:
jpg_image = base.copy()
jpg_image_inner = load_input_artifact(
ctx.input_artifact("ext", "bitmap", "Niinsaare_järv.jpg")
)
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())
# do a flip
flip_jpg_image_inner = crop_jpg_image_inner.transpose(
PIL.Image.Transpose.FLIP_LEFT_RIGHT
)
# enhance its saturation
enhancer = PIL.ImageEnhance.Color(flip_jpg_image_inner)
saturated_jpg_image_inner = enhancer.enhance(1.4)
# fit to inner rect
bestfit_jpg_image_inner = aspect_ratio_scale_and_crop(
saturated_jpg_image_inner,
IMAGE_INNER_RECT_POSSIZE.width,
IMAGE_INNER_RECT_POSSIZE.height,
PIL.Image.Resampling.NEAREST,
HorizontalAnchor.Center,
VerticalAnchor.Center,
)
# paste into inner rect
jpg_image.paste(
bestfit_jpg_image_inner, IMAGE_INNER_RECT_POSSIZE.to_pillow_tuple()
)
# build artifacts
# save for FD
ctx.save_fd_artifact(jpg_image, FdContext.MimeTypes, "image-jpeg")
# use image/jpeg as the default image icon
ctx.dup_fd_artifact(FdContext.MimeTypes, "image-jpeg", FdContext.MimeTypes, "image-x-generic")
# save for windows and mac
ctx.save_win_artifact(jpg_image, WinCategory.Extension, "jpg")
ctx.save_mac_artifact(jpg_image, MacCategory.Extension, "jpg")
@@ -1,6 +1,9 @@
from ..assemblicon.context import AdvancedContext from ..types import AuroraContext
from .action_icons import build_action_icons from . import components, action_icons, status_icons, mimetype_icons
def build_standard_icons(ctx: AdvancedContext) -> None: def register(ctx: AuroraContext) -> None:
build_action_icons(ctx) components.register(ctx)
action_icons.register(ctx)
mimetype_icons.register(ctx)
status_icons.register(ctx)
@@ -1,34 +1,176 @@
import PIL.Image import PIL.Image
from ..assemblicon.context import AdvancedContext, FdContext from ..types import AuroraContext, AuroraEnvironment
from assemblicon.geometry import Rectangle, Point
from assemblicon.lowlevel.artproc import default_art_resize, align_alpha_composite
from assemblicon.highlevel.utils import FdContext
from assemblicon.highlevel.source import SvgSource, TempArtSource
from assemblicon.highlevel.sink import FdArtSink
def build_action_icons(ctx: AdvancedContext) -> None: def register(ctx: AuroraContext) -> None:
_build_go_icons(ctx) ctx.register(
"std/action/go-[previous|down|next|up]",
(
def _build_go_icons(ctx: AdvancedContext) -> None: SvgSource("go-base.svg"),
# build go-* base SvgSource("go-inner-shining.svg"),
go_base = ctx.render_svg_then_load( SvgSource("go-inner.svg"),
ctx.input_artifact("component", "go-base.svg") FdArtSink(FdContext.Actions, "go-previous"),
FdArtSink(FdContext.Actions, "go-down"),
FdArtSink(FdContext.Actions, "go-next"),
FdArtSink(FdContext.Actions, "go-up"),
),
build_go_icons,
) )
# build go-* shining
go_inner_shining = ctx.render_svg_then_load( ctx.register(
ctx.input_artifact("component", "go-inner-shining.svg"), "std/action/object-flip-horizontal",
(
SvgSource("object-flip-horizontal.svg"),
FdArtSink(FdContext.Actions, "object-flip-horizontal"),
),
build_object_flip_horizontal_icon,
) )
# build go-* inner ctx.register(
go_inner = ctx.render_svg_then_load( "std/action/object-flip-vertical",
ctx.input_artifact("component", "go-inner.svg"), (
SvgSource("object-flip-vertical.svg"),
FdArtSink(FdContext.Actions, "object-flip-vertical"),
),
build_object_flip_vertical_icon,
) )
ctx.register(
"std/action/object-rotate-left",
(
SvgSource("object-rotate-left.svg"),
FdArtSink(FdContext.Actions, "object-rotate-left"),
),
build_object_rotate_left_icon,
)
ctx.register(
"std/action/object-rotate-right",
(
SvgSource("object-rotate-right.svg"),
FdArtSink(FdContext.Actions, "object-rotate-right"),
),
build_object_rotate_right_icon,
)
ctx.register(
"std/action/zoom-[original|fit-best]",
(
TempArtSource("component", "triangle-left.png"),
TempArtSource("component", "triangle-down.png"),
TempArtSource("component", "triangle-right.png"),
TempArtSource("component", "triangle-up.png"),
FdArtSink(FdContext.Actions, "zoom-original"),
FdArtSink(FdContext.Actions, "zoom-fit-best"),
),
build_zoom_origin_and_fit_best_icons,
)
ctx.register(
"std/action/zoom-[in|out]",
(
TempArtSource("component", "magnifying-glass.png"),
TempArtSource("component", "plus.png"),
TempArtSource("component", "minus.png"),
FdArtSink(FdContext.Actions, "zoom-in"),
FdArtSink(FdContext.Actions, "zoom-out"),
),
build_zoom_in_out_icons,
)
def build_go_icons(
env: AuroraEnvironment,
base_src: SvgSource,
shining_src: SvgSource,
inner_src: SvgSource,
sink_previous: FdArtSink,
sink_down: FdArtSink,
sink_next: FdArtSink,
sink_up: FdArtSink,
) -> None:
# build go-* base, shining and inner
go_base = base_src.pull(env)
go_inner_shining = shining_src.pull(env)
go_inner = inner_src.pull(env)
with go_base, go_inner_shining, go_inner: with go_base, go_inner_shining, go_inner:
go_mask = go_inner go_mask = go_inner
for part in ("previous", "down", "next", "up"): for sink in (sink_previous, sink_down, sink_next, sink_up):
go = go_base.copy() go = go_base.copy()
# render shining with inner mask # render shining with inner mask
go.paste(go_inner_shining, (0, 0), go_mask) go.paste(go_inner_shining, (0, 0), go_mask)
# save as FD action # save as FD action
ctx.save_fd_artifact(go, FdContext.Actions, f'go-{part}') sink.push(env, go)
# rotate it for next calling # rotate it for next calling
go_mask = go_mask.transpose(PIL.Image.Transpose.ROTATE_90) go_mask = go_mask.transpose(PIL.Image.Transpose.ROTATE_90)
def build_object_flip_horizontal_icon(
env: AuroraEnvironment, src: SvgSource, sink: FdArtSink
) -> None:
with src.pull(env) as art:
sink.push(env, art)
def build_object_flip_vertical_icon(
env: AuroraEnvironment, src: SvgSource, sink: FdArtSink
) -> None:
with src.pull(env) as art:
sink.push(env, art)
def build_object_rotate_left_icon(
env: AuroraEnvironment, src: SvgSource, sink: FdArtSink
) -> None:
with src.pull(env) as art:
sink.push(env, art)
def build_object_rotate_right_icon(
env: AuroraEnvironment, src: SvgSource, sink: FdArtSink
) -> None:
with src.pull(env) as art:
sink.push(env, art)
def build_zoom_origin_and_fit_best_icons(
env: AuroraEnvironment,
tri_left_src: TempArtSource,
tri_down_src: TempArtSource,
tri_right_src: TempArtSource,
tri_up_src: TempArtSource,
zoom_original_sink: FdArtSink,
zoom_fit_best_sink: FdArtSink,
) -> None:
# TODO: finish this in future
pass
def build_zoom_in_out_icons(
env: AuroraEnvironment,
magnifying_glass_source: TempArtSource,
plus_source: TempArtSource,
minus_source: TempArtSource,
zoom_in_sink: FdArtSink,
zoom_out_sink: FdArtSink,
) -> None:
magnifying_glass = magnifying_glass_source.pull(env)
magnifying_glass_rect = Rectangle.from_padding_rect(
Point(665, 355), 200, 200, 200, 200
)
inner_size = (magnifying_glass_rect.width, magnifying_glass_rect.height)
plus = default_art_resize(plus_source.pull(env), *inner_size)
minus = default_art_resize(minus_source.pull(env), *inner_size)
zoom_in = magnifying_glass.copy()
align_alpha_composite(zoom_in, plus, magnifying_glass_rect.pos1, Point(0, 0))
zoom_in_sink.push(env, zoom_in)
zoom_out = magnifying_glass.copy()
align_alpha_composite(zoom_out, minus, magnifying_glass_rect.pos1, Point(0, 0))
zoom_out_sink.push(env, zoom_out)
@@ -0,0 +1,138 @@
import PIL.Image
from ..types import AuroraContext, AuroraEnvironment
from assemblicon.highlevel.source import (
SvgSource,
TempArtSource,
NewArtSource,
BlenderSource,
)
from assemblicon.highlevel.sink import TempArtSink
def register(ctx: AuroraContext) -> None:
ctx.register(
"component/generic-background",
(
SvgSource("component", "generic-background.svg"),
TempArtSink("component", "generic-background.png"),
),
build_generic_background,
)
ctx.register(
"component/triangles",
(
SvgSource("component", "triangle.svg"),
TempArtSource("component", "generic-background.png"),
NewArtSource(),
TempArtSink("component", "triangle-left.png"),
TempArtSink("component", "triangle-down.png"),
TempArtSink("component", "triangle-right.png"),
TempArtSink("component", "triangle-up.png"),
),
build_triangle_icons,
)
ctx.register(
"component/plus_minus",
(
TempArtSource("component", "generic-background.png"),
SvgSource("component", "plus.svg"),
SvgSource("component", "minus.svg"),
NewArtSource(),
TempArtSink("component", "plus.png"),
TempArtSink("component", "minus.png"),
),
build_plus_minus_icons,
)
ctx.register(
"component/magnifying-glass",
(
BlenderSource("component", "magnifying-glass.blend"),
TempArtSink("component", "magnifying-glass.png"),
),
build_magnifying_glass_icon,
)
ctx.register(
"component/monitor",
(
SvgSource("component", "monitor.svg"),
TempArtSink("component", "monitor.png"),
),
build_generic_background,
)
def build_generic_background(
env: AuroraEnvironment, src: SvgSource, sink: TempArtSink
) -> None:
src.pull_to_temp(env, sink)
def build_triangle_icons(
env: AuroraEnvironment,
tri_src: SvgSource,
bg_src: TempArtSource,
new_art: NewArtSource,
sink_left: TempArtSink,
sink_down: TempArtSink,
sink_right: TempArtSink,
sink_up: TempArtSink,
) -> None:
# load triangle and background assets
tri = tri_src.pull(env)
bg = bg_src.pull(env)
# create 4 triangle icons for following building
tri_left = new_art.pull()
tri_left.paste(bg, (0, 0), tri)
tri = tri.transpose(PIL.Image.Transpose.ROTATE_90)
tri_down = new_art.pull()
tri_down.paste(bg, (0, 0), tri)
tri = tri.transpose(PIL.Image.Transpose.ROTATE_90)
tri_right = new_art.pull()
tri_right.paste(bg, (0, 0), tri)
tri = tri.transpose(PIL.Image.Transpose.ROTATE_90)
tri_up = new_art.pull()
tri_up.paste(bg, (0, 0), tri)
# save them for reuse
sink_left.push(env, tri_left)
sink_down.push(env, tri_down)
sink_right.push(env, tri_right)
sink_up.push(env, tri_up)
def build_plus_minus_icons(
env: AuroraEnvironment,
bg_src: TempArtSource,
plus_src: SvgSource,
minus_src: SvgSource,
new_art: NewArtSource,
plus_sink: TempArtSink,
minus_sink: TempArtSink,
) -> None:
bg = bg_src.pull(env)
plus = plus_src.pull(env)
art = new_art.pull()
art.paste(bg, (0, 0), plus)
plus_sink.push(env, art)
minus = minus_src.pull(env)
art = new_art.pull()
art.paste(bg, (0, 0), minus)
minus_sink.push(env, art)
def build_magnifying_glass_icon(
env: AuroraEnvironment, src: BlenderSource, sink: TempArtSink
) -> None:
src.pull_to_temp(env, sink)
def build_monitor_icon(
env: AuroraEnvironment, src: SvgSource, sink: TempArtSink
) -> None:
src.pull_to_temp(env, sink)
@@ -0,0 +1,270 @@
import PIL.Image
import PIL.ImageEnhance
from ..types import AuroraContext, AuroraEnvironment
from assemblicon.highlevel.utils import FdContext, WinCategory, MacCategory
from assemblicon.highlevel.source import (
SvgSource,
BitmapSource,
TempArtSource,
FdArtSource,
)
from assemblicon.highlevel.sink import TempArtSink, FdArtSink, WinArtSink, MacArtSink
from assemblicon.geometry import Rectangle, Point
from assemblicon.lowlevel.artio import new_artifact
from assemblicon.lowlevel.artproc import (
aspect_ratio_scale_and_crop,
VerticalAnchor,
HorizontalAnchor,
)
def register(ctx: AuroraContext) -> None:
ctx.register(
"component/image-base",
(
SvgSource("component", "image-base.svg"),
TempArtSink("component", "image-base.png"),
),
build_image_base_icon,
)
ctx.register(
"std/mimetype/image-x-generic",
(
FdArtSource(FdContext.MimeTypes, "image-jpeg"),
FdArtSink(FdContext.MimeTypes, "image-x-generic"),
),
build_image_x_generic,
)
ctx.register(
"std/mimetype/image-jpeg",
(
TempArtSource("component", "image-base.png"),
BitmapSource("bitmap", "Niinsaare_järv.jpg"),
TempArtSink("image-jpeg.png"),
FdArtSink(FdContext.MimeTypes, "image-jpeg"),
WinArtSink(WinCategory.Extension, "jpg"),
MacArtSink(MacCategory.Extension, "jpg"),
),
build_image_jpeg_icon,
)
ctx.register(
"std/mimetype/image-png",
(
TempArtSource("component", "image-base.png"),
BitmapSource("bitmap", "Tulipa_suaveolens_floriade_to_Canberra.jpg"),
BitmapSource("bitmap", "Tulipa_suaveolens_floriade_to_Canberra-mask.png"),
SvgSource("image-png-checkerboard.svg"),
FdArtSink(FdContext.MimeTypes, "image-png"),
WinArtSink(WinCategory.Extension, "png"),
MacArtSink(MacCategory.Extension, "png"),
),
build_image_png_icon,
)
ctx.register(
"std/mimetype/image-bmp",
(
TempArtSource("component", "image-base.png"),
BitmapSource("bitmap", "Van_Gogh_-_Starry_Night_-_Google_Art_Project.jpg"),
FdArtSink(FdContext.MimeTypes, "image-bmp"),
WinArtSink(WinCategory.Extension, "bmp"),
MacArtSink(MacCategory.Extension, "bmp"),
),
build_image_bmp_icon,
)
ctx.register(
"std/mimetype/image-gif",
(
TempArtSource("component", "image-base.png"),
BitmapSource("bitmap", "Hot-air_balloon_in_Tambov_-_02.jpg"),
FdArtSink(FdContext.MimeTypes, "image-gif"),
WinArtSink(WinCategory.Extension, "gif"),
MacArtSink(MacCategory.Extension, "gif"),
),
build_image_gif_icon,
)
def build_image_base_icon(
env: AuroraEnvironment, src: SvgSource, sink: TempArtSink
) -> None:
src.pull_to_temp(env, sink)
def build_image_x_generic(
env: AuroraEnvironment, src: FdArtSource, sink: FdArtSink
) -> None:
# use image/jpeg as the default image icon
src.link_to(env, sink)
IMAGE_INNER_RECT = Rectangle(Point(20 * 4, 52 * 4), Point(236 * 4, 204 * 4))
"""The valid region of image-base for putting inner image."""
def build_image_jpeg_icon(
env: AuroraEnvironment,
base_src: TempArtSource,
bitmap_src: BitmapSource,
temp_sink: TempArtSink,
fd_sink: FdArtSink,
win_sink: WinArtSink,
mac_sink: MacArtSink,
) -> None:
jpg_image = base_src.pull(env)
jpg_image_inner = bitmap_src.pull(env)
with jpg_image_inner:
# crop the area which is interested by ourselves
interest_area = Rectangle.from_pos_size_rect(Point(2700, 0), 5016, 3888)
crop_jpg_image_inner = jpg_image_inner.crop(interest_area.to_tuple())
# do a flip
flip_jpg_image_inner = crop_jpg_image_inner.transpose(
PIL.Image.Transpose.FLIP_LEFT_RIGHT
)
# enhance its saturation
enhancer = PIL.ImageEnhance.Color(flip_jpg_image_inner)
saturated_jpg_image_inner = enhancer.enhance(1.4)
# fit to inner rect
bestfit_jpg_image_inner = aspect_ratio_scale_and_crop(
saturated_jpg_image_inner,
IMAGE_INNER_RECT.width,
IMAGE_INNER_RECT.height,
PIL.Image.Resampling.NEAREST,
HorizontalAnchor.Center,
VerticalAnchor.Center,
)
# paste into inner rect
jpg_image.paste(bestfit_jpg_image_inner, IMAGE_INNER_RECT.to_tuple())
# build artifacts
# save as temporary for future use
temp_sink.push(env, jpg_image)
# save for FD
fd_sink.push(env, jpg_image)
# save for windows and mac
win_sink.push(env, jpg_image)
mac_sink.push(env, jpg_image)
def build_image_png_icon(
env: AuroraEnvironment,
base_src: TempArtSource,
bitmap_src: BitmapSource,
bitmap_mask_src: BitmapSource,
checkerboard_src: SvgSource,
fd_sink: FdArtSink,
win_sink: WinArtSink,
mac_sink: MacArtSink,
) -> None:
png_image = base_src.pull(env)
bitmap = bitmap_src.pull(env)
bitmap_mask = bitmap_mask_src.pull(env)
checkerboard = checkerboard_src.pull(env)
with bitmap, bitmap_mask, checkerboard:
# mask bitmap first
# force mask as grayscale format to apply black-white mask instead of alpha channel mask
bitmap_masked = new_artifact(bitmap.size)
bitmap_masked.paste(bitmap, (0, 0), bitmap_mask.convert("L"))
# we use the whole image as the inner,
# so we directly fit it to inner.
bestfit_bitmap = aspect_ratio_scale_and_crop(
bitmap_masked,
IMAGE_INNER_RECT.width,
IMAGE_INNER_RECT.height,
PIL.Image.Resampling.NEAREST,
HorizontalAnchor.Center,
VerticalAnchor.Top,
)
# TODO: When bestfit this checkerboard, the border of this checkerboard aer lost.
# We may need resolve this in future but currently it is so subtle to notice.
bestfit_checkerboard = aspect_ratio_scale_and_crop(
checkerboard,
IMAGE_INNER_RECT.width,
IMAGE_INNER_RECT.height,
PIL.Image.Resampling.NEAREST,
HorizontalAnchor.Center,
VerticalAnchor.Center,
)
# composite them together
png_image.alpha_composite(
bestfit_checkerboard, IMAGE_INNER_RECT.pos1.to_tuple()
)
png_image.alpha_composite(bestfit_bitmap, IMAGE_INNER_RECT.pos1.to_tuple())
# build artifacts
# save for FD
fd_sink.push(env, png_image)
# save for windows and mac
win_sink.push(env, png_image)
mac_sink.push(env, png_image)
def build_image_bmp_icon(
env: AuroraEnvironment,
base_src: TempArtSource,
bitmap_src: BitmapSource,
fd_sink: FdArtSink,
win_sink: WinArtSink,
mac_sink: MacArtSink,
) -> None:
bmp_image = base_src.pull(env)
bmp_image_inner = bitmap_src.pull(env)
with bmp_image_inner:
# we use the whole image as the inner,
# so we directly fit it to inner.
bestfit_inner = aspect_ratio_scale_and_crop(
bmp_image_inner,
IMAGE_INNER_RECT.width,
IMAGE_INNER_RECT.height,
PIL.Image.Resampling.NEAREST,
HorizontalAnchor.Center,
VerticalAnchor.Center,
)
# paste into inner rect
bmp_image.paste(bestfit_inner, IMAGE_INNER_RECT.to_tuple())
# build artifacts
# save for FD
fd_sink.push(env, bmp_image)
# save for windows and mac
win_sink.push(env, bmp_image)
mac_sink.push(env, bmp_image)
def build_image_gif_icon(
env: AuroraEnvironment,
base_src: TempArtSource,
bitmap_src: BitmapSource,
fd_sink: FdArtSink,
win_sink: WinArtSink,
mac_sink: MacArtSink,
) -> None:
gif_image = base_src.pull(env)
gif_image_inner = bitmap_src.pull(env)
with gif_image_inner:
# we use the whole image as the inner,
# so we directly fit it to inner.
bestfit_inner = aspect_ratio_scale_and_crop(
gif_image_inner,
IMAGE_INNER_RECT.width,
IMAGE_INNER_RECT.height,
PIL.Image.Resampling.NEAREST,
HorizontalAnchor.Center,
VerticalAnchor.Center,
)
# paste into inner rect
gif_image.paste(bestfit_inner, IMAGE_INNER_RECT.to_tuple())
# build artifacts
# save for FD
fd_sink.push(env, gif_image)
# save for windows and mac
win_sink.push(env, gif_image)
mac_sink.push(env, gif_image)
@@ -0,0 +1,79 @@
from ..types import AuroraContext, AuroraEnvironment
from assemblicon.highlevel.utils import FdContext
from assemblicon.highlevel.source import SvgSource
from assemblicon.highlevel.sink import TempArtSink, FdArtSink
def register(ctx: AuroraContext) -> None:
ctx.register(
"std/status/dialog-infomation",
(
SvgSource("dialog-infomation.svg"),
TempArtSink("dialog-infomation.png"),
FdArtSink(FdContext.Status, "dialog-infomation"),
),
build_dialog_infomation_icon,
)
ctx.register(
"std/status/dialog-warning",
(
SvgSource("dialog-warning.svg"),
TempArtSink("dialog-warning.png"),
FdArtSink(FdContext.Status, "dialog-warning"),
),
build_dialog_warning_icon,
)
ctx.register(
"std/status/dialog-error",
(
SvgSource("dialog-error.svg"),
TempArtSink("dialog-error.png"),
FdArtSink(FdContext.Status, "dialog-error"),
),
build_dialog_error_icon,
)
ctx.register(
"std/status/dialog-question",
(
SvgSource("dialog-question.svg"),
TempArtSink("dialog-question.png"),
FdArtSink(FdContext.Status, "dialog-question"),
),
build_dialog_question_icon,
)
def build_dialog_infomation_icon(
env: AuroraEnvironment, src: SvgSource, temp_sink: TempArtSink, fd_sink: FdArtSink
) -> None:
# save dialog icon with FreeDesktop icon and temp icon for reuse.
src.pull_to_temp(env, temp_sink)
with temp_sink.repull(env) as art:
fd_sink.push(env, art)
def build_dialog_warning_icon(
env: AuroraEnvironment, src: SvgSource, temp_sink: TempArtSink, fd_sink: FdArtSink
) -> None:
# save dialog icon with FreeDesktop icon and temp icon for reuse.
src.pull_to_temp(env, temp_sink)
with temp_sink.repull(env) as art:
fd_sink.push(env, art)
def build_dialog_error_icon(
env: AuroraEnvironment, src: SvgSource, temp_sink: TempArtSink, fd_sink: FdArtSink
) -> None:
# TODO: update this icon from rectangle to circle
# save dialog icon with FreeDesktop icon and temp icon for reuse.
src.pull_to_temp(env, temp_sink)
with temp_sink.repull(env) as art:
fd_sink.push(env, art)
def build_dialog_question_icon(
env: AuroraEnvironment, src: SvgSource, temp_sink: TempArtSink, fd_sink: FdArtSink
) -> None:
# TODO: add dialog-question generation
pass
+14
View File
@@ -0,0 +1,14 @@
from dataclasses import dataclass
from assemblicon.highlevel.environment import Environment, Prologue
from assemblicon.highlevel.context import Context
@dataclass(frozen=True)
class AuroraPrologue(Prologue):
"""Aurora icon set builder specific prologue"""
pass
type AuroraEnvironment = Environment[AuroraPrologue]
type AuroraContext = Context[AuroraPrologue]
Generated
+10 -2
View File
@@ -2,16 +2,24 @@ version = 1
revision = 2 revision = 2
requires-python = ">=3.13" requires-python = ">=3.13"
[[package]]
name = "assemblicon"
version = "0.1.0"
source = { git = "https://github.com/SarasasChipWorkshop/assemblicon#0073298d45d6c44fc164456ebdec5f0d3ada5dff" }
dependencies = [
{ name = "pillow" },
]
[[package]] [[package]]
name = "aurora-iconset-builder" name = "aurora-iconset-builder"
version = "0.1.0" version = "0.1.0"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "pillow" }, { name = "assemblicon" },
] ]
[package.metadata] [package.metadata]
requires-dist = [{ name = "pillow", specifier = ">=12.3.0" }] requires-dist = [{ name = "assemblicon", git = "https://github.com/SarasasChipWorkshop/assemblicon" }]
[[package]] [[package]]
name = "pillow" name = "pillow"