diff --git a/cairo/cairomodule.c b/cairo/cairomodule.c index 6a5e5f7b..198f3318 100644 --- a/cairo/cairomodule.c +++ b/cairo/cairomodule.c @@ -266,8 +266,13 @@ PYCAIRO_MOD_INIT(_cairo) if (PyType_Ready(&PycairoDevice_Type) < 0) return PYCAIRO_MOD_ERROR_VAL; + +#ifdef CAIRO_HAS_SCRIPT_SURFACE if (PyType_Ready(&PycairoScriptDevice_Type) < 0) return PYCAIRO_MOD_ERROR_VAL; + if (PyType_Ready(&PycairoScriptSurface_Type) < 0) + return PYCAIRO_MOD_ERROR_VAL; +#endif if (PyType_Ready(&PycairoRegion_Type) < 0) return PYCAIRO_MOD_ERROR_VAL; @@ -380,8 +385,12 @@ PYCAIRO_MOD_INIT(_cairo) Py_INCREF(&PycairoDevice_Type); PyModule_AddObject(m, "Device", (PyObject *)&PycairoDevice_Type); +#ifdef CAIRO_HAS_SCRIPT_SURFACE Py_INCREF(&PycairoScriptDevice_Type); PyModule_AddObject(m, "ScriptDevice", (PyObject *)&PycairoScriptDevice_Type); + Py_INCREF(&PycairoScriptSurface_Type); + PyModule_AddObject(m, "ScriptSurface", (PyObject *)&PycairoScriptSurface_Type); +#endif #ifdef CAIRO_HAS_IMAGE_SURFACE Py_INCREF(&PycairoImageSurface_Type); diff --git a/cairo/device.c b/cairo/device.c index a758ce47..d13b630c 100644 --- a/cairo/device.c +++ b/cairo/device.c @@ -32,12 +32,6 @@ #include "config.h" #include "private.h" - -typedef struct { - PyObject_HEAD - cairo_device_t *device; -} PycairoDevice; - static void device_dealloc(PycairoDevice *obj) { if (obj->device) { @@ -139,10 +133,6 @@ PyTypeObject PycairoDevice_Type = { #if CAIRO_HAS_SCRIPT_SURFACE #include -typedef struct { - PycairoDevice base; -} PycairoScriptDevice; - static const cairo_user_data_key_t device_base_object_key; static void diff --git a/cairo/private.h b/cairo/private.h index cd7d0e30..4bb823dd 100644 --- a/cairo/private.h +++ b/cairo/private.h @@ -92,6 +92,16 @@ PyObject *PycairoScaledFont_FromScaledFont (cairo_scaled_font_t *scaled_font); extern PyTypeObject PycairoSurface_Type; extern PyTypeObject PycairoImageSurface_Type; +typedef struct { + PyObject_HEAD + cairo_device_t *device; +} PycairoDevice; + +#if CAIRO_HAS_SCRIPT_SURFACE +typedef PycairoDevice PycairoScriptDevice; +extern PyTypeObject PycairoScriptSurface_Type; +#endif + #if CAIRO_HAS_PDF_SURFACE extern PyTypeObject PycairoPDFSurface_Type; #endif diff --git a/cairo/surface.c b/cairo/surface.c index 9b805061..f8fbf11e 100644 --- a/cairo/surface.c +++ b/cairo/surface.c @@ -112,6 +112,11 @@ PycairoSurface_FromSurface (cairo_surface_t *surface, PyObject *base) { case CAIRO_SURFACE_TYPE_XLIB: type = &PycairoXlibSurface_Type; break; +#endif +#if CAIRO_HAS_SCRIPT_SURFACE + case CAIRO_SURFACE_TYPE_SCRIPT: + type = &PycairoScriptSurface_Type; + break; #endif default: type = &PycairoSurface_Type; @@ -1169,6 +1174,93 @@ PyTypeObject PycairoPDFSurface_Type = { }; #endif /* CAIRO_HAS_PDF_SURFACE */ +#ifdef CAIRO_HAS_SCRIPT_SURFACE +#include + +typedef PycairoSurface PycairoScriptSurface; + +static PyObject * +script_surface_new (PyTypeObject *type, PyObject *args, PyObject *kwds) { + cairo_content_t content; + double width, height; + PyObject *pydevice; + + if (!PyArg_ParseTuple (args, "O!idd:ScriptSurface.__new__", + &PycairoScriptDevice_Type, &pydevice, &content, &width, &height)) + return NULL; + + return PycairoSurface_FromSurface ( + cairo_script_surface_create ( + ((PycairoDevice*)pydevice)->device, content, width, height), + NULL); +} + +static PyObject * +script_surface_create_for_target (PyTypeObject *type, PyObject *args) { + PyObject *pydevice, *target; + + if (!PyArg_ParseTuple (args, "O!O!:ScriptSurface.create_for_target", + &PycairoScriptDevice_Type, &pydevice, &PycairoSurface_Type, &target)) + return NULL; + + return PycairoSurface_FromSurface ( + cairo_script_surface_create_for_target ( + ((PycairoDevice*)pydevice)->device, ((PycairoSurface*)target)->surface), + NULL); +} + +static PyMethodDef script_surface_methods[] = { + {"create_for_target", + (PyCFunction)script_surface_create_for_target, METH_VARARGS | METH_CLASS}, + {NULL, NULL, 0, NULL}, +}; + +PyTypeObject PycairoScriptSurface_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "cairo.ScriptSurface", /* tp_name */ + sizeof(PycairoScriptSurface), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + script_surface_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + &PycairoSurface_Type, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + (newfunc)script_surface_new, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ +}; + +#endif /* CAIRO_HAS_SCRIPT_SURFACE */ + /* Class PSSurface(Surface) ----------------------------------------------- */ #ifdef CAIRO_HAS_PS_SURFACE diff --git a/docs/reference/surfaces.rst b/docs/reference/surfaces.rst index 983a51f5..7551f781 100644 --- a/docs/reference/surfaces.rst +++ b/docs/reference/surfaces.rst @@ -1022,3 +1022,37 @@ if it is available. :returns: the width of the X Drawable underlying the surface in pixels. .. versionadded:: 1.2 + + +class ScriptSurface(:class:`Surface`) +===================================== + +The script surface provides the ability to render to a native script that +matches the cairo drawing model. The scripts can be replayed using tools under +the util/cairo-script directory, or with cairo-perf-trace. + + +.. class:: ScriptSurface(script, content, width, height) + + :param cairo.ScriptDevice script: the script (output device) + :param cairo.Content content: the content of the surface + :param float width: width in pixels + :param float height: height in pixels + :rtype: cairo.ScriptSurface + :raises cairo.Error: + + Create a new surface that will emit its rendering through ``script``. + + .. versionadded:: 1.14 + + .. classmethod:: create_for_target(script, target) + + :param cairo.ScriptDevice script: the script (output device) + :param cairo.Surface target: a target surface to wrap + :rtype: cairo.ScriptSurface + :raises cairo.Error: + + Create a proxy surface that will render to ``target`` and record the + operations to ``device``. + + .. versionadded:: 1.14 diff --git a/tests/test_surface.py b/tests/test_surface.py index 759c307e..fe337087 100644 --- a/tests/test_surface.py +++ b/tests/test_surface.py @@ -141,3 +141,37 @@ def test_surface_from_stream_closed_before_finished(): fileobj.close() with pytest.raises(IOError): surface.finish() + + +def test_script_surface(): + f = io.BytesIO() + dev = cairo.ScriptDevice(f) + surface = cairo.ScriptSurface(dev, cairo.Content.COLOR_ALPHA, 42, 10) + assert isinstance(surface, cairo.ScriptSurface) + cairo.Context(surface).paint() + dev.flush() + assert b"42" in f.getvalue() + assert b"paint" in f.getvalue() + + +def test_script_surface_create_for_target(): + # paint the script proxy + f = io.BytesIO() + dev = cairo.ScriptDevice(f) + surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 10, 10) + script = cairo.ScriptSurface.create_for_target(dev, surface) + assert isinstance(script, cairo.ScriptSurface) + ctx = cairo.Context(script) + ctx.set_source_rgb(0.25, 0.5, 1.0) + ctx.paint() + assert b"paint" in f.getvalue() + surface.flush() + image_data = bytes(surface.get_data()) + + # check if the result is the same + surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 10, 10) + ctx = cairo.Context(surface) + ctx.set_source_rgb(0.25, 0.5, 1.0) + ctx.paint() + surface.flush() + assert bytes(surface.get_data()) == image_data