mirror of
https://github.com/wxWidgets/Phoenix.git
synced 2026-01-08 04:50:07 +01:00
* 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:
@@ -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.
|
||||
|
||||
|
||||
@@ -39,6 +39,8 @@ INCLUDES = [ # core
|
||||
'arrays',
|
||||
'clntdata',
|
||||
'userdata',
|
||||
'wxpybuffer',
|
||||
|
||||
'stockgdi',
|
||||
'longlong',
|
||||
'wxdatetime',
|
||||
|
||||
222
etg/image.py
222
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);
|
||||
""")
|
||||
|
||||
|
||||
|
||||
|
||||
61
src/wxpybuffer.h
Normal file
61
src/wxpybuffer.h
Normal 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
45
src/wxpybuffer.sip
Normal 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
|
||||
};
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user