Commits

Lenard Lindstrom committed f804beb

Surface.get_view('2') now returns a view for 24 bit surfaces.
pixelcopy.map_surface now accepts 24 bit integers in target array.
pixelcopy.map_surface properly handles 1 dimensional indices in source array.
Color type exports array struct interface; can be used as map_surface source array.

  • Participants
  • Parent commits 2a2b5ba

Comments (0)

Files changed (9)

lib/_numpysurfarray.py

     the array (see the Surface.lock - lock the Surface memory for pixel
     access method).
     """
-    return numpy_array(surface.get_view('2'), copy=False)
+    try:
+        return numpy_array(surface.get_view('2'), copy=False)
+    except ValueError:
+        raise ValueError("bit depth %i unsupported for 2D reference array" %
+                         (surface.get_bitsize(),))
 
 def array3d(surface):
     """pygame.numpyarray.array3d(Surface): return array
     (see the Surface.lock - lock the Surface memory for pixel access
     method).
     """
-    w, h = surface.get_size() 
+    w, h = surface.get_size()
     array = numpy.empty((w, h, 3), numpy.uint8)
     surface_to_array(array, surface)
     return array
 #include "doc/color_doc.h"
 #include "pygame.h"
 #include "pgcompat.h"
+#include "pgarrinter.h"
 #include <ctype.h>
 
 typedef struct
 static PyObject* _color_normalize (PyColor *color);
 static PyObject* _color_correct_gamma (PyColor *color, PyObject *args);
 static PyObject* _color_set_length (PyColor *color, PyObject *args);
+#if PY3
+static void _color_freeview (PyObject *c);
+#else
+#define _color_freeview PyMem_Free
+#endif
 
 /* Getters/setters */
 static PyObject* _color_get_r (PyColor *color, void *closure);
 static int _color_set_i1i2i3 (PyColor *color, PyObject *value, void *closure);
 static PyObject* _color_get_cmy (PyColor *color, void *closure);
 static int _color_set_cmy (PyColor *color, PyObject *value, void *closure);
+static PyObject* _color_get_arraystruct(PyColor *color, void *closure);
 
 /* Number protocol methods */
 static PyObject* _color_add (PyColor *color1, PyColor *color2);
       DOC_COLORI1I2I3, NULL },
     { "cmy", (getter) _color_get_cmy, (setter) _color_set_cmy, DOC_COLORCMY,
       NULL },
+    { "__array_struct__", (getter) _color_get_arraystruct, NULL,
+      "array structure interface, read only", NULL },
     { NULL, NULL, NULL, NULL, NULL }
 };
 
     return 0;
 }
 
+static PyObject*
+_color_get_arraystruct(PyColor *color, void *closure)
+{
+    typedef struct {
+        PyArrayInterface inter;
+        Py_intptr_t shape[1];
+        Uint8 data[4];
+    } _color_view_t;
+    _color_view_t *view = PyMem_New(_color_view_t, 1);
+    PyObject *cobj;
+
+    if (!view) {
+        return PyErr_NoMemory();
+    }
+    view->shape[0] = color->len;
+    view->data[0] = color->r;
+    view->data[1] = color->g;
+    view->data[2] = color->b;
+    view->data[3] = color->a;
+    view->inter.two = 2;
+    view->inter.nd = 1;
+    view->inter.typekind = 'u';
+    view->inter.itemsize = 1;
+    view->inter.flags = (PAI_CONTIGUOUS | PAI_FORTRAN |
+                         PAI_ALIGNED | PAI_NOTSWAPPED);
+    view->inter.shape = view->shape;
+    view->inter.strides = &(view->inter.itemsize);
+    view->inter.data = view->data;
+    
+    cobj = PyCapsule_New(view, NULL, _color_freeview);
+    if (!cobj) {
+        PyMem_Free(view);
+        return 0;
+    }
+    return cobj;
+}
+
+#if PY3
+static void
+_color_freeview (PyObject *c)
+{
+    PyMem_Free(PyCapsule_GetPointer (c, NULL));
+}
+#endif
+
 /* Number protocol methods */
 
 /**
         int len= 4;
         Py_ssize_t start, stop, step, slicelength;
 
-        if (PySlice_GetIndicesEx((PySliceObject*)item, len, &start, &stop, &step, &slicelength) < 0)
+        if (Slice_GET_INDICES_EX(item, len,
+                                 &start, &stop, &step, &slicelength) < 0) {
             return NULL;
+        }
 
         if (slicelength <= 0) {
             return PyTuple_New(0);
 static PyObject*
 _color_richcompare(PyObject *o1, PyObject *o2, int opid)
 {
-    Uint8 rgba1[4], rgba2[4];
+    typedef union {
+        Uint32 pixel;
+        Uint8 bytes[4];
+    } _rgba_t;
+    _rgba_t rgba1, rgba2;
 
-    switch (_coerce_obj (o1, rgba1))
+    switch (_coerce_obj (o1, rgba1.bytes))
     {
     case -1:
 	return 0;
     default:
         break;
     }
-    switch (_coerce_obj (o2, rgba2))
+    switch (_coerce_obj (o2, rgba2.bytes))
     {
     case -1:
 	return 0;
     switch (opid)
     {
     case Py_EQ:
-        return PyBool_FromLong (*((Uint32 *) rgba1) == *((Uint32 *) rgba2));
+        return PyBool_FromLong (rgba1.pixel == rgba2.pixel);
     case Py_NE:
-        return PyBool_FromLong (*((Uint32 *) rgba1) != *((Uint32 *) rgba2));
+        return PyBool_FromLong (rgba1.pixel != rgba2.pixel);
     default:
         break;
     }
 #define UNICODE_DEF_FS_CODEC Py_FileSystemDefaultEncoding
 #define UNICODE_DEF_FS_ERROR "strict"
 
-#endif /* #if PY_VERSION_HEX >= 0x03000000 */
+#endif /* #if PY_MAJOR_VERSION >= 3 */
 
 #define PY2 (!PY3)
 
 #define Py_TPFLAGS_HAVE_CLASS 0
 #endif
 
+#if PY_VERSION_HEX >= 0x03020000
+#define Slice_GET_INDICES_EX(slice, length, start, stop, step, slicelength) \
+    PySlice_GetIndicesEx(slice, length, start, stop, step, slicelength)
+#else
+#define Slice_GET_INDICES_EX(slice, length, start, stop, step, slicelength) \
+    PySlice_GetIndicesEx((PySliceObject *)(slice), length, \
+                         start, stop, step, slicelength)
+#endif
+
 #endif /* #if !defined(PGCOMPAT_H) */
     }
     dim_diff = ndim - src_ndim + 1;
     for (dim = dim_diff; dim != ndim; ++dim) {
-        if (src_inter->shape[dim - dim_diff] != shape[dim]) {
+        if (src_inter->shape[dim - dim_diff] == 1) {
+            src_strides[dim] = 0;
+        }
+        else if (src_inter->shape[dim - dim_diff] == shape[dim]) {
+            src_strides[dim] = src_inter->strides[dim - dim_diff];
+        }
+        else {
             PyErr_Format(PyExc_ValueError,
                          "size mismatch between dimension %d of source and"
                          " dimension %d of target",
                          dim - dim_diff, dim);
             goto fail;
         }
-        src_strides[dim] = src_inter->strides[dim - dim_diff];
     }
     for (dim = 0; dim != ndim - 1; ++dim) {
         tar_advances[dim] =
     switch (view_kind) {
 
     case VIEWKIND_2D:
-        if (pixelsize == 3) {
-            Py_DECREF(view);
-            return _raise_get_view_ndim_error(pixelsize * 8, view_kind);
-        }
         ndim = 2;
         itemsize = pixelsize;
         if (strides[1] == shape[0] * pixelsize) {

test/color_test.py

         c1_frompickle = pickle.loads(pickle_string)
         self.assertEqual(c1,c1_frompickle) 
 
+################################################################################
+# only available if ctypes module is also available
+
+    def test_arraystruct(self):
+        import pygame.tests.test_utils.arrinter as ai
+        import ctypes as ct
+
+        c_byte_p = ct.POINTER(ct.c_byte)
+        c = pygame.Color(5, 7, 13, 23)
+        flags = (ai.PAI_CONTIGUOUS | ai.PAI_FORTRAN |
+                 ai.PAI_ALIGNED | ai.PAI_NOTSWAPPED)
+        for i in range(1, 5):
+            c.set_length(i)
+            inter = ai.ArrayInterface(c)
+            self.assertEqual(inter.two, 2)
+            self.assertEqual(inter.nd, 1)
+            self.assertEqual(inter.typekind, 'u')
+            self.assertEqual(inter.itemsize, 1)
+            self.assertEqual(inter.flags, flags)
+            self.assertEqual(inter.shape[0], i)
+            self.assertEqual(inter.strides[0], 1)
+            data = ct.cast(inter.data, c_byte_p)
+            for j in range(i):
+                self.assertEqual(data[j], c[j])
+
+    try:
+        import ctypes
+    except ImportError:
+        del test_arraystruct
 
 
 ################################################################################

test/pixelcopy_test.py

                                   posn))
             del view
 
+    def test_map_array(self):
+        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 = pygame.Surface(self.surf_size, 0, 24,
+                                masks=[0xff, 0xff00, 0xff0000, 0])
+        source_view = source.get_view('3')  # (w, h, 3)
+        for t in targets:
+            map_array(t.get_view('2'), source_view, t)
+            for posn, i in self.test_points:
+                sc = t.map_rgb(source.get_at(posn))
+                dc = t.get_at_mapped(posn)
+                self.assertEqual(dc, sc,
+                                 "%s != %s: flags: %i"
+                                 ", bpp: %i, posn: %s" %
+                                 (dc, sc,
+                                  t.get_flags(), t.get_bitsize(),
+                                  posn))
+
+        color = pygame.Color("salmon")
+        color.set_length(3)
+        for t in targets:
+            map_array(t.get_view('2'), color, t)
+            sc = t.map_rgb(color)
+            for posn, i in self.test_points:
+                dc = t.get_at_mapped(posn)
+                self.assertEqual(dc, sc,
+                                 "%s != %s: flags: %i"
+                                 ", bpp: %i, posn: %s" %
+                                 (dc, sc,
+                                  t.get_flags(), t.get_bitsize(),
+                                  posn))
+
+        # mismatched shapes
+        w, h = source.get_size()
+        target = pygame.Surface((w, h + 1), 0, 32)
+        self.assertRaises(ValueError, map_array, target, source, target)
+        target = pygame.Surface((w - 1, h), 0, 32)
+        self.assertRaises(ValueError, map_array, target, source, target)
+
     def todo_test_array_to_surface(self):
         # target surfaces
         targets = [_make_surface(8),
                    ((5, 5), 2), ((0, 11), 3), ((4, 6), 3),
                    ((9, 11), 4), ((5, 6), 4)]
 
-    try:
-        dst_types = [numpy.uint8, numpy.uint16, numpy.uint32]
+    def __init__(self, *args, **kwds):
+        self.dst_types = [numpy.uint8, numpy.uint16, numpy.uint32]
         try:    
-            dst_types.append(numpy.uint64) 
+            self.dst_types.append(numpy.uint64) 
         except AttributeError:
             pass
-    except NameError:
-        pass
-
-    def __init__(self, *args, **kwds):
         pygame.display.init()
         try:
             unittest.TestCase.__init__(self, *args, **kwds)
         map_array(target, color, surf)
         self.assert_(alltrue(target == surf.map_rgb(color)))
 
-        # array row stripes
+        # array column stripes
         stripe = array([[2, 5, 7], [11, 19, 23], [37, 53, 101]], uint8)
-        target = zeros((4, 3), int32)
+        target = zeros((4, stripe.shape[0]), int32)
         map_array(target, stripe, surf)
         target_stripe = array([surf.map_rgb(c) for c in stripe], int32)
-        self.assert_(alltrue(target[...] == target_stripe))
+        self.assert_(alltrue(target == target_stripe))
+
+        # array row stripes
+        stripe = array([[[2, 5, 7]],
+                        [[11, 19, 24]],
+                        [[10, 20, 30]],
+                        [[37, 53, 101]]], uint8)
+        target = zeros((stripe.shape[0], 3), int32)
+        map_array(target, stripe, surf)
+        target_stripe = array([[surf.map_rgb(c)] for c in stripe[:,0]], int32)
+        self.assert_(alltrue(target == target_stripe))
+
+        # mismatched shape
+        w = 4
+        h = 5
+        source = zeros((w, h, 3), uint8)
+        target = zeros((w,), int32)
+        self.assertRaises(ValueError, map_array, target, source, surf)
+        source = zeros((12, w, h + 1), uint8)
+        self.assertRaises(ValueError, map_array, target, source, surf)
+        source = zeros((12, w - 1, 5), uint8)
+        self.assertRaises(ValueError, map_array, target, source, surf)
+
+    try:
+        numpy
+    except NameError:
+        # Ensure no methods requiring numpy are run when
+        # pixelcopy_test is '__main__'.
+        del __init__
+        del test_surface_to_array_2d
+        del test_surface_to_array_3d
+        del test_map_array
 
 if __name__ == '__main__':
     unittest.main()

test/surface_test.py

         self.assertRaises(Error, s.get_view, '3')
 
         s = pygame.Surface((5, 7), 0, 24)
-        self.assertRaises(Error, s.get_view, '2')
+        v = s.get_view('2')
+        self.assert_(isinstance(v, View))
         v = s.get_view('3')
         self.assert_(isinstance(v, View))
 
         s = pygame.Surface((5, 7), 0, 16)
         v = s.get_view()
         self.assert_(isinstance(v, View))
-        s = pygame.Surface((5, 7), 0, 24)
-        self.assertRaises(Error, s.get_view)
 
         # Check keyword arguments.
         s = pygame.Surface((5, 7), 0, 24)

test/surfarray_test.py

             self._assert_surface(surf)
 
         # Error checks
-        def do_pixels2d(surf):
-            pygame.surfarray.pixels2d(surf)
-
         self.failUnlessRaises(ValueError,
-                              do_pixels2d,
+                              pygame.surfarray.pixels2d,
                               self._make_surface(24))
 
     def test_pixels3d(self):