From c2e5a59a8f56e5bc04583c3d3935a5d115f788ed Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Sat, 28 Apr 2012 04:04:53 +0000 Subject: [PATCH] * Add a new wxPyBuffer class and MappedType to help facilitate checking input and getting the pointer and size of Python Buffer compatible objects. * Use wxPyBuffer with wx.Image * Return bytearrays from GetData and GetAlpha * Return memoryviews from GetDataBuffer and GetAlphaBuffer * Add some notes about these changes to the migration guide git-svn-id: https://svn.wxwidgets.org/svn/wx/wxPython/Phoenix/trunk@71291 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- docs/MigrationGuide.txt | 43 ++++++-- etg/_core.py | 2 + etg/image.py | 222 +++++++++++++++++----------------------- src/wxpybuffer.h | 61 +++++++++++ src/wxpybuffer.sip | 45 ++++++++ unittests/test_image.py | 59 ++++++----- 6 files changed, 271 insertions(+), 161 deletions(-) create mode 100644 src/wxpybuffer.h create mode 100644 src/wxpybuffer.sip diff --git a/docs/MigrationGuide.txt b/docs/MigrationGuide.txt index 58c8ae80..75034fd7 100644 --- a/docs/MigrationGuide.txt +++ b/docs/MigrationGuide.txt @@ -270,9 +270,40 @@ In Phoenix that should now be done like this:: self.Create(parent, ID, title) # 3 -Notice that we are (#1) calling the base class __init__ like usual, but -passing no parameters so the default C++ constructor will be invoked. Next -(#2, #3) we use self instead of pre because self is now a legitimate instance -of wx.Dialog, and (#4) there is no longer any need to call PostCreate to do -its black magic for us because there is no longer a rogue instance that needs -to be transplanted into self. +Notice that we are (#1) calling the base class __init__ like usual, +but passing no parameters so the default C++ constructor will be +invoked. Next (#2, #3) we use self instead of pre because self is now +a legitimate instance of wx.Dialog, and (#4) there is no longer any +need to call PostCreate to do its black magic for us because there is +no longer a rogue instance that needs to be transplanted into self. + + + +wx.Image and Python Buffer Objects +---------------------------------- + +wx.Image is now using the new buffer APIs for the constructors and +methods which accept any object supporting the buffer protocol. These +are methods which allow you to set the raw RGB or Alpha data in the +image in one step. As a consequence of using the new APIs the objects +passed must also implement the new buffer interface in order to be +compatible. Apparently arrays from the stock array module do not +support the new protocol, but everything else I've tried so far do. + +GetData and GetAlpha now return a copy of the image data as a +bytearray object instead of a string object. This means that since +bytearrays are mutable you can do things like make cahnges to the data +and then use it in the SetData of another image. + +GetDataBuffer and GetAlphaBuffer now return memoryview objects, which +allow direct access to the RGB and Alpha buffers inside the image. +Just as in Classic you should not use those memoryview buffers after +the wx.Image has been destroyed. Using the returned memoryview object +you can manipulate the RGB or Alpha data inside the wx.Image without +needing to make a copy of the data. + +Just as in Classic the SetDataBuffer and SetAlphaBuffer methods allow +you to tell the wx.Image to use memory buffers in other objects (such +as a numpy array) as its RGB or Alpha data, as long as the other +object supports the new buffer protocol. + diff --git a/etg/_core.py b/etg/_core.py index 6fec07c7..f9df8576 100644 --- a/etg/_core.py +++ b/etg/_core.py @@ -39,6 +39,8 @@ INCLUDES = [ # core 'arrays', 'clntdata', 'userdata', + 'wxpybuffer', + 'stockgdi', 'longlong', 'wxdatetime', diff --git a/etg/image.py b/etg/image.py index 43af76d2..ea7253b1 100644 --- a/etg/image.py +++ b/etg/image.py @@ -39,107 +39,62 @@ def run(): c.find('wxImage').findOverload('(const char *const *xpmData)').ignore() c.find('GetHandlers').ignore() # TODO - #c.find('AddHandler').ignore() - #c.find('InsertHandler').ignore() - #c.find('RemoveHandler').ignore() - #for m in c.find('FindHandler').all(): - # m.ignore() - #c.find('FindHandlerMime').ignore() - # Helper functions for dealing with data buffers for wxImage - c.addCppCode("""\ - static void* copyDataBuffer(PyObject* obj, Py_ssize_t expectedSize) - { - Py_ssize_t dataSize; - void* dataPtr; - - if (PyObject_AsReadBuffer(obj, (const void**)&dataPtr, &dataSize) == -1) - return NULL; - if (dataSize != expectedSize) { - wxPyErr_SetString(PyExc_ValueError, "Invalid data buffer size."); - return NULL; - } - void* copy = malloc(dataSize); - if (copy == NULL) { - wxPyBLOCK_THREADS(PyErr_NoMemory()); - return NULL; - } - memcpy(copy, dataPtr, dataSize); - return copy; - } - - static void* getDataBufferPtr(PyObject* obj, Py_ssize_t expectedSize) - { - Py_ssize_t dataSize; - void* dataPtr; - - if (PyObject_AsReadBuffer(obj, (const void**)&dataPtr, &dataSize) == -1) - return NULL; - if (dataSize != expectedSize) { - wxPyErr_SetString(PyExc_ValueError, "Invalid data buffer size."); - return NULL; - } - return dataPtr; - } - """) - # Ignore the ctors taking raw data buffers, so we can add in our own - # versions that are a little smarter (accepts any buffer object, checks + # versions that are a little smarter (accept any buffer object, check # the data length, etc.) c.find('wxImage').findOverload('int width, int height, unsigned char *data, bool static_data').ignore() c.find('wxImage').findOverload('const wxSize &sz, unsigned char *data, bool static_data').ignore() c.find('wxImage').findOverload('int width, int height, unsigned char *data, unsigned char *alpha, bool static_data').ignore() c.find('wxImage').findOverload('const wxSize &sz, unsigned char *data, unsigned char *alpha, bool static_data').ignore() - c.addCppCtor_sip('(int width, int height, PyObject* data)', + c.addCppCtor_sip('(int width, int height, wxPyBuffer* data)', doc="Creates an image from RGB data in memory.", body="""\ - void* dataCopy = copyDataBuffer(data, width*height*3); - if (!dataCopy) + if (! data->checkSize(width*height*3)) + return NULL; + void* copy = data->copy(); + if (! copy) return NULL; sipCpp = new sipwxImage; - sipCpp->Create(width, height, (unsigned char*)dataCopy); + sipCpp->Create(width, height, (unsigned char*)copy); """) - c.addCppCtor_sip('(int width, int height, PyObject* data, PyObject* alpha)', + c.addCppCtor_sip('(int width, int height, wxPyBuffer* data, wxPyBuffer* alpha)', doc="Creates an image from RGB data in memory, plus an alpha channel", body="""\ - void* dataCopy = copyDataBuffer(data, width*height*3); - if (!dataCopy) + void* dcopy; void* acopy; + if (!data->checkSize(width*height*3) || !alpha->checkSize(width*height)) return NULL; - void* alphaCopy = copyDataBuffer(alpha, width*height); - if (!alphaCopy) { - free(dataCopy); + if ((dcopy = data->copy()) == NULL || (acopy = alpha->copy()) == NULL) return NULL; - } sipCpp = new sipwxImage; - sipCpp->Create(width, height, (unsigned char*)dataCopy, (unsigned char*)alphaCopy, false); + sipCpp->Create(width, height, (unsigned char*)dcopy, (unsigned char*)acopy, false); """) - c.addCppCtor_sip('(const wxSize& size, PyObject* data)', + c.addCppCtor_sip('(const wxSize& size, wxPyBuffer* data)', doc="Creates an image from RGB data in memory.", body="""\ - void* dataCopy = copyDataBuffer(data, size->x*size->y*3); - if (!dataCopy) + if (! data->checkSize(size->x*size->y*3)) + return NULL; + void* copy = data->copy(); + if (! copy) return NULL; sipCpp = new sipwxImage; - sipCpp->Create(size->x, size->y, (unsigned char*)dataCopy, false); + sipCpp->Create(size->x, size->y, (unsigned char*)copy, false); """) - c.addCppCtor_sip('(const wxSize& size, PyObject* data, PyObject* alpha)', + c.addCppCtor_sip('(const wxSize& size, wxPyBuffer* data, wxPyBuffer* alpha)', doc="Creates an image from RGB data in memory, plus an alpha channel", body="""\ - void* dataCopy = copyDataBuffer(data, size->x*size->y*3); - if (!dataCopy) + void* dcopy; void* acopy; + if (!data->checkSize(size->x*size->y*3) || !alpha->checkSize(size->x*size->y)) return NULL; - void* alphaCopy = copyDataBuffer(alpha, size->x*size->y); - if (!alphaCopy) { - free(dataCopy); + if ((dcopy = data->copy()) == NULL || (acopy = alpha->copy()) == NULL) return NULL; - } sipCpp = new sipwxImage; - sipCpp->Create(size->x, size->y, (unsigned char*)dataCopy, (unsigned char*)alphaCopy, false); + sipCpp->Create(size->x, size->y, (unsigned char*)dcopy, (unsigned char*)acopy, false); """) @@ -149,50 +104,48 @@ def run(): c.find('Create').findOverload('int width, int height, unsigned char *data, unsigned char *alpha, bool static_data').ignore() c.find('Create').findOverload('const wxSize &sz, unsigned char *data, unsigned char *alpha, bool static_data').ignore() - c.addCppMethod('bool', 'Create', '(int width, int height, PyObject* data)', + c.addCppMethod('bool', 'Create', '(int width, int height, wxPyBuffer* data)', doc="", body="""\ - void* dataCopy = copyDataBuffer(data, width*height*3); - if (!dataCopy) + if (! data->checkSize(width*height*3)) return false; - return self->Create(width, height, (unsigned char*)dataCopy); + void* copy = data->copy(); + if (! copy) + return false; + return self->Create(width, height, (unsigned char*)copy); """) - c.addCppMethod('bool', 'Create', '(int width, int height, PyObject* data, PyObject* alpha)', + c.addCppMethod('bool', 'Create', '(int width, int height, wxPyBuffer* data, wxPyBuffer* alpha)', doc="", body="""\ - void* dataCopy = copyDataBuffer(data, width*height*3); - if (!dataCopy) + void* dcopy; void* acopy; + if (!data->checkSize(width*height*3) || !alpha->checkSize(width*height)) return false; - void* alphaCopy = copyDataBuffer(alpha, width*height); - if (!alphaCopy) { - free(dataCopy); + if ((dcopy = data->copy()) == NULL || (acopy = alpha->copy()) == NULL) return false; - } - return self->Create(width, height, (unsigned char*)dataCopy, (unsigned char*)alpha); + return self->Create(width, height, (unsigned char*)dcopy, (unsigned char*)acopy); """) - c.addCppMethod('bool', 'Create', '(const wxSize& size, PyObject* data)', + c.addCppMethod('bool', 'Create', '(const wxSize& size, wxPyBuffer* data)', doc="", body="""\ - void* dataCopy = copyDataBuffer(data, size->x*size->y*3); - if (!dataCopy) + if (! data->checkSize(size->x*size->y*3)) return false; - return self->Create(size->x, size->y, (unsigned char*)dataCopy); + void* copy = data->copy(); + if (! copy) + return false; + return self->Create(size->x, size->y, (unsigned char*)copy); """) - c.addCppMethod('bool', 'Create', '(const wxSize& size, PyObject* data, PyObject* alpha)', + c.addCppMethod('bool', 'Create', '(const wxSize& size, wxPyBuffer* data, wxPyBuffer* alpha)', doc="", body="""\ - void* dataCopy = copyDataBuffer(data, size->x*size->y*3); - if (!dataCopy) + void* dcopy; void* acopy; + if (!data->checkSize(size->x*size->y*3) || !alpha->checkSize(size->x*size->y)) return false; - void* alphaCopy = copyDataBuffer(alpha, size->x*size->y); - if (!alphaCopy) { - free(dataCopy); + if ((dcopy = data->copy()) == NULL || (acopy = alpha->copy()) == NULL) return false; - } - return self->Create(size->x, size->y, (unsigned char*)dataCopy, (unsigned char*)alpha); + return self->Create(size->x, size->y, (unsigned char*)dcopy, (unsigned char*)acopy); """) @@ -200,32 +153,38 @@ def run(): m = c.find('SetData').findOverload('unsigned char *data') bd, dd = m.briefDoc, m.detailedDoc m.ignore() - c.addCppMethod('void', 'SetData', '(PyObject* data)', + c.addCppMethod('void', 'SetData', '(wxPyBuffer* data)', body="""\ - void* dataCopy = copyDataBuffer(data, self->GetWidth()*self->GetHeight()*3); - if (!dataCopy) + if (!data->checkSize(self->GetWidth()*self->GetHeight()*3)) return; - self->SetData((unsigned char*)dataCopy, false); + void* copy = data->copy(); + if (!copy) + return; + self->SetData((unsigned char*)copy, false); """, briefDoc=bd, detailedDoc=dd) c.find('SetData').findOverload('int new_width').ignore() - c.addCppMethod('void', 'SetData', '(PyObject* data, int new_width, int new_height)', + c.addCppMethod('void', 'SetData', '(wxPyBuffer* data, int new_width, int new_height)', body="""\ - void* dataCopy = copyDataBuffer(data, new_width*new_height*3); - if (!dataCopy) + if (!data->checkSize(new_width*new_height*3)) return; - self->SetData((unsigned char*)dataCopy, new_width, new_height, false); + void* copy = data->copy(); + if (!copy) + return; + self->SetData((unsigned char*)copy, new_width, new_height, false); """) m = c.find('SetAlpha').findOverload('unsigned char *alpha') bd, dd = m.briefDoc, m.detailedDoc m.ignore() - c.addCppMethod('void', 'SetAlpha', '(PyObject* alpha)', + c.addCppMethod('void', 'SetAlpha', '(wxPyBuffer* alpha)', body="""\ - void* dataCopy = copyDataBuffer(alpha, self->GetWidth()*self->GetHeight()); - if (!dataCopy) + if (!alpha->checkSize(self->GetWidth()*self->GetHeight())) return; - self->SetAlpha((unsigned char*)dataCopy, false); + void* copy = alpha->copy(); + if (!copy) + return; + self->SetAlpha((unsigned char*)copy, false); """) @@ -239,7 +198,7 @@ def run(): unsigned char* data = self->GetData(); Py_ssize_t len = self->GetWidth() * self->GetHeight() * 3; PyObject* rv = NULL; - wxPyBLOCK_THREADS( rv = PyString_FromStringAndSize((char*)data, len)); + wxPyBLOCK_THREADS( rv = PyByteArray_FromStringAndSize((const char*)data, len)); return rv; """) @@ -250,13 +209,14 @@ def run(): unsigned char* data = self->GetAlpha(); Py_ssize_t len = self->GetWidth() * self->GetHeight(); PyObject* rv = NULL; - wxPyBLOCK_THREADS( rv = PyString_FromStringAndSize((char*)data, len)); + wxPyBLOCK_THREADS( rv = PyByteArray_FromStringAndSize((const char*)data, len)); return rv; """) # GetDataBuffer, GetAlphaBuffer provide direct access to the image's - # internal buffers as a writable buffer object. + # internal buffers as a writable buffer object. We'll use memoryview + # objects. c.addCppMethod('PyObject*', 'GetDataBuffer', '()', doc="""\ Returns a writable Python buffer object that is pointing at the RGB @@ -266,7 +226,11 @@ def run(): unsigned char* data = self->GetData(); Py_ssize_t len = self->GetWidth() * self->GetHeight() * 3; PyObject* rv; - wxPyBLOCK_THREADS( rv = PyBuffer_FromReadWriteMemory(data, len) ); + Py_buffer view; + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + PyBuffer_FillInfo(&view, NULL, data, len, 0, PyBUF_WRITABLE|PyBUF_FORMAT|PyBUF_ND); + rv = PyMemoryView_FromBuffer(&view); + wxPyEndBlockThreads(blocked); return rv; """) @@ -279,50 +243,54 @@ def run(): unsigned char* data = self->GetAlpha(); Py_ssize_t len = self->GetWidth() * self->GetHeight(); PyObject* rv; - wxPyBLOCK_THREADS( rv = PyBuffer_FromReadWriteMemory(data, len) ); + Py_buffer view; + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + PyBuffer_FillInfo(&view, NULL, data, len, 0, PyBUF_WRITABLE|PyBUF_FORMAT|PyBUF_ND); + rv = PyMemoryView_FromBuffer(&view); + wxPyEndBlockThreads(blocked); return rv; """) # SetDataBuffer, SetAlphaBuffer tell the image to use some other memory # buffer pointed to by a Python buffer object. - c.addCppMethod('void', 'SetDataBuffer', '(PyObject* data)', + c.addCppMethod('void', 'SetDataBuffer', '(wxPyBuffer* data)', doc="""\ Sets the internal image data pointer to point at a Python buffer object. This can save making an extra copy of the data but you must ensure that the buffer object lives lives at least as long as the wx.Image does.""", body="""\ - void* ptr = getDataBufferPtr(data, self->GetWidth() * self->GetHeight() * 3); - if (ptr) - // True means don't free() the pointer - self->SetData((unsigned char*)ptr, true); - """) - c.addCppMethod('void', 'SetDataBuffer', '(PyObject* data, int new_width, int new_height)', + if (!data->checkSize(self->GetWidth() * self->GetHeight() * 3)) + return; + // True means don't free() the pointer + self->SetData((unsigned char*)data->m_ptr, true); + """) + c.addCppMethod('void', 'SetDataBuffer', '(wxPyBuffer* data, int new_width, int new_height)', doc="""\ Sets the internal image data pointer to point at a Python buffer object. This can save making an extra copy of the data but you must ensure that the buffer object lives lives at least as long as the wx.Image does.""", body="""\ - void* ptr = getDataBufferPtr(data, new_width * new_height * 3); - if (ptr) - // True means don't free() the pointer - self->SetData((unsigned char*)ptr, new_width, new_height, true); - """) + if (!data->checkSize(new_width * new_height * 3)) + return; + // True means don't free() the pointer + self->SetData((unsigned char*)data->m_ptr, new_width, new_height, true); + """) - c.addCppMethod('void', 'SetAlphaBuffer', '(PyObject* alpha)', + c.addCppMethod('void', 'SetAlphaBuffer', '(wxPyBuffer* alpha)', doc="""\ Sets the internal image alpha pointer to point at a Python buffer object. This can save making an extra copy of the data but you must ensure that the buffer object lives lives at least as long as the wx.Image does.""", body="""\ - void* ptr = getDataBufferPtr(alpha, self->GetWidth() * self->GetHeight()); - if (ptr) - // True means don't free() the pointer - self->SetAlpha((unsigned char*)ptr, true); - """) + if (!alpha->checkSize(self->GetWidth() * self->GetHeight())) + return; + // True means don't free() the pointer + self->SetAlpha((unsigned char*)alpha->m_ptr, true); + """) diff --git a/src/wxpybuffer.h b/src/wxpybuffer.h new file mode 100644 index 00000000..0a8324da --- /dev/null +++ b/src/wxpybuffer.h @@ -0,0 +1,61 @@ +//-------------------------------------------------------------------------- +// Name: src/wxpybuffer.h +// Purpose: A simple class to hold a pointer and a size. See +// wxpybuffer.sip for code that converts from a Python buffer +// object as a MappedType. +// +// Author: Robin Dunn +// +// Created: 26-Apr-2012 +// Copyright: (c) 2012 by Total Control Software +// Licence: wxWindows license +//-------------------------------------------------------------------------- + +#ifndef WXPYBUFFER_H +#define WXPYBUFFER_H + + +class wxPyBuffer +{ +public: + wxPyBuffer() : m_ptr(NULL), m_len(0) {} + + // Initialize this object's pointer and length from a PyObject supporting + // the Python buffer protocol. Raises a TypeError if the object can not + // be used as a buffer. + bool create(PyObject* obj) { + int rv = PyObject_AsReadBuffer(obj, (const void**)&m_ptr, &m_len); + return rv != -1; + } + + + // Ensure that the buffer's size is the expected size. Raises a + // Python ValueError exception and returns false if not. + bool checkSize(Py_ssize_t expectedSize) { + if (m_len != expectedSize) { + wxPyErr_SetString(PyExc_ValueError, "Invalid data buffer size."); + return false; + } + return true; + } + + // Make a simple C copy of the data buffer. Malloc is used because + // the wxAPIs this is used with will later free() the pointer. Raises + // a Python exception if there is an allocation error. + void* copy() { + void* ptr = malloc(m_len); + if (ptr == NULL) { + wxPyBLOCK_THREADS(PyErr_NoMemory()); + return NULL; + } + memcpy(ptr, m_ptr, m_len); + return ptr; + } + + + void* m_ptr; + Py_ssize_t m_len; +}; + +#endif +//-------------------------------------------------------------------------- diff --git a/src/wxpybuffer.sip b/src/wxpybuffer.sip new file mode 100644 index 00000000..5039b4db --- /dev/null +++ b/src/wxpybuffer.sip @@ -0,0 +1,45 @@ +//-------------------------------------------------------------------------- +// Name: src/wxpybuffer.sip +// Purpose: A MappedType for wxPyBuffer to automatically convert buffer- +// compatible objects to a C pointer and length. +// +// Author: Robin Dunn +// +// Created: 26-Apr-2012 +// Copyright: (c) 2012 by Total Control Software +// Licence: wxWindows license +//-------------------------------------------------------------------------- + + +%ModuleHeaderCode +#include "wxpybuffer.h" +%End + +// TODO: Use new buffer APIs and memoryview or bytearray objects? + +%MappedType wxPyBuffer +{ + %ConvertToTypeCode + // Code to test a PyObject for compatibility + if (!sipIsErr) { + if (PyObject_CheckBuffer(sipPy)) + return TRUE; + return FALSE; + } + + // Code to create a new wxPyBuffer from the PyObject + wxPyBuffer* buf = new wxPyBuffer(); + buf->create(sipPy); + *sipCppPtr = buf; + return sipGetState(sipTransferObj); + %End + + + // This isn't being used anywhere yet, but it should work. + %ConvertFromTypeCode + Py_buffer view; + PyBuffer_FillInfo(&view, NULL, sipCpp->m_ptr, sipCpp->m_len, 0, + PyBUF_WRITABLE|PyBUF_FORMAT|PyBUF_ND); + return PyMemoryView_FromBuffer(&view); + %End +}; diff --git a/unittests/test_image.py b/unittests/test_image.py index b049a15b..6132ef5d 100644 --- a/unittests/test_image.py +++ b/unittests/test_image.py @@ -7,8 +7,19 @@ pngFile = os.path.join(os.path.dirname(__file__), 'toucan.png') #--------------------------------------------------------------------------- -class image_Tests(wtc.WidgetTestCase): +def makeBuf(w, h, bpp=1, init=0): + "Make a simple buffer for testing with" + # Apparently array objects do not implement the new buffer protocol... + #import array + #buf = array.array('B', [init] * (w*h*bpp)) + + buf = bytearray([init] * (w*h*bpp)) + return buf + + +class image_Tests(wtc.WidgetTestCase): + def test_imageCtor1(self): img = wx.Image() self.assertTrue(not img.IsOk()) @@ -27,40 +38,35 @@ class image_Tests(wtc.WidgetTestCase): def test_imageCtor4(self): - import array w = h = 10 - buf = array.array('B', '\0' * (w*h*3)) + buf = makeBuf(w,h,3) img = wx.Image(w, h, buf) self.assertTrue(img.IsOk()) def test_imageCtor5(self): - import array w = h = 10 - buf = array.array('B', '\0' * (w*h*3)) - alpha = array.array('B', '\0' * (w*h)) + buf = makeBuf(w,h,3) + alpha = makeBuf(w,h) img = wx.Image(w, h, buf, alpha) self.assertTrue(img.IsOk()) def test_imageCtor4b(self): - import array w = h = 10 - buf = array.array('B', '\0' * (w*h*3)) + buf = makeBuf(w,h,3) img = wx.Image((w, h), buf) self.assertTrue(img.IsOk()) def test_imageCtor4c(self): - import array w = h = 10 - buf = array.array('B', '\0' * (w*h*3)) + buf = makeBuf(w,h,3) with self.assertRaises(ValueError): # should be an exception here because the buffer is the wrong size img = wx.Image((w, h+1), buf) def test_imageCtor5b(self): - import array w = h = 10 - buf = array.array('B', '\0' * (w*h*3)) - alpha = array.array('B', '\0' * (w*h)) + buf = makeBuf(w,h,3) + alpha = makeBuf(w,h) img = wx.Image((w, h), buf, alpha) self.assertTrue(img.IsOk()) @@ -89,28 +95,25 @@ class image_Tests(wtc.WidgetTestCase): def test_imageSetData1(self): - import array w = h = 10 img = wx.Image(w,h) - buf = array.array('B', '\2' * (w*h*3)) + buf = makeBuf(w,h,3, init=2) img.SetData(buf) self.assertTrue(img.IsOk()) self.assertTrue(img.GetRed(1,1) == 2) def test_imageSetData2(self): - import array w = h = 10 img = wx.Image(1,1) - buf = array.array('B', '\2' * (w*h*3)) + buf = makeBuf(w,h,3, init=2) img.SetData(buf, w, h) self.assertTrue(img.IsOk()) self.assertTrue(img.GetRed(1,1) == 2) def test_imageSetAlpha1(self): - import array w = h = 10 img = wx.Image(w,h) - buf = array.array('B', '\2' * (w*h)) + buf = makeBuf(w,h, init=2) img.SetAlpha(buf) self.assertTrue(img.IsOk()) self.assertTrue(img.GetRed(1,1) == 0) @@ -120,30 +123,35 @@ class image_Tests(wtc.WidgetTestCase): img = wx.Image(pngFile) data = img.GetData() self.assertEqual(len(data), img.Width * img.Height * 3) + self.assertTrue(isinstance(data, bytearray)) def test_imageGetAlpha(self): img = wx.Image(pngFile) data = img.GetAlpha() self.assertEqual(len(data), img.Width * img.Height) + self.assertTrue(isinstance(data, bytearray)) + def test_imageGetDataBuffer(self): w = h = 10 img = wx.Image(w, h) self.assertTrue(img.IsOk()) data = img.GetDataBuffer() + self.assertTrue(isinstance(data, memoryview)) data[0] = '\1' data[1] = '\2' data[2] = '\3' self.assertEqual(1, img.GetRed(0,0)) self.assertEqual(2, img.GetGreen(0,0)) self.assertEqual(3, img.GetBlue(0,0)) - + def test_imageGetAlphaDataBuffer(self): w = h = 10 img = wx.Image(w, h) img.InitAlpha() self.assertTrue(img.IsOk()) data = img.GetAlphaBuffer() + self.assertTrue(isinstance(data, memoryview)) data[0] = '\1' data[1] = '\2' data[2] = '\3' @@ -153,10 +161,9 @@ class image_Tests(wtc.WidgetTestCase): def test_imageSetDataBuffer1(self): - import array w = h = 10 img = wx.Image(w,h) - buf = array.array('B', '\0' * (w*h*3)) + buf = makeBuf(w,h,3) img.SetDataBuffer(buf) buf[0] = 1 buf[1] = 2 @@ -165,12 +172,10 @@ class image_Tests(wtc.WidgetTestCase): self.assertEqual(2, img.GetGreen(0,0)) self.assertEqual(3, img.GetBlue(0,0)) - def test_imageSetDataBuffer2(self): - import array w = h = 10 img = wx.Image(1,1) - buf = array.array('B', '\0' * (w*h*3)) + buf = makeBuf(w,h,3) img.SetDataBuffer(buf, w, h) buf[0] = 1 buf[1] = 2 @@ -179,12 +184,10 @@ class image_Tests(wtc.WidgetTestCase): self.assertEqual(2, img.GetGreen(0,0)) self.assertEqual(3, img.GetBlue(0,0)) - def test_imageSetAlphaBuffer(self): - import array w = h = 10 img = wx.Image(w,h) - buf = array.array('B', '\0' * (w*h)) + buf = makeBuf(w,h) img.SetAlphaBuffer(buf) buf[0] = 1 buf[1] = 2