0db1fa293daca7f5ca04cddc89a10cf632041d2c
[blender-staging.git] / source / gameengine / VideoTexture / ImageBase.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/ImageBase.cpp
28  *  \ingroup bgevideotex
29  */
30
31 #include "ImageBase.h"
32 extern "C" {
33 #include "bgl.h"
34 }
35
36 #include <vector>
37 #include <string.h>
38
39 #include "EXP_PyObjectPlus.h"
40 #include <structmember.h>
41
42 #include "FilterBase.h"
43
44 #include "Exception.h"
45
46 #if (defined(WIN32) || defined(WIN64)) && !defined(FREE_WINDOWS)
47 #define strcasecmp      _stricmp
48 #endif
49
50 // ImageBase class implementation
51
52 ExceptionID ImageHasExports;
53 ExceptionID InvalidColorChannel;
54 ExceptionID InvalidImageMode;
55
56 ExpDesc ImageHasExportsDesc(ImageHasExports, "Image has exported buffers, cannot resize");
57 ExpDesc InvalidColorChannelDesc(InvalidColorChannel, "Invalid or too many color channels specified. At most 4 values within R, G, B, A, 0, 1");
58 ExpDesc InvalidImageModeDesc(InvalidImageMode, "Invalid image mode, only RGBA and BGRA are supported");
59
60 // constructor
61 ImageBase::ImageBase (bool staticSrc) : m_image(NULL), m_imgSize(0),
62 m_avail(false), m_scale(false), m_scaleChange(false), m_flip(false),
63 m_zbuff(false),
64 m_depth(false),
65 m_staticSources(staticSrc), m_pyfilter(NULL)
66 {
67         m_size[0] = m_size[1] = 0;
68         m_exports = 0;
69 }
70
71
72 // destructor
73 ImageBase::~ImageBase (void)
74 {
75         // release image
76         if (m_image)
77                 delete [] m_image;
78 }
79
80
81 // release python objects
82 bool ImageBase::release (void)
83 {
84         // iterate sources
85         for (ImageSourceList::iterator it = m_sources.begin(); it != m_sources.end(); ++it)
86         {
87                 // release source object
88                 delete *it;
89                 *it = NULL;
90         }
91         // release filter object
92         Py_XDECREF(m_pyfilter);
93         m_pyfilter = NULL;
94         return true;
95 }
96
97
98 // get image
99 unsigned int * ImageBase::getImage (unsigned int texId, double ts)
100 {
101         // if image is not available
102         if (!m_avail)
103         {
104                 // if there are any sources
105                 if (!m_sources.empty())
106                 {
107                         // get images from sources
108                         for (ImageSourceList::iterator it = m_sources.begin(); it != m_sources.end(); ++it)
109                                 // get source image
110                                 (*it)->getImage(ts);
111                         // init image
112                         init(m_sources[0]->getSize()[0], m_sources[0]->getSize()[1]);
113                 }
114                 // calculate new image
115                 calcImage(texId, ts);
116         }
117         // if image is available, return it, otherwise NULL
118         return m_avail ? m_image : NULL;
119 }
120
121 bool ImageBase::loadImage(unsigned int *buffer, unsigned int size, unsigned int format, double ts)
122 {
123         unsigned int *d, *s, v, len;
124         if (getImage(0, ts) != NULL && size >= getBuffSize()) {
125                 switch (format) {
126                 case GL_RGBA:
127                         memcpy(buffer, m_image, getBuffSize());
128                         break;
129                 case GL_BGRA:
130                         len = (unsigned int)m_size[0] * m_size[1];
131                         for (s=m_image, d=buffer; len; len--) {
132                                 v = *s++;
133                                 *d++ = VT_SWAPBR(v);
134                         }
135                         break;
136                 default:
137                         THRWEXCP(InvalidImageMode,S_OK);
138                 }
139                 return true;
140         }
141         return false;
142 }
143
144 // refresh image source
145 void ImageBase::refresh (void)
146 {
147         // invalidate this image
148         m_avail = false;
149         // refresh all sources
150         for (ImageSourceList::iterator it = m_sources.begin(); it != m_sources.end(); ++it)
151                 (*it)->refresh();
152 }
153
154
155 // get source object
156 PyImage * ImageBase::getSource (const char *id)
157 {
158         // find source
159         ImageSourceList::iterator src = findSource(id);
160         // return it, if found
161         return src != m_sources.end() ? (*src)->getSource() : NULL;
162 }
163
164
165 // set source object
166 bool ImageBase::setSource (const char *id, PyImage *source)
167 {
168         // find source
169         ImageSourceList::iterator src = findSource(id);
170         // check source loop
171         if (source != NULL && source->m_image->loopDetect(this))
172                 return false;
173         // if found, set new object
174         if (src != m_sources.end())
175                 // if new object is not empty or sources are static
176                 if (source != NULL || m_staticSources)
177                         // replace previous source
178                         (*src)->setSource(source);
179                 // otherwise delete source
180                 else
181                         m_sources.erase(src);
182         // if source is not found and adding is allowed
183         else
184                 if (!m_staticSources)
185                 {
186                         // create new source
187                         ImageSource * newSrc = newSource(id);
188                         newSrc->setSource(source);
189                         // if source was created, add it to source list
190                         if (newSrc != NULL) m_sources.push_back(newSrc);
191                 }
192                 // otherwise source wasn't set
193                 else 
194                         return false;
195         // source was set
196         return true;
197 }
198
199
200 // set pixel filter
201 void ImageBase::setFilter (PyFilter * filt)
202 {
203         // reference new filter
204         if (filt != NULL) Py_INCREF(filt);
205         // release previous filter
206         Py_XDECREF(m_pyfilter);
207         // set new filter
208         m_pyfilter = filt;
209 }
210
211 void ImageBase::swapImageBR()
212 {
213         unsigned int size, v, *s;
214
215         if (m_avail) {
216                 size = 1 * m_size[0] * m_size[1];
217                 for (s=m_image; size; size--) {
218                         v = *s;
219                         *s++ = VT_SWAPBR(v);
220                 }
221         }
222 }
223
224 // initialize image data
225 void ImageBase::init (short width, short height)
226 {
227         // if image has to be scaled
228         if (m_scale)
229         {
230                 // recalc sizes of image
231                 width = calcSize(width);
232                 height = calcSize(height);
233         }
234         // if sizes differ
235         if (width != m_size[0] || height != m_size[1])
236         {
237                 if (m_exports > 0)
238                         THRWEXCP(ImageHasExports,S_OK);
239
240                 // new buffer size
241                 unsigned int newSize = width * height;
242                 // if new buffer is larger than previous
243                 if (newSize > m_imgSize)
244                 {
245                         // set new buffer size
246                         m_imgSize = newSize;
247                         // release previous and create new buffer
248                         if (m_image)
249                                 delete [] m_image;
250                         m_image = new unsigned int[m_imgSize];
251                 }
252                 // new image size
253                 m_size[0] = width;
254                 m_size[1] = height;
255                 // scale was processed
256                 m_scaleChange = false;
257         }
258 }
259
260
261 // find source
262 ImageSourceList::iterator ImageBase::findSource (const char *id)
263 {
264         // iterate sources
265         ImageSourceList::iterator it;
266         for (it = m_sources.begin(); it != m_sources.end(); ++it)
267                 // if id matches, return iterator
268                 if ((*it)->is(id)) return it;
269         // source not found
270         return it;
271 }
272
273
274 // check sources sizes
275 bool ImageBase::checkSourceSizes (void)
276 {
277         // reference size
278         short * refSize = NULL;
279         // iterate sources
280         for (ImageSourceList::iterator it = m_sources.begin(); it != m_sources.end(); ++it)
281         {
282                 // get size of current source
283                 short * curSize = (*it)->getSize();
284                 // if size is available and is not empty
285                 if (curSize[0] != 0 && curSize[1] != 0) {
286                         // if reference size is not set
287                         if (refSize == NULL) {
288                                 // set current size as reference
289                                 refSize = curSize;
290                 // otherwise check with current size
291                         }
292                         else if (curSize[0] != refSize[0] || curSize[1] != refSize[1]) {
293                                 // if they don't match, report it
294                                 return false;
295                         }
296                 }
297         }
298         // all sizes match
299         return true;
300 }
301
302
303 // compute nearest power of 2 value
304 short ImageBase::calcSize (short size)
305 {
306         // while there is more than 1 bit in size value
307         while ((size & (size - 1)) != 0)
308                 // clear last bit
309                 size = size & (size - 1);
310         // return result
311         return size;
312 }
313
314
315 // perform loop detection
316 bool ImageBase::loopDetect (ImageBase * img)
317 {
318         // if this object is the same as parameter, loop is detected
319         if (this == img) return true;
320         // check all sources
321         for (ImageSourceList::iterator it = m_sources.begin(); it != m_sources.end(); ++it)
322                 // if source detected loop, return this result
323                 if ((*it)->getSource() != NULL && (*it)->getSource()->m_image->loopDetect(img))
324                         return true;
325         // no loop detected
326         return false;
327 }
328
329
330 // ImageSource class implementation
331
332 // constructor
333 ImageSource::ImageSource (const char *id) : m_source(NULL), m_image(NULL)
334 {
335         // copy id
336         int idx;
337         for (idx = 0; id[idx] != '\0' && idx < SourceIdSize - 1; ++idx)
338                 m_id[idx] = id[idx];
339         m_id[idx] = '\0';
340 }
341
342 // destructor
343 ImageSource::~ImageSource (void)
344 {
345         // release source
346         setSource(NULL);
347 }
348
349
350 // compare id
351 bool ImageSource::is (const char *id)
352 {
353         for (char *myId = m_id; *myId != '\0'; ++myId, ++id)
354                 if (*myId != *id) return false;
355         return *id == '\0';
356 }
357
358
359 // set source object
360 void ImageSource::setSource (PyImage *source)
361 {
362         // reference new source
363         if (source != NULL) Py_INCREF(source);
364         // release previous source
365         Py_XDECREF(m_source);
366         // set new source
367         m_source = source;
368 }
369
370
371 // get image from source
372 unsigned int * ImageSource::getImage (double ts)
373 {
374         // if source is available
375         if (m_source != NULL)
376                 // get image from source
377                 m_image = m_source->m_image->getImage(0, ts);
378         // otherwise reset buffer
379         else
380                 m_image = NULL;
381         // return image
382         return m_image;
383 }
384
385
386 // refresh source
387 void ImageSource::refresh (void)
388 {
389         // if source is available, refresh it
390         if (m_source != NULL) m_source->m_image->refresh();
391 }
392
393
394
395 // list of image types
396 PyTypeList pyImageTypes;
397
398
399
400 // functions for python interface
401
402 // object allocation
403 PyObject *Image_allocNew(PyTypeObject *type, PyObject *args, PyObject *kwds)
404 {
405         // allocate object
406         PyImage *self = reinterpret_cast<PyImage*>(type->tp_alloc(type, 0));
407         // initialize object structure
408         self->m_image = NULL;
409         // return allocated object
410         return reinterpret_cast<PyObject*>(self);
411 }
412
413 // object deallocation
414 void Image_dealloc(PyImage *self)
415 {
416         // release object attributes
417         if (self->m_image != NULL)
418         {
419                 if (self->m_image->m_exports > 0)
420                 {
421                         PyErr_SetString(PyExc_SystemError,
422                                         "deallocated Image object has exported buffers");
423                         PyErr_Print();
424                 }
425                 // if release requires deleting of object, do it
426                 if (self->m_image->release())
427                         delete self->m_image;
428                 self->m_image = NULL;
429         }
430 }
431
432 // get image data
433 PyObject *Image_getImage(PyImage *self, char *mode)
434 {
435         try
436         {
437                 unsigned int * image = self->m_image->getImage();
438                 if (image) 
439                 {
440                         // build BGL buffer
441                         int dimensions = self->m_image->getBuffSize();
442                         Buffer * buffer;
443                         if (mode == NULL || !strcasecmp(mode, "RGBA"))
444                         {
445                                 buffer = BGL_MakeBuffer( GL_BYTE, 1, &dimensions, image);
446                         }
447                         else if (!strcasecmp(mode, "F"))
448                         {
449                                 // this mode returns the image as an array of float.
450                                 // This makes sense ONLY for the depth buffer:
451                                 //   source = VideoTexture.ImageViewport()
452                                 //   source.depth = True
453                                 //   depth = VideoTexture.imageToArray(source, 'F')
454
455                                 // adapt dimension from byte to float
456                                 dimensions /= sizeof(float);
457                                 buffer = BGL_MakeBuffer( GL_FLOAT, 1, &dimensions, image);
458                         }
459                         else 
460                         {
461                                 int i, c, ncolor, pixels;
462                                 int offset[4];
463                                 unsigned char *s, *d;
464                                 // scan the mode to get the channels requested, no more than 4
465                                 for (i=ncolor=0; mode[i] != 0 && ncolor < 4; i++)
466                                 {
467                                         switch (toupper(mode[i]))
468                                         {
469                                         case 'R':
470                                                 offset[ncolor++] = 0;
471                                                 break;
472                                         case 'G':
473                                                 offset[ncolor++] = 1;
474                                                 break;
475                                         case 'B':
476                                                 offset[ncolor++] = 2;
477                                                 break;
478                                         case 'A':
479                                                 offset[ncolor++] = 3;
480                                                 break;
481                                         case '0':
482                                                 offset[ncolor++] = -1;
483                                                 break;
484                                         case '1':
485                                                 offset[ncolor++] = -2;
486                                                 break;
487                                         // if you add more color code, change the switch further down
488                                         default:
489                                                 THRWEXCP(InvalidColorChannel,S_OK);
490                                         }
491                                 }
492                                 if (mode[i] != 0) {
493                                         THRWEXCP(InvalidColorChannel,S_OK);
494                                 }
495                                 // first get the number of pixels
496                                 pixels = dimensions / 4;
497                                 // multiple by the number of channels, each is one byte
498                                 dimensions = pixels * ncolor;
499                                 // get an empty buffer
500                                 buffer = BGL_MakeBuffer( GL_BYTE, 1, &dimensions, NULL);
501                                 // and fill it
502                                 for (i = 0, d = (unsigned char *)buffer->buf.asbyte, s = (unsigned char *)image;
503                                      i < pixels;
504                                      i++, d += ncolor, s += 4)
505                                 {
506                                         for (c=0; c<ncolor; c++)
507                                         {
508                                                 switch (offset[c])
509                                                 {
510                                                 case 0: d[c] = s[0]; break;
511                                                 case 1: d[c] = s[1]; break;
512                                                 case 2: d[c] = s[2]; break;
513                                                 case 3: d[c] = s[3]; break;
514                                                 case -1: d[c] = 0; break;
515                                                 case -2: d[c] = 0xFF; break;
516                                                 }
517                                         }
518                                 }
519                         }
520                         return (PyObject *)buffer;
521                 }
522         }
523         catch (Exception & exp)
524         {
525                 exp.report();
526                 return NULL;
527         }
528         Py_RETURN_NONE;
529 }
530
531 // get image size
532 PyObject *Image_getSize (PyImage *self, void *closure)
533 {
534         return Py_BuildValue("(hh)", self->m_image->getSize()[0],
535                 self->m_image->getSize()[1]);
536 }
537
538 // refresh image
539 PyObject *Image_refresh (PyImage *self, PyObject *args)
540 {
541         Py_buffer buffer;
542         bool done = true;
543         char *mode = NULL;
544         double ts = -1.0;
545         unsigned int format;
546
547         memset(&buffer, 0, sizeof(buffer));
548         if (PyArg_ParseTuple(args, "|s*sd:refresh", &buffer, &mode, &ts)) {
549                 if (buffer.buf) {
550                         // a target buffer is provided, verify its format
551                         if (buffer.readonly) {
552                                 PyErr_SetString(PyExc_TypeError, "Buffers passed in argument must be writable");
553                         }
554                         else if (!PyBuffer_IsContiguous(&buffer, 'C')) {
555                                 PyErr_SetString(PyExc_TypeError, "Buffers passed in argument must be contiguous in memory");
556                         }
557                         else if (((intptr_t)buffer.buf & 3) != 0) {
558                                 PyErr_SetString(PyExc_TypeError, "Buffers passed in argument must be aligned to 4 bytes boundary");
559                         }
560                         else {
561                                 // ready to get the image into our buffer
562                                 try {
563                                         if (mode == NULL || !strcmp(mode, "RGBA"))
564                                                 format = GL_RGBA;
565                                         else if (!strcmp(mode, "BGRA"))
566                                                 format = GL_BGRA;
567                                         else
568                                                 THRWEXCP(InvalidImageMode,S_OK);
569
570                                         done = self->m_image->loadImage((unsigned int *)buffer.buf, buffer.len, format, ts);
571                                 }
572                                 catch (Exception & exp) {
573                                         exp.report();
574                                 }
575                         }
576                         PyBuffer_Release(&buffer);
577                         if (PyErr_Occurred()) {
578                                 return NULL;
579                         }
580                 }
581         }
582         else {
583                 return NULL;
584         }
585
586         self->m_image->refresh();
587         if (done)
588                 Py_RETURN_TRUE;
589         Py_RETURN_FALSE;
590 }
591
592 // get scale
593 PyObject *Image_getScale (PyImage *self, void *closure)
594 {
595         if (self->m_image != NULL && self->m_image->getScale()) Py_RETURN_TRUE;
596         else Py_RETURN_FALSE;
597 }
598
599 // set scale
600 int Image_setScale(PyImage *self, PyObject *value, void *closure)
601 {
602         // check parameter, report failure
603         if (value == NULL || !PyBool_Check(value))
604         {
605                 PyErr_SetString(PyExc_TypeError, "The value must be a bool");
606                 return -1;
607         }
608         // set scale
609         if (self->m_image != NULL) self->m_image->setScale(value == Py_True);
610         // success
611         return 0;
612 }
613
614 // get flip
615 PyObject *Image_getFlip (PyImage *self, void *closure)
616 {
617         if (self->m_image != NULL && self->m_image->getFlip()) Py_RETURN_TRUE;
618         else Py_RETURN_FALSE;
619 }
620
621 // set flip
622 int Image_setFlip(PyImage *self, PyObject *value, void *closure)
623 {
624         // check parameter, report failure
625         if (value == NULL || !PyBool_Check(value))
626         {
627                 PyErr_SetString(PyExc_TypeError, "The value must be a bool");
628                 return -1;
629         }
630         // set scale
631         if (self->m_image != NULL) self->m_image->setFlip(value == Py_True);
632         // success
633         return 0;
634 }
635
636 // get zbuff
637 PyObject *Image_getZbuff(PyImage *self, void *closure)
638 {
639         if (self->m_image != NULL && self->m_image->getZbuff()) Py_RETURN_TRUE;
640         else Py_RETURN_FALSE;
641 }
642
643 // set zbuff
644 int Image_setZbuff(PyImage *self, PyObject *value, void *closure)
645 {
646         // check parameter, report failure
647         if (value == NULL || !PyBool_Check(value))
648         {
649                 PyErr_SetString(PyExc_TypeError, "The value must be a bool");
650                 return -1;
651         }
652         // set scale
653         if (self->m_image != NULL) self->m_image->setZbuff(value == Py_True);
654         // success
655         return 0;
656 }
657
658 // get depth
659 PyObject *Image_getDepth(PyImage *self, void *closure)
660 {
661         if (self->m_image != NULL && self->m_image->getDepth()) Py_RETURN_TRUE;
662         else Py_RETURN_FALSE;
663 }
664
665 // set depth
666 int Image_setDepth(PyImage *self, PyObject *value, void *closure)
667 {
668         // check parameter, report failure
669         if (value == NULL || !PyBool_Check(value))
670         {
671                 PyErr_SetString(PyExc_TypeError, "The value must be a bool");
672                 return -1;
673         }
674         // set scale
675         if (self->m_image != NULL) self->m_image->setDepth(value == Py_True);
676         // success
677         return 0;
678 }
679
680
681
682
683 // get filter source object
684 PyObject *Image_getSource(PyImage *self, PyObject *args)
685 {
686         // get arguments
687         char *id;
688         if (!PyArg_ParseTuple(args, "s:getSource", &id))
689                 return NULL;
690         if (self->m_image != NULL)
691         {
692                 // get source object
693                 PyObject *src = reinterpret_cast<PyObject*>(self->m_image->getSource(id));
694                 // if source is available
695                 if (src != NULL)
696                 {
697                         // return source
698                         Py_INCREF(src);
699                         return src;
700                 }
701         }
702         // source was not found
703         Py_RETURN_NONE;
704 }
705
706
707 // set filter source object
708 PyObject *Image_setSource(PyImage *self, PyObject *args)
709 {
710         // get arguments
711         char *id;
712         PyObject *obj;
713         if (!PyArg_ParseTuple(args, "sO:setSource", &id, &obj))
714                 return NULL;
715         if (self->m_image != NULL)
716         {
717                 // check type of object
718                 if (pyImageTypes.in(Py_TYPE(obj)))
719                 {
720                         // convert to image struct
721                         PyImage * img = reinterpret_cast<PyImage*>(obj);
722                         // set source
723                         if (!self->m_image->setSource(id, img))
724                         {
725                                 // if not set, retport error
726                                 PyErr_SetString(PyExc_RuntimeError, "Invalid source or id");
727                                 return NULL;
728                         }
729                 }
730                 // else report error
731                 else
732                 {
733                         PyErr_SetString(PyExc_RuntimeError, "Invalid type of object");
734                         return NULL;
735                 }
736         }
737         // return none
738         Py_RETURN_NONE;
739 }
740
741
742 // get pixel filter object
743 PyObject *Image_getFilter(PyImage *self, void *closure)
744 {
745         // if image object is available
746         if (self->m_image != NULL)
747         {
748                 // pixel filter object
749                 PyObject *filt = reinterpret_cast<PyObject*>(self->m_image->getFilter());
750                 // if filter is present
751                 if (filt != NULL)
752                 {
753                         // return it
754                         Py_INCREF(filt);
755                         return filt;
756                 }
757         }
758         // otherwise return none
759         Py_RETURN_NONE;
760 }
761
762
763 // set pixel filter object
764 int Image_setFilter(PyImage *self, PyObject *value, void *closure)
765 {
766         // if image object is available
767         if (self->m_image != NULL)
768         {
769                 // check new value
770                 if (value == NULL || !pyFilterTypes.in(Py_TYPE(value)))
771                 {
772                         // report value error
773                         PyErr_SetString(PyExc_TypeError, "Invalid type of value");
774                         return -1;
775                 }
776                 // set new value
777                 self->m_image->setFilter(reinterpret_cast<PyFilter*>(value));
778         }
779         // return success
780         return 0;
781 }
782 PyObject *Image_valid(PyImage *self, void *closure)
783 {
784         if (self->m_image->isImageAvailable())
785         {
786                 Py_RETURN_TRUE;
787         }
788         else
789         {
790                 Py_RETURN_FALSE;
791         }
792 }
793
794 static int Image_getbuffer(PyImage *self, Py_buffer *view, int flags)
795 {
796         unsigned int * image;
797         int ret;
798
799         try {
800                 // can throw in case of resize
801                 image = self->m_image->getImage();
802         }
803         catch (Exception & exp) {
804                 exp.report();
805                 return -1;
806         }
807
808         if (!image) {
809                 PyErr_SetString(PyExc_BufferError, "Image buffer is not available");
810                 return -1;
811         }
812         if (view == NULL)
813         {
814                 self->m_image->m_exports++;
815                 return 0;
816         }
817         ret = PyBuffer_FillInfo(view, (PyObject *)self, image, self->m_image->getBuffSize(), 0, flags);
818         if (ret >= 0)
819                 self->m_image->m_exports++;
820         return ret;
821 }
822
823 static void Image_releaseBuffer(PyImage *self, Py_buffer *buffer)
824 {
825         self->m_image->m_exports--;
826 }
827
828 PyBufferProcs imageBufferProcs = 
829 {
830         (getbufferproc)Image_getbuffer,
831         (releasebufferproc)Image_releaseBuffer
832 };
833