/* -*- mode: c++; c-basic-offset: 4 -*- */ /* * This code is derived from The Python Imaging Library and is covered * by the PIL license. * * See LICENSE/LICENSE.PIL for details. * */ #include #include #include #include #include // agg:int8u // Include our own excerpts from the Tcl / Tk headers #include "_tkmini.h" #if defined(_MSC_VER) # define IMG_FORMAT "%d %d %Iu" #else # define IMG_FORMAT "%d %d %zu" #endif #define BBOX_FORMAT "%f %f %f %f" typedef struct { PyObject_HEAD Tcl_Interp *interp; } TkappObject; // Global vars for Tcl / Tk functions. We load these symbols from the tkinter // extension module or loaded Tcl / Tk libraries at run-time. static Tcl_CreateCommand_t TCL_CREATE_COMMAND; static Tcl_AppendResult_t TCL_APPEND_RESULT; static Tk_MainWindow_t TK_MAIN_WINDOW; static Tk_FindPhoto_t TK_FIND_PHOTO; static Tk_PhotoPutBlock_NoComposite_t TK_PHOTO_PUT_BLOCK_NO_COMPOSITE; static Tk_PhotoBlank_t TK_PHOTO_BLANK; static int PyAggImagePhoto(ClientData clientdata, Tcl_Interp *interp, int argc, char **argv) { Tk_PhotoHandle photo; Tk_PhotoImageBlock block; // vars for blitting size_t pdata; int wdata, hdata, bbox_parse; float x1, x2, y1, y2; bool has_bbox; agg::int8u *destbuffer, *buffer; int destx, desty, destwidth, destheight, deststride; long mode; long nval; if (TK_MAIN_WINDOW(interp) == NULL) { // Will throw a _tkinter.TclError with "this isn't a Tk application" return TCL_ERROR; } if (argc != 5) { TCL_APPEND_RESULT(interp, "usage: ", argv[0], " destPhoto srcImage", (char *)NULL); return TCL_ERROR; } /* get Tcl PhotoImage handle */ photo = TK_FIND_PHOTO(interp, argv[1]); if (photo == NULL) { TCL_APPEND_RESULT(interp, "destination photo must exist", (char *)NULL); return TCL_ERROR; } /* get buffer from str which is "height width ptr" */ if (sscanf(argv[2], IMG_FORMAT, &hdata, &wdata, &pdata) != 3) { TCL_APPEND_RESULT(interp, "error reading data, expected height width ptr", (char *)NULL); return TCL_ERROR; } buffer = (agg::int8u*)pdata; /* get array mode (0=mono, 1=rgb, 2=rgba) */ mode = atol(argv[3]); if ((mode != 0) && (mode != 1) && (mode != 2)) { TCL_APPEND_RESULT(interp, "illegal image mode", (char *)NULL); return TCL_ERROR; } /* check for bbox/blitting */ bbox_parse = sscanf(argv[4], BBOX_FORMAT, &x1, &x2, &y1, &y2); if (bbox_parse == 4) { has_bbox = true; } else if ((bbox_parse == 1) && (x1 == 0)){ has_bbox = false; } else { TCL_APPEND_RESULT(interp, "illegal bbox", (char *)NULL); return TCL_ERROR; } if (has_bbox) { int srcstride = wdata * 4; destx = (int)x1; desty = (int)(hdata - y2); destwidth = (int)(x2 - x1); destheight = (int)(y2 - y1); deststride = 4 * destwidth; destbuffer = new agg::int8u[deststride * destheight]; if (destbuffer == NULL) { TCL_APPEND_RESULT(interp, "could not allocate memory", (char *)NULL); return TCL_ERROR; } for (int i = 0; i < destheight; ++i) { memcpy(destbuffer + (deststride * i), &buffer[(i + desty) * srcstride + (destx * 4)], deststride); } } else { destbuffer = NULL; destx = desty = destwidth = destheight = deststride = 0; } /* setup tkblock */ block.pixelSize = 1; if (mode == 0) { block.offset[0] = block.offset[1] = block.offset[2] = 0; nval = 1; } else { block.offset[0] = 0; block.offset[1] = 1; block.offset[2] = 2; if (mode == 1) { block.offset[3] = 0; block.pixelSize = 3; nval = 3; } else { block.offset[3] = 3; block.pixelSize = 4; nval = 4; } } if (has_bbox) { block.width = destwidth; block.height = destheight; block.pitch = deststride; block.pixelPtr = destbuffer; TK_PHOTO_PUT_BLOCK_NO_COMPOSITE(photo, &block, destx, desty, destwidth, destheight); delete[] destbuffer; } else { block.width = wdata; block.height = hdata; block.pitch = (int)block.width * nval; block.pixelPtr = buffer; /* Clear current contents */ TK_PHOTO_BLANK(photo); /* Copy opaque block to photo image, and leave the rest to TK */ TK_PHOTO_PUT_BLOCK_NO_COMPOSITE(photo, &block, 0, 0, block.width, block.height); } return TCL_OK; } static PyObject *_tkinit(PyObject *self, PyObject *args) { Tcl_Interp *interp; TkappObject *app; PyObject *arg; int is_interp; if (!PyArg_ParseTuple(args, "Oi", &arg, &is_interp)) { return NULL; } if (is_interp) { interp = (Tcl_Interp *)PyLong_AsVoidPtr(arg); } else { /* Do it the hard way. This will break if the TkappObject layout changes */ app = (TkappObject *)arg; interp = app->interp; } /* This will bomb if interp is invalid... */ TCL_CREATE_COMMAND(interp, "PyAggImagePhoto", (Tcl_CmdProc *)PyAggImagePhoto, (ClientData)0, (Tcl_CmdDeleteProc *)NULL); Py_INCREF(Py_None); return Py_None; } static PyMethodDef functions[] = { /* Tkinter interface stuff */ { "tkinit", (PyCFunction)_tkinit, 1 }, { NULL, NULL } /* sentinel */ }; // Functions to fill global TCL / Tk function pointers by dynamic loading #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) /* * On Windows, we can't load the tkinter module to get the TCL or Tk symbols, * because Windows does not load symbols into the library name-space of * importing modules. So, knowing that tkinter has already been imported by * Python, we scan all modules in the running process for the TCL and Tk * function names. */ #include #define PSAPI_VERSION 1 #include // Must be linked with 'psapi' library FARPROC _dfunc(HMODULE lib_handle, const char *func_name) { // Load function `func_name` from `lib_handle`. // Set Python exception if we can't find `func_name` in `lib_handle`. // Returns function pointer or NULL if not present. char message[100]; FARPROC func = GetProcAddress(lib_handle, func_name); if (func == NULL) { sprintf(message, "Cannot load function %s", func_name); PyErr_SetString(PyExc_RuntimeError, message); } return func; } int get_tcl(HMODULE hMod) { // Try to fill TCL global vars with function pointers. Return 0 for no // functions found, 1 for all functions found, -1 for some but not all // functions found. TCL_CREATE_COMMAND = (Tcl_CreateCommand_t) GetProcAddress(hMod, "Tcl_CreateCommand"); if (TCL_CREATE_COMMAND == NULL) { // Maybe not TCL module return 0; } TCL_APPEND_RESULT = (Tcl_AppendResult_t) _dfunc(hMod, "Tcl_AppendResult"); return (TCL_APPEND_RESULT == NULL) ? -1 : 1; } int get_tk(HMODULE hMod) { // Try to fill Tk global vars with function pointers. Return 0 for no // functions found, 1 for all functions found, -1 for some but not all // functions found. TK_MAIN_WINDOW = (Tk_MainWindow_t) GetProcAddress(hMod, "Tk_MainWindow"); if (TK_MAIN_WINDOW == NULL) { // Maybe not Tk module return 0; } return ( // -1 if any remaining symbols are NULL ((TK_FIND_PHOTO = (Tk_FindPhoto_t) _dfunc(hMod, "Tk_FindPhoto")) == NULL) || ((TK_PHOTO_PUT_BLOCK_NO_COMPOSITE = (Tk_PhotoPutBlock_NoComposite_t) _dfunc(hMod, "Tk_PhotoPutBlock_NoComposite")) == NULL) || ((TK_PHOTO_BLANK = (Tk_PhotoBlank_t) _dfunc(hMod, "Tk_PhotoBlank")) == NULL)) ? -1 : 1; } int load_tkinter_funcs(void) { // Load TCL and Tk functions by searching all modules in current process. // Return 0 for success, non-zero for failure. HMODULE hMods[1024]; HANDLE hProcess; DWORD cbNeeded; unsigned int i; int found_tcl = 0; int found_tk = 0; // Returns pseudo-handle that does not need to be closed hProcess = GetCurrentProcess(); // Iterate through modules in this process looking for TCL / Tk names if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) { for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) { if (!found_tcl) { found_tcl = get_tcl(hMods[i]); if (found_tcl == -1) { return 1; } } if (!found_tk) { found_tk = get_tk(hMods[i]); if (found_tk == -1) { return 1; } } if (found_tcl && found_tk) { return 0; } } } if (found_tcl == 0) { PyErr_SetString(PyExc_RuntimeError, "Could not find TCL routines"); } else { PyErr_SetString(PyExc_RuntimeError, "Could not find Tk routines"); } return 1; } #else // not Windows /* * On Unix, we can get the TCL and Tk synbols from the tkinter module, because * tkinter uses these symbols, and the symbols are therefore visible in the * tkinter dynamic library (module). */ #if PY_MAJOR_VERSION >= 3 #define TKINTER_PKG "tkinter" #define TKINTER_MOD "_tkinter" // From module __file__ attribute to char *string for dlopen. char *fname2char(PyObject *fname) { PyObject* bytes; bytes = PyUnicode_EncodeFSDefault(fname); if (bytes == NULL) { return NULL; } return PyBytes_AsString(bytes); } #else #define TKINTER_PKG "Tkinter" #define TKINTER_MOD "tkinter" // From module __file__ attribute to char *string for dlopen #define fname2char(s) (PyString_AsString(s)) #endif #include void *_dfunc(void *lib_handle, const char *func_name) { // Load function `func_name` from `lib_handle`. // Set Python exception if we can't find `func_name` in `lib_handle`. // Returns function pointer or NULL if not present. void* func; // Reset errors. dlerror(); func = dlsym(lib_handle, func_name); if (func == NULL) { const char *error = dlerror(); PyErr_SetString(PyExc_RuntimeError, error); } return func; } int _func_loader(void *lib) { // Fill global function pointers from dynamic lib. // Return 1 if any pointer is NULL, 0 otherwise. return ( ((TCL_CREATE_COMMAND = (Tcl_CreateCommand_t) _dfunc(lib, "Tcl_CreateCommand")) == NULL) || ((TCL_APPEND_RESULT = (Tcl_AppendResult_t) _dfunc(lib, "Tcl_AppendResult")) == NULL) || ((TK_MAIN_WINDOW = (Tk_MainWindow_t) _dfunc(lib, "Tk_MainWindow")) == NULL) || ((TK_FIND_PHOTO = (Tk_FindPhoto_t) _dfunc(lib, "Tk_FindPhoto")) == NULL) || ((TK_PHOTO_PUT_BLOCK_NO_COMPOSITE = (Tk_PhotoPutBlock_NoComposite_t) _dfunc(lib, "Tk_PhotoPutBlock_NoComposite")) == NULL) || ((TK_PHOTO_BLANK = (Tk_PhotoBlank_t) _dfunc(lib, "Tk_PhotoBlank")) == NULL)); } int load_tkinter_funcs(void) { // Load tkinter global funcs from tkinter compiled module. // Return 0 for success, non-zero for failure. int ret = -1; void *main_program, *tkinter_lib; char *tkinter_libname; PyObject *pModule = NULL, *pSubmodule = NULL, *pString = NULL; // Try loading from the main program namespace first main_program = dlopen(NULL, RTLD_LAZY); if (_func_loader(main_program) == 0) { return 0; } // Clear exception triggered when we didn't find symbols above. PyErr_Clear(); // Now try finding the tkinter compiled module pModule = PyImport_ImportModule(TKINTER_PKG); if (pModule == NULL) { goto exit; } pSubmodule = PyObject_GetAttrString(pModule, TKINTER_MOD); if (pSubmodule == NULL) { goto exit; } pString = PyObject_GetAttrString(pSubmodule, "__file__"); if (pString == NULL) { goto exit; } tkinter_libname = fname2char(pString); if (tkinter_libname == NULL) { goto exit; } tkinter_lib = dlopen(tkinter_libname, RTLD_LAZY); if (tkinter_lib == NULL) { /* Perhaps it is a cffi module, like in PyPy? */ pString = PyObject_GetAttrString(pSubmodule, "tklib_cffi"); if (pString == NULL) { goto fail; } pString = PyObject_GetAttrString(pString, "__file__"); if (pString == NULL) { goto fail; } tkinter_libname = fname2char(pString); if (tkinter_libname == NULL) { goto fail; } tkinter_lib = dlopen(tkinter_libname, RTLD_LAZY); } if (tkinter_lib == NULL) { goto fail; } ret = _func_loader(tkinter_lib); // dlclose probably safe because tkinter has been imported. dlclose(tkinter_lib); goto exit; fail: PyErr_SetString(PyExc_RuntimeError, "Cannot dlopen tkinter module file"); exit: Py_XDECREF(pModule); Py_XDECREF(pSubmodule); Py_XDECREF(pString); return ret; } #endif // end not Windows #if PY_MAJOR_VERSION >= 3 static PyModuleDef _tkagg_module = { PyModuleDef_HEAD_INIT, "_tkagg", "", -1, functions, NULL, NULL, NULL, NULL }; PyMODINIT_FUNC PyInit__tkagg(void) { PyObject *m; m = PyModule_Create(&_tkagg_module); return (load_tkinter_funcs() == 0) ? m : NULL; } #else PyMODINIT_FUNC init_tkagg(void) { Py_InitModule("_tkagg", functions); load_tkinter_funcs(); } #endif