Skip to content

Commit

Permalink
Merge pull request #47 from lazka/script-surface
Browse files Browse the repository at this point in the history
Add cairo.ScriptSurface. Fixes #17
  • Loading branch information
lazka authored Jul 10, 2017
2 parents ed2136a + c8713e8 commit 7396302
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 10 deletions.
9 changes: 9 additions & 0 deletions cairo/cairomodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
10 changes: 0 additions & 10 deletions cairo/device.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -139,10 +133,6 @@ PyTypeObject PycairoDevice_Type = {
#if CAIRO_HAS_SCRIPT_SURFACE
#include <cairo-script.h>

typedef struct {
PycairoDevice base;
} PycairoScriptDevice;

static const cairo_user_data_key_t device_base_object_key;

static void
Expand Down
10 changes: 10 additions & 0 deletions cairo/private.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
92 changes: 92 additions & 0 deletions cairo/surface.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -1169,6 +1174,93 @@ PyTypeObject PycairoPDFSurface_Type = {
};
#endif /* CAIRO_HAS_PDF_SURFACE */

#ifdef CAIRO_HAS_SCRIPT_SURFACE
#include <cairo-script.h>

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
Expand Down
34 changes: 34 additions & 0 deletions docs/reference/surfaces.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
34 changes: 34 additions & 0 deletions tests/test_surface.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 7396302

Please sign in to comment.