Skip to content

Commit 80b67b0

Browse files
refactor(rendering): movie clips rendering feature almost done (buggy for now)
1 parent 3e0dadc commit 80b67b0

File tree

14 files changed

+322
-95
lines changed

14 files changed

+322
-95
lines changed

system/lib/features/cut_sprites.py

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
from pathlib import Path
33

44
from system.lib.console import Console
5+
from system.lib.matrices import Matrix2x3
6+
from system.lib.objects.renderable.renderable_factory import (
7+
create_renderable_from_plain,
8+
)
59
from system.lib.swf import SupercellSWF
610
from system.localization import locale
711

@@ -12,26 +16,28 @@ def render_objects(swf: SupercellSWF, output_folder: Path):
1216
os.makedirs(output_folder / "movie_clips", exist_ok=True)
1317

1418
# TODO: Too slow, fix it
15-
# movie_clips_skipped = 0
16-
# movie_clip_count = len(swf.movie_clips)
17-
# for movie_clip_index in range(movie_clip_count):
18-
# movie_clip = swf.movie_clips[movie_clip_index]
19-
#
20-
# rendered_movie_clip = movie_clip.render(swf)
21-
# if sum(rendered_movie_clip.size) >= 2:
22-
# clip_name = movie_clip.export_name or movie_clip.id
23-
# rendered_movie_clip.save(f"{output_folder}/movie_clips/{clip_name}.png")
24-
# else:
25-
# # For debug:
26-
# # logger.warning(f'MovieClip {movie_clip.id} cannot be rendered.')
27-
# movie_clips_skipped += 1
28-
#
29-
# Console.progress_bar(
30-
# "Rendering movie clips (%d/%d). Skipped count: %d"
31-
# % (movie_clip_index + 1, movie_clip_count, movie_clips_skipped),
32-
# movie_clip_index,
33-
# movie_clip_count,
34-
# )
19+
movie_clips_skipped = 0
20+
movie_clip_count = len(swf.movie_clips)
21+
for movie_clip_index in range(movie_clip_count):
22+
movie_clip = swf.movie_clips[movie_clip_index]
23+
24+
rendered_movie_clip = create_renderable_from_plain(swf, movie_clip).render(
25+
Matrix2x3()
26+
)
27+
if sum(rendered_movie_clip.size) >= 2:
28+
clip_name = movie_clip.export_name or movie_clip.id
29+
rendered_movie_clip.save(f"{output_folder}/movie_clips/{clip_name}.png")
30+
else:
31+
# For debug:
32+
# logger.warning(f'MovieClip {movie_clip.id} cannot be rendered.')
33+
movie_clips_skipped += 1
34+
35+
Console.progress_bar(
36+
"Rendering movie clips (%d/%d). Skipped count: %d"
37+
% (movie_clip_index + 1, movie_clip_count, movie_clips_skipped),
38+
movie_clip_index,
39+
movie_clip_count,
40+
)
3541

3642
print()
3743

@@ -47,7 +53,7 @@ def render_objects(swf: SupercellSWF, output_folder: Path):
4753
shapes_count,
4854
)
4955

50-
rendered_shape = shape.render()
56+
rendered_shape = create_renderable_from_plain(swf, shape).render(Matrix2x3())
5157
rendered_shape.save(f"{output_folder}/shapes/{shape.id}.png")
5258

5359
regions_count = len(shape.regions)

system/lib/matrices/matrix2x3.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Self
2+
13
from system.bytestream import Reader
24

35
DEFAULT_MULTIPLIER = 1024
@@ -6,19 +8,27 @@
68

79
class Matrix2x3:
810
"""
9-
This matrix looks like:
11+
self matrix looks like:
1012
[a c x]
1113
[b d y]
1214
"""
1315

14-
def __init__(self):
16+
def __init__(self, matrix: Self | None = None):
1517
self.a: float = 1
1618
self.b: float = 0
1719
self.c: float = 0
1820
self.d: float = 1
1921
self.x: float = 0
2022
self.y: float = 0
2123

24+
if matrix is not None:
25+
self.a = matrix.a
26+
self.b = matrix.b
27+
self.c = matrix.c
28+
self.d = matrix.d
29+
self.x = matrix.x
30+
self.y = matrix.y
31+
2232
def load(self, reader: Reader, tag: int):
2333
divider: int
2434
if tag == 8:
@@ -40,3 +50,20 @@ def apply_x(self, x: float, y: float):
4050

4151
def apply_y(self, x: float, y: float):
4252
return y * self.d + x * self.b + self.y
53+
54+
def multiply(self, matrix: Self) -> Self:
55+
a = (self.a * matrix.a) + (self.b * matrix.c)
56+
b = (self.a * matrix.b) + (self.b * matrix.d)
57+
c = (self.d * matrix.d) + (self.c * matrix.b)
58+
d = (self.d * matrix.c) + (self.c * matrix.a)
59+
x = matrix.apply_x(self.x, self.y)
60+
y = matrix.apply_y(self.x, self.y)
61+
62+
self.a = a
63+
self.b = b
64+
self.d = c
65+
self.c = d
66+
self.x = x
67+
self.y = y
68+
69+
return self

system/lib/matrices/matrix_bank.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,20 @@
44

55
class MatrixBank:
66
def __init__(self):
7-
self.matrices: list[Matrix2x3] = []
8-
self.color_transforms: list[ColorTransform] = []
7+
self._matrices: list[Matrix2x3] = []
8+
self._color_transforms: list[ColorTransform] = []
99

1010
def init(self, matrix_count: int, color_transform_count: int):
11-
self.matrices = []
11+
self._matrices = []
1212
for i in range(matrix_count):
13-
self.matrices.append(Matrix2x3())
13+
self._matrices.append(Matrix2x3())
1414

15-
self.color_transforms = []
15+
self._color_transforms = []
1616
for i in range(color_transform_count):
17-
self.color_transforms.append(ColorTransform())
17+
self._color_transforms.append(ColorTransform())
1818

1919
def get_matrix(self, index: int) -> Matrix2x3:
20-
return self.matrices[index]
20+
return self._matrices[index]
2121

2222
def get_color_transform(self, index: int) -> ColorTransform:
23-
return self.color_transforms[index]
23+
return self._color_transforms[index]
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
__all__ = ["MovieClip", "MovieClipFrame"]
2+
3+
from system.lib.objects.movie_clip.movie_clip import MovieClip
4+
from system.lib.objects.movie_clip.movie_clip_frame import MovieClipFrame

system/lib/objects/movie_clip.py renamed to system/lib/objects/movie_clip/movie_clip.py

Lines changed: 5 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@
22

33
from typing import TYPE_CHECKING
44

5-
from PIL import Image
6-
7-
from system.bytestream import Reader
8-
from system.lib.matrices import Matrix2x3
5+
from ..plain_object import PlainObject
6+
from .movie_clip_frame import MovieClipFrame
97

108
if TYPE_CHECKING:
119
from system.lib.swf import SupercellSWF
@@ -14,38 +12,14 @@
1412
CACHE = {}
1513

1614

17-
class MovieClipFrame:
18-
def __init__(self):
19-
self._elements_count: int = 0
20-
self._label: str | None = None
21-
22-
self._elements: list[tuple[int, int, int]] = []
23-
24-
def load(self, reader: Reader) -> None:
25-
self._elements_count = reader.read_short()
26-
self._label = reader.read_string()
27-
28-
def get_elements_count(self) -> int:
29-
return self._elements_count
30-
31-
def set_elements(self, elements: list[tuple[int, int, int]]) -> None:
32-
self._elements = elements
33-
34-
def get_elements(self) -> list[tuple[int, int, int]]:
35-
return self._elements
36-
37-
def get_element(self, index: int) -> tuple[int, int, int]:
38-
return self._elements[index]
39-
40-
41-
class MovieClip:
15+
class MovieClip(PlainObject):
4216
def __init__(self):
4317
super().__init__()
4418

4519
self.id = -1
4620
self.export_name: str | None = None
4721
self.fps: int = 30
48-
self.frames_count: int = 0
22+
self.frame_count: int = 0
4923
self.frames: list[MovieClipFrame] = []
5024
self.frame_elements: list[tuple[int, int, int]] = []
5125
self.blends: list[int] = []
@@ -56,7 +30,7 @@ def load(self, swf: SupercellSWF, tag: int):
5630
self.id = swf.reader.read_ushort()
5731

5832
self.fps = swf.reader.read_char()
59-
self.frames_count = swf.reader.read_ushort()
33+
self.frame_count = swf.reader.read_ushort()
6034

6135
if tag in (3, 14):
6236
pass
@@ -113,6 +87,3 @@ def load(self, swf: SupercellSWF, tag: int):
11387
self.matrix_bank_index = swf.reader.read_uchar()
11488
else:
11589
swf.reader.read(frame_length)
116-
117-
def render(self, swf: SupercellSWF, matrix: Matrix2x3 | None = None) -> Image.Image:
118-
raise Exception("Not implemented yet")
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from system.bytestream import Reader
2+
3+
4+
class MovieClipFrame:
5+
def __init__(self):
6+
self._elements_count: int = 0
7+
self._label: str | None = None
8+
9+
self._elements: list[tuple[int, int, int]] = []
10+
11+
def load(self, reader: Reader) -> None:
12+
self._elements_count = reader.read_short()
13+
self._label = reader.read_string()
14+
15+
def get_elements_count(self) -> int:
16+
return self._elements_count
17+
18+
def set_elements(self, elements: list[tuple[int, int, int]]) -> None:
19+
self._elements = elements
20+
21+
def get_elements(self) -> list[tuple[int, int, int]]:
22+
return self._elements
23+
24+
def get_element(self, index: int) -> tuple[int, int, int]:
25+
return self._elements[index]

system/lib/objects/plain_object.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from __future__ import annotations
2+
3+
from abc import ABC, abstractmethod
4+
from typing import TYPE_CHECKING
5+
6+
if TYPE_CHECKING:
7+
from system.lib.swf import SupercellSWF
8+
9+
10+
class PlainObject(ABC):
11+
@abstractmethod
12+
def load(self, swf: SupercellSWF, tag: int):
13+
...

system/lib/objects/renderable/__init__.py

Whitespace-only changes.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from __future__ import annotations
2+
3+
from abc import ABC, abstractmethod
4+
5+
from PIL import Image
6+
7+
from system.lib.math.rect import Rect
8+
from system.lib.matrices import ColorTransform, Matrix2x3
9+
10+
11+
class DisplayObject(ABC):
12+
def __init__(self):
13+
self._matrix = Matrix2x3()
14+
self._color_transform = ColorTransform()
15+
16+
@abstractmethod
17+
def calculate_bounds(self, matrix: Matrix2x3 | None = None) -> Rect:
18+
...
19+
20+
@abstractmethod
21+
def render(self, matrix: Matrix2x3) -> Image.Image:
22+
...
23+
24+
def set_matrix(self, matrix: Matrix2x3):
25+
self._matrix = matrix
26+
27+
def set_color_transform(self, color_transform: ColorTransform):
28+
self._color_transform = color_transform
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from system.lib.objects import Shape
2+
from system.lib.objects.movie_clip.movie_clip import MovieClip
3+
from system.lib.objects.plain_object import PlainObject
4+
from system.lib.objects.renderable.display_object import DisplayObject
5+
from system.lib.objects.renderable.renderable_movie_clip import RenderableMovieClip
6+
from system.lib.objects.renderable.renderable_shape import RenderableShape
7+
from system.lib.swf import SupercellSWF
8+
9+
10+
def create_renderable_from_plain(
11+
swf: SupercellSWF, plain_object: PlainObject
12+
) -> DisplayObject:
13+
if isinstance(plain_object, Shape):
14+
return RenderableShape(plain_object)
15+
if isinstance(plain_object, MovieClip):
16+
children = []
17+
18+
for bind_id in plain_object.binds:
19+
bind_object = swf.get_display_object(bind_id)
20+
21+
display_object = None
22+
if bind_object is not None:
23+
display_object = create_renderable_from_plain(swf, bind_object)
24+
25+
children.append(display_object)
26+
27+
return RenderableMovieClip.create_from_plain(swf, plain_object, children)
28+
29+
raise Exception(f"Unsupported object type: {plain_object}")

0 commit comments

Comments
 (0)