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