/* * Tkinter 3000 Widget Construction Kit * $Id: /work/modules/tkinter3000/_tk3draw.c 816 2005-10-16T20:37:26.127213Z Fredrik $ * * Simple Tk-based drawing layer. This implementation should run * on any platform supported by Tk. * * history: * 1998-09-13 fl created (Tk drawing layer) * 2000-12-30 fl adapted to Tkinter 3000 * 2000-12-31 fl added more special cases to getpoints * 2001-01-07 fl added support for pixmap drawables * 2001-01-10 fl more drawing primitives * 2001-01-11 fl added setmargin; fixed paste offset; minor optimizations * 2001-02-04 fl minor cleanup (1.0 alpha 1) * 2001-02-05 fl don't crash in text if font is omitted; font attributes * 2002-08-18 fl added C API interface (WCKSimple2DAPI); textsize * 2003-05-09 fl fixed paste interface (crop will have to wait) * 2003-05-09 fl added unicode support to text/textsize * 2003-05-31 fl added optional pixmap fill brush argument * 2003-12-03 fl added settransform(), pixmap crop(), etc * 2003-12-06 fl added getpath() stub * 2004-09-27 fl driver refactoring * 2004-09-12 fl added image string interface * 2004-10-15 fl added some more error checking * * Copyright (c) 1998-2005 by Secret Labs AB * Copyright (c) 1998-2005 by Fredrik Lundh * * info@pythonware.com * http://www.pythonware.com */ /* -------------------------------------------------------------------- * The Tkinter 3000 Widget Construction Kit is * * Copyright (c) 1998-2005 by Secret Labs AB * Copyright (c) 1998-2005 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. * -------------------------------------------------------------------- */ /* TODO: add fallback core to 2D API (?) */ /* TODO: clean up logical font/physical font issues (measure->textsize) */ #include #include "tk.h" /* #include "tkInt.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 /* -------------------------------------------------------------------- */ /* window drawing context */ typedef struct { PyObject_HEAD /* attributes */ Tcl_Interp* interp; Tk_Window window; Display* display; Drawable drawable; /* None = use window */ int margin, xoffset, yoffset; unsigned int is_pixmap:1; } DrawObject; staticforward PyTypeObject Draw_Type; /* check if this is a drawable */ #define Draw_Check(op) ((op) != NULL && (op)->ob_type == &Draw_Type) /* get current drawable (pixmap or window) */ #define Draw_GetDrawable(self)\ (self->drawable != None) ? self->drawable :\ (self->drawable = Tk_WindowId(self->window)) /* -------------------------------------------------------------------- */ /* tk pen objects */ typedef struct { PyObject_HEAD /* attributes */ GC gc; XColor *color; int width; Display* display; } PenObject; staticforward PyTypeObject Pen_Type; #define Pen_Check(op) ((op) != NULL && (op)->ob_type == &Pen_Type) #define Pen_GetGC(op, drawable) (((PenObject*)(op))->gc) /* -------------------------------------------------------------------- */ /* tk brush objects */ typedef struct { PyObject_HEAD /* attributes */ GC gc; XColor *color; Display* display; } BrushObject; staticforward PyTypeObject Brush_Type; #define Brush_Check(op) ((op) != NULL && (op)->ob_type == &Brush_Type) #define Brush_GetGC(op, drawable)\ ((((BrushObject*)(op))->gc != None) ? (((BrushObject*)(op))->gc) :\ Tk_GCForColor((((BrushObject*)(op))->color), (drawable))) /* -------------------------------------------------------------------- */ /* tk font objects */ typedef struct { PyObject_HEAD /* attributes */ GC gc; XColor *color; Tk_Font tkfont; int ascent; int descent; Display* display; } FontObject; staticforward PyTypeObject Font_Type; #define Font_Check(op) ((op) != NULL && (op)->ob_type == &Font_Type) #define Font_GetGC(op, drawable) (((FontObject*)(op))->gc) /* -------------------------------------------------------------------- */ /* pixmap crop objects */ typedef struct { PyObject_HEAD /* attributes */ DrawObject* pixmap; int x0, y0, x1, y1; } CropObject; staticforward PyTypeObject Crop_Type; /* check if this is a crop region */ #define Crop_Check(op) ((op) != NULL && (op)->ob_type == &Crop_Type) /* -------------------------------------------------------------------- */ /* helpers */ /* convert a color description to an RGB mask */ #define XCOLOR_RGB(xc)\ (((xc)->red&0xff00)<<8) + ((xc)->green&0xff00) + (((xc)->blue&0xff00)>>8) /* pick the pen from a set of drawing objects (NULL if neither is a pen) */ #define PEN(p1, p2)\ (PenObject*) (Pen_Check(p1) ? (p1) : (Pen_Check(p2) ? (p2) : NULL)) /* pick the brush from a set of drawing objects (NULL if neither is a brush) */ #define BRUSH(p1, p2)\ (BrushObject*) (Brush_Check(p1) ? (p1) : (Brush_Check(p2) ? (p2) : NULL)) static int _lookupcolor(DrawObject* self, PyObject* colorIn) { XColor* xc; int color; if (PyInt_Check(colorIn)) { /* integer: 0xrrggbb */ color = (int) PyInt_AS_LONG(colorIn); } else if (PyString_Check(colorIn)) { /* string: lookup in colour database */ xc = Tk_GetColor(self->interp, self->window, PyString_AS_STRING(colorIn)); if (!xc) { PyErr_SetString(PyExc_ValueError, "unknown color name"); return -1; } color = XCOLOR_RGB(xc); Tk_FreeColor(xc); } else { PyErr_SetString(PyExc_TypeError, "expected string or integer"); return -1; } return color; } /* -------------------------------------------------------------------- */ static PenObject * draw_getpen(DrawObject* self, int color, int width) { XColor xc; XGCValues xgc; PenObject* pen; pen = PyObject_NEW(PenObject, &Pen_Type); if (!pen) return NULL; pen->width = width; pen->display = self->display; xc.red = (color>>8) & 0xff00; xc.green = color & 0xff00; xc.blue = (color<<8) & 0xff00; pen->color = Tk_GetColorByValue(self->window, &xc); if (!pen->color) { PyErr_SetString(PyExc_ValueError, "unknown color name"); return 0; } xgc.foreground = pen->color->pixel; xgc.line_width = width; pen->gc = Tk_GetGC(self->window, GCForeground|GCLineWidth, &xgc); return pen; } static PenObject * draw_pygetpen(DrawObject* self, PyObject* args) { int color; PyObject* colorIn; int width; if (!PyArg_ParseTuple(args, "Oi:getpen", &colorIn, &width)) return NULL; color = _lookupcolor(self, colorIn); if (color == -1) return NULL; return draw_getpen(self, color, width); } static void pen_dealloc(PenObject* pen) { Tk_FreeGC(pen->display, pen->gc); Tk_FreeColor(pen->color); PyMem_DEL(pen); } static PyMethodDef pen_methods[] = { {NULL, NULL} }; static PyObject* pen_getattr(PenObject* pen, char* name) { return Py_FindMethod(pen_methods, (PyObject*) pen, name); } statichere PyTypeObject Pen_Type = { PyObject_HEAD_INIT(NULL) 0, "_tk3draw.Pen", sizeof(PenObject), 0, (destructor)pen_dealloc, /* tp_dealloc */ 0, /* tp_print */ (getattrfunc)pen_getattr, /* tp_getattr */ }; /* -------------------------------------------------------------------- */ static BrushObject * draw_getbrush(DrawObject* self, int color, int style) { XColor xc; BrushObject* brush; brush = PyObject_NEW(BrushObject, &Brush_Type); if (!brush) return NULL; brush->display = self->display; xc.red = (color>>8) & 0xff00; xc.green = color & 0xff00; xc.blue = (color<<8) & 0xff00; brush->color = Tk_GetColorByValue(self->window, &xc); if (!brush->color) { PyErr_SetString(PyExc_ValueError, "unknown color name"); return 0; } brush->gc = None; return brush; } static BrushObject * draw_pygetbrush(DrawObject* self, PyObject* args) { int color; PyObject* colorIn; if (!PyArg_ParseTuple(args, "O:getbrush", &colorIn)) return NULL; color = _lookupcolor(self, colorIn); if (color == -1) return NULL; return draw_getbrush(self, color, 0); } static void brush_dealloc(BrushObject* brush) { if (brush->gc) Tk_FreeGC(brush->display, brush->gc); Tk_FreeColor(brush->color); PyMem_DEL(brush); } static PyMethodDef brush_methods[] = { {NULL, NULL} }; static PyObject* brush_getattr(BrushObject* brush, char* name) { return Py_FindMethod(brush_methods, (PyObject*) brush, name); } statichere PyTypeObject Brush_Type = { PyObject_HEAD_INIT(NULL) 0, "_tk3draw.Brush", sizeof(BrushObject), 0, (destructor)brush_dealloc, /* tp_dealloc */ 0, /* tp_print */ (getattrfunc)brush_getattr, /* tp_getattr */ }; /* -------------------------------------------------------------------- */ static PyObject * draw_getfont(DrawObject* self, int color, char* fontname) { XColor xc; XGCValues xgc; FontObject* font; Tk_FontMetrics fm; font = PyObject_NEW(FontObject, &Font_Type); if (!font) return NULL; font->display = self->display; xc.red = (color>>8) & 0xff00; xc.green = color & 0xff00; xc.blue = (color<<8) & 0xff00; font->color = Tk_GetColorByValue(self->window, &xc); if (!font->color) { PyErr_SetString(PyExc_ValueError, "unknown color"); PyMem_DEL(font); return 0; } font->tkfont = Tk_GetFont(self->interp, self->window, fontname); if (!font->tkfont) { Tk_FreeColor(font->color); PyErr_SetString(PyExc_ValueError, "unknown font name"); PyMem_DEL(font); return NULL; } /* get basic font metrics */ Tk_GetFontMetrics(font->tkfont, &fm); font->ascent = fm.ascent; font->descent = fm.descent; xgc.foreground = font->color->pixel; xgc.font = Tk_FontId(font->tkfont); font->gc = Tk_GetGC(self->window, GCForeground|GCFont, &xgc); return (PyObject*) font; } static PyObject * draw_pygetfont(DrawObject* self, PyObject* args) { int color; PyObject* colorIn; char* fontname; if (!PyArg_ParseTuple(args, "Os:getfont", &colorIn, &fontname)) return NULL; color = _lookupcolor(self, colorIn); if (color == -1) return NULL; return draw_getfont(self, color, fontname); } static void font_dealloc(FontObject* font) { Tk_FreeGC(font->display, font->gc); Tk_FreeColor(font->color); Tk_FreeFont(font->tkfont); PyMem_DEL(font); } static PyObject * font_measure(FontObject* font, PyObject* args) { int width; /* FIXME: deprecated: use textsize instead */ PyObject* string = NULL; if (!PyArg_ParseTuple(args, "|O!:measure", &PyString_Type, &string)) return NULL; if (string) width = Tk_TextWidth( font->tkfont, PyString_AS_STRING(string), PyString_GET_SIZE(string) ); else { /* return average size */ width = Tk_TextWidth( font->tkfont, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52 ); width = (width + 26) / 52; } return Py_BuildValue("ii", width, font->ascent + font->descent); } static PyMethodDef font_methods[] = { {"measure", (PyCFunction)font_measure, 1}, {NULL, NULL} }; static PyObject* font_getattr(FontObject* font, char* name) { PyObject* res; res = Py_FindMethod(font_methods, (PyObject*) font, name); if (res) return res; PyErr_Clear(); /* font attributes */ if (strcmp(name, "ascent") == 0) return Py_BuildValue("i", font->ascent); if (strcmp(name, "descent") == 0) return Py_BuildValue("i", font->descent); if (strcmp(name, "height") == 0) return Py_BuildValue("i", font->ascent + font->descent); PyErr_SetString(PyExc_AttributeError, name); return NULL; } statichere PyTypeObject Font_Type = { PyObject_HEAD_INIT(NULL) 0, "_tk3draw.Font", sizeof(FontObject), 0, (destructor)font_dealloc, /* tp_dealloc */ 0, /* tp_print */ (getattrfunc)font_getattr, /* tp_getattr */ }; /* -------------------------------------------------------------------- */ static PyObject* draw_lookupcolor(DrawObject* self, PyObject* args) { /* map colour name to RGB value */ int color; PyObject* colorIn; if (!PyArg_ParseTuple(args, "O:_lookupcolor", &colorIn)) return NULL; color = _lookupcolor(self, colorIn); if (color == -1) return NULL; return Py_BuildValue("i", color); } static PyObject* draw_lookuprelief(DrawObject* self, PyObject* args) { /* map relief name to Tk relief code */ int relief; int ok; char* reliefIn; if (!PyArg_ParseTuple(args, "s:_lookuprelief", &reliefIn)) return NULL; ok = Tk_GetRelief(self->interp, reliefIn, &relief); if (ok != TCL_OK) { PyErr_SetString(PyExc_TypeError, "unrecognized relief value"); return NULL; } return Py_BuildValue("i", relief); } static PyObject* draw_setmargin(DrawObject* self, PyObject* args) { /* (re)set display offset */ int margin; if (!PyArg_ParseTuple(args, "i:_setoffset", &margin)) return NULL; self->margin = self->xoffset = self->yoffset = margin; Py_INCREF(Py_None); return Py_None; } static PyObject* draw_settransform(DrawObject* self, PyObject* args) { /* (re)set display transform */ int xoffset = 0, yoffset = 0; if (!PyArg_ParseTuple(args, "|(ii):settransform", &xoffset, &yoffset)) return NULL; self->xoffset = self->margin + xoffset; self->yoffset = self->margin + yoffset; Py_INCREF(Py_None); return Py_None; } static DrawObject* getpixmap(DrawObject* self, int width, int height) { /* create compatible pixmap for the given drawable */ DrawObject* draw; if (!Tk_WindowId(self->window)) { PyErr_SetString(PyExc_RuntimeError, "window not available"); return NULL; } draw = PyObject_NEW(DrawObject, &Draw_Type); if (!draw) return NULL; draw->is_pixmap = 1; draw->interp = self->interp; draw->display = self->display; draw->window = self->window; draw->drawable = Tk_GetPixmap( self->display, Tk_WindowId(self->window), width, height, Tk_Depth(self->window) ); if (!draw->drawable) { PyMem_DEL(draw); PyErr_SetString(PyExc_ValueError, "failed to create pixmap"); return NULL; } draw->xoffset = draw->yoffset = 0; return draw; } static PyObject* module_getdraw(PyObject* self, PyObject* args) { DrawObject* draw; long arg; char* name; if (!PyArg_ParseTuple(args, "ls:getdraw", &arg, &name)) return NULL; draw = PyObject_NEW(DrawObject, &Draw_Type); if (!draw) return NULL; draw->is_pixmap = 0; draw->interp = (Tcl_Interp*) arg; draw->window = Tk_NameToWindow( draw->interp, name, Tk_MainWindow(draw->interp) ); if (!draw->window) { PyMem_DEL(draw); PyErr_SetString(PyExc_ValueError, "unknown window reference"); return NULL; } draw->display = Tk_Display(draw->window); draw->drawable = None; draw->xoffset = draw->yoffset = 0; return (PyObject*) draw; } static void draw_dealloc(DrawObject* self) { if (self->is_pixmap) Tk_FreePixmap(self->display, self->drawable); PyMem_DEL(self); } /* -------------------------------------------------------------------- */ /* drawing primitives */ #define GETINT(op)\ (PyInt_Check(op) ? PyInt_AS_LONG((op)) :\ PyFloat_Check(op) ? (int) PyFloat_AS_DOUBLE((op)) :\ PyInt_AsLong(op)) static XPoint* getpoints(DrawObject* self, PyObject* xyIn, int* count, int auto_close) { XPoint *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) * sizeof(XPoint)); 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].x = (short) (GETINT(PyList_GET_ITEM(xyIn, i+i)) + x); xy[i].y = (short) (GETINT(PyList_GET_ITEM(xyIn, i+i+1)) + y); } else if (PyTuple_Check(xyIn)) for (i = 0; i < n; i++) { xy[i].x = (short) (GETINT(PyTuple_GET_ITEM(xyIn, i+i)) + x); xy[i].y = (short) (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].x = (short) (GETINT(op) + x); Py_DECREF(op); op = PySequence_GetItem(xyIn, i+i+1); xy[i].y = (short) (GETINT(op) + y); Py_DECREF(op); } PyErr_Clear(); /* close polygon */ if (auto_close && i > 0 && (xy[i-1].x != xy[0].x || xy[i-1].y != xy[0].y)) { xy[i].x = xy[0].x; xy[i].y = xy[0].y; n++; } *count = n; return xy; } /* -------------------------------------------------------------------- */ static PyObject* draw_pyellipse(DrawObject* self, PyObject* args) { Drawable drawable; PyObject* resource; PyObject* pen = NULL; PyObject* brush = NULL; int x0, y0, x1, y1; if (!PyArg_ParseTuple(args, "(iiii)|OO:ellipse", &x0, &y0, &x1, &y1, &pen, &brush)) return NULL; x0 = x0 + self->xoffset; x1 = x1 + self->xoffset; y0 = y0 + self->yoffset; y1 = y1 + self->yoffset; drawable = Draw_GetDrawable(self); resource = (PyObject*) BRUSH(brush, pen); if (resource) XFillArc( self->display, drawable, Brush_GetGC(resource, drawable), x0, y0, (unsigned) (x1 - x0 - 1), (unsigned) (y1 - y0 - 1), 0, 360*64); resource = (PyObject*) PEN(pen, brush); if (resource) XDrawArc( self->display, drawable, Pen_GetGC(resource, drawable), x0, y0, (unsigned) (x1 - x0 - 1), (unsigned) (y1 - y0 - 1), 0, 360*64); Py_INCREF(Py_None); return Py_None; } static int draw_line(DrawObject* self, int x0, int y0, int x1, int y1, PyObject* pen) { if (Pen_Check(pen)) { x0 += self->xoffset; y0 += self->yoffset; x1 += self->xoffset; y1 += self->yoffset; XDrawLine(self->display, Draw_GetDrawable(self), Pen_GetGC(pen, drawable), x0, y0, x1, y1); } return 0; } static PyObject* draw_pyline(DrawObject* self, PyObject* args) { XPoint* xy; int count; Drawable drawable; PyObject* xyIn; PyObject* pen = NULL; if (!PyArg_ParseTuple(args, "O|O:line", &xyIn, &pen)) return NULL; if (Pen_Check(pen)) { xy = getpoints(self, xyIn, &count, 0); if (!xy) return NULL; drawable = Draw_GetDrawable(self); XDrawLines(self->display, drawable, Pen_GetGC(pen, drawable), xy, count, CoordModeOrigin); free(xy); } Py_INCREF(Py_None); return Py_None; } static PyObject* draw_pypolygon(DrawObject* self, PyObject* args) { XPoint* xy; int count; Drawable drawable; PyObject* resource; PyObject* xyIn; PyObject* pen = NULL; PyObject* brush = NULL; if (!PyArg_ParseTuple(args, "O|OO:polygon", &xyIn, &pen, &brush)) return NULL; xy = getpoints(self, xyIn, &count, 1); if (!xy) return NULL; drawable = Draw_GetDrawable(self); resource = (PyObject*) BRUSH(brush, pen); if (resource) XFillPolygon( self->display, drawable, Brush_GetGC(resource, drawable), xy, count-1, Complex, CoordModeOrigin); resource = (PyObject*) PEN(pen, brush); if (resource) XDrawLines( self->display, drawable, Pen_GetGC(resource, drawable), xy, count, CoordModeOrigin); free(xy); Py_INCREF(Py_None); return Py_None; } static int draw_rectangle(DrawObject* self, int x0, int y0, int x1, int y1, PyObject* pen, PyObject* brush) { Drawable drawable; PyObject* resource; x0 = x0 + self->xoffset; x1 = x1 + self->xoffset; y0 = y0 + self->yoffset; y1 = y1 + self->yoffset; drawable = Draw_GetDrawable(self); resource = (PyObject*) BRUSH(brush, pen); if (resource) XFillRectangle( self->display, drawable, Brush_GetGC(resource, drawable), x0, y0, (unsigned) (x1 - x0), (unsigned) (y1 - y0)); resource = (PyObject*) PEN(pen, brush); if (resource) XDrawRectangle( self->display, drawable, Pen_GetGC(resource, drawable), x0, y0, (unsigned) (x1 - x0), (unsigned) (y1 - y0)); return 0; } static PyObject* draw_pyrectangle(DrawObject* self, PyObject* args) { PyObject* pen = NULL; PyObject* brush = NULL; int x0, y0, x1, y1; if (!PyArg_ParseTuple(args, "(iiii)|OO:rectangle", &x0, &y0, &x1, &y1, &pen, &brush)) return NULL; draw_rectangle(self, x0, y0, x1, y1, pen, brush); Py_INCREF(Py_None); return Py_None; } static PyObject* draw_text(DrawObject* self, int x0, int y0, char* text, int textsize, FontObject* font) { Drawable drawable; if (Font_Check(font)) { x0 = x0 + self->xoffset; y0 = y0 + self->yoffset; drawable = Draw_GetDrawable(self); Tk_DrawChars( self->display, drawable, Font_GetGC(font, drawable), font->tkfont, text, textsize, x0, y0 + font->ascent ); } return 0; } static PyObject* draw_pytext(DrawObject* self, PyObject* args) { PyObject* text; FontObject* font; int x0, y0; if (!PyArg_ParseTuple(args, "(ii)OO!:text", &x0, &y0, &text, &Font_Type, &font)) return NULL; if (PyString_Check(text)) { draw_text( self, x0, y0, PyString_AS_STRING(text), PyString_GET_SIZE(text), font ); } #if defined(HAVE_UNICODE) else if (PyUnicode_Check(text)) { PyObject* utf8text; utf8text = PyUnicode_AsUTF8String(text); if (!utf8text) return NULL; draw_text( self, x0, y0, PyString_AS_STRING(utf8text), PyString_GET_SIZE(utf8text), font ); Py_DECREF(utf8text); } #endif else { PyErr_SetString(PyExc_TypeError, "expected utf-8 string or unicode"); return NULL; } Py_INCREF(Py_None); return Py_None; } static PyObject* draw_textsize(DrawObject* self, char* text, int textsize, FontObject* font, int* width, int* height) { int w; if (!text) { w = Tk_TextWidth( font->tkfont, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52 ); w = (w + 26) / 52; } else w = Tk_TextWidth(font->tkfont, text, textsize); *width = w; *height = font->ascent + font->descent; return 0; } static PyObject* draw_pytextsize(DrawObject* self, PyObject* args) { int width, height; PyObject* text; FontObject* font; if (!PyArg_ParseTuple(args, "OO!:text", &text, &Font_Type, &font)) return NULL; if (text == Py_None) { draw_textsize( self, NULL, 0, font, &width, &height ); } else if (PyString_Check(text)) { draw_textsize( self, PyString_AS_STRING(text), PyString_GET_SIZE(text), font, &width, &height ); } #if defined(HAVE_UNICODE) else if (PyUnicode_Check(text)) { PyObject* utf8text; utf8text = PyUnicode_AsUTF8String(text); if (!utf8text) return NULL; draw_textsize( self, PyString_AS_STRING(utf8text), PyString_GET_SIZE(utf8text), font, &width, &height ); Py_DECREF(utf8text); } #endif else { PyErr_SetString(PyExc_TypeError, "expected utf-8 string or unicode"); return NULL; } return Py_BuildValue("ii", width, height); } static PyObject* draw_pygetpixmap(DrawObject* self, PyObject* args) { DrawObject* pixmap; int width, height; PyObject* brush = NULL; if (!PyArg_ParseTuple(args, "ii|O:getpixmap", &width, &height, &brush)) return NULL; pixmap = getpixmap(self, width, height); if (brush) draw_rectangle(pixmap, 0, 0, width, height, NULL, brush); return (PyObject*) pixmap; } static PyObject* draw_pygetimage(DrawObject* self, PyObject* args) { Tk_Image image; DrawObject* pixmap; int w, h; /* (experimental) convert a photoimage to a pixmap */ char* imagename; int xoffset = 0, yoffset = 0; if (!PyArg_ParseTuple(args, "s|(ii):image", &imagename, &xoffset, &yoffset)) return NULL; xoffset += self->xoffset; yoffset += self->yoffset; image = Tk_GetImage(self->interp, self->window, imagename, NULL, NULL); if (!image) { PyErr_SetString(PyExc_ValueError, "unknown image reference"); return NULL; } Tk_SizeOfImage(image, &w, &h); pixmap = getpixmap(self, w, h); if (!pixmap) { Tk_FreeImage(image); return NULL; } Tk_RedrawImage(image, 0, 0, w, h, Draw_GetDrawable(pixmap), 0, 0); Tk_FreeImage(image); return (PyObject*) pixmap; } static PyObject* draw_pysetimage(DrawObject* self, PyObject* args) { Tk_PhotoHandle photo; Tk_PhotoImageBlock block; /* (experimental) add pixels from string to photoimage */ char* imagename; char* mode; int w, h; char* data; int size; if (!PyArg_ParseTuple(args, "ss(ii)s#:setimage", &imagename, &mode, &w, &h, &data, &size)) return NULL; if (!strcmp(mode, "L")) { block.pixelSize = 1; block.offset[0] = block.offset[1] = block.offset[2] = 0; } else if (!strcmp(mode, "RGB")) { block.pixelSize = 3; block.offset[0] = 0; block.offset[1] = 1; block.offset[2] = 2; block.offset[3] = 0; /* no alpha (or reserved, under 8.2) */ } else if (!strcmp(mode, "RGBA")) { block.pixelSize = 4; block.offset[0] = 0; block.offset[1] = 1; block.offset[2] = 2; block.offset[3] = 3; } else { PyErr_SetString(PyExc_ValueError, "unknown image mode"); return NULL; } block.width = w; block.height = h; block.pitch = block.pixelSize * w; block.pixelPtr = data; if (block.pitch * h > size) { PyErr_SetString(PyExc_ValueError, "not enough raster data"); return NULL; } photo = Tk_FindPhoto(self->interp, imagename); if (!photo) { PyErr_SetString(PyExc_ValueError, "unknown image reference"); return NULL; } Tk_PhotoPutBlock(photo, &block, 0, 0, block.width, block.height, TK_PHOTO_COMPOSITE_SET); 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 PyObject* draw_pycrop(DrawObject* self, PyObject* args) { CropObject* crop; int x0, y0, x1, y1; if (!PyArg_ParseTuple(args, "(iiii):crop", &x0, &y0, &x1, &y1)) return NULL; if (!self->is_pixmap) { PyErr_SetString(PyExc_TypeError, "crop only works for pixmaps"); return NULL; }; crop = PyObject_NEW(CropObject, &Crop_Type); if (!crop) return NULL; Py_INCREF(self); crop->pixmap = self; crop->x0 = x0; crop->y0 = y0; crop->x1 = x1; crop->y1 = y1; return (PyObject*) crop; } static PyObject* draw_pypaste(DrawObject* self, PyObject* args) { GC gc; int sx0, sy0, sx1, sy1; int dx0, dy0, dx1, dy1; DrawObject* draw; PyObject* dxy = NULL; if (!PyArg_ParseTuple(args, "O|O:paste", &draw, &dxy)) return NULL; if (Draw_Check(draw)) { sx0 = 0; sx1 = sx0 + Tk_Width(draw->window); /* window? */ sy0 = 0; sy1 = sy0 + Tk_Height(draw->window); /* window? */ } else if (Crop_Check(draw)) { CropObject* crop = (CropObject*) draw; sx0 = crop->x0; sx1 = crop->x1; sy0 = crop->y0; sy1 = crop->y1; /* FIXME: check size */ draw = crop->pixmap; } else { PyErr_SetString(PyExc_TypeError, "drawing object expected"); return NULL; } /* destination area */ if (dxy) { if (!PyArg_ParseTuple(dxy, "ii", &dx0, &dy0)) { PyErr_Clear(); if (!PyArg_ParseTuple(dxy, "iiii", &dx0, &dy0, &dx1, &dy1)) return NULL; /* FIXME: check size */ } } else dx0 = dy0 = 0; dx0 += self->xoffset; dy0 += self->yoffset; gc = Tk_GetGC(self->window, 0, NULL); XCopyArea(self->display, Draw_GetDrawable(draw), Draw_GetDrawable(self), gc, sx0, sy0, sx1-sx0, sy1-sy0, dx0, dy0); Tk_FreeGC(self->display, gc); Py_INCREF(Py_None); return Py_None; } static WCKSimple2DAPI capi2d = { WCK_SIMPLE_2D_API_VERSION, NULL, /* fallback */ (void*) draw_line, /* line */ (void*) draw_rectangle, /* rectangle */ (void*) draw_getpen, /* getpen */ (void*) draw_getbrush, /* getbrush */ (void*) draw_text, /* text */ (void*) draw_textsize, /* textsize */ (void*) draw_getfont, /* getfont */ }; static PyObject* draw_getcapi(DrawObject* self, PyObject* args) { if (!PyArg_ParseTuple(args, ":getcapi")) return NULL; return PyCObject_FromVoidPtrAndDesc(&capi2d, WCK_SIMPLE_2D_API, NULL); } static PyObject* draw_border(DrawObject* self, PyObject* args) { Tk_3DBorder border3D; Tk_Uid uid; char color[16]; /* internal interface. may change in future releases */ BrushObject* brush; int margin, border, relief; if (!PyArg_ParseTuple(args, "O!iii:_border", &Brush_Type, &brush, &margin, &border, &relief)) return NULL; if (margin + border <= 0) { Py_INCREF(Py_None); return Py_None; } sprintf(color, "#%04x%04x%04x", brush->color->red, brush->color->green, brush->color->blue); uid = Tk_GetUid(color); border3D = Tk_Get3DBorder(self->interp, self->window, uid); Tk_Draw3DRectangle( self->window, Draw_GetDrawable(self), border3D, margin, margin, Tk_Width(self->window)-2*margin, Tk_Height(self->window)-2*margin, border, relief); Tk_Free3DBorder(border3D); Py_INCREF(Py_None); return Py_None; } static PyObject* draw_resize(DrawObject* self, PyObject* args) { Py_INCREF(self); return (PyObject*) self; } static PyObject* draw_flush(DrawObject* self, PyObject* args) { Py_INCREF(Py_None); return Py_None; } 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}, {"setimage", (PyCFunction)draw_pysetimage, 1}, {"getpath", (PyCFunction)draw_pygetpath, 1}, /* c api (experimental) */ {"getcapi", (PyCFunction)draw_getcapi, 1}, /* geometry */ {"settransform", (PyCFunction)draw_settransform, 1}, /* helpers (internal; subject to change) */ {"_border", (PyCFunction)draw_border, 1}, {"_flush", (PyCFunction)draw_flush, 1}, {"_resize", (PyCFunction)draw_resize, 1}, {"_setmargin", (PyCFunction)draw_setmargin, 1}, {"_lookupcolor", (PyCFunction)draw_lookupcolor, 1}, {"_lookuprelief", (PyCFunction)draw_lookuprelief, 1}, {NULL, NULL} }; static PyObject* draw_getattr(DrawObject* self, char* name) { return Py_FindMethod(draw_methods, (PyObject*) self, name); } statichere PyTypeObject Draw_Type = { PyObject_HEAD_INIT(NULL) 0, "_tk3draw.Draw", sizeof(DrawObject), 0, /* methods */ (destructor)draw_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ (getattrfunc)draw_getattr, /*tp_getattr*/ }; /* -------------------------------------------------------------------- */ static void crop_dealloc(CropObject* self) { Py_DECREF(self->pixmap); PyMem_DEL(self); } static PyMethodDef crop_methods[] = { {NULL, NULL} }; static PyObject* crop_getattr(CropObject* self, char* name) { return Py_FindMethod(crop_methods, (PyObject*) self, name); } statichere PyTypeObject Crop_Type = { PyObject_HEAD_INIT(NULL) 0, "_tk3draw.Crop", sizeof(CropObject), 0, (destructor)crop_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ (getattrfunc)crop_getattr, /*tp_getattr*/ }; /* -------------------------------------------------------------------- */ static PyMethodDef module_functions[] = { {"getdraw", (PyCFunction)module_getdraw, 1}, {NULL, NULL} }; void #ifdef WIN32 __declspec(dllexport) #endif init_tk3draw(void) { /* Patch object types */ Pen_Type.ob_type = Brush_Type.ob_type = Draw_Type.ob_type = Font_Type.ob_type = Crop_Type.ob_type = &PyType_Type; Py_InitModule("_tk3draw", module_functions); }