mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-10-30 23:47:27 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			944 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			944 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * The Python Imaging Library.
 | |
|  *
 | |
|  * display support (and other windows-related stuff)
 | |
|  *
 | |
|  * History:
 | |
|  * 1996-05-13 fl  Windows DIB support
 | |
|  * 1996-05-21 fl  Added palette stuff
 | |
|  * 1996-05-28 fl  Added display_mode stuff
 | |
|  * 1997-09-21 fl  Added draw primitive
 | |
|  * 2001-09-17 fl  Added ImagingGrabScreen (from _grabscreen.c)
 | |
|  * 2002-05-12 fl  Added ImagingListWindows
 | |
|  * 2002-11-19 fl  Added clipboard support
 | |
|  * 2002-11-25 fl  Added GetDC/ReleaseDC helpers
 | |
|  * 2003-05-21 fl  Added create window support (including window callback)
 | |
|  * 2003-09-05 fl  Added fromstring/tostring methods
 | |
|  * 2009-03-14 fl  Added WMF support (from pilwmf)
 | |
|  *
 | |
|  * Copyright (c) 1997-2003 by Secret Labs AB.
 | |
|  * Copyright (c) 1996-1997 by Fredrik Lundh.
 | |
|  *
 | |
|  * See the README file for information on usage and redistribution.
 | |
|  */
 | |
| 
 | |
| #define PY_SSIZE_T_CLEAN
 | |
| #include "Python.h"
 | |
| 
 | |
| #include "libImaging/Imaging.h"
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /* Windows DIB support */
 | |
| 
 | |
| #ifdef _WIN32
 | |
| 
 | |
| #include "libImaging/ImDib.h"
 | |
| 
 | |
| #if SIZEOF_VOID_P == 8
 | |
| #define F_HANDLE "K"
 | |
| #else
 | |
| #define F_HANDLE "k"
 | |
| #endif
 | |
| 
 | |
| typedef struct {
 | |
|     PyObject_HEAD ImagingDIB dib;
 | |
| } ImagingDisplayObject;
 | |
| 
 | |
| static PyTypeObject ImagingDisplayType;
 | |
| 
 | |
| static ImagingDisplayObject *
 | |
| _new(const char *mode, int xsize, int ysize) {
 | |
|     ImagingDisplayObject *display;
 | |
| 
 | |
|     if (PyType_Ready(&ImagingDisplayType) < 0) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     display = PyObject_New(ImagingDisplayObject, &ImagingDisplayType);
 | |
|     if (display == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     display->dib = ImagingNewDIB(mode, xsize, ysize);
 | |
|     if (!display->dib) {
 | |
|         Py_DECREF(display);
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     return display;
 | |
| }
 | |
| 
 | |
| static void
 | |
| _delete(ImagingDisplayObject *display) {
 | |
|     if (display->dib) {
 | |
|         ImagingDeleteDIB(display->dib);
 | |
|     }
 | |
|     PyObject_Del(display);
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| _expose(ImagingDisplayObject *display, PyObject *args) {
 | |
|     HDC hdc;
 | |
|     if (!PyArg_ParseTuple(args, F_HANDLE, &hdc)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     ImagingExposeDIB(display->dib, hdc);
 | |
| 
 | |
|     Py_RETURN_NONE;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| _draw(ImagingDisplayObject *display, PyObject *args) {
 | |
|     HDC hdc;
 | |
|     int dst[4];
 | |
|     int src[4];
 | |
|     if (!PyArg_ParseTuple(
 | |
|             args,
 | |
|             F_HANDLE "(iiii)(iiii)",
 | |
|             &hdc,
 | |
|             dst + 0,
 | |
|             dst + 1,
 | |
|             dst + 2,
 | |
|             dst + 3,
 | |
|             src + 0,
 | |
|             src + 1,
 | |
|             src + 2,
 | |
|             src + 3
 | |
|         )) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     ImagingDrawDIB(display->dib, hdc, dst, src);
 | |
| 
 | |
|     Py_RETURN_NONE;
 | |
| }
 | |
| 
 | |
| extern Imaging
 | |
| PyImaging_AsImaging(PyObject *op);
 | |
| 
 | |
| static PyObject *
 | |
| _paste(ImagingDisplayObject *display, PyObject *args) {
 | |
|     Imaging im;
 | |
| 
 | |
|     PyObject *op;
 | |
|     int xy[4];
 | |
|     xy[0] = xy[1] = xy[2] = xy[3] = 0;
 | |
|     if (!PyArg_ParseTuple(args, "O|(iiii)", &op, xy + 0, xy + 1, xy + 2, xy + 3)) {
 | |
|         return NULL;
 | |
|     }
 | |
|     im = PyImaging_AsImaging(op);
 | |
|     if (!im) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     if (xy[2] <= xy[0]) {
 | |
|         xy[2] = xy[0] + im->xsize;
 | |
|     }
 | |
|     if (xy[3] <= xy[1]) {
 | |
|         xy[3] = xy[1] + im->ysize;
 | |
|     }
 | |
| 
 | |
|     ImagingPasteDIB(display->dib, im, xy);
 | |
| 
 | |
|     Py_RETURN_NONE;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| _query_palette(ImagingDisplayObject *display, PyObject *args) {
 | |
|     HDC hdc;
 | |
|     int status;
 | |
| 
 | |
|     if (!PyArg_ParseTuple(args, F_HANDLE, &hdc)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     status = ImagingQueryPaletteDIB(display->dib, hdc);
 | |
| 
 | |
|     return Py_BuildValue("i", status);
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| _getdc(ImagingDisplayObject *display, PyObject *args) {
 | |
|     HWND window;
 | |
|     HDC dc;
 | |
| 
 | |
|     if (!PyArg_ParseTuple(args, F_HANDLE, &window)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     dc = GetDC(window);
 | |
|     if (!dc) {
 | |
|         PyErr_SetString(PyExc_OSError, "cannot create dc");
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     return Py_BuildValue(F_HANDLE, dc);
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| _releasedc(ImagingDisplayObject *display, PyObject *args) {
 | |
|     HWND window;
 | |
|     HDC dc;
 | |
| 
 | |
|     if (!PyArg_ParseTuple(args, F_HANDLE F_HANDLE, &window, &dc)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     ReleaseDC(window, dc);
 | |
| 
 | |
|     Py_RETURN_NONE;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| _frombytes(ImagingDisplayObject *display, PyObject *args) {
 | |
|     Py_buffer buffer;
 | |
| 
 | |
|     if (!PyArg_ParseTuple(args, "y*:frombytes", &buffer)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     if (display->dib->ysize * display->dib->linesize != buffer.len) {
 | |
|         PyBuffer_Release(&buffer);
 | |
|         PyErr_SetString(PyExc_ValueError, "wrong size");
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     memcpy(display->dib->bits, buffer.buf, buffer.len);
 | |
| 
 | |
|     PyBuffer_Release(&buffer);
 | |
|     Py_RETURN_NONE;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| _tobytes(ImagingDisplayObject *display, PyObject *args) {
 | |
|     if (!PyArg_ParseTuple(args, ":tobytes")) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     return PyBytes_FromStringAndSize(
 | |
|         display->dib->bits, display->dib->ysize * display->dib->linesize
 | |
|     );
 | |
| }
 | |
| 
 | |
| static struct PyMethodDef methods[] = {
 | |
|     {"draw", (PyCFunction)_draw, METH_VARARGS},
 | |
|     {"expose", (PyCFunction)_expose, METH_VARARGS},
 | |
|     {"paste", (PyCFunction)_paste, METH_VARARGS},
 | |
|     {"query_palette", (PyCFunction)_query_palette, METH_VARARGS},
 | |
|     {"getdc", (PyCFunction)_getdc, METH_VARARGS},
 | |
|     {"releasedc", (PyCFunction)_releasedc, METH_VARARGS},
 | |
|     {"frombytes", (PyCFunction)_frombytes, METH_VARARGS},
 | |
|     {"tobytes", (PyCFunction)_tobytes, METH_VARARGS},
 | |
|     {NULL, NULL} /* sentinel */
 | |
| };
 | |
| 
 | |
| static PyObject *
 | |
| _getattr_mode(ImagingDisplayObject *self, void *closure) {
 | |
|     return Py_BuildValue("s", self->dib->mode);
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| _getattr_size(ImagingDisplayObject *self, void *closure) {
 | |
|     return Py_BuildValue("ii", self->dib->xsize, self->dib->ysize);
 | |
| }
 | |
| 
 | |
| static struct PyGetSetDef getsetters[] = {
 | |
|     {"mode", (getter)_getattr_mode}, {"size", (getter)_getattr_size}, {NULL}
 | |
| };
 | |
| 
 | |
| static PyTypeObject ImagingDisplayType = {
 | |
|     PyVarObject_HEAD_INIT(NULL, 0).tp_name = "ImagingDisplay",
 | |
|     .tp_basicsize = sizeof(ImagingDisplayObject),
 | |
|     .tp_dealloc = (destructor)_delete,
 | |
|     .tp_methods = methods,
 | |
|     .tp_getset = getsetters,
 | |
| };
 | |
| 
 | |
| PyObject *
 | |
| PyImaging_DisplayWin32(PyObject *self, PyObject *args) {
 | |
|     ImagingDisplayObject *display;
 | |
|     char *mode;
 | |
|     int xsize, ysize;
 | |
| 
 | |
|     if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     display = _new(mode, xsize, ysize);
 | |
|     if (display == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     return (PyObject *)display;
 | |
| }
 | |
| 
 | |
| PyObject *
 | |
| PyImaging_DisplayModeWin32(PyObject *self, PyObject *args) {
 | |
|     char *mode;
 | |
|     int size[2];
 | |
| 
 | |
|     mode = ImagingGetModeDIB(size);
 | |
| 
 | |
|     return Py_BuildValue("s(ii)", mode, size[0], size[1]);
 | |
| }
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /* Windows screen grabber */
 | |
| 
 | |
| typedef HANDLE(__stdcall *Func_GetWindowDpiAwarenessContext)(HANDLE);
 | |
| typedef HANDLE(__stdcall *Func_SetThreadDpiAwarenessContext)(HANDLE);
 | |
| 
 | |
| PyObject *
 | |
| PyImaging_GrabScreenWin32(PyObject *self, PyObject *args) {
 | |
|     int x = 0, y = 0, width = -1, height;
 | |
|     int includeLayeredWindows = 0, screens = 0;
 | |
|     HBITMAP bitmap;
 | |
|     BITMAPCOREHEADER core;
 | |
|     HDC screen, screen_copy;
 | |
|     HWND wnd;
 | |
|     DWORD rop;
 | |
|     PyObject *buffer;
 | |
|     HANDLE dpiAwareness = NULL;
 | |
|     HMODULE user32;
 | |
|     Func_GetWindowDpiAwarenessContext GetWindowDpiAwarenessContext_function;
 | |
|     Func_SetThreadDpiAwarenessContext SetThreadDpiAwarenessContext_function;
 | |
| 
 | |
|     if (!PyArg_ParseTuple(
 | |
|             args, "|ii" F_HANDLE, &includeLayeredWindows, &screens, &wnd
 | |
|         )) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     /* step 1: create a memory DC large enough to hold the
 | |
|        entire screen */
 | |
| 
 | |
|     if (screens == -1) {
 | |
|         screen = GetDC(wnd);
 | |
|         if (screen == NULL) {
 | |
|             PyErr_SetString(PyExc_OSError, "unable to get device context for handle");
 | |
|             return NULL;
 | |
|         }
 | |
|     } else {
 | |
|         screen = CreateDC("DISPLAY", NULL, NULL, NULL);
 | |
|     }
 | |
|     screen_copy = CreateCompatibleDC(screen);
 | |
| 
 | |
|     // added in Windows 10 (1607)
 | |
|     // loaded dynamically to avoid link errors
 | |
|     user32 = LoadLibraryA("User32.dll");
 | |
|     SetThreadDpiAwarenessContext_function = (Func_SetThreadDpiAwarenessContext
 | |
|     )GetProcAddress(user32, "SetThreadDpiAwarenessContext");
 | |
|     if (SetThreadDpiAwarenessContext_function != NULL) {
 | |
|         GetWindowDpiAwarenessContext_function = (Func_GetWindowDpiAwarenessContext
 | |
|         )GetProcAddress(user32, "GetWindowDpiAwarenessContext");
 | |
|         if (screens == -1 && GetWindowDpiAwarenessContext_function != NULL) {
 | |
|             dpiAwareness = GetWindowDpiAwarenessContext_function(wnd);
 | |
|         }
 | |
|         // DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = ((DPI_CONTEXT_HANDLE)-3)
 | |
|         dpiAwareness = SetThreadDpiAwarenessContext_function(
 | |
|             dpiAwareness == NULL ? (HANDLE)-3 : dpiAwareness
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     if (screens == 1) {
 | |
|         x = GetSystemMetrics(SM_XVIRTUALSCREEN);
 | |
|         y = GetSystemMetrics(SM_YVIRTUALSCREEN);
 | |
|         width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
 | |
|         height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
 | |
|     } else if (screens == -1) {
 | |
|         RECT rect;
 | |
|         if (GetClientRect(wnd, &rect)) {
 | |
|             width = rect.right;
 | |
|             height = rect.bottom;
 | |
|         }
 | |
|     } else {
 | |
|         width = GetDeviceCaps(screen, HORZRES);
 | |
|         height = GetDeviceCaps(screen, VERTRES);
 | |
|     }
 | |
| 
 | |
|     if (SetThreadDpiAwarenessContext_function != NULL) {
 | |
|         SetThreadDpiAwarenessContext_function(dpiAwareness);
 | |
|     }
 | |
| 
 | |
|     FreeLibrary(user32);
 | |
| 
 | |
|     if (width == -1) {
 | |
|         goto error;
 | |
|     }
 | |
| 
 | |
|     bitmap = CreateCompatibleBitmap(screen, width, height);
 | |
|     if (!bitmap) {
 | |
|         goto error;
 | |
|     }
 | |
| 
 | |
|     if (!SelectObject(screen_copy, bitmap)) {
 | |
|         goto error;
 | |
|     }
 | |
| 
 | |
|     /* step 2: copy bits into memory DC bitmap */
 | |
| 
 | |
|     rop = SRCCOPY;
 | |
|     if (includeLayeredWindows) {
 | |
|         rop |= CAPTUREBLT;
 | |
|     }
 | |
|     if (!BitBlt(screen_copy, 0, 0, width, height, screen, x, y, rop)) {
 | |
|         goto error;
 | |
|     }
 | |
| 
 | |
|     /* step 3: extract bits from bitmap */
 | |
| 
 | |
|     buffer = PyBytes_FromStringAndSize(NULL, height * ((width * 3 + 3) & -4));
 | |
|     if (!buffer) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     core.bcSize = sizeof(core);
 | |
|     core.bcWidth = width;
 | |
|     core.bcHeight = height;
 | |
|     core.bcPlanes = 1;
 | |
|     core.bcBitCount = 24;
 | |
|     if (!GetDIBits(
 | |
|             screen_copy,
 | |
|             bitmap,
 | |
|             0,
 | |
|             height,
 | |
|             PyBytes_AS_STRING(buffer),
 | |
|             (BITMAPINFO *)&core,
 | |
|             DIB_RGB_COLORS
 | |
|         )) {
 | |
|         goto error;
 | |
|     }
 | |
| 
 | |
|     DeleteObject(bitmap);
 | |
|     DeleteDC(screen_copy);
 | |
|     if (screens == -1) {
 | |
|         ReleaseDC(wnd, screen);
 | |
|     } else {
 | |
|         DeleteDC(screen);
 | |
|     }
 | |
| 
 | |
|     return Py_BuildValue("(ii)(ii)N", x, y, width, height, buffer);
 | |
| 
 | |
| error:
 | |
|     PyErr_SetString(PyExc_OSError, "screen grab failed");
 | |
| 
 | |
|     DeleteDC(screen_copy);
 | |
|     if (screens == -1) {
 | |
|         ReleaseDC(wnd, screen);
 | |
|     } else {
 | |
|         DeleteDC(screen);
 | |
|     }
 | |
| 
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /* Windows clipboard grabber */
 | |
| 
 | |
| PyObject *
 | |
| PyImaging_GrabClipboardWin32(PyObject *self, PyObject *args) {
 | |
|     HANDLE handle = NULL;
 | |
|     int size;
 | |
|     void *data;
 | |
|     PyObject *result;
 | |
|     UINT format;
 | |
|     UINT formats[] = {CF_DIB, CF_DIBV5, CF_HDROP, RegisterClipboardFormatA("PNG"), 0};
 | |
|     LPCSTR format_names[] = {"DIB", "DIB", "file", "png", NULL};
 | |
| 
 | |
|     if (!OpenClipboard(NULL)) {
 | |
|         // Maybe the clipboard is temporarily in use by another process.
 | |
|         // Wait and try again
 | |
|         Sleep(500);
 | |
| 
 | |
|         if (!OpenClipboard(NULL)) {
 | |
|             PyErr_SetString(PyExc_OSError, "failed to open clipboard");
 | |
|             return NULL;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // find best format as set by clipboard owner
 | |
|     format = 0;
 | |
|     while (!handle && (format = EnumClipboardFormats(format))) {
 | |
|         for (UINT i = 0; formats[i] != 0; i++) {
 | |
|             if (format == formats[i]) {
 | |
|                 handle = GetClipboardData(format);
 | |
|                 format = i;
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (!handle) {
 | |
|         CloseClipboard();
 | |
|         return Py_BuildValue("zO", NULL, Py_None);
 | |
|     }
 | |
| 
 | |
|     data = GlobalLock(handle);
 | |
|     size = GlobalSize(handle);
 | |
| 
 | |
|     result = PyBytes_FromStringAndSize(data, size);
 | |
| 
 | |
|     GlobalUnlock(handle);
 | |
|     CloseClipboard();
 | |
| 
 | |
|     return Py_BuildValue("zN", format_names[format], result);
 | |
| }
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /* Windows class */
 | |
| 
 | |
| #ifndef WM_MOUSEWHEEL
 | |
| #define WM_MOUSEWHEEL 522
 | |
| #endif
 | |
| 
 | |
| static int mainloop = 0;
 | |
| 
 | |
| static void
 | |
| callback_error(const char *handler) {
 | |
|     PyObject *sys_stderr;
 | |
| 
 | |
|     sys_stderr = PySys_GetObject("stderr");
 | |
| 
 | |
|     if (sys_stderr) {
 | |
|         PyFile_WriteString("*** ImageWin: error in ", sys_stderr);
 | |
|         PyFile_WriteString((char *)handler, sys_stderr);
 | |
|         PyFile_WriteString(":\n", sys_stderr);
 | |
|     }
 | |
| 
 | |
|     PyErr_Print();
 | |
|     PyErr_Clear();
 | |
| }
 | |
| 
 | |
| static LRESULT CALLBACK
 | |
| windowCallback(HWND wnd, UINT message, WPARAM wParam, LPARAM lParam) {
 | |
|     PAINTSTRUCT ps;
 | |
|     PyObject *callback = NULL;
 | |
|     PyObject *result;
 | |
|     PyThreadState *threadstate;
 | |
|     PyThreadState *current_threadstate;
 | |
|     HDC dc;
 | |
|     RECT rect;
 | |
|     LRESULT status = 0;
 | |
| 
 | |
|     /* set up threadstate for messages that calls back into python */
 | |
|     switch (message) {
 | |
|         case WM_CREATE:
 | |
|             mainloop++;
 | |
|             break;
 | |
|         case WM_DESTROY:
 | |
|             mainloop--;
 | |
|             /* fall through... */
 | |
|         case WM_PAINT:
 | |
|         case WM_SIZE:
 | |
|             callback = (PyObject *)GetWindowLongPtr(wnd, 0);
 | |
|             if (callback) {
 | |
|                 threadstate =
 | |
|                     (PyThreadState *)GetWindowLongPtr(wnd, sizeof(PyObject *));
 | |
|                 current_threadstate = PyThreadState_Swap(NULL);
 | |
|                 PyEval_RestoreThread(threadstate);
 | |
|             } else {
 | |
|                 return DefWindowProc(wnd, message, wParam, lParam);
 | |
|             }
 | |
|     }
 | |
| 
 | |
|     /* process message */
 | |
|     switch (message) {
 | |
|         case WM_PAINT:
 | |
|             /* redraw (part of) window.  this generates a WCK-style
 | |
|                damage/clear/repair cascade */
 | |
|             BeginPaint(wnd, &ps);
 | |
|             dc = GetDC(wnd);
 | |
|             GetWindowRect(wnd, &rect); /* in screen coordinates */
 | |
| 
 | |
|             result = PyObject_CallFunction(
 | |
|                 callback,
 | |
|                 "siiii",
 | |
|                 "damage",
 | |
|                 ps.rcPaint.left,
 | |
|                 ps.rcPaint.top,
 | |
|                 ps.rcPaint.right,
 | |
|                 ps.rcPaint.bottom
 | |
|             );
 | |
|             if (result) {
 | |
|                 Py_DECREF(result);
 | |
|             } else {
 | |
|                 callback_error("window damage callback");
 | |
|             }
 | |
| 
 | |
|             result = PyObject_CallFunction(
 | |
|                 callback,
 | |
|                 "s" F_HANDLE "iiii",
 | |
|                 "clear",
 | |
|                 dc,
 | |
|                 0,
 | |
|                 0,
 | |
|                 rect.right - rect.left,
 | |
|                 rect.bottom - rect.top
 | |
|             );
 | |
|             if (result) {
 | |
|                 Py_DECREF(result);
 | |
|             } else {
 | |
|                 callback_error("window clear callback");
 | |
|             }
 | |
| 
 | |
|             result = PyObject_CallFunction(
 | |
|                 callback,
 | |
|                 "s" F_HANDLE "iiii",
 | |
|                 "repair",
 | |
|                 dc,
 | |
|                 0,
 | |
|                 0,
 | |
|                 rect.right - rect.left,
 | |
|                 rect.bottom - rect.top
 | |
|             );
 | |
|             if (result) {
 | |
|                 Py_DECREF(result);
 | |
|             } else {
 | |
|                 callback_error("window repair callback");
 | |
|             }
 | |
| 
 | |
|             ReleaseDC(wnd, dc);
 | |
|             EndPaint(wnd, &ps);
 | |
|             break;
 | |
| 
 | |
|         case WM_SIZE:
 | |
|             /* resize window */
 | |
|             result = PyObject_CallFunction(
 | |
|                 callback, "sii", "resize", LOWORD(lParam), HIWORD(lParam)
 | |
|             );
 | |
|             if (result) {
 | |
|                 InvalidateRect(wnd, NULL, 1);
 | |
|                 Py_DECREF(result);
 | |
|             } else {
 | |
|                 callback_error("window resize callback");
 | |
|             }
 | |
|             break;
 | |
| 
 | |
|         case WM_DESTROY:
 | |
|             /* destroy window */
 | |
|             result = PyObject_CallFunction(callback, "s", "destroy");
 | |
|             if (result) {
 | |
|                 Py_DECREF(result);
 | |
|             } else {
 | |
|                 callback_error("window destroy callback");
 | |
|             }
 | |
|             Py_DECREF(callback);
 | |
|             break;
 | |
| 
 | |
|         default:
 | |
|             status = DefWindowProc(wnd, message, wParam, lParam);
 | |
|     }
 | |
| 
 | |
|     if (callback) {
 | |
|         /* restore thread state */
 | |
|         PyEval_SaveThread();
 | |
|         PyThreadState_Swap(current_threadstate);
 | |
|     }
 | |
| 
 | |
|     return status;
 | |
| }
 | |
| 
 | |
| PyObject *
 | |
| PyImaging_CreateWindowWin32(PyObject *self, PyObject *args) {
 | |
|     HWND wnd;
 | |
|     WNDCLASS windowClass;
 | |
| 
 | |
|     char *title;
 | |
|     PyObject *callback;
 | |
|     int width = 0, height = 0;
 | |
|     if (!PyArg_ParseTuple(args, "sO|ii", &title, &callback, &width, &height)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     if (width <= 0) {
 | |
|         width = CW_USEDEFAULT;
 | |
|     }
 | |
|     if (height <= 0) {
 | |
|         height = CW_USEDEFAULT;
 | |
|     }
 | |
| 
 | |
|     /* register toplevel window class */
 | |
|     windowClass.style = CS_CLASSDC;
 | |
|     windowClass.cbClsExtra = 0;
 | |
|     windowClass.cbWndExtra = sizeof(PyObject *) + sizeof(PyThreadState *);
 | |
|     windowClass.hInstance = GetModuleHandle(NULL);
 | |
|     /* windowClass.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1); */
 | |
|     windowClass.hbrBackground = NULL;
 | |
|     windowClass.lpszMenuName = NULL;
 | |
|     windowClass.lpszClassName = "pilWindow";
 | |
|     windowClass.lpfnWndProc = windowCallback;
 | |
|     windowClass.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(1));
 | |
|     windowClass.hCursor = LoadCursor(NULL, IDC_ARROW); /* CROSS? */
 | |
| 
 | |
|     RegisterClass(&windowClass); /* FIXME: check return status */
 | |
| 
 | |
|     wnd = CreateWindowEx(
 | |
|         0,
 | |
|         windowClass.lpszClassName,
 | |
|         title,
 | |
|         WS_OVERLAPPEDWINDOW,
 | |
|         CW_USEDEFAULT,
 | |
|         CW_USEDEFAULT,
 | |
|         width,
 | |
|         height,
 | |
|         HWND_DESKTOP,
 | |
|         NULL,
 | |
|         NULL,
 | |
|         NULL
 | |
|     );
 | |
| 
 | |
|     if (!wnd) {
 | |
|         PyErr_SetString(PyExc_OSError, "failed to create window");
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     /* register window callback */
 | |
|     Py_INCREF(callback);
 | |
|     SetWindowLongPtr(wnd, 0, (LONG_PTR)callback);
 | |
|     SetWindowLongPtr(wnd, sizeof(callback), (LONG_PTR)PyThreadState_Get());
 | |
| 
 | |
|     Py_BEGIN_ALLOW_THREADS;
 | |
|     ShowWindow(wnd, SW_SHOWNORMAL);
 | |
|     SetForegroundWindow(wnd); /* to make sure it's visible */
 | |
|     Py_END_ALLOW_THREADS;
 | |
| 
 | |
|     return Py_BuildValue(F_HANDLE, wnd);
 | |
| }
 | |
| 
 | |
| PyObject *
 | |
| PyImaging_EventLoopWin32(PyObject *self, PyObject *args) {
 | |
|     MSG msg;
 | |
| 
 | |
|     Py_BEGIN_ALLOW_THREADS;
 | |
|     while (mainloop && GetMessage(&msg, NULL, 0, 0)) {
 | |
|         TranslateMessage(&msg);
 | |
|         DispatchMessage(&msg);
 | |
|     }
 | |
|     Py_END_ALLOW_THREADS;
 | |
| 
 | |
|     Py_RETURN_NONE;
 | |
| }
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /* windows WMF renderer */
 | |
| 
 | |
| #define GET32(p, o) ((DWORD *)(p + o))[0]
 | |
| 
 | |
| static int CALLBACK
 | |
| enhMetaFileProc(
 | |
|     HDC hdc, HANDLETABLE *lpht, const ENHMETARECORD *lpmr, int nHandles, LPARAM data
 | |
| ) {
 | |
|     PlayEnhMetaFileRecord(hdc, lpht, lpmr, nHandles);
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| PyObject *
 | |
| PyImaging_DrawWmf(PyObject *self, PyObject *args) {
 | |
|     HBITMAP bitmap;
 | |
|     HENHMETAFILE meta;
 | |
|     BITMAPCOREHEADER core;
 | |
|     HDC dc;
 | |
|     RECT rect;
 | |
|     PyObject *buffer = NULL;
 | |
|     void *ptr;
 | |
| 
 | |
|     char *data;
 | |
|     Py_ssize_t datasize;
 | |
|     int width, height;
 | |
|     int x0, y0, x1, y1;
 | |
|     if (!PyArg_ParseTuple(
 | |
|             args,
 | |
|             "y#(ii)(iiii):_load",
 | |
|             &data,
 | |
|             &datasize,
 | |
|             &width,
 | |
|             &height,
 | |
|             &x0,
 | |
|             &x1,
 | |
|             &y0,
 | |
|             &y1
 | |
|         )) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     /* step 1: copy metafile contents into METAFILE object */
 | |
| 
 | |
|     if (datasize > 22 && GET32(data, 0) == 0x9ac6cdd7) {
 | |
|         /* placeable windows metafile (22-byte aldus header) */
 | |
|         meta = SetWinMetaFileBits(datasize - 22, data + 22, NULL, NULL);
 | |
| 
 | |
|     } else if (datasize > 80 && GET32(data, 0) == 1 && GET32(data, 40) == 0x464d4520) {
 | |
|         /* enhanced metafile */
 | |
|         meta = SetEnhMetaFileBits(datasize, data);
 | |
| 
 | |
|     } else {
 | |
|         /* unknown meta format */
 | |
|         meta = NULL;
 | |
|     }
 | |
| 
 | |
|     if (!meta) {
 | |
|         PyErr_SetString(PyExc_OSError, "cannot load metafile");
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     /* step 2: create bitmap */
 | |
| 
 | |
|     core.bcSize = sizeof(core);
 | |
|     core.bcWidth = width;
 | |
|     core.bcHeight = height;
 | |
|     core.bcPlanes = 1;
 | |
|     core.bcBitCount = 24;
 | |
| 
 | |
|     dc = CreateCompatibleDC(NULL);
 | |
| 
 | |
|     bitmap = CreateDIBSection(dc, (BITMAPINFO *)&core, DIB_RGB_COLORS, &ptr, NULL, 0);
 | |
| 
 | |
|     if (!bitmap) {
 | |
|         PyErr_SetString(PyExc_OSError, "cannot create bitmap");
 | |
|         goto error;
 | |
|     }
 | |
| 
 | |
|     if (!SelectObject(dc, bitmap)) {
 | |
|         PyErr_SetString(PyExc_OSError, "cannot select bitmap");
 | |
|         goto error;
 | |
|     }
 | |
| 
 | |
|     /* step 3: render metafile into bitmap */
 | |
| 
 | |
|     rect.left = rect.top = 0;
 | |
|     rect.right = width;
 | |
|     rect.bottom = height;
 | |
| 
 | |
|     /* FIXME: make background transparent? configurable? */
 | |
|     FillRect(dc, &rect, GetStockObject(WHITE_BRUSH));
 | |
| 
 | |
|     EnumEnhMetaFile(dc, meta, enhMetaFileProc, NULL, &rect);
 | |
| 
 | |
|     /* step 4: extract bits from bitmap */
 | |
| 
 | |
|     GdiFlush();
 | |
| 
 | |
|     buffer = PyBytes_FromStringAndSize(ptr, height * ((width * 3 + 3) & -4));
 | |
| 
 | |
| error:
 | |
|     DeleteEnhMetaFile(meta);
 | |
| 
 | |
|     if (bitmap) {
 | |
|         DeleteObject(bitmap);
 | |
|     }
 | |
| 
 | |
|     DeleteDC(dc);
 | |
| 
 | |
|     return buffer;
 | |
| }
 | |
| 
 | |
| #endif /* _WIN32 */
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /* X11 support */
 | |
| 
 | |
| #ifdef HAVE_XCB
 | |
| #include <xcb/xcb.h>
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /* X11 screen grabber */
 | |
| 
 | |
| PyObject *
 | |
| PyImaging_GrabScreenX11(PyObject *self, PyObject *args) {
 | |
|     int width, height;
 | |
|     char *display_name;
 | |
|     xcb_connection_t *connection;
 | |
|     int screen_number;
 | |
|     xcb_screen_iterator_t iter;
 | |
|     xcb_screen_t *screen = NULL;
 | |
|     xcb_get_image_reply_t *reply;
 | |
|     xcb_generic_error_t *error;
 | |
|     PyObject *buffer = NULL;
 | |
| 
 | |
|     if (!PyArg_ParseTuple(args, "|z", &display_name)) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     /* connect to X and get screen data */
 | |
| 
 | |
|     connection = xcb_connect(display_name, &screen_number);
 | |
|     if (xcb_connection_has_error(connection)) {
 | |
|         PyErr_Format(
 | |
|             PyExc_OSError,
 | |
|             "X connection failed: error %i",
 | |
|             xcb_connection_has_error(connection)
 | |
|         );
 | |
|         xcb_disconnect(connection);
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     iter = xcb_setup_roots_iterator(xcb_get_setup(connection));
 | |
|     for (; iter.rem; --screen_number, xcb_screen_next(&iter)) {
 | |
|         if (screen_number == 0) {
 | |
|             screen = iter.data;
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
|     if (screen == NULL || screen->root == 0) {
 | |
|         // this case is usually caught with "X connection failed: error 6" above
 | |
|         xcb_disconnect(connection);
 | |
|         PyErr_SetString(PyExc_OSError, "X screen not found");
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     width = screen->width_in_pixels;
 | |
|     height = screen->height_in_pixels;
 | |
| 
 | |
|     /* get image data */
 | |
| 
 | |
|     reply = xcb_get_image_reply(
 | |
|         connection,
 | |
|         xcb_get_image(
 | |
|             connection,
 | |
|             XCB_IMAGE_FORMAT_Z_PIXMAP,
 | |
|             screen->root,
 | |
|             0,
 | |
|             0,
 | |
|             width,
 | |
|             height,
 | |
|             0x00ffffff
 | |
|         ),
 | |
|         &error
 | |
|     );
 | |
|     if (reply == NULL) {
 | |
|         PyErr_Format(
 | |
|             PyExc_OSError,
 | |
|             "X get_image failed: error %i (%i, %i, %i)",
 | |
|             error->error_code,
 | |
|             error->major_code,
 | |
|             error->minor_code,
 | |
|             error->resource_id
 | |
|         );
 | |
|         free(error);
 | |
|         xcb_disconnect(connection);
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     /* store data in Python buffer */
 | |
| 
 | |
|     if (reply->depth == 24) {
 | |
|         buffer = PyBytes_FromStringAndSize(
 | |
|             (char *)xcb_get_image_data(reply), xcb_get_image_data_length(reply)
 | |
|         );
 | |
|     } else {
 | |
|         PyErr_Format(PyExc_OSError, "unsupported bit depth: %i", reply->depth);
 | |
|     }
 | |
| 
 | |
|     free(reply);
 | |
|     xcb_disconnect(connection);
 | |
| 
 | |
|     if (!buffer) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     return Py_BuildValue("(ii)N", width, height, buffer);
 | |
| }
 | |
| 
 | |
| #endif /* HAVE_XCB */
 |