thorvg-python 1.0.1__py3-none-win32.whl

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.
@@ -0,0 +1,196 @@
1
+ #!/usr/bin/env python3
2
+ import ctypes
3
+ from typing import TYPE_CHECKING, Optional, Tuple
4
+
5
+ from ..base import CanvasStruct, Colorspace, MempoolPolicy, Result
6
+ from ..engine import Engine
7
+ from . import Canvas
8
+
9
+ if TYPE_CHECKING:
10
+ from PIL import Image
11
+
12
+
13
+ class SwCanvas(Canvas):
14
+ """
15
+ SwCanvas API
16
+
17
+ A module for rendering the graphical elements using the software engine.
18
+ """
19
+
20
+ def __init__(self, engine: Engine, canvas: Optional[CanvasStruct] = None):
21
+ self.engine = engine
22
+ self.thorvg_lib = engine.thorvg_lib
23
+ self.buffer_arr: Optional[ctypes.Array[ctypes.c_uint32]] = None
24
+ self.w: Optional[int] = None
25
+ self.h: Optional[int] = None
26
+ self.stride: Optional[int] = None
27
+ self.cs: Optional[Colorspace] = None
28
+ if canvas is None:
29
+ self._canvas = self._create()
30
+ else:
31
+ self._canvas = canvas
32
+
33
+ def _create(self) -> CanvasStruct:
34
+ """Creates a Canvas object.
35
+
36
+ Note that you need not call this method as it is auto called when initializing ``SwCanvas()``.
37
+
38
+ .. code-block:: python
39
+ from thorvg_python import Engine, SwCanvas
40
+
41
+ engine = Engine()
42
+ canvas = SwCanvas(engine)
43
+ result, buffer = canvas.set_target(100, 100, 100, Colorspace.ARGB8888)
44
+
45
+ //set up paints and add them into the canvas before drawing it
46
+
47
+ canvas.destroy()
48
+ engine.term()
49
+
50
+ :return: new CanvasStruct object.
51
+ :rtype: CanvasStruct
52
+ """
53
+ self.thorvg_lib.tvg_swcanvas_create.restype = ctypes.POINTER(CanvasStruct)
54
+ return self.thorvg_lib.tvg_swcanvas_create().contents
55
+
56
+ def set_target(
57
+ self,
58
+ w: int,
59
+ h: int,
60
+ stride: Optional[int] = None,
61
+ cs: Colorspace = Colorspace.ABGR8888,
62
+ ) -> Tuple[Result, ctypes.Array[ctypes.c_uint32]]:
63
+ """Sets the buffer used in the rasterization process and defines the used colorspace.
64
+
65
+ For optimisation reasons TVG does not allocate memory for the output buffer on its own.
66
+ The buffer of a desirable size should be allocated and owned by the caller.
67
+
68
+ w, h, stride, cs and buffer_arr will be stored in instance when calling this method.
69
+
70
+ :param int w: The width of the raster image.
71
+ :param int h: The height of the raster image.
72
+ :param Optional[int] stride: The stride of the raster image - default is same value as ``w``.
73
+ :param Colorspace cs: The colorspace value defining the way the 32-bits colors should be read/written.
74
+ - ABGR8888 (Default)
75
+ - ARGB8888
76
+
77
+ :return:
78
+ - INVALID_ARGUMENTS An invalid canvas or buffer pointer passed or one of the ``stride``, ``w`` or ``h`` being zero.
79
+ - INSUFFICIENT_CONDITION if the canvas is performing rendering. Please ensure the canvas is synced.
80
+ - NOT_SUPPORTED The software engine is not supported.
81
+ :rtype: Result
82
+ :return: A pointer to the allocated memory block of the size ``stride`` x ``h``.
83
+ :rtype: ctypes.Array[ctypes.c_uint32]
84
+
85
+ .. warning::
86
+ Do not access ``buffer`` during tvg_canvas_draw() - tvg_canvas_sync(). It should not be accessed while the engine is writing on it.
87
+
88
+ .. seealso:: Colorspace
89
+ """
90
+ if stride is None:
91
+ stride = w
92
+ buffer_arr_type = ctypes.c_uint32 * (stride * h)
93
+ buffer_arr = buffer_arr_type()
94
+ self.thorvg_lib.tvg_swcanvas_set_target.argtypes = [
95
+ ctypes.POINTER(CanvasStruct),
96
+ ctypes.POINTER(buffer_arr_type),
97
+ ctypes.c_uint32,
98
+ ctypes.c_uint32,
99
+ ctypes.c_uint32,
100
+ ctypes.c_int,
101
+ ]
102
+ self.thorvg_lib.tvg_swcanvas_set_target.restype = Result
103
+ result = self.thorvg_lib.tvg_swcanvas_set_target(
104
+ ctypes.pointer(self._canvas),
105
+ ctypes.pointer(buffer_arr),
106
+ ctypes.c_uint32(stride),
107
+ ctypes.c_uint32(w),
108
+ ctypes.c_uint32(h),
109
+ cs,
110
+ )
111
+ self.buffer_arr = buffer_arr
112
+ self.w = w
113
+ self.h = h
114
+ self.stride = stride
115
+ self.cs = cs
116
+ return result
117
+
118
+ def get_pillow(self, pil_mode: str = "RGBA") -> "Image.Image":
119
+ """Gets Pillow Image from buffer of canvas
120
+
121
+ .. code-block:: python
122
+
123
+ from thorvg_python import Engine, SwCanvas, Shape
124
+
125
+ engine = tvg.Engine()
126
+ canvas = tvg.SwCanvas(engine)
127
+ canvas.set_target(1920, 1080)
128
+
129
+ // Draw on canvas
130
+ rect = Shape(engine)
131
+ rect.append_rect(50, 50, 200, 200, 20, 20)
132
+ rect.set_fill_color(100, 100, 100, 100)
133
+ canvas.push(rect)
134
+
135
+ canvas.draw()
136
+ canvas.sync()
137
+
138
+ im = canvas.get_pillow()
139
+
140
+ canvas.destroy()
141
+ engine.term()
142
+
143
+ :param str pil_mode: Color mode of Pillow Image. Defaults to RGBA
144
+
145
+ :return: Pillow image
146
+ :rtype: PIL.Image.Image
147
+ """
148
+ from PIL import Image
149
+
150
+ if self.w is None:
151
+ raise RuntimeError("w cannot be None")
152
+ if self.h is None:
153
+ raise RuntimeError("h cannot be None")
154
+ if self.buffer_arr is None:
155
+ raise RuntimeError("buffer_arr cannot be None")
156
+
157
+ return Image.frombuffer( # type: ignore
158
+ "RGBA", (self.w, self.h), bytes(self.buffer_arr), "raw"
159
+ ).convert(pil_mode)
160
+
161
+ def set_mempool(
162
+ self,
163
+ policy: MempoolPolicy,
164
+ ) -> Result:
165
+ """Sets the software engine memory pool behavior policy.
166
+
167
+ ThorVG draws a lot of shapes, it allocates/deallocates a few chunk of memory
168
+ while processing rendering. It internally uses one shared memory pool
169
+ which can be reused among the canvases in order to avoid memory overhead.
170
+
171
+ Thus ThorVG suggests using a memory pool policy to satisfy user demands,
172
+ if it needs to guarantee the thread-safety of the internal data access.
173
+
174
+ :param MempoolPolicy policy: The method specifying the Memory Pool behavior. The default value is ``DEFAULT``.
175
+
176
+ :return:
177
+ - INVALID_ARGUMENTS An invalid canvas pointer passed.
178
+ - INSUFFICIENT_CONDITION The canvas contains some paints already.
179
+ - NOT_SUPPORTED The software engine is not supported.
180
+ :rtype: Result
181
+
182
+ .. note::
183
+ When ``policy`` is set as ``INDIVIDUAL``, the current instance of canvas uses its own individual
184
+ memory data, which is not shared with others. This is necessary when the canvas is accessed on a worker-thread.
185
+
186
+ .. warning::
187
+ It's not allowed after pushing any paints.
188
+ """
189
+ self.thorvg_lib.tvg_swcanvas_set_mempool.argtypes = [
190
+ ctypes.POINTER(CanvasStruct),
191
+ ctypes.c_int,
192
+ ]
193
+ self.thorvg_lib.tvg_swcanvas_set_mempool.restype = Result
194
+ return self.thorvg_lib.tvg_swcanvas_set_mempool(
195
+ ctypes.pointer(self._canvas), policy
196
+ )
@@ -0,0 +1,371 @@
1
+ #!/usr/bin/env python3
2
+ import ctypes
3
+ import os
4
+ import sys
5
+ import sysconfig
6
+ from types import TracebackType
7
+ from typing import List, Optional, Tuple, Type
8
+
9
+ from .base import EngineBackend, Result
10
+
11
+
12
+ def _load_lib_with_prefix_suffix(
13
+ lib_prefix: str, lib_suffix: str
14
+ ) -> Optional[ctypes.CDLL]:
15
+ package_dir = os.path.dirname(__file__)
16
+ thorvg_lib_name = lib_prefix + "thorvg" + lib_suffix
17
+ thorvg_lib_path_local = os.path.join(package_dir, thorvg_lib_name)
18
+
19
+ if os.path.isfile(thorvg_lib_path_local):
20
+ thorvg_lib_path = thorvg_lib_path_local
21
+ elif os.path.isfile(thorvg_lib_name):
22
+ thorvg_lib_path = os.path.abspath(thorvg_lib_name)
23
+ else:
24
+ thorvg_lib_path = thorvg_lib_name
25
+
26
+ try:
27
+ return ctypes.cdll.LoadLibrary(thorvg_lib_path)
28
+ except OSError:
29
+ return None
30
+
31
+
32
+ def _load_lib(thorvg_lib_path: Optional[str] = None) -> Optional[ctypes.CDLL]:
33
+ if thorvg_lib_path:
34
+ try:
35
+ return ctypes.cdll.LoadLibrary(thorvg_lib_path)
36
+ except OSError:
37
+ return None
38
+
39
+ if sys.platform.startswith(("win32", "cygwin", "msys", "os2")):
40
+ lib = _load_lib_with_prefix_suffix("", "-0.dll")
41
+ elif sys.platform.startswith("darwin"):
42
+ lib = _load_lib_with_prefix_suffix("lib", ".dylib")
43
+ else:
44
+ lib = _load_lib_with_prefix_suffix("lib", ".so")
45
+
46
+ if lib:
47
+ return lib
48
+
49
+ lib_suffixes: List[str] = []
50
+ shlib_suffix = sysconfig.get_config_var("SHLIB_SUFFIX")
51
+ if isinstance(shlib_suffix, str):
52
+ lib_suffixes.append(shlib_suffix)
53
+ if sys.platform.startswith(("win32", "cygwin", "msys", "os2")):
54
+ lib_prefixes = ("", "lib")
55
+ elif sys.platform.startswith("darwin"):
56
+ lib_prefixes = ("lib", "")
57
+ else:
58
+ lib_prefixes = ("lib", "")
59
+ lib_suffixes.extend([".so", "-0.dll", ".dll", ".dylib"])
60
+
61
+ for lib_prefix in lib_prefixes:
62
+ for lib_suffix in set(lib_suffixes):
63
+ lib = _load_lib_with_prefix_suffix(lib_prefix, lib_suffix)
64
+ if lib:
65
+ return lib
66
+
67
+ return None
68
+
69
+
70
+ THORVG_LIB = _load_lib()
71
+
72
+
73
+ class Engine:
74
+ """
75
+ Engine API
76
+
77
+ A module enabling initialization and termination of the TVG engines.
78
+ """
79
+
80
+ def __init__(
81
+ self,
82
+ thorvg_lib_path: Optional[str] = None,
83
+ engine_method: EngineBackend = EngineBackend.SW,
84
+ threads: int = 0,
85
+ ) -> None:
86
+ self.engine_method = engine_method
87
+ self.threads = threads
88
+ self._load_lib(thorvg_lib_path)
89
+ self.init_result = self.init(self.engine_method, threads)
90
+
91
+ def _load_lib(self, thorvg_lib_path: Optional[str] = None) -> None:
92
+ if thorvg_lib_path is None:
93
+ if THORVG_LIB is None:
94
+ raise OSError("Could not load thorvg library")
95
+ else:
96
+ self.thorvg_lib = THORVG_LIB
97
+ return
98
+
99
+ thorvg_lib = _load_lib(thorvg_lib_path)
100
+ if thorvg_lib is None:
101
+ raise OSError(f"Could not load thorvg library from {thorvg_lib_path}")
102
+ else:
103
+ self.thorvg_lib = thorvg_lib
104
+
105
+ def __del__(self) -> None:
106
+ if self.thorvg_lib:
107
+ self.term(self.engine_method)
108
+
109
+ def __enter__(self) -> "Engine":
110
+ return self
111
+
112
+ def __exit__(
113
+ self,
114
+ exc_type: Optional[Type[BaseException]],
115
+ exc_val: Optional[BaseException],
116
+ exc_tb: Optional[TracebackType],
117
+ ) -> None:
118
+ if self.thorvg_lib:
119
+ self.term(self.engine_method)
120
+
121
+ def init(self, engine_method: EngineBackend, threads: int) -> Result:
122
+ """Initializes TVG engines.
123
+
124
+ TVG requires the running-engine environment.
125
+ TVG runs its own task-scheduler for parallelizing rendering tasks efficiently.
126
+ You can indicate the number of threads, the count of which is designated ``threads``.
127
+ In the initialization step, TVG will generate/spawn the threads as set by ``threads`` count.
128
+
129
+ .. code-block:: python
130
+
131
+ from thorvg_python import Engine
132
+ engine = Engine.init(EngineBackend.SW, 0); //Initialize software renderer and use the main thread only
133
+
134
+ :param EngineBackend engine_method: The engine types to initialize. This is relative to the Canvas types, in which it will be used. For multiple backends bitwise operation is allowed.
135
+ - SW: CPU rasterizer
136
+ - GL: OpenGL rasterizer (not supported yet)
137
+ :param int threads: The number of additional threads used to perform rendering. Zero indicates only the main thread is to be used.
138
+
139
+ :return:
140
+ - INVALID_ARGUMENT Unknown engine type.
141
+ - NOT_SUPPORTED Unsupported engine type.
142
+ :rtype: Result
143
+
144
+ .. note::
145
+ The Initializer keeps track of the number of times it was called. Threads count is fixed at the first init() call.
146
+ .. seealso:: Engine.term()
147
+ .. seealso:: EngineBackend
148
+ """
149
+ self.thorvg_lib.tvg_engine_init.argtypes = [ctypes.c_int, ctypes.c_int]
150
+ self.thorvg_lib.tvg_engine_init.restype = Result
151
+ return self.thorvg_lib.tvg_engine_init(engine_method, ctypes.c_int(threads))
152
+
153
+ def term(self, engine_method: Optional[EngineBackend] = None) -> Result:
154
+ """Terminates TVG engines.
155
+
156
+ It should be called in case of termination of the TVG client with the same engine types as were passed when tvg_engine_init() was called.
157
+
158
+ .. code-block:: python
159
+
160
+ from thorvg_python import Engine
161
+ engine = Engine()
162
+ //define canvas and shapes, update shapes, general rendering calls
163
+ engine.tvg_engine_term()
164
+
165
+ :param Optional[EngineBackend] engine_method: The engine types to terminate.
166
+ This is relative to the Canvas types, in which it will be used.
167
+ For multiple backends bitwise operation is allowed.
168
+ If ``None`` is passed, all engine types will be terminated
169
+ - SW: CPU rasterizer
170
+ - GL: OpenGL rasterizer (not supported yet)
171
+
172
+ :return:
173
+ - INSUFFICIENT_CONDITION Nothing to be terminated.
174
+ - INVALID_ARGUMENT Unknown engine type.
175
+ - NOT_SUPPORTED Unsupported engine type.
176
+ :rtype: Result
177
+
178
+ .. seealso:: Engine.init()
179
+ .. seealso:: EngineBackend
180
+ """
181
+ if engine_method is None:
182
+ engine_method = self.engine_method
183
+ self.thorvg_lib.tvg_engine_term.argtypes = [ctypes.c_int]
184
+ self.thorvg_lib.tvg_engine_term.restype = Result
185
+ return self.thorvg_lib.tvg_engine_term(engine_method)
186
+
187
+ def version(self) -> Tuple[Result, int, int, int, Optional[str]]:
188
+ """
189
+ Retrieves the version of the TVG engine.
190
+
191
+ :return: SUCCESS
192
+ :rtype: Result
193
+ :return: A major version number.
194
+ :rtype: int
195
+ :return: A minor version number.
196
+ :rtype: int
197
+ :return: A micro version number.
198
+ :rtype: int
199
+ :return: The version of the engine in the format major.minor.micro, or a ``nullptr`` in case of an internal error.
200
+ :rtype: Optional[str]
201
+
202
+ .. versionadded:: 0.15
203
+ """
204
+ self.thorvg_lib.tvg_engine_version.argtypes = [
205
+ ctypes.POINTER(ctypes.c_uint32),
206
+ ctypes.POINTER(ctypes.c_uint32),
207
+ ctypes.POINTER(ctypes.c_uint32),
208
+ ctypes.POINTER(ctypes.c_char_p),
209
+ ]
210
+ self.thorvg_lib.tvg_engine_version.restype = Result
211
+ major = ctypes.c_uint32()
212
+ minor = ctypes.c_uint32()
213
+ micro = ctypes.c_uint32()
214
+ version = ctypes.c_char_p()
215
+ result = self.thorvg_lib.tvg_engine_version(
216
+ ctypes.pointer(major),
217
+ ctypes.pointer(minor),
218
+ ctypes.pointer(micro),
219
+ ctypes.pointer(version),
220
+ )
221
+ if version.value is not None:
222
+ v = version.value.decode("utf-8")
223
+ else:
224
+ v = None
225
+ return result, major.value, minor.value, micro.value, v
226
+
227
+ def font_load(
228
+ self,
229
+ path: str,
230
+ ) -> Result:
231
+ """Loads a scalable font data from a file.
232
+
233
+ ThorVG efficiently caches the loaded data using the specified ``path`` as a key.
234
+ This means that loading the same file again will not result in duplicate operations;
235
+ instead, ThorVG will reuse the previously loaded font data.
236
+
237
+ :param str path: The path to the font file.
238
+
239
+ :return:
240
+ - INVALID_ARGUMENT An invalid ``path`` passed as an argument.
241
+ - NOT_SUPPORTED When trying to load a file with an unknown extension.
242
+ :rtype: Result
243
+
244
+ .. seealso:: Engine.font_unload()
245
+
246
+ .. versionadded:: 0.15
247
+ """
248
+ path_bytes = path.encode() + b"\x00"
249
+ path_arr_type = ctypes.c_char * len(path_bytes)
250
+ path_arr = path_arr_type.from_buffer_copy(path_bytes)
251
+ self.thorvg_lib.tvg_font_load.argtypes = [
252
+ ctypes.POINTER(path_arr_type),
253
+ ]
254
+ self.thorvg_lib.tvg_font_load.restype = Result
255
+ return self.thorvg_lib.tvg_font_load(ctypes.pointer(path_arr))
256
+
257
+ def font_load_data(
258
+ self,
259
+ name: str,
260
+ data: bytes,
261
+ mimetype: Optional[str],
262
+ copy: bool,
263
+ ) -> Result:
264
+ """Loads a scalable font data from a memory block of a given size.
265
+
266
+ ThorVG efficiently caches the loaded font data using the specified ``name`` as a key.
267
+ This means that loading the same fonts again will not result in duplicate operations.
268
+ Instead, ThorVG will reuse the previously loaded font data.
269
+
270
+ :param str name: The name under which the font will be stored and accessible (e.x. in a ``tvg_text_set_font`` API).
271
+ :param bytes data: A pointer to a memory location where the content of the font data is stored.
272
+ :param str mimetype: Mimetype or extension of font data. In case a ``None`` or an empty "" value is provided the loader will be determined automatically.
273
+ :param bool copy: If ``true`` the data are copied into the engine local buffer, otherwise they are not (default).
274
+
275
+ :return:
276
+ - INVALID_ARGUMENT If no name is provided or if ``size`` is zero while ``data`` points to a valid memory location.
277
+ - NOT_SUPPORTED When trying to load a file with an unknown extension.
278
+ - INSUFFICIENT_CONDITION When trying to unload the font data that has not been previously loaded.
279
+ :rtype: Result
280
+
281
+ .. warning::
282
+ : It's the user responsibility to release the ``data`` memory.
283
+
284
+ .. note::
285
+ To unload the font data loaded using this API, pass the proper ``name`` and ``nullptr`` as ``data``.
286
+
287
+ .. versionadded:: 0.15
288
+ """
289
+ name_bytes = name.encode() + b"\x00"
290
+ name_bytes += b"\x00"
291
+ name_char_type = ctypes.c_char * len(name_bytes)
292
+ name_char = name_char_type.from_buffer_copy(name_bytes)
293
+ data_arr_type = ctypes.c_ubyte * len(data)
294
+ data_arr = data_arr_type.from_buffer_copy(data)
295
+ if mimetype is not None and mimetype != "":
296
+ mimetype_bytes = name.encode() + b"\x00"
297
+ mimetype_char_type = ctypes.c_char * len(mimetype_bytes)
298
+ mimetype_char_ptr_type = ctypes.POINTER(mimetype_char_type)
299
+ mimetype_char = mimetype_char_type.from_buffer_copy(mimetype_bytes)
300
+ mimetype_char_ptr = ctypes.pointer(mimetype_char)
301
+ else:
302
+ mimetype_char_ptr_type = ctypes.c_void_p # type: ignore
303
+ mimetype_char_ptr = ctypes.c_void_p() # type: ignore
304
+ self.thorvg_lib.tvg_picture_load_raw.argtypes = [
305
+ ctypes.POINTER(name_char_type),
306
+ ctypes.POINTER(data_arr_type),
307
+ ctypes.c_uint32,
308
+ mimetype_char_ptr_type,
309
+ ctypes.c_bool,
310
+ ]
311
+ return self.thorvg_lib.tvg_picture_load_raw(
312
+ ctypes.pointer(name_char),
313
+ ctypes.pointer(data_arr),
314
+ ctypes.c_uint32(ctypes.sizeof(data_arr)),
315
+ mimetype_char_ptr,
316
+ ctypes.c_bool(copy),
317
+ )
318
+
319
+ def font_unload(
320
+ self,
321
+ path: str,
322
+ ) -> Result:
323
+ """Unloads the specified scalable font data that was previously loaded.
324
+
325
+ This function is used to release resources associated with a font file that has been loaded into memory.
326
+
327
+ :param str path: The path to the loaded font file.
328
+
329
+ :return: INSUFFICIENT_CONDITION The loader is not initialized.
330
+ :rtype: Result
331
+
332
+ .. note::
333
+ If the font data is currently in use, it will not be immediately unloaded.
334
+ .. seealso:: Engine.font_load()
335
+
336
+ .. versionadded:: 0.15
337
+ """
338
+ path_bytes = path.encode() + b"\x00"
339
+ path_arr_type = ctypes.c_char * len(path_bytes)
340
+ path_arr = path_arr_type.from_buffer_copy(path_bytes)
341
+ self.thorvg_lib.tvg_font_unload.argtypes = [
342
+ ctypes.POINTER(path_arr_type),
343
+ ]
344
+ self.thorvg_lib.tvg_font_unload.restype = Result
345
+ return self.thorvg_lib.tvg_font_unload(ctypes.pointer(path_arr))
346
+
347
+ def accessor_generate_id(
348
+ self,
349
+ name: str,
350
+ ) -> int:
351
+ """Generate a unique ID (hash key) from a given name.
352
+
353
+ This function computes a unique identifier value based on the provided string.
354
+ You can use this to assign a unique ID to the Paint object.
355
+
356
+ :param str name: The input string to generate the unique identifier from.
357
+
358
+ :return: The generated unique identifier value.
359
+ :rtype: int
360
+
361
+ .. note::
362
+ Experimental API
363
+ """
364
+ name_bytes = name.encode() + b"\x00"
365
+ name_char_type = ctypes.c_char * len(name_bytes)
366
+ name_char = name_char_type.from_buffer_copy(name_bytes)
367
+ self.thorvg_lib.tvg_accessor_generate_id.argtypes = [
368
+ ctypes.POINTER(name_char_type)
369
+ ]
370
+ self.thorvg_lib.tvg_accessor_generate_id.restype = ctypes.c_uint32
371
+ return self.thorvg_lib.tvg_accessor_generate_id(ctypes.pointer(name_char)).value