-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathlayergroup.py
More file actions
184 lines (159 loc) · 5.23 KB
/
layergroup.py
File metadata and controls
184 lines (159 loc) · 5.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
"""Base class."""
from __future__ import annotations
from typing import Any
from blendmodes.blend import BlendType
from PIL import Image
class LayerGroup:
"""A representation of an image layer or group."""
def __init__(
self,
name: str,
dimensions: tuple[int, int],
offsets: tuple[int, int] = (0, 0),
opacity: float = 1.0,
*,
visible: bool = True,
blendmode: BlendType = BlendType.NORMAL,
**kwargs: dict[str, Any],
) -> None:
"""Represent an image layer or group.
Args:
----
name (str): Name of the layer or group
dimensions ((int, int)): A tuple representing the dimensions in
pixels
offsets (tuple, optional): A tuple representing the left and top
offsets in pixels. Defaults to (0, 0).
opacity (float, optional): A float representing the alpha value
where 0 is invisible and 1 is fully visible. Defaults to 1.0.
visible (bool, optional): Is the layer visible to the user (this
is often configured per layer or per group by an 'eye' icon).
Defaults to True.
blendmode (Blendtype): The blending mode to use. Defaults to BlendType.NORMAL
**kwargs (Any): add any keyword args to self.extras
"""
self.name = name
self.offsets = offsets
self.opacity = opacity
self.visible = visible
self.dimensions = dimensions
self.blendmode = blendmode
self.extras = kwargs
def __repr__(self) -> str:
"""Get the string representation."""
return self.__str__()
def __str__(self) -> str:
"""Get the string representation."""
return f'<LayeredImage "{self.name}" ({self.dimensions[0]}x{self.dimensions[1]})>'
def json(self) -> dict[str, Any]:
"""Get the object as a dict."""
return {
"name": self.name,
"offsets": self.offsets,
"opacity": self.opacity,
"visible": self.visible,
"dimensions": self.dimensions,
"blendmode": self.blendmode.name,
}
class Layer(LayerGroup):
"""A representation of an image layer."""
def __init__(
self,
name: str,
image: Image.Image,
dimensions: tuple[int, int] | None = None,
offsets: tuple[int, int] = (0, 0),
opacity: float = 1.0,
*,
visible: bool = True,
blendmode: BlendType = BlendType.NORMAL,
) -> None:
"""Representation of an image layer.
Args:
----
name (str): Name of the layer or group
image (Image.Image): A PIL Image
dimensions (tuple[int, int]): A tuple representing the dimensions in
pixels
offsets (tuple[int, int], optional): A tuple representing the left and top
offsets in pixels. Defaults to (0, 0).
opacity (float, optional): A float representing the alpha value
where 0 is invisible and 1 is fully visible. Defaults to 1.0.
visible (bool, optional): Is the layer visible to the user (this
is often configured per layer or per group by an 'eye' icon).
Defaults to True.
blendmode (Blendtype): The blending mode to use. Defaults to BlendType.NORMAL
"""
super().__init__(
name, (0, 0), offsets=offsets, opacity=opacity, visible=visible, blendmode=blendmode
)
self.image = image
# If the user does not specify the dimensions use image.size
self.dimensions = dimensions or image.size
def json(self) -> dict[str, Any]:
"""Get the object as a dict."""
return {
"name": self.name,
"offsets": self.offsets,
"opacity": self.opacity,
"visible": self.visible,
"dimensions": self.dimensions,
"type": "LAYER",
"blendmode": self.blendmode.name,
}
class Group(LayerGroup):
"""A representation of an image group."""
def __init__(
self,
name: str,
layers: list[Layer],
dimensions: tuple[int, int] | None = None,
offsets: tuple[int, int] = (0, 0),
opacity: float = 1.0,
*,
visible: bool = True,
blendmode: BlendType = BlendType.NORMAL,
) -> None:
"""Representation of an image group.
Args:
----
name (str): Name of the layer or group
layers (layeredimage.Layer[]): A list of layers where the next
index stacks upon the previous layer
dimensions ((int, int)): A tuple representing the dimensions in
pixels
offsets (tuple, optional): A tuple representing the left and top
offsets in pixels. Defaults to (0, 0).
opacity (float, optional): A float representing the alpha value
where 0 is invisible and 1 is fully visible. Defaults to 1.0.
visible (bool, optional): Is the layer visible to the user (this
is often configured per layer or per group by an 'eye' icon).
Defaults to True.
blendmode (Blendtype): The blending mode to use. Defaults to BlendType.NORMAL
"""
# Initialise dimens to 0 and then calculate as below
super().__init__(
name, (0, 0), offsets=offsets, opacity=opacity, visible=visible, blendmode=blendmode
)
self.layers = layers
# If the user does not specify the dimensions use the largest x and y of
# the layers
self.dimensions = dimensions or (0, 0)
if dimensions is None:
self.dimensions = (
max(layer.dimensions[0] + layer.offsets[0] for layer in layers),
max(layer.dimensions[1] + layer.offsets[1] for layer in layers),
)
def json(self) -> dict[str, Any]:
"""Get the object as a dict."""
layers = [layer.json() for layer in self.layers]
return {
"name": self.name,
"offsets": self.offsets,
"opacity": self.opacity,
"visible": self.visible,
"dimensions": self.dimensions,
"type": "GROUP",
"blendmode": self.blendmode.name,
"layers": layers,
}