diff options
author | Steve Chaplin <> | 2012-04-21 21:40:53 +0800 |
---|---|---|
committer | Steve Chaplin <> | 2012-04-21 21:40:53 +0800 |
commit | a2d54c73e2d50159fef593d112e1c1dccb8b4222 (patch) | |
tree | 6a158862c8798f6f47aef264f4e17071df25e470 | |
parent | 0c5c297b67381f341e089e5a26c511807fa9290f (diff) | |
download | pycairo-a2d54c73e2d50159fef593d112e1c1dccb8b4222.tar.gz |
Implement ImageSurface.get_data(), using some code from Paul Colomiets.
bug #44935.
-rw-r--r-- | doc/reference/surfaces.rst | 7 | ||||
-rw-r--r-- | src/cairomodule.c | 2 | ||||
-rw-r--r-- | src/surface.c | 30 | ||||
-rwxr-xr-x | test/isurface_get_data.py | 113 | ||||
-rwxr-xr-x | test/isurface_get_data_memleak.py | 33 |
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) |