mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-25 17:36:18 +03:00
f3565f3300
New Tk code that picks up libraries at run-time causes complaints and errors; fix. See: https://github.com/python-pillow/Pillow/issues/2017
472 lines
14 KiB
C
472 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 *tkinter_lib;
|
|
char *tkinter_libname;
|
|
PyObject *pModule = NULL, *pString = NULL;
|
|
|
|
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 */
|