/* * MiniWCK for Windows. * $Id$ * * a minimalist implementation of the WCK interface, for Windows. * * History: * 2006-04-27 fl Created (based on code from IronPIL) * * Copyright (c) 2004-2006 by Fredrik Lundh. * * See the README file for information on usage and redistribution. */ #include "Python.h" #include "_wckdraw.h" #if _MSC_VER <= 1100 /* gdiplus hacks for VC 5.0 */ #define MIDL_INTERFACE(x) struct typedef unsigned int UINT32; typedef unsigned int UINT_PTR; #define STRICT #endif #if _MSC_VER <= 1200 /* gdiplus hacks for VC 5.0 and VC 6.0 */ #if defined(_WIN64) typedef unsigned __int64 ULONG_PTR; #else typedef unsigned long ULONG_PTR; #endif #endif #if (PY_VERSION_HEX >= 0x02030000) /* hacks for Python 2.3 and later (sigh) */ #undef staticforward #define staticforward extern #endif #define WINDOWS_LEAN_AND_MEAN #include "Windows.h" #include "GdiPlus.h" #define CLAMPF(x)\ ((x) < 0.0F ? 0.0F : (x) > 1.0F ? 1.0F : (x)) #define NEAREST 0 #define ANTIALIAS 1 #define LINEAR 2 #define CUBIC 3 using namespace Gdiplus; static PyObject* imagecolor_getrgb; typedef struct { PyObject_HEAD Graphics* graphics; } WCKDrawObject; staticforward PyTypeObject WCKDrawType; typedef struct { PyObject_HEAD Font* font; Brush* brush; /* optional */ const StringFormat* format; } WCKFontObject; staticforward PyTypeObject WCKFontType; typedef struct { PyObject_HEAD Pen* pen; } WCKPenObject; staticforward PyTypeObject WCKPenType; typedef struct { PyObject_HEAD Brush* brush; } WCKBrushObject; staticforward PyTypeObject WCKBrushType; typedef struct { PyObject_HEAD Bitmap* image; } WCKImageObject; staticforward PyTypeObject WCKImageType; /* -------------------------------------------------------------------- */ void wckdraw_init(void) { /* initialize the graphics library */ GdiplusStartupInput gdiplusStartupInput; ULONG_PTR gdiplusToken; GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); } void wckdraw_exit(void) { ; } /* -------------------------------------------------------------------- */ /* helpers */ static WCHAR* makewstring(PyObject* object) { /* convert Python object to wide string */ if (PyUnicode_Check(object)) { PyUnicodeObject* path = (PyUnicodeObject*) object; int size = PyUnicode_GET_SIZE(path); WCHAR* p = new WCHAR[size+1]; if (PyUnicode_AsWideChar(path, p, size) < 0) { PyErr_SetString(PyExc_ValueError, "cannot convert to wide string"); delete p; return NULL; } p[size] = 0; return p; } else if (PyString_Check(object)) { unsigned char* s = (unsigned char *) PyString_AS_STRING(object); int size = PyString_GET_SIZE(object); WCHAR* p = new WCHAR[size+1]; for (int i = 0; i <= size; i++) p[i] = s[i]; return p; } PyErr_SetString(PyExc_TypeError, "expected string or Unicode object"); return NULL; } #define GETINT(op)\ (PyInt_Check(op) ? PyInt_AS_LONG((op)) :\ PyFloat_Check(op) ? (int) PyFloat_AS_DOUBLE((op)) :\ PyInt_AsLong(op)) #define GETFLOAT(op)\ (PyInt_Check(op) ? (float) PyInt_AS_LONG((op)) :\ PyFloat_Check(op) ? (float) PyFloat_AS_DOUBLE((op)) :\ (float) PyFloat_AsDouble(op)) static PointF* getpoints(PyObject* xyIn, int* count) { PointF *xy; int i, n; /* FIXME: use local buffer (provided by caller) for short sequences */ 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 = new PointF[n+1]; if (!xy) { PyErr_NoMemory(); *count = -1; return NULL; } if (PyList_Check(xyIn)) for (i = 0; i < n; i++) { xy[i].X = GETFLOAT(PyList_GET_ITEM(xyIn, i+i)); xy[i].Y = GETFLOAT(PyList_GET_ITEM(xyIn, i+i+1)); } else if (PyTuple_Check(xyIn)) for (i = 0; i < n; i++) { xy[i].X = GETFLOAT(PyTuple_GET_ITEM(xyIn, i+i)); xy[i].Y = GETFLOAT(PyTuple_GET_ITEM(xyIn, i+i+1)); } else for (i = 0; i < n; i++) { PyObject *op; op = PySequence_GetItem(xyIn, i+i); xy[i].X = GETFLOAT(op); Py_DECREF(op); op = PySequence_GetItem(xyIn, i+i+1); xy[i].Y = GETFLOAT(op); Py_DECREF(op); } PyErr_Clear(); *count = n; return xy; } static int getrect(PyObject* xyIn, RectF* rect) { int count; PointF *xy = getpoints(xyIn, &count); if (!xy) return 0; if (count != 2) { PyErr_SetString(PyExc_ValueError, "two points required"); delete xy; return 0; } rect->X = xy[0].X; rect->Y = xy[0].Y; rect->Width = xy[1].X - xy[0].X; rect->Height = xy[1].Y - xy[0].Y; delete xy; return 1; } static Color getcolor(PyObject* color, int opacity=255, bool grayscale=0) { if (!color) goto error; if (PyString_Check(color)) { char* ink = PyString_AS_STRING(color); if (!strcmp(ink, "aqua")) return Color(opacity,0x00,0xFF,0xFF); if (!strcmp(ink, "black")) return Color(opacity,0x00,0x00,0x00); if (!strcmp(ink, "blue")) return Color(opacity,0x00,0x00,0xFF); if (!strcmp(ink, "fuchsia")) return Color(opacity,0xFF,0x00,0xFF); if (!strcmp(ink, "gray")) return Color(opacity,0x80,0x80,0x80); if (!strcmp(ink, "green")) return Color(opacity,0x00,0x80,0x00); if (!strcmp(ink, "lime")) return Color(opacity,0x00,0xFF,0x00); if (!strcmp(ink, "maroon")) return Color(opacity,0x80,0x00,0x00); if (!strcmp(ink, "navy")) return Color(opacity,0x00,0x00,0x80); if (!strcmp(ink, "olive")) return Color(opacity,0x80,0x80,0x00); if (!strcmp(ink, "purple")) return Color(opacity,0x80,0x00,0x80); if (!strcmp(ink, "red")) return Color(opacity,0xFF,0x00,0x00); if (!strcmp(ink, "silver")) return Color(opacity,0xC0,0xC0,0xC0); if (!strcmp(ink, "teal")) return Color(opacity,0x00,0x80,0x80); if (!strcmp(ink, "white")) return Color(opacity,0xFF,0xFF,0xFF); if (!strcmp(ink, "yellow")) return Color(opacity,0xFF,0xFF,0x00); } PyObject* rgb; if (PyTuple_Check(color)) { Py_INCREF(color); rgb = color; } else { if (!imagecolor_getrgb) goto error; rgb = PyObject_CallFunction(imagecolor_getrgb, "O", color); if (!rgb) goto error; } int red, green, blue; if (!PyArg_ParseTuple(rgb, "iii", &red, &green, &blue)) { Py_DECREF(rgb); goto error; } Py_DECREF(rgb); if (grayscale) { int ink = red*299/1000 + green*587/1000 + blue*114/1000; return Color(opacity, ink, ink, ink); } return Color(opacity, red, green, blue); error: PyErr_Clear(); return Color(opacity, 0, 0, 0); } static Pen* getpen(WCKDrawObject* self, PyObject* obj, PyObject* obj2=NULL) { if (obj && obj->ob_type == &WCKPenType) return ((WCKPenObject*) obj)->pen->Clone(); if (obj2 && obj2->ob_type == &WCKPenType) return ((WCKPenObject*) obj2)->pen->Clone(); if (obj && obj != Py_None) return new Pen(getcolor(obj)); return NULL; } static Brush* getbrush(WCKDrawObject* self, PyObject* obj, PyObject* obj2=NULL) { if (obj && obj->ob_type == &WCKBrushType) return ((WCKBrushObject*) obj)->brush->Clone(); if (obj2 && obj2->ob_type == &WCKBrushType) return ((WCKBrushObject*) obj2)->brush->Clone(); if (obj && obj != Py_None) return new SolidBrush(getcolor(obj)); return NULL; } static PixelFormat getformat(char* mode) { if (!strcmp(mode, "RGB")) { return PixelFormat24bppRGB; } else if (!strcmp(mode, "RGBA")) { return PixelFormat32bppARGB; } else if (!strcmp(mode, "RGBa")) { return PixelFormat32bppPARGB; } PyErr_SetString(PyExc_ValueError, "unknown mode"); return NULL; } static enum InterpolationMode getinterpolation(int filter) { switch (filter) { case NEAREST: return InterpolationModeNearestNeighbor; case LINEAR: return InterpolationModeBilinear; case CUBIC: return InterpolationModeBicubic; case ANTIALIAS: return InterpolationModeHighQualityBicubic; default: /* FIXME: return error code instead? */ return InterpolationModeNearestNeighbor; } } /* -------------------------------------------------------------------- */ /* Draw */ static PyObject* makegraphics(Graphics *graphics) { WCKDrawObject* self = PyObject_NEW(WCKDrawObject, &WCKDrawType); if (!self) return NULL; self->graphics = graphics; self->graphics->SetSmoothingMode(SmoothingModeAntiAlias); return (PyObject*) self; } PyObject* wckdraw_graphics(PyObject* self, PyObject* args) { WCKImageObject* image; if (!PyArg_ParseTuple(args, "O!", &WCKImageType, &image)) return NULL; return makegraphics(new Graphics(image->image)); } PyObject* wckdraw_graphics_dc(PyObject* self, PyObject* args) { int dc; if (!PyArg_ParseTuple(args, "i", &dc)) return NULL; return makegraphics(new Graphics((HDC) dc)); } PyObject* wckdraw_graphics_window(PyObject* self, PyObject* args) { int wnd; if (!PyArg_ParseTuple(args, "i", &wnd)) return NULL; return makegraphics(new Graphics((HWND) wnd)); } static PyObject* draw_arc(WCKDrawObject* self, PyObject* args, PyObject* kw) { PyObject* xy_obj; float start, end; PyObject* fill = NULL; static char* kwlist[] = { "xy", "start", "end", "fill", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kw, "Off|OO:arc", kwlist, &xy_obj, &start, &end, &fill)) return NULL; RectF rect; if (!getrect(xy_obj, &rect)) return NULL; Pen* pen = getpen(self, fill); if (pen) { self->graphics->DrawArc(pen, rect, start, start-end); delete pen; } Py_INCREF(Py_None); return Py_None; } static PyObject* draw_chord(WCKDrawObject* self, PyObject* args, PyObject* kw) { PyObject* xy_obj; float start, end; PyObject* fill = NULL; PyObject* outline = NULL; static char* kwlist[] = { "xy", "start", "end", "fill", "outline", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kw, "Off|OO:chord", kwlist, &xy_obj, &start, &end, &fill, &outline)) return NULL; RectF rect; if (!getrect(xy_obj, &rect)) return NULL; GraphicsPath path; path.AddArc(rect, start, start-end); path.CloseFigure(); Brush* brush = getbrush(self, fill, outline); if (brush) { self->graphics->FillPath(brush, &path); delete brush; } Pen* pen = getpen(self, outline, fill); if (pen) { self->graphics->DrawPath(pen, &path); delete pen; } Py_INCREF(Py_None); return Py_None; } static PyObject* draw_ellipse(WCKDrawObject* self, PyObject* args, PyObject* kw) { PyObject* xy_obj; PyObject* fill = NULL; PyObject* outline = NULL; static char* kwlist[] = { "xy", "fill", "outline", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kw, "O|OO:ellipse", kwlist, &xy_obj, &fill, &outline)) return NULL; RectF rect; if (!getrect(xy_obj, &rect)) return NULL; Brush* brush = getbrush(self, fill, outline); if (brush) { self->graphics->FillEllipse(brush, rect); delete brush; } Pen* pen = getpen(self, outline, fill); if (pen) { self->graphics->DrawEllipse(pen, rect); delete pen; } Py_INCREF(Py_None); return Py_None; } static PyObject* draw_image(WCKDrawObject* self, PyObject* args, PyObject* kw) { WCKImageObject* image; PyObject* dst_obj; int filter = NEAREST; static char* kwlist[] = { "image", "dst", "filter", NULL }; /* FIXME: swap arguments? (xy, image) */ if (!PyArg_ParseTupleAndKeywords(args, kw, "O!O|i:image", kwlist, &WCKImageType, &image, &dst_obj, &filter)) return NULL; RectF dst; if (!getrect(dst_obj, &dst)) return NULL; self->graphics->SetInterpolationMode(getinterpolation(filter)); /* FIXME: add support for source rectangles */ self->graphics->DrawImage(image->image, dst); Py_INCREF(Py_None); return Py_None; } static PyObject* draw_line(WCKDrawObject* self, PyObject* args, PyObject* kw) { PyObject* xy_obj; PyObject* fill = NULL; static char* kwlist[] = { "xy", "fill", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kw, "O|O:line", kwlist, &xy_obj, &fill)) return NULL; /* FIXME: deal with fill object */ Color color(255, 255, 255); int count; PointF *xy = getpoints(xy_obj, &count); if (!xy) return NULL; if (count < 2) { PyErr_SetString(PyExc_ValueError, "at least two points required"); delete xy; return NULL; } Pen* pen = getpen(self, fill); if (pen) { self->graphics->DrawLines(pen, xy, count); delete pen; } delete xy; Py_INCREF(Py_None); return Py_None; } static PyObject* draw_pieslice(WCKDrawObject* self, PyObject* args, PyObject* kw) { PyObject* xy_obj; float start, end; PyObject* fill = NULL; PyObject* outline = NULL; static char* kwlist[] = { "xy", "start", "end", "fill", "outline", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kw, "Off|OO:pieslice", kwlist, &xy_obj, &start, &end, &fill, &outline)) return NULL; RectF rect; if (!getrect(xy_obj, &rect)) return NULL; Brush* brush = getbrush(self, fill, outline); if (brush) { self->graphics->FillPie(brush, rect, start, start-end); delete brush; } Pen* pen = getpen(self, outline, fill); if (pen) { self->graphics->DrawPie(pen, rect, start, start-end); delete pen; } Py_INCREF(Py_None); return Py_None; } static PyObject* draw_polygon(WCKDrawObject* self, PyObject* args, PyObject* kw) { PyObject* xy_obj; PyObject* fill = NULL; PyObject* outline = NULL; static char* kwlist[] = { "xy", "fill", "outline", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kw, "O|OO:polygon", kwlist, &xy_obj, &fill, &outline)) return NULL; int count; PointF *xy = getpoints(xy_obj, &count); if (!xy) return NULL; if (count < 2) { PyErr_SetString(PyExc_ValueError, "at least two points required"); delete xy; return NULL; } Brush* brush = getbrush(self, fill, outline); if (brush) { self->graphics->FillPolygon(brush, xy, count); delete brush; } Pen* pen = getpen(self, outline, fill); if (pen) { self->graphics->DrawPolygon(pen, xy, count); delete pen; } delete xy; Py_INCREF(Py_None); return Py_None; } static PyObject* draw_rectangle(WCKDrawObject* self, PyObject* args, PyObject* kw) { PyObject* xy_obj; PyObject* fill = NULL; PyObject* outline = NULL; static char* kwlist[] = { "xy", "fill", "outline", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kw, "O|OO:rectangle", kwlist, &xy_obj, &fill, &outline)) return NULL; RectF rect; if (!getrect(xy_obj, &rect)) return NULL; Brush* brush = getbrush(self, fill, outline); if (brush) { self->graphics->FillRectangle(brush, rect); delete brush; } Pen* pen = getpen(self, outline, fill); if (pen) { self->graphics->DrawRectangle(pen, rect); delete pen; } Py_INCREF(Py_None); return Py_None; } static PyObject* draw_text(WCKDrawObject* self, PyObject* args, PyObject* kw) { PyObject* xy_obj; PyObject* text_obj; WCKFontObject* font; PyObject* fill = NULL; static char* kwlist[] = { "xy", "text", "font", "fill", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kw, "OOO!|O:text", kwlist, &xy_obj, &text_obj, &WCKFontType, &font, &fill)) return NULL; int count; PointF *xy = getpoints(xy_obj, &count); if (!xy) return NULL; if (count != 1) { PyErr_SetString(PyExc_ValueError, "exactly one point required"); delete xy; return NULL; } RectF rect(xy[0].X, xy[0].Y, 0, 0); Brush* brush = NULL; if (font->brush) brush = font->brush; else brush = getbrush(self, fill); if (brush) { WCHAR* text = makewstring(text_obj); self->graphics->DrawString(text, -1, font->font, rect, font->format, brush); delete text; if (brush != font->brush) delete brush; } delete xy; Py_INCREF(Py_None); return Py_None; } static PyObject* draw_textsize(WCKDrawObject* self, PyObject* args, PyObject* kw) { static WCHAR* average = L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; PyObject* text_obj; WCKFontObject* font; static char* kwlist[] = { "text", "font", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kw, "OO!:textsize", kwlist, &text_obj, &WCKFontType, &font)) return NULL; WCHAR* text; if (text_obj == Py_None) text = average; else text = makewstring(text_obj); RectF rect; RectF bbox; self->graphics->MeasureString(text, -1, font->font, rect, font->format, &bbox); if (text_obj == Py_None) bbox.Width = bbox.Width / 52; else delete text; return Py_BuildValue("ff", bbox.Width, bbox.Height); } static void draw_dealloc(WCKDrawObject* self) { delete self->graphics; PyMem_DEL(self); } static PyMethodDef draw_methods[] = { {"arc", (PyCFunction) draw_arc, METH_VARARGS|METH_KEYWORDS}, {"chord", (PyCFunction) draw_chord, METH_VARARGS|METH_KEYWORDS}, {"ellipse", (PyCFunction) draw_ellipse, METH_VARARGS|METH_KEYWORDS}, {"image", (PyCFunction) draw_image, METH_VARARGS|METH_KEYWORDS}, {"line", (PyCFunction) draw_line, METH_VARARGS|METH_KEYWORDS}, {"pieslice", (PyCFunction) draw_pieslice, METH_VARARGS|METH_KEYWORDS}, // {"point", (PyCFunction) draw_point, METH_VARARGS|METH_KEYWORDS}, {"polygon", (PyCFunction) draw_polygon, METH_VARARGS|METH_KEYWORDS}, {"rectangle", (PyCFunction) draw_rectangle, METH_VARARGS|METH_KEYWORDS}, {"text", (PyCFunction) draw_text, METH_VARARGS|METH_KEYWORDS}, {"textsize", (PyCFunction) draw_textsize, METH_VARARGS|METH_KEYWORDS}, {NULL, NULL} }; static PyObject* draw_getattr(WCKDrawObject* self, char* name) { return Py_FindMethod(draw_methods, (PyObject*) self, name); } statichere PyTypeObject WCKDrawType = { PyObject_HEAD_INIT(NULL) 0, "Draw", sizeof(WCKDrawObject), 0, (destructor)draw_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ (getattrfunc)draw_getattr, /*tp_getattr*/ }; /* -------------------------------------------------------------------- */ /* Font */ PyObject* wckdraw_font(PyObject* self_, PyObject* args, PyObject* kw) { PyObject* color; PyObject* family_obj; float size = 16.0F; int opacity = 255; static char* kwlist[] = { "color", "family", "opacity", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|i:Font", kwlist, &color, &family_obj, &opacity)) return NULL; WCKFontObject* self = PyObject_NEW(WCKFontObject, &WCKFontType); if (!self) return NULL; WCHAR* family = makewstring(family_obj); if (!family) return NULL; self->font = new Font(family, size, FontStyleRegular, UnitPixel); delete family; if (color != Py_None) { self->brush = new SolidBrush(getcolor(color, opacity)); } else self->brush = NULL; self->format = StringFormat::GenericTypographic(); return (PyObject*) self; } static void font_dealloc(WCKFontObject* self) { delete self->font; delete self->format; PyMem_DEL(self); } static PyMethodDef font_methods[] = { // {"getmask", (PyCFunction) font_getmask, METH_VARARGS}, // {"getsize", (PyCFunction) font_getsize, METH_VARARGS}, {NULL, NULL} }; static PyObject* font_getattr(WCKFontObject* self, char* name) { return Py_FindMethod(font_methods, (PyObject*) self, name); } statichere PyTypeObject WCKFontType = { PyObject_HEAD_INIT(NULL) 0, "Font", sizeof(WCKFontObject), 0, (destructor)font_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ (getattrfunc)font_getattr, /*tp_getattr*/ }; /* -------------------------------------------------------------------- */ /* Pen */ PyObject* wckdraw_pen(PyObject* self_, PyObject* args, PyObject* kw) { PyObject* color; float width = 1; int opacity = 255; static char* kwlist[] = { "color", "width", "opacity", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kw, "O|fi:Pen", kwlist, &color, &width, &opacity)) return NULL; WCKPenObject* self = PyObject_NEW(WCKPenObject, &WCKPenType); if (!self) return NULL; self->pen = new Pen(getcolor(color, opacity), width); if (!self->pen) { PyErr_SetString(PyExc_TypeError, "cannot convert object to pen"); PyMem_DEL(self); return NULL; } return (PyObject*) self; } static void pen_dealloc(WCKPenObject* self) { delete self->pen; PyMem_DEL(self); } static PyMethodDef pen_methods[] = { {NULL, NULL} }; static PyObject* pen_getattr(WCKPenObject* self, char* name) { return Py_FindMethod(pen_methods, (PyObject*) self, name); } statichere PyTypeObject WCKPenType = { PyObject_HEAD_INIT(NULL) 0, "Pen", sizeof(WCKPenObject), 0, (destructor)pen_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ (getattrfunc)pen_getattr, /*tp_getattr*/ }; /* -------------------------------------------------------------------- */ /* Brush */ PyObject* wckdraw_brush(PyObject* self_, PyObject* args, PyObject* kw) { PyObject* color; int opacity = 255; static char* kwlist[] = { "color", "opacity", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kw, "O|i:Brush", kwlist, &color, &opacity)) return NULL; WCKBrushObject* self = PyObject_NEW(WCKBrushObject, &WCKBrushType); if (!self) return NULL; self->brush = new SolidBrush(getcolor(color, opacity)); if (!self->brush) { PyErr_SetString(PyExc_TypeError, "cannot convert object to brush"); PyMem_DEL(self); return NULL; } return (PyObject*) self; } static void brush_dealloc(WCKBrushObject* self) { delete self->brush; PyMem_DEL(self); } static PyMethodDef brush_methods[] = { {NULL, NULL} }; static PyObject* brush_getattr(WCKBrushObject* self, char* name) { return Py_FindMethod(brush_methods, (PyObject*) self, name); } statichere PyTypeObject WCKBrushType = { PyObject_HEAD_INIT(NULL) 0, "Brush", sizeof(WCKBrushObject), 0, (destructor)brush_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ (getattrfunc)brush_getattr, /*tp_getattr*/ }; /* -------------------------------------------------------------------- */ static PyObject* makeimage(Bitmap* bitmap) { WCKImageObject* self = PyObject_NEW(WCKImageObject, &WCKImageType); if (!self) return NULL; self->image = bitmap; return (PyObject*) self; } PyObject* wckdraw_image_new(PyObject* draw, PyObject* args) { char* mode; int width, height; PyObject* color = NULL; if (!PyArg_ParseTuple(args, "s(ii)|O:new", &mode, &width, &height, &color)) return NULL; PixelFormat format = getformat(mode); if (!format) return NULL; Bitmap* bitmap = new Bitmap(width, height, format); if (color != Py_None) { Graphics graphics(bitmap); Brush* brush = new SolidBrush(getcolor(color)); graphics.FillRectangle(brush, 0, 0, width, height); delete brush; } return makeimage(bitmap); } PyObject* wckdraw_image_open(PyObject* draw, PyObject* args) { PyObject* filename_obj; if (!PyArg_ParseTuple(args, "O:open", &filename_obj)) return NULL; WCHAR* filename = makewstring(filename_obj); if (!filename) return NULL; Bitmap* bitmap = new Bitmap(filename); delete filename; int status = bitmap->GetLastStatus(); if (status != Ok) { PyErr_Format(PyExc_IOError, "cannot load image (status %d)", status); delete bitmap; return NULL; } return makeimage(bitmap); } static PyObject* image_tostring(WCKImageObject* self, PyObject* args) { /* FIXME: fix byte order (BGRA vs. RGBA). note that the alpha canvas stuff wants pre-multiplied BGRA, which is what PARGB returns. */ char* mode = NULL; if (!PyArg_ParseTuple(args, "|s:tostring", &mode)) return NULL; PixelFormat format; if (mode) { format = getformat(mode); if (!format) return NULL; } else format = self->image->GetPixelFormat(); int width = self->image->GetWidth(); int height = self->image->GetHeight(); BitmapData bitmap; Rect rect(0, 0, width, height); int pixel; switch (format) { case PixelFormat8bppIndexed: pixel = 1; break; case PixelFormat24bppRGB: pixel = 3; break; case PixelFormat32bppRGB: case PixelFormat32bppARGB: case PixelFormat32bppPARGB: pixel = 4; break; } self->image->LockBits( &rect, ImageLockModeRead, format, &bitmap ); PyObject* buffer = PyString_FromStringAndSize( NULL, width * height * pixel ); char* p = PyString_AS_STRING(buffer); char* q = (char*) bitmap.Scan0; for (int y = 0; y < height; y++) { memcpy(p, q, width * pixel); p = p + width * pixel; q = q + bitmap.Stride; } self->image->UnlockBits(&bitmap); return buffer; } static void image_dealloc(WCKImageObject* self) { delete self->image; PyMem_DEL(self); } static PyMethodDef image_methods[] = { {"tostring", (PyCFunction) image_tostring, METH_VARARGS}, {NULL, NULL} }; static PyObject* image_getattr(WCKImageObject* self, char* name) { if (!strcmp(name, "mode")) { char* mode; switch (self->image->GetPixelFormat()) { case PixelFormat1bppIndexed: mode = "1"; break; case PixelFormat8bppIndexed: mode = "P"; break; case PixelFormat24bppRGB: mode = "RGB"; break; case PixelFormat32bppRGB: mode = "RGBX"; break; case PixelFormat32bppARGB: mode = "RGBA"; break; case PixelFormat32bppPARGB: mode = "RGBa"; break; default: mode = "RGB"; } return PyString_FromString(mode); } if (!strcmp(name, "size")) return Py_BuildValue("ii", self->image->GetWidth(), self->image->GetHeight()); return Py_FindMethod(image_methods, (PyObject*) self, name); } statichere PyTypeObject WCKImageType = { PyObject_HEAD_INIT(NULL) 0, "Image", sizeof(WCKImageObject), 0, (destructor)image_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ (getattrfunc)image_getattr, /*tp_getattr*/ };