1 /* -*- mode: c++; c-basic-offset: 4 -*- */
2
3 /*
4 * This code is derived from The Python Imaging Library and is covered
5 * by the PIL license.
6 *
7 * See LICENSE/LICENSE.PIL for details.
8 *
9 */
10
11 #include <Python.h>
12 #include <cstdlib>
13 #include <cstdio>
14 #include <sstream>
15
16 #include <agg_basics.h> // agg:int8u
17
18 // Include our own excerpts from the Tcl / Tk headers
19 #include "_tkmini.h"
20
21 #if defined(_MSC_VER)
22 # define IMG_FORMAT "%d %d %Iu"
23 #else
24 # define IMG_FORMAT "%d %d %zu"
25 #endif
26 #define BBOX_FORMAT "%f %f %f %f"
27
28 typedef struct
29 {
30 PyObject_HEAD
31 Tcl_Interp *interp;
32 } TkappObject;
33
34 // Global vars for Tcl / Tk functions. We load these symbols from the tkinter
35 // extension module or loaded Tcl / Tk libraries at run-time.
36 static Tcl_CreateCommand_t TCL_CREATE_COMMAND;
37 static Tcl_AppendResult_t TCL_APPEND_RESULT;
38 static Tk_MainWindow_t TK_MAIN_WINDOW;
39 static Tk_FindPhoto_t TK_FIND_PHOTO;
40 static Tk_PhotoPutBlock_NoComposite_t TK_PHOTO_PUT_BLOCK_NO_COMPOSITE;
41 static Tk_PhotoBlank_t TK_PHOTO_BLANK;
42
PyAggImagePhoto(ClientData clientdata,Tcl_Interp * interp,int argc,char ** argv)43 static int PyAggImagePhoto(ClientData clientdata, Tcl_Interp *interp, int
44 argc, char **argv)
45 {
46 Tk_PhotoHandle photo;
47 Tk_PhotoImageBlock block;
48
49 // vars for blitting
50
51 size_t pdata;
52 int wdata, hdata, bbox_parse;
53 float x1, x2, y1, y2;
54 bool has_bbox;
55 agg::int8u *destbuffer, *buffer;
56 int destx, desty, destwidth, destheight, deststride;
57
58 long mode;
59 long nval;
60 if (TK_MAIN_WINDOW(interp) == NULL) {
61 // Will throw a _tkinter.TclError with "this isn't a Tk application"
62 return TCL_ERROR;
63 }
64
65 if (argc != 5) {
66 TCL_APPEND_RESULT(interp, "usage: ", argv[0], " destPhoto srcImage", (char *)NULL);
67 return TCL_ERROR;
68 }
69
70 /* get Tcl PhotoImage handle */
71 photo = TK_FIND_PHOTO(interp, argv[1]);
72 if (photo == NULL) {
73 TCL_APPEND_RESULT(interp, "destination photo must exist", (char *)NULL);
74 return TCL_ERROR;
75 }
76 /* get buffer from str which is "height width ptr" */
77 if (sscanf(argv[2], IMG_FORMAT, &hdata, &wdata, &pdata) != 3) {
78 TCL_APPEND_RESULT(interp,
79 "error reading data, expected height width ptr",
80 (char *)NULL);
81 return TCL_ERROR;
82 }
83 buffer = (agg::int8u*)pdata;
84
85 /* get array mode (0=mono, 1=rgb, 2=rgba) */
86 mode = atol(argv[3]);
87 if ((mode != 0) && (mode != 1) && (mode != 2)) {
88 TCL_APPEND_RESULT(interp, "illegal image mode", (char *)NULL);
89 return TCL_ERROR;
90 }
91
92 /* check for bbox/blitting */
93 bbox_parse = sscanf(argv[4], BBOX_FORMAT, &x1, &x2, &y1, &y2);
94 if (bbox_parse == 4) {
95 has_bbox = true;
96 }
97 else if ((bbox_parse == 1) && (x1 == 0)){
98 has_bbox = false;
99 } else {
100 TCL_APPEND_RESULT(interp, "illegal bbox", (char *)NULL);
101 return TCL_ERROR;
102 }
103
104 if (has_bbox) {
105 int srcstride = wdata * 4;
106 destx = (int)x1;
107 desty = (int)(hdata - y2);
108 destwidth = (int)(x2 - x1);
109 destheight = (int)(y2 - y1);
110 deststride = 4 * destwidth;
111
112 destbuffer = new agg::int8u[deststride * destheight];
113 if (destbuffer == NULL) {
114 TCL_APPEND_RESULT(interp, "could not allocate memory", (char *)NULL);
115 return TCL_ERROR;
116 }
117
118 for (int i = 0; i < destheight; ++i) {
119 memcpy(destbuffer + (deststride * i),
120 &buffer[(i + desty) * srcstride + (destx * 4)],
121 deststride);
122 }
123 } else {
124 destbuffer = NULL;
125 destx = desty = destwidth = destheight = deststride = 0;
126 }
127
128 /* setup tkblock */
129 block.pixelSize = 1;
130 if (mode == 0) {
131 block.offset[0] = block.offset[1] = block.offset[2] = 0;
132 nval = 1;
133 } else {
134 block.offset[0] = 0;
135 block.offset[1] = 1;
136 block.offset[2] = 2;
137 if (mode == 1) {
138 block.offset[3] = 0;
139 block.pixelSize = 3;
140 nval = 3;
141 } else {
142 block.offset[3] = 3;
143 block.pixelSize = 4;
144 nval = 4;
145 }
146 }
147
148 if (has_bbox) {
149 block.width = destwidth;
150 block.height = destheight;
151 block.pitch = deststride;
152 block.pixelPtr = destbuffer;
153
154 TK_PHOTO_PUT_BLOCK_NO_COMPOSITE(photo, &block, destx, desty,
155 destwidth, destheight);
156 delete[] destbuffer;
157
158 } else {
159 block.width = wdata;
160 block.height = hdata;
161 block.pitch = (int)block.width * nval;
162 block.pixelPtr = buffer;
163
164 /* Clear current contents */
165 TK_PHOTO_BLANK(photo);
166 /* Copy opaque block to photo image, and leave the rest to TK */
167 TK_PHOTO_PUT_BLOCK_NO_COMPOSITE(photo, &block, 0, 0, block.width,
168 block.height);
169 }
170
171 return TCL_OK;
172 }
173
_tkinit(PyObject * self,PyObject * args)174 static PyObject *_tkinit(PyObject *self, PyObject *args)
175 {
176 Tcl_Interp *interp;
177 TkappObject *app;
178
179 PyObject *arg;
180 int is_interp;
181 if (!PyArg_ParseTuple(args, "Oi", &arg, &is_interp)) {
182 return NULL;
183 }
184
185 if (is_interp) {
186 interp = (Tcl_Interp *)PyLong_AsVoidPtr(arg);
187 } else {
188 /* Do it the hard way. This will break if the TkappObject
189 layout changes */
190 app = (TkappObject *)arg;
191 interp = app->interp;
192 }
193
194 /* This will bomb if interp is invalid... */
195
196 TCL_CREATE_COMMAND(interp,
197 "PyAggImagePhoto",
198 (Tcl_CmdProc *)PyAggImagePhoto,
199 (ClientData)0,
200 (Tcl_CmdDeleteProc *)NULL);
201
202 Py_INCREF(Py_None);
203 return Py_None;
204 }
205
206 static PyMethodDef functions[] = {
207 /* Tkinter interface stuff */
208 { "tkinit", (PyCFunction)_tkinit, 1 },
209 { NULL, NULL } /* sentinel */
210 };
211
212 // Functions to fill global TCL / Tk function pointers by dynamic loading
213 #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
214
215 /*
216 * On Windows, we can't load the tkinter module to get the TCL or Tk symbols,
217 * because Windows does not load symbols into the library name-space of
218 * importing modules. So, knowing that tkinter has already been imported by
219 * Python, we scan all modules in the running process for the TCL and Tk
220 * function names.
221 */
222 #include <windows.h>
223 #define PSAPI_VERSION 1
224 #include <psapi.h>
225 // Must be linked with 'psapi' library
226
_dfunc(HMODULE lib_handle,const char * func_name)227 FARPROC _dfunc(HMODULE lib_handle, const char *func_name)
228 {
229 // Load function `func_name` from `lib_handle`.
230 // Set Python exception if we can't find `func_name` in `lib_handle`.
231 // Returns function pointer or NULL if not present.
232
233 char message[100];
234
235 FARPROC func = GetProcAddress(lib_handle, func_name);
236 if (func == NULL) {
237 sprintf(message, "Cannot load function %s", func_name);
238 PyErr_SetString(PyExc_RuntimeError, message);
239 }
240 return func;
241 }
242
get_tcl(HMODULE hMod)243 int get_tcl(HMODULE hMod)
244 {
245 // Try to fill TCL global vars with function pointers. Return 0 for no
246 // functions found, 1 for all functions found, -1 for some but not all
247 // functions found.
248 TCL_CREATE_COMMAND = (Tcl_CreateCommand_t)
249 GetProcAddress(hMod, "Tcl_CreateCommand");
250 if (TCL_CREATE_COMMAND == NULL) { // Maybe not TCL module
251 return 0;
252 }
253 TCL_APPEND_RESULT = (Tcl_AppendResult_t) _dfunc(hMod,
254 "Tcl_AppendResult");
255 return (TCL_APPEND_RESULT == NULL) ? -1 : 1;
256 }
257
get_tk(HMODULE hMod)258 int get_tk(HMODULE hMod)
259 {
260 // Try to fill Tk global vars with function pointers. Return 0 for no
261 // functions found, 1 for all functions found, -1 for some but not all
262 // functions found.
263 TK_MAIN_WINDOW = (Tk_MainWindow_t)
264 GetProcAddress(hMod, "Tk_MainWindow");
265 if (TK_MAIN_WINDOW == NULL) { // Maybe not Tk module
266 return 0;
267 }
268 return ( // -1 if any remaining symbols are NULL
269 ((TK_FIND_PHOTO = (Tk_FindPhoto_t)
270 _dfunc(hMod, "Tk_FindPhoto")) == NULL) ||
271 ((TK_PHOTO_PUT_BLOCK_NO_COMPOSITE = (Tk_PhotoPutBlock_NoComposite_t)
272 _dfunc(hMod, "Tk_PhotoPutBlock_NoComposite")) == NULL) ||
273 ((TK_PHOTO_BLANK = (Tk_PhotoBlank_t)
274 _dfunc(hMod, "Tk_PhotoBlank")) == NULL))
275 ? -1 : 1;
276 }
277
load_tkinter_funcs(void)278 int load_tkinter_funcs(void)
279 {
280 // Load TCL and Tk functions by searching all modules in current process.
281 // Return 0 for success, non-zero for failure.
282
283 HMODULE hMods[1024];
284 HANDLE hProcess;
285 DWORD cbNeeded;
286 unsigned int i;
287 int found_tcl = 0;
288 int found_tk = 0;
289
290 // Returns pseudo-handle that does not need to be closed
291 hProcess = GetCurrentProcess();
292
293 // Iterate through modules in this process looking for TCL / Tk names
294 if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) {
295 for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) {
296 if (!found_tcl) {
297 found_tcl = get_tcl(hMods[i]);
298 if (found_tcl == -1) {
299 return 1;
300 }
301 }
302 if (!found_tk) {
303 found_tk = get_tk(hMods[i]);
304 if (found_tk == -1) {
305 return 1;
306 }
307 }
308 if (found_tcl && found_tk) {
309 return 0;
310 }
311 }
312 }
313
314 if (found_tcl == 0) {
315 PyErr_SetString(PyExc_RuntimeError, "Could not find TCL routines");
316 } else {
317 PyErr_SetString(PyExc_RuntimeError, "Could not find Tk routines");
318 }
319 return 1;
320 }
321
322 #else // not Windows
323
324 /*
325 * On Unix, we can get the TCL and Tk synbols from the tkinter module, because
326 * tkinter uses these symbols, and the symbols are therefore visible in the
327 * tkinter dynamic library (module).
328 */
329 #if PY_MAJOR_VERSION >= 3
330 #define TKINTER_PKG "tkinter"
331 #define TKINTER_MOD "_tkinter"
332 // From module __file__ attribute to char *string for dlopen.
fname2char(PyObject * fname)333 char *fname2char(PyObject *fname)
334 {
335 PyObject* bytes;
336 bytes = PyUnicode_EncodeFSDefault(fname);
337 if (bytes == NULL) {
338 return NULL;
339 }
340 return PyBytes_AsString(bytes);
341 }
342 #else
343 #define TKINTER_PKG "Tkinter"
344 #define TKINTER_MOD "tkinter"
345 // From module __file__ attribute to char *string for dlopen
346 #define fname2char(s) (PyString_AsString(s))
347 #endif
348
349 #include <dlfcn.h>
350
_dfunc(void * lib_handle,const char * func_name)351 void *_dfunc(void *lib_handle, const char *func_name)
352 {
353 // Load function `func_name` from `lib_handle`.
354 // Set Python exception if we can't find `func_name` in `lib_handle`.
355 // Returns function pointer or NULL if not present.
356
357 void* func;
358 // Reset errors.
359 dlerror();
360 func = dlsym(lib_handle, func_name);
361 if (func == NULL) {
362 const char *error = dlerror();
363 PyErr_SetString(PyExc_RuntimeError, error);
364 }
365 return func;
366 }
367
_func_loader(void * lib)368 int _func_loader(void *lib)
369 {
370 // Fill global function pointers from dynamic lib.
371 // Return 1 if any pointer is NULL, 0 otherwise.
372 return (
373 ((TCL_CREATE_COMMAND = (Tcl_CreateCommand_t)
374 _dfunc(lib, "Tcl_CreateCommand")) == NULL) ||
375 ((TCL_APPEND_RESULT = (Tcl_AppendResult_t)
376 _dfunc(lib, "Tcl_AppendResult")) == NULL) ||
377 ((TK_MAIN_WINDOW = (Tk_MainWindow_t)
378 _dfunc(lib, "Tk_MainWindow")) == NULL) ||
379 ((TK_FIND_PHOTO = (Tk_FindPhoto_t)
380 _dfunc(lib, "Tk_FindPhoto")) == NULL) ||
381 ((TK_PHOTO_PUT_BLOCK_NO_COMPOSITE = (Tk_PhotoPutBlock_NoComposite_t)
382 _dfunc(lib, "Tk_PhotoPutBlock_NoComposite")) == NULL) ||
383 ((TK_PHOTO_BLANK = (Tk_PhotoBlank_t)
384 _dfunc(lib, "Tk_PhotoBlank")) == NULL));
385 }
386
load_tkinter_funcs(void)387 int load_tkinter_funcs(void)
388 {
389 // Load tkinter global funcs from tkinter compiled module.
390 // Return 0 for success, non-zero for failure.
391 int ret = -1;
392 void *main_program, *tkinter_lib;
393 char *tkinter_libname;
394 PyObject *pModule = NULL, *pSubmodule = NULL, *pString = NULL;
395
396 // Try loading from the main program namespace first
397 main_program = dlopen(NULL, RTLD_LAZY);
398 if (_func_loader(main_program) == 0) {
399 return 0;
400 }
401 // Clear exception triggered when we didn't find symbols above.
402 PyErr_Clear();
403
404 // Now try finding the tkinter compiled module
405 pModule = PyImport_ImportModule(TKINTER_PKG);
406 if (pModule == NULL) {
407 goto exit;
408 }
409 pSubmodule = PyObject_GetAttrString(pModule, TKINTER_MOD);
410 if (pSubmodule == NULL) {
411 goto exit;
412 }
413 pString = PyObject_GetAttrString(pSubmodule, "__file__");
414 if (pString == NULL) {
415 goto exit;
416 }
417 tkinter_libname = fname2char(pString);
418 if (tkinter_libname == NULL) {
419 goto exit;
420 }
421 tkinter_lib = dlopen(tkinter_libname, RTLD_LAZY);
422 if (tkinter_lib == NULL) {
423 /* Perhaps it is a cffi module, like in PyPy? */
424 pString = PyObject_GetAttrString(pSubmodule, "tklib_cffi");
425 if (pString == NULL) {
426 goto fail;
427 }
428 pString = PyObject_GetAttrString(pString, "__file__");
429 if (pString == NULL) {
430 goto fail;
431 }
432 tkinter_libname = fname2char(pString);
433 if (tkinter_libname == NULL) {
434 goto fail;
435 }
436 tkinter_lib = dlopen(tkinter_libname, RTLD_LAZY);
437 }
438 if (tkinter_lib == NULL) {
439 goto fail;
440 }
441 ret = _func_loader(tkinter_lib);
442 // dlclose probably safe because tkinter has been imported.
443 dlclose(tkinter_lib);
444 goto exit;
445 fail:
446 PyErr_SetString(PyExc_RuntimeError,
447 "Cannot dlopen tkinter module file");
448 exit:
449 Py_XDECREF(pModule);
450 Py_XDECREF(pSubmodule);
451 Py_XDECREF(pString);
452 return ret;
453 }
454 #endif // end not Windows
455
456 #if PY_MAJOR_VERSION >= 3
457 static PyModuleDef _tkagg_module = { PyModuleDef_HEAD_INIT, "_tkagg", "", -1, functions,
458 NULL, NULL, NULL, NULL };
459
PyInit__tkagg(void)460 PyMODINIT_FUNC PyInit__tkagg(void)
461 {
462 PyObject *m;
463
464 m = PyModule_Create(&_tkagg_module);
465
466 return (load_tkinter_funcs() == 0) ? m : NULL;
467 }
468 #else
init_tkagg(void)469 PyMODINIT_FUNC init_tkagg(void)
470 {
471 Py_InitModule("_tkagg", functions);
472
473 load_tkinter_funcs();
474 }
475 #endif
476