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