[Bf-committers] Patch to implement Blender.Draw.Image()

Jonathan Merritt j.merritt at pgrad.unimelb.edu.au
Sat Nov 6 09:06:59 CET 2004


Hi Everyone,

I guess this might be very poor timing (because of the CVS freeze), but 
attached is a patch to implement a new function, Blender.Draw.Image():


Blender.Draw.Image(image, x, y, zoomx, zoomy, clipx, clipy, clipw, cliph):

    Draws a Blender.Image on the screen using raster operations.

    (image) - Blender.Image to draw.
    (x, y) - Raster coordinates of the lower-left corner of the image 
(can be negative).
    (zoomx, zoomy) - Horizontal and vertical zoom factors.
    (clipx, clipy, clipw, cliph) - Image space clipping rectangle.


Let me know if I should re-submit for consideration after the freeze.  
This method is one that I'd like very much to use from within my camera 
calibration script.  It allows for non-pixel-aligned image drawing, 
which is something I can't achieve using BGL.

Thanks,

Jonathan Merritt.

-------------- next part --------------
Index: source/blender/python/api2_2x/Draw.c
===================================================================
RCS file: /cvsroot/bf-blender/blender/source/blender/python/api2_2x/Draw.c,v
retrieving revision 1.30
diff -u -r1.30 Draw.c
--- source/blender/python/api2_2x/Draw.c	25 Sep 2004 20:30:39 -0000	1.30
+++ source/blender/python/api2_2x/Draw.c	6 Nov 2004 08:02:54 -0000
@@ -55,6 +55,7 @@
 #include "DNA_text_types.h"
 
 #include "BKE_global.h"
+#include "BKE_image.h"
 #include "BKE_library.h"
 #include "BKE_object.h"
 
@@ -67,6 +68,10 @@
 
 #include "BPI_script.h"		/* script struct */
 
+#include "Image.h"              /* for accessing Blender.Image objects */
+#include "IMB_imbuf_types.h"    /* for the IB_rect define */
+#include "blendef.h"            /* for the CLAMP() macro */
+
 #include "interface.h"
 #include "mydevice.h"		/*@ for all the event constants */
 
@@ -112,6 +117,8 @@
 static PyObject *Method_PupIntInput( PyObject * self, PyObject * args );
 static PyObject *Method_PupFloatInput( PyObject * self, PyObject * args );
 static PyObject *Method_PupStrInput( PyObject * self, PyObject * args );
+/* next by Jonathan Merritt (lancelet): */
+static PyObject *Method_Image( PyObject * self, PyObject * args);
 
 static uiBlock *Get_uiBlock( void );
 static void py_slider_update( void *butv, void *data2_unused );
@@ -267,6 +274,14 @@
 the float value show.\n\
 Return the user input value or None on user exit";
 
+static char Method_Image_doc[] =
+        "(image, x, y, zoomx = 1.0, zoomy = 1.0, [clipx, clipy, clipw, cliph])) \n\
+    - Draw an image.\n\
+(image) - Blender.Image to draw.\n\
+(x, y) - floats specifying the location of the image.\n\
+(zoomx, zoomy) - float zoom factors in horizontal and vertical directions.\n\
+(clipx, clipy, clipw, cliph) - integers specifying a clipping rectangle within the original image.";
+
 static char Method_PupStrInput_doc[] =
 	"(text, default, max = 20) - Display a float pop-up input.\n\
 (text) - text string to display on the button;\n\
@@ -304,6 +319,7 @@
 	MethodDef( PupIntInput ),
 	MethodDef( PupFloatInput ),
 	MethodDef( PupStrInput ),
+        MethodDef( Image ),
 	MethodDef( Exit ),
 	MethodDef( Redraw ),
 	MethodDef( Draw ),
@@ -1194,6 +1210,102 @@
 				      "couldn't create a PyString" );
 }
 
+/*****************************************************************************
+ * Function:            Method_Image                                         *
+ * Python equivalent:   Blender.Draw.Image                                   *
+ *                                                                           *
+ * @author Jonathan Merritt <j.merritt at pgrad.unimelb.edu.au>                 *
+ ****************************************************************************/
+static PyObject *Method_Image( PyObject * self, PyObject * args )
+{
+        PyObject *pyObjImage;
+        BPy_Image *py_img;
+        Image *image;
+        float originX, originY;
+        float zoomX = 1.0, zoomY = 1.0;
+        int clipX = 0, clipY = 0, clipW = -1, clipH = -1;
+
+        /* parse the arguments passed-in from Python */
+        if( !PyArg_ParseTuple( args, "Off|ffiiii", &pyObjImage, 
+             &originX, &originY, &zoomX, &zoomY, 
+             &clipX, &clipY, &clipW, &clipH ) )
+                return EXPP_ReturnPyObjError( PyExc_TypeError,
+                        "expected a Blender.Image and 2 floats, and " \
+                        "optionally 2 floats and 4 ints as arguments" );
+        /* check that the first PyObject is actually a Blender.Image */
+        if( !Image_CheckPyObject( pyObjImage ) )
+                return EXPP_ReturnPyObjError( PyExc_TypeError,
+                        "expected a Blender.Image and 2 floats, and " \
+                        "optionally 2 floats and 4 ints as arguments" );
+        /* check for valid zoom */
+        if( ( zoomX <= 0.0 ) || ( zoomY <= 0.0 ) )
+                return EXPP_ReturnPyObjError( PyExc_TypeError,
+                        "invalid zoom factors - they must be >= 0.0" );
+
+        /* fetch a C Image pointer from the passed-in Python object */
+        py_img = ( BPy_Image * ) pyObjImage;
+        image = py_img->image;
+
+        /* load the image data if necessary */
+        if( !image->ibuf )      /* if no image data is available */
+                load_image( image, IB_rect, "", 0 );    /* load it */
+        if( !image->ibuf )      /* failed to load the image */
+                return EXPP_ReturnPyObjError( PyExc_RuntimeError,
+                        "couldn't load image data in Blender" );
+       
+        /* set up a valid clipping rectangle.  if no clip rectangle was
+         * given, this results in inclusion of the entire image.  otherwise,
+         * the clipping is just checked against the bounds of the image.
+         * if clipW or clipH are less than zero then they include as much of
+         * the image as they can. */
+        CLAMP( clipX, 0, image->ibuf->x );
+        CLAMP( clipY, 0, image->ibuf->y );
+        if( ( clipW < 0 ) || ( clipW > ( image->ibuf->x - clipW ) ) )
+                clipW = image->ibuf->x - clipX;
+        if( ( clipH < 0 ) || ( clipH > ( image->ibuf->y - clipH ) ) )
+                clipH = image->ibuf->y - clipY;
+
+        /* -- we are "Go" to Draw! -- */
+
+        /* set the raster position.
+         *
+         * If the raster position is negative, then using glRasterPos2i() would
+         * cause it to be clipped.  Instead, we set the raster position to
+         * (0,0), which is hopefully valid, and we then use the glBitmap()
+         * method to offset it.  This results in a valid, unclipped raster
+         * position whether or not it is outside of the window.
+         *
+         * This particular technique is documented in the glRasterPos() man
+         * page, although I haven't seen it used elsewhere in Blender.
+         */
+        glRasterPos2i( 0, 0 );
+        glBitmap( 0, 0, 0.0, 0.0, originX, originY, NULL );
+
+        /* set the zoom */
+        glPixelZoom( zoomX, zoomY );
+
+        /* set the width of the image (ROW_LENGTH), and the offset to the
+         * clip origin within the image in x (SKIP_PIXELS) and 
+         * y (SKIP_ROWS) */
+        glPixelStorei( GL_UNPACK_ROW_LENGTH,  image->ibuf->x );
+        glPixelStorei( GL_UNPACK_SKIP_PIXELS, clipX );
+        glPixelStorei( GL_UNPACK_SKIP_ROWS,   clipY );
+
+        /* draw the image */
+        glDrawPixels( clipW, clipH, GL_RGBA, GL_UNSIGNED_BYTE, 
+                      image->ibuf->rect );
+
+        /* restore the defaults for these parameters (could also use a
+         * glPushClientAttrib() and glPopClientAttrib() pair). */
+        glPixelZoom( 1.0, 1.0 );
+        glPixelStorei( GL_UNPACK_SKIP_ROWS,   0 );
+        glPixelStorei( GL_UNPACK_SKIP_PIXELS, 0 );
+        glPixelStorei( GL_UNPACK_ROW_LENGTH,  0 );
+        
+        Py_INCREF( Py_None );
+        return Py_None;
+        
+}
 
 PyObject *Draw_Init( void )
 {
Index: source/blender/python/api2_2x/doc/Draw.py
===================================================================
RCS file: /cvsroot/bf-blender/blender/source/blender/python/api2_2x/doc/Draw.py,v
retrieving revision 1.13
diff -u -r1.13 Draw.py
--- source/blender/python/api2_2x/doc/Draw.py	16 Jul 2004 03:08:43 -0000	1.13
+++ source/blender/python/api2_2x/doc/Draw.py	6 Nov 2004 08:02:58 -0000
@@ -594,6 +594,67 @@
   @return: The width of I{string} drawn with the chosen I{fontsize}.
   """
 
+def Image(image, x, y, zoomx=1.0, zoomy=1.0, clipx=0, clipy=0, clipw=-1, cliph=-1):
+  """
+  Draw an image on the screen.
+
+  The image is drawn at the location specified by the coordinates (x,y).  A
+  pair of optional zoom factors (in horizontal and vertical directions) can
+  be applied to the image as it is drawn, and an additional clipping rectangle
+  can be applied to extract a particular sub-region of the image to draw.
+
+  Note that the clipping rectangle is given in image space coordinates.  In
+  image space, the origin is located at the bottom left, with x coordinates 
+  increasing to the right and y coordinates increasing upwards.  No matter 
+  where the clipping rectangle is placed in image space, the lower-left pixel 
+  drawn on the screen is always placed at the coordinates (x,y).  The
+  clipping rectangle is itself clipped to the dimensions of the image.  If
+  either the width or the height of the clipping rectangle are negative then
+  the corresponding dimension (width or height) is set to include as much of 
+  the image as possible.
+
+  Example::
+   import Blender
+   from Blender import BGL, Image, Draw
+
+   myimage = Image.Load('myimage.png')
+
+   def gui():
+        Draw.Image(myimage, 50, 50)
+   def event(evt, val):
+        if evt == Draw.ESCKEY:
+              Draw.Exit()
+
+   Draw.Register(gui, event, None)
+
+  @type image: Blender.Image
+  @param image: The image to draw.
+  @type x: int
+  @param x: The lower left x (horizontal) position of the origin of the image.
+  @type y: int
+  @param y: The lower left y (vertical) position of the origin of the image.
+  @type zoomx: float
+  @param zoomx: The x (horizontal) zoom factor to use when drawing the image.
+  @type zoomy: float
+  @param zoomy: The y (vertical) zoom factor to use when drawing the image.
+  @type clipx: int
+  @param clipx: The lower left x (horizontal) origin of the clipping rectangle
+                within the image.  A value of 0 indicates the left of the
+                image.
+  @type clipy: int
+  @param clipy: The lower left y (vertical) origin of the clipping rectangle
+                within the image.  A value of 0 indicates the bottom of the
+                image.
+  @type clipw: int
+  @param clipw: The width of the clipping rectangle within the image. If this
+                value is negative then the clipping rectangle includes as much
+                of the image as possible in the x (horizontal) direction.
+  @type cliph: int
+  @param cliph: The height of the clipping rectangle within the image. If this
+                value is negative then the clipping rectangle includes as much
+                of the image as possible in the y (vertical) direction.
+  """
+
 class Button:
   """
   The Button object


More information about the Bf-committers mailing list