423 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			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 */
 | |
| };
 | |
| 
 |