Adds beginning support for get_data() and create_for_data() back into the bindings. Also adds/cleans up tests to demonstrate functionality. Also modifies INSTALL to instruct users how to use different versions of Python. --- INSTALL | 2 + src/surface.c | 25 ++++++++++-------- test/isurface_create_for_data2.py | 2 +- test/isurface_create_from_png.py | 8 +++--- test/isurface_get_data.py | 50 ------------------------------------- test/isurface_get_data1.py | 50 +++++++++++++++++++++++++++++++++++++ test/isurface_get_data2.py | 47 ++++++++++++++++++++++++++++++++++ 7 files changed, 118 insertions(+), 66 deletions(-) delete mode 100755 test/isurface_get_data.py create mode 100755 test/isurface_get_data1.py create mode 100755 test/isurface_get_data2.py diff --git a/INSTALL b/INSTALL index 14357b8..0b96f0c 100644 --- a/INSTALL +++ b/INSTALL @@ -9,7 +9,9 @@ $ ./waf install Use $ python3 ./waf ... if you have python2 and python3 installed, and the default is python 2. +However, you specify a full path to the python binary using a command like: +$ PYTHON=/usr/bin/python3 ./waf configure Testing ------- diff --git a/src/surface.c b/src/surface.c index 35ca169..74d9dbc 100644 --- a/src/surface.c +++ b/src/surface.c @@ -418,20 +418,15 @@ static PyObject * image_surface_create_for_data (PyTypeObject *type, PyObject *args) { cairo_surface_t *surface; cairo_format_t format; - unsigned char *buffer; int width, height, stride = -1, res; - Py_ssize_t buffer_len; PyObject *obj; - - // buffer function disabled - PyErr_SetString(PyExc_NotImplementedError, "Surface.create_for_data: Not Implemented yet."); - return NULL; + Py_buffer buffer; if (!PyArg_ParseTuple(args, "Oiii|i:Surface.create_for_data", &obj, &format, &width, &height, &stride)) return NULL; - res = PyObject_AsWriteBuffer (obj, (void **)&buffer, &buffer_len); + res = PyObject_GetBuffer (obj, &buffer, PyBUF_WRITABLE); if (res == -1) return NULL; @@ -452,12 +447,12 @@ image_surface_create_for_data (PyTypeObject *type, PyObject *args) { return NULL; } } - if (height * stride > buffer_len) { + if (height * stride > buffer.len) { PyErr_SetString(PyExc_TypeError, "buffer is not long enough"); return NULL; } Py_BEGIN_ALLOW_THREADS; - surface = cairo_image_surface_create_for_data (buffer, format, width, + surface = cairo_image_surface_create_for_data (buffer.buf, format, width, height, stride); Py_END_ALLOW_THREADS; return PycairoSurface_FromSurface(surface, obj); @@ -546,9 +541,17 @@ 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."); + Py_buffer buffer; + void *data; + int height, stride; + + height = cairo_image_surface_get_height (o->surface); + stride = cairo_image_surface_get_stride (o->surface); + data = cairo_image_surface_get_data (o->surface); + + if(!PyBuffer_FillInfo(&buffer, NULL, data, height * stride, 0, PyBUF_CONTIG)) + return PyMemoryView_FromBuffer(&buffer); return NULL; - // return PyBuffer_FromReadWriteObject((PyObject *)o, 0, Py_END_OF_BUFFER); } static PyObject * diff --git a/test/isurface_create_for_data2.py b/test/isurface_create_for_data2.py index c4f290b..29a9984 100755 --- a/test/isurface_create_for_data2.py +++ b/test/isurface_create_for_data2.py @@ -27,4 +27,4 @@ surface = cairo.ImageSurface.create_for_data (data, cairo.FORMAT_ARGB32, width, height) ctx = cairo.Context(surface) surface.write_to_png(fileName) -print "see %s output file" % fileName +print("see %s output file" % fileName) diff --git a/test/isurface_create_from_png.py b/test/isurface_create_from_png.py index 8d1ce5d..ae8af18 100755 --- a/test/isurface_create_from_png.py +++ b/test/isurface_create_from_png.py @@ -18,7 +18,7 @@ surface = cairo.ImageSurface.create_from_png(inFileName) # write to filename _, outFileName = tempfile.mkstemp(prefix='pycairo_', suffix='.png') surface.write_to_png(outFileName) -print "see %s output file" % outFileName +print("see %s output file" % outFileName) # write to file object h, outFileName = tempfile.mkstemp(prefix='pycairo_', suffix='.png') @@ -26,7 +26,7 @@ os.close(h) f=file(outFileName, "wb") surface.write_to_png(f) f.close() -print "see %s output file" % outFileName +print("see %s output file" % outFileName) # write to object that has a "write" method import StringIO @@ -38,7 +38,7 @@ buf.close() f=file(outFileName, "wb") f.write(png_string) f.close() -print "see %s output file" % outFileName +print("see %s output file" % outFileName) # write to object that has a "write" method _, outFileName = tempfile.mkstemp(prefix='pycairo_', suffix='.png') @@ -50,7 +50,7 @@ buf.close() f=file(outFileName, "wb") f.write(png_string) f.close() -print "see %s output file" % outFileName +print("see %s output file" % outFileName) # error test - to check the error message, should raise TypeError #surface.write_to_png(101) diff --git a/test/isurface_get_data.py b/test/isurface_get_data.py deleted file mode 100755 index f2662d4..0000000 --- a/test/isurface_get_data.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python -""" -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 diff --git a/test/isurface_get_data1.py b/test/isurface_get_data1.py new file mode 100755 index 0000000..92d25cf --- /dev/null +++ b/test/isurface_get_data1.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +""" +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) diff --git a/test/isurface_get_data2.py b/test/isurface_get_data2.py new file mode 100755 index 0000000..f4c6e0e --- /dev/null +++ b/test/isurface_get_data2.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +""" +Test ImageSurface.get_data() +""" +import tempfile + +import cairo + +import IPython + +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) + +buf = surface.get_data() + +for i in range(h): + offset = (i * surface.get_stride()) + 120 + buf[offset] = b'\xFF' + buf[offset + 1] = b'\x00' + buf[offset + 2] = b'\x00' + +_, outFileName = tempfile.mkstemp(prefix='pycairo_', suffix='.png') +surface.write_to_png(outFileName) +print("see %s output file" % outFileName) + -- 1.7.7.3