apftool 1.2.1__tar.gz → 1.2.2__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.1 → apftool-1.2.2}/PKG-INFO +4 -1
- {apftool-1.2.1 → apftool-1.2.2}/README.md +2 -0
- {apftool-1.2.1 → apftool-1.2.2}/apftool/__init__.py +2 -2
- {apftool-1.2.1 → apftool-1.2.2}/apftool/apf2.py +33 -33
- {apftool-1.2.1 → apftool-1.2.2}/apftool/apf2quant.py +7 -4
- {apftool-1.2.1 → apftool-1.2.2}/apftool.egg-info/PKG-INFO +4 -1
- apftool-1.2.2/apftool.egg-info/requires.txt +2 -0
- {apftool-1.2.1 → apftool-1.2.2}/pyproject.toml +2 -2
- apftool-1.2.1/apftool.egg-info/requires.txt +0 -1
- {apftool-1.2.1 → apftool-1.2.2}/LICENSE +0 -0
- {apftool-1.2.1 → apftool-1.2.2}/apftool/apf.py +0 -0
- {apftool-1.2.1 → apftool-1.2.2}/apftool/apfcli.py +0 -0
- {apftool-1.2.1 → apftool-1.2.2}/apftool/asciipak.py +0 -0
- {apftool-1.2.1 → apftool-1.2.2}/apftool/bittools.py +0 -0
- {apftool-1.2.1 → apftool-1.2.2}/apftool/bruh.py +0 -0
- {apftool-1.2.1 → apftool-1.2.2}/apftool/mqif.py +0 -0
- {apftool-1.2.1 → apftool-1.2.2}/apftool/otb.py +0 -0
- {apftool-1.2.1 → apftool-1.2.2}/apftool/scqoi.py +0 -0
- {apftool-1.2.1 → apftool-1.2.2}/apftool/wbmp.py +0 -0
- {apftool-1.2.1 → apftool-1.2.2}/apftool.egg-info/SOURCES.txt +0 -0
- {apftool-1.2.1 → apftool-1.2.2}/apftool.egg-info/dependency_links.txt +0 -0
- {apftool-1.2.1 → apftool-1.2.2}/apftool.egg-info/entry_points.txt +0 -0
- {apftool-1.2.1 → apftool-1.2.2}/apftool.egg-info/top_level.txt +0 -0
- {apftool-1.2.1 → apftool-1.2.2}/setup.cfg +0 -0
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: apftool
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.2
|
|
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
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
8
|
License-File: LICENSE
|
|
9
9
|
Requires-Dist: Pillow
|
|
10
|
+
Requires-Dist: psqueeze
|
|
10
11
|
Dynamic: license-file
|
|
11
12
|
|
|
12
13
|
# A library for converting to and from Aperture Science apf files, the custom extended apf2s, otb/wbmp images, and facedev's bruh format.
|
|
@@ -100,6 +101,8 @@ apftool provides many extensions tuples:
|
|
|
100
101
|
|
|
101
102
|
## changelog:
|
|
102
103
|
|
|
104
|
+
1.2.2 - add support for squeeze, improve handling for numpy/scipy not being installed. Signifigant performance improvement for Truecolor modes.
|
|
105
|
+
|
|
103
106
|
1.2.1 - add support for 3 of apf2's compressed modes [bzip2, gzip, ascii packing]
|
|
104
107
|
|
|
105
108
|
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.
|
|
@@ -89,6 +89,8 @@ apftool provides many extensions tuples:
|
|
|
89
89
|
|
|
90
90
|
## changelog:
|
|
91
91
|
|
|
92
|
+
1.2.2 - add support for squeeze, improve handling for numpy/scipy not being installed. Signifigant performance improvement for Truecolor modes.
|
|
93
|
+
|
|
92
94
|
1.2.1 - add support for 3 of apf2's compressed modes [bzip2, gzip, ascii packing]
|
|
93
95
|
|
|
94
96
|
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.
|
|
@@ -14,7 +14,7 @@ extensions_mqif = (".mqif", ".mqi")
|
|
|
14
14
|
|
|
15
15
|
extensions_txt = (".txt", ".text") # txt is seen as a generic container for apf/apf2 because they're just txt files
|
|
16
16
|
|
|
17
|
-
version = "1.2.
|
|
17
|
+
version = "1.2.2"
|
|
18
18
|
|
|
19
19
|
extensions_all = []
|
|
20
20
|
extensions_all.extend(extensions_apf+extensions_apf2+extensions_capf2+extensions_wbmp+extensions_otb+extensions_bruh+extensions_mqif)
|
|
@@ -26,7 +26,7 @@ def decode(data, format: str = None, returnImageObject: bool = False, provide_ex
|
|
|
26
26
|
return apf2.decode(data, format, returnImageObject) # apf and af2 decoder
|
|
27
27
|
|
|
28
28
|
elif isinstance(data, bytes):
|
|
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
|
+
if data.startswith(b'APERTURE IMAGE FORMAT (c) ') or data.startswith(b'APF2') or data.startswith(b'BZh') or data.startswith(b'v\xff') or data.startswith(b'\x1f\x8b\x08') or data.startswith(b'\x83\x42\x2D\x2A\x95\x69\x45\x41'): # this makes me GLaD
|
|
30
30
|
if format is None:
|
|
31
31
|
format = "PNG"
|
|
32
32
|
return apf2.decode(data, format, returnImageObject, provide_extra_data=provide_extra_data)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from PIL import Image, ImageSequence
|
|
2
2
|
from collections import Counter
|
|
3
|
-
import io, textwrap, math
|
|
3
|
+
import io, textwrap, math, squeeze
|
|
4
4
|
|
|
5
5
|
compressgood = False
|
|
6
6
|
|
|
@@ -34,6 +34,10 @@ def quant_to_base95(g: int):
|
|
|
34
34
|
mapping = []
|
|
35
35
|
for i in range(256):
|
|
36
36
|
mapping.append(chr(quant_to_base95(i)+32))
|
|
37
|
+
mapping_invert = {}
|
|
38
|
+
for i, c in enumerate(mapping):
|
|
39
|
+
if not c in mapping_invert:
|
|
40
|
+
mapping_invert[c] = i
|
|
37
41
|
|
|
38
42
|
# slow pure python version
|
|
39
43
|
def quantize_most_frequent_basic(img: Image.Image, n_colors: int, palette: list = None, dither: bool = False, dither_mode: str = None): # for use with A and P>256 images, dithering unsupported for basic mode
|
|
@@ -81,20 +85,11 @@ else:
|
|
|
81
85
|
def split_datapoints(data: str, datlen: int):
|
|
82
86
|
leng = len(data)
|
|
83
87
|
|
|
84
|
-
|
|
85
|
-
if not leng%datlen == 0:
|
|
88
|
+
if leng % datlen:
|
|
86
89
|
print("Error: Data must be divisible by the apf2 segment length! Attempting decode anyways...")
|
|
87
|
-
|
|
90
|
+
leng -= leng % datlen
|
|
88
91
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
for n in range(0, leng, datlen):
|
|
92
|
-
split_points.append(data[n:(n+datlen)])
|
|
93
|
-
|
|
94
|
-
if trunc:
|
|
95
|
-
split_points.pop(-1)
|
|
96
|
-
|
|
97
|
-
return split_points
|
|
92
|
+
return [data[n:n + datlen] for n in range(0, leng, datlen)]
|
|
98
93
|
|
|
99
94
|
def apf2_apfdecodedata(data: str, h: int, w: int, apfbuffer: list, lineskip: int, pals: list, trans: bool = False, topdown: bool = False):
|
|
100
95
|
x = 0
|
|
@@ -156,19 +151,19 @@ def flexTypeToRGB(inpt: str, mode: int, trans: bool = False):
|
|
|
156
151
|
# 7 - 95-level Grayscale
|
|
157
152
|
# 8 - 8-bit Grayscale
|
|
158
153
|
if mode == 3:
|
|
159
|
-
r =
|
|
160
|
-
g =
|
|
161
|
-
b =
|
|
154
|
+
r = mapping_invert[inpt[0]]
|
|
155
|
+
g = mapping_invert[inpt[1]]
|
|
156
|
+
b = mapping_invert[inpt[2]]
|
|
162
157
|
r = r+int(r == 254)
|
|
163
158
|
g = g+int(g == 254)
|
|
164
159
|
b = b+int(b == 254)
|
|
165
160
|
return r, g, b
|
|
166
161
|
|
|
167
162
|
elif mode == 4:
|
|
168
|
-
r =
|
|
169
|
-
g =
|
|
170
|
-
b =
|
|
171
|
-
a =
|
|
163
|
+
r = mapping_invert[inpt[0]]
|
|
164
|
+
g = mapping_invert[inpt[1]]
|
|
165
|
+
b = mapping_invert[inpt[2]]
|
|
166
|
+
a = mapping_invert[inpt[3]]
|
|
172
167
|
r = r+int(r == 254)
|
|
173
168
|
g = g+int(g == 254)
|
|
174
169
|
b = b+int(b == 254)
|
|
@@ -176,9 +171,9 @@ def flexTypeToRGB(inpt: str, mode: int, trans: bool = False):
|
|
|
176
171
|
return r, g, b, a
|
|
177
172
|
|
|
178
173
|
elif mode == 5:
|
|
179
|
-
r =
|
|
180
|
-
g =
|
|
181
|
-
b =
|
|
174
|
+
r = mapping_invert[inpt[0]]
|
|
175
|
+
g = mapping_invert[inpt[1]]
|
|
176
|
+
b = mapping_invert[inpt[2]]
|
|
182
177
|
|
|
183
178
|
# compute corrections
|
|
184
179
|
c = ord(inpt[3])-32
|
|
@@ -193,10 +188,10 @@ def flexTypeToRGB(inpt: str, mode: int, trans: bool = False):
|
|
|
193
188
|
return r+rc, g+gc, b+bc
|
|
194
189
|
|
|
195
190
|
elif mode == 6:
|
|
196
|
-
r =
|
|
197
|
-
g =
|
|
198
|
-
b =
|
|
199
|
-
a =
|
|
191
|
+
r = mapping_invert[inpt[0]]
|
|
192
|
+
g = mapping_invert[inpt[1]]
|
|
193
|
+
b = mapping_invert[inpt[2]]
|
|
194
|
+
a = mapping_invert[inpt[3]]
|
|
200
195
|
|
|
201
196
|
# compute corrections
|
|
202
197
|
c = ord(inpt[4])-32
|
|
@@ -208,7 +203,7 @@ def flexTypeToRGB(inpt: str, mode: int, trans: bool = False):
|
|
|
208
203
|
return r+rc, g+gc, b+bc, a+ac
|
|
209
204
|
|
|
210
205
|
elif mode == 7:
|
|
211
|
-
r =
|
|
206
|
+
r = mapping_invert[inpt[0]]
|
|
212
207
|
return [r, r, r]
|
|
213
208
|
|
|
214
209
|
elif mode == 8:
|
|
@@ -343,7 +338,7 @@ def graypal(perfect: bool = False):
|
|
|
343
338
|
else:
|
|
344
339
|
for n in set(mapping):
|
|
345
340
|
if not n in pal:
|
|
346
|
-
pal[n] = (
|
|
341
|
+
pal[n] = (mapping_invert[n], mapping_invert[n], mapping_invert[n])
|
|
347
342
|
return pal
|
|
348
343
|
|
|
349
344
|
def int_alpha_composit(l1: tuple, l2: tuple):
|
|
@@ -408,6 +403,8 @@ def decode(apf2: str | bytes, format: str = 'PNG', returnImageObject: bool = Fal
|
|
|
408
403
|
apf2 = gzip.decompress(compressed_blob)
|
|
409
404
|
elif alg == b'ci':
|
|
410
405
|
apf2 = asciipak.decompress(compressed_blob)
|
|
406
|
+
elif alg == b'sq':
|
|
407
|
+
apf2 = squeeze.decompress(compressed_blob)
|
|
411
408
|
else:
|
|
412
409
|
raise Exception("Invalid/Unsupported Compression Algorithm")
|
|
413
410
|
apf2 = apf2.decode("ascii")
|
|
@@ -417,6 +414,9 @@ def decode(apf2: str | bytes, format: str = 'PNG', returnImageObject: bool = Fal
|
|
|
417
414
|
elif apf2.startswith(b'\x1f\x8b\x08'):
|
|
418
415
|
apf2 = gzip.decompress(apf2)
|
|
419
416
|
apf2 = apf2.decode("ascii")
|
|
417
|
+
elif apf2.startswith(b'v\xff'):
|
|
418
|
+
apf2 = squeeze.decompress(apf2)
|
|
419
|
+
apf2 = apf2.decode("ascii")
|
|
420
420
|
elif apf2.startswith(b'\x83\x42\x2D\x2A\x95\x69\x45\x41'):
|
|
421
421
|
apf2 = asciipak.decompress(apf2)
|
|
422
422
|
apf2 = apf2.decode("ascii")
|
|
@@ -793,7 +793,7 @@ def generate_runs_apf2_l(bitmap: list, lineskip: int, w: int, h: int, topside_fi
|
|
|
793
793
|
|
|
794
794
|
def uint8_to_base95_with_corrector(val):
|
|
795
795
|
d = mapping[val]
|
|
796
|
-
c =
|
|
796
|
+
c = mapping_invert[d]
|
|
797
797
|
e = val - c
|
|
798
798
|
return d, e
|
|
799
799
|
|
|
@@ -1199,15 +1199,15 @@ def encode(img: bytes | Image.Image, lineskip: int = None, findbestlineskip: boo
|
|
|
1199
1199
|
if not compressor in (None, "none", "bzip2", "gzip", "asciipak", "squeeze"):
|
|
1200
1200
|
raise Exception('Error: Invalid Compressor! Options: none, bzip2, gzip, asciipak, squeeze')
|
|
1201
1201
|
|
|
1202
|
-
if compressor == "squeeze":
|
|
1203
|
-
raise Exception('Error: squeeze is not currently supported by apftool!')
|
|
1204
|
-
|
|
1205
1202
|
if compressor in ("bzip2", "gzip") and not compressgood:
|
|
1206
1203
|
raise Exception('Error: bzip2 and gzip is not currently supported by your python install!')
|
|
1207
1204
|
|
|
1208
1205
|
if compressor == "none":
|
|
1209
1206
|
compressor = None # set it to the real nonetype
|
|
1210
1207
|
|
|
1208
|
+
if useadvancedquant and qmf == "basic": # disable the good quantizer
|
|
1209
|
+
useadvancedquant = False
|
|
1210
|
+
|
|
1211
1211
|
trans = int(trans) # compatability filter
|
|
1212
1212
|
if trans > 2:
|
|
1213
1213
|
raise Exception("Error: Invalid Transparency Mode!")
|
|
@@ -16,9 +16,12 @@ Pipeline
|
|
|
16
16
|
"""
|
|
17
17
|
import heapq
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
from
|
|
19
|
+
try:
|
|
20
|
+
import numpy as np
|
|
21
|
+
from PIL import Image
|
|
22
|
+
from scipy.spatial import cKDTree
|
|
23
|
+
except Exception:
|
|
24
|
+
print("Error Loading numpy or scipy")
|
|
22
25
|
|
|
23
26
|
# ---------------------------------------------------------------- Oklab ----
|
|
24
27
|
_M1 = np.array([[0.4122214708, 0.5363325363, 0.0514459929],
|
|
@@ -233,4 +236,4 @@ def quantize_perceptual(img: Image.Image, n_colors: int, palette: list = None,
|
|
|
233
236
|
raise ValueError("dither_mode must be 'ordered' or 'fs'")
|
|
234
237
|
|
|
235
238
|
quantized = pal_rgba[idx].reshape(h, w, 4)
|
|
236
|
-
return Image.fromarray(quantized, "RGBA"), [tuple(int(v) for v in c) for c in pal_rgba]
|
|
239
|
+
return Image.fromarray(quantized, "RGBA"), [tuple(int(v) for v in c) for c in pal_rgba]
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: apftool
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.2
|
|
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
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
8
|
License-File: LICENSE
|
|
9
9
|
Requires-Dist: Pillow
|
|
10
|
+
Requires-Dist: psqueeze
|
|
10
11
|
Dynamic: license-file
|
|
11
12
|
|
|
12
13
|
# A library for converting to and from Aperture Science apf files, the custom extended apf2s, otb/wbmp images, and facedev's bruh format.
|
|
@@ -100,6 +101,8 @@ apftool provides many extensions tuples:
|
|
|
100
101
|
|
|
101
102
|
## changelog:
|
|
102
103
|
|
|
104
|
+
1.2.2 - add support for squeeze, improve handling for numpy/scipy not being installed. Signifigant performance improvement for Truecolor modes.
|
|
105
|
+
|
|
103
106
|
1.2.1 - add support for 3 of apf2's compressed modes [bzip2, gzip, ascii packing]
|
|
104
107
|
|
|
105
108
|
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.
|
|
@@ -4,12 +4,12 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "apftool"
|
|
7
|
-
version = "1.2.
|
|
7
|
+
version = "1.2.2"
|
|
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"
|
|
11
11
|
requires-python = ">=3.10"
|
|
12
|
-
dependencies = ["Pillow"]
|
|
12
|
+
dependencies = ["Pillow", "psqueeze"]
|
|
13
13
|
|
|
14
14
|
[project.scripts]
|
|
15
15
|
apfcli = "apftool:cli"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
Pillow
|
|
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
|
|
File without changes
|