blender/source/gameengine/VideoTexture/ImageBuff.cpp

423 lines
12 KiB
C++

/*
-----------------------------------------------------------------------------
This source file is part of VideoTexture library
Copyright (c) 2007 The Zdeno Ash Miklas
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA, or go to
http://www.gnu.org/copyleft/lesser.txt.
-----------------------------------------------------------------------------
*/
/** \file gameengine/VideoTexture/ImageBuff.cpp
* \ingroup bgevideotex
*/
// implementation
#include "PyObjectPlus.h"
#include <structmember.h>
#include "ImageBuff.h"
#include "Exception.h"
#include "ImageBase.h"
#include "FilterSource.h"
// use ImBuf API for image manipulation
extern "C" {
#include "IMB_imbuf_types.h"
#include "IMB_imbuf.h"
#include "bgl.h"
};
// default filter
FilterRGB24 defFilter;
// forward declaration;
extern PyTypeObject ImageBuffType;
static int ImageBuff_init (PyObject * pySelf, PyObject * args, PyObject * kwds)
{
short width = -1;
short height = -1;
unsigned char color = 0;
PyObject *py_scale = Py_False;
ImageBuff *image;
PyImage * self = reinterpret_cast<PyImage*>(pySelf);
// create source object
if (self->m_image != NULL)
delete self->m_image;
image = new ImageBuff();
self->m_image = image;
if (PyArg_ParseTuple(args, "hh|bO!:ImageBuff", &width, &height, &color, &PyBool_Type, &py_scale))
{
// initialize image buffer
image->setScale(py_scale == Py_True);
image->clear(width, height, color);
}
else
{
// check if at least one argument was passed
if (width != -1 || height != -1)
// yes and they didn't match => it's an error
return -1;
// empty argument list is okay
PyErr_Clear();
}
// initialization succeded
return 0;
}
ImageBuff::~ImageBuff (void)
{
if (m_imbuf)
IMB_freeImBuf(m_imbuf);
}
// load image from buffer
void ImageBuff::load (unsigned char * img, short width, short height)
{
// loading a new buffer implies to reset the imbuf if any, because the size may change
if (m_imbuf)
{
IMB_freeImBuf(m_imbuf);
m_imbuf = NULL;
}
// initialize image buffer
init(width, height);
// original size
short orgSize[2] = {width, height};
// is filter available
if (m_pyfilter != NULL)
// use it to process image
convImage(*(m_pyfilter->m_filter), img, orgSize);
else
// otherwise use default filter
convImage(defFilter, img, orgSize);
// image is available
m_avail = true;
}
void ImageBuff::clear (short width, short height, unsigned char color)
{
unsigned char *p;
int size;
// loading a new buffer implies to reset the imbuf if any, because the size may change
if (m_imbuf)
{
IMB_freeImBuf(m_imbuf);
m_imbuf = NULL;
}
// initialize image buffer
init(width, height);
// the width/height may be different due to scaling
size = (m_size[0] * m_size[1]);
// initialize memory with color for all channels
memset(m_image, color, size*4);
// and change the alpha channel
p = &((unsigned char*)m_image)[3];
for (; size>0; size--)
{
*p = 0xFF;
p += 4;
}
// image is available
m_avail = true;
}
// img must point to a array of RGBA data of size width*height
void ImageBuff::plot (unsigned char * img, short width, short height, short x, short y, short mode)
{
struct ImBuf* tmpbuf;
if (m_size[0] == 0 || m_size[1] == 0 || width <= 0 || height <= 0)
return;
if (!m_imbuf) {
// allocate most basic imbuf, we will assign the rect buffer on the fly
m_imbuf = IMB_allocImBuf(m_size[0], m_size[1], 0, 0);
}
tmpbuf = IMB_allocImBuf(width, height, 0, 0);
// assign temporarily our buffer to the ImBuf buffer, we use the same format
tmpbuf->rect = (unsigned int*)img;
m_imbuf->rect = m_image;
IMB_rectblend(m_imbuf, tmpbuf, x, y, 0, 0, width, height, (IMB_BlendMode)mode);
// remove so that MB_freeImBuf will free our buffer
m_imbuf->rect = NULL;
tmpbuf->rect = NULL;
IMB_freeImBuf(tmpbuf);
}
void ImageBuff::plot (ImageBuff* img, short x, short y, short mode)
{
if (m_size[0] == 0 || m_size[1] == 0 || img->m_size[0] == 0 || img->m_size[1] == 0)
return;
if (!m_imbuf) {
// allocate most basic imbuf, we will assign the rect buffer on the fly
m_imbuf = IMB_allocImBuf(m_size[0], m_size[1], 0, 0);
}
if (!img->m_imbuf) {
// allocate most basic imbuf, we will assign the rect buffer on the fly
img->m_imbuf = IMB_allocImBuf(img->m_size[0], img->m_size[1], 0, 0);
}
// assign temporarily our buffer to the ImBuf buffer, we use the same format
img->m_imbuf->rect = img->m_image;
m_imbuf->rect = m_image;
IMB_rectblend(m_imbuf, img->m_imbuf, x, y, 0, 0, img->m_imbuf->x, img->m_imbuf->y, (IMB_BlendMode)mode);
// remove so that MB_freeImBuf will free our buffer
m_imbuf->rect = NULL;
img->m_imbuf->rect = NULL;
}
// cast Image pointer to ImageBuff
inline ImageBuff * getImageBuff (PyImage * self)
{ return static_cast<ImageBuff*>(self->m_image); }
// python methods
static bool testPyBuffer(Py_buffer* buffer, int width, int height, unsigned int pixsize)
{
if (buffer->itemsize != 1)
{
PyErr_SetString(PyExc_ValueError, "Buffer must be an array of bytes");
return false;
}
if (buffer->len != width*height*pixsize)
{
PyErr_SetString(PyExc_ValueError, "Buffer hasn't the correct size");
return false;
}
// multi dimension are ok as long as there is no hole in the memory
Py_ssize_t size = buffer->itemsize;
for (int i=buffer->ndim-1; i>=0 ; i--)
{
if (buffer->suboffsets != NULL && buffer->suboffsets[i] >= 0)
{
PyErr_SetString(PyExc_ValueError, "Buffer must be of one block");
return false;
}
if (buffer->strides != NULL && buffer->strides[i] != size)
{
PyErr_SetString(PyExc_ValueError, "Buffer must be of one block");
return false;
}
if (i > 0)
size *= buffer->shape[i];
}
return true;
}
static bool testBGLBuffer(Buffer* buffer, int width, int height, unsigned int pixsize)
{
unsigned int size = BGL_typeSize(buffer->type);
for (int i=0; i<buffer->ndimensions; i++)
{
size *= buffer->dimensions[i];
}
if (size != width*height*pixsize)
{
PyErr_SetString(PyExc_ValueError, "Buffer hasn't the correct size");
return false;
}
return true;
}
// load image
static PyObject * load (PyImage * self, PyObject * args)
{
// parameters: string image buffer, its size, width, height
Py_buffer buffer;
Buffer *bglBuffer;
short width;
short height;
unsigned int pixSize;
// calc proper buffer size
// use pixel size from filter
if (self->m_image->getFilter() != NULL)
pixSize = self->m_image->getFilter()->m_filter->firstPixelSize();
else
pixSize = defFilter.firstPixelSize();
// parse parameters
if (!PyArg_ParseTuple(args, "s*hh:load", &buffer, &width, &height))
{
PyErr_Clear();
// check if it is BGL buffer
if (!PyArg_ParseTuple(args, "O!hh:load", &BGL_bufferType, &bglBuffer, &width, &height))
{
// report error
return NULL;
}
else
{
if (testBGLBuffer(bglBuffer, width, height, pixSize))
{
try
{
// if correct, load image
getImageBuff(self)->load((unsigned char*)bglBuffer->buf.asvoid, width, height);
}
catch (Exception & exp)
{
exp.report();
}
}
}
}
else
{
// check if buffer size is correct
if (testPyBuffer(&buffer, width, height, pixSize))
{
try
{
// if correct, load image
getImageBuff(self)->load((unsigned char*)buffer.buf, width, height);
}
catch (Exception & exp)
{
exp.report();
}
}
PyBuffer_Release(&buffer);
}
if (PyErr_Occurred())
return NULL;
Py_RETURN_NONE;
}
static PyObject * plot (PyImage * self, PyObject * args)
{
PyImage * other;
Buffer* bglBuffer;
Py_buffer buffer;
//unsigned char * buff;
//unsigned int buffSize;
short width;
short height;
short x, y;
short mode = IMB_BLEND_COPY;
if (PyArg_ParseTuple(args, "s*hhhh|h:plot", &buffer, &width, &height, &x, &y, &mode))
{
// correct decoding, verify that buffer size is correct
// we need a continuous memory buffer
if (testPyBuffer(&buffer, width, height, 4))
{
getImageBuff(self)->plot((unsigned char*)buffer.buf, width, height, x, y, mode);
}
PyBuffer_Release(&buffer);
if (PyErr_Occurred())
return NULL;
Py_RETURN_NONE;
}
PyErr_Clear();
// try the other format
if (PyArg_ParseTuple(args, "O!hh|h:plot", &ImageBuffType, &other, &x, &y, &mode))
{
getImageBuff(self)->plot(getImageBuff(other), x, y, mode);
Py_RETURN_NONE;
}
PyErr_Clear();
// try the last format (BGL buffer)
if (!PyArg_ParseTuple(args, "O!hhhh|h:plot", &BGL_bufferType, &bglBuffer, &width, &height, &x, &y, &mode))
{
PyErr_SetString(PyExc_TypeError, "Expecting ImageBuff or Py buffer or BGL buffer as first argument; width, height next; postion x, y and mode as last arguments");
return NULL;
}
if (testBGLBuffer(bglBuffer, width, height, 4))
{
getImageBuff(self)->plot((unsigned char*)bglBuffer->buf.asvoid, width, height, x, y, mode);
}
if (PyErr_Occurred())
return NULL;
Py_RETURN_NONE;
}
// methods structure
static PyMethodDef imageBuffMethods[] =
{
{"load", (PyCFunction)load, METH_VARARGS, "Load image from buffer"},
{"plot", (PyCFunction)plot, METH_VARARGS, "update image buffer"},
{NULL}
};
// attributes structure
static PyGetSetDef imageBuffGetSets[] =
{ // attributes from ImageBase class
{(char*)"valid", (getter)Image_valid, NULL, (char*)"bool to tell if an image is available", NULL},
{(char*)"image", (getter)Image_getImage, NULL, (char*)"image data", NULL},
{(char*)"size", (getter)Image_getSize, NULL, (char*)"image size", NULL},
{(char*)"scale", (getter)Image_getScale, (setter)Image_setScale, (char*)"fast scale of image (near neighbor)", NULL},
{(char*)"flip", (getter)Image_getFlip, (setter)Image_setFlip, (char*)"flip image vertically", NULL},
{(char*)"filter", (getter)Image_getFilter, (setter)Image_setFilter, (char*)"pixel filter", NULL},
{NULL}
};
// define python type
PyTypeObject ImageBuffType =
{
PyVarObject_HEAD_INIT(NULL, 0)
"VideoTexture.ImageBuff", /*tp_name*/
sizeof(PyImage), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)Image_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
&imageBufferProcs, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
"Image source from image buffer", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
imageBuffMethods, /* tp_methods */
0, /* tp_members */
imageBuffGetSets, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)ImageBuff_init, /* tp_init */
0, /* tp_alloc */
Image_allocNew, /* tp_new */
};