brulegfx 0.0.6__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.
- brulegfx-0.0.6/LICENSE +21 -0
- brulegfx-0.0.6/PKG-INFO +63 -0
- brulegfx-0.0.6/README.md +49 -0
- brulegfx-0.0.6/pyproject.toml +20 -0
- brulegfx-0.0.6/setup.cfg +4 -0
- brulegfx-0.0.6/setup.py +123 -0
- brulegfx-0.0.6/src/brule/__init__.py +30 -0
- brulegfx-0.0.6/src/brule/__metadata__.py +28 -0
- brulegfx-0.0.6/src/brule/brule.py +247 -0
- brulegfx-0.0.6/src/brule/hextree/_hextree.cc +648 -0
- brulegfx-0.0.6/src/brule/hextree.py +69 -0
- brulegfx-0.0.6/src/brule/layouteng/_layouteng.cc +545 -0
- brulegfx-0.0.6/src/brule/layouteng.py +180 -0
- brulegfx-0.0.6/src/brule/qtzr/_qtzrutc.cc +699 -0
- brulegfx-0.0.6/src/brule/qtzrutc.py +93 -0
- brulegfx-0.0.6/src/brule/rle/_brule.cc +163 -0
- brulegfx-0.0.6/src/brule/rle/_librle.cc +367 -0
- brulegfx-0.0.6/src/brulegfx.egg-info/PKG-INFO +63 -0
- brulegfx-0.0.6/src/brulegfx.egg-info/SOURCES.txt +21 -0
- brulegfx-0.0.6/src/brulegfx.egg-info/dependency_links.txt +1 -0
- brulegfx-0.0.6/src/brulegfx.egg-info/not-zip-safe +1 -0
- brulegfx-0.0.6/src/brulegfx.egg-info/requires.txt +3 -0
- brulegfx-0.0.6/src/brulegfx.egg-info/top_level.txt +1 -0
brulegfx-0.0.6/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 cubicibo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
brulegfx-0.0.6/PKG-INFO
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: brulegfx
|
|
3
|
+
Version: 0.0.6
|
|
4
|
+
Summary: Bitmap utilities (RLE, RGBA quantization, Segmentation)
|
|
5
|
+
Author-email: cubicibo <cubicibo@outlook.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Requires-Python: >=3.12
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: numpy>=2.0.1
|
|
11
|
+
Requires-Dist: numba
|
|
12
|
+
Requires-Dist: opencv-python
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
|
|
15
|
+
# Brule
|
|
16
|
+
Collection of C extension modules for SUPer with pure-python fallbacks
|
|
17
|
+
- Brule: Bitmap RUn LEngth coder and decoder.
|
|
18
|
+
- LayoutEngine: An optimal two-rects layout finder for a sequence of images.
|
|
19
|
+
- HexTree: RGBA image quantizer (turbo, good quality)
|
|
20
|
+
- QtzrUTC: RGBA image quantizer (fast, excellent quality)
|
|
21
|
+
|
|
22
|
+
## Brule brief
|
|
23
|
+
Brule implements 2 or 3 times the same function, and select the fastest implementation available at runtime.
|
|
24
|
+
- C (fastest)
|
|
25
|
+
- numba (only for the RLE codec)
|
|
26
|
+
- pure Python (slow)
|
|
27
|
+
|
|
28
|
+
For the encoder, the C implementation is up to 20 times faster than numba. The pure Python implementation does not compete and is only there for convenience.
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
Given `./brule` the (cloned) repository:
|
|
32
|
+
```bash
|
|
33
|
+
$ python -m pip install ./brule
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Example (RLE Codec)
|
|
37
|
+
|
|
38
|
+
The run-length codec is operated like this:
|
|
39
|
+
```python
|
|
40
|
+
>>> from brule import Brule
|
|
41
|
+
>>> import numpy as np
|
|
42
|
+
>>> bitmap_orig = np.ones((1080, 1920), np.uint8)
|
|
43
|
+
>>> rle_data = Brule.encode(bitmap_orig)
|
|
44
|
+
>>> bitmap = Brule.decode(rle_data)
|
|
45
|
+
>>> assert np.all(bitmap == bitmap_orig)
|
|
46
|
+
True
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Example (HexTree, QtzrUTC)
|
|
50
|
+
The image quantizer is used like this:
|
|
51
|
+
```python
|
|
52
|
+
>>> from brule import HexTree, QtzrUTC
|
|
53
|
+
>>> import numpy as np
|
|
54
|
+
>>> from PIL import Image
|
|
55
|
+
>>> rgba = np.asarray(Image.open(...).convert('RGBA'))
|
|
56
|
+
>>> #quantize with 255 colours
|
|
57
|
+
>>> bitmap1, palette1 = HexTree.quantize(rgba, 255)
|
|
58
|
+
>>> bitmap2, palette2 = QtzrUTC.quantize(rgba, 255)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Credits
|
|
62
|
+
- https://github.com/pallets/markupsafe markupsafe for the pip install fallback mechanism with compiled extensions.
|
|
63
|
+
- https://github.com/DarthSim/quantizr for Qtzr
|
brulegfx-0.0.6/README.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Brule
|
|
2
|
+
Collection of C extension modules for SUPer with pure-python fallbacks
|
|
3
|
+
- Brule: Bitmap RUn LEngth coder and decoder.
|
|
4
|
+
- LayoutEngine: An optimal two-rects layout finder for a sequence of images.
|
|
5
|
+
- HexTree: RGBA image quantizer (turbo, good quality)
|
|
6
|
+
- QtzrUTC: RGBA image quantizer (fast, excellent quality)
|
|
7
|
+
|
|
8
|
+
## Brule brief
|
|
9
|
+
Brule implements 2 or 3 times the same function, and select the fastest implementation available at runtime.
|
|
10
|
+
- C (fastest)
|
|
11
|
+
- numba (only for the RLE codec)
|
|
12
|
+
- pure Python (slow)
|
|
13
|
+
|
|
14
|
+
For the encoder, the C implementation is up to 20 times faster than numba. The pure Python implementation does not compete and is only there for convenience.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
Given `./brule` the (cloned) repository:
|
|
18
|
+
```bash
|
|
19
|
+
$ python -m pip install ./brule
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Example (RLE Codec)
|
|
23
|
+
|
|
24
|
+
The run-length codec is operated like this:
|
|
25
|
+
```python
|
|
26
|
+
>>> from brule import Brule
|
|
27
|
+
>>> import numpy as np
|
|
28
|
+
>>> bitmap_orig = np.ones((1080, 1920), np.uint8)
|
|
29
|
+
>>> rle_data = Brule.encode(bitmap_orig)
|
|
30
|
+
>>> bitmap = Brule.decode(rle_data)
|
|
31
|
+
>>> assert np.all(bitmap == bitmap_orig)
|
|
32
|
+
True
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Example (HexTree, QtzrUTC)
|
|
36
|
+
The image quantizer is used like this:
|
|
37
|
+
```python
|
|
38
|
+
>>> from brule import HexTree, QtzrUTC
|
|
39
|
+
>>> import numpy as np
|
|
40
|
+
>>> from PIL import Image
|
|
41
|
+
>>> rgba = np.asarray(Image.open(...).convert('RGBA'))
|
|
42
|
+
>>> #quantize with 255 colours
|
|
43
|
+
>>> bitmap1, palette1 = HexTree.quantize(rgba, 255)
|
|
44
|
+
>>> bitmap2, palette2 = QtzrUTC.quantize(rgba, 255)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Credits
|
|
48
|
+
- https://github.com/pallets/markupsafe markupsafe for the pip install fallback mechanism with compiled extensions.
|
|
49
|
+
- https://github.com/DarthSim/quantizr for Qtzr
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=64", "numpy>=2.0.1"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "brulegfx"
|
|
7
|
+
version = "0.0.6"
|
|
8
|
+
authors = [{ name = "cubicibo", email = "cubicibo@outlook.com" }]
|
|
9
|
+
description = "Bitmap utilities (RLE, RGBA quantization, Segmentation)"
|
|
10
|
+
readme = { file = "README.md", content-type = "text/markdown" }
|
|
11
|
+
license = "MIT"
|
|
12
|
+
requires-python = ">=3.12"
|
|
13
|
+
dependencies = [ "numpy>=2.0.1", "numba", "opencv-python", ]
|
|
14
|
+
|
|
15
|
+
[tool.setuptools]
|
|
16
|
+
package-dir = {"" = "src"}
|
|
17
|
+
zip-safe = false
|
|
18
|
+
|
|
19
|
+
[tool.setuptools.packages.find]
|
|
20
|
+
where = ["src"]
|
brulegfx-0.0.6/setup.cfg
ADDED
brulegfx-0.0.6/setup.py
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MIT License
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2024-26 cubicibo
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
import os
|
|
26
|
+
import setuptools
|
|
27
|
+
import sysconfig
|
|
28
|
+
import numpy as np
|
|
29
|
+
from setuptools.command.build_ext import build_ext
|
|
30
|
+
from setuptools.errors import CCompilerError
|
|
31
|
+
from setuptools.errors import ExecError
|
|
32
|
+
from setuptools.errors import PlatformError
|
|
33
|
+
|
|
34
|
+
NAME = 'brule'
|
|
35
|
+
|
|
36
|
+
# This fallback build system is taken from https://github.com/pallets/markupsafe (BSD-3 license)
|
|
37
|
+
class BuildFailed(Exception):
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
class ve_build_ext(build_ext):
|
|
41
|
+
"""This class allows C extension building to fail."""
|
|
42
|
+
def run(self):
|
|
43
|
+
try:
|
|
44
|
+
super().run()
|
|
45
|
+
except PlatformError as e:
|
|
46
|
+
raise BuildFailed() from e
|
|
47
|
+
|
|
48
|
+
def build_extension(self, ext):
|
|
49
|
+
try:
|
|
50
|
+
super().build_extension(ext)
|
|
51
|
+
except (CCompilerError, ExecError, PlatformError) as e:
|
|
52
|
+
raise BuildFailed() from e
|
|
53
|
+
except ValueError as e:
|
|
54
|
+
import sys
|
|
55
|
+
# this can happen on Windows 64 bit, see Python issue 7511
|
|
56
|
+
if "'path'" in str(sys.exc_info()[1]):
|
|
57
|
+
raise BuildFailed() from e
|
|
58
|
+
raise
|
|
59
|
+
|
|
60
|
+
def show_message(*lines):
|
|
61
|
+
print("=" * 74)
|
|
62
|
+
for line in lines:
|
|
63
|
+
print(line)
|
|
64
|
+
print("=" * 74)
|
|
65
|
+
|
|
66
|
+
if __name__ == "__main__":
|
|
67
|
+
try:
|
|
68
|
+
extra_compile_args = sysconfig.get_config_var('CFLAGS').split()
|
|
69
|
+
except AttributeError:
|
|
70
|
+
extra_compile_args = []
|
|
71
|
+
|
|
72
|
+
include_dirs = [np.get_include()]
|
|
73
|
+
pthread_flag = ["-pthread"] if os.name == 'posix' else []
|
|
74
|
+
|
|
75
|
+
brule_codec = setuptools.Extension(
|
|
76
|
+
f"{NAME}._brule",
|
|
77
|
+
sources=[f"src/{NAME}/rle/_brule.cc", f"src/{NAME}/rle/_librle.cc"],
|
|
78
|
+
include_dirs=include_dirs,
|
|
79
|
+
language="c",
|
|
80
|
+
extra_compile_args=extra_compile_args + pthread_flag,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
layout_eng = setuptools.Extension(
|
|
84
|
+
f"{NAME}._layouteng",
|
|
85
|
+
sources=[f"src/{NAME}/layouteng/_layouteng.cc"],
|
|
86
|
+
include_dirs=include_dirs,
|
|
87
|
+
language="c",
|
|
88
|
+
extra_compile_args=extra_compile_args,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
hextree = setuptools.Extension(
|
|
92
|
+
f"{NAME}._hextree",
|
|
93
|
+
sources=[f"src/{NAME}/hextree/_hextree.cc"],
|
|
94
|
+
include_dirs=include_dirs,
|
|
95
|
+
language="c",
|
|
96
|
+
extra_compile_args=extra_compile_args,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
qtzrutc = setuptools.Extension(
|
|
100
|
+
f"{NAME}._qtzrutc",
|
|
101
|
+
sources=[f"src/{NAME}/qtzr/_qtzrutc.cc"],
|
|
102
|
+
include_dirs=include_dirs,
|
|
103
|
+
language="c",
|
|
104
|
+
extra_compile_args=extra_compile_args,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
modules = [brule_codec, layout_eng, hextree, qtzrutc]
|
|
108
|
+
|
|
109
|
+
def run_setup(modules):
|
|
110
|
+
setuptools.setup(
|
|
111
|
+
cmdclass={"build_ext": ve_build_ext},
|
|
112
|
+
ext_modules=modules,
|
|
113
|
+
)
|
|
114
|
+
####
|
|
115
|
+
|
|
116
|
+
try:
|
|
117
|
+
run_setup(modules)
|
|
118
|
+
except BuildFailed:
|
|
119
|
+
show_message(
|
|
120
|
+
"WARNING: Failed to compile optimised implementation."
|
|
121
|
+
"Retrying the build with only pure Python implementation.",
|
|
122
|
+
)
|
|
123
|
+
run_setup([])
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MIT License
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2024 cubicibo
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from .__metadata__ import __version__, __author__
|
|
26
|
+
|
|
27
|
+
from .brule import Brule
|
|
28
|
+
from .layouteng import LayoutEngine
|
|
29
|
+
from .hextree import HexTree
|
|
30
|
+
from .qtzrutc import QtzrUTC
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MIT License
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2024 cubicibo
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
__MAJOR, __MINOR, __REVISION = 0, 0, 6
|
|
26
|
+
|
|
27
|
+
__version__ = '.'.join(map(str, [__MAJOR, __MINOR, __REVISION]))
|
|
28
|
+
__author__ = 'cubicibo'
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MIT License
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2024 cubicibo
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from typing import Any, Callable, Optional, Union
|
|
26
|
+
from dataclasses import dataclass
|
|
27
|
+
|
|
28
|
+
import numpy as np
|
|
29
|
+
from numpy import typing as npt
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class _CodecFunctions:
|
|
33
|
+
encode: Callable[[npt.NDArray[np.uint8]], Union[bytes, bytearray]]
|
|
34
|
+
decode: Callable[[Union[bytes, bytearray]], npt.NDArray[np.uint8]]
|
|
35
|
+
|
|
36
|
+
def _try_numba() -> _CodecFunctions:
|
|
37
|
+
#If numba is available, provide compiled functions for the encoder/decoder
|
|
38
|
+
from numba import njit
|
|
39
|
+
|
|
40
|
+
@njit(fastmath=True)
|
|
41
|
+
def njit_encode_rle(bitmap: npt.NDArray[np.uint8]) -> list[np.uint8]:
|
|
42
|
+
"""
|
|
43
|
+
Encode a 2D map using the RLE defined in 'US 7912305 B1' patent.
|
|
44
|
+
:param bitmap: Palette mapped image to encode (2d array)
|
|
45
|
+
:return: Encoded data (vector)
|
|
46
|
+
"""
|
|
47
|
+
i, j = 0, 0
|
|
48
|
+
rle_data = [np.uint8(x) for x in range(0)]
|
|
49
|
+
|
|
50
|
+
height, width = bitmap.shape
|
|
51
|
+
assert width <= 16383, "Bitmap too large."
|
|
52
|
+
|
|
53
|
+
while i < height:
|
|
54
|
+
color = bitmap[i, j]
|
|
55
|
+
prev_j = j
|
|
56
|
+
while (j := j+1) < width and bitmap[i, j] == color: pass
|
|
57
|
+
|
|
58
|
+
dist = j - prev_j
|
|
59
|
+
if color == 0:
|
|
60
|
+
if dist > 63:
|
|
61
|
+
rle_data += [0x00, 0x40 | ((dist >> 8) & 0x3F), dist & 0xFF]
|
|
62
|
+
else:
|
|
63
|
+
rle_data += [0x00, dist & 0x3F]
|
|
64
|
+
else:
|
|
65
|
+
if dist > 63:
|
|
66
|
+
rle_data += [0x00, 0xC0 | ((dist >> 8) & 0x3F), dist & 0xFF, color]
|
|
67
|
+
elif dist > 2:
|
|
68
|
+
rle_data += [0x00, 0x80 | (dist & 0x3F), color]
|
|
69
|
+
else:
|
|
70
|
+
rle_data += [color] * dist
|
|
71
|
+
if j == width:
|
|
72
|
+
j = 0
|
|
73
|
+
i += 1
|
|
74
|
+
rle_data += [0x00, 0x00]
|
|
75
|
+
return rle_data
|
|
76
|
+
|
|
77
|
+
@njit(fastmath=True)
|
|
78
|
+
def njit_decode_rle(rle_data: Union[list[np.uint8], npt.NDArray[np.uint8], bytes], width: int, height: int, check_rle: bool = False) -> npt.NDArray[np.uint8]:
|
|
79
|
+
i, j, k = 0, -1, -2
|
|
80
|
+
# RLE is terminated by new line command ([0x00, 0x00]), we can ignore it.
|
|
81
|
+
len_rle = len(rle_data) - 2
|
|
82
|
+
bitmap = np.zeros((height, width), np.uint8)
|
|
83
|
+
line_length = 0
|
|
84
|
+
|
|
85
|
+
while k < len_rle:
|
|
86
|
+
if i % width == 0:
|
|
87
|
+
if line_length > 0:
|
|
88
|
+
assert 0 == rle_data[k] == rle_data[k+1], "RLE line terminator not found at given width."
|
|
89
|
+
assert not check_rle or line_length <= width, "Illegal RLE line width."
|
|
90
|
+
line_length = 0
|
|
91
|
+
i = 0
|
|
92
|
+
j += 1
|
|
93
|
+
k += 2
|
|
94
|
+
k_start = k
|
|
95
|
+
byte = rle_data[k]
|
|
96
|
+
if byte == 0:
|
|
97
|
+
byte = rle_data[(k:=k+1)]
|
|
98
|
+
if byte & 0x40:
|
|
99
|
+
length = ((byte & 0x3F) << 8) | rle_data[(k:=k+1)]
|
|
100
|
+
else:
|
|
101
|
+
length = byte & 0x3F
|
|
102
|
+
if byte & 0x80:
|
|
103
|
+
bitmap[j, i:(i:=i+length)] = rle_data[(k:=k+1)]
|
|
104
|
+
else:
|
|
105
|
+
bitmap[j, i:(i:=i+length)] = 0
|
|
106
|
+
else:
|
|
107
|
+
bitmap[j, i:(i:=i+1)] = byte
|
|
108
|
+
k+=1
|
|
109
|
+
line_length += (k - k_start)
|
|
110
|
+
return bitmap
|
|
111
|
+
return _CodecFunctions(njit_encode_rle, njit_decode_rle)
|
|
112
|
+
####
|
|
113
|
+
|
|
114
|
+
#%%
|
|
115
|
+
class Brule:
|
|
116
|
+
_numba_codec = None
|
|
117
|
+
_cimpl_codec = None
|
|
118
|
+
|
|
119
|
+
@classmethod
|
|
120
|
+
def _setup(cls) -> None:
|
|
121
|
+
try:
|
|
122
|
+
from . import _brule
|
|
123
|
+
cls._cimpl_codec = _CodecFunctions(_brule.encode, _brule.decode)
|
|
124
|
+
except (ImportError, ModuleNotFoundError): ...
|
|
125
|
+
try:
|
|
126
|
+
cls._numba_codec = _try_numba()
|
|
127
|
+
except (ImportError, ModuleNotFoundError): ...
|
|
128
|
+
|
|
129
|
+
@classmethod
|
|
130
|
+
def get_capabilities(cls) -> str:
|
|
131
|
+
cap = ['C'] * (cls._cimpl_codec is not None)
|
|
132
|
+
cap += ['Numba'] * (cls._numba_codec is not None)
|
|
133
|
+
return cap + ['Python']
|
|
134
|
+
|
|
135
|
+
@classmethod
|
|
136
|
+
def _select_impl(cls, impl: str) -> bool:
|
|
137
|
+
impl = impl.tolower()
|
|
138
|
+
if impl == 'numba':
|
|
139
|
+
cls._setup()
|
|
140
|
+
cls._cimpl_codec = None
|
|
141
|
+
return cls._numba_codec is not None
|
|
142
|
+
if impl == 'python':
|
|
143
|
+
cls._cimpl_codec = None
|
|
144
|
+
cls._numba_codec = None
|
|
145
|
+
return True
|
|
146
|
+
if impl == 'c':
|
|
147
|
+
cls._setup()
|
|
148
|
+
return cls._cimpl_codec is not None
|
|
149
|
+
return False
|
|
150
|
+
|
|
151
|
+
@classmethod
|
|
152
|
+
def encode(cls, bitmap: npt.NDArray[np.uint8]) -> bytes:
|
|
153
|
+
"""
|
|
154
|
+
Encode a 2D map using the RLE defined in 'US 7912305 B1' patent.
|
|
155
|
+
:param bitmap: Palette mapped image to encode (2d array)
|
|
156
|
+
:return: Encoded bytes (vector)
|
|
157
|
+
"""
|
|
158
|
+
if cls._cimpl_codec is not None:
|
|
159
|
+
if not bitmap.flags['C_CONTIGUOUS']:
|
|
160
|
+
bitmap = np.ascontiguousarray(bitmap)
|
|
161
|
+
return cls._cimpl_codec.encode(bitmap)
|
|
162
|
+
if cls._numba_codec is not None:
|
|
163
|
+
return cls._numba_codec.encode(bitmap)
|
|
164
|
+
|
|
165
|
+
rle_data = []
|
|
166
|
+
i, j = 0, 0
|
|
167
|
+
|
|
168
|
+
height, width = bitmap.shape
|
|
169
|
+
assert width <= 16383, "Bitmap too large."
|
|
170
|
+
|
|
171
|
+
while i < height:
|
|
172
|
+
color = bitmap[i, j]
|
|
173
|
+
prev_j = j
|
|
174
|
+
while (j := j+1) < width and bitmap[i, j] == color: pass
|
|
175
|
+
|
|
176
|
+
dist = j - prev_j
|
|
177
|
+
if color == 0:
|
|
178
|
+
if dist > 63:
|
|
179
|
+
rle_data += [0x00, 0x40 | ((dist >> 8) & 0x3F), dist & 0xFF]
|
|
180
|
+
else:
|
|
181
|
+
rle_data += [0x00, dist & 0x3F]
|
|
182
|
+
else:
|
|
183
|
+
if dist > 63:
|
|
184
|
+
rle_data += [0x00, 0xC0 | ((dist >> 8) & 0x3F), dist & 0xFF, color]
|
|
185
|
+
elif dist > 2:
|
|
186
|
+
rle_data += [0x00, 0x80 | (dist & 0x3F), color]
|
|
187
|
+
else:
|
|
188
|
+
rle_data += [color] * dist
|
|
189
|
+
if j == width:
|
|
190
|
+
j = 0
|
|
191
|
+
i += 1
|
|
192
|
+
rle_data += [0x00, 0x00]
|
|
193
|
+
return bytes(rle_data)
|
|
194
|
+
|
|
195
|
+
@classmethod
|
|
196
|
+
def decode(cls, data: Union[bytes, bytearray], width: Optional[int] = None, height: Optional[int] = None, check_rle: bool = False) -> npt.NDArray[np.uint8]:
|
|
197
|
+
"""
|
|
198
|
+
Decode a RLE object, as defined in 'US 7912305 B1' patent.
|
|
199
|
+
:param data: Bytes to decode
|
|
200
|
+
:return: numpy bitmap
|
|
201
|
+
"""
|
|
202
|
+
if isinstance(data, list):
|
|
203
|
+
data = bytearray(data)
|
|
204
|
+
|
|
205
|
+
if cls._cimpl_codec is not None and check_rle is False:
|
|
206
|
+
return cls._cimpl_codec.decode(data)
|
|
207
|
+
if cls._numba_codec is not None and width and height:
|
|
208
|
+
return cls._numba_codec.decode(np.frombuffer(data, dtype=np.uint8), width, height, check_rle)
|
|
209
|
+
|
|
210
|
+
if check_rle:
|
|
211
|
+
assert width is not None and width > 0
|
|
212
|
+
|
|
213
|
+
k = 0
|
|
214
|
+
len_data = len(data)
|
|
215
|
+
bitmap = []
|
|
216
|
+
line = []
|
|
217
|
+
rle_line_length = 0
|
|
218
|
+
|
|
219
|
+
while k < len_data:
|
|
220
|
+
k_start = k
|
|
221
|
+
byte = data[k]
|
|
222
|
+
if byte == 0:
|
|
223
|
+
byte = data[(k:=k+1)]
|
|
224
|
+
if byte == 0:
|
|
225
|
+
assert not check_rle or rle_line_length <= width
|
|
226
|
+
bitmap.append(line)
|
|
227
|
+
line = []
|
|
228
|
+
rle_line_length = 0
|
|
229
|
+
k += 1
|
|
230
|
+
continue
|
|
231
|
+
if byte & 0x40:
|
|
232
|
+
length = ((byte & 0x3F) << 8) | data[(k:=k+1)]
|
|
233
|
+
else:
|
|
234
|
+
length = byte & 0x3F
|
|
235
|
+
if byte & 0x80:
|
|
236
|
+
line += [data[(k:=k+1)]]*length
|
|
237
|
+
else:
|
|
238
|
+
line += [0]*length
|
|
239
|
+
else:
|
|
240
|
+
line.append(byte)
|
|
241
|
+
k+=1
|
|
242
|
+
rle_line_length += (k - k_start)
|
|
243
|
+
return np.asarray(bitmap, np.uint8)
|
|
244
|
+
####
|
|
245
|
+
|
|
246
|
+
#init module
|
|
247
|
+
Brule._setup()
|