blender/source/gameengine/VideoTexture/ImageBuff.cpp
Brecht Van Lommel 70eaf2fe44 Image paint: 2D painting now supports texture masks and does masking more
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.
2013-05-12 09:14:13 +00:00

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 */
};