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