Add buffer related methods and helpers for wxBitmap

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxPython/Phoenix/trunk@71293 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Robin Dunn
2012-04-28 04:05:08 +00:00
parent df774efcd0
commit 18098181e3
9 changed files with 806 additions and 49 deletions

View File

@@ -107,9 +107,6 @@ WAF Build
other dev stuff
---------------
* port all of the wx.Bitmap copy-to and copy-from buffer methods, and
raw bitmap access routines from Classic.
* Come up with some way to implement the MustHaveApp check that
Classic does. It should raise an exception when something is
created/used that should not be done before there is an application

View File

@@ -18,7 +18,13 @@ DOCSTRING = ""
# The classes and/or the basename of the Doxygen XML files to be processed by
# this script.
ITEMS = [ 'wxBitmap', 'wxBitmapHandler', 'wxMask' ]
ITEMS = [ 'wxBitmap',
'wxBitmapHandler',
'wxMask' ]
OTHERDEPS = [ 'src/bitmap_ex.h',
'src/bitmap_ex.cpp', ]
#---------------------------------------------------------------------------
@@ -52,9 +58,36 @@ def run():
return self->IsOk();
""")
# On MSW the handler classes are different than what is documented, and
# this causes compile errors. Nobody has needed them from Python thus far,
# so just ignore them all for now.
c.addCppMethod('long', 'GetHandle', '()',
doc='MSW-only method to fetch the windows handle for the bitmap.',
body="""\
#ifdef __WXMSW__
return self->GetHandle();
#else
return 0;
#endif
""")
c.addCppMethod('void', 'SetHandle', '(long handle)',
doc='MSW-only method to set the windows handle for the bitmap.',
body="""\
#ifdef __WXMSW__
self->SetHandle((WXHANDLE)handle);
#endif
""")
c.addCppMethod('void', 'SetSize', '(const wxSize& size)',
doc='Set the bitmap size (does not affect the existing bitmap data).',
body="""\
self->SetWidth(size->x);
self->SetHeight(size->y);
""")
# On MSW the bitmap handler classes are different than what is
# documented, and this causes compile errors. Nobody has needed them from
# Python thus far, so just ignore them all for now.
for m in c.find('FindHandler').all():
m.ignore()
c.find('AddHandler').ignore()
@@ -70,9 +103,230 @@ def run():
module.find('wxBitmapHandler').ignore()
#module.addItem(tools.wxListWrapperTemplate('wxList', 'wxBitmapHandler', module))
# TODO: The ctors and methods from Classic for converting to/from
# buffer objects with raw bitmap access.
#-----------------------------------------------------------------------
# Declarations, helpers and methods for converting to/from buffer objects
# with raw bitmap access.
from etgtools import EnumDef, EnumValueDef
e = EnumDef(name='wxBitmapBufferFormat')
e.items.extend([ EnumValueDef(name='wxBitmapBufferFormat_RGB'),
EnumValueDef(name='wxBitmapBufferFormat_RGBA'),
EnumValueDef(name='wxBitmapBufferFormat_RGB32'),
EnumValueDef(name='wxBitmapBufferFormat_ARGB32'),
])
module.insertItem(0, e)
c.includeCppCode('src/bitmap_ex.cpp')
module.addHeaderCode('#include "bitmap_ex.h"')
c.addCppMethod('void', 'CopyFromBuffer',
'(wxPyBuffer* data, wxBitmapBufferFormat format=wxBitmapBufferFormat_RGB, int stride=-1)',
doc="""\
Copy data from a buffer object to replace the bitmap pixel data.
Default format is plain RGB, but other formats are now supported as
well. The following symbols are used to specify the format of the
bytes in the buffer:
============================= ================================
wx.BitmapBufferFormat_RGB A simple sequence of RGB bytes
wx.BitmapBufferFormat_RGBA A simple sequence of RGBA bytes
wx.BitmapBufferFormat_ARGB32 A sequence of 32-bit values in native
endian order, with alpha in the upper
8 bits, followed by red, green, and
blue.
wx.BitmapBufferFormat_RGB32 Same as above but the alpha byte
is ignored.
============================= ================================""",
body="""\
wxPyCopyBitmapFromBuffer(self, (byte*)data->m_ptr, data->m_len, format, stride);
""")
c.addCppMethod('void', 'CopyToBuffer',
'(wxPyBuffer* data, wxBitmapBufferFormat format=wxBitmapBufferFormat_RGB, int stride=-1)',
doc="""\
Copy pixel data to a buffer object. See `CopyFromBuffer` for buffer
format details.""",
body="""\
wxPyCopyBitmapToBuffer(self, (byte*)data->m_ptr, data->m_len, format, stride);
""")
# Some bitmap factories added as static methods
c.addCppMethod('wxBitmap*', 'FromBufferAndAlpha',
'(int width, int height, wxPyBuffer* data, wxPyBuffer* alpha)',
isStatic=True,
factory=True,
doc="""\
Creates a `wx.Bitmap` from in-memory data. The data and alpha
parameters must be a Python object that implements the buffer
interface, such as a string, bytearray, etc. The data object
is expected to contain a series of RGB bytes and be at least
width*height*3 bytes long, while the alpha object is expected
to be width*height bytes long and represents the image's alpha
channel. On Windows and Mac the RGB values will be
'premultiplied' by the alpha values. (The other platforms do
the multiplication themselves.)
Unlike `wx.ImageFromBuffer` the bitmap created with this function
does not share the memory block with the buffer object. This is
because the native pixel buffer format varies on different
platforms, and so instead an efficient as possible copy of the
data is made from the buffer object to the bitmap's native pixel
buffer.
""",
body="""\
if (!data->checkSize(width*height*3) || !alpha->checkSize(width*height))
return NULL;
byte* ddata = (byte*)data->m_ptr;
byte* adata = (byte*)alpha->m_ptr;
wxBitmap* bmp = new wxBitmap(width, height, 32);
wxAlphaPixelData pixData(*bmp, wxPoint(0,0), wxSize(width,height));
if (! pixData) {
wxPyErr_SetString(PyExc_RuntimeError, "Failed to gain raw access to bitmap data.");
return NULL;
}
wxAlphaPixelData::Iterator p(pixData);
for (int y=0; y<height; y++) {
wxAlphaPixelData::Iterator rowStart = p;
for (int x=0; x<width; x++) {
byte a = *(adata++);
p.Red() = wxPy_premultiply(*(ddata++), a);
p.Green() = wxPy_premultiply(*(ddata++), a);
p.Blue() = wxPy_premultiply(*(ddata++), a);
p.Alpha() = a;
++p;
}
p = rowStart;
p.OffsetY(pixData, 1);
}
return bmp;
""")
c.addCppMethod('wxBitmap*', 'FromBuffer', '(int width, int height, wxPyBuffer* data)',
isStatic=True,
factory=True,
doc="""\
Creates a `wx.Bitmap` from in-memory data. The data parameter
must be a Python object that implements the buffer interface, such
as a string, bytearray, etc. The data object is expected to contain
a series of RGB bytes and be at least width*height*3 bytes long.
Unlike `wx.ImageFromBuffer` the bitmap created with this function
does not share the memory block with the buffer object. This is
because the native pixel buffer format varies on different
platforms, and so instead an efficient as possible copy of the
data is made from the buffer object to the bitmap's native pixel
buffer.
""",
body="""\
wxBitmap* bmp = new wxBitmap(width, height, 24);
wxPyCopyBitmapFromBuffer(bmp, (byte*)data->m_ptr, data->m_len, wxBitmapBufferFormat_RGB);
if (PyErr_Occurred()) {
delete bmp;
bmp = NULL;
}
return bmp;
""")
module.addPyFunction('BitmapFromBuffer', '(width, height, dataBuffer, alphaBuffer=None)',
deprecated=True,
doc='A compatibility wrapper for Bitmap.FromBuffer and Bitmap.FromBufferAndAlpha',
body="""\
if alphaBuffer is not None:
return Bitmap.FromBufferAndAlpha(width, height, dataBuffer, alphaBuffer)
else:
return Bitmap.FromBuffer(width, height, dataBuffer)
""")
c.addCppMethod('wxBitmap*', 'FromBufferRGBA', '(int width, int height, wxPyBuffer* data)',
isStatic=True,
factory=True,
doc="""\
Creates a `wx.Bitmap` from in-memory data. The data parameter
must be a Python object that implements the buffer interface, such
as a string, bytearray, etc. The data object is expected to contain
a series of RGBA bytes and be at least width*height*4 bytes long.
On Windows and Mac the RGB values will be 'premultiplied' by the
alpha values. (The other platforms do the multiplication themselves.)
Unlike `wx.ImageFromBuffer` the bitmap created with this function
does not share the memory block with the buffer object. This is
because the native pixel buffer format varies on different
platforms, and so instead an efficient as possible copy of the
data is made from the buffer object to the bitmap's native pixel
buffer.
""",
body="""\
wxBitmap* bmp = new wxBitmap(width, height, 32);
wxPyCopyBitmapFromBuffer(bmp, (byte*)data->m_ptr, data->m_len, wxBitmapBufferFormat_RGBA);
if (PyErr_Occurred()) {
delete bmp;
bmp = NULL;
}
return bmp;
""")
module.addPyFunction('BitmapFromBufferRGBA', '(width, height, dataBuffer)',
deprecated=True,
doc='A compatibility wrapper for Bitmap.FromBufferRGBA',
body='return Bitmap.FromBufferRGBA(width, height, dataBuffer)')
c.addCppMethod('wxBitmap*', 'FromRGBA',
'(int width, int height, byte red=0, byte green=0, byte blue=0, byte alpha=0)',
isStatic=True,
factory=True,
doc="""\
Creates a new empty 32-bit `wx.Bitmap` where every pixel has been
initialized with the given RGBA values.
""",
body="""\
if ( !(width > 0 && height > 0) ) {
wxPyErr_SetString(PyExc_ValueError, "Width and height must be greater than zero");
return NULL;
}
wxBitmap* bmp = new wxBitmap(width, height, 32);
wxAlphaPixelData pixData(*bmp, wxPoint(0,0), wxSize(width,height));
if (! pixData) {
wxPyErr_SetString(PyExc_RuntimeError, "Failed to gain raw access to bitmap data.");
return NULL;
}
wxAlphaPixelData::Iterator p(pixData);
for (int y=0; y<height; y++) {
wxAlphaPixelData::Iterator rowStart = p;
for (int x=0; x<width; x++) {
p.Red() = wxPy_premultiply(red, alpha);
p.Green() = wxPy_premultiply(green, alpha);
p.Blue() = wxPy_premultiply(blue, alpha);
p.Alpha() = alpha;
++p;
}
p = rowStart;
p.OffsetY(pixData, 1);
}
return bmp;
""")
module.addPyFunction('EmptyBitmapRGBA', '(width, height, red=0, green=0, blue=0, alpha=0)',
deprecated=True,
doc='A compatibility wrapper for Bitmap.FromRGBA',
body='return Bitmap.FromRGBA(width, height, red, green, blue, alpha)')
#-----------------------------------------------------------------------
# For compatibility:
module.addPyFunction('EmptyBitmap', '(width, height, depth=BITMAP_SCREEN_DEPTH)',

View File

@@ -56,6 +56,7 @@ def run():
module.insertItemAfter(td, etgtools.TypedefDef(type='long', name='time_t'))
module.insertItemAfter(td, etgtools.TypedefDef(type='long long', name='wxFileOffset'))
module.insertItemAfter(td, etgtools.TypedefDef(type='SIP_SSIZE_T', name='ssize_t'))
module.insertItemAfter(td, etgtools.TypedefDef(type='unsigned char', name='byte', pyInt=True))

View File

@@ -21,6 +21,8 @@ DOCSTRING = ""
ITEMS = [ 'wxImage',
'wxImageHistogram',
'wxImageHandler',
#'wxQuantize',
#'wxPalette',
]
#---------------------------------------------------------------------------
@@ -58,7 +60,7 @@ def run():
if (! copy)
return NULL;
sipCpp = new sipwxImage;
sipCpp->Create(width, height, (unsigned char*)copy);
sipCpp->Create(width, height, (byte*)copy);
""")
c.addCppCtor_sip('(int width, int height, wxPyBuffer* data, wxPyBuffer* alpha)',
@@ -70,7 +72,7 @@ def run():
if ((dcopy = data->copy()) == NULL || (acopy = alpha->copy()) == NULL)
return NULL;
sipCpp = new sipwxImage;
sipCpp->Create(width, height, (unsigned char*)dcopy, (unsigned char*)acopy, false);
sipCpp->Create(width, height, (byte*)dcopy, (byte*)acopy, false);
""")
c.addCppCtor_sip('(const wxSize& size, wxPyBuffer* data)',
@@ -82,7 +84,7 @@ def run():
if (! copy)
return NULL;
sipCpp = new sipwxImage;
sipCpp->Create(size->x, size->y, (unsigned char*)copy, false);
sipCpp->Create(size->x, size->y, (byte*)copy, false);
""")
c.addCppCtor_sip('(const wxSize& size, wxPyBuffer* data, wxPyBuffer* alpha)',
@@ -94,7 +96,7 @@ def run():
if ((dcopy = data->copy()) == NULL || (acopy = alpha->copy()) == NULL)
return NULL;
sipCpp = new sipwxImage;
sipCpp->Create(size->x, size->y, (unsigned char*)dcopy, (unsigned char*)acopy, false);
sipCpp->Create(size->x, size->y, (byte*)dcopy, (byte*)acopy, false);
""")
@@ -112,7 +114,7 @@ def run():
void* copy = data->copy();
if (! copy)
return false;
return self->Create(width, height, (unsigned char*)copy);
return self->Create(width, height, (byte*)copy);
""")
c.addCppMethod('bool', 'Create', '(int width, int height, wxPyBuffer* data, wxPyBuffer* alpha)',
@@ -123,7 +125,7 @@ def run():
return false;
if ((dcopy = data->copy()) == NULL || (acopy = alpha->copy()) == NULL)
return false;
return self->Create(width, height, (unsigned char*)dcopy, (unsigned char*)acopy);
return self->Create(width, height, (byte*)dcopy, (byte*)acopy);
""")
c.addCppMethod('bool', 'Create', '(const wxSize& size, wxPyBuffer* data)',
@@ -134,7 +136,7 @@ def run():
void* copy = data->copy();
if (! copy)
return false;
return self->Create(size->x, size->y, (unsigned char*)copy);
return self->Create(size->x, size->y, (byte*)copy);
""")
c.addCppMethod('bool', 'Create', '(const wxSize& size, wxPyBuffer* data, wxPyBuffer* alpha)',
@@ -145,7 +147,7 @@ def run():
return false;
if ((dcopy = data->copy()) == NULL || (acopy = alpha->copy()) == NULL)
return false;
return self->Create(size->x, size->y, (unsigned char*)dcopy, (unsigned char*)acopy);
return self->Create(size->x, size->y, (byte*)dcopy, (byte*)acopy);
""")
@@ -160,7 +162,7 @@ def run():
void* copy = data->copy();
if (!copy)
return;
self->SetData((unsigned char*)copy, false);
self->SetData((byte*)copy, false);
""", briefDoc=bd, detailedDoc=dd)
c.find('SetData').findOverload('int new_width').ignore()
@@ -171,7 +173,7 @@ def run():
void* copy = data->copy();
if (!copy)
return;
self->SetData((unsigned char*)copy, new_width, new_height, false);
self->SetData((byte*)copy, new_width, new_height, false);
""")
m = c.find('SetAlpha').findOverload('unsigned char *alpha')
@@ -184,7 +186,7 @@ def run():
void* copy = alpha->copy();
if (!copy)
return;
self->SetAlpha((unsigned char*)copy, false);
self->SetAlpha((byte*)copy, false);
""")
@@ -195,7 +197,7 @@ def run():
c.addCppMethod('PyObject*', 'GetData', '()',
doc="Returns a copy of the RGB bytes of the image.",
body="""\
unsigned char* data = self->GetData();
byte* data = self->GetData();
Py_ssize_t len = self->GetWidth() * self->GetHeight() * 3;
PyObject* rv = NULL;
wxPyBLOCK_THREADS( rv = PyByteArray_FromStringAndSize((const char*)data, len));
@@ -206,7 +208,7 @@ def run():
c.addCppMethod('PyObject*', 'GetAlpha', '()',
doc="Returns a copy of the Alpha bytes of the image.",
body="""\
unsigned char* data = self->GetAlpha();
byte* data = self->GetAlpha();
Py_ssize_t len = self->GetWidth() * self->GetHeight();
PyObject* rv = NULL;
wxPyBLOCK_THREADS( rv = PyByteArray_FromStringAndSize((const char*)data, len));
@@ -223,7 +225,7 @@ def run():
image data buffer inside the wx.Image. You need to ensure that you do
not use this buffer object after the image has been destroyed.""",
body="""\
unsigned char* data = self->GetData();
byte* data = self->GetData();
Py_ssize_t len = self->GetWidth() * self->GetHeight() * 3;
PyObject* rv;
Py_buffer view;
@@ -240,7 +242,7 @@ def run():
data buffer inside the wx.Image. You need to ensure that you do
not use this buffer object after the image has been destroyed.""",
body="""\
unsigned char* data = self->GetAlpha();
byte* data = self->GetAlpha();
Py_ssize_t len = self->GetWidth() * self->GetHeight();
PyObject* rv;
Py_buffer view;
@@ -263,7 +265,7 @@ def run():
if (!data->checkSize(self->GetWidth() * self->GetHeight() * 3))
return;
// True means don't free() the pointer
self->SetData((unsigned char*)data->m_ptr, true);
self->SetData((byte*)data->m_ptr, true);
""")
c.addCppMethod('void', 'SetDataBuffer', '(wxPyBuffer* data, int new_width, int new_height)',
doc="""\
@@ -275,7 +277,7 @@ def run():
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);
self->SetData((byte*)data->m_ptr, new_width, new_height, true);
""")
@@ -289,7 +291,7 @@ def run():
if (!alpha->checkSize(self->GetWidth() * self->GetHeight()))
return;
// True means don't free() the pointer
self->SetAlpha((unsigned char*)alpha->m_ptr, true);
self->SetAlpha((byte*)alpha->m_ptr, true);
""")
@@ -387,10 +389,10 @@ def run():
unsigned rgblen = 3 * self->GetWidth() * self->GetHeight();
unsigned alphalen = self->GetWidth() * self->GetHeight();
unsigned char* src_data = self->GetData();
unsigned char* src_alpha = self->GetAlpha();
unsigned char* dst_data = dest->GetData();
unsigned char* dst_alpha = NULL;
byte* src_data = self->GetData();
byte* src_alpha = self->GetAlpha();
byte* dst_data = dest->GetData();
byte* dst_alpha = NULL;
// adjust rgb
if ( factor_red == 1.0 && factor_green == 1.0 && factor_blue == 1.0)
@@ -403,18 +405,18 @@ def run():
// rgb pixel for pixel
for ( unsigned i = 0; i < rgblen; i= i + 3 )
{
dst_data[i] = (unsigned char) wxMin( 255, (int) (factor_red * src_data[i]) );
dst_data[i + 1] = (unsigned char) wxMin( 255, (int) (factor_green * src_data[i + 1]) );
dst_data[i + 2] = (unsigned char) wxMin( 255, (int) (factor_blue * src_data[i + 2]) );
dst_data[i] = (byte) wxMin( 255, (int) (factor_red * src_data[i]) );
dst_data[i + 1] = (byte) wxMin( 255, (int) (factor_green * src_data[i + 1]) );
dst_data[i + 2] = (byte) wxMin( 255, (int) (factor_blue * src_data[i + 2]) );
}
}
// adjust the mask colour
if ( self->HasMask() )
{
dest->SetMaskColour((unsigned char) wxMin( 255, (int) (factor_red * self->GetMaskRed() ) ),
(unsigned char) wxMin( 255, (int) (factor_green * self->GetMaskGreen() ) ),
(unsigned char) wxMin( 255, (int) (factor_blue * self->GetMaskBlue() ) ) );
dest->SetMaskColour((byte) wxMin( 255, (int) (factor_red * self->GetMaskRed() ) ),
(byte) wxMin( 255, (int) (factor_green * self->GetMaskGreen() ) ),
(byte) wxMin( 255, (int) (factor_blue * self->GetMaskBlue() ) ) );
}
// adjust the alpha channel
@@ -436,7 +438,7 @@ def run():
// alpha value for alpha value
for ( unsigned i = 0; i < alphalen; ++i )
{
dst_alpha[i] = (unsigned char) wxMin( 255, (int) (factor_alpha * src_alpha[i]) );
dst_alpha[i] = (byte) wxMin( 255, (int) (factor_alpha * src_alpha[i]) );
}
}
}
@@ -450,7 +452,7 @@ def run():
for ( unsigned i = 0; i < alphalen; ++i )
{
dst_alpha[i] = (unsigned char) wxMin( 255, (int) (factor_alpha * 255) );
dst_alpha[i] = (byte) wxMin( 255, (int) (factor_alpha * 255) );
}
}
@@ -458,9 +460,9 @@ def run():
if ( dst_alpha && dest->HasMask() )
{
// make the mask transparent honoring the alpha channel
const unsigned char mr = dest->GetMaskRed();
const unsigned char mg = dest->GetMaskGreen();
const unsigned char mb = dest->GetMaskBlue();
const byte mr = dest->GetMaskRed();
const byte mg = dest->GetMaskGreen();
const byte mb = dest->GetMaskBlue();
for ( unsigned i = 0; i < alphalen; ++i )
{
@@ -508,6 +510,41 @@ def run():
module.addPyFunction('ImageFromBuffer', '(width, height, dataBuffer, alphaBuffer=None)',
doc="""\
Creates a `wx.Image` from the data in dataBuffer. The dataBuffer
parameter must be a Python object that implements the buffer interface,
such as a string, array, etc. The dataBuffer object is expected to
contain a series of RGB bytes and be width*height*3 bytes long. A buffer
object can optionally be supplied for the image's alpha channel data, and
it is expected to be width*height bytes long.
The wx.Image will be created with its data and alpha pointers initialized
to the memory address pointed to by the buffer objects, thus saving the
time needed to copy the image data from the buffer object to the wx.Image.
While this has advantages, it also has the shoot-yourself-in-the-foot
risks associated with sharing a C pointer between two objects.
To help alleviate the risk a reference to the data and alpha buffer
objects are kept with the wx.Image, so that they won't get deleted until
after the wx.Image is deleted. However please be aware that it is not
guaranteed that an object won't move its memory buffer to a new location
when it needs to resize its contents. If that happens then the wx.Image
will end up referring to an invalid memory location and could cause the
application to crash. Therefore care should be taken to not manipulate
the objects used for the data and alpha buffers in a way that would cause
them to change size.
""",
body="""\
img = Image(width, height)
img.SetDataBuffer(dataBuffer)
if alphaBuffer:
img.SetAlphaBuffer(alphaBuffer)
img._buffer = dataBuffer
img._alpha = alphaBuffer
return img
""")
#-------------------------------------------------------
c = module.find('wxImageHistogram')
setParamsPyInt('MakeKey')

319
src/bitmap_ex.cpp Normal file
View File

@@ -0,0 +1,319 @@
//--------------------------------------------------------------------------
// Name: src/bitmap_ex.h
// Purpose: Helper functions and etc. for copying bitmap data to/from
// buffer objects. This file is included in etg/bitmap.py and
// used in the wxBitmap wrapper.
//
// Author: Robin Dunn
//
// Created: 27-Apr-2012
// Copyright: (c) 2012 by Total Control Software
// Licence: wxWindows license
//--------------------------------------------------------------------------
#include <wx/rawbmp.h>
// TODO: Switch these APIs to use the new wxPyBuffer class
void wxPyCopyBitmapFromBuffer(wxBitmap* bmp,
buffer data, Py_ssize_t DATASIZE,
wxBitmapBufferFormat format, int stride)
{
int height = bmp->GetHeight();
int width = bmp->GetWidth();
switch (format) {
// A simple sequence of RGB bytes
case wxBitmapBufferFormat_RGB:
{
if (DATASIZE < width * height * 3) {
wxPyErr_SetString(PyExc_ValueError, "Invalid data buffer size.");
return;
}
wxNativePixelData pixData(*bmp, wxPoint(0,0), wxSize(width, height));
if (! pixData) {
wxPyErr_SetString(PyExc_RuntimeError,
"Failed to gain raw access to bitmap data.");
return;
}
wxNativePixelData::Iterator p(pixData);
for (int y=0; y<height; y++) {
wxNativePixelData::Iterator rowStart = p;
for (int x=0; x<width; x++) {
p.Red() = *(data++);
p.Green() = *(data++);
p.Blue() = *(data++);
++p;
}
p = rowStart;
p.OffsetY(pixData, 1);
}
break;
}
// A simple sequence of RGBA bytes
case wxBitmapBufferFormat_RGBA:
{
if (DATASIZE < width * height * 4) {
wxPyErr_SetString(PyExc_ValueError, "Invalid data buffer size.");
return;
}
wxAlphaPixelData pixData(*bmp, wxPoint(0,0), wxSize(width, height));
if (! pixData) {
wxPyErr_SetString(PyExc_RuntimeError,
"Failed to gain raw access to bitmap data.");
return;
}
wxAlphaPixelData::Iterator p(pixData);
for (int y=0; y<height; y++) {
wxAlphaPixelData::Iterator rowStart = p;
for (int x=0; x<width; x++) {
byte a = data[3];
p.Red() = wxPy_premultiply(*(data++), a);
p.Green() = wxPy_premultiply(*(data++), a);
p.Blue() = wxPy_premultiply(*(data++), a);
p.Alpha() = a; data++;
++p;
}
p = rowStart;
p.OffsetY(pixData, 1);
}
break;
}
// A sequence of 32-bit values in native endian order,
// where the alpha is in the upper 8 bits, then red, then
// green, then blue. The stride is the distance in bytes
// from the beginning of one row of the image data to the
// beginning of the next row. This may not be the same as
// width*4 if alignment or platform specific optimizations
// have been utilized.
// NOTE: This is normally used with Cairo, which seems to
// already have the values premultiplied. Should we have
// a way to optionally do it anyway?
case wxBitmapBufferFormat_RGB32:
case wxBitmapBufferFormat_ARGB32:
{
bool useAlpha = (format == wxBitmapBufferFormat_ARGB32);
byte* rowStart = data;
wxUint32* bufptr;
wxUint32 value;
if (stride == -1)
stride = width * 4;
if (DATASIZE < stride * height) {
wxPyErr_SetString(PyExc_ValueError, "Invalid data buffer size.");
return;
}
wxAlphaPixelData pixData(*bmp, wxPoint(0,0), wxSize(width,height));
if (! pixData) {
wxPyErr_SetString(PyExc_RuntimeError,
"Failed to gain raw access to bitmap data.");
return;
}
wxAlphaPixelData::Iterator pix(pixData);
for (int y=0; y<height; y++) {
pix.MoveTo(pixData, 0, y);
bufptr = (wxUint32*)rowStart;
for (int x=0; x<width; x++) {
value = *bufptr;
pix.Alpha() = useAlpha ? (value >> 24) & 0xFF : 255;
pix.Red() = (value >> 16) & 0xFF;
pix.Green() = (value >> 8) & 0xFF;
pix.Blue() = (value >> 0) & 0xFF;
++pix;
++bufptr;
}
rowStart += stride;
}
break;
}
}
}
// Some helper macros used below to help declutter the code
#define MAKE_PIXDATA(type) \
type pixData(*bmp, wxPoint(0,0), wxSize(width, height)); \
if (! pixData) { \
wxPyErr_SetString(PyExc_RuntimeError, "Failed to gain raw access to bitmap data."); \
return; \
} \
type::Iterator p(pixData); \
type::Iterator rowStart
#define CHECK_BUFFERSIZE(size_needed) \
if (DATASIZE < size_needed) { \
wxPyErr_SetString(PyExc_ValueError, "Invalid data buffer size."); \
return; \
}
void wxPyCopyBitmapToBuffer(wxBitmap* bmp,
buffer data, Py_ssize_t DATASIZE,
wxBitmapBufferFormat format, int stride)
{
int height = bmp->GetHeight();
int width = bmp->GetWidth();
int depth = bmp->GetDepth();
// images loaded from a file may not have set the depth, at least on Mac...
if (depth == -1) {
if (bmp->HasAlpha())
depth = 32;
else
depth = 24;
}
switch (format) {
// A simple sequence of RGB bytes
case wxBitmapBufferFormat_RGB:
{
CHECK_BUFFERSIZE(width * height * 3);
if (depth == 24) {
MAKE_PIXDATA(wxNativePixelData);
for (int y=0; y<height; y++) {
rowStart = p;
for (int x=0; x<width; x++) {
*(data++) = p.Red();
*(data++) = p.Green();
*(data++) = p.Blue();
++p;
}
p = rowStart;
p.OffsetY(pixData, 1);
}
}
if (depth == 32) {
// Source has alpha, but we won't be using it because the
// destination buffer doesn't
MAKE_PIXDATA(wxAlphaPixelData);
for (int y=0; y<height; y++) {
rowStart = p;
for (int x=0; x<width; x++) {
*(data++) = p.Red();
*(data++) = p.Green();
*(data++) = p.Blue();
++p;
}
p = rowStart;
p.OffsetY(pixData, 1);
}
}
break;
}
// A simple sequence of RGBA bytes
case wxBitmapBufferFormat_RGBA:
{
CHECK_BUFFERSIZE(width * height * 4);
if (depth == 24) {
MAKE_PIXDATA(wxNativePixelData);
for (int y=0; y<height; y++) {
rowStart = p;
for (int x=0; x<width; x++) {
byte a = wxALPHA_OPAQUE;
*(data++) = wxPy_unpremultiply(p.Red(), a);
*(data++) = wxPy_unpremultiply(p.Green(), a);
*(data++) = wxPy_unpremultiply(p.Blue(), a);
*(data++) = a;
}
p = rowStart;
p.OffsetY(pixData, 1);
}
}
if (depth == 32) {
MAKE_PIXDATA(wxAlphaPixelData);
for (int y=0; y<height; y++) {
rowStart = p;
for (int x=0; x<width; x++) {
byte a = p.Alpha();
*(data++) = wxPy_unpremultiply(p.Red(), a);
*(data++) = wxPy_unpremultiply(p.Green(), a);
*(data++) = wxPy_unpremultiply(p.Blue(), a);
*(data++) = a;
}
p = rowStart;
p.OffsetY(pixData, 1);
}
}
break;
}
// A sequence of 32-bit values in native endian order,
// where the alpha is in the upper 8 bits, then red, then
// green, then blue. The stride is the distance in bytes
// from the beginning of one row of the image data to the
// beginning of the next row. This may not be the same as
// width*4 if alignment or platform specific optimizations
// have been utilized.
// NOTE: This is normally used with Cairo, which seems to
// already have the values premultiplied. Should we have
// a way to optionally do it anyway?
case wxBitmapBufferFormat_RGB32:
case wxBitmapBufferFormat_ARGB32:
{
bool useAlpha = (format == wxBitmapBufferFormat_ARGB32);
byte* dataRow = data;
wxUint32* bufptr;
wxUint32 value;
if (stride == -1)
stride = width * 4;
CHECK_BUFFERSIZE(stride * height);
if (useAlpha && depth == 32) {
MAKE_PIXDATA(wxAlphaPixelData);
for (int y=0; y<height; y++) {
p.MoveTo(pixData, 0, y);
bufptr = (wxUint32*)dataRow;
for (int x=0; x<width; x++) {
value =
(p.Alpha() << 24) |
(p.Red() << 16) |
(p.Green() << 8) |
(p.Blue());
*bufptr = value;
++p;
++bufptr;
}
dataRow += stride;
}
}
else // if (!useAlpha /*depth == 24*/)
{
MAKE_PIXDATA(wxNativePixelData);
for (int y=0; y<height; y++) {
p.MoveTo(pixData, 0, y);
bufptr = (wxUint32*)dataRow;
for (int x=0; x<width; x++) {
value =
(wxALPHA_OPAQUE << 24) |
(p.Red() << 16) |
(p.Green() << 8) |
(p.Blue());
*bufptr = value;
++p;
++bufptr;
}
dataRow += stride;
}
}
break;
}
}
}
//--------------------------------------------------------------------------

46
src/bitmap_ex.h Normal file
View File

@@ -0,0 +1,46 @@
//--------------------------------------------------------------------------
// Name: src/bitmap_ex.h
// Purpose: Helper functions and etc. for copying bitmap data to/from
// buffer objects.
//
// Author: Robin Dunn
//
// Created: 27-Apr-2012
// Copyright: (c) 2012 by Total Control Software
// Licence: wxWindows license
//--------------------------------------------------------------------------
#ifndef BITMAP_EX_H
#define BITMAP_EX_H
enum wxBitmapBufferFormat {
wxBitmapBufferFormat_RGB,
wxBitmapBufferFormat_RGBA,
wxBitmapBufferFormat_RGB32,
wxBitmapBufferFormat_ARGB32,
};
// See http://tinyurl.com/e5adr for what premultiplying alpha means. wxMSW and
// wxMac want to have the values premultiplied by the alpha value, but the
// other platforms don't. These macros help keep the code clean.
#if defined(__WXMSW__) || defined(__WXMAC__)
#define wxPy_premultiply(p, a) ((p) * (a) / 0xff)
#define wxPy_unpremultiply(p, a) ((a) ? ((p) * 0xff / (a)) : (p))
#else
#define wxPy_premultiply(p, a) (p)
#define wxPy_unpremultiply(p, a) (p)
#endif
void wxPyCopyBitmapFromBuffer(wxBitmap* bmp,
buffer data, Py_ssize_t DATASIZE,
wxBitmapBufferFormat format, int stride=-1);
void wxPyCopyBitmapToBuffer(wxBitmap* bmp,
buffer data, Py_ssize_t DATASIZE,
wxBitmapBufferFormat format, int stride=-1);
#endif

View File

@@ -46,6 +46,8 @@
//--------------------------------------------------------------------------
typedef PyGILState_STATE wxPyBlock_t;
typedef unsigned char byte;
typedef unsigned char* buffer;
// Convert a wxString to a Python string (actually a PyUnicode object).

View File

@@ -7,19 +7,35 @@ pngFile = os.path.join(os.path.dirname(__file__), 'toucan.png')
#---------------------------------------------------------------------------
def makeBuf(w, h, bpp=1, init=0):
"Make a simple buffer for testing with"
buf = bytearray([init] * (w*h*bpp))
return buf
class BitmapTests(wtc.WidgetTestCase):
def test_BitmapCtors(self):
def test_BitmapCtor1(self):
b1 = wx.Bitmap()
self.assertTrue( not b1.IsOk() )
def test_BitmapCtor2(self):
b2 = wx.Bitmap(5, 10, 32)
self.assertTrue( b2.IsOk() )
def test_BitmapCtor3(self):
b3 = wx.Bitmap(wx.Size(5,10), 32)
self.assertTrue( b3.IsOk() )
def test_BitmapCtor4(self):
b4 = wx.Bitmap((5,10), 32)
self.assertTrue( b4.IsOk() )
def test_BitmapCtor5(self):
b5 = wx.Bitmap(pngFile)
self.assertTrue( b5.IsOk() )
def test_BitmapCtor6(self):
img = wx.Image(pngFile)
b6 = wx.Bitmap(img)
self.assertTrue( b6.IsOk() )
@@ -76,6 +92,96 @@ class BitmapTests(wtc.WidgetTestCase):
m = wx.Mask(bmp, wx.Colour(1,2,3))
def test_bitmapFormatConstants(self):
wx.BitmapBufferFormat_RGB
wx.BitmapBufferFormat_RGBA
wx.BitmapBufferFormat_RGB32
wx.BitmapBufferFormat_ARGB32
def test_bitmapSetSize(self):
b1 = wx.Bitmap(1,1)
b1.SetSize((20,30))
self.assertTrue(b1.GetSize() == (20,30))
self.assertTrue(b1.Size == (20,30))
b1.Size = (25,35)
self.assertTrue(b1.GetSize() == (25,35))
def test_bitmapHandle(self):
b1 = wx.Bitmap(1,1)
b1.Handle
b1.GetHandle()
def test_bitmapCopyFromBuffer1(self):
w = h = 10
buf = makeBuf(w,h,3)
bmp = wx.Bitmap(w,h,24)
bmp.CopyFromBuffer(buf, wx.BitmapBufferFormat_RGB)
def test_bitmapCopyFromBuffer2(self):
w = h = 10
buf = makeBuf(w,h,4)
bmp = wx.Bitmap(w,h,32)
bmp.CopyFromBuffer(buf, wx.BitmapBufferFormat_RGBA)
def test_bitmapCopyFromBuffer3(self):
w = h = 10
buf = makeBuf(w,h,4)
bmp = wx.Bitmap(w,h,32)
bmp.CopyFromBuffer(buf, wx.BitmapBufferFormat_ARGB32)
def test_bitmapCopyToBuffer1(self):
w = h = 10
buf = makeBuf(w,h,3)
bmp = wx.Bitmap(w,h,24)
bmp.CopyToBuffer(buf, wx.BitmapBufferFormat_RGB)
def test_bitmapCopyToBuffer2(self):
w = h = 10
buf = makeBuf(w,h,4)
bmp = wx.Bitmap(w,h,32)
bmp.CopyToBuffer(buf, wx.BitmapBufferFormat_RGBA)
def test_bitmapCopyToBuffer3(self):
w = h = 10
buf = makeBuf(w,h,4)
bmp = wx.Bitmap(w,h,32)
bmp.CopyToBuffer(buf, wx.BitmapBufferFormat_ARGB32)
def test_bitmapBufferFactory1(self):
w = h = 10
buf = makeBuf(w,h,3, 111)
bmp = wx.Bitmap.FromBuffer(w, h, buf)
self.assertTrue(bmp.IsOk())
def test_bitmapBufferFactory2(self):
w = h = 10
buf = makeBuf(w,h,3, 111)
alpha = makeBuf(w,h,1)
bmp = wx.Bitmap.FromBufferAndAlpha(w, h, buf, alpha)
self.assertTrue(bmp.IsOk())
def test_bitmapBufferFactory3(self):
w = h = 10
buf = makeBuf(w,h,4, 111)
bmp = wx.Bitmap.FromBufferRGBA(w, h, buf)
self.assertTrue(bmp.IsOk())
def test_bitmapEmptyFactory1(self):
w = h = 10
bmp = wx.Bitmap.FromRGBA(w, h)
self.assertTrue(bmp.IsOk())
def test_bitmapEmptyFactory2(self):
w = h = 10
bmp = wx.Bitmap.FromRGBA(w, h, 1, 2, 3, 4)
self.assertTrue(bmp.IsOk())
#---------------------------------------------------------------------------

View File

@@ -9,11 +9,6 @@ pngFile = os.path.join(os.path.dirname(__file__), 'toucan.png')
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