1
1
from __future__ import annotations
2
2
3
- from typing import TYPE_CHECKING
3
+ from typing import TYPE_CHECKING , Iterable , cast , Dict , Any , Union
4
4
5
5
from PIL import Image , ImageDraw
6
+ from PIL .Image import Transform , Transpose
6
7
8
+ from ..classes import SpriteAtlasData
7
9
from ..enums import (
8
10
ClassIDType ,
9
11
SpriteMeshType ,
@@ -42,21 +44,25 @@ def __init__(self, settings_raw):
42
44
def get_image (
43
45
sprite : Sprite , texture : PPtr [Texture2D ], alpha_texture : Optional [PPtr [Texture2D ]]
44
46
) -> Image .Image :
47
+ assert sprite .assets_file , "Sprite assets file is not set!"
48
+ cache = cast (
49
+ Dict [Any , Any ], sprite .assets_file ._cache
50
+ ) # TODO: edit in SerializibleFile
45
51
if alpha_texture :
46
52
cache_id = (texture .path_id , alpha_texture .path_id )
47
- if cache_id not in sprite . assets_file . _cache :
53
+ if cache_id not in cache :
48
54
original_image = get_image_from_texture2d (texture .read (), False )
49
55
alpha_image = get_image_from_texture2d (alpha_texture .read (), False )
50
56
original_image = Image .merge (
51
57
"RGBA" , (* original_image .split ()[:3 ], alpha_image .split ()[0 ])
52
58
)
53
- sprite . assets_file . _cache [cache_id ] = original_image
59
+ cache [cache_id ] = original_image
54
60
else :
55
61
cache_id = texture .path_id
56
- if cache_id not in sprite . assets_file . _cache :
62
+ if cache_id not in cache :
57
63
original_image = get_image_from_texture2d (texture .read (), False )
58
- sprite . assets_file . _cache [cache_id ] = original_image
59
- return sprite . assets_file . _cache [cache_id ]
64
+ cache [cache_id ] = original_image
65
+ return cache [cache_id ]
60
66
61
67
62
68
def get_image_from_sprite (m_Sprite : Sprite ) -> Image .Image :
@@ -65,6 +71,7 @@ def get_image_from_sprite(m_Sprite: Sprite) -> Image.Image:
65
71
atlas = m_Sprite .m_SpriteAtlas .read ()
66
72
elif m_Sprite .m_AtlasTags :
67
73
# looks like the direct pointer is empty, let's try to find the Atlas via its name
74
+ assert m_Sprite .assets_file , "Sprite assets file is not set!"
68
75
for obj in m_Sprite .assets_file .objects .values ():
69
76
if obj .type == ClassIDType .SpriteAtlas :
70
77
atlas = obj .read ()
@@ -78,6 +85,9 @@ def get_image_from_sprite(m_Sprite: Sprite) -> Image.Image:
78
85
for key , value in atlas .m_RenderDataMap
79
86
if key == m_Sprite .m_RenderDataKey
80
87
)
88
+ assert isinstance (sprite_atlas_data , SpriteAtlasData ), (
89
+ "SpriteAtlasData not found!"
90
+ )
81
91
else :
82
92
sprite_atlas_data = m_Sprite .m_RD
83
93
@@ -88,41 +98,44 @@ def get_image_from_sprite(m_Sprite: Sprite) -> Image.Image:
88
98
89
99
original_image = get_image (m_Sprite , m_Texture2D , alpha_texture )
90
100
91
- sprite_image = original_image .crop ((
92
- texture_rect .x ,
93
- texture_rect .y ,
94
- texture_rect .x + texture_rect .width ,
95
- texture_rect .y + texture_rect .height ,
96
- ))
101
+ sprite_image = original_image .crop (
102
+ (
103
+ texture_rect .x ,
104
+ texture_rect .y ,
105
+ texture_rect .x + texture_rect .width ,
106
+ texture_rect .y + texture_rect .height ,
107
+ )
108
+ )
97
109
98
110
settings_raw = SpriteSettings (settings_raw )
99
111
if settings_raw .packed == 1 :
100
112
rotation = settings_raw .packingRotation
101
113
if rotation == SpritePackingRotation .kSPRFlipHorizontal :
102
- sprite_image = sprite_image .transpose (Image .FLIP_LEFT_RIGHT )
114
+ sprite_image = sprite_image .transpose (Transpose .FLIP_LEFT_RIGHT )
103
115
# spriteImage = RotateFlip(RotateFlipType.RotateNoneFlipX)
104
116
elif rotation == SpritePackingRotation .kSPRFlipVertical :
105
- sprite_image = sprite_image .transpose (Image .FLIP_TOP_BOTTOM )
117
+ sprite_image = sprite_image .transpose (Transpose .FLIP_TOP_BOTTOM )
106
118
# spriteImage.RotateFlip(RotateFlipType.RotateNoneFlipY)
107
119
elif rotation == SpritePackingRotation .kSPRRotate180 :
108
- sprite_image = sprite_image .transpose (Image .ROTATE_180 )
120
+ sprite_image = sprite_image .transpose (Transpose .ROTATE_180 )
109
121
# spriteImage.RotateFlip(RotateFlipType.Rotate180FlipNone)
110
122
elif rotation == SpritePackingRotation .kSPRRotate90 :
111
- sprite_image = sprite_image .transpose (Image .ROTATE_270 )
123
+ sprite_image = sprite_image .transpose (Transpose .ROTATE_270 )
112
124
# spriteImage.RotateFlip(RotateFlipType.Rotate270FlipNone)
113
125
114
126
if settings_raw .packingMode == SpritePackingMode .kSPMTight :
127
+ assert m_Sprite .object_reader , "Sprite object reader is not set!"
115
128
mesh = MeshHandler (m_Sprite .m_RD , m_Sprite .object_reader .version )
116
129
mesh .process ()
117
130
118
- if any (u or v for u , v in mesh .m_UV0 ):
131
+ if mesh . m_UV0 and any (u or v for u , v in mesh .m_UV0 ):
119
132
# copy triangles from mesh
120
133
sprite_image = render_sprite_mesh (m_Sprite , mesh , original_image )
121
134
else :
122
135
# create mask to keep only the polygon
123
136
sprite_image = mask_sprite (m_Sprite , mesh , sprite_image )
124
137
125
- return sprite_image .transpose (Image .FLIP_TOP_BOTTOM )
138
+ return sprite_image .transpose (Transpose .FLIP_TOP_BOTTOM )
126
139
127
140
128
141
def mask_sprite (
@@ -135,6 +148,9 @@ def mask_sprite(
135
148
# shift the whole point matrix into the positive space
136
149
# multiply them with a factor to scale them to the image
137
150
positions = mesh .m_Vertices
151
+ assert positions , "No vertices found in sprite mesh!"
152
+ # find the axis that has only one value - can be removed
153
+ # usually the z axis
138
154
min_x = min (x for x , _y , _z in positions )
139
155
min_y = min (y for _x , y , _z in positions )
140
156
factor = m_Sprite .m_PixelsToUnits
@@ -174,7 +190,11 @@ def render_sprite_mesh(
174
190
) -> Image .Image :
175
191
for triangles in mesh .get_triangles ():
176
192
positions = mesh .m_Vertices
193
+ if not positions :
194
+ continue
177
195
uv = mesh .m_UV0
196
+ if not uv :
197
+ raise ValueError ("No UV coordinates found in sprite mesh!" )
178
198
179
199
# 2. patch position data
180
200
# 2.1 make positions 2d
@@ -212,19 +232,21 @@ def render_sprite_mesh(
212
232
for tri in triangles :
213
233
copy_triangle (
214
234
texture ,
215
- [ uv_abs [i ] for i in tri ],
235
+ tuple ( uv_abs [i ] for i in tri ), # type: ignore
216
236
sprite ,
217
- [ positions_abs [i ] for i in tri ],
237
+ tuple ( positions_abs [i ] for i in tri ), # type: ignore
218
238
)
219
239
220
240
return sprite
241
+ else :
242
+ raise ValueError ("No triangles found in mesh!" )
221
243
222
244
223
245
def copy_triangle (
224
246
src_img : Image .Image ,
225
- src_tri : Tuple [Tuple [float , float ], Tuple [float , float ], Tuple [float , float ]],
247
+ src_tri : Tuple [Tuple [int , int ], Tuple [int , int ], Tuple [int , int ]],
226
248
dst_img : Image .Image ,
227
- dst_tri : Tuple [Tuple [float , float ], Tuple [float , float ], Tuple [float , float ]],
249
+ dst_tri : Tuple [Tuple [int , int ], Tuple [int , int ], Tuple [int , int ]],
228
250
) -> None :
229
251
src_off = (
230
252
(src_tri [1 ][0 ] - src_tri [0 ][0 ], src_tri [1 ][1 ] - src_tri [0 ][1 ]),
@@ -286,9 +308,9 @@ def copy_triangle(
286
308
A = np .linalg .solve (M , y )
287
309
else :
288
310
# np.lingal.solve - obviously way faster, but numpy will only come with 2.0
289
- A = linalg_solve (M , y )
311
+ A = linalg_solve (M , y ) # type: ignore
290
312
291
- transformed = src_img .transform (dst_img .size , Image .AFFINE , A )
313
+ transformed = src_img .transform (dst_img .size , Transform .AFFINE , A )
292
314
293
315
mask = Image .new ("1" , dst_img .size )
294
316
maskdraw = ImageDraw .Draw (mask )
@@ -297,18 +319,20 @@ def copy_triangle(
297
319
dst_img .paste (transformed , mask = mask )
298
320
299
321
300
- def linalg_solve (M : List [List [float ]], y : List [float ]) -> List [float ]:
322
+ def linalg_solve (
323
+ M : List [List [Union [float , int ]]], y : List [Union [float , int ]]
324
+ ) -> List [float ]:
301
325
# M^-1 * y
302
326
M_i = get_matrix_inverse (M )
303
327
return [sum (M_i [i ][j ] * y [j ] for j in range (len (y ))) for i in range (len (M_i ))]
304
328
305
329
306
- def transpose_matrix (m : List [List [float ]]) -> List [List [float ]]:
330
+ def transpose_matrix (m : List [List [float ]]) -> Iterable [List [float ]]:
307
331
# https://stackoverflow.com/a/39881366
308
332
return map (list , zip (* m ))
309
333
310
334
311
- def get_matrix_minor (m : List [List [float ]], i : int , j : int ) -> List [float ]:
335
+ def get_matrix_minor (m : List [List [float ]], i : int , j : int ) -> List [List [ float ] ]:
312
336
# https://stackoverflow.com/a/39881366
313
337
return [row [:j ] + row [j + 1 :] for row in (m [:i ] + m [i + 1 :])]
314
338
0 commit comments