feat: support image-png/bmp/gif

This commit is contained in:
2026-09-09 12:09:33 +08:00
parent 61e003c741
commit 42a2b08abf
8 changed files with 299 additions and 90 deletions
+20
View File
@@ -13,3 +13,23 @@ I, Jonathan Zander, CC BY-SA 3.0 <http://creativecommons.org/licenses/by-sa/3.0/
https://commons.wikimedia.org/wiki/File:Daisy_(Argyranthemum_frutescens).jpg 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 Fir0002, CC BY-SA 3.0 <http://creativecommons.org/licenses/by-sa/3.0/>, via Wikimedia Commons
https://commons.wikimedia.org/wiki/File:Nycticorax_nycticorax12.jpg
This file is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported, 2.5 Generic, 2.0 Generic and 1.0 Generic license.
Calibas
https://commons.wikimedia.org/wiki/File:Van_Gogh_-_Starry_Night_-_Google_Art_Project.jpg
This work is in the public domain in its country of origin and other countries and areas where the copyright term is the author's life plus 100 years or fewer.
Vincent van Gogh
Google Arts & Culture
https://commons.wikimedia.org/wiki/File:Tulipa_suaveolens_floriade_to_Canberra.jpg
This file is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license.
John O'Neill
https://commons.wikimedia.org/wiki/File:International_hot_air_balloon_festival_in_leon_guanajuato_mexico_2012.jpg
This file is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license.
Tomascastelazo
https://commons.wikimedia.org/wiki/File:Hot-air_balloon_in_Tambov_-_02.jpg
This file is licensed under the Creative Commons Attribution-Share Alike 4.0 International license.
Alexander Novikov
+19 -1
View File
@@ -1,5 +1,23 @@
[[licenses]] [[licenses]]
file = 'Niinsaare_järv.jpg' file = 'Niinsaare_järv.jpg'
license = "CC-BY-SA-3.0" license = "CC-BY-SA-3.0"
authors = ["Ivar Leidus", "Wikimedia Commons"] authors = ["Ivar Leidus"]
source = 'https://commons.wikimedia.org/wiki/File:Niinsaare_j%C3%A4rv.jpg' source = 'https://commons.wikimedia.org/wiki/File:Niinsaare_j%C3%A4rv.jpg'
[[licenses]]
file = 'Tulipa_suaveolens_floriade_to_Canberra.jpg'
license = "CC-BY-SA-3.0"
authors = ["John O'Neill"]
source = 'https://commons.wikimedia.org/wiki/File:Tulipa_suaveolens_floriade_to_Canberra.jpg'
[[licenses]]
file = 'Hot-air_balloon_in_Tambov_-_02.jpg'
license = "CC-BY-SA-4.0"
authors = ["Alexander Novikov"]
source = 'https://commons.wikimedia.org/wiki/File:Hot-air_balloon_in_Tambov_-_02.jpg'
[[licenses]]
file = 'Van_Gogh_-_Starry_Night_-_Google_Art_Project.jpg'
license = "CC0-1.0" # Public Domain actually.
authors = ["Vincent van Gogh", "Google Arts & Culture"]
source = 'https://commons.wikimedia.org/wiki/File:Van_Gogh_-_Starry_Night_-_Google_Art_Project.jpg'
Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

@@ -78,7 +78,7 @@ class Rectangle:
width: int, height: int, top: int, right: int, bottom: int, left: int width: int, height: int, top: int, right: int, bottom: int, left: int
) -> "Rectangle": ) -> "Rectangle":
""" """
TODO Create the rectangle with the size of this region's parent, and the margin between this region and its parent.
:param width: The width of parent. :param width: The width of parent.
:param height: The height of parent. :param height: The height of parent.
@@ -94,7 +94,7 @@ class Rectangle:
pos: Point, top: int, right: int, bottom: int, left: int pos: Point, top: int, right: int, bottom: int, left: int
) -> "Rectangle": ) -> "Rectangle":
""" """
TODO Create the rectangle with the central point in this region's parent, and the padding between this point and region's border.
:param x: The X factor of reference point. :param x: The X factor of reference point.
:param y: The Y factor of reference point. :param y: The Y factor of reference point.
@@ -103,7 +103,9 @@ class Rectangle:
:param bottom: The distance from reference point to this rectangle's bottom 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. :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)) return Rectangle(
Point(pos.x - left, pos.y - top), Point(pos.x + right, pos.y + bottom)
)
@property @property
def width(self) -> int: def width(self) -> int:
@@ -1,75 +1,8 @@
import PIL.Image
import PIL.ImageEnhance
from ..types import AuroraContext, AuroraEnvironment from ..types import AuroraContext, AuroraEnvironment
from ..assemblicon.highlevel.utils import FdContext, WinCategory, MacCategory from ..assemblicon.highlevel.utils import FdContext, WinCategory, MacCategory
from ..assemblicon.highlevel.source import SvgSource, BitmapSource, TempArtSource from ..assemblicon.highlevel.source import SvgSource, TempArtSource
from ..assemblicon.highlevel.sink import TempArtSink, FdArtSink, WinArtSink, MacArtSink from ..assemblicon.highlevel.sink import TempArtSink, FdArtSink, WinArtSink, MacArtSink
from ..assemblicon.geometry import Rectangle, Point
from ..assemblicon.lowlevel.artproc import (
aspect_ratio_scale_and_crop,
VerticalAnchor,
HorizontalAnchor,
)
def register(ctx: AuroraContext) -> None: def register(ctx: AuroraContext) -> None:
ctx.register( pass
"nostd/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,
)
IMAGE_INNER_RECT = Rectangle(Point(20 * 4, 52 * 4), Point(236 * 4, 204 * 4))
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)
@@ -18,14 +18,6 @@ def register(ctx: AuroraContext) -> None:
), ),
build_generic_background, build_generic_background,
) )
ctx.register(
"component/image-base",
(
SvgSource("component", "image-base.svg"),
TempArtSink("component", "image-base.png"),
),
build_image_base_icon,
)
ctx.register( ctx.register(
"component/triangles", "component/triangles",
( (
@@ -67,12 +59,6 @@ def build_generic_background(
src.pull_to_temp(env, sink) src.pull_to_temp(env, sink)
def build_image_base_icon(
env: AuroraEnvironment, src: SvgSource, sink: TempArtSink
) -> None:
src.pull_to_temp(env, sink)
def build_triangle_icons( def build_triangle_icons(
env: AuroraEnvironment, env: AuroraEnvironment,
tri_src: SvgSource, tri_src: SvgSource,
@@ -1,10 +1,32 @@
import PIL.Image
import PIL.ImageEnhance
from ..types import AuroraContext, AuroraEnvironment from ..types import AuroraContext, AuroraEnvironment
from ..assemblicon.highlevel.utils import FdContext from ..assemblicon.highlevel.utils import FdContext, WinCategory, MacCategory
from ..assemblicon.highlevel.source import FdArtSource from ..assemblicon.highlevel.source import (
from ..assemblicon.highlevel.sink import FdArtSink 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: 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( ctx.register(
"std/mimetype/image-x-generic", "std/mimetype/image-x-generic",
( (
@@ -13,6 +35,59 @@ def register(ctx: AuroraContext) -> None:
), ),
build_image_x_generic, build_image_x_generic,
) )
ctx.register(
"std/mimetype/image-jpeg",
(
TempArtSource("component", "image-base.png"),
BitmapSource("bitmap", "Niinsaare_järv.jpg"),
TempArtSink("image-jpeg.png"),
FdArtSink(FdContext.MimeTypes, "image-jpeg"),
WinArtSink(WinCategory.Extension, "jpg"),
MacArtSink(MacCategory.Extension, "jpg"),
),
build_image_jpeg_icon,
)
ctx.register(
"std/mimetype/image-png",
(
TempArtSource("component", "image-base.png"),
BitmapSource("bitmap", "Tulipa_suaveolens_floriade_to_Canberra.jpg"),
BitmapSource("bitmap", "Tulipa_suaveolens_floriade_to_Canberra-mask.png"),
SvgSource("image-png-checkerboard.svg"),
FdArtSink(FdContext.MimeTypes, "image-png"),
WinArtSink(WinCategory.Extension, "png"),
MacArtSink(MacCategory.Extension, "png"),
),
build_image_png_icon,
)
ctx.register(
"std/mimetype/image-bmp",
(
TempArtSource("component", "image-base.png"),
BitmapSource("bitmap", "Van_Gogh_-_Starry_Night_-_Google_Art_Project.jpg"),
FdArtSink(FdContext.MimeTypes, "image-bmp"),
WinArtSink(WinCategory.Extension, "bmp"),
MacArtSink(MacCategory.Extension, "bmp"),
),
build_image_bmp_icon,
)
ctx.register(
"std/mimetype/image-gif",
(
TempArtSource("component", "image-base.png"),
BitmapSource("bitmap", "Hot-air_balloon_in_Tambov_-_02.jpg"),
FdArtSink(FdContext.MimeTypes, "image-gif"),
WinArtSink(WinCategory.Extension, "gif"),
MacArtSink(MacCategory.Extension, "gif"),
),
build_image_gif_icon,
)
def build_image_base_icon(
env: AuroraEnvironment, src: SvgSource, sink: TempArtSink
) -> None:
src.pull_to_temp(env, sink)
def build_image_x_generic( def build_image_x_generic(
@@ -20,3 +95,176 @@ def build_image_x_generic(
) -> None: ) -> None:
# use image/jpeg as the default image icon # use image/jpeg as the default image icon
src.link_to(env, sink) 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)
@@ -64,6 +64,8 @@ def build_dialog_warning_icon(
def build_dialog_error_icon( def build_dialog_error_icon(
env: AuroraEnvironment, src: SvgSource, temp_sink: TempArtSink, fd_sink: FdArtSink env: AuroraEnvironment, src: SvgSource, temp_sink: TempArtSink, fd_sink: FdArtSink
) -> None: ) -> None:
# TODO: update this icon from rectangle to circle
# save dialog icon with FreeDesktop icon and temp icon for reuse. # save dialog icon with FreeDesktop icon and temp icon for reuse.
src.pull_to_temp(env, temp_sink) src.pull_to_temp(env, temp_sink)
with temp_sink.repull(env) as art: with temp_sink.repull(env) as art: