Compare commits
20
Commits
28fa01992e
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a951c6d11 | ||
|
|
890019bc54 | ||
|
|
0d43fe3962 | ||
|
|
a33c3538f5 | ||
|
|
72edad6edc | ||
|
|
2015ebf925 | ||
|
|
1e15fb4783 | ||
|
|
42a2b08abf | ||
|
|
61e003c741 | ||
|
|
7b216b8865 | ||
|
|
36adf8029a | ||
|
|
e0d6aa63e6 | ||
|
|
765375f04c | ||
|
|
a563917aa8 | ||
|
|
bb5032d502 | ||
|
|
db04b9c809 | ||
|
|
44ba5fbbee | ||
|
|
f650d3f98a | ||
|
|
43e3bd39ef | ||
|
|
49edad7d33 |
+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
|
||||
|
||||
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"
|
||||
@@ -1,23 +0,0 @@
|
||||
[[licenses]]
|
||||
file = 'object-flip-horizontal.svg'
|
||||
license = "LGPL-2.1-only"
|
||||
authors = ["KDE Oxygen Icon Theme Team", "yyc12345"]
|
||||
source = 'https://invent.kde.org/frameworks/oxygen-icons'
|
||||
|
||||
[[licenses]]
|
||||
file = 'object-flip-vertical.svg'
|
||||
license = "LGPL-2.1-only"
|
||||
authors = ["KDE Oxygen Icon Theme Team", "yyc12345"]
|
||||
source = 'https://invent.kde.org/frameworks/oxygen-icons'
|
||||
|
||||
[[licenses]]
|
||||
file = 'object-rotate-left.svg'
|
||||
license = "LGPL-2.1-only"
|
||||
authors = ["KDE Oxygen Icon Theme Team", "yyc12345"]
|
||||
source = 'https://invent.kde.org/frameworks/oxygen-icons'
|
||||
|
||||
[[licenses]]
|
||||
file = 'object-rotate-right.svg'
|
||||
license = "LGPL-2.1-only"
|
||||
authors = ["KDE Oxygen Icon Theme Team", "yyc12345"]
|
||||
source = 'https://invent.kde.org/frameworks/oxygen-icons'
|
||||
@@ -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 |
@@ -1,20 +0,0 @@
|
||||
[[licenses]]
|
||||
file = 'go-base.svg'
|
||||
license = "CC-BY-SA-4.0"
|
||||
authors = ["yyc12345"]
|
||||
|
||||
[[licenses]]
|
||||
file = 'go-inner-shining.svg'
|
||||
license = "CC-BY-SA-4.0"
|
||||
authors = ["yyc12345"]
|
||||
|
||||
[[licenses]]
|
||||
file = 'go-inner.svg'
|
||||
license = "Apache-2.0"
|
||||
authors = ["Google", "yyc12345"]
|
||||
source = 'https://github.com/google/material-design-icon'
|
||||
|
||||
[[licenses]]
|
||||
file = 'notepad.blend'
|
||||
license = "CC-BY-SA-4.0"
|
||||
authors = ["yyc12345"]
|
||||
@@ -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]]
|
||||
file = 'Niinsaare_järv.jpg'
|
||||
license = "CC-BY-SA-3.0"
|
||||
authors = ["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"
|
||||
dependencies = [
|
||||
"pillow>=12.3.0",
|
||||
"assemblicon",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
@@ -17,3 +17,6 @@ aurora-iconset-builder = "aurora_iconset_builder:main"
|
||||
[build-system]
|
||||
requires = ["uv_build>=0.8.0,<0.9"]
|
||||
build-backend = "uv_build"
|
||||
|
||||
[tool.uv.sources]
|
||||
assemblicon = { git = "https://github.com/SarasasChipWorkshop/assemblicon" }
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from pathlib import Path
|
||||
from .assemblicon.highlevel.environment import Environment
|
||||
from .assemblicon.highlevel.context import Context
|
||||
from .assemblicon.logger import APP_LOGGER as LOGGER
|
||||
from assemblicon.highlevel.utils import DEFAULT_FD_THUMBNAIL_HWS
|
||||
from assemblicon.highlevel.environment import Environment
|
||||
from assemblicon.highlevel.context import Context
|
||||
from assemblicon.logger import APP_LOGGER as LOGGER
|
||||
from .cli import parse
|
||||
from .types import AuroraPrologue, AuroraContext
|
||||
from . import std_icons, nostd_icons
|
||||
@@ -10,18 +11,27 @@ from . import std_icons, nostd_icons
|
||||
def main() -> None:
|
||||
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
|
||||
iconset_input = repository_root / "src"
|
||||
iconset_output = repository_root / "artifact"
|
||||
prologue = AuroraPrologue(iconset_input, iconset_output, opts.svg_render)
|
||||
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)
|
||||
|
||||
# TODO: use explicit temporary directory initializer in prologue to do this.
|
||||
|
||||
# create our special temporary subdirectory
|
||||
env.temporary_artifact("component").mkdir(parents=True, exist_ok=True)
|
||||
|
||||
try:
|
||||
register(ctx)
|
||||
ctx.run(opts.selection)
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
import enum
|
||||
from typing import Optional
|
||||
from pathlib import Path
|
||||
from argparse import ArgumentParser, Namespace
|
||||
from dataclasses import dataclass
|
||||
from .lowlevel.artrdr import SvgRenderKind
|
||||
|
||||
|
||||
class CliSvgRenderKind(enum.StrEnum):
|
||||
Inkscape = "inkscape"
|
||||
ReSvg = "resvg"
|
||||
|
||||
def to_assemblicon_type(self) -> SvgRenderKind:
|
||||
match self:
|
||||
case CliSvgRenderKind.Inkscape:
|
||||
return SvgRenderKind.Inkscape
|
||||
case CliSvgRenderKind.ReSvg:
|
||||
return SvgRenderKind.ReSvg
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class StdOpts:
|
||||
"""Assemblicon standard command line options"""
|
||||
|
||||
input_directory: Optional[Path]
|
||||
"""The path to input directory. None if you doesn't request it."""
|
||||
output_directory: Optional[Path]
|
||||
"""The path to output directory. None if you doesn't request it."""
|
||||
selection: Optional[list[str]]
|
||||
"""The list holding all name of jobs for building."""
|
||||
svg_render: CliSvgRenderKind
|
||||
"""The render kind for SVG file."""
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class StdOptsSetup:
|
||||
require_input: bool
|
||||
"""Whether add input path option in argument parser."""
|
||||
require_output: bool
|
||||
"""Whether add outputput path option in argument parser."""
|
||||
|
||||
|
||||
def create_argparser(setup: StdOptsSetup) -> ArgumentParser:
|
||||
"""
|
||||
Create Assemblicon standard argument parser.
|
||||
|
||||
This argument parser are filled properly with the requirement of Assemblicon itself.
|
||||
It can be directly used or modified with extra options.
|
||||
|
||||
:param cfg: The setup of argument parser.
|
||||
:return: The built argument parser.
|
||||
"""
|
||||
parser = ArgumentParser(
|
||||
prog="Assemblicon",
|
||||
description="The Programmatic Icon Assembly Workflow.",
|
||||
)
|
||||
if setup.require_input:
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
"--input",
|
||||
dest="input",
|
||||
action="store",
|
||||
type=Path,
|
||||
required=True,
|
||||
help="The path to input directory.",
|
||||
metavar="DIR",
|
||||
)
|
||||
if setup.require_output:
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
"--output",
|
||||
dest="output",
|
||||
action="store",
|
||||
type=Path,
|
||||
required=True,
|
||||
help="The path to output directory.",
|
||||
metavar="DIR",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--select",
|
||||
dest="selection",
|
||||
action="extend",
|
||||
nargs="+",
|
||||
type=str,
|
||||
help="Only build given name represented jobs.",
|
||||
metavar="RENDER",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--svg-render",
|
||||
dest="svg_render",
|
||||
action="store",
|
||||
type=CliSvgRenderKind,
|
||||
default=CliSvgRenderKind.Inkscape,
|
||||
help="The render used for rendering SVG into PNG.",
|
||||
metavar="RENDER",
|
||||
)
|
||||
return parser
|
||||
|
||||
|
||||
def parse_standard_opts(cfg: StdOptsSetup, ns: Namespace) -> StdOpts:
|
||||
return StdOpts(
|
||||
input_directory=ns.input if cfg.require_input else None,
|
||||
output_directory=ns.output if cfg.require_output else None,
|
||||
selection=ns.selection,
|
||||
svg_render=ns.svg_render,
|
||||
)
|
||||
@@ -1,121 +0,0 @@
|
||||
import math
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class Point:
|
||||
"""A point (or vector) on 2D plane with integer coordinates."""
|
||||
|
||||
x: int
|
||||
y: int
|
||||
|
||||
@staticmethod
|
||||
def from_tuple(t: tuple[int, int]) -> "Point":
|
||||
return Point(t[0], t[1])
|
||||
|
||||
def __add__(self, other: "Point") -> "Point":
|
||||
if not isinstance(other, Point):
|
||||
return NotImplemented
|
||||
return Point(self.x + other.x, self.y + other.y)
|
||||
|
||||
def __sub__(self, other: "Point") -> "Point":
|
||||
if not isinstance(other, Point):
|
||||
return NotImplemented
|
||||
return Point(self.x - other.x, self.y - other.y)
|
||||
|
||||
def __neg__(self) -> "Point":
|
||||
return Point(-self.x, -self.y)
|
||||
|
||||
def __mul__(self, other: int | float) -> "Point":
|
||||
if not isinstance(other, (int, float)):
|
||||
return NotImplemented
|
||||
return Point(math.floor(self.x * other), math.floor(self.y * other))
|
||||
|
||||
def __rmul__(self, other: int | float) -> "Point":
|
||||
return self.__mul__(other)
|
||||
|
||||
def __floordiv__(self, other: int | float) -> "Point":
|
||||
if not isinstance(other, (int, float)):
|
||||
return NotImplemented
|
||||
return Point(math.floor(self.x / other), math.floor(self.y / other))
|
||||
|
||||
def to_tuple(self) -> tuple[int, int]:
|
||||
"""
|
||||
Create the tuple representing this point by ``(x, y)``
|
||||
which can be used by Pillow.
|
||||
"""
|
||||
return (self.x, self.y)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Rectangle:
|
||||
"""
|
||||
A rectangle framed by two corner points.
|
||||
|
||||
pos1 is always assumed to be the top-left corner and pos2 the
|
||||
bottom-right one. This relation is a pure convention and is never
|
||||
validated: constructing a Rectangle with swapped or inverted corners
|
||||
produces a meaningless-but-silent result.
|
||||
"""
|
||||
|
||||
pos1: Point
|
||||
pos2: Point
|
||||
|
||||
@staticmethod
|
||||
def from_tuple(t: tuple[int, int, int, int]) -> "Rectangle":
|
||||
return Rectangle(Point(t[0], t[1]), Point(t[2], t[3]))
|
||||
|
||||
@staticmethod
|
||||
def from_min_max_rect(pos1: Point, pos2: Point) -> "Rectangle":
|
||||
return Rectangle(pos1, pos2)
|
||||
|
||||
@staticmethod
|
||||
def from_pos_size_rect(pos: Point, width: int, height: int) -> "Rectangle":
|
||||
return Rectangle(pos, pos + Point(width, height))
|
||||
|
||||
@staticmethod
|
||||
def from_margin_rect(
|
||||
width: int, height: int, top: int, right: int, bottom: int, left: int
|
||||
) -> "Rectangle":
|
||||
"""
|
||||
TODO
|
||||
|
||||
:param width: The width of parent.
|
||||
:param height: The height of parent.
|
||||
:param top: The distance from this rectangle to the top border of parent.
|
||||
:param right: The distance from this rectangle to the right border of parent.
|
||||
:param bottom: The distance from this rectangle to the bottom border of parent.
|
||||
:param left: The distance from this rectangle to the top border of parent.
|
||||
"""
|
||||
return Rectangle(Point(left, top), Point(width - right, height - bottom))
|
||||
|
||||
@staticmethod
|
||||
def from_padding_rect(
|
||||
pos: Point, top: int, right: int, bottom: int, left: int
|
||||
) -> "Rectangle":
|
||||
"""
|
||||
TODO
|
||||
|
||||
:param x: The X factor of reference point.
|
||||
:param y: The Y factor of reference point.
|
||||
:param top: The distance from reference point to this rectangle's top border.
|
||||
:param right: The distance from reference point to this rectangle's right border.
|
||||
:param bottom: The distance from reference point to this rectangle's bottom border.
|
||||
:param left: The distance from reference point to this rectangle's left border.
|
||||
"""
|
||||
return Rectangle(Point(pos.x - left, pos.y - top), Point(pos.x + right, pos.y + bottom))
|
||||
|
||||
@property
|
||||
def width(self) -> int:
|
||||
return self.pos2.x - self.pos1.x
|
||||
|
||||
@property
|
||||
def height(self) -> int:
|
||||
return self.pos2.y - self.pos1.y
|
||||
|
||||
def to_tuple(self) -> tuple[int, int, int, int]:
|
||||
"""
|
||||
Create the tuple representing this rectangle by ``(x1, y2 x2, y2)``
|
||||
which can be used by Pillow.
|
||||
"""
|
||||
return (self.pos1.x, self.pos1.y, self.pos2.x, self.pos2.y)
|
||||
@@ -1,114 +0,0 @@
|
||||
from collections import deque
|
||||
from dataclasses import dataclass
|
||||
from typing import Callable, Iterable, TypeVar, Generic
|
||||
from .environment import Environment, Prologue
|
||||
from ..logger import HIGHLEVEL_LOGGER as LOGGER
|
||||
|
||||
|
||||
P = TypeVar("P", bound=Prologue)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Job(Generic[P]):
|
||||
"""The class represents a job."""
|
||||
|
||||
name: str
|
||||
"""The name of this job."""
|
||||
dependencies: set[str]
|
||||
"""The dependencies of this job. Each item is a job name. Empty for no dependencies."""
|
||||
executor: Callable[[Environment[P]], None]
|
||||
"""The executor of this job."""
|
||||
|
||||
|
||||
class Context(Generic[P]):
|
||||
__jobs: dict[str, Job[P]]
|
||||
"""The dict storing all jobs. Key is job name and value is job payload."""
|
||||
__env: Environment[P]
|
||||
"""The shared environment passed to every job executor."""
|
||||
|
||||
def __init__(self, env: Environment[P]) -> None:
|
||||
self.__jobs = {}
|
||||
self.__env = env
|
||||
|
||||
def run(self, selected: Iterable[str] | None) -> None:
|
||||
"""
|
||||
Execute builder with given selections.
|
||||
|
||||
The selected jobs, together with all their transitive dependencies,
|
||||
are resolved and topologically sorted (Kahn's algorithm), then
|
||||
executed sequentially with the shared environment. The chain is
|
||||
aborted on the first failing executor.
|
||||
|
||||
:param selected: Only build jobs matching these given names.
|
||||
Their dependencies are always built first. None for building
|
||||
all jobs.
|
||||
"""
|
||||
# Step 1: resolve the selection closure. Collect the selected jobs
|
||||
# themselves plus all their transitive dependencies into "needed".
|
||||
needed: set[str] = set()
|
||||
if selected is None:
|
||||
needed = set(self.__jobs.keys())
|
||||
else:
|
||||
pending = list(selected)
|
||||
while pending:
|
||||
name = pending.pop()
|
||||
if name in needed:
|
||||
continue
|
||||
job = self.__jobs.get(name)
|
||||
if job is None:
|
||||
raise ValueError(f'Unknown job name: "{name}"')
|
||||
needed.add(name)
|
||||
pending.extend(job.dependencies)
|
||||
|
||||
# Step 2: Kahn topological sort over the sub-graph induced by "needed".
|
||||
# "in_degree": key is a job name, value is how many of its (needed)
|
||||
# dependencies are not built yet.
|
||||
# "dependents": key is a job name, value is the list of jobs in
|
||||
# "needed" that directly depend on the key job.
|
||||
in_degree: dict[str, int] = {name: 0 for name in needed}
|
||||
dependents: dict[str, list[str]] = {name: [] for name in needed}
|
||||
for name in needed:
|
||||
for dep in self.__jobs[name].dependencies:
|
||||
dependents[dep].append(name)
|
||||
in_degree[name] += 1
|
||||
queue = deque(
|
||||
name for name in self.__jobs if name in needed and in_degree[name] == 0
|
||||
)
|
||||
order: list[str] = []
|
||||
while queue:
|
||||
name = queue.popleft()
|
||||
order.append(name)
|
||||
for dependent in dependents[name]:
|
||||
in_degree[dependent] -= 1
|
||||
if in_degree[dependent] == 0:
|
||||
queue.append(dependent)
|
||||
if len(order) != len(needed):
|
||||
cyclic = sorted(needed - set(order))
|
||||
raise ValueError(f"Dependency cycle detected among jobs: {cyclic}")
|
||||
|
||||
# Step 3: execute the build chain in resolved order.
|
||||
for name in order:
|
||||
LOGGER.info('Running job "%s"...', name)
|
||||
self.__jobs[name].executor(self.__env)
|
||||
|
||||
def register(
|
||||
self,
|
||||
name: str,
|
||||
deps: Iterable[str],
|
||||
exec: Callable[[Environment[P]], None],
|
||||
) -> None:
|
||||
"""
|
||||
Register a new job with given name, dependencies and executor.
|
||||
|
||||
:param name: The name of job.
|
||||
:param deps: An iterable whose each item is the name of dependencies of this job.
|
||||
:param exec: The executor of this job.
|
||||
"""
|
||||
if name in self.__jobs:
|
||||
raise ValueError(f'Can not register 2 jobs with same name: "{name}"')
|
||||
|
||||
job_deps = set(deps)
|
||||
if name in job_deps:
|
||||
raise ValueError(f'Self-dependency is not allowed in job: "{name}"')
|
||||
|
||||
self.__jobs[name] = Job(name, job_deps, exec)
|
||||
@@ -1,404 +0,0 @@
|
||||
import tempfile
|
||||
import os
|
||||
import enum
|
||||
from typing import Optional
|
||||
from types import TracebackType
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import TypeVar, Generic
|
||||
from .utils import Artifact, ArtifactFile, VART_HW, check_vart
|
||||
from ..logger import HIGHLEVEL_LOGGER as LOGGER
|
||||
from ..lowlevel.artrdr import SvgRenderKind, SvgConfig, BlenderConfig
|
||||
from ..lowlevel import artio, artrdr
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Prologue:
|
||||
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 an existing directory."
|
||||
)
|
||||
if not self.output_directory.is_dir():
|
||||
raise ValueError(
|
||||
f"Given output path {self.output_directory} is not an existing directory."
|
||||
)
|
||||
|
||||
|
||||
P = TypeVar("P", bound=Prologue)
|
||||
|
||||
|
||||
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 = artrdr.SvgConfig(VART_HW, VART_HW)
|
||||
_DEFAULT_BLD_CONFIG = artrdr.BlenderConfig(VART_HW, VART_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 Environment(Generic[P]):
|
||||
__prologue: P
|
||||
"""The prologue used for environment."""
|
||||
__temp_dir: tempfile.TemporaryDirectory[str]
|
||||
|
||||
def __init__(self, prologue: P) -> None:
|
||||
self.__prologue = prologue
|
||||
self.__temp_dir = tempfile.TemporaryDirectory()
|
||||
|
||||
# construct basic layout of temporary directory
|
||||
self.temporary_artifact(_TEMP_USER_DIR).mkdir(parents=True, exist_ok=True)
|
||||
self.temporary_artifact(_TEMP_LIB_DIR, _TEMP_LIB_ALLOC_DIR).mkdir(
|
||||
parents=True, exist_ok=True
|
||||
)
|
||||
self.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.output_artifact(f"{hw}x{hw}", str(kind)).mkdir(
|
||||
parents=True, exist_ok=True
|
||||
)
|
||||
for kind in WinCategory:
|
||||
self.output_artifact(_ART_WIN, str(kind)).mkdir(parents=True, exist_ok=True)
|
||||
for kind in MacCategory:
|
||||
self.output_artifact(_ART_MAC, str(kind)).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# region: With Visitor Requirements
|
||||
|
||||
def __enter__(self) -> "Environment":
|
||||
return self
|
||||
|
||||
def __exit__(
|
||||
self,
|
||||
exc_type: type[BaseException] | None,
|
||||
exc_value: BaseException | None,
|
||||
traceback: TracebackType | None,
|
||||
) -> None:
|
||||
self.__temp_dir.cleanup()
|
||||
|
||||
# endregion
|
||||
|
||||
# region: Prologue Visitor
|
||||
|
||||
@property
|
||||
def prologue(self) -> P:
|
||||
return self.__prologue
|
||||
|
||||
# endregion
|
||||
|
||||
# region: New Artifact
|
||||
|
||||
def new_artifact(self) -> Artifact:
|
||||
"""
|
||||
Create new RGBA vanilla size (1024x1024) empty (#ffffff00) artifact.
|
||||
"""
|
||||
return artio.new_artifact((VART_HW, VART_HW))
|
||||
|
||||
# endregion
|
||||
|
||||
# region: Input Artifact
|
||||
|
||||
def input_artifact(self, *args: str) -> Path:
|
||||
"""
|
||||
Get the path to input artifact.
|
||||
"""
|
||||
return self.__prologue.input_directory / Path(*args)
|
||||
|
||||
def load_input_bitmap_artifact(self, *args: str) -> ArtifactFile:
|
||||
"""
|
||||
Load given input bitmap asset.
|
||||
"""
|
||||
p = self.input_artifact(*args)
|
||||
LOGGER.info("Loading input artifact: %s", p)
|
||||
return artio.load_input_artifact(p)
|
||||
|
||||
# endregion
|
||||
|
||||
# region: Temporary Artifact
|
||||
|
||||
def __user_temporary_artifact(self, *args: str) -> Path:
|
||||
return self.temporary_artifact(_TEMP_USER_DIR, *args)
|
||||
|
||||
def __lib_temporary_artifact(self, *args: str) -> Path:
|
||||
return self.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 Path(self.__temp_dir.name) / Path(*args)
|
||||
|
||||
def load_temporary_artifact(self, *args: str) -> ArtifactFile:
|
||||
p = self.temporary_artifact(*args)
|
||||
LOGGER.info("Loading temporary artifact: %s", p)
|
||||
return artio.load_artifact(p)
|
||||
|
||||
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.
|
||||
"""
|
||||
p = self.temporary_artifact(*args)
|
||||
LOGGER.info("Saving temporary artifact: %s", p)
|
||||
artio.save_artifact(art, p)
|
||||
|
||||
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:
|
||||
LOGGER.info("Rendering temporary SVG: %s", src)
|
||||
dst = self.__allocate_lib_temporary_artifact(".png")
|
||||
else:
|
||||
LOGGER.info("Rendering SVG: %s -> %s", src, dst)
|
||||
artrdr.render_svg(src, dst, self.__prologue.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:
|
||||
LOGGER.info("Rendering temporary Blender composition: %s", src)
|
||||
dst = self.__allocate_lib_temporary_artifact(".png")
|
||||
else:
|
||||
LOGGER.info("Rendering Blender composition: %s -> %s", src, dst)
|
||||
artrdr.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: Output Artifact
|
||||
|
||||
def output_artifact(self, *args: str) -> Path:
|
||||
return self.__prologue.output_directory / Path(*args)
|
||||
|
||||
# endregion
|
||||
|
||||
# region: Advanced Saver
|
||||
|
||||
def __fd_artifact(self, hw: int, kind: FdContext, name: str) -> Path:
|
||||
return self.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.
|
||||
"""
|
||||
LOGGER.info(
|
||||
'Saving FreeDesktop artifact with kind "%s" and name "%s".', kind, name
|
||||
)
|
||||
|
||||
check_vart(art)
|
||||
|
||||
for hw, thumbnail in zip(
|
||||
_FD_THUMBNAIL_HWS, artrdr.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:
|
||||
LOGGER.info(
|
||||
'Duplicating FreeDesktop artifact from kind "%s" name "%s" to kind "%s" name "%s".',
|
||||
src_kind,
|
||||
src_name,
|
||||
dst_kind,
|
||||
dst_name,
|
||||
)
|
||||
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.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.
|
||||
"""
|
||||
LOGGER.info(
|
||||
'Saving Windows-only artifact with kind "%s" and name "%s".', kind, name
|
||||
)
|
||||
check_vart(art)
|
||||
artrdr.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:
|
||||
LOGGER.info(
|
||||
'Duplicating Windows-only artifact from kind "%s" name "%s" to kind "%s" name "%s".',
|
||||
src_kind,
|
||||
src_name,
|
||||
dst_kind,
|
||||
dst_name,
|
||||
)
|
||||
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.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.
|
||||
"""
|
||||
LOGGER.info(
|
||||
'Saving macOS-only artifact with kind "%s" and name "%s".', kind, name
|
||||
)
|
||||
check_vart(art)
|
||||
artrdr.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:
|
||||
LOGGER.info(
|
||||
'Duplicating macOS-only artifact from kind "%s" name "%s" to kind "%s" name "%s".',
|
||||
src_kind,
|
||||
src_name,
|
||||
dst_kind,
|
||||
dst_name,
|
||||
)
|
||||
artio.dup_artifact(
|
||||
self.__mac_artifact(src_kind, src_name),
|
||||
self.__mac_artifact(dst_kind, dst_name),
|
||||
)
|
||||
|
||||
# endregion
|
||||
@@ -1,7 +0,0 @@
|
||||
from .common import Sink
|
||||
from .tempdir_sink import TempDirSink
|
||||
|
||||
__all__ = [
|
||||
"Sink",
|
||||
"TempDirSink"
|
||||
]
|
||||
@@ -1,11 +0,0 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from ..utils import Artifact
|
||||
|
||||
class Sink(ABC):
|
||||
"""
|
||||
Abstract base class for all sinks.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def push_artifact(self, art: Artifact) -> None:
|
||||
pass
|
||||
@@ -1,12 +0,0 @@
|
||||
from ..utils import Artifact
|
||||
from .common import Sink
|
||||
|
||||
class TempDirSink(Sink):
|
||||
|
||||
__args: tuple[str, ...]
|
||||
|
||||
def __init__(self, *args: str) -> None:
|
||||
self.__args = tuple(args)
|
||||
|
||||
def push_artifact(self, art: Artifact) -> None:
|
||||
pass
|
||||
@@ -1,7 +0,0 @@
|
||||
from .common import Source
|
||||
from .tempdir_source import TempDirSource
|
||||
|
||||
__all__ = [
|
||||
"Source",
|
||||
"TempDirSource"
|
||||
]
|
||||
@@ -1,11 +0,0 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from ..utils import Artifact
|
||||
|
||||
class Source(ABC):
|
||||
"""
|
||||
Abstract base class for all sources.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def pull_artifact(self) -> Artifact:
|
||||
pass
|
||||
@@ -1,12 +0,0 @@
|
||||
from ..utils import Artifact
|
||||
from .common import Source
|
||||
|
||||
class TempDirSource(Source):
|
||||
|
||||
__path_comps: tuple[str, ...]
|
||||
|
||||
def __init__(self, *args: str) -> None:
|
||||
self.__path_comps = tuple(args)
|
||||
|
||||
def pull_artifact(self) -> Artifact:
|
||||
pass
|
||||
@@ -1,11 +0,0 @@
|
||||
from ..lowlevel.utils import Artifact, ArtifactFile
|
||||
|
||||
VART_HW: int = 1024
|
||||
"""The height or width of Vanilla Artifact."""
|
||||
|
||||
|
||||
def check_vart(art: Artifact) -> None:
|
||||
if art.size != (VART_HW, VART_HW):
|
||||
raise ValueError(
|
||||
f"The size of general highlevel used artifact must be {VART_HW}x{VART_HW}, got {art.width}x{art.height} instead"
|
||||
)
|
||||
@@ -1,24 +0,0 @@
|
||||
import logging
|
||||
|
||||
|
||||
class LoggerFactory:
|
||||
def __init__(self) -> None:
|
||||
# setup log format and level
|
||||
logging.basicConfig(
|
||||
format="[%(levelname)s] [%(name)s] %(message)s", level=logging.INFO
|
||||
)
|
||||
|
||||
def get_app_logger(self) -> logging.Logger:
|
||||
return logging.getLogger("App")
|
||||
|
||||
def get_lowlevel_logger(self) -> logging.Logger:
|
||||
return logging.getLogger("Lowlevel")
|
||||
|
||||
def get_highlevel_logger(self) -> logging.Logger:
|
||||
return logging.getLogger("Highlevel")
|
||||
|
||||
|
||||
LOGGER_FACTORY = LoggerFactory()
|
||||
LOWLEVEL_LOGGER = LOGGER_FACTORY.get_lowlevel_logger()
|
||||
HIGHLEVEL_LOGGER = LOGGER_FACTORY.get_highlevel_logger()
|
||||
APP_LOGGER = LOGGER_FACTORY.get_app_logger()
|
||||
@@ -1,38 +0,0 @@
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
import PIL.Image
|
||||
from .utils import Artifact, ArtifactFile
|
||||
from ..logger import LOWLEVEL_LOGGER as LOGGER
|
||||
|
||||
|
||||
def new_artifact(size: tuple[int, int]) -> Artifact:
|
||||
"""
|
||||
Create new RGBA empty (#ffffff00) artifact with given size.
|
||||
"""
|
||||
return PIL.Image.new("RGBA", size, "#ffffff00")
|
||||
|
||||
|
||||
def load_input_artifact(p: Path) -> ArtifactFile:
|
||||
"""
|
||||
Load bitmap artifact (PNG, JPEG or BMP) which usually is located in input directory.
|
||||
"""
|
||||
LOGGER.debug("Loading input artifact: %s", p)
|
||||
return PIL.Image.open(p, "r", ["PNG", "JPEG", "BMP"])
|
||||
|
||||
|
||||
def load_artifact(p: Path) -> ArtifactFile:
|
||||
"""
|
||||
Load bitmap artifact (PNG-only) saved previously as the intermediary.
|
||||
"""
|
||||
LOGGER.debug("Loading temporary or output artifact: %s", p)
|
||||
return PIL.Image.open(p, "r", ["PNG"])
|
||||
|
||||
|
||||
def save_artifact(art: Artifact, p: Path) -> None:
|
||||
LOGGER.debug("Saving artifact: %s", p)
|
||||
art.save(p, format="PNG")
|
||||
|
||||
|
||||
def dup_artifact(src: Path, dst: Path) -> None:
|
||||
LOGGER.debug("Duplicating artifact: %s -> %s", src, dst)
|
||||
shutil.copyfile(src, dst)
|
||||
@@ -1,136 +0,0 @@
|
||||
import enum
|
||||
import math
|
||||
import PIL.Image
|
||||
from .utils import Artifact
|
||||
from ..geometry import Point
|
||||
|
||||
|
||||
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_to_offset(
|
||||
oversize_width: int,
|
||||
oversize_height: int,
|
||||
horizontal_anchor: HorizontalAnchor,
|
||||
vertical_anchor: VerticalAnchor,
|
||||
) -> Point:
|
||||
x: int
|
||||
match horizontal_anchor:
|
||||
case HorizontalAnchor.Left:
|
||||
x = 0
|
||||
case HorizontalAnchor.Center:
|
||||
x = oversize_width // 2
|
||||
case HorizontalAnchor.Right:
|
||||
x = oversize_width
|
||||
|
||||
y: int
|
||||
match vertical_anchor:
|
||||
case VerticalAnchor.Top:
|
||||
y = 0
|
||||
case VerticalAnchor.Center:
|
||||
y = oversize_height // 2
|
||||
case VerticalAnchor.Bottom:
|
||||
y = oversize_height
|
||||
|
||||
return Point(x, y)
|
||||
|
||||
|
||||
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 = anchor_to_offset(
|
||||
scaled_width - target_width,
|
||||
scaled_height - target_height,
|
||||
crop_horizontal_anchor,
|
||||
crop_vertical_anchor,
|
||||
)
|
||||
(crop_x, crop_y) = crop.to_tuple()
|
||||
return scaled.crop((crop_x, crop_y, crop_x + target_width, crop_y + target_height))
|
||||
|
||||
|
||||
def default_art_resize(art: Artifact, width: int, height: int) -> Artifact:
|
||||
"""
|
||||
Resize given artifact with default resample (LANCZOS).
|
||||
"""
|
||||
return art.resize((width, height), PIL.Image.Resampling.LANCZOS)
|
||||
|
||||
|
||||
def art_anchor_to_offset(
|
||||
art: Artifact,
|
||||
horizontal_anchor: HorizontalAnchor,
|
||||
vertical_anchor: VerticalAnchor,
|
||||
) -> Point:
|
||||
"""
|
||||
Resolve the anchor point position inside given artifact.
|
||||
|
||||
:param art: The artifact whose anchor point should be resolved.
|
||||
:param horizontal_anchor: The anchor selection at horizontal direction.
|
||||
:param vertical_anchor: The anchor selection at vertical direction.
|
||||
:return: The (x, y) coordinate of the anchor point. For Left/Top
|
||||
anchors this is the corresponding edge; for Right/Bottom anchors
|
||||
this is just outside the artifact (the exact corner coordinate).
|
||||
"""
|
||||
return anchor_to_offset(*art.size, horizontal_anchor, vertical_anchor)
|
||||
|
||||
|
||||
def align_paste(
|
||||
art: Artifact,
|
||||
clipboard: Artifact,
|
||||
art_pos: Point,
|
||||
clipboard_pos: Point,
|
||||
) -> None:
|
||||
"""
|
||||
Paste clipboard onto given artifact with two anchor points aligned.
|
||||
|
||||
The clipboard is pasted in the way that the point indicated by
|
||||
clipboard_pos on the clipboard exactly overlaps the point indicated
|
||||
by art_pos on the artifact. The art is modified in place. Positions
|
||||
resolving to negative paste origins are allowed and clipped by PIL
|
||||
just like a normal paste call.
|
||||
|
||||
:param art: The artifact pasted onto. Modified in place.
|
||||
:param clipboard: The artifact pasted as the clipboard.
|
||||
:param art_pos: The (x, y) point on the art to be aligned, usually
|
||||
resolved by anchor_to_pos.
|
||||
:param clipboard_pos: The (x, y) point on the clipboard to be
|
||||
aligned, usually resolved by anchor_to_pos.
|
||||
"""
|
||||
art.paste(clipboard, (art_pos - clipboard_pos).to_tuple())
|
||||
|
||||
|
||||
def align_alpha_composite(
|
||||
art: Artifact,
|
||||
clipboard: Artifact,
|
||||
art_pos: Point,
|
||||
clipboard_pos: Point,
|
||||
) -> None:
|
||||
"""
|
||||
Alpha-supported ``align_paste``.
|
||||
"""
|
||||
art.alpha_composite(clipboard, (art_pos - clipboard_pos).to_tuple())
|
||||
@@ -1,232 +0,0 @@
|
||||
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
|
||||
from ..logger import LOWLEVEL_LOGGER as LOGGER
|
||||
|
||||
|
||||
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.
|
||||
"""
|
||||
LOGGER.debug(
|
||||
"Rendering SVG artifact with render %s: %s -> %s", kind.name, src, dst
|
||||
)
|
||||
|
||||
# YYC MARK:
|
||||
# Inkscape will silently quit with return code 0
|
||||
# if given source file doesn't exist in file system.
|
||||
# So we need to check whether it is existing and popularize it to all render.
|
||||
if not src.is_file():
|
||||
raise ValueError(f"Source SVG file is not presented in file system: {src}")
|
||||
|
||||
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}. 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.
|
||||
"""
|
||||
LOGGER.debug("Rendering Blender artifact: %s -> %s", src, dst)
|
||||
|
||||
if not src.is_file():
|
||||
raise ValueError(
|
||||
f"Source Blender composition file is not presented in file system: {src}"
|
||||
)
|
||||
|
||||
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.
|
||||
"""
|
||||
LOGGER.debug("Rendering thumbnail...")
|
||||
|
||||
# check whether given artifact is square rectangle.
|
||||
if art.width != art.height:
|
||||
raise ValueError(f'Thumbnail render only accept square rectangle.')
|
||||
# scale artifact up to the maximum resolution hws if it is too small.
|
||||
max_hws = max(hws)
|
||||
if art.width < max_hws:
|
||||
art = art.resize((max_hws, max_hws), PIL.Image.Resampling.LANCZOS)
|
||||
|
||||
# render thumbnail
|
||||
for hw in hws:
|
||||
LOGGER.debug("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.
|
||||
"""
|
||||
LOGGER.debug("Rendering .ICO artifact: %s", dst)
|
||||
|
||||
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.
|
||||
"""
|
||||
LOGGER.debug("Rendering .ICNS artifact: %s", dst)
|
||||
|
||||
# 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,5 +0,0 @@
|
||||
from PIL.Image import Image
|
||||
from PIL.ImageFile import ImageFile
|
||||
|
||||
type Artifact = Image
|
||||
type ArtifactFile = ImageFile
|
||||
@@ -1,6 +1,6 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
from .assemblicon.cli import (
|
||||
from assemblicon.cli import (
|
||||
SvgRenderKind,
|
||||
StdOptsSetup,
|
||||
create_argparser,
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
from ..types import AuroraContext, AuroraEnvironment
|
||||
from ..assemblicon.highlevel.environment import FdContext
|
||||
from ..assemblicon.highlevel.utils import VART_HW
|
||||
from ..assemblicon.geometry import Rectangle
|
||||
from ..assemblicon.lowlevel.artproc import (
|
||||
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,
|
||||
@@ -16,20 +17,50 @@ from ..assemblicon.lowlevel.artproc import (
|
||||
def register(ctx: AuroraContext) -> None:
|
||||
ctx.register(
|
||||
"nostd/action/image-properties",
|
||||
("nostd/mimetype/image-jpeg", "std/status/dialog-infomation"),
|
||||
(
|
||||
TempArtSource("image-jpeg.png"),
|
||||
TempArtSource("dialog-infomation.png"),
|
||||
FdArtSink(FdContext.Actions, "image-properties"),
|
||||
),
|
||||
build_image_properties_icon,
|
||||
)
|
||||
ctx.register(
|
||||
"nostd/action/image-zoom-original",
|
||||
("nostd/mimetype/image-jpeg", "component/triangles"),
|
||||
(
|
||||
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) -> None:
|
||||
art = env.load_temporary_artifact("image-jpeg.png")
|
||||
def build_image_properties_icon(
|
||||
env: AuroraEnvironment,
|
||||
jpeg_src: TempArtSource,
|
||||
infomation_src: TempArtSource,
|
||||
fd_sink: FdArtSink,
|
||||
) -> None:
|
||||
art = jpeg_src.pull(env)
|
||||
|
||||
info_mark = env.load_temporary_artifact("dialog-infomation.png")
|
||||
info_mark = infomation_src.pull(env)
|
||||
resized_info_mark = default_art_resize(info_mark, 500, 500)
|
||||
|
||||
align_alpha_composite(
|
||||
@@ -42,31 +73,32 @@ def build_image_properties_icon(env: AuroraEnvironment) -> None:
|
||||
)
|
||||
|
||||
# save artifact
|
||||
env.save_fd_artifact(art, FdContext.Actions, "image-properties")
|
||||
fd_sink.push(env, art)
|
||||
|
||||
|
||||
def build_image_zoom_original_icon(env: AuroraEnvironment) -> None:
|
||||
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(
|
||||
env.load_temporary_artifact("component", "triangle-left.png"), *TRI_SIZE
|
||||
)
|
||||
tri_down = default_art_resize(
|
||||
env.load_temporary_artifact("component", "triangle-down.png"), *TRI_SIZE
|
||||
)
|
||||
tri_right = default_art_resize(
|
||||
env.load_temporary_artifact("component", "triangle-right.png"), *TRI_SIZE
|
||||
)
|
||||
tri_up = default_art_resize(
|
||||
env.load_temporary_artifact("component", "triangle-up.png"), *TRI_SIZE
|
||||
)
|
||||
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 = env.load_temporary_artifact("image-jpeg.png")
|
||||
inner = inner_src.pull(env)
|
||||
resized_inner = default_art_resize(inner, 680, 680)
|
||||
|
||||
# composite this icon
|
||||
icon = env.new_artifact()
|
||||
icon = new_art.pull()
|
||||
|
||||
align_paste(
|
||||
icon,
|
||||
@@ -125,4 +157,87 @@ def build_image_zoom_original_icon(env: AuroraEnvironment) -> None:
|
||||
)
|
||||
|
||||
# save it
|
||||
env.save_fd_artifact(icon, FdContext.Actions, "image-zoom-original")
|
||||
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)
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import PIL.Image
|
||||
from ..types import AuroraContext, AuroraEnvironment
|
||||
from ..assemblicon.highlevel.environment import FdContext, WinCategory, MacCategory
|
||||
from ..assemblicon.lowlevel.artproc import (
|
||||
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,
|
||||
@@ -12,19 +18,41 @@ from ..assemblicon.lowlevel.artproc import (
|
||||
|
||||
def register(ctx: AuroraContext) -> None:
|
||||
ctx.register(
|
||||
"nostd/application/org.sarasachipworkshop.picture",
|
||||
("nostd/mimetype/image-jpeg", ),
|
||||
build_sarasacw_picture_icons,
|
||||
"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_icons(env: AuroraEnvironment) -> None:
|
||||
app = env.new_artifact()
|
||||
frame = env.render_svg_then_load(
|
||||
env.input_artifact("app", "sarasacw-picture", "app-frame.svg")
|
||||
)
|
||||
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 = env.load_temporary_artifact("image-jpeg.png")
|
||||
inner = inner_src.pull(env)
|
||||
resized_inner = default_art_resize(inner, 990, 990)
|
||||
|
||||
align_paste(
|
||||
@@ -38,6 +66,17 @@ def build_sarasacw_picture_icons(env: AuroraEnvironment) -> None:
|
||||
app.alpha_composite(frame, (0, 0))
|
||||
|
||||
# save artifact
|
||||
env.save_fd_artifact(app, FdContext.Applications, "org.sarasachipworkshop.picture")
|
||||
env.save_win_artifact(app, WinCategory.Application, "sarasacw-picture")
|
||||
env.save_mac_artifact(app, MacCategory.Application, "sarasacw-picture")
|
||||
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,68 +1,8 @@
|
||||
import PIL.Image
|
||||
import PIL.ImageEnhance
|
||||
from ..types import AuroraContext, AuroraEnvironment
|
||||
from ..assemblicon.highlevel.environment import FdContext, WinCategory, MacCategory
|
||||
from ..assemblicon.highlevel.utils import Artifact
|
||||
from ..assemblicon.geometry import Rectangle, Point
|
||||
from ..assemblicon.lowlevel.artproc import (
|
||||
aspect_ratio_scale_and_crop,
|
||||
VerticalAnchor,
|
||||
HorizontalAnchor,
|
||||
)
|
||||
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:
|
||||
ctx.register("component/image-base", (), build_image_base_icon)
|
||||
ctx.register(
|
||||
"nostd/mimetype/image-jpeg", ("component/image-base",), build_image_jpeg_icon
|
||||
)
|
||||
|
||||
|
||||
IMAGE_INNER_RECT = Rectangle(Point(20 * 4, 52 * 4), Point(236 * 4, 204 * 4))
|
||||
|
||||
|
||||
def build_image_base_icon(env: AuroraEnvironment) -> None:
|
||||
env.render_svg(
|
||||
env.input_artifact("component", "image-base.svg"),
|
||||
env.temporary_artifact("component", "image-base.png"),
|
||||
)
|
||||
|
||||
|
||||
def build_image_jpeg_icon(env: AuroraEnvironment) -> None:
|
||||
jpg_image = env.load_temporary_artifact("component", "image-base.png")
|
||||
|
||||
jpg_image_inner = env.load_input_bitmap_artifact(
|
||||
"ext", "bitmap", "Niinsaare_järv.jpg"
|
||||
)
|
||||
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
|
||||
env.save_temporary_artifact(jpg_image, "image-jpeg.png")
|
||||
# save for FD
|
||||
env.save_fd_artifact(jpg_image, FdContext.MimeTypes, "image-jpeg")
|
||||
# save for windows and mac
|
||||
env.save_win_artifact(jpg_image, WinCategory.Extension, "jpg")
|
||||
env.save_mac_artifact(jpg_image, MacCategory.Extension, "jpg")
|
||||
pass
|
||||
|
||||
@@ -1,101 +1,176 @@
|
||||
import PIL.Image
|
||||
from ..types import AuroraContext, AuroraEnvironment
|
||||
from ..assemblicon.highlevel.environment import FdContext
|
||||
from ..assemblicon.highlevel.utils import Artifact
|
||||
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 register(ctx: AuroraContext) -> None:
|
||||
ctx.register("std/action/go-[previous|down|next|up]", (), build_go_icons)
|
||||
ctx.register(
|
||||
"std/action/go-[previous|down|next|up]",
|
||||
(
|
||||
SvgSource("go-base.svg"),
|
||||
SvgSource("go-inner-shining.svg"),
|
||||
SvgSource("go-inner.svg"),
|
||||
FdArtSink(FdContext.Actions, "go-previous"),
|
||||
FdArtSink(FdContext.Actions, "go-down"),
|
||||
FdArtSink(FdContext.Actions, "go-next"),
|
||||
FdArtSink(FdContext.Actions, "go-up"),
|
||||
),
|
||||
build_go_icons,
|
||||
)
|
||||
|
||||
ctx.register("std/action/object-flip-*", (), build_object_flip_icons)
|
||||
ctx.register("std/action/object-rotate-*", (), build_object_rotate_icons)
|
||||
ctx.register(
|
||||
"std/action/object-flip-horizontal",
|
||||
(
|
||||
SvgSource("object-flip-horizontal.svg"),
|
||||
FdArtSink(FdContext.Actions, "object-flip-horizontal"),
|
||||
),
|
||||
build_object_flip_horizontal_icon,
|
||||
)
|
||||
ctx.register(
|
||||
"std/action/object-flip-vertical",
|
||||
(
|
||||
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]",
|
||||
("component/triangles",),
|
||||
(
|
||||
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]", (), build_zoom_in_out_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) -> None:
|
||||
# build go-* base
|
||||
go_base = env.render_svg_then_load(env.input_artifact("component", "go-base.svg"))
|
||||
# build go-* shining
|
||||
go_inner_shining = env.render_svg_then_load(
|
||||
env.input_artifact("component", "go-inner-shining.svg"),
|
||||
)
|
||||
# build go-* inner
|
||||
go_inner = env.render_svg_then_load(
|
||||
env.input_artifact("component", "go-inner.svg"),
|
||||
)
|
||||
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:
|
||||
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()
|
||||
# render shining with inner mask
|
||||
go.paste(go_inner_shining, (0, 0), go_mask)
|
||||
|
||||
# save as FD action
|
||||
env.save_fd_artifact(go, FdContext.Actions, f"go-{part}")
|
||||
sink.push(env, go)
|
||||
|
||||
# rotate it for next calling
|
||||
go_mask = go_mask.transpose(PIL.Image.Transpose.ROTATE_90)
|
||||
|
||||
|
||||
def build_object_flip_icons(env: AuroraEnvironment) -> None:
|
||||
art = env.render_svg_then_load(env.input_artifact("object-flip-horizontal.svg"))
|
||||
with art:
|
||||
env.save_fd_artifact(art, FdContext.Actions, "object-flip-horizontal")
|
||||
art = env.render_svg_then_load(env.input_artifact("object-flip-vertical.svg"))
|
||||
with art:
|
||||
env.save_fd_artifact(art, FdContext.Actions, "object-flip-vertical")
|
||||
|
||||
|
||||
def build_object_rotate_icons(env: AuroraEnvironment) -> None:
|
||||
art = env.render_svg_then_load(env.input_artifact("object-rotate-left.svg"))
|
||||
with art:
|
||||
env.save_fd_artifact(art, FdContext.Actions, "object-rotate-left")
|
||||
art = env.render_svg_then_load(env.input_artifact("object-rotate-right.svg"))
|
||||
with art:
|
||||
env.save_fd_artifact(art, FdContext.Actions, "object-rotate-right")
|
||||
|
||||
|
||||
def build_zoom_origin_and_fit_best_icons(env: AuroraEnvironment) -> None:
|
||||
# load triangle assets
|
||||
tri_left = env.load_temporary_artifact("component", "triangle-left.png")
|
||||
tri_down = env.load_temporary_artifact("component", "triangle-down.png")
|
||||
tri_right = env.load_temporary_artifact("component", "triangle-right.png")
|
||||
tri_up = env.load_temporary_artifact("component", "triangle-up.png")
|
||||
|
||||
# build icons
|
||||
with tri_left, tri_down, tri_right, tri_up:
|
||||
build_zoom_origin_icon(env, tri_left, tri_down, tri_right, tri_up)
|
||||
build_zoom_fit_best_icon(env, tri_left, tri_down, tri_right, tri_up)
|
||||
|
||||
|
||||
def build_zoom_origin_icon(
|
||||
env: AuroraEnvironment,
|
||||
tri_left: Artifact,
|
||||
tri_down: Artifact,
|
||||
tri_right: Artifact,
|
||||
tri_up: Artifact,
|
||||
def build_object_flip_horizontal_icon(
|
||||
env: AuroraEnvironment, src: SvgSource, sink: FdArtSink
|
||||
) -> None:
|
||||
# TODO: finihs this in future
|
||||
pass
|
||||
with src.pull(env) as art:
|
||||
sink.push(env, art)
|
||||
|
||||
|
||||
def build_zoom_fit_best_icon(
|
||||
env: AuroraEnvironment,
|
||||
tri_left: Artifact,
|
||||
tri_down: Artifact,
|
||||
tri_right: Artifact,
|
||||
tri_up: Artifact,
|
||||
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) -> None:
|
||||
# TODO: finihs 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)
|
||||
|
||||
@@ -1,46 +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", (), build_generic_background)
|
||||
ctx.register(
|
||||
"component/generic-background",
|
||||
(
|
||||
SvgSource("component", "generic-background.svg"),
|
||||
TempArtSink("component", "generic-background.png"),
|
||||
),
|
||||
build_generic_background,
|
||||
)
|
||||
ctx.register(
|
||||
"component/triangles",
|
||||
("component/generic-background",),
|
||||
(
|
||||
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,
|
||||
)
|
||||
|
||||
|
||||
def build_generic_background(env: AuroraEnvironment) -> None:
|
||||
env.render_svg(
|
||||
env.input_artifact("component", "generic-background.svg"),
|
||||
env.temporary_artifact("component", "generic-background.png"),
|
||||
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_triangle_icons(env: AuroraEnvironment) -> None:
|
||||
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 = env.render_svg_then_load(env.input_artifact("component", "triangle.svg"))
|
||||
bg = env.load_temporary_artifact("component", "generic-background.png")
|
||||
tri = tri_src.pull(env)
|
||||
bg = bg_src.pull(env)
|
||||
|
||||
# create 4 triangle icons for following building
|
||||
tri_left = env.new_artifact()
|
||||
tri_left = new_art.pull()
|
||||
tri_left.paste(bg, (0, 0), tri)
|
||||
tri = tri.transpose(PIL.Image.Transpose.ROTATE_90)
|
||||
|
||||
tri_down = env.new_artifact()
|
||||
tri_down = new_art.pull()
|
||||
tri_down.paste(bg, (0, 0), tri)
|
||||
tri = tri.transpose(PIL.Image.Transpose.ROTATE_90)
|
||||
|
||||
tri_right = env.new_artifact()
|
||||
tri_right = new_art.pull()
|
||||
tri_right.paste(bg, (0, 0), tri)
|
||||
tri = tri.transpose(PIL.Image.Transpose.ROTATE_90)
|
||||
|
||||
tri_up = env.new_artifact()
|
||||
tri_up = new_art.pull()
|
||||
tri_up.paste(bg, (0, 0), tri)
|
||||
|
||||
# save them for reuse
|
||||
env.save_temporary_artifact(tri_left, "component", "triangle-left.png")
|
||||
env.save_temporary_artifact(tri_down, "component", "triangle-down.png")
|
||||
env.save_temporary_artifact(tri_right, "component", "triangle-right.png")
|
||||
env.save_temporary_artifact(tri_up, "component", "triangle-up.png")
|
||||
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)
|
||||
|
||||
@@ -1,17 +1,270 @@
|
||||
import PIL.Image
|
||||
import PIL.ImageEnhance
|
||||
from ..types import AuroraContext, AuroraEnvironment
|
||||
from ..assemblicon.highlevel.environment import FdContext
|
||||
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",
|
||||
("nostd/mimetype/image-jpeg",),
|
||||
(
|
||||
FdArtSource(FdContext.MimeTypes, "image-jpeg"),
|
||||
FdArtSink(FdContext.MimeTypes, "image-x-generic"),
|
||||
),
|
||||
build_image_x_generic,
|
||||
)
|
||||
|
||||
|
||||
def build_image_x_generic(env: AuroraEnvironment) -> None:
|
||||
# use image/jpeg as the default image icon
|
||||
env.dup_fd_artifact(
|
||||
FdContext.MimeTypes, "image-jpeg", FdContext.MimeTypes, "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)
|
||||
|
||||
@@ -1,51 +1,79 @@
|
||||
from ..types import AuroraContext, AuroraEnvironment
|
||||
from ..assemblicon.highlevel.environment import FdContext
|
||||
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", (), build_dialog_infomation_icon)
|
||||
ctx.register("std/status/dialog-warning", (), build_dialog_warning_icon)
|
||||
ctx.register("std/status/dialog-error", (), build_dialog_error_icon)
|
||||
ctx.register("std/status/dialog-question", (), build_dialog_question_icon)
|
||||
|
||||
|
||||
def build_dialog_infomation_icon(env: AuroraEnvironment) -> None:
|
||||
# save dialog icon with FreeDesktop icon and temp icon for reuse.
|
||||
art = env.render_svg_then_load(
|
||||
env.input_artifact("dialog-infomation.svg"),
|
||||
env.temporary_artifact("dialog-infomation.png"),
|
||||
ctx.register(
|
||||
"std/status/dialog-infomation",
|
||||
(
|
||||
SvgSource("dialog-infomation.svg"),
|
||||
TempArtSink("dialog-infomation.png"),
|
||||
FdArtSink(FdContext.Status, "dialog-infomation"),
|
||||
),
|
||||
build_dialog_infomation_icon,
|
||||
)
|
||||
with art:
|
||||
env.save_fd_artifact(art, FdContext.Status, "dialog-infomation")
|
||||
|
||||
|
||||
def build_dialog_warning_icon(env: AuroraEnvironment) -> None:
|
||||
# save dialog icon with FreeDesktop icon and temp icon for reuse.
|
||||
art = env.render_svg_then_load(
|
||||
env.input_artifact("dialog-warning.svg"),
|
||||
env.temporary_artifact("dialog-warning.png"),
|
||||
ctx.register(
|
||||
"std/status/dialog-warning",
|
||||
(
|
||||
SvgSource("dialog-warning.svg"),
|
||||
TempArtSink("dialog-warning.png"),
|
||||
FdArtSink(FdContext.Status, "dialog-warning"),
|
||||
),
|
||||
build_dialog_warning_icon,
|
||||
)
|
||||
with art:
|
||||
env.save_fd_artifact(art, FdContext.Status, "dialog-warning")
|
||||
|
||||
|
||||
def build_dialog_error_icon(env: AuroraEnvironment) -> None:
|
||||
# save dialog icon with FreeDesktop icon and temp icon for reuse.
|
||||
art = env.render_svg_then_load(
|
||||
env.input_artifact("dialog-error.svg"),
|
||||
env.temporary_artifact("dialog-error.png"),
|
||||
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,
|
||||
)
|
||||
with art:
|
||||
env.save_fd_artifact(art, FdContext.Status, "dialog-error")
|
||||
|
||||
|
||||
def build_dialog_question_icon(env: AuroraEnvironment) -> None:
|
||||
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
|
||||
# # save dialog icon with FreeDesktop icon and temp icon for reuse.
|
||||
# art = env.render_svg_then_load(
|
||||
# env.input_artifact("dialog-question.svg"),
|
||||
# env.temporary_artifact("dialog-question.png"),
|
||||
# )
|
||||
# with art:
|
||||
# env.save_fd_artifact(art, FdContext.Status, "dialog-question")
|
||||
pass
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from dataclasses import dataclass
|
||||
from .assemblicon.highlevel.environment import Environment, Prologue
|
||||
from .assemblicon.highlevel.context import Context
|
||||
from assemblicon.highlevel.environment import Environment, Prologue
|
||||
from assemblicon.highlevel.context import Context
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
||||
Generated
+10
-2
@@ -2,16 +2,24 @@ version = 1
|
||||
revision = 2
|
||||
requires-python = ">=3.13"
|
||||
|
||||
[[package]]
|
||||
name = "assemblicon"
|
||||
version = "0.1.0"
|
||||
source = { git = "https://github.com/SarasasChipWorkshop/assemblicon#0073298d45d6c44fc164456ebdec5f0d3ada5dff" }
|
||||
dependencies = [
|
||||
{ name = "pillow" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aurora-iconset-builder"
|
||||
version = "0.1.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "pillow" },
|
||||
{ name = "assemblicon" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [{ name = "pillow", specifier = ">=12.3.0" }]
|
||||
requires-dist = [{ name = "assemblicon", git = "https://github.com/SarasasChipWorkshop/assemblicon" }]
|
||||
|
||||
[[package]]
|
||||
name = "pillow"
|
||||
|
||||
Reference in New Issue
Block a user