Skip to content

Commit 83fb038

Browse files
fix(swf texture): saving texture more properly, get rid of ktx tags
1 parent f70dc48 commit 83fb038

9 files changed

Lines changed: 110 additions & 82 deletions

File tree

src/xcoder/bytestream.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,28 +56,33 @@ def __init__(self, endian: Literal["little", "big"] = "little"):
5656
super().__init__()
5757
self._endian: Literal["little", "big"] = endian
5858

59-
def write_int(self, integer: int, length: int = 1, signed: bool = False):
59+
def write_tagged(self, tag: int, data: bytes) -> None:
60+
self.write_ubyte(tag)
61+
self.write_uint32(len(data))
62+
self.write(data)
63+
64+
def write_int(self, integer: int, length: int = 1, signed: bool = False) -> None:
6065
self.write(integer.to_bytes(length, self._endian, signed=signed))
6166

62-
def write_ubyte(self, integer: int):
67+
def write_ubyte(self, integer: int) -> None:
6368
self.write_int(integer)
6469

65-
def write_byte(self, integer: int):
70+
def write_byte(self, integer: int) -> None:
6671
self.write_int(integer, signed=True)
6772

68-
def write_uint16(self, integer: int):
73+
def write_uint16(self, integer: int) -> None:
6974
self.write_int(integer, 2)
7075

71-
def write_int16(self, integer: int):
76+
def write_int16(self, integer: int) -> None:
7277
self.write_int(integer, 2, True)
7378

74-
def write_uint32(self, integer: int):
79+
def write_uint32(self, integer: int) -> None:
7580
self.write_int(integer, 4)
7681

77-
def write_int32(self, integer: int):
82+
def write_int32(self, integer: int) -> None:
7883
self.write_int(integer, 4, True)
7984

80-
def write_string(self, string: str | None = None):
85+
def write_string(self, string: str | None) -> None:
8186
if string is None:
8287
self.write_byte(0xFF)
8388
return

src/xcoder/console.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
1+
from typing import ClassVar
2+
3+
14
class Console:
2-
previous_percentage: int = -1
5+
_previous_percentage: ClassVar[int] = -1
36

47
@classmethod
58
def progress_bar(cls, message: str, current: int, total: int) -> None:
69
percentage = (current + 1) * 100 // total
7-
if percentage == cls.previous_percentage:
10+
if percentage == cls._previous_percentage:
811
return
912

1013
print(f"\r[{percentage}%] {message}", end="")
1114

1215
if percentage == 100:
1316
print()
14-
cls.previous_percentage = -1
17+
cls._previous_percentage = -1
1518
else:
16-
cls.previous_percentage = percentage
19+
cls._previous_percentage = percentage
1720

1821
@staticmethod
1922
def ask_integer(message: str):

src/xcoder/features/place_sprites.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,11 @@ def place_sprites(
1616
file_info: FileInfo, folder: Path, overwrite: bool = False
1717
) -> list[Image.Image]:
1818
files_to_overwrite = os.listdir(folder / ("overwrite" if overwrite else ""))
19-
texture_files = os.listdir(folder / "textures")
20-
21-
sheets = []
22-
for i in range(len(file_info.sheets)):
23-
sheet_info = file_info.sheets[i]
2419

20+
sheets: list[Image.Image] = []
21+
for i, sheet_info in enumerate(file_info.sheets):
2522
sheets.append(
26-
Image.open(f"{folder}/textures/{texture_files[i]}")
23+
Image.open(f"{folder}/textures/{folder.name}_{i}.png")
2724
if overwrite
2825
else Image.new(
2926
get_format_by_pixel_type(sheet_info.pixel_type), sheet_info.size

src/xcoder/features/sc/__init__.py

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,68 @@
1+
from collections.abc import Sequence
12
from pathlib import Path
2-
import struct
33

44
from PIL import Image
55
from loguru import logger
66

77
from xcoder.bytestream import Writer
88
from xcoder.console import Console
99
from xcoder.features.files import write_sc
10-
from xcoder.images import get_byte_count_by_pixel_type, save_texture, split_image
1110
from xcoder.localization import locale
11+
from xcoder.objects import SWFTexture
1212
from xcoder.xcod import FileInfo
1313

1414

1515
def compile_sc(
1616
output_folder: Path,
1717
file_info: FileInfo,
18-
sheets: list[Image.Image],
18+
sheets: Sequence[Image.Image],
1919
):
20-
sc = Writer()
20+
writer = Writer()
2121

22-
for picture_index in range(len(sheets)):
23-
sheet_info = file_info.sheets[picture_index]
24-
sheet = sheets[picture_index]
22+
for i, image in enumerate(sheets):
23+
sheet_info = file_info.sheets[i]
2524

26-
file_type = sheet_info.file_type
25+
tag = sheet_info.file_type
2726
pixel_type = sheet_info.pixel_type
2827

29-
if sheet.size != sheet_info.size:
28+
if image.size != sheet_info.size:
3029
logger.info(
3130
locale.illegal_size
32-
% (sheet_info.width, sheet_info.height, sheet.width, sheet.height)
31+
% (sheet_info.width, sheet_info.height, image.width, image.height)
3332
)
3433

3534
if Console.question(locale.resize_qu):
3635
logger.info(locale.resizing)
37-
sheet = sheet.resize(sheet_info.size, Image.Resampling.LANCZOS)
36+
image = image.resize(sheet_info.size, Image.Resampling.LANCZOS)
3837

39-
width, height = sheet.size
40-
pixel_size = get_byte_count_by_pixel_type(pixel_type)
41-
42-
file_size = width * height * pixel_size + 5
38+
width, height = image.size
4339

4440
logger.info(
4541
locale.about_sc.format(
4642
filename=file_info.name,
47-
index=picture_index,
43+
index=i,
4844
pixel_type=pixel_type,
4945
width=width,
5046
height=height,
5147
)
5248
)
5349

54-
sc.write(struct.pack("<BIBHH", file_type, file_size, pixel_type, width, height))
50+
swf_texture = SWFTexture(width=width, height=height, pixel_type=pixel_type)
51+
swf_texture.image = image
52+
53+
texture_writer = Writer()
54+
swf_texture.save(writer, tag, True)
5555

56-
if file_type in (27, 28):
57-
sheet = split_image(sheet)
56+
writer.write_tagged(tag, texture_writer.getvalue())
5857

59-
save_texture(sc, sheet, pixel_type)
6058
print()
6159

62-
sc.write(bytes(5))
60+
writer.write_tagged(0, b"") # EOF tag
6361

6462
logger.info(locale.compressing_with % file_info.signature.name.upper())
6563
write_sc(
6664
output_folder / f"{file_info.name}.sc",
67-
sc.getvalue(),
65+
writer.getvalue(),
6866
file_info.signature,
6967
file_info.signature_version,
7068
)

src/xcoder/images.py

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66
if TYPE_CHECKING:
77
from PIL._imaging import PixelAccess
88

9-
from xcoder.bytestream import Reader, Writer
9+
from xcoder.bytestream import Reader
1010
from xcoder.console import Console
1111
from xcoder.localization import locale
1212
from xcoder.math.point import Point
1313
from xcoder.matrices import Matrix2x3
14-
from xcoder.pixel_utils import get_pixel_encode_function, get_raw_mode
14+
from xcoder.pixel_utils import get_raw_mode
1515

1616
CHUNK_SIZE = 32
1717

@@ -128,31 +128,6 @@ def get_format_by_pixel_type(pixel_type: int) -> str:
128128
raise Exception(locale.unknown_pixel_type % pixel_type)
129129

130130

131-
def save_texture(writer: Writer, image: Image.Image, pixel_type: int) -> None:
132-
raw_mode = get_raw_mode(pixel_type)
133-
encode_pixel = get_pixel_encode_function(pixel_type)
134-
135-
width, height = image.size
136-
137-
pixels = image.getdata()
138-
139-
# Some packers for raw_encoder are absent
140-
# https://github.com/python-pillow/Pillow/blob/58e48745cc7b6c6f7dd26a50fe68d1a82ea51562/src/encode.c#L337
141-
# https://github.com/python-pillow/Pillow/blob/main/src/libImaging/Pack.c#L668
142-
if raw_mode != image.mode and encode_pixel is not None:
143-
for y in range(height):
144-
for x in range(width):
145-
# noinspection PyTypeChecker
146-
writer.write(encode_pixel(pixels[y * width + x]))
147-
148-
Console.progress_bar(locale.writing_pic, y, height)
149-
150-
return
151-
152-
writer.write(image.tobytes("raw", raw_mode, 0, 1))
153-
Console.progress_bar(locale.writing_pic, height - 1, height)
154-
155-
156131
def transform_image(
157132
image: Image.Image, scale_x: float, scale_y: float, angle: float, x: float, y: float
158133
) -> Image.Image:

src/xcoder/matrices/matrix2x3.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,10 @@ def load(self, reader: Reader, tag: int):
4646
self.x = reader.read_twip()
4747
self.y = reader.read_twip()
4848

49-
def apply_x(self, x: float, y: float):
49+
def apply_x(self, x: float, y: float) -> float:
5050
return x * self.a + y * self.c + self.x
5151

52-
def apply_y(self, x: float, y: float):
52+
def apply_y(self, x: float, y: float) -> float:
5353
return y * self.d + x * self.b + self.y
5454

5555
def multiply(self, matrix: Self) -> Self:
@@ -86,5 +86,5 @@ def get_scale(self) -> tuple[float, float]:
8686

8787
return scale_x, scale_y
8888

89-
def __str__(self):
89+
def __str__(self) -> str:
9090
return f"Matrix2x3{self.a, self.b, self.c, self.d, self.x, self.y}"

src/xcoder/objects/texture.py

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,38 @@
11
from __future__ import annotations
22

3-
from typing import TYPE_CHECKING
3+
from typing import TYPE_CHECKING, TypeAlias
44

55
from PIL import Image
66
import zstandard
77

88
from ktx import get_image_from_ktx_data
9-
from xcoder.bytestream import Reader
10-
from xcoder.images import join_image, load_image_from_buffer
9+
from xcoder.bytestream import Reader, Writer
10+
from xcoder.console import Console
11+
from xcoder.images import join_image, load_image_from_buffer, split_image
12+
from xcoder.localization import locale
13+
from xcoder.pixel_utils import get_pixel_encode_function, get_raw_mode
1114

1215
if TYPE_CHECKING:
1316
from xcoder.swf import SupercellSWF
1417

1518

19+
PixelType: TypeAlias = int
20+
21+
_interleaved_tags = (27, 28, 29)
22+
23+
1624
class SWFTexture:
17-
def __init__(self):
18-
self.width = 0
19-
self.height = 0
25+
def __init__(
26+
self, *, width: int = 0, height: int = 0, pixel_type: PixelType = -1
27+
) -> None:
28+
self.width: int = width
29+
self.height: int = height
2030

21-
self.pixel_type = -1
31+
self.pixel_type: PixelType = pixel_type
2232

2333
self.image: Image.Image | None = None
2434

25-
def load(self, swf: SupercellSWF, tag: int, has_texture: bool):
35+
def load(self, swf: SupercellSWF, tag: int, has_texture: bool) -> None:
2636
assert swf.reader is not None
2737

2838
khronos_texture_length = 0
@@ -56,8 +66,44 @@ def load(self, swf: SupercellSWF, tag: int, has_texture: bool):
5666

5767
self.image = self._load_texture(swf.reader, tag)
5868

69+
def save(self, writer: Writer, tag: int, has_texture: bool) -> None:
70+
writer.write_ubyte(self.pixel_type)
71+
writer.write_uint16(self.width)
72+
writer.write_uint16(self.height)
73+
74+
if not has_texture:
75+
return
76+
77+
assert self.image is not None
78+
image = self.image
79+
80+
if tag in _interleaved_tags:
81+
image = split_image(self.image)
82+
83+
raw_mode = get_raw_mode(self.pixel_type)
84+
encode_pixel = get_pixel_encode_function(self.pixel_type)
85+
86+
width, height = image.size
87+
88+
pixels = image.getdata()
89+
90+
# Note: Some packers for raw_encoder are absent
91+
# https://github.com/python-pillow/Pillow/blob/58e48745cc7b6c6f7dd26a50fe68d1a82ea51562/src/encode.c#L337
92+
# https://github.com/python-pillow/Pillow/blob/main/src/libImaging/Pack.c#L668
93+
if raw_mode != image.mode and encode_pixel is not None:
94+
for y in range(height):
95+
for x in range(width):
96+
writer.write(encode_pixel(pixels[y * width + x])) # type: ignore
97+
98+
Console.progress_bar(locale.writing_pic, y, height)
99+
100+
return
101+
102+
writer.write(image.tobytes("raw", raw_mode, 0, 1))
103+
Console.progress_bar(locale.writing_pic, height - 1, height)
104+
59105
def _load_texture(self, reader: Reader, tag: int) -> Image.Image:
60-
if tag in (27, 28, 29):
106+
if tag in _interleaved_tags:
61107
return join_image(self.pixel_type, self.width, self.height, reader)
62108

63109
return load_image_from_buffer(self.pixel_type, self.width, self.height, reader)

src/xcoder/swf.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,10 @@ def _load_tags(self, is_texture_file: bool) -> bool:
172172
)
173173
)
174174

175+
# Note: get rid of ktx texture tags
176+
if tag in (45, 47):
177+
tag = 34
178+
175179
self.xcod_writer.write_ubyte(tag)
176180
self.xcod_writer.write_ubyte(texture.pixel_type)
177181
self.xcod_writer.write_uint16(texture.width)

src/xcoder/xcod.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def parse_base_info(file_info: FileInfo, reader: Reader) -> None:
7373
file_info.signature_version = 1 if reader.read_string() == "LZMA" else 3
7474

7575
sheets_count = reader.read_uchar()
76-
for i in range(sheets_count):
76+
for _i in range(sheets_count):
7777
file_type = reader.read_uchar()
7878
pixel_type = reader.read_uchar()
7979
width = reader.read_ushort()
@@ -84,13 +84,13 @@ def parse_base_info(file_info: FileInfo, reader: Reader) -> None:
8484

8585
def parse_detailed_info(file_info: FileInfo, reader: Reader) -> None:
8686
shapes_count = reader.read_ushort()
87-
for shape_index in range(shapes_count):
87+
for _shape_index in range(shapes_count):
8888
shape_id = reader.read_ushort()
8989

9090
regions = []
9191

9292
regions_count = reader.read_ushort()
93-
for region_index in range(regions_count):
93+
for _region_index in range(regions_count):
9494
texture_id, points_count = reader.read_uchar(), reader.read_uchar()
9595

9696
points = [

0 commit comments

Comments
 (0)