Pillow/Tk/tkImaging.c
Matthew Brett 92272f8195 FIX: search for tkinter first in builtins
Python compiled from Python.org source builds the tkinter module as a
built-in module, not an external module, as is the case for the packaged
builds of Debian etc:

    >>> Tkinter.tkinter
    <module '_tkinter' (built-in)>

This breaks the current algorithm for searching for tkinter symbols,
which loaded the external module .so file to get the symbols.

Try searching in the main program namespace for the tkinter symbols,
before looking for the extermal module .so file.

Thanks to github user ettaka for reporting : see
https://github.com/matplotlib/matplotlib/issues/7428
2016-11-09 09:00:26 -08:00

481 lines
14 KiB
C

/*
* The Python Imaging Library.
* $Id$
*
* TK interface for Python Imaging objects
*
* Copies (parts of) a named display memory to a photo image object.
* Also contains code to create an display memory. Under Tk, a
* display memory is simply an "L" or "RGB" image memory that is
* allocated in a single block.
*
* To use this module, import the _imagingtk module (ImageTk does
* this for you).
*
* If you're using Python in an embedded context, you can add the
* following lines to your Tcl_AppInit function (in tkappinit.c)
* instead. Put them after the calls to Tcl_Init and Tk_Init:
*
* {
* extern void TkImaging_Init(Tcl_Interp* interp);
* TkImaging_Init(interp);
* }
*
* This registers a Tcl command called "PyImagingPhoto", which is used
* to communicate between PIL and Tk's PhotoImage handler.
*
* Compile and link tkImaging.c with tkappinit.c and _tkinter (see the
* Setup file for details on how to use tkappinit.c). Note that
* _tkinter.c must be compiled with WITH_APPINIT.
*
* History:
* 1995-09-12 fl Created
* 1996-04-08 fl Ready for release
* 1997-05-09 fl Use command instead of image type
* 2001-03-18 fl Initialize alpha layer pointer (struct changed in 8.3)
* 2003-04-23 fl Fixed building for Tk 8.4.1 and later (Jack Jansen)
* 2004-06-24 fl Fixed building for Tk 8.4.6 and later.
*
* Copyright (c) 1997-2004 by Secret Labs AB
* Copyright (c) 1995-2004 by Fredrik Lundh
*
* See the README file for information on usage and redistribution.
*/
#include "Imaging.h"
#include "_tkmini.h"
#include <stdlib.h>
/*
* Global vars for Tcl / Tk functions. We load these symbols from the tkinter
* extension module or loaded Tcl / Tk libraries at run-time.
*/
static int TK_LT_85 = 0;
static Tcl_CreateCommand_t TCL_CREATE_COMMAND;
static Tcl_AppendResult_t TCL_APPEND_RESULT;
static Tk_FindPhoto_t TK_FIND_PHOTO;
static Tk_PhotoGetImage_t TK_PHOTO_GET_IMAGE;
static Tk_PhotoPutBlock_84_t TK_PHOTO_PUT_BLOCK_84;
static Tk_PhotoSetSize_84_t TK_PHOTO_SET_SIZE_84;
static Tk_PhotoPutBlock_85_t TK_PHOTO_PUT_BLOCK_85;
static Imaging
ImagingFind(const char* name)
{
Py_ssize_t id;
/* FIXME: use CObject instead? */
#if defined(_MSC_VER) && defined(_WIN64)
id = _atoi64(name);
#else
id = atol(name);
#endif
if (!id)
return NULL;
return (Imaging) id;
}
static int
PyImagingPhotoPut(ClientData clientdata, Tcl_Interp* interp,
int argc, const char **argv)
{
Imaging im;
Tk_PhotoHandle photo;
Tk_PhotoImageBlock block;
if (argc != 3) {
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 PIL Image handle */
im = ImagingFind(argv[2]);
if (!im) {
TCL_APPEND_RESULT(interp, "bad name", (char*) NULL);
return TCL_ERROR;
}
if (!im->block) {
TCL_APPEND_RESULT(interp, "bad display memory", (char*) NULL);
return TCL_ERROR;
}
/* Active region */
#if 0
if (src_xoffset + xsize > im->xsize)
xsize = im->xsize - src_xoffset;
if (src_yoffset + ysize > im->ysize)
ysize = im->ysize - src_yoffset;
if (xsize < 0 || ysize < 0
|| src_xoffset >= im->xsize
|| src_yoffset >= im->ysize)
return TCL_OK;
#endif
/* Mode */
if (strcmp(im->mode, "1") == 0 || strcmp(im->mode, "L") == 0) {
block.pixelSize = 1;
block.offset[0] = block.offset[1] = block.offset[2] = 0;
} else if (strncmp(im->mode, "RGB", 3) == 0) {
block.pixelSize = 4;
block.offset[0] = 0;
block.offset[1] = 1;
block.offset[2] = 2;
if (strcmp(im->mode, "RGBA") == 0)
block.offset[3] = 3; /* alpha (or reserved, under 8.2) */
else
block.offset[3] = 0; /* no alpha */
} else {
TCL_APPEND_RESULT(interp, "Bad mode", (char*) NULL);
return TCL_ERROR;
}
block.width = im->xsize;
block.height = im->ysize;
block.pitch = im->linesize;
block.pixelPtr = (unsigned char*) im->block;
#if 0
block.pixelPtr = (unsigned char*) im->block +
src_yoffset * im->linesize +
src_xoffset * im->pixelsize;
#endif
if (TK_LT_85) { /* Tk 8.4 */
TK_PHOTO_PUT_BLOCK_84(photo, &block, 0, 0, block.width, block.height,
TK_PHOTO_COMPOSITE_SET);
if (strcmp(im->mode, "RGBA") == 0)
/* Tk workaround: we need apply ToggleComplexAlphaIfNeeded */
/* (fixed in Tk 8.5a3) */
TK_PHOTO_SET_SIZE_84(photo, block.width, block.height);
} else {
/* Tk >=8.5 */
TK_PHOTO_PUT_BLOCK_85(interp, photo, &block, 0, 0, block.width,
block.height, TK_PHOTO_COMPOSITE_SET);
}
return TCL_OK;
}
static int
PyImagingPhotoGet(ClientData clientdata, Tcl_Interp* interp,
int argc, const char **argv)
{
Tk_PhotoHandle photo;
Tk_PhotoImageBlock block;
if (argc != 2) {
TCL_APPEND_RESULT(interp, "usage: ", argv[0],
" srcPhoto", (char *) NULL);
return TCL_ERROR;
}
/* get Tcl PhotoImage handle */
photo = TK_FIND_PHOTO(interp, argv[1]);
if (photo == NULL) {
TCL_APPEND_RESULT(
interp, "source photo must exist", (char *) NULL
);
return TCL_ERROR;
}
TK_PHOTO_GET_IMAGE(photo, &block);
printf("pixelPtr = %p\n", block.pixelPtr);
printf("width = %d\n", block.width);
printf("height = %d\n", block.height);
printf("pitch = %d\n", block.pitch);
printf("pixelSize = %d\n", block.pixelSize);
printf("offset = %d %d %d %d\n", block.offset[0], block.offset[1],
block.offset[2], block.offset[3]);
TCL_APPEND_RESULT(
interp, "this function is not yet supported", (char *) NULL
);
return TCL_ERROR;
}
void
TkImaging_Init(Tcl_Interp* interp)
{
TCL_CREATE_COMMAND(interp, "PyImagingPhoto", PyImagingPhotoPut,
(ClientData) 0, (Tcl_CmdDeleteProc*) NULL);
TCL_CREATE_COMMAND(interp, "PyImagingPhotoGet", PyImagingPhotoGet,
(ClientData) 0, (Tcl_CmdDeleteProc*) NULL);
}
/*
* Functions to fill global Tcl / Tk function pointers by dynamic loading
*/
#define TKINTER_FINDER "PIL._tkinter_finder"
#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 <windows.h>
#define PSAPI_VERSION 1
#include <psapi.h>
/* Must be linked with 'psapi' library */
#if PY_VERSION_HEX >= 0x03000000
#define TKINTER_PKG "tkinter"
#else
#define TKINTER_PKG "Tkinter"
#endif
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.
*/
if ((TCL_CREATE_COMMAND = (Tcl_CreateCommand_t)
GetProcAddress(hMod, "Tcl_CreateCommand")) == NULL) {
return 0; /* Maybe not Tcl module */
}
return ((TCL_APPEND_RESULT = (Tcl_AppendResult_t) _dfunc(hMod,
"Tcl_AppendResult")) == 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.
*/
FARPROC func = GetProcAddress(hMod, "Tk_PhotoPutBlock");
if (func == NULL) { /* Maybe not Tk module */
return 0;
}
if ((TK_PHOTO_GET_IMAGE = (Tk_PhotoGetImage_t)
_dfunc(hMod, "Tk_PhotoGetImage")) == NULL) { return -1; };
if ((TK_FIND_PHOTO = (Tk_FindPhoto_t)
_dfunc(hMod, "Tk_FindPhoto")) == NULL) { return -1; };
TK_LT_85 = GetProcAddress(hMod, "Tk_PhotoPutBlock_Panic") == NULL;
/* Tk_PhotoPutBlock_Panic defined as of 8.5.0 */
if (TK_LT_85) {
TK_PHOTO_PUT_BLOCK_84 = (Tk_PhotoPutBlock_84_t) func;
return ((TK_PHOTO_SET_SIZE_84 = (Tk_PhotoSetSize_84_t)
_dfunc(hMod, "Tk_PhotoSetSize")) == NULL) ? -1 : 1;
}
TK_PHOTO_PUT_BLOCK_85 = (Tk_PhotoPutBlock_85_t) func;
return 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;
/* First load tkinter module to make sure libraries are loaded */
PyObject *pModule = PyImport_ImportModule(TKINTER_PKG);
if (pModule == NULL) {
return 1;
}
Py_DECREF(pModule);
/* 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 symbols from the tkinter module, because
* tkinter uses these symbols, and the symbols are therefore visible in the
* tkinter dynamic library (module).
*/
/* From module __file__ attribute to char *string for dlopen. */
#if PY_VERSION_HEX >= 0x03000000
char *fname2char(PyObject *fname)
{
PyObject* bytes;
bytes = PyUnicode_EncodeFSDefault(fname);
if (bytes == NULL) {
return NULL;
}
return PyBytes_AsString(bytes);
}
#else
#define fname2char(s) (PyString_AsString(s))
#endif
#include <dlfcn.h>
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.
*/
if ((TCL_CREATE_COMMAND = (Tcl_CreateCommand_t)
_dfunc(lib, "Tcl_CreateCommand")) == NULL) { return 1; }
if ((TCL_APPEND_RESULT = (Tcl_AppendResult_t) _dfunc(lib,
"Tcl_AppendResult")) == NULL) { return 1; }
if ((TK_PHOTO_GET_IMAGE = (Tk_PhotoGetImage_t)
_dfunc(lib, "Tk_PhotoGetImage")) == NULL) { return 1; }
if ((TK_FIND_PHOTO = (Tk_FindPhoto_t)
_dfunc(lib, "Tk_FindPhoto")) == NULL) { return 1; }
/* Tk_PhotoPutBlock_Panic defined as of 8.5.0 */
TK_LT_85 = (dlsym(lib, "Tk_PhotoPutBlock_Panic") == NULL);
if (TK_LT_85) {
return (((TK_PHOTO_PUT_BLOCK_84 = (Tk_PhotoPutBlock_84_t)
_dfunc(lib, "Tk_PhotoPutBlock")) == NULL) ||
((TK_PHOTO_SET_SIZE_84 = (Tk_PhotoSetSize_84_t)
_dfunc(lib, "Tk_PhotoSetSize")) == NULL));
}
return ((TK_PHOTO_PUT_BLOCK_85 = (Tk_PhotoPutBlock_85_t)
_dfunc(lib, "Tk_PhotoPutBlock")) == 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, *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_FINDER);
if (pModule == NULL) {
goto exit;
}
pString = PyObject_GetAttrString(pModule, "TKINTER_LIB");
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) {
PyErr_SetString(PyExc_RuntimeError,
"Cannot dlopen tkinter module file");
goto exit;
}
ret = _func_loader(tkinter_lib);
/* dlclose probably safe because tkinter has been imported. */
dlclose(tkinter_lib);
exit:
Py_XDECREF(pModule);
Py_XDECREF(pString);
return ret;
}
#endif /* end not Windows */