forked from bartvdbraak/blender
445d077cf4
Synopsis: plot(brush,width,height,x,y,mode) plot(imgbuff,x,y,mode) The first form uses a byte array containing the brush shape. The second form uses another ImageBuff object as a brush. The ImageBuff object must be initialized before you can call these methods. Use load(rgb_buffer,sizex,sizey) method to create an image buffer of given size (with alpha channel set to 255). The brush is plotted directly in the image buffer. The texture is updated only when the VideoTexture.Texture parent object is refreshed: this will download the image buffer to the GPU. brush: Byte array containing RGBA data to be plotted in image buffer. The data must be continuous in memory, organized row by row starting from lower left corner of the image. Each pixel is 4 bytes representing RGBA data in that order. width: Horizontal size in pixels of image in brush. height: Vertical size in pixels of the image in brush. imgbuff:Another ImageBuff object that is used as a brush. The object must have been initialized first with load(). x: Horizontal position in pixel from left side of the image buffer where the brush will be plotted. The brush is plotted on pixels positions x->x+width-1. Clipping is performed if the brush falls partially outside the image buffer. y: Vertical position in pixel from bottom side of the image buffer where the brush will be plotted. mode: Mode of drawing. Use one of the following value: 0 : MIX 1 : ADD 2 : SUB 3 : MUL 4 : LIGHTEN 5 : DARKEN 6 : ERASE ALPHA 7 : ADD ALPHA 1000 : COPY RGBA (default) 1001 : COPY RGB 1002 : COPY ALPHA Modes 0 to 7 are 'blend' modes: the brush pixels are combined with the image pixel in various ways. Refer to Blender documentation to learn more about these modes.
292 lines
8.8 KiB
C++
292 lines
8.8 KiB
C++
/* $Id$
|
|
-----------------------------------------------------------------------------
|
|
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.
|
|
-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
// implementation
|
|
|
|
#include <PyObjectPlus.h>
|
|
#include <structmember.h>
|
|
|
|
#include "ImageBuff.h"
|
|
|
|
#include "ImageBase.h"
|
|
#include "FilterSource.h"
|
|
|
|
// use ImBuf API for image manipulation
|
|
extern "C" {
|
|
#include "IMB_imbuf_types.h"
|
|
#include "IMB_imbuf.h"
|
|
};
|
|
|
|
// default filter
|
|
FilterRGB24 defFilter;
|
|
|
|
// forward declaration;
|
|
extern PyTypeObject ImageBuffType;
|
|
|
|
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;
|
|
}
|
|
|
|
// 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, 0);
|
|
}
|
|
|
|
tmpbuf = IMB_allocImBuf(width, height, 0, 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, 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, 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 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;
|
|
}
|
|
|
|
// load image
|
|
static PyObject * load (PyImage * self, PyObject * args)
|
|
{
|
|
// parameters: string image buffer, its size, width, height
|
|
Py_buffer buffer;
|
|
short width;
|
|
short height;
|
|
// parse parameters
|
|
if (!PyArg_ParseTuple(args, "s*hh:load", &buffer, &width, &height))
|
|
{
|
|
// report error
|
|
return NULL;
|
|
}
|
|
// else check buffer size
|
|
else
|
|
{
|
|
// calc proper buffer size
|
|
unsigned int pixSize;
|
|
// use pixel size from filter
|
|
if (self->m_image->getFilter() != NULL)
|
|
pixSize = self->m_image->getFilter()->m_filter->firstPixelSize();
|
|
else
|
|
pixSize = defFilter.firstPixelSize();
|
|
// check if buffer size is correct
|
|
if (testPyBuffer(&buffer, width, height, pixSize))
|
|
{
|
|
// if correct, load image
|
|
getImageBuff(self)->load((unsigned char*)buffer.buf, width, height);
|
|
}
|
|
PyBuffer_Release(&buffer);
|
|
}
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
static PyObject * plot (PyImage * self, PyObject * args)
|
|
{
|
|
PyImage * other;
|
|
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 continous memory buffer
|
|
if (testPyBuffer(&buffer, width, height, 4))
|
|
{
|
|
getImageBuff(self)->plot((unsigned char*)buffer.buf, width, height, x, y, mode);
|
|
}
|
|
PyBuffer_Release(&buffer);
|
|
Py_RETURN_NONE;
|
|
}
|
|
PyErr_Clear();
|
|
// try the other format
|
|
if (!PyArg_ParseTuple(args, "O!hh|h:plot", &ImageBuffType, &other, &x, &y, &mode))
|
|
{
|
|
PyErr_SetString(PyExc_TypeError, "Expecting ImageBuff or string,width,height as first arguments, postion x, y and mode and last arguments");
|
|
return NULL;
|
|
}
|
|
getImageBuff(self)->plot(getImageBuff(other), x, y, mode);
|
|
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*)"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 neighbour)", 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*/
|
|
0, /*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)Image_init<ImageBuff>, /* tp_init */
|
|
0, /* tp_alloc */
|
|
Image_allocNew, /* tp_new */
|
|
};
|
|
|