code cleanup: gpl header update (formatting)
[blender.git] / source / gameengine / VideoTexture / ImageBuff.cpp
index 9cd661a24225ac64c5f310863d14afc86f869a74..1593a08bb4b4c3b0a20e18ec08684cfc31b004f4 100644 (file)
-/* $Id$
------------------------------------------------------------------------------
-This source file is part of VideoTexture library
+/*
+ * ***** 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 *****
+ */
 
-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 "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
@@ -53,66 +117,264 @@ void ImageBuff::load (unsigned char * img, short width, short height)
        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)
+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)
+static PyObject *load(PyImage *self, PyObject *args)
 {
        // parameters: string image buffer, its size, width, height
-       unsigned char * buff;
-       unsigned int buffSize;
+       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", &buff, &buffSize, &width, &height))
+       if (!PyArg_ParseTuple(args, "s*hh:load", &buffer, &width, &height))
        {
-               // report error
-               return NULL;
+               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 buffer size
        else
        {
-               // calc proper buffer size
-               unsigned int propSize = width * height;
-               // use pixel size from filter
-               if (self->m_image->getFilter() != NULL)
-                       propSize *= self->m_image->getFilter()->m_filter->firstPixelSize();
-               else
-                       propSize *= defFilter.firstPixelSize();
                // check if buffer size is correct
-               if (propSize != buffSize)
+               if (testPyBuffer(&buffer, width, height, pixSize))
                {
-                       // if not, report error
-                       PyErr_SetString(PyExc_TypeError, "Buffer hasn't correct size");
-                       return NULL;
+                       try 
+                       {
+                               // if correct, load image
+                               getImageBuff(self)->load((unsigned char*)buffer.buf, width, height);
+                       }
+                       catch (Exception & exp)
+                       {
+                               exp.report();
+                       }
                }
-               else
-                       // if correct, load image
-                       getImageBuff(self)->load(buff, width, height);
+               PyBuffer_Release(&buffer);
        }
-       Py_RETURN_NONE; 
+       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 neighbour)", 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}
@@ -140,7 +402,7 @@ PyTypeObject ImageBuffType =
        0,                         /*tp_str*/
        0,                         /*tp_getattro*/
        0,                         /*tp_setattro*/
-       0,                         /*tp_as_buffer*/
+       &imageBufferProcs,         /*tp_as_buffer*/
        Py_TPFLAGS_DEFAULT,        /*tp_flags*/
        "Image source from image buffer",       /* tp_doc */
        0,                             /* tp_traverse */
@@ -157,7 +419,7 @@ PyTypeObject ImageBuffType =
        0,                         /* tp_descr_get */
        0,                         /* tp_descr_set */
        0,                         /* tp_dictoffset */
-       (initproc)Image_init<ImageBuff>,     /* tp_init */
+       (initproc)ImageBuff_init,     /* tp_init */
        0,                         /* tp_alloc */
        Image_allocNew,           /* tp_new */
 };