|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
3 | | -from typing import TYPE_CHECKING |
| 3 | +from typing import TYPE_CHECKING, TypeAlias |
4 | 4 |
|
5 | 5 | from PIL import Image |
6 | 6 | import zstandard |
7 | 7 |
|
8 | 8 | 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 |
11 | 14 |
|
12 | 15 | if TYPE_CHECKING: |
13 | 16 | from xcoder.swf import SupercellSWF |
14 | 17 |
|
15 | 18 |
|
| 19 | +PixelType: TypeAlias = int |
| 20 | + |
| 21 | +_interleaved_tags = (27, 28, 29) |
| 22 | + |
| 23 | + |
16 | 24 | 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 |
20 | 30 |
|
21 | | - self.pixel_type = -1 |
| 31 | + self.pixel_type: PixelType = pixel_type |
22 | 32 |
|
23 | 33 | self.image: Image.Image | None = None |
24 | 34 |
|
25 | | - def load(self, swf: SupercellSWF, tag: int, has_texture: bool): |
| 35 | + def load(self, swf: SupercellSWF, tag: int, has_texture: bool) -> None: |
26 | 36 | assert swf.reader is not None |
27 | 37 |
|
28 | 38 | khronos_texture_length = 0 |
@@ -56,8 +66,44 @@ def load(self, swf: SupercellSWF, tag: int, has_texture: bool): |
56 | 66 |
|
57 | 67 | self.image = self._load_texture(swf.reader, tag) |
58 | 68 |
|
| 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 | + |
59 | 105 | def _load_texture(self, reader: Reader, tag: int) -> Image.Image: |
60 | | - if tag in (27, 28, 29): |
| 106 | + if tag in _interleaved_tags: |
61 | 107 | return join_image(self.pixel_type, self.width, self.height, reader) |
62 | 108 |
|
63 | 109 | return load_image_from_buffer(self.pixel_type, self.width, self.height, reader) |
0 commit comments