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 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.
@@ -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
@@ -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"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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()