ReverseBox 0.43.2__tar.gz → 0.45.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. {reversebox-0.43.2 → reversebox-0.45.0}/PKG-INFO +2 -1
  2. {reversebox-0.43.2 → reversebox-0.45.0}/README.md +1 -0
  3. {reversebox-0.43.2 → reversebox-0.45.0}/ReverseBox.egg-info/PKG-INFO +2 -1
  4. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/common.py +10 -0
  5. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/image_encoder.py +137 -57
  6. {reversebox-0.43.2 → reversebox-0.45.0}/setup.py +1 -1
  7. {reversebox-0.43.2 → reversebox-0.45.0}/LICENSE +0 -0
  8. {reversebox-0.43.2 → reversebox-0.45.0}/ReverseBox.egg-info/SOURCES.txt +0 -0
  9. {reversebox-0.43.2 → reversebox-0.45.0}/ReverseBox.egg-info/dependency_links.txt +0 -0
  10. {reversebox-0.43.2 → reversebox-0.45.0}/ReverseBox.egg-info/requires.txt +0 -0
  11. {reversebox-0.43.2 → reversebox-0.45.0}/ReverseBox.egg-info/top_level.txt +0 -0
  12. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/__init__.py +0 -0
  13. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/checksum/__init__.py +0 -0
  14. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/checksum/checksum_adler32.py +0 -0
  15. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/checksum/checksum_cocos2d_pvr.py +0 -0
  16. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/checksum/checksum_fletcher16.py +0 -0
  17. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/checksum/checksum_fletcher32.py +0 -0
  18. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/checksum/checksum_internet_ipv4_header.py +0 -0
  19. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/checksum/checksum_sum8.py +0 -0
  20. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/checksum/checksum_sum8_2s_complement.py +0 -0
  21. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/checksum/checksum_unix_sum_bsd16.py +0 -0
  22. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/checksum/checksum_unix_sum_sysv.py +0 -0
  23. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/checksum/checksum_xor8.py +0 -0
  24. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/common/__init__.py +0 -0
  25. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/common/common.py +0 -0
  26. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/common/logger.py +0 -0
  27. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/common/numpy_helper_functions.py +0 -0
  28. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/compression/__init__.py +0 -0
  29. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/compression/compression_jcalg1.py +0 -0
  30. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/compression/compression_lzo.py +0 -0
  31. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/compression/compression_mio0.py +0 -0
  32. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/compression/compression_re_tiyoruga_dat.py +0 -0
  33. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/compression/compression_refpack.py +0 -0
  34. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/compression/compression_zlib.py +0 -0
  35. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/crc/__init__.py +0 -0
  36. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/crc/crc16_arc.py +0 -0
  37. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/crc/crc16_ccitt.py +0 -0
  38. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/crc/crc16_dnp.py +0 -0
  39. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/crc/crc16_ea_crcf.py +0 -0
  40. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/crc/crc16_kermit.py +0 -0
  41. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/crc/crc16_modbus.py +0 -0
  42. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/crc/crc16_sick.py +0 -0
  43. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/crc/crc32_asobo.py +0 -0
  44. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/crc/crc32_iso_hdlc.py +0 -0
  45. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/crc/crc64_asobo.py +0 -0
  46. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/crc/crc64_go_iso.py +0 -0
  47. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/crc/crc8.py +0 -0
  48. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/crc/crc_unix_cksum.py +0 -0
  49. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/encryption/__init__.py +0 -0
  50. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/encryption/encryption_hatch_engine.py +0 -0
  51. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/encryption/encryption_rot13.py +0 -0
  52. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/encryption/encryption_xor_basic.py +0 -0
  53. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/encryption/encryption_xor_basic_key_guesser.py +0 -0
  54. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/encryption/encryption_xor_gianas_return_zda.py +0 -0
  55. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/encryption/encryption_xor_retro64_eco.py +0 -0
  56. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/hash/__init__.py +0 -0
  57. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/hash/hash_additive.py +0 -0
  58. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/hash/hash_ap.py +0 -0
  59. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/hash/hash_djb2.py +0 -0
  60. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/hash/hash_fnv.py +0 -0
  61. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/hash/hash_jenkins_one_at_a_time.py +0 -0
  62. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/hash/hash_md2.py +0 -0
  63. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/hash/hash_md5.py +0 -0
  64. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/hash/hash_murmur3.py +0 -0
  65. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/hash/hash_pivotal_games_dat.py +0 -0
  66. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/hash/hash_sdbm.py +0 -0
  67. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/hash/hash_sha1.py +0 -0
  68. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/hash/hash_sha2.py +0 -0
  69. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/__init__.py +0 -0
  70. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/byte_swap.py +0 -0
  71. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/compression/__init__.py +0 -0
  72. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/compression/compression_gst.py +0 -0
  73. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/compression/compression_lzw.py +0 -0
  74. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/compression/compression_packbits.py +0 -0
  75. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/compression/compression_rle_executioners.py +0 -0
  76. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/compression/compression_rle_tga.py +0 -0
  77. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/compression/compression_zlib.py +0 -0
  78. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/decoders/__init__.py +0 -0
  79. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/decoders/bumpmap_decoder.py +0 -0
  80. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/decoders/compressed_decoder_encoder.py +0 -0
  81. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/decoders/gst_decoder.py +0 -0
  82. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/decoders/n64_decoder.py +0 -0
  83. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/decoders/pvrtexlib_decoder_encoder.py +0 -0
  84. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/decoders/yuv_decoder.py +0 -0
  85. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/encoders/__init__.py +0 -0
  86. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/encoders/gst_encoder.py +0 -0
  87. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/file_formats/__init__.py +0 -0
  88. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/file_formats/image_dds.py +0 -0
  89. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/image_decoder.py +0 -0
  90. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/image_formats.py +0 -0
  91. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/palettes/__init__.py +0 -0
  92. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/palettes/palette_random.py +0 -0
  93. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/palettes/palette_vga.py +0 -0
  94. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/pillow_wrapper.py +0 -0
  95. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/swizzling/__init__.py +0 -0
  96. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/swizzling/swizzle_3ds.py +0 -0
  97. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/swizzling/swizzle_bc.py +0 -0
  98. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/swizzling/swizzle_cmpr.py +0 -0
  99. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/swizzling/swizzle_gamecube.py +0 -0
  100. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/swizzling/swizzle_gst.py +0 -0
  101. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/swizzling/swizzle_morton.py +0 -0
  102. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/swizzling/swizzle_morton_ps4.py +0 -0
  103. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/swizzling/swizzle_ps2.py +0 -0
  104. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/swizzling/swizzle_ps2_4bit.py +0 -0
  105. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/swizzling/swizzle_ps2_suba.py +0 -0
  106. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/swizzling/swizzle_psp.py +0 -0
  107. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/swizzling/swizzle_psvita_dreamcast.py +0 -0
  108. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/swizzling/swizzle_switch.py +0 -0
  109. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/swizzling/swizzle_wii_u.py +0 -0
  110. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/image/swizzling/swizzle_x360.py +0 -0
  111. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/io_files/__init__.py +0 -0
  112. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/io_files/bytes_handler.py +0 -0
  113. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/io_files/bytes_helper_functions.py +0 -0
  114. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/io_files/check_file.py +0 -0
  115. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/io_files/file_handler.py +0 -0
  116. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/io_files/mod_handler.py +0 -0
  117. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/io_files/translation_text_handler.py +0 -0
  118. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/libs/DirectXTex.dll +0 -0
  119. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/libs/PVRTexLib.dll +0 -0
  120. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/libs/PVRTexLibWrapper.dll +0 -0
  121. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/libs/__init__.py +0 -0
  122. {reversebox-0.43.2 → reversebox-0.45.0}/reversebox/libs/refpack.dll +0 -0
  123. {reversebox-0.43.2 → reversebox-0.45.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ReverseBox
3
- Version: 0.43.2
3
+ Version: 0.45.0
4
4
  Summary: A set of functions useful in reverse engineering.
5
5
  Home-page: https://github.com/bartlomiejduda/ReverseBox
6
6
  Author: Bartlomiej Duda
@@ -296,6 +296,7 @@ or software researchers).
296
296
  - RLE TGA compression ✔️
297
297
  - PackBits (Macintosh RLE) compression ✔️
298
298
  - Executioners RLE compression ✔️
299
+ - Generating mipmaps ✔️
299
300
 
300
301
  * IO
301
302
  - File Reader ✔️
@@ -259,6 +259,7 @@ or software researchers).
259
259
  - RLE TGA compression ✔️
260
260
  - PackBits (Macintosh RLE) compression ✔️
261
261
  - Executioners RLE compression ✔️
262
+ - Generating mipmaps ✔️
262
263
 
263
264
  * IO
264
265
  - File Reader ✔️
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ReverseBox
3
- Version: 0.43.2
3
+ Version: 0.45.0
4
4
  Summary: A set of functions useful in reverse engineering.
5
5
  Home-page: https://github.com/bartlomiejduda/ReverseBox
6
6
  Author: Bartlomiej Duda
@@ -296,6 +296,7 @@ or software researchers).
296
296
  - RLE TGA compression ✔️
297
297
  - PackBits (Macintosh RLE) compression ✔️
298
298
  - Executioners RLE compression ✔️
299
+ - Generating mipmaps ✔️
299
300
 
300
301
  * IO
301
302
  - File Reader ✔️
@@ -283,3 +283,13 @@ def unalign_data(aligned_bytes: bytes, img_width: int, img_height: int, align_va
283
283
  output_bytes[dst_offset: dst_offset + (orig_block_w * block_data_size)] = aligned_bytes[src_offset: src_offset + (orig_block_w * block_data_size)]
284
284
 
285
285
  return output_bytes
286
+
287
+
288
+ def get_linear_image_data_size(image_bpp: int, image_width: int, image_height: int) -> int:
289
+ if image_bpp == 4:
290
+ return (image_width * image_height) // 2
291
+ elif image_bpp < 8:
292
+ raise Exception("Not supported!")
293
+ else:
294
+ bytes_per_pixel: int = convert_bpp_to_bytes_per_pixel(image_bpp)
295
+ return image_width * image_height * bytes_per_pixel
@@ -11,6 +11,7 @@ from reversebox.common.logger import get_logger
11
11
  from reversebox.image.common import (
12
12
  convert_bpp_to_bytes_per_pixel,
13
13
  get_bpp_for_image_format,
14
+ get_linear_image_data_size,
14
15
  )
15
16
  from reversebox.image.decoders.compressed_decoder_encoder import (
16
17
  CompressedImageDecoderEncoder,
@@ -282,7 +283,7 @@ class ImageEncoder:
282
283
  raise Exception("Wrong endianess!")
283
284
  return endianess_format
284
285
 
285
- def _encode_generic(self, image_data: bytes, img_width: int, img_height: int, image_format: ImageFormats, image_endianess: str) -> bytes:
286
+ def _encode_generic_image(self, image_data: bytes, img_width: int, img_height: int, image_format: ImageFormats, image_endianess: str) -> bytes:
286
287
  encode_function, bits_per_pixel, _, _ = self.generic_data_formats[image_format]
287
288
  image_handler = BytesHandler(image_data)
288
289
  source_image_bytes_per_pixel = 4 # always 4 for RGBA8888
@@ -323,52 +324,121 @@ class ImageEncoder:
323
324
 
324
325
  return texture_data
325
326
 
326
- def _encode_indexed(self, image_data: bytes, img_width: int,
327
- img_height: int, image_format: ImageFormats, palette_format: ImageFormats, image_endianess: str, palette_endianess: str, max_colors_count: int) -> Tuple[bytes, bytes]:
328
- encode_function, palette_bpp, read_function, write_function = self.generic_data_formats[palette_format]
329
- image_bpp: int = get_bpp_for_image_format(image_format)
330
- palette_bpp: int = get_bpp_for_image_format(palette_format)
331
- palette_bytes_per_pixel: int = convert_bpp_to_bytes_per_pixel(palette_bpp)
332
- image_bytes_per_pixel: int = convert_bpp_to_bytes_per_pixel(image_bpp)
333
- image_endianess_format: str = self._get_endianess_format(image_endianess)
327
+ def _encode_generic(self, image_data: bytes, img_width: int, img_height: int, image_format: ImageFormats, image_endianess: str, number_of_mipmaps: int) -> bytes:
328
+ # main image logic
329
+ encoded_texture_data: bytes = self._encode_generic_image(image_data, img_width, img_height, image_format, image_endianess)
330
+
331
+ # mipmaps logic
332
+ if number_of_mipmaps > 0:
333
+ base_img: Image = PillowWrapper().get_pillow_image_from_rgba8888_data(
334
+ image_data, img_width, img_height
335
+ )
336
+ mip_width: int = img_width
337
+ mip_height: int = img_height
338
+ for i in range(number_of_mipmaps):
339
+ mip_width //= 2
340
+ mip_height //= 2
341
+ mip_pillow_img: Image = base_img.resize((mip_width, mip_height))
342
+ mip_rgba_data: bytes = PillowWrapper().get_image_data_from_pillow_image(mip_pillow_img)
343
+ encoded_mipmap_data: bytes = self._encode_generic_image(mip_rgba_data, mip_width, mip_height, image_format, image_endianess)
344
+ encoded_texture_data += encoded_mipmap_data
345
+
346
+ return encoded_texture_data
347
+
348
+ def _encode_indexed_get_intermediate_image(self, image_data: bytes, img_width: int, img_height: int,
349
+ palette_format: ImageFormats, image_endianess: str, max_colors_count: int) -> bytes:
334
350
 
335
- # prepare empty bytearray for output data
351
+ # colour quantization and dithering
352
+ pillow_img: Image = PillowWrapper().get_pillow_image_from_rgba8888_data(image_data, img_width, img_height)
353
+ unique_colors_count: int = len(set(pillow_img.getdata()))
354
+ if unique_colors_count > max_colors_count:
355
+ pillow_img = pillow_img.quantize(colors=max_colors_count, method=2).convert("RGBA", dither=Image.Dither.FLOYDSTEINBERG)
356
+ image_data = pillow_img.tobytes()
357
+
358
+ # encode to intermediate image (e.g. RGBA8888 -> RGB565)
359
+ return self._encode_generic(image_data, img_width, img_height, palette_format, image_endianess, 0)
360
+
361
+ def _encode_indexed_calculate_output_size(self, image_bpp: int, img_width: int, img_height: int) -> int:
336
362
  if image_bpp == 4:
337
- texture_data = bytearray(img_width * img_height // 2)
363
+ return img_width * img_height // 2
338
364
  elif image_bpp == 8:
339
- texture_data = bytearray(img_width * img_height)
365
+ return img_width * img_height
340
366
  elif image_bpp == 16:
341
- texture_data = bytearray(img_width * img_height * 2)
367
+ return img_width * img_height * 2
342
368
  elif image_bpp == 24:
343
- texture_data = bytearray(img_width * img_height * 3)
369
+ return img_width * img_height * 3
344
370
  elif image_bpp == 32:
345
- texture_data = bytearray(img_width * img_height * 4)
371
+ return img_width * img_height * 4
346
372
  else:
347
373
  raise Exception(f"[1] Image_bits_per_pixel={image_bpp} not supported!")
348
374
 
349
- texture_data_expected_size: int = len(texture_data)
375
+ def _encode_indexed_encode_indices(self, image_bpp: int, pixel_int_values: list[int], pixel_map: Dict[int, int], palette_endianess: str,
376
+ image_endianess_format: str, image_bytes_per_pixel: int, texture_data: bytes):
377
+ img_entry_number: int = 0
378
+ if image_bpp == 4: # PAL4
379
+ for i in range(0, len(pixel_int_values), 2):
380
+ pal_entry_number_1: int = pixel_map[pixel_int_values[i]]
381
+ pal_entry_number_2: int = pixel_map[pixel_int_values[i+1]]
382
+ if palette_endianess == "little":
383
+ pal_entry_combined: int = ((pal_entry_number_2 << 4) | pal_entry_number_1) & 0xFF
384
+ else:
385
+ pal_entry_combined: int = ((pal_entry_number_1 << 4) | pal_entry_number_2) & 0xFF
386
+ pixel_bytes: bytes = set_uint8(pal_entry_combined, image_endianess_format)
387
+ texture_data[img_entry_number * image_bytes_per_pixel: (img_entry_number + 1) * image_bytes_per_pixel] = pixel_bytes
388
+ img_entry_number += 1
350
389
 
351
- # colour quantization and dithering
352
- pillow_img: Image = PillowWrapper().get_pillow_image_from_rgba8888_data(image_data, img_width, img_height)
353
- unique_colors_count: int = len(set(pillow_img.getdata()))
354
- if unique_colors_count > max_colors_count:
355
- pillow_img = pillow_img.quantize(colors=max_colors_count, method=2).convert("RGBA", dither=Image.Dither.FLOYDSTEINBERG)
356
- image_data = pillow_img.tobytes()
390
+ elif image_bpp == 8: # PAL8
391
+ for pixel_int_value in pixel_int_values:
392
+ pal_entry_number: int = pixel_map[pixel_int_value]
393
+ pixel_bytes: bytes = set_uint8(pal_entry_number, image_endianess_format)
394
+ texture_data[img_entry_number * image_bytes_per_pixel: (img_entry_number + 1) * image_bytes_per_pixel] = pixel_bytes
395
+ img_entry_number += 1
357
396
 
358
- # encode to intermediate image (e.g. RGBA8888 -> RGB565)
359
- encoded_intermediate_image: bytes = self._encode_generic(image_data, img_width, img_height, palette_format, image_endianess)
397
+ else:
398
+ raise Exception(f"Not supported img_bpp={image_bpp}!") # TODO - support other formats like PAL16
360
399
 
361
- # get pixels from intermediate image
400
+ return texture_data
401
+
402
+ def _encode_indexed_get_pixel_int_values(self, image_bpp: int, encoded_intermediate_image: bytes, palette_bytes_per_pixel: int,
403
+ read_function, image_endianess_format: str) -> list[int]:
362
404
  pixel_int_values: list[int] = []
363
405
  if image_bpp in (4, 8):
364
406
  number_of_pixels: int = len(encoded_intermediate_image) // palette_bytes_per_pixel
365
407
  for i in range(number_of_pixels):
366
- pixel_bytes: bytes = encoded_intermediate_image[i * palette_bytes_per_pixel: (i + 1) * palette_bytes_per_pixel]
408
+ pixel_bytes: bytes = encoded_intermediate_image[
409
+ i * palette_bytes_per_pixel: (i + 1) * palette_bytes_per_pixel]
367
410
  pixel_int: int = read_function(pixel_bytes, image_endianess_format)
368
411
  pixel_int_values.append(pixel_int)
369
412
  else:
370
413
  raise Exception(f"Not supported img_bpp={image_bpp}!") # TODO - support other formats like PAL16
371
414
 
415
+ return pixel_int_values
416
+
417
+ def _encode_indexed(self, image_data: bytes, img_width: int,
418
+ img_height: int, image_format: ImageFormats, palette_format: ImageFormats, image_endianess: str, palette_endianess: str, max_colors_count: int, number_of_mipmaps: int) -> Tuple[bytes, bytes]:
419
+ encode_function, palette_bpp, read_function, write_function = self.generic_data_formats[palette_format]
420
+ image_bpp: int = get_bpp_for_image_format(image_format)
421
+ palette_bpp: int = get_bpp_for_image_format(palette_format)
422
+ palette_bytes_per_pixel: int = convert_bpp_to_bytes_per_pixel(palette_bpp)
423
+ image_bytes_per_pixel: int = convert_bpp_to_bytes_per_pixel(image_bpp)
424
+ image_endianess_format: str = self._get_endianess_format(image_endianess)
425
+ texture_data: bytes = b''
426
+
427
+ # prepare empty bytearray for output data
428
+ main_texture_data = bytearray(self._encode_indexed_calculate_output_size(image_bpp, img_width, img_height))
429
+ main_texture_data_expected_size: int = len(main_texture_data)
430
+
431
+ # get intermediate image
432
+ encoded_intermediate_image: bytes = self._encode_indexed_get_intermediate_image(image_data, img_width, img_height,
433
+ palette_format, image_endianess, max_colors_count)
434
+ expected_encoded_intermediate_image_size: int = get_linear_image_data_size(get_bpp_for_image_format(palette_format), img_width, img_height)
435
+ if len(encoded_intermediate_image) != expected_encoded_intermediate_image_size:
436
+ raise Exception("Error! Wrong intermediate image data size!")
437
+
438
+ # get pixels from intermediate image
439
+ pixel_int_values: list[int] = self._encode_indexed_get_pixel_int_values(image_bpp, encoded_intermediate_image, palette_bytes_per_pixel,
440
+ read_function, image_endianess_format)
441
+
372
442
  # get aligned number of colors
373
443
  if max_colors_count <= 4: # PAL2 (4-color)
374
444
  aligned_colors_count = 4
@@ -399,44 +469,54 @@ class ImageEncoder:
399
469
  palette_data[pal_entry_number * palette_bytes_per_pixel: (pal_entry_number + 1) * palette_bytes_per_pixel] = pal_entry_bytes
400
470
  pal_entry_number += 1
401
471
 
402
- # encode indices
403
- img_entry_number: int = 0
404
- if image_bpp == 4: # PAL4
405
- for i in range(0, len(pixel_int_values), 2):
406
- pal_entry_number_1: int = pixel_map[pixel_int_values[i]]
407
- pal_entry_number_2: int = pixel_map[pixel_int_values[i+1]]
408
- if palette_endianess == "little":
409
- pal_entry_combined: int = ((pal_entry_number_2 << 4) | pal_entry_number_1) & 0xFF
410
- else:
411
- pal_entry_combined: int = ((pal_entry_number_1 << 4) | pal_entry_number_2) & 0xFF
412
- pixel_bytes: bytes = set_uint8(pal_entry_combined, image_endianess_format)
413
- texture_data[img_entry_number * image_bytes_per_pixel: (img_entry_number + 1) * image_bytes_per_pixel] = pixel_bytes
414
- img_entry_number += 1
415
-
416
- elif image_bpp == 8: # PAL8
417
- for pixel_int_value in pixel_int_values:
418
- pal_entry_number: int = pixel_map[pixel_int_value]
419
- pixel_bytes: bytes = set_uint8(pal_entry_number, image_endianess_format)
420
- texture_data[img_entry_number * image_bytes_per_pixel: (img_entry_number + 1) * image_bytes_per_pixel] = pixel_bytes
421
- img_entry_number += 1
422
-
423
- else:
424
- raise Exception(f"Not supported img_bpp={image_bpp}!") # TODO - support other formats like PAL16
472
+ # encode indices for main texture
473
+ main_texture_data: bytes = self._encode_indexed_encode_indices(image_bpp, pixel_int_values, pixel_map, palette_endianess,
474
+ image_endianess_format, image_bytes_per_pixel, main_texture_data)
475
+ texture_data += main_texture_data
476
+
477
+ # mipmaps logic
478
+ if number_of_mipmaps > 0:
479
+ base_img: Image = PillowWrapper().get_pillow_image_from_rgba8888_data(image_data, img_width, img_height)
480
+ mip_width: int = img_width
481
+ mip_height: int = img_height
482
+ for i in range(number_of_mipmaps):
483
+ mip_width //= 2
484
+ mip_height //= 2
485
+ mip_pillow_img: Image = base_img.resize((mip_width, mip_height), resample=Image.Resampling.NEAREST)
486
+ mip_rgba_data: bytes = PillowWrapper().get_image_data_from_pillow_image(mip_pillow_img)
487
+ mipmap_intermediate_image: bytes = self._encode_indexed_get_intermediate_image(mip_rgba_data, mip_width,
488
+ mip_height, palette_format,
489
+ image_endianess, max_colors_count)
490
+ mipmap_pixel_int_values: list[int] = self._encode_indexed_get_pixel_int_values(image_bpp,
491
+ mipmap_intermediate_image,
492
+ palette_bytes_per_pixel,
493
+ read_function,
494
+ image_endianess_format)
495
+
496
+ mipmap_texture_data = bytearray(self._encode_indexed_calculate_output_size(image_bpp, mip_width, mip_height))
497
+ mipmap_texture_data_expected_size: int = len(mipmap_texture_data)
498
+ mipmap_texture_data: bytes = self._encode_indexed_encode_indices(image_bpp, mipmap_pixel_int_values, pixel_map, palette_endianess,
499
+ image_endianess_format, image_bytes_per_pixel, mipmap_texture_data)
500
+
501
+ if len(mipmap_texture_data) != mipmap_texture_data_expected_size:
502
+ raise Exception("Error! Wrong size of mipmap data!")
503
+
504
+ texture_data += mipmap_texture_data
425
505
 
426
506
  # final checks
427
- if len(palette_data) > palette_data_expected_size or len(palette_data) > 1024:
428
- raise Exception("Error! Palette data too big!")
507
+ if len(palette_data) != palette_data_expected_size or len(palette_data) > 1024:
508
+ raise Exception("Error! Wrong size of palette data!")
429
509
 
430
- if len(texture_data) > texture_data_expected_size:
431
- raise Exception("Error! Texture data too big!")
510
+ if len(main_texture_data) != main_texture_data_expected_size:
511
+ raise Exception("Error! Wrong size of main texture data!")
432
512
 
433
513
  return texture_data, palette_data
434
514
 
435
- def encode_image(self, image_data: bytes, img_width: int, img_height: int, image_format: ImageFormats, image_endianess: str = "little") -> bytes:
436
- return self._encode_generic(image_data, img_width, img_height, image_format, image_endianess)
515
+ def encode_image(self, image_data: bytes, img_width: int, img_height: int, image_format: ImageFormats, image_endianess: str = "little", number_of_mipmaps: int = 0) -> bytes:
516
+ return self._encode_generic(image_data, img_width, img_height, image_format, image_endianess, number_of_mipmaps)
437
517
 
438
- def encode_indexed_image(self, image_data: bytes, img_width: int, img_height: int, image_format: ImageFormats, palette_format: ImageFormats, max_color_count: int, image_endianess: str = "little", palette_endianess: str = "little") -> Tuple[bytes, bytes]:
439
- return self._encode_indexed(image_data, img_width, img_height, image_format, palette_format, image_endianess, palette_endianess, max_color_count)
518
+ def encode_indexed_image(self, image_data: bytes, img_width: int, img_height: int, image_format: ImageFormats, palette_format: ImageFormats, max_color_count: int, image_endianess: str = "little", palette_endianess: str = "little", number_of_mipmaps: int = 0) -> Tuple[bytes, bytes]:
519
+ return self._encode_indexed(image_data, img_width, img_height, image_format, palette_format, image_endianess, palette_endianess, max_color_count, number_of_mipmaps)
440
520
 
441
521
  def encode_compressed_image(self, image_data: bytes, img_width: int, img_height: int, image_format: ImageFormats) -> bytes:
442
522
  return CompressedImageDecoderEncoder().encode_compressed_image_main(image_data, img_width, img_height, image_format)
@@ -8,7 +8,7 @@ from typing import Final
8
8
 
9
9
  import setuptools
10
10
 
11
- VERSION_NUM: Final[str] = "0.43.2"
11
+ VERSION_NUM: Final[str] = "0.45.0"
12
12
 
13
13
 
14
14
  def get_long_description() -> str:
File without changes
File without changes