|
1 | 1 | # based on https://github.com/nesrak1/UABEA/blob/master/TexturePlugin/Texture2DSwitchDeswizzler.cs |
2 | 2 | from typing import Dict, Tuple |
3 | 3 |
|
4 | | -from PIL import Image |
5 | | - |
6 | 4 | from ..enums import TextureFormat |
7 | 5 |
|
8 | | -try: |
9 | | - from ..UnityPyBoost import switch_deswizzle as switch_deswizzle_c |
10 | | -except: |
11 | | - switch_deswizzle_c = None |
12 | | - |
13 | | - |
14 | | -GOB_X_BLOCK_COUNT = 4 |
15 | | -GOB_Y_BLOCK_COUNT = 8 |
16 | | -BLOCKS_IN_GOB = GOB_X_BLOCK_COUNT * GOB_Y_BLOCK_COUNT |
| 6 | +GOB_X_TEXEL_COUNT = 4 |
| 7 | +GOB_Y_TEXEL_COUNT = 8 |
| 8 | +TEXEL_BYTE_SIZE = 16 |
| 9 | +TEXELS_IN_GOB = GOB_X_TEXEL_COUNT * GOB_Y_TEXEL_COUNT |
| 10 | +GOB_MAP = [ |
| 11 | + (((l >> 3) & 0b10) | ((l >> 1) & 0b1), ((l >> 1) & 0b110) | (l & 0b1)) |
| 12 | + for l in range(TEXELS_IN_GOB) |
| 13 | +] |
17 | 14 |
|
18 | 15 |
|
19 | 16 | def ceil_divide(a: int, b: int) -> int: |
20 | | - return (a + b - 1) / b |
21 | | - |
22 | | - |
23 | | -def switch_deswizzle( |
24 | | - src_image: Image.Image, block_size: Tuple[int, int], gobs_per_block: int |
25 | | -) -> Image.Image: |
26 | | - src_bytes = src_image.tobytes() |
27 | | - pixel_width = len(src_image.mode) |
28 | | - |
29 | | - if switch_deswizzle_c: |
30 | | - dst_bytes = switch_deswizzle_c( |
31 | | - src_bytes, pixel_width, *src_image.size, *block_size, gobs_per_block |
32 | | - ) |
33 | | - return Image.frombytes(src_image.mode, src_image.size, dst_bytes) |
34 | | - |
35 | | - dst_bytes = bytearray(len(src_bytes)) |
36 | | - |
37 | | - width, height = src_image.size |
38 | | - block_width, block_height = block_size |
39 | | - block_count_x = width // block_width |
40 | | - block_count_y = height // block_height |
41 | | - |
42 | | - gob_count_x = block_count_x // GOB_X_BLOCK_COUNT |
43 | | - gob_count_y = block_count_y // GOB_Y_BLOCK_COUNT |
44 | | - |
45 | | - src_x = 0 |
46 | | - src_y = 0 |
47 | | - |
48 | | - block_pixel_width = block_width * pixel_width |
49 | | - row_pixel_width = width * pixel_width |
50 | | - data_size = len(src_bytes) |
51 | | - |
52 | | - GOB_MAP = [ |
53 | | - (((l >> 3) & 0b10) | ((l >> 1) & 0b1), ((l >> 1) & 0b110) | (l & 0b1)) |
54 | | - for l in range(BLOCKS_IN_GOB) |
55 | | - ] |
56 | | - |
57 | | - for y in range(gob_count_y): |
58 | | - for x in range(gob_count_x): |
59 | | - for k in range(gobs_per_block): |
| 17 | + return (a + b - 1) // b |
| 18 | + |
| 19 | + |
| 20 | +def deswizzle( |
| 21 | + data: bytes, |
| 22 | + width: int, |
| 23 | + height: int, |
| 24 | + block_width: int, |
| 25 | + block_height: int, |
| 26 | + texels_per_block: int, |
| 27 | +) -> bytearray: |
| 28 | + block_count_x = ceil_divide(width, block_width) |
| 29 | + block_count_y = ceil_divide(height, block_height) |
| 30 | + gob_count_x = block_count_x // GOB_X_TEXEL_COUNT |
| 31 | + gob_count_y = block_count_y // GOB_Y_TEXEL_COUNT |
| 32 | + new_data = bytearray(len(data)) |
| 33 | + data_view = memoryview(data) |
| 34 | + |
| 35 | + for i in range(gob_count_y // texels_per_block): |
| 36 | + for j in range(gob_count_x): |
| 37 | + base_gob_dst_x = j * 4 |
| 38 | + for k in range(texels_per_block): |
| 39 | + base_gob_dst_y = (i * texels_per_block + k) * GOB_Y_TEXEL_COUNT |
60 | 40 | for gob_x, gob_y in GOB_MAP: |
61 | | - gob_dst_x = x * GOB_X_BLOCK_COUNT + gob_x |
62 | | - gob_dst_y = (y * gobs_per_block + k) * GOB_Y_BLOCK_COUNT + gob_y |
63 | | - |
64 | | - src_offset = ( |
65 | | - src_x * block_width + (src_y * block_height) * width |
66 | | - ) * pixel_width |
67 | 41 | dst_offset = ( |
68 | | - gob_dst_x * block_width + (gob_dst_y * block_height) * width |
69 | | - ) * pixel_width |
70 | | - |
71 | | - for by in range(block_height): |
72 | | - if dst_offset > data_size or src_offset > data_size: |
73 | | - break |
74 | | - copy_width = block_pixel_width |
75 | | - if (data_size - src_offset) < block_pixel_width: |
76 | | - copy_width = data_size - src_offset |
77 | | - elif (data_size - dst_offset) < block_pixel_width: |
78 | | - copy_width = data_size - dst_offset |
79 | | - |
80 | | - dst_bytes[dst_offset : dst_offset + copy_width] = src_bytes[ |
81 | | - src_offset : src_offset + copy_width |
82 | | - ] |
83 | | - src_offset += row_pixel_width |
84 | | - dst_offset += row_pixel_width |
85 | | - |
86 | | - src_x += 1 |
87 | | - if src_x >= block_count_x: |
88 | | - src_x = 0 |
89 | | - src_y += 1 |
90 | | - |
91 | | - return Image.frombytes(src_image.mode, src_image.size, dst_bytes) |
92 | | - |
93 | | - |
94 | | -def switch_swizzle( |
95 | | - src_image: Image.Image, block_size: Tuple[int, int], gobs_per_block: int |
96 | | -) -> Image.Image: |
97 | | - dstImage = Image.new(src_image.mode, src_image.size) |
98 | | - |
99 | | - blockCountX, blockCountY = map(ceil_divide, zip(src_image.size, block_size)) |
100 | | - |
101 | | - gob_count_x = blockCountX / GOB_X_BLOCK_COUNT |
102 | | - gob_count_y = blockCountY / GOB_Y_BLOCK_COUNT |
103 | | - |
104 | | - dstX = 0 |
105 | | - dstY = 0 |
106 | | - |
107 | | - for i in range(gob_count_y): |
108 | | - for j in range(gob_count_x): |
109 | | - for k in range(gobs_per_block): |
110 | | - for l in range(BLOCKS_IN_GOB): |
111 | | - # todo: use table for speedy boi |
112 | | - gobX = ((l >> 3) & 0b10) | ((l >> 1) & 0b1) |
113 | | - gobY = ((l >> 1) & 0b110) | (l & 0b1) |
114 | | - gobSrcX = j * GOB_X_BLOCK_COUNT + gobX |
115 | | - gobSrcY = (i * gobs_per_block + k) * GOB_Y_BLOCK_COUNT + gobY |
116 | | - CopyBlock( |
117 | | - src_image, dstImage, gobSrcX, gobSrcY, dstX, dstY, *block_size |
118 | | - ) |
119 | | - |
120 | | - dstX += 1 |
121 | | - if dstX >= blockCountX: |
122 | | - dstX = 0 |
123 | | - dstY += 1 |
124 | | - |
125 | | - return dstImage |
| 42 | + (base_gob_dst_y + gob_y) * block_count_x |
| 43 | + + (base_gob_dst_x + gob_x) |
| 44 | + ) * TEXEL_BYTE_SIZE |
| 45 | + new_data[dst_offset : dst_offset + TEXEL_BYTE_SIZE] = data_view[ |
| 46 | + :TEXEL_BYTE_SIZE |
| 47 | + ] |
| 48 | + data_view = data_view[TEXEL_BYTE_SIZE:] |
| 49 | + return new_data |
| 50 | + |
| 51 | + |
| 52 | +# def switch_swizzle( |
| 53 | +# src_image: Image.Image, block_size: Tuple[int, int], texels_per_block: int |
| 54 | +# ) -> Image.Image: |
| 55 | +# dstImage = Image.new(src_image.mode, src_image.size) |
| 56 | + |
| 57 | +# blockCountX, blockCountY = map(ceil_divide, zip(src_image.size, block_size)) |
| 58 | + |
| 59 | +# gob_count_x = blockCountX / GOB_X_BLOCK_COUNT |
| 60 | +# gob_count_y = blockCountY / GOB_Y_BLOCK_COUNT |
| 61 | + |
| 62 | +# dstX = 0 |
| 63 | +# dstY = 0 |
| 64 | + |
| 65 | +# for i in range(gob_count_y): |
| 66 | +# for j in range(gob_count_x): |
| 67 | +# for k in range(texels_per_block): |
| 68 | +# for l in range(BLOCKS_IN_GOB): |
| 69 | +# # todo: use table for speedy boi |
| 70 | +# gobX = ((l >> 3) & 0b10) | ((l >> 1) & 0b1) |
| 71 | +# gobY = ((l >> 1) & 0b110) | (l & 0b1) |
| 72 | +# gobSrcX = j * GOB_X_BLOCK_COUNT + gobX |
| 73 | +# gobSrcY = (i * texels_per_block + k) * GOB_Y_BLOCK_COUNT + gobY |
| 74 | +# CopyBlock( |
| 75 | +# src_image, dstImage, gobSrcX, gobSrcY, dstX, dstY, *block_size |
| 76 | +# ) |
| 77 | + |
| 78 | +# dstX += 1 |
| 79 | +# if dstX >= blockCountX: |
| 80 | +# dstX = 0 |
| 81 | +# dstY += 1 |
| 82 | + |
| 83 | +# return dstImage |
126 | 84 |
|
127 | 85 |
|
128 | 86 | # this should be the amount of pixels that can fit 16 bytes |
129 | 87 | TEXTUREFORMAT_BLOCK_SIZE_MAP: Dict[TextureFormat, Tuple[int, int]] = { |
130 | | - TextureFormat.Alpha8: [16, 1], # 1 byte per pixel |
131 | | - TextureFormat.ARGB4444: [8, 1], # 2 bytes per pixel |
132 | | - TextureFormat.RGBA32: [4, 1], # 4 bytes per pixel |
133 | | - TextureFormat.ARGB32: [4, 1], # 4 bytes per pixel |
134 | | - TextureFormat.ARGBFloat: [1, 1], # 16 bytes per pixel (?) |
135 | | - TextureFormat.RGB565: [8, 1], # 2 bytes per pixel |
136 | | - TextureFormat.R16: [8, 1], # 2 bytes per pixel |
137 | | - TextureFormat.DXT1: [8, 4], # 8 bytes per 4x4=16 pixels |
138 | | - TextureFormat.DXT5: [4, 4], # 16 bytes per 4x4=16 pixels |
139 | | - TextureFormat.RGBA4444: [8, 1], # 2 bytes per pixel |
140 | | - TextureFormat.BGRA32: [4, 1], # 4 bytes per pixel |
141 | | - TextureFormat.BC6H: [4, 4], # 16 bytes per 4x4=16 pixels |
142 | | - TextureFormat.BC7: [4, 4], # 16 bytes per 4x4=16 pixels |
143 | | - TextureFormat.BC4: [8, 4], # 8 bytes per 4x4=16 pixels |
144 | | - TextureFormat.BC5: [4, 4], # 16 bytes per 4x4=16 pixels |
145 | | - TextureFormat.ASTC_RGB_4x4: [4, 4], # 16 bytes per 4x4=16 pixels |
146 | | - TextureFormat.ASTC_RGB_5x5: [5, 5], # 16 bytes per 5x5=25 pixels |
147 | | - TextureFormat.ASTC_RGB_6x6: [6, 6], # 16 bytes per 6x6=36 pixels |
148 | | - TextureFormat.ASTC_RGB_8x8: [8, 8], # 16 bytes per 8x8=64 pixels |
149 | | - TextureFormat.ASTC_RGB_10x10: [10, 10], # 16 bytes per 10x10=100 pixels |
150 | | - TextureFormat.ASTC_RGB_12x12: [12, 12], # 16 bytes per 12x12=144 pixels |
151 | | - TextureFormat.ASTC_RGBA_4x4: [4, 4], # 16 bytes per 4x4=16 pixels |
152 | | - TextureFormat.ASTC_RGBA_5x5: [5, 5], # 16 bytes per 5x5=25 pixels |
153 | | - TextureFormat.ASTC_RGBA_6x6: [6, 6], # 16 bytes per 6x6=36 pixels |
154 | | - TextureFormat.ASTC_RGBA_8x8: [8, 8], # 16 bytes per 8x8=64 pixels |
155 | | - TextureFormat.ASTC_RGBA_10x10: [10, 10], # 16 bytes per 10x10=100 pixels |
156 | | - TextureFormat.ASTC_RGBA_12x12: [12, 12], # 16 bytes per 12x12=144 pixels |
157 | | - TextureFormat.RG16: [8, 1], # 2 bytes per pixel |
158 | | - TextureFormat.R8: [16, 1], # 1 byte per pixel |
| 88 | + TextureFormat.Alpha8: (16, 1), # 1 byte per pixel |
| 89 | + TextureFormat.ARGB4444: (8, 1), # 2 bytes per pixel |
| 90 | + TextureFormat.RGBA32: (4, 1), # 4 bytes per pixel |
| 91 | + TextureFormat.ARGB32: (4, 1), # 4 bytes per pixel |
| 92 | + TextureFormat.ARGBFloat: (1, 1), # 16 bytes per pixel (?) |
| 93 | + TextureFormat.RGB565: (8, 1), # 2 bytes per pixel |
| 94 | + TextureFormat.R16: (8, 1), # 2 bytes per pixel |
| 95 | + TextureFormat.DXT1: (8, 4), # 8 bytes per 4x4=16 pixels |
| 96 | + TextureFormat.DXT5: (4, 4), # 16 bytes per 4x4=16 pixels |
| 97 | + TextureFormat.RGBA4444: (8, 1), # 2 bytes per pixel |
| 98 | + TextureFormat.BGRA32: (4, 1), # 4 bytes per pixel |
| 99 | + TextureFormat.BC6H: (4, 4), # 16 bytes per 4x4=16 pixels |
| 100 | + TextureFormat.BC7: (4, 4), # 16 bytes per 4x4=16 pixels |
| 101 | + TextureFormat.BC4: (8, 4), # 8 bytes per 4x4=16 pixels |
| 102 | + TextureFormat.BC5: (4, 4), # 16 bytes per 4x4=16 pixels |
| 103 | + TextureFormat.ASTC_RGB_4x4: (4, 4), # 16 bytes per 4x4=16 pixels |
| 104 | + TextureFormat.ASTC_RGB_5x5: (5, 5), # 16 bytes per 5x5=25 pixels |
| 105 | + TextureFormat.ASTC_RGB_6x6: (6, 6), # 16 bytes per 6x6=36 pixels |
| 106 | + TextureFormat.ASTC_RGB_8x8: (8, 8), # 16 bytes per 8x8=64 pixels |
| 107 | + TextureFormat.ASTC_RGB_10x10: (10, 10), # 16 bytes per 10x10=100 pixels |
| 108 | + TextureFormat.ASTC_RGB_12x12: (12, 12), # 16 bytes per 12x12=144 pixels |
| 109 | + TextureFormat.ASTC_RGBA_4x4: (4, 4), # 16 bytes per 4x4=16 pixels |
| 110 | + TextureFormat.ASTC_RGBA_5x5: (5, 5), # 16 bytes per 5x5=25 pixels |
| 111 | + TextureFormat.ASTC_RGBA_6x6: (6, 6), # 16 bytes per 6x6=36 pixels |
| 112 | + TextureFormat.ASTC_RGBA_8x8: (8, 8), # 16 bytes per 8x8=64 pixels |
| 113 | + TextureFormat.ASTC_RGBA_10x10: (10, 10), # 16 bytes per 10x10=100 pixels |
| 114 | + TextureFormat.ASTC_RGBA_12x12: (12, 12), # 16 bytes per 12x12=144 pixels |
| 115 | + TextureFormat.RG16: (8, 1), # 2 bytes per pixel |
| 116 | + TextureFormat.R8: (16, 1), # 1 byte per pixel |
159 | 117 | } |
160 | 118 |
|
161 | 119 |
|
162 | 120 | def get_padded_texture_size( |
163 | | - width: int, height: int, block_width: int, block_height: int, gobs_per_block: int |
| 121 | + width: int, height: int, block_width: int, block_height: int, texels_per_block: int |
164 | 122 | ): |
165 | 123 | width = ( |
166 | | - ceil_divide(width, block_width * GOB_X_BLOCK_COUNT) |
| 124 | + ceil_divide(width, block_width * GOB_X_TEXEL_COUNT) |
167 | 125 | * block_width |
168 | | - * GOB_X_BLOCK_COUNT |
| 126 | + * GOB_X_TEXEL_COUNT |
169 | 127 | ) |
170 | 128 | height = ( |
171 | | - ceil_divide(height, block_height * GOB_Y_BLOCK_COUNT * gobs_per_block) |
| 129 | + ceil_divide(height, block_height * GOB_Y_TEXEL_COUNT * texels_per_block) |
172 | 130 | * block_height |
173 | | - * GOB_Y_BLOCK_COUNT |
174 | | - * gobs_per_block |
| 131 | + * GOB_Y_TEXEL_COUNT |
| 132 | + * texels_per_block |
175 | 133 | ) |
176 | 134 | return width, height |
177 | 135 |
|
|
0 commit comments