forked from bartvdbraak/blender
70eaf2fe44
consistent with projection painting. Also did some refactoring of this code, moving the brush image creation code out of brush.c and making it consistent with image updating code.
425 lines
12 KiB
C++
425 lines
12 KiB
C++
/*
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* Copyright (c) 2007 The Zdeno Ash Miklas
|
|
*
|
|
* This source file is part of VideoTexture library
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
/** \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, m_imbuf, tmpbuf, NULL, NULL, 0, x, y, 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, m_imbuf, img->m_imbuf, NULL, NULL, 0, x, y, 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 */
|
|
};
|
|
|