code cleanup: gpl header update (formatting)
[blender.git] / source / gameengine / VideoTexture / ImageBuff.cpp
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software  Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Copyright (c) 2007 The Zdeno Ash Miklas
19  *
20  * This source file is part of VideoTexture library
21  *
22  * Contributor(s):
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file gameengine/VideoTexture/ImageBuff.cpp
28  *  \ingroup bgevideotex
29  */
30
31 // implementation
32
33 #include "PyObjectPlus.h"
34 #include <structmember.h>
35
36 #include "ImageBuff.h"
37 #include "Exception.h"
38 #include "ImageBase.h"
39 #include "FilterSource.h"
40
41 // use ImBuf API for image manipulation
42 extern "C" {
43 #include "IMB_imbuf_types.h"
44 #include "IMB_imbuf.h"
45 #include "bgl.h"
46 };
47
48 // default filter
49 FilterRGB24 defFilter;
50
51 // forward declaration;
52 extern PyTypeObject ImageBuffType;
53
54 static int ImageBuff_init (PyObject *pySelf, PyObject *args, PyObject *kwds)
55 {
56         short width = -1;
57         short height = -1;
58         unsigned char color = 0;
59         PyObject *py_scale = Py_False;
60         ImageBuff *image;
61
62         PyImage *self = reinterpret_cast<PyImage*>(pySelf);
63         // create source object
64         if (self->m_image != NULL) 
65                 delete self->m_image;
66         image = new ImageBuff();
67         self->m_image = image;
68
69         if (PyArg_ParseTuple(args, "hh|bO!:ImageBuff", &width, &height, &color, &PyBool_Type, &py_scale)) 
70         {
71                 // initialize image buffer
72                 image->setScale(py_scale == Py_True);
73                 image->clear(width, height, color);
74         }
75         else
76         {
77                 // check if at least one argument was passed
78                 if (width != -1 || height != -1)
79                         // yes and they didn't match => it's an error
80                         return -1;
81                 // empty argument list is okay
82                 PyErr_Clear();
83         }
84         // initialization succeded
85         return 0;
86
87 }
88
89 ImageBuff::~ImageBuff (void)
90 {
91         if (m_imbuf)
92                 IMB_freeImBuf(m_imbuf);
93 }
94
95
96 // load image from buffer
97 void ImageBuff::load (unsigned char * img, short width, short height)
98 {
99         // loading a new buffer implies to reset the imbuf if any, because the size may change
100         if (m_imbuf)
101         {
102                 IMB_freeImBuf(m_imbuf);
103                 m_imbuf = NULL;
104         }
105         // initialize image buffer
106         init(width, height);
107         // original size
108         short orgSize[2] = {width, height};
109         // is filter available
110         if (m_pyfilter != NULL)
111                 // use it to process image
112                 convImage(*(m_pyfilter->m_filter), img, orgSize);
113         else
114                 // otherwise use default filter
115                 convImage(defFilter, img, orgSize);
116         // image is available
117         m_avail = true;
118 }
119
120 void ImageBuff::clear (short width, short height, unsigned char color)
121 {
122         unsigned char *p;
123         int size;
124
125         // loading a new buffer implies to reset the imbuf if any, because the size may change
126         if (m_imbuf)
127         {
128                 IMB_freeImBuf(m_imbuf);
129                 m_imbuf = NULL;
130         }
131         // initialize image buffer
132         init(width, height);
133         // the width/height may be different due to scaling
134         size = (m_size[0] * m_size[1]);
135         // initialize memory with color for all channels
136         memset(m_image, color, size*4);
137         // and change the alpha channel
138         p = &((unsigned char*)m_image)[3];
139         for (; size>0; size--)
140         {
141                 *p = 0xFF;
142                 p += 4;
143         }
144         // image is available
145         m_avail = true;
146 }
147
148 // img must point to a array of RGBA data of size width*height
149 void ImageBuff::plot (unsigned char * img, short width, short height, short x, short y, short mode)
150 {
151         struct ImBuf *tmpbuf;
152
153         if (m_size[0] == 0 || m_size[1] == 0 || width <= 0 || height <= 0)
154                 return;
155
156         if (!m_imbuf) {
157                 // allocate most basic imbuf, we will assign the rect buffer on the fly
158                 m_imbuf = IMB_allocImBuf(m_size[0], m_size[1], 0, 0);
159         }
160
161         tmpbuf = IMB_allocImBuf(width, height, 0, 0);
162
163         // assign temporarily our buffer to the ImBuf buffer, we use the same format
164         tmpbuf->rect = (unsigned int*)img;
165         m_imbuf->rect = m_image;
166         IMB_rectblend(m_imbuf, tmpbuf, x, y, 0, 0, width, height, (IMB_BlendMode)mode);
167         // remove so that MB_freeImBuf will free our buffer
168         m_imbuf->rect = NULL;
169         tmpbuf->rect = NULL;
170         IMB_freeImBuf(tmpbuf);
171 }
172
173 void ImageBuff::plot (ImageBuff* img, short x, short y, short mode)
174 {
175         if (m_size[0] == 0 || m_size[1] == 0 || img->m_size[0] == 0 || img->m_size[1] == 0)
176                 return;
177
178         if (!m_imbuf) {
179                 // allocate most basic imbuf, we will assign the rect buffer on the fly
180                 m_imbuf = IMB_allocImBuf(m_size[0], m_size[1], 0, 0);
181         }
182         if (!img->m_imbuf) {
183                 // allocate most basic imbuf, we will assign the rect buffer on the fly
184                 img->m_imbuf = IMB_allocImBuf(img->m_size[0], img->m_size[1], 0, 0);
185         }
186         // assign temporarily our buffer to the ImBuf buffer, we use the same format
187         img->m_imbuf->rect = img->m_image;
188         m_imbuf->rect = m_image;
189         IMB_rectblend(m_imbuf, img->m_imbuf, x, y, 0, 0, img->m_imbuf->x, img->m_imbuf->y, (IMB_BlendMode)mode);
190         // remove so that MB_freeImBuf will free our buffer
191         m_imbuf->rect = NULL;
192         img->m_imbuf->rect = NULL;
193 }
194
195
196 // cast Image pointer to ImageBuff
197 inline ImageBuff * getImageBuff (PyImage *self)
198 { return static_cast<ImageBuff*>(self->m_image); }
199
200
201 // python methods
202
203 static bool testPyBuffer(Py_buffer* buffer, int width, int height, unsigned int pixsize)
204 {
205         if (buffer->itemsize != 1)
206         {
207                 PyErr_SetString(PyExc_ValueError, "Buffer must be an array of bytes");
208                 return false;
209         } 
210         if (buffer->len != width*height*pixsize)
211         {
212                 PyErr_SetString(PyExc_ValueError, "Buffer hasn't the correct size");
213                 return false;
214         } 
215         // multi dimension are ok as long as there is no hole in the memory
216         Py_ssize_t size = buffer->itemsize;
217         for (int i=buffer->ndim-1; i>=0 ; i--)
218         {
219                 if (buffer->suboffsets != NULL && buffer->suboffsets[i] >= 0)
220                 {
221                         PyErr_SetString(PyExc_ValueError, "Buffer must be of one block");
222                         return false;
223                 }
224                 if (buffer->strides != NULL && buffer->strides[i] != size)
225                 {
226                         PyErr_SetString(PyExc_ValueError, "Buffer must be of one block");
227                         return false;
228                 }
229                 if (i > 0)
230                         size *= buffer->shape[i];
231         }
232         return true;
233 }
234
235 static bool testBGLBuffer(Buffer* buffer, int width, int height, unsigned int pixsize)
236 {
237         unsigned int size = BGL_typeSize(buffer->type);
238         for (int i=0; i<buffer->ndimensions; i++)
239         {
240                 size *= buffer->dimensions[i];
241         }
242         if (size != width*height*pixsize)
243         {
244                 PyErr_SetString(PyExc_ValueError, "Buffer hasn't the correct size");
245                 return false;
246         } 
247         return true;
248 }
249
250
251 // load image
252 static PyObject *load(PyImage *self, PyObject *args)
253 {
254         // parameters: string image buffer, its size, width, height
255         Py_buffer buffer;
256         Buffer *bglBuffer;
257         short width;
258         short height;
259         unsigned int pixSize;
260
261         // calc proper buffer size
262         // use pixel size from filter
263         if (self->m_image->getFilter() != NULL)
264                 pixSize = self->m_image->getFilter()->m_filter->firstPixelSize();
265         else
266                 pixSize = defFilter.firstPixelSize();
267
268         // parse parameters
269         if (!PyArg_ParseTuple(args, "s*hh:load", &buffer, &width, &height))
270         {
271                 PyErr_Clear();
272                 // check if it is BGL buffer
273                 if (!PyArg_ParseTuple(args, "O!hh:load", &BGL_bufferType, &bglBuffer, &width, &height))
274                 {
275                         // report error
276                         return NULL;
277                 }
278                 else
279                 {
280                         if (testBGLBuffer(bglBuffer, width, height, pixSize))
281                         {
282                                 try
283                                 {
284                                         // if correct, load image
285                                         getImageBuff(self)->load((unsigned char*)bglBuffer->buf.asvoid, width, height);
286                                 }
287                                 catch (Exception & exp)
288                                 {
289                                         exp.report();
290                                 }
291                         }
292                 }
293         }
294         else
295         {
296                 // check if buffer size is correct
297                 if (testPyBuffer(&buffer, width, height, pixSize))
298                 {
299                         try 
300                         {
301                                 // if correct, load image
302                                 getImageBuff(self)->load((unsigned char*)buffer.buf, width, height);
303                         }
304                         catch (Exception & exp)
305                         {
306                                 exp.report();
307                         }
308                 }
309                 PyBuffer_Release(&buffer);
310         }
311         if (PyErr_Occurred())
312                 return NULL;
313         Py_RETURN_NONE;
314 }
315
316 static PyObject *plot (PyImage *self, PyObject *args)
317 {
318         PyImage * other;
319         Buffer* bglBuffer;
320         Py_buffer buffer;
321         //unsigned char * buff;
322         //unsigned int buffSize;
323         short width;
324         short height;
325         short x, y;
326         short mode = IMB_BLEND_COPY;
327
328         if (PyArg_ParseTuple(args, "s*hhhh|h:plot", &buffer, &width, &height, &x, &y, &mode))
329         {
330                 // correct decoding, verify that buffer size is correct
331                 // we need a continuous memory buffer
332                 if (testPyBuffer(&buffer, width, height, 4))
333                 {
334                         getImageBuff(self)->plot((unsigned char*)buffer.buf, width, height, x, y, mode);
335                 }
336                 PyBuffer_Release(&buffer);
337                 if (PyErr_Occurred())
338                         return NULL;
339                 Py_RETURN_NONE;
340         }
341         PyErr_Clear();
342         // try the other format
343         if (PyArg_ParseTuple(args, "O!hh|h:plot", &ImageBuffType, &other, &x, &y, &mode))
344         {
345                 getImageBuff(self)->plot(getImageBuff(other), x, y, mode);
346                 Py_RETURN_NONE;
347         }
348         PyErr_Clear();
349         // try the last format (BGL buffer)
350         if (!PyArg_ParseTuple(args, "O!hhhh|h:plot", &BGL_bufferType, &bglBuffer, &width, &height, &x, &y, &mode))
351         {
352                 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");
353                 return NULL;
354         }
355         if (testBGLBuffer(bglBuffer, width, height, 4))
356         {
357                 getImageBuff(self)->plot((unsigned char*)bglBuffer->buf.asvoid, width, height, x, y, mode);
358         }
359         if (PyErr_Occurred())
360                 return NULL;
361         Py_RETURN_NONE;
362 }
363
364 // methods structure
365 static PyMethodDef imageBuffMethods[] =
366
367         {"load", (PyCFunction)load, METH_VARARGS, "Load image from buffer"},
368         {"plot", (PyCFunction)plot, METH_VARARGS, "update image buffer"},
369         {NULL}
370 };
371 // attributes structure
372 static PyGetSetDef imageBuffGetSets[] =
373 {       // attributes from ImageBase class
374         {(char*)"valid", (getter)Image_valid, NULL, (char*)"bool to tell if an image is available", NULL},
375         {(char*)"image", (getter)Image_getImage, NULL, (char*)"image data", NULL},
376         {(char*)"size", (getter)Image_getSize, NULL, (char*)"image size", NULL},
377         {(char*)"scale", (getter)Image_getScale, (setter)Image_setScale, (char*)"fast scale of image (near neighbor)", NULL},
378         {(char*)"flip", (getter)Image_getFlip, (setter)Image_setFlip, (char*)"flip image vertically", NULL},
379         {(char*)"filter", (getter)Image_getFilter, (setter)Image_setFilter, (char*)"pixel filter", NULL},
380         {NULL}
381 };
382
383
384 // define python type
385 PyTypeObject ImageBuffType =
386
387         PyVarObject_HEAD_INIT(NULL, 0)
388         "VideoTexture.ImageBuff",   /*tp_name*/
389         sizeof(PyImage),          /*tp_basicsize*/
390         0,                         /*tp_itemsize*/
391         (destructor)Image_dealloc, /*tp_dealloc*/
392         0,                         /*tp_print*/
393         0,                         /*tp_getattr*/
394         0,                         /*tp_setattr*/
395         0,                         /*tp_compare*/
396         0,                         /*tp_repr*/
397         0,                         /*tp_as_number*/
398         0,                         /*tp_as_sequence*/
399         0,                         /*tp_as_mapping*/
400         0,                         /*tp_hash */
401         0,                         /*tp_call*/
402         0,                         /*tp_str*/
403         0,                         /*tp_getattro*/
404         0,                         /*tp_setattro*/
405         &imageBufferProcs,         /*tp_as_buffer*/
406         Py_TPFLAGS_DEFAULT,        /*tp_flags*/
407         "Image source from image buffer",       /* tp_doc */
408         0,                             /* tp_traverse */
409         0,                             /* tp_clear */
410         0,                             /* tp_richcompare */
411         0,                             /* tp_weaklistoffset */
412         0,                             /* tp_iter */
413         0,                             /* tp_iternext */
414         imageBuffMethods,    /* tp_methods */
415         0,                   /* tp_members */
416         imageBuffGetSets,          /* tp_getset */
417         0,                         /* tp_base */
418         0,                         /* tp_dict */
419         0,                         /* tp_descr_get */
420         0,                         /* tp_descr_set */
421         0,                         /* tp_dictoffset */
422         (initproc)ImageBuff_init,     /* tp_init */
423         0,                         /* tp_alloc */
424         Image_allocNew,           /* tp_new */
425 };
426