Applied [#7076] Updated Python Image API to use float buffers in getPixelF/setPixelF
authorCampbell Barton <ideasman42@gmail.com>
Mon, 10 Mar 2008 12:17:58 +0000 (12:17 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Mon, 10 Mar 2008 12:17:58 +0000 (12:17 +0000)
and added an image.updateDisplay() function to update imbuf->rect from imbuf->rect_float
also corrected some docstrings and epydocs

source/blender/python/api2_2x/Image.c
source/blender/python/api2_2x/bpy_data.c
source/blender/python/api2_2x/doc/Armature.py
source/blender/python/api2_2x/doc/Group.py
source/blender/python/api2_2x/doc/Image.py

index ec822004596de75424c037ae385171c9e95d0a73..a182563170d5a6da281da84c015406abbed60fb5 100644 (file)
@@ -38,6 +38,7 @@
 #include "BKE_library.h"
 #include "BKE_image.h"
 #include "BKE_idprop.h"
+#include "BKE_utildefines.h"
 #include "BIF_drawimage.h"
 #include "BLI_blenlib.h"
 #include "DNA_space_types.h"   /* FILE_MAXDIR = 160 */
@@ -106,6 +107,7 @@ static PyObject *Image_setStart( BPy_Image * self, PyObject * args );
 static PyObject *Image_setEnd( BPy_Image * self, PyObject * args );
 static PyObject *Image_setSpeed( BPy_Image * self, PyObject * args );
 static PyObject *Image_reload( BPy_Image * self );
+static PyObject *Image_updateDisplay( BPy_Image * self );
 static PyObject *Image_glLoad( BPy_Image * self );
 static PyObject *Image_glFree( BPy_Image * self );
 static PyObject *Image_getPixelF( BPy_Image * self, PyObject * args );
@@ -126,11 +128,11 @@ static PyObject *Image_makeCurrent( BPy_Image * self );
 static PyMethodDef BPy_Image_methods[] = {
        /* name, method, flags, doc */
        {"getPixelF", ( PyCFunction ) Image_getPixelF, METH_VARARGS,
-        "(int, int) - Get pixel color as floats 0.0-1.0 returns [r,g,b,a]"},
+        "(int, int) - Get pixel color as floats returns [r,g,b,a]"},
        {"getPixelI", ( PyCFunction ) Image_getPixelI, METH_VARARGS,
         "(int, int) - Get pixel color as ints 0-255 returns [r,g,b,a]"},
        {"setPixelF", ( PyCFunction ) Image_setPixelF, METH_VARARGS,
-        "(int, int, [f r,f g,f b,f a]) - Set pixel color using floats 0.0-1.0"},
+        "(int, int, [f r,f g,f b,f a]) - Set pixel color using floats"},
        {"setPixelI", ( PyCFunction ) Image_setPixelI, METH_VARARGS,
         "(int, int, [i r, i g, i b, i a]) - Set pixel color using ints 0-255"},
        {"getMaxXY", ( PyCFunction ) Image_getMaxXY, METH_NOARGS,
@@ -159,6 +161,8 @@ static PyMethodDef BPy_Image_methods[] = {
         "() - Return Image object's bind code value"},
        {"reload", ( PyCFunction ) Image_reload, METH_NOARGS,
         "() - Reload the image from the filesystem"},
+       {"updateDisplay", ( PyCFunction ) Image_updateDisplay, METH_NOARGS,
+        "() - Update the display image from the floating point buffer (if it exists)"},
        {"glLoad", ( PyCFunction ) Image_glLoad, METH_NOARGS,
         "() - Load the image data in OpenGL texture memory.\n\
        The bindcode (int) is returned."},
@@ -199,7 +203,7 @@ static PyMethodDef BPy_Image_methods[] = {
 static char M_Image_doc[] = "The Blender Image module\n\n";
 
 static char M_Image_New_doc[] =
-       "() - return a new Image object";
+       "(name, width, height, depth) - all args are optional, return a new Image object";
 
 static char M_Image_Get_doc[] =
        "(name) - return the image with the name 'name', \
@@ -243,7 +247,7 @@ static PyObject *M_Image_New( PyObject * self, PyObject * args)
        if (width > 5000 || height > 5000 || width < 1 || height < 1)
                return ( EXPP_ReturnPyObjError( PyExc_TypeError,
                                        "Image width and height must be between 1 and 5000" ) );
-       image = BKE_add_image_size(width, height, name, 0, 0, color);
+       image = BKE_add_image_size(width, height, name, depth==128 ? 1 : 0, 0, color);
        if( !image )
                return ( EXPP_ReturnPyObjError( PyExc_MemoryError,
                                                "couldn't create PyObject Image_Type" ) );
@@ -382,27 +386,25 @@ static PyObject *M_Image_Load( PyObject * self, PyObject * value )
 /**
  * getPixelF( x, y )
  *  returns float list of pixel colors in rgba order.
- *  returned values are floats normalized to 0.0 - 1.0.
- *  blender images are all 4x8 bit at the moment apr-2005
- */
+ *  returned values are floats , in the full range of the float image source.
+  */
 
 static PyObject *Image_getPixelF( BPy_Image * self, PyObject * args )
 {
 
        PyObject *attr;
        ImBuf *ibuf= BKE_image_get_ibuf(self->image, NULL);
-       char *pixel;            /* image data */
        int index;              /* offset into image data */
        int x = 0;
        int y = 0;
-       int pixel_size = 4;     /* each pixel is 4 x 8-bits packed in unsigned int */
+       int pixel_size = 4;     /* each pixel is 4 x 32 bit float */
        int i;
        
        if( !PyArg_ParseTuple( args, "ii", &x, &y ) )
                return EXPP_ReturnPyObjError( PyExc_TypeError,
                                              "expected 2 integers" );
 
-       if( !ibuf || !ibuf->rect )      /* loading didn't work */
+       if( !ibuf || (!ibuf->rect_float && !ibuf->rect))        /* loading didn't work */
                return EXPP_ReturnPyObjError( PyExc_RuntimeError,
                                              "couldn't load image data in Blender" );
 
@@ -428,15 +430,26 @@ static PyObject *Image_getPixelF( BPy_Image * self, PyObject * args )
                                      "couldn't allocate memory for color list" );
        
        index = ( x + y * ibuf->x ) * pixel_size;
-
-       pixel = ( char * ) ibuf->rect;
-       for (i=0; i<4; i++) {
-               PyList_SetItem( attr, i, PyFloat_FromDouble( ( ( double ) pixel[index+i] ) / 255.0 ));
+       
+       /* if a float buffer exists, use it, otherwise convert the 8bpc buffer to float values */
+       if (ibuf->rect_float) {
+               float *pixelf;          /* image data */
+               
+               pixelf = ibuf->rect_float;
+               for (i=0; i<4; i++) {
+                       PyList_SetItem( attr, i, PyFloat_FromDouble( (double)pixelf[index+i] ) );
+               }
+       } else {
+               char *pixelc;           /* image data */
+               
+               pixelc = ( char * ) ibuf->rect;
+               for (i=0; i<4; i++) {
+                       PyList_SetItem( attr, i, PyFloat_FromDouble( ( ( double ) pixelc[index+i] ) / 255.0 ));
+               }
        }
        return attr;
 }
 
-
 /**
  * getPixelI( x, y )
  *  returns integer list of pixel colors in rgba order.
@@ -497,12 +510,11 @@ static PyObject *Image_getPixelI( BPy_Image * self, PyObject * args )
 static PyObject *Image_setPixelF( BPy_Image * self, PyObject * args )
 {
        ImBuf *ibuf= BKE_image_get_ibuf(self->image, NULL);
-       char *pixel;            /* image data */
+       float *pixel;           /* image data */
        int index;              /* offset into image data */
        int x = 0;
        int y = 0;
-       int a = 0;
-       int pixel_size = 4;     /* each pixel is 4 x 8-bits packed in unsigned int */
+       int pixel_size = 4;     /* each pixel is 4 x 32 bit float */
        float p[4];
 
        if( !PyArg_ParseTuple
@@ -510,7 +522,7 @@ static PyObject *Image_setPixelF( BPy_Image * self, PyObject * args )
                return EXPP_ReturnPyObjError( PyExc_TypeError,
                                              "expected 2 integers and an array of 4 floats" );
 
-       if( !ibuf || !ibuf->rect )      /* didn't work */
+       if( !ibuf )     /* didn't work */
                return EXPP_ReturnPyObjError( PyExc_RuntimeError,
                                              "couldn't load image data in Blender" );
 
@@ -522,28 +534,16 @@ static PyObject *Image_setPixelF( BPy_Image * self, PyObject * args )
            || y > ( ibuf->y - 1 )
            || x < ibuf->xorig || y < ibuf->yorig )
                return EXPP_ReturnPyObjError( PyExc_RuntimeError,
-                                             "x or y is out of ruange" );
-
-       for( a = 0; a < 4; a++ ) {
-               if( p[a] > 1.0 || p[a] < 0.0 )
-                       return EXPP_ReturnPyObjError( PyExc_RuntimeError,
-                                                     "r, g, b, or a is out of range" );
-       }
-
-
-       /* 
-          assumption: from looking at source, skipx is often not set,
-          so we calc ourselves
-        */
+                                             "x or y is out of range" );
 
+       /* if no float buffer already exists, add it */
+       if (!ibuf->rect_float) imb_addrectfloatImBuf(ibuf);
+       
        index = ( x + y * ibuf->x ) * pixel_size;
 
-       pixel = ( char * ) ibuf->rect;
-
-       pixel[index] = ( char ) ( p[0] * 255.0 );
-       pixel[index + 1] = ( char ) ( p[1] * 255.0 );
-       pixel[index + 2] = ( char ) ( p[2] * 255.0 );
-       pixel[index + 3] = ( char ) ( p[3] * 255.0 );
+       pixel = ibuf->rect_float + index;
+       
+       QUATCOPY(pixel, p);
 
        ibuf->userflags |= IB_BITMAPDIRTY;
        Py_RETURN_NONE;
@@ -821,8 +821,11 @@ static PyObject *Image_getDepth( BPy_Image * self )
        if( !ibuf )     /* didn't work */
                return EXPP_ReturnPyObjError( PyExc_RuntimeError,
                                              "couldn't load image data in Blender" );
-
-       return PyInt_FromLong( (long)ibuf->depth );
+       if (ibuf->rect_float) {
+               return PyInt_FromLong( (long)128 );
+       } else {
+               return PyInt_FromLong( (long)ibuf->depth );
+       }
 }
 
 
@@ -865,6 +868,21 @@ static PyObject *Image_reload( BPy_Image * self )
        Py_RETURN_NONE;
 }
 
+static PyObject *Image_updateDisplay( BPy_Image * self )
+{
+       ImBuf *ibuf= BKE_image_get_ibuf(self->image, NULL);
+
+       if( !ibuf )     /* didn't work */
+               return EXPP_ReturnPyObjError( PyExc_RuntimeError,
+                                             "couldn't load image data in Blender" );
+
+       IMB_rect_from_float(ibuf);
+
+       Py_RETURN_NONE;
+}
+
+
+
 static PyObject *Image_glFree( BPy_Image * self )
 {
        Image *image = self->image;
index 3fb58146ca9a30c907cf381364055a6cd4ccd043..428f8cc6d9aae4db14c084c594a583f8d88c0c8d 100644 (file)
@@ -394,7 +394,7 @@ PyObject *LibBlockSeq_new(BPy_LibBlockSeq *self, PyObject * args, PyObject *kwd)
 {
        ID *id = NULL;
        char *name=NULL, *filename=NULL, *data_type=NULL;
-       int img_width=256, img_height=256;
+       int img_width=256, img_height=256, img_depth=32;
        float color[] = {0, 0, 0, 1};
        short data_code = 0;
        int user_count = 0;
@@ -456,8 +456,8 @@ PyObject *LibBlockSeq_new(BPy_LibBlockSeq *self, PyObject * args, PyObject *kwd)
        
        /* New Data */
        if (self->type == ID_IM) {
-               /* Image, accepts width and height*/
-               if( !PyArg_ParseTuple( args, "|sii", &name, &img_width, &img_height ) )
+               /* Image, accepts width and height, depth */
+               if( !PyArg_ParseTuple( args, "|siii", &name, &img_width, &img_height, &img_depth ) )
                        return EXPP_ReturnPyObjError( PyExc_TypeError,
                                "one string and two ints expected as arguments" );
                CLAMP(img_width,  4, 5000);
@@ -538,7 +538,7 @@ PyObject *LibBlockSeq_new(BPy_LibBlockSeq *self, PyObject * args, PyObject *kwd)
                break;
        case ID_IM: 
        {
-               id = (ID *)BKE_add_image_size(img_width, img_height, name?name:"Image", 0, 0, color);
+               id = (ID *)BKE_add_image_size(img_width, img_height, name?name:"Image", img_depth==128 ? 1:0, 0, color);
                if( !id )
                        return ( EXPP_ReturnPyObjError( PyExc_MemoryError,
                                "couldn't create PyObject Image_Type" ) );
@@ -562,14 +562,14 @@ PyObject *LibBlockSeq_new(BPy_LibBlockSeq *self, PyObject * args, PyObject *kwd)
                break;
        case ID_VF:
                return EXPP_ReturnPyObjError( PyExc_TypeError,
-                       "Cannot create new fonts, use the load() function to load from a file" );
+                       "Cannot create new fonts, use the new(name, filename) function to load from a file" );
        case ID_TXT:
                id = (ID *)add_empty_text( name?name:"Text" );
                user_count = 1;
                break;
        case ID_SO:
                return EXPP_ReturnPyObjError( PyExc_TypeError,
-                       "Cannot create new sounds, use the load() function to load from a file" );
+                       "Cannot create new sounds, use the new(name, filename) function to load from a file" );
        case ID_GR:     
                id = (ID *)add_group( name?name:"Group" );
                user_count = 1;
index 0dbaf28d6f878f510a8010ab0c18c453bf9d65a5..68916af6166e16c944e96abeca49259027b5844d 100644 (file)
@@ -192,6 +192,16 @@ class Armature:
                @note: Must have called makeEditable() first.
                @rtype: None
                """
+       def copy():
+               """
+               Return a copy of this armature.
+               @rtype: Armature
+               """
+       def __copy__():
+               """
+               Return a copy of this armature.
+               @rtype: Armature
+               """
 
 import id_generics
 Armature.__doc__ += id_generics.attributes 
index aca7c56b4a52c8bb0a9fa155518a40f0b60c740f..6bd6e105ec37de1eb6fb156bb577f6f8af45d5e2 100644 (file)
@@ -121,6 +121,12 @@ class Group:
                @rtype: Group
                @return:  a copy of this group
                """
+       def copy ():
+               """
+               Make a copy of this group
+               @rtype: Group
+               @return:  a copy of this group
+               """
 
 import id_generics
 Group.__doc__ += id_generics.attributes 
index 6d613b3c8fe251aa2a136a635de2e5f1e9bb38ed..6b725fa9f78977498a7a480c509250bbb9a112db 100644 (file)
@@ -48,7 +48,7 @@ def New (name, width, height, depth):
        @type height: int
        @param height: The height of the new Image object, between 1 and 5000.
        @type depth: int
-       @param depth: The colour depth of the new Image object. (8:Grey, 24:RGB, 32:RGBA). (Not implimented yet, all new images will be 24bit)
+       @param depth: The colour depth of the new Image object. (32:RGBA 8bit channels, 128:RGBA 32bit high dynamic range float channels).
        @rtype: Blender Image
        @return: A new Blender Image object.
        """
@@ -83,7 +83,7 @@ class Image:
        @type filename: string
        @ivar size: The [width, height] dimensions of the image (in pixels).
        @type size: list
-       @ivar depth: The pixel depth of the image. [8, 16, 18, 24, 32]
+       @ivar depth: The pixel depth of the image, read only. [8, 16, 18, 24, 32, 128 (for 32bit float color channels)]
        @type depth: int
        @ivar xrep: Texture tiling: the number of repetitions in the x (horizontal)
                 axis. [1, 16].
@@ -137,14 +137,14 @@ class Image:
 
        def getDepth():
                """
-               Get the pixel depth of this image.
+               Get the pixel depth of this image. [8,16,24,32,128 for 32bit float images]
                @rtype: int
                """
 
        def getPixelF(x, y):
                """
                Get the the colors of the current pixel in the form [r,g,b,a].
-               Returned values are floats normalized to 0.0 - 1.0.
+               For float image types, returned values can be greater then the useual [0.0, 1.0] range.
                Pixel coordinates are in the range from 0 to N-1.  See L{getMaxXY}
                @returns: [ r, g, b, a]
                @rtype: list of 4 floats
@@ -229,6 +229,12 @@ class Image:
                @returns: None
                """
 
+       def updateDisplay():
+               """
+               Update the display image from the floating point buffer (if it exists)
+               @returns: None
+               """
+
        def glLoad():
                """
                Load this image's data into OpenGL texture memory, if it is not already
@@ -306,7 +312,7 @@ class Image:
        def setPixelF(x, y, (r, g, b,a )):
                """
                Set the the colors of the current pixel in the form [r,g,b,a].
-               Color values must be floats in the range 0.0 - 1.0.
+               For float image types, returned values can be greater then the useual [0.0, 1.0] range.
                Pixel coordinates are in the range from 0 to N-1.  See L{getMaxXY}
                @type x: int
                @type y: int