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.
@@ -1,12 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: apftool
3
- Version: 1.2.1
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.1"
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
- trunc = False
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
- trunc = True
90
+ leng -= leng % datlen
88
91
 
89
- split_points = []
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 = mapping.index(inpt[0])
160
- g = mapping.index(inpt[1])
161
- b = mapping.index(inpt[2])
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 = mapping.index(inpt[0])
169
- g = mapping.index(inpt[1])
170
- b = mapping.index(inpt[2])
171
- a = mapping.index(inpt[3])
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 = mapping.index(inpt[0])
180
- g = mapping.index(inpt[1])
181
- b = mapping.index(inpt[2])
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 = mapping.index(inpt[0])
197
- g = mapping.index(inpt[1])
198
- b = mapping.index(inpt[2])
199
- a = mapping.index(inpt[3])
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 = mapping.index(inpt[0])
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] = (mapping.index(n), mapping.index(n), mapping.index(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 = mapping.index(d)
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
- import numpy as np
20
- from PIL import Image
21
- from scipy.spatial import cKDTree
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.1
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.
@@ -0,0 +1,2 @@
1
+ Pillow
2
+ psqueeze
@@ -4,12 +4,12 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "apftool"
7
- version = "1.2.1"
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