Commits

Lenard Lindstrom  committed bb0a859

new _arraysurfarray module. blit_array bug 24 fix.
blit_array now sets alpha to 255 for 3D array blit onto a SRCALPHA surface.
_arraysurfarray is the foundation for adding new C surfarray C functions.
It uses the array interface so works with the Numeric 24.2 as well.
It has no NumPy specific code, doesn't include NumPy or Numeric headers.

  • Participants
  • Parent commits d480367

Comments (0)

Files changed (8)

 mask src/mask.c src/bitmask.c $(SDL) $(DEBUG)
 bufferproxy src/bufferproxy.c $(SDL) $(DEBUG)
 pixelarray src/pixelarray.c $(SDL) $(DEBUG)
+_arraysurfarray src/_arraysurfarray.c $(SDL) $(DEBUG)
 

File docs/ref/surfarray.html

 <big><b>pygame.surfarray.blit_array</big></b><br><ul>
   <i>Blit directly from a array values</i><br>
   <tt>pygame.surfarray.blit_array(Surface, array): return None</tt><br>
-<p>Directly copy values from an array into a Surface. This is faster than converting the array into a Surface and blitting. The array must be the same dimensions as the Surface and will completely replace all pixel values. </p>
+<p>Directly copy values from an array into a Surface. This is faster than converting the array into a Surface and blitting. The array must be the same dimensions as the Surface and will completely replace all pixel values. Only integer, ascii character and record arrays are accepted. </p>
 <p>This function will temporarily lock the Surface as the new values are copied. </p>
 <!--COMMENTS:pygame.surfarray.blit_array--> &nbsp;<br> 
 <br></ul>

File examples/arraydemo.py

                 raise SystemExit
 
     #allblack
-    allblack = N.zeros((128, 128))
+    allblack = N.zeros((128, 128), int32)
     surfdemo_show(allblack, 'allblack')
 
 

File lib/surfarray.doc

 Directly copy values from an array into a Surface. This is faster than
 converting the array into a Surface and blitting. The array must be the
 same dimensions as the Surface and will completely replace all pixel
-values.
+values. Only integer, ascii character and record arrays are accepted.
 
 This function will temporarily lock the Surface as the new values are
 copied.

File lib/surfarray.py

 if not __hasnumpy and not __hasnumeric:
     raise ImportError, "no module named numpy or Numeric found"
 
+from _arraysurfarray import blit_array
+
 def array2d (surface):
     """pygame.surfarray.array2d (Surface): return array
 
         return numpysf.make_surface (array)
     raise NotImplementedError, "surface arrays are not supported"
 
-def blit_array (surface, array):
-    """pygame.surfarray.blit_array (Surface, array): return None
-
-    Blit directly from a array values.
-
-    Directly copy values from an array into a Surface. This is faster
-    than converting the array into a Surface and blitting. The array
-    must be the same dimensions as the Surface and will completely
-    replace all pixel values.
-
-    This function will temporarily lock the Surface as the new values
-    are copied.
-    """
-    if __arraytype == "numeric":
-        return numericsf.blit_array (surface, array)
-    elif __arraytype == "numpy":
-        return numpysf.blit_array (surface, array)
-    raise NotImplementedError, "surface arrays are not supported"
+##def blit_array (surface, array):
+##    """pygame.surfarray.blit_array (Surface, array): return None
+##
+##    Blit directly from a array values.
+##
+##    Directly copy values from an array into a Surface. This is faster
+##    than converting the array into a Surface and blitting. The array
+##    must be the same dimensions as the Surface and will completely
+##    replace all pixel values.
+##
+##    This function will temporarily lock the Surface as the new values
+##    are copied.
+##    """
+##    if __arraytype == "numeric":
+##        return numericsf.blit_array (surface, array)
+##    elif __arraytype == "numpy":
+##        return numpysf.blit_array (surface, array)
+##    raise NotImplementedError, "surface arrays are not supported"
 
 def map_array (surface, array):
     """pygame.surfarray.map_array (Surface, array3d): return array2d

File src/_arraysurfarray.c

+/*
+  pygame - Python Game Library
+  Copyright (C) 2000-2001  Pete Shinners
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Library General Public
+  License as published by the Free Software Foundation; either
+  version 2 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Library General Public License for more details.
+
+  You should have received a copy of the GNU Library General Public
+  License along with this library; if not, write to the Free
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+  Pete Shinners
+  pete@shinners.org
+*/
+
+#include <stddef.h>
+#include "pygame.h"
+#include "pygamedocs.h"
+#include "pgarrinter.h"
+#include <SDL_byteorder.h>
+
+static int
+_get_array_interface(PyObject *obj,
+		     PyObject **cobj_p,
+		     PyArrayInterface **inter_p)
+{
+    if (!(*cobj_p = PyObject_GetAttrString(obj, "__array_struct__"))) {
+	if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
+		PyErr_Clear();
+		PyErr_SetString(PyExc_ValueError,
+				"no C-struct array interface");
+	    }
+	return 0;
+    }
+
+    if (!PyCObject_Check(*cobj_p) ||  /* conditional 'or's */
+	!(*inter_p = PyCObject_AsVoidPtr(*cobj_p)) ||
+	(*inter_p)->two != 2) {
+	Py_DECREF(*cobj_p);
+	PyErr_SetString(PyExc_ValueError, "invalid array interface");
+	return 0;
+    }
+    return 1;
+}
+
+/*macros used to blit arrays*/
+#define COPYMACRO_2D(DST, SRC)                                           \
+    for (loopy = 0; loopy < sizey; ++loopy)                              \
+    {                                                                    \
+        DST* imgrow = (DST*)(((char*)surf->pixels)+loopy*surf->pitch);   \
+        Uint8* datarow = (Uint8*)array_data + stridey * loopy;           \
+        for (loopx = 0; loopx < sizex; ++loopx)                          \
+            *(imgrow + loopx) = (DST)*(SRC*)(datarow + stridex * loopx); \
+    }
+
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+#define COPYMACRO_2D_24_PIXEL(pix, data, SRC)    			\
+    *pix++ = *data;					                \
+    *pix++ = *(data + 1);					        \
+    *pix++ = *(data + 2);
+#else
+#define COPYMACRO_2D_24_PIXEL(pix, data, SRC)    			\
+    *pix++ = *(data + sizeof (SRC) - 1);				\
+    *pix++ = *(data + sizeof (SRC) - 2);				\
+    *pix++ = *(data + sizeof (SRC) - 3);
+#endif
+
+#define COPYMACRO_2D_24(SRC)                                            \
+    for (loopy = 0; loopy < sizey; ++loopy)                             \
+    {                                                                   \
+        Uint8 *pix = ((Uint8 *)surf->pixels) + loopy * surf->pitch;     \
+        Uint8 *data = (Uint8 *)array_data + stridey * loopy;            \
+        Uint8 *end = pix + 3 * sizex;                                   \
+        while (pix != end) {                                            \
+            COPYMACRO_2D_24_PIXEL(pix, data, SRC)			\
+            data += stridex;                                            \
+        }                                                               \
+    }
+
+#define COPYMACRO_3D(DST, SRC)                                          \
+    for (loopy = 0; loopy < sizey; ++loopy)                             \
+    {                                                                   \
+        DST* pix = (DST*)(((char*)surf->pixels) + surf->pitch * loopy); \
+        char* data = array_data + stridey * loopy;                      \
+        for (loopx = 0; loopx < sizex; ++loopx) {                       \
+            *pix++ = (DST)(*(SRC*)(data) >> Rloss << Rshift) |          \
+                (*(SRC*)(data+stridez) >> Gloss << Gshift) |            \
+                (*(SRC*)(data+stridez2) >> Bloss << Bshift) |           \
+                alpha;                                                  \
+            data += stridex;                                            \
+        }                                                               \
+    }
+
+#define COPYMACRO_3D_24(SRC)                                            \
+    for (loopy = 0; loopy < sizey; ++loopy)                             \
+    {                                                                   \
+        Uint8 *pix = ((Uint8*)surf->pixels) + surf->pitch * loopy;      \
+        Uint8 *data = (Uint8*)array_data + stridey * loopy;             \
+	Uint8 *end = pix + 3 * sizex;                                   \
+        while (pix != end) {                                            \
+            *pix++ = (Uint8)*(SRC*)(data + stridez_0);                  \
+            *pix++ = (Uint8)*(SRC*)(data + stridez_1);                  \
+            *pix++ = (Uint8)*(SRC*)(data + stridez_2);                  \
+            data += stridex;                                            \
+        }                                                               \
+    }
+
+static PyObject*
+blit_array(PyObject* self, PyObject* arg)
+{
+    PyObject *surfobj, *arrayobj;
+    PyObject *cobj;
+    PyArrayInterface *inter;
+    void *array_data;
+    SDL_Surface* surf;
+    SDL_PixelFormat* format;
+    int loopx, loopy;
+    int stridex, stridey, stridez=0, stridez2=0, sizex, sizey;
+    int Rloss, Gloss, Bloss, Rshift, Gshift, Bshift;
+    char typekind, *cptr;
+    const char *validtypekinds = "iuSV";
+    
+    if (!PyArg_ParseTuple(arg, "O!O", &PySurface_Type, &surfobj, &arrayobj)) {
+        return NULL;
+    }
+    surf = PySurface_AsSurface(surfobj);
+    format = surf->format;
+    
+    if (!_get_array_interface(arrayobj, &cobj, &inter)) {
+        return 0;
+    }
+
+    typekind = inter->typekind;
+    for (cptr = validtypekinds; *cptr && *cptr != typekind; ++cptr) {
+	;
+    }
+    if (!*cptr) {
+	Py_DECREF(cobj);
+	PyErr_Format(PyExc_ValueError, "unsupported array type '%c'",
+		     typekind);
+	return NULL;
+    }
+
+    if (!(inter->nd == 2 || (inter->nd == 3 && inter->shape[2] == 3)))
+        return RAISE(PyExc_ValueError, "must be a valid 2d or 3d array\n");
+
+    if (surf->format->BytesPerPixel <= 0 || surf->format->BytesPerPixel > 4)
+        return RAISE(PyExc_ValueError, "unsupport bit depth for surface");
+
+    stridex = inter->strides[0];
+    stridey = inter->strides[1];
+    if (inter->nd == 3) {
+        stridez = inter->strides[2];
+        stridez2 = stridez*2;
+    }
+    sizex = inter->shape[0];
+    sizey = inter->shape[1];
+    Rloss = format->Rloss; Gloss = format->Gloss; Bloss = format->Bloss;
+    Rshift = format->Rshift; Gshift = format->Gshift; Bshift = format->Bshift;
+
+    if (sizex != surf->w || sizey != surf->h) {
+        Py_DECREF(cobj);
+        return RAISE(PyExc_ValueError, "array must match surface dimensions");
+    }
+    if (!PySurface_LockBy(surfobj, (PyObject *) arrayobj)) {
+        Py_DECREF(cobj);
+        return NULL;
+    }
+    
+    array_data = inter->data;
+
+    switch (surf->format->BytesPerPixel) {
+    case 1:
+        if (inter->nd == 2) {
+            switch (inter->itemsize) {
+            case sizeof (Uint8):
+                COPYMACRO_2D(Uint8, Uint8);
+                break;
+            case sizeof (Uint16):
+                COPYMACRO_2D(Uint8, Uint16);
+                break;
+            case sizeof (Uint32):
+                COPYMACRO_2D(Uint8, Uint32);
+                break;
+            case sizeof (Uint64):
+                COPYMACRO_2D(Uint8, Uint64);
+                break;
+            default:
+                Py_DECREF(cobj);
+                if (!PySurface_UnlockBy(surfobj, (PyObject *) arrayobj)) {
+                    return NULL;
+                }
+                return RAISE(PyExc_ValueError,
+                             "unsupported datatype for array\n");
+            }
+        }
+        else {
+            Py_DECREF(cobj);
+            if (!PySurface_UnlockBy(surfobj, (PyObject *) arrayobj)) {
+                return NULL;
+            }
+            return RAISE(PyExc_ValueError,
+                         "unsupported datatype for array\n");
+        }
+        break;
+    case 2:
+        if (inter->nd == 2) {
+            switch (inter->itemsize) {
+            case sizeof (Uint16):
+                COPYMACRO_2D(Uint16, Uint16);
+                break;
+            case sizeof (Uint32):
+                COPYMACRO_2D(Uint16, Uint32);
+                break;
+            case sizeof (Uint64):
+                COPYMACRO_2D(Uint16, Uint64);
+                break;
+            default:
+                Py_DECREF(cobj);
+                if (!PySurface_UnlockBy(surfobj, (PyObject *) arrayobj)) {
+                    return NULL;
+                }
+                return RAISE(PyExc_ValueError,
+                             "unsupported datatype for array\n");
+            }
+        }
+        else {
+	    Uint16 alpha = 0;
+	    if (format->Amask) {
+		alpha = 255 >> format->Aloss << format->Ashift;
+	    }
+            switch (inter->itemsize) {
+            case sizeof (Uint8):
+                COPYMACRO_3D(Uint16, Uint8);
+                break;
+            case sizeof (Uint16):
+                COPYMACRO_3D(Uint16, Uint16);
+                break;
+            case sizeof (Uint32):
+                COPYMACRO_3D(Uint16, Uint32);
+                break;
+            case sizeof (Uint64):
+                COPYMACRO_3D(Uint16, Uint64);
+                break;
+            default:
+                Py_DECREF(cobj);
+                if (!PySurface_UnlockBy(surfobj, (PyObject *) arrayobj)) {
+                    return NULL;
+                }
+                return RAISE(PyExc_ValueError,
+                             "unsupported datatype for array\n");
+            }
+        }
+        break;
+    case 3:
+        /* Assumption: The rgb components of a 24 bit pixel are in
+           separate bytes.
+	*/
+        if (inter->nd == 2) {
+            switch (inter->itemsize) {
+            case sizeof (Uint32):
+                COPYMACRO_2D_24(Uint32);
+                break;
+            case sizeof (Uint64):
+                COPYMACRO_2D_24(Uint64);
+                break;
+            default:
+                Py_DECREF(cobj);
+                if (!PySurface_UnlockBy(surfobj, (PyObject *) arrayobj)) {
+                    return NULL;
+                }
+                return RAISE(PyExc_ValueError,
+                             "unsupported datatype for array\n");
+            }
+        }
+        else {
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+	    size_t stridez_0 = (format->Rmask & 0x0000ff ? 0        : 
+			        format->Gmask & 0x0000ff ? stridez  :
+                                                           stridez2   );
+	    size_t stridez_1 = (format->Rmask & 0x00ff00 ? 0        :
+			        format->Gmask & 0x00ff00 ? stridez  :
+                                                           stridez2   );
+	    size_t stridez_2 = (format->Rmask & 0xff0000 ? 0        :
+			        format->Gmask & 0xff0000 ? stridez  :
+                                                           stridez2   );
+#else
+	    size_t stridez_2 = (format->Rmask & 0x0000ff ? 0        : 
+			        format->Gmask & 0x0000ff ? stridez  :
+                                                           stridez2   );
+	    size_t stridez_1 = (format->Rmask & 0x00ff00 ? 0        :
+			        format->Gmask & 0x00ff00 ? stridez  :
+                                                           stridez2   );
+	    size_t stridez_0 = (format->Rmask & 0xff0000 ? 0        :
+			        format->Gmask & 0xff0000 ? stridez  :
+                                                           stridez2   );
+#endif
+            switch (inter->itemsize) {
+            case sizeof (Uint8):
+                COPYMACRO_3D_24(Uint8);
+                break;
+            case sizeof (Uint16):
+                COPYMACRO_3D_24(Uint16);
+                break;
+            case sizeof (Uint32):
+                COPYMACRO_3D_24(Uint32);
+                break;
+            case sizeof (Uint64):
+                COPYMACRO_3D_24(Uint64);
+                break;
+            default:
+                Py_DECREF(cobj);
+                if (!PySurface_UnlockBy(surfobj, (PyObject *) arrayobj)) {
+                    return NULL;
+                }
+                return RAISE(PyExc_ValueError,
+                             "unsupported datatype for array\n");
+            }
+        }
+        break;
+    case 4:
+        if (inter->nd == 2) {
+            switch (inter->itemsize) {
+            case sizeof (Uint32):
+                COPYMACRO_2D(Uint32, Uint32);
+                break;
+            case sizeof (Uint64):
+                COPYMACRO_2D(Uint32, Uint64);
+                break;
+            default:
+                Py_DECREF(cobj);
+                if (!PySurface_UnlockBy(surfobj, (PyObject *) arrayobj)) {
+                    return NULL;
+                }
+                return RAISE(PyExc_ValueError,
+                            "unsupported datatype for array\n");
+            }
+        }
+        else {
+	    Uint32 alpha = 0;
+	    if (format->Amask) {
+		alpha = 255 >> format->Aloss << format->Ashift;
+	    }
+            switch (inter->itemsize) {
+            case sizeof (Uint8):
+                COPYMACRO_3D(Uint32, Uint8);
+                break;
+            case sizeof (Uint16):
+                COPYMACRO_3D(Uint32, Uint16);
+                break;
+            case sizeof (Uint32):
+                COPYMACRO_3D(Uint32, Uint32);
+                break;
+            case sizeof (Uint64):
+                COPYMACRO_3D(Uint32, Uint64);
+                break;
+            default:
+                Py_DECREF(cobj);
+                if (!PySurface_UnlockBy(surfobj, (PyObject *) arrayobj)) {
+                    return NULL;
+                }
+                return RAISE(PyExc_ValueError,
+                             "unsupported datatype for array\n");
+            }
+        }
+        break;
+    default:
+        Py_DECREF(cobj);
+        if (!PySurface_UnlockBy(surfobj, (PyObject *) arrayobj)) {
+            return NULL;
+        }
+        return RAISE(PyExc_RuntimeError, "unsupported bit depth for image");
+    }
+    
+    Py_DECREF(cobj);
+    if (!PySurface_UnlockBy(surfobj, (PyObject *) arrayobj)) {
+        return NULL;
+    }
+    Py_RETURN_NONE;
+}
+
+static PyMethodDef surfarray_builtins[] =
+{
+    { "blit_array", blit_array, METH_VARARGS, DOC_PYGAMESURFARRAYBLITARRAY },
+    { NULL, NULL, 0, NULL}
+};
+
+PYGAME_EXPORT
+void init_arraysurfarray(void)
+{
+    PyObject *module;
+    
+    module = Py_InitModule3("_arraysurfarray", surfarray_builtins,
+                            DOC_PYGAMESURFARRAY);
+    import_pygame_base();
+    import_pygame_surface();
+}

File src/pgarrinter.h

+/* array structure interface version 3 declarations */
+
+const int PAI_CONTIGUOUS = 0x01;
+const int PAI_FORTRAN = 0x02;
+const int PAI_ALIGNED = 0x100;
+const int PAI_NOTSWAPPED = 0x200;
+const int PAI_WRITEABLE = 0x400;
+
+typedef struct {
+    int two;              /* contains the integer 2 -- simple sanity check */
+    int nd;               /* number of dimensions */
+    char typekind;        /* kind in array -- character code of typestr */
+    int itemsize;         /* size of each element */
+    int flags;            /* flags indicating how the data should be */
+                          /* interpreted */
+    Py_intptr_t *shape;   /* A length-nd array of shape information */
+    Py_intptr_t *strides; /* A length-nd array of stride information */
+    void *data;           /* A pointer to the first element of the array */
+    PyObject *descr;      /* NULL or a data-description */
+} PyArrayInterface;

File test/surfarray_test.py

 else:
     from test.test_utils import test_not_implemented, unittest
 import pygame
+from pygame.locals import *
+
+# Needed for 8 bits-per-pixel color palette surface tests
+pygame.init()
+if pygame.surfarray.get_arraytype() == 'numpy':
+    from numpy import \
+         uint8, uint16, uint32, uint64, zeros, float64
+else:
+    from Numeric import \
+         UInt8 as uint8, UInt16 as uint16, UInt32 as uint32, zeros, \
+         Float64 as float64
 
 class SurfarrayModuleTest (unittest.TestCase):
+
     def test_import(self):
         'does it import'
         import pygame.surfarray
 
+    pixels2d = {8: True, 16: True, 24: False, 32: True}
+    pixels3d = {8: False, 16: False, 24: True, 32: True}
+    array2d = {8: True, 16: True, 24: True, 32: True}
+    array3d = {8: False, 16: False, 24: True, 32: True}
+
+    test_palette = [(0, 0, 0, 255),
+                    (10, 30, 60, 255),
+                    (25, 75, 100, 255),
+                    (100, 150, 200, 255),
+                    (0, 100, 200, 255)]
+    surf_size = (10, 12)
+    test_points = [((0, 0), 1), ((4, 5), 1), ((9, 0), 2),
+                   ((5, 5), 2), ((0, 11), 3), ((4, 6), 3),
+                   ((9, 11), 4), ((5, 6), 4)]
+
+    def _make_surface(self, bitsize, srcalpha=False):
+        flags = 0
+        if srcalpha:
+            flags |= SRCALPHA
+        surf = pygame.Surface(self.surf_size, flags, bitsize)
+        if bitsize == 8:
+            surf.set_palette([c[:3] for c in self.test_palette])
+        return surf
+
+    def _fill_surface(self, surf):
+        surf.fill(self.test_palette[1], (0, 0, 5, 6))
+        surf.fill(self.test_palette[2], (5, 0, 5, 6))
+        surf.fill(self.test_palette[3], (0, 6, 5, 6))
+        surf.fill(self.test_palette[4], (5, 6, 5, 6))
+
+    def _make_src_surface(self, bitsize, srcalpha=False):
+        surf = self._make_surface(bitsize, srcalpha)
+        self._fill_surface(surf)
+        return surf
+
+    def _assert_surface(self, surf, palette=None, msg=""):
+        if palette is None:
+            palette = self.test_palette
+        if surf.get_bitsize() == 16:
+            palette = [surf.unmap_rgb(surf.map_rgb(c)) for c in palette]
+        for posn, i in self.test_points:
+            self.failUnlessEqual(surf.get_at(posn), palette[i],
+                                 "%s != %s: flags: %i, bpp: %i, posn: %s%s" %
+                                 (surf.get_at(posn),
+                                  palette[i], surf.get_flags(),
+                                  surf.get_bitsize(), posn, msg))
+
+    def _make_array3d(self, dtype):
+        return zeros((self.surf_size[0], self.surf_size[1], 3), dtype)
+
+    def _fill_array3d(self, arr):
+        palette = self.test_palette
+        arr[:5,:6] = palette[1][:3]
+        arr[5:,:6] = palette[2][:3]
+        arr[:5,6:] = palette[3][:3]
+        arr[5:,6:] = palette[4][:3]
+
+    def _make_src_array3d(self, dtype):
+        arr = self._make_array3d(dtype)
+        self._fill_array3d(arr)
+        return arr
+
+    def _make_array2d(self, dtype):
+        return zeros(self.surf_size, dtype)
+
     def todo_test_array2d(self):
 
         # __doc__ (as of 2008-08-02) for pygame.surfarray.array2d:
 
         self.fail() 
 
-    def todo_test_blit_array(self):
+    def test_blit_array(self):
+        # bug 24 at http://pygame.motherhamster.org/bugzilla/
+        if 'numpy' in pygame.surfarray.get_arraytypes():
+            prev = pygame.surfarray.get_arraytype()
+            # This would raise exception:
+            #  File "[...]\pygame\_numpysurfarray.py", line 381, in blit_array
+            #    (array[:,:,1::3] >> losses[1] << shifts[1]) | \
+            # TypeError: unsupported operand type(s) for >>: 'float' and 'int'
+            pygame.surfarray.use_arraytype('numpy')
+            s = pygame.Surface((10,10), 0, 24)
+            a = pygame.surfarray.array3d(s)
+            pygame.surfarray.blit_array(s, a)
+            prev = pygame.surfarray.use_arraytype(prev)
 
-        # __doc__ (as of 2008-08-02) for pygame.surfarray.blit_array:
+        # target surfaces
+        targets = [self._make_surface(8),
+                   self._make_surface(16),
+                   self._make_surface(16, srcalpha=True),
+                   self._make_surface(24),
+                   self._make_surface(32),
+                   self._make_surface(32, srcalpha=True),
+                   ]
+        
+        # source arrays
+        arrays3d = []
+        dtypes = [(8, uint8), (16, uint16), (32, uint32)]
+        try:
+            dtypes.append((64, uint64))
+        except NameError:
+            pass
+        arrays3d = [(self._make_src_array3d(dtype), None)
+                    for __, dtype in dtypes]
+        for bitsize in [8, 16, 24, 32]:
+            palette = None
+            if bitsize == 16:
+                s = pygame.Surface((1,1), 0, 16)
+                palette = [s.unmap_rgb(s.map_rgb(c))
+                           for c in self.test_palette]
+            if self.pixels3d[bitsize]:
+                surf = self._make_src_surface(bitsize)
+                arr = pygame.surfarray.pixels3d(surf)
+                arrays3d.append((arr, palette))
+            if self.array3d[bitsize]:
+                surf = self._make_src_surface(bitsize)
+                arr = pygame.surfarray.array3d(surf)
+                arrays3d.append((arr, palette))
+                for sz, dtype in dtypes:
+                    arrays3d.append((arr.astype(dtype), palette))
 
-          # pygame.surfarray.blit_array (Surface, array): return None
-          # 
-          # Blit directly from a array values.
-          # 
-          # Directly copy values from an array into a Surface. This is faster
-          # than converting the array into a Surface and blitting. The array
-          # must be the same dimensions as the Surface and will completely
-          # replace all pixel values.
-          # 
-          # This function will temporarily lock the Surface as the new values
-          # are copied.
-          # 
-          # Directly copy values from an array into a Surface. This is faster
-          # than converting the array into a Surface and blitting. The array
-          # must be the same dimensions as the Surface and will completely
-          # replace all pixel values.
-          # 
-          # This function will temporarily lock the Surface as the new values are copied. 
+        # tests on arrays
+        def do_blit(surf, arr):
+            pygame.surfarray.blit_array(surf, arr)
 
-        self.fail() 
+        for surf in targets:
+            bitsize = surf.get_bitsize()
+            for arr, palette in arrays3d:
+                surf.fill((0, 0, 0, 0))
+                if bitsize == 8:
+                    self.failUnlessRaises(ValueError, do_blit, surf, arr)
+                else:
+                    pygame.surfarray.blit_array(surf, arr)
+                    self._assert_surface(surf, palette)
 
+            if self.pixels2d[bitsize]:
+                surf.fill((0, 0, 0, 0))
+                s = self._make_src_surface(bitsize, surf.get_flags())
+                arr = pygame.surfarray.pixels2d(s)
+                pygame.surfarray.blit_array(surf, arr)
+                self._assert_surface(surf)
+
+            if self.array2d[bitsize]:
+                s = self._make_src_surface(bitsize, surf.get_flags())
+                arr = pygame.surfarray.array2d(s)
+                for sz, dtype in dtypes:
+                    surf.fill((0, 0, 0, 0))
+                    if sz >= bitsize:
+                        pygame.surfarray.blit_array(surf, arr.astype(dtype))
+                        self._assert_surface(surf)
+                    else:
+                        self.failUnlessRaises(ValueError, do_blit,
+                                              surf, self._make_array2d(dtype))
+
+        # Check alpha for 2D arrays
+        surf = self._make_surface(16, srcalpha=True)
+        arr = zeros(surf.get_size(), uint16)
+        arr[...] = surf.map_rgb((0, 128, 255, 64))
+        color = surf.unmap_rgb(arr[0, 0])
+        pygame.surfarray.blit_array(surf, arr)
+        self.failUnlessEqual(surf.get_at((5, 5)), color)
+
+        surf = self._make_surface(32, srcalpha=True)
+        arr = zeros(surf.get_size(), uint32)
+        color = (0, 111, 255, 63)
+        arr[...] = surf.map_rgb(color)
+        pygame.surfarray.blit_array(surf, arr)
+        self.failUnlessEqual(surf.get_at((5, 5)), color)
+        
+        # Invalid arrays
+        surf = pygame.Surface((1,1), 0, 32)
+        t = 'abcd'
+        self.failUnlessRaises(ValueError, do_blit, surf, t)
+
+        surf_size = self.surf_size
+        surf = pygame.Surface(surf_size, 0, 32)
+        arr = zeros([surf_size[0], surf_size[1] + 1, 3], uint32)
+        self.failUnlessRaises(ValueError, do_blit, surf, arr)
+        arr = zeros([surf_size[0] + 1, surf_size[1], 3], uint32)
+        self.failUnlessRaises(ValueError, do_blit, surf, arr)
+
+        surf = pygame.Surface((1, 4), 0, 32)
+        arr = zeros((4,), uint32)
+        self.failUnlessRaises(ValueError, do_blit, surf, arr)
+        arr.shape = (1, 1, 1, 4)
+        self.failUnlessRaises(ValueError, do_blit, surf, arr)
+
+        arr = zeros((10, 10), float64)
+        surf = pygame.Surface((10, 10), 0, 32)
+        self.failUnlessRaises(ValueError, do_blit, surf, arr)
+        
     def todo_test_get_arraytype(self):
 
         # __doc__ (as of 2008-08-02) for pygame.surfarray.get_arraytype: