summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Chaplin <>2012-04-21 21:40:53 +0800
committerSteve Chaplin <>2012-04-21 21:40:53 +0800
commita2d54c73e2d50159fef593d112e1c1dccb8b4222 (patch)
tree6a158862c8798f6f47aef264f4e17071df25e470
parent0c5c297b67381f341e089e5a26c511807fa9290f (diff)
downloadpycairo-a2d54c73e2d50159fef593d112e1c1dccb8b4222.tar.gz
Implement ImageSurface.get_data(), using some code from Paul Colomiets.
bug #44935.
-rw-r--r--doc/reference/surfaces.rst7
-rw-r--r--src/cairomodule.c2
-rw-r--r--src/surface.c30
-rwxr-xr-xtest/isurface_get_data.py113
-rwxr-xr-xtest/isurface_get_data_memleak.py33
5 files changed, 137 insertions, 48 deletions
diff --git a/doc/reference/surfaces.rst b/doc/reference/surfaces.rst
index 2058a2a..bc57a6e 100644
--- a/doc/reference/surfaces.rst
+++ b/doc/reference/surfaces.rst
@@ -313,12 +313,9 @@ those defined in :ref:`FORMAT attributes <constants_FORMAT>`.
.. method:: get_data()
- Not yet available in Python 3
-
- .. comment block - old docs:
- :returns: a Python buffer object for the data of the *ImageSurface*, for direct inspection or modification.
+ :returns: a Python memoryview for the data of the *ImageSurface*, for direct inspection or modification.
- .. versionadded:: 1.2
+ .. versionadded:: 1.10.0
.. method:: get_format()
diff --git a/src/cairomodule.c b/src/cairomodule.c
index 675676d..886c6d5 100644
--- a/src/cairomodule.c
+++ b/src/cairomodule.c
@@ -255,7 +255,7 @@ PyInit__cairo(void)
#endif
#ifdef CAIRO_HAS_RECORDING_SURFACE
if (PyType_Ready(&PycairoRecordingSurface_Type) < 0)
- return;
+ return NULL;
#endif
#ifdef CAIRO_HAS_SVG_SURFACE
if (PyType_Ready(&PycairoSVGSurface_Type) < 0)
diff --git a/src/surface.c b/src/surface.c
index 35ca169..7d00141 100644
--- a/src/surface.c
+++ b/src/surface.c
@@ -546,9 +546,7 @@ image_surface_format_stride_for_width (PyObject *self, PyObject *args) {
static PyObject *
image_surface_get_data (PycairoImageSurface *o) {
- PyErr_SetString(PyExc_NotImplementedError, "Surface.get_data: Not Implemented yet.");
- return NULL;
- // return PyBuffer_FromReadWriteObject((PyObject *)o, 0, Py_END_OF_BUFFER);
+ return PyMemoryView_FromObject((PyObject*)o);
}
static PyObject *
@@ -573,6 +571,24 @@ image_surface_get_width (PycairoImageSurface *o) {
/* Buffer interface functions, used by ImageSurface.get_data() */
+static int
+image_surface_buffer_getbufferproc (PycairoImageSurface *o, Py_buffer *view,
+ int flags) {
+ cairo_surface_t *surface = o->surface;
+ int height, stride;
+ void *data;
+
+ height = cairo_image_surface_get_height (surface);
+ stride = cairo_image_surface_get_stride (surface);
+ data = cairo_image_surface_get_data (surface);
+
+ if(!PyBuffer_FillInfo(view, (PyObject *)o, data,
+ height * stride, 0, PyBUF_CONTIG))
+ return 0;
+ return -1;
+}
+
+/* Buffer interface functions, used by ImageSurface.get_data() */
/*
static int
image_surface_buffer_getreadbuf (PycairoImageSurface *o, int segment,
@@ -630,6 +646,11 @@ static PyBufferProcs image_surface_as_buffer = {
(charbufferproc) NULL,
};
*/
+static PyBufferProcs image_surface_as_buffer = {
+ (getbufferproc) image_surface_buffer_getbufferproc,
+ (releasebufferproc) NULL,
+};
+
static PyMethodDef image_surface_methods[] = {
{"create_for_data",(PyCFunction)image_surface_create_for_data,
@@ -669,8 +690,7 @@ PyTypeObject PycairoImageSurface_Type = {
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
- // &image_surface_as_buffer, /* tp_as_buffer */
- 0, /* tp_as_buffer */
+ &image_surface_as_buffer, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
0, /* tp_doc */
0, /* tp_traverse */
diff --git a/test/isurface_get_data.py b/test/isurface_get_data.py
index f2662d4..6875042 100755
--- a/test/isurface_get_data.py
+++ b/test/isurface_get_data.py
@@ -5,46 +5,85 @@ Test ImageSurface.get_data()
import tempfile
import cairo
-import numpy
if not (cairo.HAS_IMAGE_SURFACE and cairo.HAS_PNG_FUNCTIONS):
raise SystemExit ('cairo was not compiled with ImageSurface and PNG support')
w, h = 128, 128
-surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h)
-ctx = cairo.Context(surface)
-
-ctx.set_source_rgb(1, 1, 1) # white
-ctx.set_operator(cairo.OPERATOR_SOURCE)
-ctx.paint()
-
-# Draw out the triangle using absolute coordinates
-ctx.move_to(w/2, h/3)
-ctx.line_to(2*w/3, 2*h/3)
-ctx.rel_line_to(-1*w/3, 0)
-ctx.close_path()
-
-ctx.set_source_rgb(0, 0, 0) # black
-ctx.set_line_width(15)
-ctx.stroke()
-_, outFileName = tempfile.mkstemp(prefix='pycairo_', suffix='.png')
-surface.write_to_png(outFileName)
-print "see %s output file" % outFileName
-
-# modify surface using numpy
-buf = surface.get_data()
-# alternative which should work (?) but reports
-# TypeError: buffer is read-only
-# - is a Python bug?
-#buf = buffer (surface1)
-
-a = numpy.ndarray(shape=(w,h,4), dtype=numpy.uint8, buffer=buf)
-
-# draw a vertical line
-a[:,40,0] = 255 # byte 0 is blue on little-endian systems
-a[:,40,1] = 0
-a[:,40,2] = 0
-_, outFileName = tempfile.mkstemp(prefix='pycairo_', suffix='.png')
-surface.write_to_png(outFileName)
-print "see %s output file" % outFileName
+def create_surface():
+ "create black triangle on white background"
+ surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h)
+ ctx = cairo.Context(surface)
+
+ ctx.set_source_rgb(1, 1, 1) # white
+ ctx.set_operator(cairo.OPERATOR_SOURCE)
+ ctx.paint()
+
+ # Draw out the triangle using absolute coordinates
+ ctx.move_to(w/2, h/3)
+ ctx.line_to(2*w/3, 2*h/3)
+ ctx.rel_line_to(-1*w/3, 0)
+ ctx.close_path()
+
+ ctx.set_source_rgb(0, 0, 0) # black
+ ctx.set_line_width(15)
+ ctx.stroke()
+ return surface
+
+
+def test_python_buffer():
+ "get_data() and modify data using Python"
+ surface = create_surface()
+ _, f1 = tempfile.mkstemp(prefix='pycairo_', suffix='.png')
+ surface.write_to_png(f1)
+
+ buf = surface.get_data()
+ stride = surface.get_stride()
+ for i in range(h):
+ offset = i * stride + 120
+ buf[offset] = b'\xFF'
+ buf[offset + 1] = b'\x00'
+ buf[offset + 2] = b'\x00'
+
+ _, f2 = tempfile.mkstemp(prefix='pycairo_', suffix='.png')
+ surface.write_to_png(f2)
+ print("""\
+test_python_buffer:
+ original data: %s
+ modified data: %s
+""" % (f1, f2))
+
+
+def test_numpy_and_python_buffer():
+ "get_data() and modify data using numpy"
+ try:
+ import numpy
+ except:
+ print("numpy not installed")
+ return
+
+ surface = create_surface()
+ _, f1 = tempfile.mkstemp(prefix='pycairo_', suffix='.png')
+ surface.write_to_png(f1)
+
+ buf = surface.get_data()
+
+ a = numpy.ndarray(shape=(w,h,4), dtype=numpy.uint8, buffer=buf)
+
+ # draw a vertical line
+ a[:,40,0] = 255 # byte 0 is blue on little-endian systems
+ a[:,40,1] = 0
+ a[:,40,2] = 0
+
+ _, f2 = tempfile.mkstemp(prefix='pycairo_', suffix='.png')
+ surface.write_to_png(f2)
+ print("""\
+test_numpy_and_python_buffer:
+ original data: %s
+ modified data: %s
+""" % (f1, f2))
+
+
+test_python_buffer()
+test_numpy_and_python_buffer()
diff --git a/test/isurface_get_data_memleak.py b/test/isurface_get_data_memleak.py
new file mode 100755
index 0000000..5a30c1e
--- /dev/null
+++ b/test/isurface_get_data_memleak.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+"""test cairo.ImageSurface.get_data() for a memory leak
+"""
+
+import array
+import resource
+import tempfile
+
+import cairo
+
+
+pagesize = resource.getpagesize()
+
+if not (cairo.HAS_IMAGE_SURFACE and cairo.HAS_PNG_FUNCTIONS):
+ raise SystemExit ('cairo was not compiled with ImageSurface and PNG support')
+
+width, height = 32, 32
+
+while True:
+ for i in range(100000):
+ surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
+ ctx = cairo.Context(surface)
+ data = surface.get_data()
+ b = memoryview(memoryview(data))
+ del surface
+ del ctx
+ b = bytes(data)
+
+ surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
+ #ctx = cairo.Context(surface)
+ b = bytes(surface.get_data())
+
+ print(resource.getrusage(resource.RUSAGE_SELF).ru_maxrss * pagesize)