/* * Widget Construction Kit * $Id: _windraw.c 2074 2004-09-18 20:48:37Z fredrik $ * * Simple WCK drawing layer for Windows' standard GDI. * * history: * 2004-01-04 fl created (based on uiToolkit code) * 2004-09-05 fl fixed textsize method * * Copyright (c) 1997-2004 by Secret Labs AB * Copyright (c) 1997-2004 by Fredrik Lundh * * info@pythonware.com * http://www.pythonware.com */ /* -------------------------------------------------------------------- * The Widget Construction Kit is * * Copyright (c) 1997-2004 by Secret Labs AB * Copyright (c) 1997-2004 by Fredrik Lundh * * By obtaining, using, and/or copying this software and/or its * associated documentation, you agree that you have read, understood, * and will comply with the following terms and conditions: * * Permission to use, copy, modify, and distribute this software and its * associated documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appears in all * copies, and that both that copyright notice and this permission notice * appear in supporting documentation, and that the name of Secret Labs * AB or the author not be used in advertising or publicity pertaining to * distribution of the software without specific, written prior * permission. * * SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR * ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * -------------------------------------------------------------------- */ #include #include #define WINDOWS_LEAN_AND_MEAN #include "windows.h" #include "Python.h" #include "tk3000.h" #if PY_VERSION_HEX >= 0x01060000 #if PY_VERSION_HEX < 0x02020000 || defined(Py_USING_UNICODE) /* defining this enables unicode support (default under 1.6a1 and later) */ #define HAVE_UNICODE #endif #endif /* -------------------------------------------------------------------- */ /* basic types */ typedef struct { PyObject_HEAD HWND window; HDC dc; HBITMAP bitmap, old_bitmap; int width, height; int xoffset, yoffset; } DrawObject; #define GET_DC(self)\ (self->dc ? self->dc : GetDC(self->window)) #define RELEASE_DC(self, this_dc)\ (self->dc ? 1 : ReleaseDC(self->window, this_dc)) typedef struct { PyObject_HEAD HPEN pen; } PenObject; typedef struct { PyObject_HEAD HBRUSH brush; COLORREF ink; } BrushObject; typedef struct { PyObject_HEAD HFONT font; int height; int ascent; int descent; COLORREF ink; } FontObject; staticforward PyTypeObject DrawType; staticforward PyTypeObject PenType; staticforward PyTypeObject BrushType; staticforward PyTypeObject FontType; #define IS_DRAW(draw)\ ((draw) && (draw)->ob_type == &DrawType) #define IS_PEN(pen)\ ((pen) && (pen)->ob_type == &PenType) #define IS_BRUSH(brush)\ ((brush) && (brush)->ob_type == &BrushType) #define IS_FONT(font)\ ((font) && (font)->ob_type == &FontType) static PyObject* module_getdraw(DrawObject* draw, PyObject* args) { DrawObject* self; long window; if (!PyArg_ParseTuple(args, "l:window", &window)) return NULL; self = PyObject_NEW(DrawObject, &DrawType); if (!self) return NULL; self->dc = NULL; self->bitmap = self->old_bitmap = NULL; self->window = (HWND) window; self->width = self->height = 0; self->xoffset = self->yoffset = 0; return (PyObject*) self; } static DrawObject* draw_getpixmap(DrawObject* draw, int width, int height) { DrawObject* self; HDC parent_dc, dc; HBITMAP bitmap, old_bitmap; self = PyObject_NEW(DrawObject, &DrawType); if (!self) return NULL; parent_dc = GET_DC(draw); dc = CreateCompatibleDC(parent_dc); bitmap = CreateCompatibleBitmap(parent_dc, width, height); old_bitmap = SelectObject(dc, bitmap); RELEASE_DC(draw, parent_dc); self->dc = dc; self->bitmap = bitmap; self->old_bitmap = old_bitmap; self->window = NULL; self->width = width; self->height = height; self->xoffset = self->yoffset = 0; return self; } static void draw_dealloc(DrawObject* self) { if (self->dc) { if (self->bitmap) { SelectObject(self->dc, self->old_bitmap); DeleteObject(self->bitmap); } DeleteDC(self->dc); } PyMem_DEL(self); } /* -------------------------------------------------------------------- */ /* Pen type */ static PenObject* draw_getpen(DrawObject* window, int color, int width) { PenObject* pen; pen = PyObject_NEW(PenObject, &PenType); if (!pen) return NULL; pen->pen = CreatePen(PS_SOLID, width, color); return pen; } static void pen_dealloc(PenObject* pen) { DeleteObject(pen->pen); PyMem_DEL(pen); } static PyObject* pen_getattr(PenObject* pen, char* name) { PyErr_SetString(PyExc_AttributeError, name); return NULL; } statichere PyTypeObject PenType = { PyObject_HEAD_INIT(NULL) 0, "_windraw.Pen", sizeof(PenObject), 0, (destructor) pen_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ (getattrfunc) pen_getattr, /*tp_getattr*/ }; /* -------------------------------------------------------------------- */ /* Brush type */ static BrushObject* draw_getbrush(DrawObject* window, int color) { BrushObject* brush; brush = PyObject_NEW(BrushObject, &BrushType); if (!brush) return NULL; brush->ink = color; brush->brush = CreateSolidBrush(brush->ink); return brush; } static void brush_dealloc(BrushObject* brush) { DeleteObject(brush->brush); PyMem_DEL(brush); } static PyObject* brush_getattr(BrushObject* brush, char* name) { PyErr_SetString(PyExc_AttributeError, name); return NULL; } statichere PyTypeObject BrushType = { PyObject_HEAD_INIT(NULL) 0, "_windraw.Brush", sizeof(BrushObject), 0, (destructor) brush_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ (getattrfunc) brush_getattr, /*tp_getattr*/ }; /* -------------------------------------------------------------------- */ /* Font type */ static FontObject* draw_getfont(DrawObject* self, int color, char* font) { int i, size; LOGFONT lf; TEXTMETRIC tm; HDC dc; HFONT old_font; FontObject* font_obj; font_obj = PyObject_NEW(FontObject, &FontType); if (!font_obj) return NULL; dc = GET_DC(self); memset(&lf, 0, sizeof(LOGFONT)); lf.lfCharSet = ANSI_CHARSET; lf.lfOutPrecision = OUT_TT_PRECIS; lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; if (font[0] == '{') { for (i = 1; font[i] && font[i] != '}'; i++) lf.lfFaceName[i-1] = font[i]; lf.lfFaceName[i-1] = '\0'; i++; } else { for (i = 0; font[i] && font[i] != ' '; i++) lf.lfFaceName[i] = font[i]; lf.lfFaceName[i] = '\0'; } if (!strcmp(lf.lfFaceName, "courier")) lstrcpy(lf.lfFaceName, "Courier New"); else if (!strcmp(lf.lfFaceName, "times")) lstrcpy(lf.lfFaceName, "Times New Roman"); else if (!strcmp(lf.lfFaceName, "helvetica")) lstrcpy(lf.lfFaceName, "Arial"); /* printf("family = %s\n", lf.lfFaceName); */ while (font[i] && font[i] == ' ') i++; size = 0; while (font[i] && isdigit(font[i])) { size = size*10 + font[i] - '0'; i++; } if (!size) size = 12; lf.lfHeight = -MulDiv(size, GetDeviceCaps(dc, LOGPIXELSY), 72); /* printf("size = %d\n", size); */ while (font[i] && font[i] == ' ') i++; lf.lfWeight = FW_NORMAL; /* FIXME: process tokens, not characters */ while (font[i]) { switch (font[i]) { case 'i': lf.lfItalic = 1; break; case 'b': lf.lfWeight = FW_BOLD; break; case 'u': lf.lfUnderline = 1; break; case 's': lf.lfStrikeOut = 1; } i++; } font_obj->font = CreateFontIndirect(&lf); font_obj->ink = color; old_font = SelectObject(dc, font_obj->font); GetTextMetrics(dc, &tm); /* FIXME: store the entire textmetric structure? */ font_obj->height = tm.tmHeight; font_obj->ascent = tm.tmAscent; font_obj->descent = tm.tmDescent; SelectObject(dc, old_font); RELEASE_DC(self, dc); return font_obj; } static void font_dealloc(FontObject* font) { DeleteObject(font->font); PyMem_DEL(font); } static PyObject* font_getattr(FontObject* font, char* name) { if (strcmp(name, "ascent") == 0) return PyInt_FromLong(font->ascent); if (strcmp(name, "descent") == 0) return PyInt_FromLong(font->descent); if (strcmp(name, "height") == 0) return PyInt_FromLong(font->height); PyErr_SetString(PyExc_AttributeError, name); return NULL; } statichere PyTypeObject FontType = { PyObject_HEAD_INIT(NULL) 0, "_windraw.Font", sizeof(FontObject), 0, (destructor) font_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ (getattrfunc) font_getattr, /*tp_getattr*/ }; /* -------------------------------------------------------------------- */ /* Drawing layer */ static int draw_ellipse(DrawObject* self, int x0, int y0, int x1, int y1, PenObject* pen, BrushObject* brush) { HDC dc; HPEN default_pen; HBRUSH default_brush; if (IS_PEN(brush) || IS_BRUSH(pen)) { void* tmp = pen; pen = (void*) brush; brush = tmp; } dc = GET_DC(self); default_pen = SelectObject(dc, IS_PEN(pen) ? pen->pen : GetStockObject(NULL_PEN)); default_brush = SelectObject(dc, IS_BRUSH(brush) ? brush->brush : GetStockObject(NULL_BRUSH)); Ellipse(dc, x0, y0, x1, y1); SelectObject(dc, default_brush); SelectObject(dc, default_pen); RELEASE_DC(self, dc); return 0; } static int draw_lines(DrawObject* self, int* xy, int count, PenObject* pen) { HDC dc; HPEN default_pen; if (IS_PEN(pen)) { dc = GET_DC(self); default_pen = SelectObject(dc, pen->pen); Polyline(dc, (POINT*) xy, count); SelectObject(dc, default_pen); RELEASE_DC(self, dc); } return 0; } static int draw_paste(DrawObject* self, DrawObject* pixmap, int* xy, int count) { HDC dc; if (IS_DRAW(pixmap) && pixmap->dc && count >= 1) { dc = GET_DC(self); if (count == 1) BitBlt(dc, xy[0], xy[1], pixmap->width, pixmap->height, pixmap->dc, 0, 0, SRCCOPY); else BitBlt(dc, xy[0], xy[1], xy[2]-xy[0], xy[3]-xy[1], pixmap->dc, 0, 0, SRCCOPY); RELEASE_DC(self, dc); } return 0; } static int draw_polygon(DrawObject* self, int* xy, int count, PenObject* pen, BrushObject* brush) { HDC dc; HPEN default_pen; HBRUSH default_brush; dc = GET_DC(self); if (IS_PEN(brush) || IS_BRUSH(pen)) { void* tmp = pen; pen = (void*) brush; brush = tmp; } default_pen = SelectObject(dc, IS_PEN(pen) ? pen->pen : GetStockObject(NULL_PEN)); default_brush = SelectObject(dc, IS_BRUSH(brush) ? brush->brush : GetStockObject(NULL_BRUSH)); Polygon(dc, (POINT*) xy, count); SelectObject(dc, default_pen); SelectObject(dc, default_brush); RELEASE_DC(self, dc); return 0; } static int draw_rectangle(DrawObject* self, int x0, int y0, int x1, int y1, PenObject* pen, BrushObject* brush) { HDC dc; HPEN default_pen; HBRUSH default_brush; if (IS_PEN(brush) || IS_BRUSH(pen)) { void* tmp = pen; pen = (void*) brush; brush = tmp; } dc = GET_DC(self); if (IS_PEN(pen)) { default_pen = SelectObject(dc, pen->pen); default_brush = SelectObject(dc, IS_BRUSH(brush) ? brush->brush : GetStockObject(NULL_BRUSH)); Rectangle(dc, x0, y0, x1, y1); SelectObject(dc, default_brush); SelectObject(dc, default_pen); } else if (IS_BRUSH(brush)) { RECT rect; rect.left = x0; rect.top = y0; rect.right = x1; rect.bottom = y1; FillRect(dc, &rect, brush->brush); } RELEASE_DC(self, dc); return 0; } static int draw_text(DrawObject* self, int x0, int y0, PyObject* text, FontObject* font) { HDC dc; HFONT old_font; #if defined(HAVE_UNICODE) if (IS_FONT(font) && (PyString_Check(text) || PyUnicode_Check(text))) #else if (IS_FONT(font) && PyString_Check(text)) #endif { dc = GET_DC(self); old_font = SelectObject(dc, font->font); SetTextColor(dc, font->ink); SetTextAlign(dc, TA_LEFT | TA_TOP); SetBkMode(dc, TRANSPARENT); if (PyString_Check(text)) ExtTextOut( dc, x0, y0, 0, NULL, PyString_AS_STRING(text), PyString_GET_SIZE(text), NULL ); #if defined(HAVE_UNICODE) else ExtTextOutW( dc, x0, y0, 0, NULL, (wchar_t*) PyUnicode_AS_DATA(text), PyUnicode_GET_SIZE(text), NULL ); #endif SelectObject(dc, old_font); RELEASE_DC(self, dc); } return 0; } static int draw_textsize(DrawObject* self, PyObject* text, FontObject* font, int* width, int* height) { HDC dc; HFONT old_font; SIZE size; size.cx = size.cy = 0; if (IS_FONT(font)) { dc = GET_DC(self); old_font = SelectObject(dc, font->font); if (!text) { /* width of "average character" (dialogue unit) */ GetTextExtentPoint32( dc, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52, &size ); size.cx = (size.cx + 26) / 52; } else if (PyString_Check(text)) GetTextExtentPoint32( dc, PyString_AS_STRING(text), PyString_GET_SIZE(text), &size ); #if defined(HAVE_UNICODE) else if (PyUnicode_Check(text)) GetTextExtentPoint32W( dc, (wchar_t*) PyUnicode_AS_DATA(text), PyUnicode_GET_SIZE(text), &size ); #endif SelectObject(dc, old_font); RELEASE_DC(self, dc); } *width = size.cx; *height = size.cy; return 0; } /* -------------------------------------------------------------------- */ /* Python bindings */ #define GETINT(op)\ (PyInt_Check(op) ? PyInt_AS_LONG((op)) :\ PyFloat_Check(op) ? (int) PyFloat_AS_DOUBLE((op)) :\ PyInt_AsLong(op)) static int* getpoints(DrawObject* self, PyObject* xyIn, int* count, int auto_close) { int *xy; int i, n; int x, y; if (!PySequence_Check(xyIn)) { PyErr_SetString(PyExc_TypeError, "argument must be a sequence"); return NULL; } n = PyObject_Length(xyIn); if (PyErr_Occurred()) return NULL; if (n & 1) { PyErr_SetString(PyExc_TypeError, "expected even number of coordinates"); return NULL; } n /= 2; xy = malloc((n+1) * 2 * sizeof(int)); if (!xy) { PyErr_NoMemory(); *count = -1; return NULL; } x = self->xoffset; y = self->yoffset; if (PyList_Check(xyIn)) for (i = 0; i < n; i++) { xy[i+i] = (GETINT(PyList_GET_ITEM(xyIn, i+i)) + x); xy[i+i+1] = (GETINT(PyList_GET_ITEM(xyIn, i+i+1)) + y); } else if (PyTuple_Check(xyIn)) for (i = 0; i < n; i++) { xy[i+i] = (GETINT(PyTuple_GET_ITEM(xyIn, i+i)) + x); xy[i+i+1] = (GETINT(PyTuple_GET_ITEM(xyIn, i+i+1)) + y); } else for (i = 0; i < n; i++) { PyObject *op; op = PySequence_GetItem(xyIn, i+i); xy[i+i] = (GETINT(op) + x); Py_DECREF(op); op = PySequence_GetItem(xyIn, i+i+1); xy[i+i+1] = (GETINT(op) + y); Py_DECREF(op); } PyErr_Clear(); /* close polygon */ if (auto_close && i > 0 && (xy[i+i-1] != xy[0] || xy[i+i+1-1] != xy[1])) { xy[i+i] = xy[0]; xy[i+i+1] = xy[1]; n++; } *count = n; return xy; } static PyObject* draw_pygetpen(DrawObject* self, PyObject* args) { int color; int red, green, blue; int width; if (!PyArg_ParseTuple(args, "(iii)i:getpen", &red, &green, &blue, &width)) return NULL; color = RGB(red/256, green/256, blue/256); return (PyObject*) draw_getpen(self, color, width); } static PyObject* draw_pygetbrush(DrawObject* self, PyObject* args) { int color; int red, green, blue; if (!PyArg_ParseTuple(args, "(iii):getbrush", &red, &green, &blue)) return NULL; color = RGB(red/256, green/256, blue/256); return (PyObject*) draw_getbrush(self, color); } static PyObject * draw_pygetfont(DrawObject* self, PyObject* args) { int color; int red, green, blue; char* font; if (!PyArg_ParseTuple(args, "(iii)s:getfont", &red, &green, &blue, &font)) return NULL; color = RGB(red/256, green/256, blue/256); return (PyObject*) draw_getfont(self, color, font); } static PyObject* draw_pyellipse(DrawObject* self, PyObject* args) { int status; PenObject* pen = NULL; BrushObject* brush = NULL; int x0, y0, x1, y1; if (!PyArg_ParseTuple(args, "(iiii)|OO:ellipse", &x0, &y0, &x1, &y1, &pen, &brush)) return NULL; status = draw_ellipse(self, x0, y0, x1, y1, pen, brush); if (status < 0) return NULL; Py_INCREF(Py_None); return Py_None; } static PyObject* draw_pyline(DrawObject* self, PyObject* args) { int* xy; int count; int status; PyObject* xyIn; PenObject* pen = NULL; if (!PyArg_ParseTuple(args, "O|O:line", &xyIn, &pen)) return NULL; xy = getpoints(self, xyIn, &count, 0); if (!xy) return NULL; status = draw_lines(self, xy, count, pen); free(xy); if (status < 0) return NULL; Py_INCREF(Py_None); return Py_None; } static PyObject* draw_pypolygon(DrawObject* self, PyObject* args) { int* xy; int count; int status; PyObject* xyIn; PenObject* pen = NULL; BrushObject* brush = NULL; if (!PyArg_ParseTuple(args, "O|OO:polygon", &xyIn, &pen, &brush)) return NULL; xy = getpoints(self, xyIn, &count, 1); if (!xy) return NULL; status = draw_polygon(self, xy, count, pen, brush); free(xy); if (status < 0) return NULL; Py_INCREF(Py_None); return Py_None; } static PyObject* draw_pyrectangle(DrawObject* self, PyObject* args) { int status; int x0, y0, x1, y1; PenObject* pen = NULL; BrushObject* brush = NULL; if (!PyArg_ParseTuple(args, "(iiii)|OO:rectangle", &x0, &y0, &x1, &y1, &pen, &brush)) return NULL; status = draw_rectangle(self, x0, y0, x1, y1, pen, brush); if (status < 0) return NULL; Py_INCREF(Py_None); return Py_None; } static PyObject* draw_pytext(DrawObject* self, PyObject* args) { int status; int x0, y0; PyObject* text; FontObject* font; if (!PyArg_ParseTuple(args, "(ii)OO!:text", &x0, &y0, &text, &FontType, &font)) return NULL; status = draw_text(self, x0, y0, text, font); if (status < 0) return NULL; Py_INCREF(Py_None); return Py_None; } static PyObject* draw_pytextsize(DrawObject* self, PyObject* args) { int width, height; int status; PyObject* text; FontObject* font; if (!PyArg_ParseTuple(args, "OO!:text", &text, &FontType, &font)) return NULL; if (text == Py_None) status = draw_textsize(self, NULL, font, &width, &height); else status = draw_textsize(self, text, font, &width, &height); if (status < 0) return NULL; return Py_BuildValue("ii", width, height); } static PyObject* draw_pygetpixmap(DrawObject* self, PyObject* args) { DrawObject* pixmap; int width, height; BrushObject* brush = NULL; if (!PyArg_ParseTuple(args, "ii|O:getpixmap", &width, &height, &brush)) return NULL; pixmap = draw_getpixmap(self, width, height); if (pixmap && brush) draw_rectangle(pixmap, 0, 0, width, height, NULL, brush); return (PyObject*) pixmap; } static PyObject* draw_pygetimage(DrawObject* self, PyObject* args) { Py_INCREF(Py_None); return Py_None; } static PyObject* draw_pygetpath(DrawObject* self, PyObject* args) { PyObject* path; if (!PyArg_ParseTuple(args, "O:getpath", &path)) return NULL; Py_INCREF(path); return path; } static PyMethodDef draw_methods[] = { /* draw interface */ {"text", (PyCFunction)draw_pytext, 1}, {"line", (PyCFunction)draw_pyline, 1}, {"rectangle", (PyCFunction)draw_pyrectangle, 1}, {"textsize", (PyCFunction)draw_pytextsize, 1}, {"polygon", (PyCFunction)draw_pypolygon, 1}, /* {"paste", (PyCFunction)draw_pypaste, 1}, */ /* {"crop", (PyCFunction)draw_pycrop, 1}, */ {"ellipse", (PyCFunction)draw_pyellipse, 1}, /* factories (access via widget interface) */ {"getpen", (PyCFunction)draw_pygetpen, 1}, {"getbrush", (PyCFunction)draw_pygetbrush, 1}, {"getfont", (PyCFunction)draw_pygetfont, 1}, /* {"getpixmap", (PyCFunction)draw_pygetpixmap, 1}, */ /* {"getimage", (PyCFunction)draw_pygetimage, 1}, */ /* {"getpath", (PyCFunction)draw_pygetpath, 1}, */ /* c api (experimental) */ /* {"getcapi", (PyCFunction)draw_getcapi, 1}, */ /* geometry */ /* {"settransform", (PyCFunction)draw_settransform, 1}, */ {NULL, NULL} }; static PyObject* draw_getattr(DrawObject* self, char* name) { return Py_FindMethod(draw_methods, (PyObject*) self, name); } statichere PyTypeObject DrawType = { PyObject_HEAD_INIT(NULL) 0, "_windraw.Draw", sizeof(DrawObject), 0, (destructor) draw_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ (getattrfunc) draw_getattr, /*tp_getattr*/ }; static PyMethodDef module_functions[] = { {"getdraw", (PyCFunction)module_getdraw, 1}, {NULL, NULL} }; void #ifdef WIN32 __declspec(dllexport) #endif init_windraw(void) { /* FIXME: the following should be handled by configuration code, and the library should fall back on a slower implementation if the sizes don't match */ if (sizeof(int) != sizeof(LONG)) { printf("*** bad integer size ***\n"); exit(1); } #if defined(HAVE_UNICODE) if (sizeof(Py_UNICODE) != sizeof(WCHAR)) { printf("*** bad wide character size ***\n"); exit(1); } #endif DrawType.ob_type = PenType.ob_type = BrushType.ob_type = FontType.ob_type = &PyType_Type; Py_InitModule("_windraw", module_functions); }