apftool 1.2.0__tar.gz → 1.2.1__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.
- {apftool-1.2.0 → apftool-1.2.1}/PKG-INFO +5 -3
- {apftool-1.2.0 → apftool-1.2.1}/README.md +4 -2
- {apftool-1.2.0 → apftool-1.2.1}/apftool/__init__.py +5 -4
- {apftool-1.2.0 → apftool-1.2.1}/apftool/apf2.py +68 -5
- {apftool-1.2.0 → apftool-1.2.1}/apftool/apfcli.py +22 -10
- apftool-1.2.1/apftool/asciipak.py +27 -0
- {apftool-1.2.0 → apftool-1.2.1}/apftool.egg-info/PKG-INFO +5 -3
- {apftool-1.2.0 → apftool-1.2.1}/apftool.egg-info/SOURCES.txt +1 -0
- {apftool-1.2.0 → apftool-1.2.1}/pyproject.toml +1 -1
- {apftool-1.2.0 → apftool-1.2.1}/LICENSE +0 -0
- {apftool-1.2.0 → apftool-1.2.1}/apftool/apf.py +0 -0
- {apftool-1.2.0 → apftool-1.2.1}/apftool/apf2quant.py +0 -0
- {apftool-1.2.0 → apftool-1.2.1}/apftool/bittools.py +0 -0
- {apftool-1.2.0 → apftool-1.2.1}/apftool/bruh.py +0 -0
- {apftool-1.2.0 → apftool-1.2.1}/apftool/mqif.py +0 -0
- {apftool-1.2.0 → apftool-1.2.1}/apftool/otb.py +0 -0
- {apftool-1.2.0 → apftool-1.2.1}/apftool/scqoi.py +0 -0
- {apftool-1.2.0 → apftool-1.2.1}/apftool/wbmp.py +0 -0
- {apftool-1.2.0 → apftool-1.2.1}/apftool.egg-info/dependency_links.txt +0 -0
- {apftool-1.2.0 → apftool-1.2.1}/apftool.egg-info/entry_points.txt +0 -0
- {apftool-1.2.0 → apftool-1.2.1}/apftool.egg-info/requires.txt +0 -0
- {apftool-1.2.0 → apftool-1.2.1}/apftool.egg-info/top_level.txt +0 -0
- {apftool-1.2.0 → apftool-1.2.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: apftool
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.1
|
|
4
4
|
Summary: Encoder and decoder for Aperture Science Image Format (apf), Aperture Science Image Format 2 (apf2), Wireless Bitmap (wbmp), Over-The-Air Bitmap (otb), and FaceDev's .bruh format
|
|
5
5
|
Author: Mari (kbity)
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -40,9 +40,9 @@ bruh is an intentionally terrible format made by facedev. info can be found [her
|
|
|
40
40
|
|
|
41
41
|
`apf.encode(img: bytes | Image.Image, lineskip: int = 1, findbestlineskip: bool = False, returnbytes: bool = False)` takes image bytes or pil image object and outputs apf string. lineskip is interleave value, findbestlineskip brute-forces different interleave values to the possible max of 199 and uses the smallest one. returnbytes makes it return bytes instead of a string.
|
|
42
42
|
|
|
43
|
-
`apf2.encode(img: bytes | Image.Image, lineskip: int =
|
|
43
|
+
`apf2.encode(img: bytes | Image.Image, lineskip: int = None, findbestlineskip: bool = False, legacy: bool = False, trans = False, pal: int = 95, desc: str = "", prepalette: str = None, dodithering: bool = False, returnbytes: bool = False, compress: bool = True, topside_first: bool = False, emit_redundant_flag: bool = False, mode: int = None, combine: bool = False, avoid_run_breaks: bool = False, width: int = None, height: int = None, maxrunerror: float = 0, maxmotionerror: float = 0, verbose: bool = False, useadvancedquant: bool = True, dither_mode: str = "fs", compressor: str = None, wrapper: bool = True, compresslevel: int = 9)`
|
|
44
44
|
|
|
45
|
-
takes image bytes or pil image object and outputs apf2 as a string or ascii bytes. lineskip is interleave value, findbestlineskip brute-forces different interleave values to the provided lineskip and uses the smallest one. legacy uses apf1-style 2 color data instead of a 95 color palette. trans sets transparency mode, with mode 0 being off, mode 1 being index 0 transparency (GIF-like, it overrides a color), and mode 2 being indexed alpha. pal allows you to manually force a smaller or larger palette anything over 95 will use APF2-1994's Dual Indexed Mode. Prepalette is an already encoded APF2 palette you input into the encoder to make the colors deterministic. dodithering enables or disables dithering. returnbytes makes it return bytes instead of a string. These options use the APF2-2000 mode: compress - disable RLE compression, allows for smaller file sizes where RLE is not beneficial. Topside_first makes the image scan top-to-bottom, like most formats. emit_redundant_flag - adds the implicit i flag explicitally to baseline 95 color images. mode - selects apf2 coding mode [0: 2 colors, 1: 95 colors, 2: 9025 colors, 3: near-truecolor, 4: near-truecolor with alpha, 5: truecolor, 6: truecolor with alpha, 7: quantized grayscale (based on 95 color mode), 8: 8-bit grayscale (based on 9025 color mode)], combine attempts to do cross-frame delta coding. avoid_run_breaks reduces the amount of transparency pixels emitted by combine, typically harms efficiency. width and height force the output resolution of the image. if both are specified, the output is streched to the specified resolution. if only 1 is present, the other is determined based on aspect ratio. If unspecfied, no scaling happens.
|
|
45
|
+
takes image bytes or pil image object and outputs apf2 as a string or ascii bytes. lineskip is interleave value, findbestlineskip brute-forces different interleave values to the provided lineskip and uses the smallest one. legacy uses apf1-style 2 color data instead of a 95 color palette. trans sets transparency mode, with mode 0 being off, mode 1 being index 0 transparency (GIF-like, it overrides a color), and mode 2 being indexed alpha. pal allows you to manually force a smaller or larger palette anything over 95 will use APF2-1994's Dual Indexed Mode. Prepalette is an already encoded APF2 palette you input into the encoder to make the colors deterministic. dodithering enables or disables dithering. returnbytes makes it return bytes instead of a string. These options use the APF2-2000 mode: compress - disable RLE compression, allows for smaller file sizes where RLE is not beneficial. Topside_first makes the image scan top-to-bottom, like most formats. emit_redundant_flag - adds the implicit i flag explicitally to baseline 95 color images. mode - selects apf2 coding mode [0: 2 colors, 1: 95 colors, 2: 9025 colors, 3: near-truecolor, 4: near-truecolor with alpha, 5: truecolor, 6: truecolor with alpha, 7: quantized grayscale (based on 95 color mode), 8: 8-bit grayscale (based on 9025 color mode)], combine attempts to do cross-frame delta coding. avoid_run_breaks reduces the amount of transparency pixels emitted by combine, typically harms efficiency. width and height force the output resolution of the image. if both are specified, the output is streched to the specified resolution. if only 1 is present, the other is determined based on aspect ratio. If unspecfied, no scaling happens. maxrunerror and maxmotionerror control how lossy the image is, verbose increases logging output, useadvancedquant allows you to enable/disable the new quantizer, dither_mode sets its ditherer, which can be "fs" (floyd-steinberg) or "ordered" (bayer), compressor sets the apf2 post-compression algorithm (supports gzip, bzip2, and asciipak (ascii packing)). wrapper toggles wether to append an identifyable magic string to a compressed apf2. compresslevel sets the compression level for bzip2 and gzip
|
|
46
46
|
|
|
47
47
|
`wbmp.encode(img: Image)` takes pil image object and outputs wbmp bytes
|
|
48
48
|
|
|
@@ -100,6 +100,8 @@ apftool provides many extensions tuples:
|
|
|
100
100
|
|
|
101
101
|
## changelog:
|
|
102
102
|
|
|
103
|
+
1.2.1 - add support for 3 of apf2's compressed modes [bzip2, gzip, ascii packing]
|
|
104
|
+
|
|
103
105
|
1.2.0 - increase apfcli verbosity, add lossy encoding options. Allow apfcli to take its own formats as inputs and outputs, also adds a much better quantization algorithm and removes the limitations on indexed animations.
|
|
104
106
|
|
|
105
107
|
1.1.3 - add version field, add width and height options to apf2 encoding
|
|
@@ -29,9 +29,9 @@ bruh is an intentionally terrible format made by facedev. info can be found [her
|
|
|
29
29
|
|
|
30
30
|
`apf.encode(img: bytes | Image.Image, lineskip: int = 1, findbestlineskip: bool = False, returnbytes: bool = False)` takes image bytes or pil image object and outputs apf string. lineskip is interleave value, findbestlineskip brute-forces different interleave values to the possible max of 199 and uses the smallest one. returnbytes makes it return bytes instead of a string.
|
|
31
31
|
|
|
32
|
-
`apf2.encode(img: bytes | Image.Image, lineskip: int =
|
|
32
|
+
`apf2.encode(img: bytes | Image.Image, lineskip: int = None, findbestlineskip: bool = False, legacy: bool = False, trans = False, pal: int = 95, desc: str = "", prepalette: str = None, dodithering: bool = False, returnbytes: bool = False, compress: bool = True, topside_first: bool = False, emit_redundant_flag: bool = False, mode: int = None, combine: bool = False, avoid_run_breaks: bool = False, width: int = None, height: int = None, maxrunerror: float = 0, maxmotionerror: float = 0, verbose: bool = False, useadvancedquant: bool = True, dither_mode: str = "fs", compressor: str = None, wrapper: bool = True, compresslevel: int = 9)`
|
|
33
33
|
|
|
34
|
-
takes image bytes or pil image object and outputs apf2 as a string or ascii bytes. lineskip is interleave value, findbestlineskip brute-forces different interleave values to the provided lineskip and uses the smallest one. legacy uses apf1-style 2 color data instead of a 95 color palette. trans sets transparency mode, with mode 0 being off, mode 1 being index 0 transparency (GIF-like, it overrides a color), and mode 2 being indexed alpha. pal allows you to manually force a smaller or larger palette anything over 95 will use APF2-1994's Dual Indexed Mode. Prepalette is an already encoded APF2 palette you input into the encoder to make the colors deterministic. dodithering enables or disables dithering. returnbytes makes it return bytes instead of a string. These options use the APF2-2000 mode: compress - disable RLE compression, allows for smaller file sizes where RLE is not beneficial. Topside_first makes the image scan top-to-bottom, like most formats. emit_redundant_flag - adds the implicit i flag explicitally to baseline 95 color images. mode - selects apf2 coding mode [0: 2 colors, 1: 95 colors, 2: 9025 colors, 3: near-truecolor, 4: near-truecolor with alpha, 5: truecolor, 6: truecolor with alpha, 7: quantized grayscale (based on 95 color mode), 8: 8-bit grayscale (based on 9025 color mode)], combine attempts to do cross-frame delta coding. avoid_run_breaks reduces the amount of transparency pixels emitted by combine, typically harms efficiency. width and height force the output resolution of the image. if both are specified, the output is streched to the specified resolution. if only 1 is present, the other is determined based on aspect ratio. If unspecfied, no scaling happens.
|
|
34
|
+
takes image bytes or pil image object and outputs apf2 as a string or ascii bytes. lineskip is interleave value, findbestlineskip brute-forces different interleave values to the provided lineskip and uses the smallest one. legacy uses apf1-style 2 color data instead of a 95 color palette. trans sets transparency mode, with mode 0 being off, mode 1 being index 0 transparency (GIF-like, it overrides a color), and mode 2 being indexed alpha. pal allows you to manually force a smaller or larger palette anything over 95 will use APF2-1994's Dual Indexed Mode. Prepalette is an already encoded APF2 palette you input into the encoder to make the colors deterministic. dodithering enables or disables dithering. returnbytes makes it return bytes instead of a string. These options use the APF2-2000 mode: compress - disable RLE compression, allows for smaller file sizes where RLE is not beneficial. Topside_first makes the image scan top-to-bottom, like most formats. emit_redundant_flag - adds the implicit i flag explicitally to baseline 95 color images. mode - selects apf2 coding mode [0: 2 colors, 1: 95 colors, 2: 9025 colors, 3: near-truecolor, 4: near-truecolor with alpha, 5: truecolor, 6: truecolor with alpha, 7: quantized grayscale (based on 95 color mode), 8: 8-bit grayscale (based on 9025 color mode)], combine attempts to do cross-frame delta coding. avoid_run_breaks reduces the amount of transparency pixels emitted by combine, typically harms efficiency. width and height force the output resolution of the image. if both are specified, the output is streched to the specified resolution. if only 1 is present, the other is determined based on aspect ratio. If unspecfied, no scaling happens. maxrunerror and maxmotionerror control how lossy the image is, verbose increases logging output, useadvancedquant allows you to enable/disable the new quantizer, dither_mode sets its ditherer, which can be "fs" (floyd-steinberg) or "ordered" (bayer), compressor sets the apf2 post-compression algorithm (supports gzip, bzip2, and asciipak (ascii packing)). wrapper toggles wether to append an identifyable magic string to a compressed apf2. compresslevel sets the compression level for bzip2 and gzip
|
|
35
35
|
|
|
36
36
|
`wbmp.encode(img: Image)` takes pil image object and outputs wbmp bytes
|
|
37
37
|
|
|
@@ -89,6 +89,8 @@ apftool provides many extensions tuples:
|
|
|
89
89
|
|
|
90
90
|
## changelog:
|
|
91
91
|
|
|
92
|
+
1.2.1 - add support for 3 of apf2's compressed modes [bzip2, gzip, ascii packing]
|
|
93
|
+
|
|
92
94
|
1.2.0 - increase apfcli verbosity, add lossy encoding options. Allow apfcli to take its own formats as inputs and outputs, also adds a much better quantization algorithm and removes the limitations on indexed animations.
|
|
93
95
|
|
|
94
96
|
1.1.3 - add version field, add width and height options to apf2 encoding
|
|
@@ -6,6 +6,7 @@ from .apfcli import main as cli
|
|
|
6
6
|
extensions = (".apf", ".apf2", ".aif", ".af2", ".ap2", ".aif2", ".a2k", ".ap2k")
|
|
7
7
|
extensions_apf = (".apf", ".aif")
|
|
8
8
|
extensions_apf2 = (".apf2", ".af2", ".ap2", ".aif2", ".a2k", ".ap2k")
|
|
9
|
+
extensions_capf2 = (".a2g", ".a2c", ".a2ci", ".apc", ".aq2", ".aqf", ".a2b")
|
|
9
10
|
extensions_wbmp = (".wbmp", ".wbitmap", ".wbm")
|
|
10
11
|
extensions_otb = (".otb", ".ota", ".otab")
|
|
11
12
|
extensions_bruh = (".bruh", ".brh")
|
|
@@ -13,10 +14,10 @@ extensions_mqif = (".mqif", ".mqi")
|
|
|
13
14
|
|
|
14
15
|
extensions_txt = (".txt", ".text") # txt is seen as a generic container for apf/apf2 because they're just txt files
|
|
15
16
|
|
|
16
|
-
version = "1.2.
|
|
17
|
+
version = "1.2.1"
|
|
17
18
|
|
|
18
19
|
extensions_all = []
|
|
19
|
-
extensions_all.extend(extensions_apf+extensions_apf2+extensions_wbmp+extensions_otb+extensions_bruh+extensions_mqif)
|
|
20
|
+
extensions_all.extend(extensions_apf+extensions_apf2+extensions_capf2+extensions_wbmp+extensions_otb+extensions_bruh+extensions_mqif)
|
|
20
21
|
|
|
21
22
|
def decode(data, format: str = None, returnImageObject: bool = False, provide_extra_data=True):
|
|
22
23
|
if isinstance(data, str):
|
|
@@ -25,7 +26,7 @@ def decode(data, format: str = None, returnImageObject: bool = False, provide_ex
|
|
|
25
26
|
return apf2.decode(data, format, returnImageObject) # apf and af2 decoder
|
|
26
27
|
|
|
27
28
|
elif isinstance(data, bytes):
|
|
28
|
-
if data.startswith(b'APERTURE IMAGE FORMAT (c) '): # this makes me GLaD
|
|
29
|
+
if data.startswith(b'APERTURE IMAGE FORMAT (c) ') or data.startswith(b'APF2') or data.startswith(b'BZh') or data.startswith(b'\x1f\x8b\x08') or data.startswith(b'\x83\x42\x2D\x2A\x95\x69\x45\x41'): # this makes me GLaD
|
|
29
30
|
if format is None:
|
|
30
31
|
format = "PNG"
|
|
31
32
|
return apf2.decode(data, format, returnImageObject, provide_extra_data=provide_extra_data)
|
|
@@ -60,7 +61,7 @@ def decode(data, format: str = None, returnImageObject: bool = False, provide_ex
|
|
|
60
61
|
return otb.decode(data, format, returnImageObject) # assume otb if it doesnt look like a wbmp or doesnt taste like a bruh
|
|
61
62
|
|
|
62
63
|
else:
|
|
63
|
-
Exception(f"decoding failed
|
|
64
|
+
Exception(f"decoding failed!. this likely means the format isnt supported by apftool.")
|
|
64
65
|
|
|
65
66
|
else:
|
|
66
67
|
raise Exception("Invalid data! Must be bytes or str")
|
|
@@ -2,6 +2,15 @@ from PIL import Image, ImageSequence
|
|
|
2
2
|
from collections import Counter
|
|
3
3
|
import io, textwrap, math
|
|
4
4
|
|
|
5
|
+
compressgood = False
|
|
6
|
+
|
|
7
|
+
from . import asciipak
|
|
8
|
+
try:
|
|
9
|
+
import gzip, bz2
|
|
10
|
+
compressgood = True
|
|
11
|
+
except Exception as e:
|
|
12
|
+
print(f"Importing gzip or bz2 failed, compression will be limited to ascii packing\n{e}")
|
|
13
|
+
|
|
5
14
|
qmf = "basic"
|
|
6
15
|
|
|
7
16
|
# try numpy and sklearn
|
|
@@ -390,7 +399,29 @@ def diff_images(base, overlay, magenter = False):
|
|
|
390
399
|
|
|
391
400
|
def decode(apf2: str | bytes, format: str = 'PNG', returnImageObject: bool = False, provide_extra_data: bool = False, composite_layers: bool = True):
|
|
392
401
|
if type(apf2) == bytes:
|
|
393
|
-
|
|
402
|
+
if apf2.startswith(b"APF2"):
|
|
403
|
+
compressed_blob = apf2[6:]
|
|
404
|
+
alg = apf2[4:6]
|
|
405
|
+
if alg == b'b2':
|
|
406
|
+
apf2 = bz2.decompress(compressed_blob)
|
|
407
|
+
elif alg == b'gz':
|
|
408
|
+
apf2 = gzip.decompress(compressed_blob)
|
|
409
|
+
elif alg == b'ci':
|
|
410
|
+
apf2 = asciipak.decompress(compressed_blob)
|
|
411
|
+
else:
|
|
412
|
+
raise Exception("Invalid/Unsupported Compression Algorithm")
|
|
413
|
+
apf2 = apf2.decode("ascii")
|
|
414
|
+
elif apf2.startswith(b'BZh'):
|
|
415
|
+
apf2 = bz2.decompress(apf2)
|
|
416
|
+
apf2 = apf2.decode("ascii")
|
|
417
|
+
elif apf2.startswith(b'\x1f\x8b\x08'):
|
|
418
|
+
apf2 = gzip.decompress(apf2)
|
|
419
|
+
apf2 = apf2.decode("ascii")
|
|
420
|
+
elif apf2.startswith(b'\x83\x42\x2D\x2A\x95\x69\x45\x41'):
|
|
421
|
+
apf2 = asciipak.decompress(apf2)
|
|
422
|
+
apf2 = apf2.decode("ascii")
|
|
423
|
+
else: # also add the check for unwrapped gzip/bzip2 apf2 here
|
|
424
|
+
apf2 = apf2.decode("ascii")
|
|
394
425
|
|
|
395
426
|
apf_list = apf2.splitlines()
|
|
396
427
|
apf_lines = []
|
|
@@ -1159,12 +1190,24 @@ def encode_wrapper(legacy, bitmaps, gray, truecolor, lineskip, w, h, topside_fir
|
|
|
1159
1190
|
|
|
1160
1191
|
return output, apf2pal
|
|
1161
1192
|
|
|
1162
|
-
def encode(img: bytes | Image.Image, lineskip: int = None, findbestlineskip: bool = False, legacy: bool = False, trans = False, pal: int = 95, desc: str = "", prepalette: str = None, dodithering: bool = False, returnbytes: bool = False, compress: bool = True, topside_first: bool = False, emit_redundant_flag: bool = False, mode: int = None, combine: bool = False, avoid_run_breaks: bool = False, width: int = None, height: int = None, maxrunerror: float = 0, maxmotionerror: float = 0, verbose: bool = False, useadvancedquant: bool = True, dither_mode: str = "fs"):
|
|
1193
|
+
def encode(img: bytes | Image.Image, lineskip: int = None, findbestlineskip: bool = False, legacy: bool = False, trans = False, pal: int = 95, desc: str = "", prepalette: str = None, dodithering: bool = False, returnbytes: bool = False, compress: bool = True, topside_first: bool = False, emit_redundant_flag: bool = False, mode: int = None, combine: bool = False, avoid_run_breaks: bool = False, width: int = None, height: int = None, maxrunerror: float = 0, maxmotionerror: float = 0, verbose: bool = False, useadvancedquant: bool = True, dither_mode: str = "fs", compressor: str = None, wrapper: bool = True, compresslevel: int = 9):
|
|
1163
1194
|
frametiming = None
|
|
1164
1195
|
bakedpal = None
|
|
1165
1196
|
dim = False
|
|
1166
1197
|
transmag = False
|
|
1167
1198
|
|
|
1199
|
+
if not compressor in (None, "none", "bzip2", "gzip", "asciipak", "squeeze"):
|
|
1200
|
+
raise Exception('Error: Invalid Compressor! Options: none, bzip2, gzip, asciipak, squeeze')
|
|
1201
|
+
|
|
1202
|
+
if compressor == "squeeze":
|
|
1203
|
+
raise Exception('Error: squeeze is not currently supported by apftool!')
|
|
1204
|
+
|
|
1205
|
+
if compressor in ("bzip2", "gzip") and not compressgood:
|
|
1206
|
+
raise Exception('Error: bzip2 and gzip is not currently supported by your python install!')
|
|
1207
|
+
|
|
1208
|
+
if compressor == "none":
|
|
1209
|
+
compressor = None # set it to the real nonetype
|
|
1210
|
+
|
|
1168
1211
|
trans = int(trans) # compatability filter
|
|
1169
1212
|
if trans > 2:
|
|
1170
1213
|
raise Exception("Error: Invalid Transparency Mode!")
|
|
@@ -1476,7 +1519,27 @@ def encode(img: bytes | Image.Image, lineskip: int = None, findbestlineskip: boo
|
|
|
1476
1519
|
print(f"APF2 Version: {version}")
|
|
1477
1520
|
print(f"APF2 Header Line: {apflist[1]}")
|
|
1478
1521
|
|
|
1479
|
-
if
|
|
1480
|
-
|
|
1522
|
+
if compressor is None:
|
|
1523
|
+
if returnbytes:
|
|
1524
|
+
return apftext.encode()
|
|
1525
|
+
else:
|
|
1526
|
+
return apftext
|
|
1481
1527
|
else:
|
|
1482
|
-
|
|
1528
|
+
apfbin = apftext.encode()
|
|
1529
|
+
# "none", "bzip2", "gzip", "asciipak", "squeeze"
|
|
1530
|
+
wraphead = b"APF2"
|
|
1531
|
+
if compressor == "bzip2":
|
|
1532
|
+
apfbin_c = bz2.compress(apfbin, compresslevel=compresslevel)
|
|
1533
|
+
wraphead+=b"b2"
|
|
1534
|
+
elif compressor == "gzip":
|
|
1535
|
+
apfbin_c = gzip.compress(apfbin, compresslevel=compresslevel)
|
|
1536
|
+
wraphead+=b"gz"
|
|
1537
|
+
elif compressor == "squeeze":
|
|
1538
|
+
apfbin_c = squeeze.compress(apfbin)
|
|
1539
|
+
wraphead+=b"sq"
|
|
1540
|
+
else:
|
|
1541
|
+
apfbin_c = asciipak.compress(apfbin)
|
|
1542
|
+
wraphead+=b"ci"
|
|
1543
|
+
if wrapper:
|
|
1544
|
+
apfbin_c = wraphead+apfbin_c
|
|
1545
|
+
return apfbin_c
|
|
@@ -36,6 +36,9 @@ encoding:
|
|
|
36
36
|
--quality=INT - (APF2) (0 to 100) sets both --run-quality and --motion-quality [Alias: -Q=INT]
|
|
37
37
|
--pillow - (APF2) use pillow's quantizer [Alias: -P]
|
|
38
38
|
--bayer - (APF2) use faster bayer/ordered dithering for perceptual mode instead of the slower, but prettier floyd-steinberg
|
|
39
|
+
--compressor=[bzip2, gzip, asciipak, none] - (APF2) set APF2 post-compressor
|
|
40
|
+
--compresslevel=[1-9] - (APF2) set compression level for apf2 compressor (gzip/bzip2) (default: 9)
|
|
41
|
+
--nowrap - (APF2) disable the Compressed APF2 wrapper on compressed APF2 Files
|
|
39
42
|
--mode=INT - (APF2) color mode (specified in A2K) for APF2. [Alias: -M=INT]
|
|
40
43
|
0 - 2 colors
|
|
41
44
|
1 - 95 colors
|
|
@@ -75,12 +78,12 @@ TODO: MQIF encoding
|
|
|
75
78
|
lineskip = None
|
|
76
79
|
dither = False
|
|
77
80
|
description = ""
|
|
78
|
-
if opext in apftool.
|
|
79
|
-
wid = None
|
|
80
|
-
hei = None
|
|
81
|
-
else:
|
|
81
|
+
if opext in apftool.extensions_otb:
|
|
82
82
|
wid = 255
|
|
83
83
|
hei = 255
|
|
84
|
+
else:
|
|
85
|
+
wid = None
|
|
86
|
+
hei = None
|
|
84
87
|
transcolor = None
|
|
85
88
|
flip = False
|
|
86
89
|
compress = True
|
|
@@ -91,6 +94,9 @@ TODO: MQIF encoding
|
|
|
91
94
|
maxrunerror = 0
|
|
92
95
|
useadvancedquant = True
|
|
93
96
|
dither_mode = "fs"
|
|
97
|
+
compressor = None
|
|
98
|
+
wrapper = True
|
|
99
|
+
compresslevel = 9
|
|
94
100
|
if args:
|
|
95
101
|
if "--findbestlineskip" in args:
|
|
96
102
|
fbls = True
|
|
@@ -114,6 +120,8 @@ TODO: MQIF encoding
|
|
|
114
120
|
useadvancedquant = False
|
|
115
121
|
if "--bayer" in args:
|
|
116
122
|
dither_mode = "ordered"
|
|
123
|
+
if "--nowrap" in args:
|
|
124
|
+
wrapper = False
|
|
117
125
|
for arg in args:
|
|
118
126
|
if arg.startswith("--desc=") or arg.startswith("-D="):
|
|
119
127
|
description = arg.replace("--desc=", "").replace("-D=", "")
|
|
@@ -131,10 +139,11 @@ TODO: MQIF encoding
|
|
|
131
139
|
if arg.startswith("--height=") or arg.startswith("-H="):
|
|
132
140
|
hei = int(arg.replace("--height=", "").replace("-H=", ""))
|
|
133
141
|
|
|
134
|
-
if arg.startswith("--mode="):
|
|
135
|
-
mode = int(arg.replace("--mode=", ""))
|
|
136
|
-
|
|
137
|
-
|
|
142
|
+
if arg.startswith("--mode=") or arg.startswith("-M="):
|
|
143
|
+
mode = int(arg.replace("--mode=", "").replace("-M=", ""))
|
|
144
|
+
|
|
145
|
+
if arg.startswith("--compresslevel="):
|
|
146
|
+
compresslevel = int(arg.replace("--compresslevel=", ""))
|
|
138
147
|
|
|
139
148
|
if arg.startswith("--quality=") or arg.startswith("-Q="):
|
|
140
149
|
qual = int(arg.replace("--quality=", "").replace("-Q=", ""))/2
|
|
@@ -156,6 +165,9 @@ TODO: MQIF encoding
|
|
|
156
165
|
tcb = int(transcolor_hex[4:6], 16)
|
|
157
166
|
transcolor = (tcr, tcg, tcb)
|
|
158
167
|
|
|
168
|
+
if arg.startswith("--compressor="):
|
|
169
|
+
compressor = arg.replace("--compressor=", "")
|
|
170
|
+
|
|
159
171
|
if mode is None:
|
|
160
172
|
if maxpalette > 9025 and 857375 >= maxpalette:
|
|
161
173
|
maxpalette = 95
|
|
@@ -203,8 +215,8 @@ TODO: MQIF encoding
|
|
|
203
215
|
elif opext in apftool.extensions_mqif:
|
|
204
216
|
encoded = apftool.mqif.encode(img_bytes, transcolor)
|
|
205
217
|
|
|
206
|
-
elif opext in apftool.extensions_apf2:
|
|
207
|
-
encoded = apftool.apf2.encode(img_bytes, lineskip=lineskip, findbestlineskip=fbls, legacy=legacy, trans=trans, pal=maxpalette, desc=description, prepalette= None, dodithering=dither, returnbytes=True, compress=compress, topside_first=flip, mode=mode, combine=combine, avoid_run_breaks=avoid_run_breaks, width=wid, height=hei, maxrunerror = maxrunerror, maxmotionerror = maxmotionerror, verbose = True, useadvancedquant=useadvancedquant, dither_mode=dither_mode)
|
|
218
|
+
elif opext in apftool.extensions_apf2 or opext in apftool.extensions_capf2:
|
|
219
|
+
encoded = apftool.apf2.encode(img_bytes, lineskip=lineskip, findbestlineskip=fbls, legacy=legacy, trans=trans, pal=maxpalette, desc=description, prepalette= None, dodithering=dither, returnbytes=True, compress=compress, topside_first=flip, mode=mode, combine=combine, avoid_run_breaks=avoid_run_breaks, width=wid, height=hei, maxrunerror = maxrunerror, maxmotionerror = maxmotionerror, verbose = True, useadvancedquant=useadvancedquant, dither_mode=dither_mode, compressor=compressor, wrapper=wrapper, compresslevel=compresslevel)
|
|
208
220
|
|
|
209
221
|
else:
|
|
210
222
|
raise ValueError("Unsupported Image Format!")
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from .bittools import byte_to_boollist, boollist_to_int_var
|
|
2
|
+
|
|
3
|
+
def compress(inpt: bytes):
|
|
4
|
+
buffr = []
|
|
5
|
+
output = bytearray()
|
|
6
|
+
for byte in inpt:
|
|
7
|
+
buffr.extend(byte_to_boollist(byte)[1:])
|
|
8
|
+
if len(buffr) >= 8:
|
|
9
|
+
output.append(boollist_to_int_var(buffr[0:8]))
|
|
10
|
+
buffr = buffr[8:]
|
|
11
|
+
if buffr:
|
|
12
|
+
while len(buffr) < 8:
|
|
13
|
+
buffr.append(False)
|
|
14
|
+
output.append(boollist_to_int_var(buffr))
|
|
15
|
+
return output
|
|
16
|
+
|
|
17
|
+
def decompress(inpt: bytes):
|
|
18
|
+
buffr = []
|
|
19
|
+
output = bytearray()
|
|
20
|
+
for byte in inpt:
|
|
21
|
+
buffr.extend(byte_to_boollist(byte))
|
|
22
|
+
while len(buffr) >= 8:
|
|
23
|
+
v = boollist_to_int_var([0]+buffr[0:7])
|
|
24
|
+
if v:
|
|
25
|
+
output.append(v)
|
|
26
|
+
buffr = buffr[7:]
|
|
27
|
+
return output
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: apftool
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.1
|
|
4
4
|
Summary: Encoder and decoder for Aperture Science Image Format (apf), Aperture Science Image Format 2 (apf2), Wireless Bitmap (wbmp), Over-The-Air Bitmap (otb), and FaceDev's .bruh format
|
|
5
5
|
Author: Mari (kbity)
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -40,9 +40,9 @@ bruh is an intentionally terrible format made by facedev. info can be found [her
|
|
|
40
40
|
|
|
41
41
|
`apf.encode(img: bytes | Image.Image, lineskip: int = 1, findbestlineskip: bool = False, returnbytes: bool = False)` takes image bytes or pil image object and outputs apf string. lineskip is interleave value, findbestlineskip brute-forces different interleave values to the possible max of 199 and uses the smallest one. returnbytes makes it return bytes instead of a string.
|
|
42
42
|
|
|
43
|
-
`apf2.encode(img: bytes | Image.Image, lineskip: int =
|
|
43
|
+
`apf2.encode(img: bytes | Image.Image, lineskip: int = None, findbestlineskip: bool = False, legacy: bool = False, trans = False, pal: int = 95, desc: str = "", prepalette: str = None, dodithering: bool = False, returnbytes: bool = False, compress: bool = True, topside_first: bool = False, emit_redundant_flag: bool = False, mode: int = None, combine: bool = False, avoid_run_breaks: bool = False, width: int = None, height: int = None, maxrunerror: float = 0, maxmotionerror: float = 0, verbose: bool = False, useadvancedquant: bool = True, dither_mode: str = "fs", compressor: str = None, wrapper: bool = True, compresslevel: int = 9)`
|
|
44
44
|
|
|
45
|
-
takes image bytes or pil image object and outputs apf2 as a string or ascii bytes. lineskip is interleave value, findbestlineskip brute-forces different interleave values to the provided lineskip and uses the smallest one. legacy uses apf1-style 2 color data instead of a 95 color palette. trans sets transparency mode, with mode 0 being off, mode 1 being index 0 transparency (GIF-like, it overrides a color), and mode 2 being indexed alpha. pal allows you to manually force a smaller or larger palette anything over 95 will use APF2-1994's Dual Indexed Mode. Prepalette is an already encoded APF2 palette you input into the encoder to make the colors deterministic. dodithering enables or disables dithering. returnbytes makes it return bytes instead of a string. These options use the APF2-2000 mode: compress - disable RLE compression, allows for smaller file sizes where RLE is not beneficial. Topside_first makes the image scan top-to-bottom, like most formats. emit_redundant_flag - adds the implicit i flag explicitally to baseline 95 color images. mode - selects apf2 coding mode [0: 2 colors, 1: 95 colors, 2: 9025 colors, 3: near-truecolor, 4: near-truecolor with alpha, 5: truecolor, 6: truecolor with alpha, 7: quantized grayscale (based on 95 color mode), 8: 8-bit grayscale (based on 9025 color mode)], combine attempts to do cross-frame delta coding. avoid_run_breaks reduces the amount of transparency pixels emitted by combine, typically harms efficiency. width and height force the output resolution of the image. if both are specified, the output is streched to the specified resolution. if only 1 is present, the other is determined based on aspect ratio. If unspecfied, no scaling happens.
|
|
45
|
+
takes image bytes or pil image object and outputs apf2 as a string or ascii bytes. lineskip is interleave value, findbestlineskip brute-forces different interleave values to the provided lineskip and uses the smallest one. legacy uses apf1-style 2 color data instead of a 95 color palette. trans sets transparency mode, with mode 0 being off, mode 1 being index 0 transparency (GIF-like, it overrides a color), and mode 2 being indexed alpha. pal allows you to manually force a smaller or larger palette anything over 95 will use APF2-1994's Dual Indexed Mode. Prepalette is an already encoded APF2 palette you input into the encoder to make the colors deterministic. dodithering enables or disables dithering. returnbytes makes it return bytes instead of a string. These options use the APF2-2000 mode: compress - disable RLE compression, allows for smaller file sizes where RLE is not beneficial. Topside_first makes the image scan top-to-bottom, like most formats. emit_redundant_flag - adds the implicit i flag explicitally to baseline 95 color images. mode - selects apf2 coding mode [0: 2 colors, 1: 95 colors, 2: 9025 colors, 3: near-truecolor, 4: near-truecolor with alpha, 5: truecolor, 6: truecolor with alpha, 7: quantized grayscale (based on 95 color mode), 8: 8-bit grayscale (based on 9025 color mode)], combine attempts to do cross-frame delta coding. avoid_run_breaks reduces the amount of transparency pixels emitted by combine, typically harms efficiency. width and height force the output resolution of the image. if both are specified, the output is streched to the specified resolution. if only 1 is present, the other is determined based on aspect ratio. If unspecfied, no scaling happens. maxrunerror and maxmotionerror control how lossy the image is, verbose increases logging output, useadvancedquant allows you to enable/disable the new quantizer, dither_mode sets its ditherer, which can be "fs" (floyd-steinberg) or "ordered" (bayer), compressor sets the apf2 post-compression algorithm (supports gzip, bzip2, and asciipak (ascii packing)). wrapper toggles wether to append an identifyable magic string to a compressed apf2. compresslevel sets the compression level for bzip2 and gzip
|
|
46
46
|
|
|
47
47
|
`wbmp.encode(img: Image)` takes pil image object and outputs wbmp bytes
|
|
48
48
|
|
|
@@ -100,6 +100,8 @@ apftool provides many extensions tuples:
|
|
|
100
100
|
|
|
101
101
|
## changelog:
|
|
102
102
|
|
|
103
|
+
1.2.1 - add support for 3 of apf2's compressed modes [bzip2, gzip, ascii packing]
|
|
104
|
+
|
|
103
105
|
1.2.0 - increase apfcli verbosity, add lossy encoding options. Allow apfcli to take its own formats as inputs and outputs, also adds a much better quantization algorithm and removes the limitations on indexed animations.
|
|
104
106
|
|
|
105
107
|
1.1.3 - add version field, add width and height options to apf2 encoding
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "apftool"
|
|
7
|
-
version = "1.2.
|
|
7
|
+
version = "1.2.1"
|
|
8
8
|
description = "Encoder and decoder for Aperture Science Image Format (apf), Aperture Science Image Format 2 (apf2), Wireless Bitmap (wbmp), Over-The-Air Bitmap (otb), and FaceDev's .bruh format"
|
|
9
9
|
authors = [{name = "Mari (kbity)"}]
|
|
10
10
|
readme = "README.md"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|