diff --git a/doc/api/next_api_changes/development/20840-RJS.rst b/doc/api/next_api_changes/development/20840-RJS.rst new file mode 100644 index 000000000000..443331d7c887 --- /dev/null +++ b/doc/api/next_api_changes/development/20840-RJS.rst @@ -0,0 +1,13 @@ +Increase to minimum supported optional dependencies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For Matplotlib 3.5, the :ref:`minimum supported versions of optional dependencies +` are being bumped: + ++------------+-----------------+---------------+ +| Dependency | min in mpl3.4 | min in mpl3.5 | ++============+=================+===============+ +| Tk | 8.3 | 8.4 | ++------------+-----------------+---------------+ + +This is consistent with our :ref:`min_deps_policy` diff --git a/doc/devel/dependencies.rst b/doc/devel/dependencies.rst index 33a35b3dde1a..0a3c40da5524 100644 --- a/doc/devel/dependencies.rst +++ b/doc/devel/dependencies.rst @@ -40,7 +40,7 @@ Matplotlib figures can be rendered to various user interfaces. See :ref:`what-is-a-backend` for more details on the optional Matplotlib backends and the capabilities they provide. -* Tk_ (>= 8.3, != 8.6.0 or 8.6.1) [#]_: for the Tk-based backends. +* Tk_ (>= 8.4, != 8.6.0 or 8.6.1) [#]_: for the Tk-based backends. * PyQt6_ (>= 6.1), PySide6_, PyQt5_, or PySide2_: for the Qt-based backends. * PyGObject_: for the GTK3-based backends [#]_. * wxPython_ (>= 4) [#]_: for the wx-based backends. diff --git a/lib/matplotlib/backends/_backend_tk.py b/lib/matplotlib/backends/_backend_tk.py index 28c5ae737c9b..e07c089676b4 100644 --- a/lib/matplotlib/backends/_backend_tk.py +++ b/lib/matplotlib/backends/_backend_tk.py @@ -53,6 +53,9 @@ def _restore_foreground_window_at_end(): # Initialize to a non-empty string that is not a Tcl command _blit_tcl_name = "mpl_blit_" + uuid.uuid4().hex +TK_PHOTO_COMPOSITE_OVERLAY = 0 # apply transparency rules pixel-wise +TK_PHOTO_COMPOSITE_SET = 1 # set image buffer directly + def _blit(argsid): """ @@ -60,15 +63,10 @@ def _blit(argsid): *argsid* is a unique string identifier to fetch the correct arguments from the ``_blit_args`` dict, since arguments cannot be passed directly. - - photoimage blanking must occur in the same event and thread as blitting - to avoid flickering. """ - photoimage, dataptr, offsets, bboxptr, blank = _blit_args.pop(argsid) - if blank: - photoimage.blank() - _tkagg.blit( - photoimage.tk.interpaddr(), str(photoimage), dataptr, offsets, bboxptr) + photoimage, dataptr, offsets, bboxptr, comp_rule = _blit_args.pop(argsid) + _tkagg.blit(photoimage.tk.interpaddr(), str(photoimage), dataptr, + comp_rule, offsets, bboxptr) def blit(photoimage, aggimage, offsets, bbox=None): @@ -81,7 +79,7 @@ def blit(photoimage, aggimage, offsets, bbox=None): for big-endian ARGB32 (i.e. ARGB8888) data. If *bbox* is passed, it defines the region that gets blitted. That region - will NOT be blanked before blitting. + will be composed with the previous data according to the alpha channel. Tcl events must be dispatched to trigger a blit from a non-Tcl thread. """ @@ -95,10 +93,10 @@ def blit(photoimage, aggimage, offsets, bbox=None): y1 = max(math.floor(y1), 0) y2 = min(math.ceil(y2), height) bboxptr = (x1, x2, y1, y2) - blank = False + comp_rule = TK_PHOTO_COMPOSITE_OVERLAY else: bboxptr = (0, width, 0, height) - blank = True + comp_rule = TK_PHOTO_COMPOSITE_SET # NOTE: _tkagg.blit is thread unsafe and will crash the process if called # from a thread (GH#13293). Instead of blanking and blitting here, @@ -107,7 +105,7 @@ def blit(photoimage, aggimage, offsets, bbox=None): # tkapp.call coerces all arguments to strings, so to avoid string parsing # within _blit, pack up the arguments into a global data structure. - args = photoimage, dataptr, offsets, bboxptr, blank + args = photoimage, dataptr, offsets, bboxptr, comp_rule # Need a unique key to avoid thread races. # Again, make the key a string to avoid string parsing in _blit. argsid = str(id(args)) diff --git a/lib/matplotlib/tests/test_backend_tk.py b/lib/matplotlib/tests/test_backend_tk.py index 4c714b303bd1..7c6a051df9ad 100644 --- a/lib/matplotlib/tests/test_backend_tk.py +++ b/lib/matplotlib/tests/test_backend_tk.py @@ -80,7 +80,7 @@ def test_blit(): # pragma: no cover for bad_box in bad_boxes: try: _tkagg.blit( - photoimage.tk.interpaddr(), str(photoimage), dataptr, + photoimage.tk.interpaddr(), str(photoimage), dataptr, 0, (0, 1, 2, 3), bad_box) except ValueError: print("success") diff --git a/src/_tkagg.cpp b/src/_tkagg.cpp index fc1fe2d82787..8c5ba4a4c800 100644 --- a/src/_tkagg.cpp +++ b/src/_tkagg.cpp @@ -50,7 +50,7 @@ static int convert_voidptr(PyObject *obj, void *p) // Global vars for Tk functions. We load these symbols from the tkinter // extension module or loaded Tk libraries at run-time. static Tk_FindPhoto_t TK_FIND_PHOTO; -static Tk_PhotoPutBlock_NoComposite_t TK_PHOTO_PUT_BLOCK_NO_COMPOSITE; +static Tk_PhotoPutBlock_t TK_PHOTO_PUT_BLOCK; #ifdef WIN32_DLL // Global vars for Tcl functions. We load these symbols from the tkinter // extension module or loaded Tcl libraries at run-time. @@ -63,13 +63,16 @@ static PyObject *mpl_tk_blit(PyObject *self, PyObject *args) char const *photo_name; int height, width; unsigned char *data_ptr; + int comp_rule; + int put_retval; int o0, o1, o2, o3; int x1, x2, y1, y2; Tk_PhotoHandle photo; Tk_PhotoImageBlock block; - if (!PyArg_ParseTuple(args, "O&s(iiO&)(iiii)(iiii):blit", + if (!PyArg_ParseTuple(args, "O&s(iiO&)i(iiii)(iiii):blit", convert_voidptr, &interp, &photo_name, &height, &width, convert_voidptr, &data_ptr, + &comp_rule, &o0, &o1, &o2, &o3, &x1, &x2, &y1, &y2)) { goto exit; @@ -82,7 +85,12 @@ static PyObject *mpl_tk_blit(PyObject *self, PyObject *args) PyErr_SetString(PyExc_ValueError, "Attempting to draw out of bounds"); goto exit; } + if (comp_rule != TK_PHOTO_COMPOSITE_OVERLAY && comp_rule != TK_PHOTO_COMPOSITE_SET) { + PyErr_SetString(PyExc_ValueError, "Invalid comp_rule argument"); + goto exit; + } + Py_BEGIN_ALLOW_THREADS block.pixelPtr = data_ptr + 4 * ((height - y2) * width + x1); block.width = x2 - x1; block.height = y2 - y1; @@ -92,8 +100,13 @@ static PyObject *mpl_tk_blit(PyObject *self, PyObject *args) block.offset[1] = o1; block.offset[2] = o2; block.offset[3] = o3; - TK_PHOTO_PUT_BLOCK_NO_COMPOSITE( - photo, &block, x1, height - y2, x2 - x1, y2 - y1); + put_retval = TK_PHOTO_PUT_BLOCK( + interp, photo, &block, x1, height - y2, x2 - x1, y2 - y1, comp_rule); + Py_END_ALLOW_THREADS + if (put_retval == TCL_ERROR) { + return PyErr_NoMemory(); + } + exit: if (PyErr_Occurred()) { return NULL; @@ -219,8 +232,8 @@ int load_tk(T lib) return !!(TK_FIND_PHOTO = (Tk_FindPhoto_t)dlsym(lib, "Tk_FindPhoto")) + - !!(TK_PHOTO_PUT_BLOCK_NO_COMPOSITE = - (Tk_PhotoPutBlock_NoComposite_t)dlsym(lib, "Tk_PhotoPutBlock_NoComposite")); + !!(TK_PHOTO_PUT_BLOCK = + (Tk_PhotoPutBlock_t)dlsym(lib, "Tk_PhotoPutBlock")); } #ifdef WIN32_DLL @@ -341,8 +354,8 @@ PyMODINIT_FUNC PyInit__tkagg(void) } else if (!TK_FIND_PHOTO) { PyErr_SetString(PyExc_RuntimeError, "Failed to load Tk_FindPhoto"); return NULL; - } else if (!TK_PHOTO_PUT_BLOCK_NO_COMPOSITE) { - PyErr_SetString(PyExc_RuntimeError, "Failed to load Tk_PhotoPutBlock_NoComposite"); + } else if (!TK_PHOTO_PUT_BLOCK) { + PyErr_SetString(PyExc_RuntimeError, "Failed to load Tk_PhotoPutBlock"); return NULL; } return PyModule_Create(&_tkagg_module); diff --git a/src/_tkmini.h b/src/_tkmini.h index e45184166b67..be7c13fa14d5 100644 --- a/src/_tkmini.h +++ b/src/_tkmini.h @@ -86,14 +86,19 @@ typedef struct Tk_PhotoImageBlock int offset[4]; } Tk_PhotoImageBlock; +#define TK_PHOTO_COMPOSITE_OVERLAY 0 // apply transparency rules pixel-wise +#define TK_PHOTO_COMPOSITE_SET 1 // set image buffer directly +#define TCL_OK 0 +#define TCL_ERROR 1 + /* Typedefs derived from function signatures in Tk header */ /* Tk_FindPhoto typedef */ typedef Tk_PhotoHandle (*Tk_FindPhoto_t) (Tcl_Interp *interp, const char *imageName); -/* Tk_PhotoPutBLock_NoComposite typedef */ -typedef void (*Tk_PhotoPutBlock_NoComposite_t) (Tk_PhotoHandle handle, +/* Tk_PhotoPutBLock typedef */ +typedef int (*Tk_PhotoPutBlock_t) (Tcl_Interp *interp, Tk_PhotoHandle handle, Tk_PhotoImageBlock *blockPtr, int x, int y, - int width, int height); + int width, int height, int compRule); #ifdef WIN32_DLL /* Typedefs derived from function signatures in Tcl header */ pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy