Skip to content

Commit ebbf1b9

Browse files
refactor(texture): texture loading accelerated
1 parent 0075af9 commit ebbf1b9

File tree

3 files changed

+58
-73
lines changed

3 files changed

+58
-73
lines changed

xcoder/console.py

+18-11
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
class Console:
2-
@staticmethod
3-
def progress_bar(message, current, total):
2+
previous_percentage: int = -1
3+
4+
@classmethod
5+
def progress_bar(cls, message: str, current: int, total: int) -> None:
46
percentage = (current + 1) * 100 // total
5-
print("\r", end="")
6-
print(f"[{percentage}%] {message}", end="")
7-
if current + 1 == total:
8-
print()
7+
if percentage == cls.previous_percentage:
8+
return
99

10-
@staticmethod
11-
def percent(current: int, total: int) -> int:
12-
return (current + 1) * 100 // total
10+
print(f"\r[{percentage}%] {message}", end="")
11+
12+
if percentage == 100:
13+
print()
14+
cls.previous_percentage = -1
15+
else:
16+
cls.previous_percentage = percentage
1317

1418
@staticmethod
1519
def ask_integer(message: str):
@@ -20,13 +24,16 @@ def ask_integer(message: str):
2024
pass
2125

2226
@staticmethod
23-
def question(message):
27+
def question(message: str) -> bool:
2428
while True:
2529
answer = input(f"[????] {message} [Y/n] ").lower()
30+
if not answer:
31+
return True
32+
2633
if answer in "ny":
2734
break
2835

29-
return "ny".index(answer)
36+
return bool("ny".index(answer))
3037

3138

3239
if __name__ == "__main__":

xcoder/images.py

+31-45
Original file line numberDiff line numberDiff line change
@@ -20,49 +20,45 @@
2020
CHUNK_SIZE = 32
2121

2222

23-
def load_image_from_buffer(img: Image.Image) -> None:
24-
width, height = img.size
25-
loaded_image = img.load()
26-
if loaded_image is None:
27-
raise Exception("loaded_image is None")
28-
23+
def load_image_from_buffer(pixel_type: int, width: int, height: int) -> Image.Image:
2924
with open("pixel_buffer", "rb") as pixel_buffer:
30-
channel_count = int.from_bytes(pixel_buffer.read(1), "little")
25+
pixel_buffer.read(1)
26+
27+
return Image.frombytes(
28+
get_format_by_pixel_type(pixel_type), (width, height), pixel_buffer.read()
29+
)
3130

32-
for y in range(height):
33-
for x in range(width):
34-
loaded_image[x, y] = tuple(pixel_buffer.read(channel_count))
3531

32+
def join_image(pixel_type: int, width: int, height: int) -> Image.Image:
33+
mode = get_format_by_pixel_type(pixel_type)
34+
image = Image.new(mode, (width, height))
3635

37-
def join_image(img: Image.Image) -> None:
3836
with open("pixel_buffer", "rb") as pixel_buffer:
3937
channel_count = int.from_bytes(pixel_buffer.read(1), "little")
4038

41-
width, height = img.size
42-
loaded_image = img.load()
43-
if loaded_image is None:
44-
raise Exception("loaded_image is None")
39+
chunk_count_x = math.ceil(width / CHUNK_SIZE)
40+
chunk_count_y = math.ceil(height / CHUNK_SIZE)
41+
chunk_count = chunk_count_x * chunk_count_y
4542

46-
x_chunks_count = width // CHUNK_SIZE
47-
y_chunks_count = height // CHUNK_SIZE
43+
for chunk_index in range(chunk_count):
44+
chunk_x = chunk_index % chunk_count_x
45+
chunk_y = chunk_index // chunk_count_x
4846

49-
for y_chunk in range(y_chunks_count + 1):
50-
for x_chunk in range(x_chunks_count + 1):
51-
for y in range(CHUNK_SIZE):
52-
pixel_y = y_chunk * CHUNK_SIZE + y
53-
if pixel_y >= height:
54-
break
47+
chunk_width = min(width - chunk_x * CHUNK_SIZE, CHUNK_SIZE)
48+
chunk_height = min(height - chunk_y * CHUNK_SIZE, CHUNK_SIZE)
49+
50+
sub_image = Image.frombuffer(
51+
mode,
52+
(chunk_width, chunk_height),
53+
pixel_buffer.read(channel_count * chunk_width * chunk_height),
54+
"raw",
55+
)
5556

56-
for x in range(CHUNK_SIZE):
57-
pixel_x = x_chunk * CHUNK_SIZE + x
58-
if pixel_x >= width:
59-
break
57+
image.paste(sub_image, (chunk_x * CHUNK_SIZE, chunk_y * CHUNK_SIZE))
6058

61-
loaded_image[pixel_x, pixel_y] = tuple(
62-
pixel_buffer.read(channel_count)
63-
)
59+
Console.progress_bar(locale.join_pic, chunk_index, chunk_count)
6460

65-
Console.progress_bar(locale.join_pic, y_chunk, y_chunks_count + 1)
61+
return image
6662

6763

6864
def _add_pixel(
@@ -130,7 +126,7 @@ def get_format_by_pixel_type(pixel_type: int) -> str:
130126
raise Exception(locale.unknown_pixel_type % pixel_type)
131127

132128

133-
def load_texture(reader: Reader, pixel_type: int, img: Image.Image) -> None:
129+
def load_texture(reader: Reader, pixel_type: int, width: int, height: int) -> None:
134130
channel_count = get_channel_count_by_pixel_type(pixel_type)
135131
read_pixel = get_read_function(pixel_type)
136132
if read_pixel is None:
@@ -139,18 +135,12 @@ def load_texture(reader: Reader, pixel_type: int, img: Image.Image) -> None:
139135
with open("pixel_buffer", "wb") as pixel_buffer:
140136
pixel_buffer.write(channel_count.to_bytes(1, "little"))
141137

142-
width, height = img.size
143-
point = -1
144138
for y in range(height):
145139
for x in range(width):
146140
pixel = read_pixel(reader)
147-
for channel in pixel:
148-
pixel_buffer.write(channel.to_bytes(1, "little"))
141+
pixel_buffer.write(bytearray(pixel))
149142

150-
curr = Console.percent(y, height)
151-
if curr > point:
152-
Console.progress_bar(locale.crt_pic, y, height)
153-
point = curr
143+
Console.progress_bar(locale.crt_pic, y, height)
154144

155145

156146
def save_texture(writer: Writer, image: Image.Image, pixel_type: int) -> None:
@@ -161,16 +151,12 @@ def save_texture(writer: Writer, image: Image.Image, pixel_type: int) -> None:
161151
width, height = image.size
162152

163153
pixels = image.getdata()
164-
point = -1
165154
for y in range(height):
166155
for x in range(width):
167156
# noinspection PyTypeChecker
168157
writer.write(encode_pixel(pixels[y * width + x]))
169158

170-
curr = Console.percent(y, height)
171-
if curr > point:
172-
Console.progress_bar(locale.writing_pic, y, height)
173-
point = curr
159+
Console.progress_bar(locale.writing_pic, y, height)
174160

175161

176162
def transform_image(

xcoder/objects/texture.py

+9-17
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,7 @@
66
import zstandard
77
from PIL import Image
88

9-
from xcoder.images import (
10-
get_format_by_pixel_type,
11-
join_image,
12-
load_image_from_buffer,
13-
load_texture,
14-
)
9+
from xcoder.images import join_image, load_image_from_buffer, load_texture
1510
from xcoder.pvr_tex_tool import get_image_from_ktx_data
1611

1712
if TYPE_CHECKING:
@@ -59,17 +54,14 @@ def load(self, swf: SupercellSWF, tag: int, has_texture: bool):
5954
)
6055
return
6156

62-
image = Image.new(
63-
get_format_by_pixel_type(self.pixel_type), (self.width, self.height)
64-
)
57+
try:
58+
load_texture(swf.reader, self.pixel_type, self.width, self.height)
6559

66-
load_texture(swf.reader, self.pixel_type, image)
67-
68-
if tag in (27, 28, 29):
69-
join_image(image)
70-
else:
71-
load_image_from_buffer(image)
72-
73-
os.remove("pixel_buffer")
60+
if tag in (27, 28, 29):
61+
image = join_image(self.pixel_type, self.width, self.height)
62+
else:
63+
image = load_image_from_buffer(self.pixel_type, self.width, self.height)
64+
finally:
65+
os.remove("pixel_buffer")
7466

7567
self.image = image

0 commit comments

Comments
 (0)