Compare commits
35
Commits
1d48f54c40
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a951c6d11 | ||
|
|
890019bc54 | ||
|
|
0d43fe3962 | ||
|
|
a33c3538f5 | ||
|
|
72edad6edc | ||
|
|
2015ebf925 | ||
|
|
1e15fb4783 | ||
|
|
42a2b08abf | ||
|
|
61e003c741 | ||
|
|
7b216b8865 | ||
|
|
36adf8029a | ||
|
|
e0d6aa63e6 | ||
|
|
765375f04c | ||
|
|
a563917aa8 | ||
|
|
bb5032d502 | ||
|
|
db04b9c809 | ||
|
|
44ba5fbbee | ||
|
|
f650d3f98a | ||
|
|
43e3bd39ef | ||
|
|
49edad7d33 | ||
|
|
28fa01992e | ||
|
|
d13f18cc90 | ||
|
|
da52ab5028 | ||
|
|
988a9fc04f | ||
|
|
63ba6d44f7 | ||
|
|
bcac59ceb1 | ||
|
|
54af04a48f | ||
|
|
e43029ea47 | ||
|
|
3a9a6d99cb | ||
|
|
892a946998 | ||
|
|
9d20e60adb | ||
|
|
80738d1254 | ||
|
|
c54ae438c1 | ||
|
|
f33523259a | ||
|
|
96e0eab9d2 |
+14
@@ -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.
|
||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -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"
|
||||||
@@ -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"
|
||||||
@@ -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"
|
||||||
@@ -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 |
@@ -0,0 +1,5 @@
|
|||||||
|
Blue Background Node 1,#001fbe
|
||||||
|
Blue Background Node 2,#1439ff
|
||||||
|
|
||||||
|
Paper Background Node 1,#ffffff
|
||||||
|
Paper Backgroundr Node 2,#e3e3e3
|
||||||
|
@@ -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"
|
||||||
@@ -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'
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
version = 1
|
||||||
|
|
||||||
|
[[annotations]]
|
||||||
|
path = "**"
|
||||||
|
precedence = "override"
|
||||||
|
SPDX-FileCopyrightText = "2026 Sarasa's Chip Workshop"
|
||||||
|
SPDX-License-Identifier = "MIT"
|
||||||
+4
-1
@@ -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" }
|
||||||
|
|||||||
@@ -1,25 +1,44 @@
|
|||||||
import logging
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from .assemblicon.context import Context
|
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 .ext_icons import build_ext_icons
|
from assemblicon.logger import APP_LOGGER as LOGGER
|
||||||
|
from .cli import parse
|
||||||
|
from .types import AuroraPrologue, AuroraContext
|
||||||
|
from . import std_icons, nostd_icons
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
setup_logging()
|
opts = parse()
|
||||||
|
|
||||||
|
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"
|
||||||
ctx = Context(iconset_input, iconset_output)
|
prologue = AuroraPrologue(
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
env = Environment(prologue)
|
||||||
|
ctx = Context(env)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_build_icons(ctx)
|
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: Context) -> None:
|
def register(ctx: AuroraContext) -> None:
|
||||||
build_standard_icons(ctx)
|
std_icons.register(ctx)
|
||||||
#build_ext_icons(ctx)
|
nostd_icons.register(ctx)
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
import logging
|
|
||||||
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")
|
|
||||||
@@ -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,314 +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 48x48.
|
|
||||||
# Enable full generation after testing.
|
|
||||||
_FD_THUMBNAIL_HWS: tuple[int, ...] = (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 and output directory.
|
|
||||||
|
|
||||||
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], 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], 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 __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 __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))
|
|
||||||
|
|
||||||
# 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"
|
|
||||||
)
|
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Optional
|
||||||
|
from assemblicon.cli import (
|
||||||
|
SvgRenderKind,
|
||||||
|
StdOptsSetup,
|
||||||
|
create_argparser,
|
||||||
|
parse_standard_opts,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class AuroraOpts:
|
||||||
|
selection: Optional[list[str]]
|
||||||
|
"""The list holding all name of jobs for building."""
|
||||||
|
svg_render: SvgRenderKind
|
||||||
|
"""The render kind for SVG file."""
|
||||||
|
|
||||||
|
|
||||||
|
def parse() -> AuroraOpts:
|
||||||
|
setup = StdOptsSetup(require_input=False, require_output=False)
|
||||||
|
|
||||||
|
parser = create_argparser(setup)
|
||||||
|
parser.prog = "Aurora Icon Set Builder"
|
||||||
|
parser.description = "The Programmatic Icon Assembly Workflow for Aurora Icon Set."
|
||||||
|
|
||||||
|
ns = parser.parse_args()
|
||||||
|
opts = parse_standard_opts(setup, ns)
|
||||||
|
|
||||||
|
return AuroraOpts(
|
||||||
|
selection=opts.selection, svg_render=opts.svg_render.to_assemblicon_type()
|
||||||
|
)
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
from ..assemblicon.context import Context
|
|
||||||
from .image_icons import build_image_icons
|
|
||||||
|
|
||||||
|
|
||||||
def build_ext_icons(ctx: Context) -> None:
|
|
||||||
build_image_icons(ctx)
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
import PIL.Image
|
|
||||||
import PIL.ImageEnhance
|
|
||||||
from ..assemblicon.artrender import render_svg, render_ico, SvgConfig
|
|
||||||
from ..assemblicon.context import Context
|
|
||||||
from ..assemblicon.utils import Artifact
|
|
||||||
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,
|
|
||||||
)
|
|
||||||
|
|
||||||
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: Context) -> None:
|
|
||||||
# build image base
|
|
||||||
image_base = render_svg(
|
|
||||||
ctx.input_artifact("component", "image-base.svg"),
|
|
||||||
ctx.temporary_artifact("component", "image-base.png"),
|
|
||||||
SvgConfig(1024, 1024),
|
|
||||||
)
|
|
||||||
# load image base
|
|
||||||
with image_base:
|
|
||||||
_build_jpg_icon(ctx, image_base)
|
|
||||||
|
|
||||||
|
|
||||||
def _build_jpg_icon(ctx: Context, 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_artifact(jpg_image, ctx.temporary_artifact("ext", "jpg-image.png"))
|
|
||||||
render_ico(
|
|
||||||
ctx,
|
|
||||||
jpg_image,
|
|
||||||
ctx.output_artifact("ext", "jpg-image.ico"),
|
|
||||||
)
|
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
from ..assemblicon.context import Context
|
from ..types import AuroraContext
|
||||||
|
from . import action_icons, application_icons, mimetype_icons
|
||||||
|
|
||||||
|
|
||||||
def build_non_standard_icons(ctx: Context) -> None:
|
def register(ctx: AuroraContext) -> None:
|
||||||
pass
|
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)
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
from ..types import AuroraContext, AuroraEnvironment
|
||||||
|
from assemblicon.highlevel.utils import FdContext, WinCategory, MacCategory
|
||||||
|
from assemblicon.highlevel.source import SvgSource, TempArtSource
|
||||||
|
from assemblicon.highlevel.sink import TempArtSink, FdArtSink, WinArtSink, MacArtSink
|
||||||
|
|
||||||
|
|
||||||
|
def register(ctx: AuroraContext) -> None:
|
||||||
|
pass
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
from ..assemblicon.context import Context
|
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: Context) -> 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,43 +1,176 @@
|
|||||||
from pathlib import Path
|
|
||||||
import PIL.Image
|
import PIL.Image
|
||||||
from ..assemblicon.artrender import render_svg, SvgConfig
|
from ..types import AuroraContext, AuroraEnvironment
|
||||||
from ..assemblicon.context import Context
|
from assemblicon.geometry import Rectangle, Point
|
||||||
from ..assemblicon.utils import Artifact
|
from assemblicon.lowlevel.artproc import default_art_resize, align_alpha_composite
|
||||||
from ..assemblicon.artio import save_artifact
|
from assemblicon.highlevel.utils import FdContext
|
||||||
|
from assemblicon.highlevel.source import SvgSource, TempArtSource
|
||||||
|
from assemblicon.highlevel.sink import FdArtSink
|
||||||
|
|
||||||
|
|
||||||
def build_action_icons(ctx: Context) -> None:
|
def register(ctx: AuroraContext) -> None:
|
||||||
_build_go_icons(ctx)
|
ctx.register(
|
||||||
|
"std/action/go-[previous|down|next|up]",
|
||||||
|
(
|
||||||
def _build_go_icons(ctx: Context) -> None:
|
SvgSource("go-base.svg"),
|
||||||
# build go-* base
|
SvgSource("go-inner-shining.svg"),
|
||||||
go_base = render_svg(
|
SvgSource("go-inner.svg"),
|
||||||
ctx.input_artifact("component", "go-base.svg"),
|
FdArtSink(FdContext.Actions, "go-previous"),
|
||||||
ctx.temporary_artifact("component", "go-base.png"),
|
FdArtSink(FdContext.Actions, "go-down"),
|
||||||
SvgConfig(1024, 1024),
|
FdArtSink(FdContext.Actions, "go-next"),
|
||||||
|
FdArtSink(FdContext.Actions, "go-up"),
|
||||||
|
),
|
||||||
|
build_go_icons,
|
||||||
)
|
)
|
||||||
# build go-* shining
|
|
||||||
go_inner_shining = render_svg(
|
ctx.register(
|
||||||
ctx.input_artifact("component", "go-inner-shining.svg"),
|
"std/action/object-flip-horizontal",
|
||||||
ctx.temporary_artifact("component", "go-inner-shining.png"),
|
(
|
||||||
SvgConfig(1024, 1024),
|
SvgSource("object-flip-horizontal.svg"),
|
||||||
|
FdArtSink(FdContext.Actions, "object-flip-horizontal"),
|
||||||
|
),
|
||||||
|
build_object_flip_horizontal_icon,
|
||||||
)
|
)
|
||||||
# build go-* inner
|
ctx.register(
|
||||||
go_inner = render_svg(
|
"std/action/object-flip-vertical",
|
||||||
ctx.input_artifact("component", "go-inner.svg"),
|
(
|
||||||
ctx.temporary_artifact("component", "go-inner.png"),
|
SvgSource("object-flip-vertical.svg"),
|
||||||
SvgConfig(1024, 1024),
|
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 image
|
|
||||||
save_artifact(go, ctx.output_artifact(f"go-{part}.png"))
|
# save as FD action
|
||||||
|
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
|
||||||
@@ -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
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user