* 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
This commit is contained in:
Robin Dunn
2012-04-28 04:04:53 +00:00
parent 259869c431
commit c2e5a59a8f
6 changed files with 271 additions and 161 deletions

View File

@@ -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.

View File

@@ -39,6 +39,8 @@ INCLUDES = [ # core
'arrays',
'clntdata',
'userdata',
'wxpybuffer',
'stockgdi',
'longlong',
'wxdatetime',

View File

@@ -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);
""")

61
src/wxpybuffer.h Normal file
View File

@@ -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
//--------------------------------------------------------------------------

45
src/wxpybuffer.sip Normal file
View File

@@ -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
};

View File

@@ -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