Commits

Lenard Lindstrom committed eaa04c2

Fix some bufferproxy/view backwards compatibility issues

Now passes unit tests for array buffer related changes. Breaks nothing else.
Once again support a 2D BufferProxy view, Surface.get_buffer('2'), on
a 24 bit surface, while surfarray.pixels2d still raises a ValueError.
Fix a problem where the 'l'/'L' Py_buffer format types were not recognized.

  • Participants
  • Parent commits db7a252

Comments (0)

Files changed (4)

File lib/_numpysurfarray.py

     if hasattr(numpy, type_name):
         numpy_floats.append(getattr(numpy, type_name))
 
+# Pixel sizes corresponding to NumPy supported integer sizes, and therefore
+# permissible for 2D reference arrays.
+_pixel2d_bitdepths = set([8, 16, 32])
+
 
 def blit_array (surface, array):
     """pygame.surfarray.blit_array(Surface, array): return None
     the array (see the Surface.lock - lock the Surface memory for pixel
     access method).
     """
+    if (surface.get_bitsize() not in _pixel2d_bitdepths):
+        raise ValueError("unsupport bit depth for 2D reference array")
     try:
         return numpy_array(surface.get_buffer('2'), copy=False)
     except (ValueError, TypeError):
 _as_arrayinter_typekind (Py_buffer* view)
 {
     char type = view->format ? view->format[0] : 'B';
-    char typekind;
+    char typekind = 'V';
 
     switch (type) {
 
     case 'Q':
         typekind = 'u';
         break;
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+    case '8':
+    case '9':
+        typekind = (view->format[1] == 'x') ? 'u' : 'V';
+        break;
     case 'f':
     case 'd':
         typekind = 'f';
         break;
     default:
         /* Unknown type */
-        typekind = 's';
+        typekind = 'V';
     }
     return typekind;
 }
     PyArrayInterface* inter_p = 0;
     ViewInternals* internal_p;
     size_t sz;
-    char fchar;
+    char *fchar_p;
     Py_ssize_t i;
 
     pg_view_p->release_buffer = _release_buffer_generic;
     view_p->len = 0;
 
 #if PG_ENABLE_NEWBUF
-    char *fchar_p;
 
     if (PyObject_CheckBuffer (obj)) {
         if (PyObject_GetBuffer (obj, view_p, flags)) {
         case 'H':
         case 'i':
         case 'I':
+        case 'l':
+        case 'L':
         case 'q':
         case 'Q':
         case 'f':
         case '7':
         case '8':
         case '9':
-            /* A record: will raise exception later */
+            ++fchar_p;
+            if (*fchar_p == 'x') {
+                ++fchar_p;
+            }
             break;
         default:
             PgBuffer_Release (pg_view_p);
             PyErr_NoMemory ();
             return -1;
         }
+        fchar_p = internal_p->format;
         switch (inter_p->typekind) {
 
         case 'i':
+            *fchar_p = (inter_p->flags & PAI_NOTSWAPPED ?
+                        PAI_MY_ENDIAN : PAI_OTHER_ENDIAN);
+            ++fchar_p;
             switch (inter_p->itemsize) {
 
             case 1:
-                fchar = 'b';
+                *fchar_p = 'b';
                 break; 
             case 2:
-                fchar = 'h';
+                *fchar_p = 'h';
                 break;
             case 4:
-                fchar = 'i';
+                *fchar_p = 'i';
                 break;
             case 8:
-                fchar = 'q';
+                *fchar_p = 'q';
                 break;
             default:
                 PyErr_Format (PyExc_ValueError,
             }
             break;
         case 'u':
+            *fchar_p = (inter_p->flags & PAI_NOTSWAPPED ?
+                        PAI_MY_ENDIAN : PAI_OTHER_ENDIAN);
+            ++fchar_p;
             switch (inter_p->itemsize) {
 
             case 1:
-                fchar = 'B';
+                *fchar_p = 'B';
                 break; 
             case 2:
-                fchar = 'H';
+                *fchar_p = 'H';
                 break;
             case 4:
-                fchar = 'I';
+                *fchar_p = 'I';
                 break;
             case 8:
-                fchar = 'Q';
+                *fchar_p = 'Q';
                 break;
             default:
                 PyErr_Format (PyExc_ValueError,
             }
             break;
         case 'f':
+            *fchar_p = (inter_p->flags & PAI_NOTSWAPPED ?
+                        PAI_MY_ENDIAN : PAI_OTHER_ENDIAN);
+            ++fchar_p;
             switch (inter_p->itemsize) {
 
             case 4:
-                fchar = 'f';
+                *fchar_p = 'f';
                 break;
             case 8:
-                fchar = 'd';
+                *fchar_p = 'd';
                 break;
             default:
                 PyErr_Format (PyExc_ValueError,
                 return -1;
             }
             break;
+        case 'V':
+            if (inter_p->itemsize > 9) {
+                PyErr_Format (PyExc_ValueError,
+                              "Unsupported void size %d",
+                              (int)inter_p->itemsize);
+                Py_DECREF (cobj);
+                return -1;
+            }
+            switch (inter_p->itemsize) {
+
+            case 1:
+                *fchar_p = '1';
+                break;
+            case 2:
+                *fchar_p = '2';
+                break;
+            case 3:
+                *fchar_p = '3';
+                break;
+            case 4:
+                *fchar_p = '4';
+                break;
+            case 5:
+                *fchar_p = '5';
+                break;
+            case 6:
+                *fchar_p = '6';
+                break;
+            case 7:
+                *fchar_p = '7';
+                break;
+            case 8:
+                *fchar_p = '8';
+                break;
+            case 9:
+                *fchar_p = '9';
+                break;
+            default:
+                PyErr_Format (PyExc_ValueError,
+                              "Unsupported void size %d",
+                              (int)inter_p->itemsize);
+                Py_DECREF (cobj);
+                return -1;
+            }
+            ++fchar_p;
+            *fchar_p = 'x';
+            break;
         default:
             PyErr_Format (PyExc_ValueError,
                           "Unsupported value type '%c'",
             Py_DECREF (cobj);
             return -1;
         }
+        ++fchar_p;
+        *fchar_p = '\0';
         view_p->internal = internal_p;
         view_p->format = internal_p->format;
         view_p->shape = internal_p->imem;
         view_p->ndim = (Py_ssize_t)inter_p->nd;
         view_p->itemsize = (Py_ssize_t)inter_p->itemsize;
         view_p->readonly = inter_p->flags & PAI_WRITEABLE ? 0 : 1;
-        view_p->format[0] = (inter_p->flags & PAI_NOTSWAPPED ?
-                           PAI_MY_ENDIAN : PAI_OTHER_ENDIAN);
-        view_p->format[1] = fchar;
-        view_p->format[2] = '\0';
         for (i = 0; i < view_p->ndim; ++i) {
             view_p->shape[i] = (Py_ssize_t)inter_p->shape[i];
             view_p->strides[i] = (Py_ssize_t)inter_p->strides[i];
      * as well as significant unicode changes in Python 3.3, this will
      * only handle integer types. Must call _shape_arg_convert first.
      */
-    char type;
-    int is_signed;
+    char *fchar_p;
     int is_swapped;
-    char byteorder = '\0';
-    int itemsize;
+    int itemsize = 0;
     PyObject* s;
     const char* typestr;
 
         return -1;
     }
     typestr = Bytes_AsString (s);
+    fchar_p = view->format;
     switch (typestr[0]) {
 
     case PAI_MY_ENDIAN:
         is_swapped = 0;
         break;
     default:
-        PyErr_Format( PyExc_ValueError,
-                      "unknown byteorder character %c in typestr",
-                      typestr[0]);
+        PyErr_Format (PyExc_ValueError, "unsupported typestr %s", typestr);
         Py_DECREF (s);
         return -1;
     }
     switch (typestr[1]) {
 
     case 'i':
+    case 'u':
         switch (typestr[2]) {
 
         case '1':
-            type = 'b';
+            *fchar_p = '=';
+            ++fchar_p;
+            *fchar_p = 'B';
             itemsize = 1;
             break;
         case '2':
-            byteorder = is_swapped ? PAI_OTHER_ENDIAN : '=';
-            type = 'h';
+            *fchar_p = is_swapped ? PAI_OTHER_ENDIAN : '=';
+            ++fchar_p;
+            *fchar_p = 'H';
             itemsize = 2;
             break;
+        case '3':
+            *fchar_p = '3';
+            ++fchar_p;
+            *fchar_p = 'x';
+            itemsize = 3;
+            break;
         case '4':
-            byteorder = is_swapped ? PAI_OTHER_ENDIAN : '=';
-            type = 'i';
+            *fchar_p = is_swapped ? PAI_OTHER_ENDIAN : '=';
+            ++fchar_p;
+            *fchar_p = 'I';
+            itemsize = 4;
+            break;
+        case '5':
+            *fchar_p = '5';
+            ++fchar_p;
+            *fchar_p = 'x';
+            itemsize = 5;
+            break;
+        case '6':
+            *fchar_p = '6';
+            ++fchar_p;
+            *fchar_p = 'x';
+            itemsize = 6;
+            break;
+        case '7':
+            *fchar_p = '7';
+            ++fchar_p;
+            *fchar_p = 'x';
+            itemsize = 7;
+            break;
+        case '8':
+            *fchar_p = is_swapped ? PAI_OTHER_ENDIAN : '=';
+            ++fchar_p;
+            *fchar_p = 'Q';
+            itemsize = 8;
+            break;
+        case '9':
+            *fchar_p = '9';
+            ++fchar_p;
+            *fchar_p = 'x';
+            itemsize = 9;
+            break;
+        default:
+            PyErr_Format (PyExc_ValueError, "unsupported typestr %s", typestr);
+            Py_DECREF (s);
+            return -1;
+        }
+        if (typestr[1] == 'i') {
+            /* This leaves 'x' uneffected. */
+            *fchar_p = tolower(*fchar_p);
+        }
+        break;
+    case 'f':
+        *fchar_p = is_swapped ? PAI_OTHER_ENDIAN : '=';
+        ++fchar_p;
+        switch (typestr[2]) {
+
+        case '4':
+            *fchar_p = 'f';
             itemsize = 4;
             break;
         case '8':
-            byteorder = is_swapped ? PAI_OTHER_ENDIAN : '=';
-            type = 'q';
+            *fchar_p = 'd';
             itemsize = 8;
             break;
         default:
-            PyErr_Format (PyExc_ValueError,
-                          "unsupported size %c in typestr",
-                          typestr[2]);
+            PyErr_Format (PyExc_ValueError, "unsupported typestr %s", typestr);
             Py_DECREF (s);
             return -1;
         }
         break;
-    case 'u':
+    case 'V':
         switch (typestr[2]) {
 
         case '1':
-            type = 'B';
+            *fchar_p = '1';
             itemsize = 1;
             break;
         case '2':
-            byteorder = is_swapped ? PAI_OTHER_ENDIAN : '=';
-            type = 'H';
+            *fchar_p = '2';
             itemsize = 2;
             break;
+        case '3':
+            *fchar_p = '3';
+            itemsize = 3;
+            break;
         case '4':
-            byteorder = is_swapped ? PAI_OTHER_ENDIAN : '=';
-            type = 'H';
+            *fchar_p = '4';
             itemsize = 4;
             break;
+        case '5':
+            *fchar_p = '5';
+            itemsize = 5;
+            break;
+        case '6':
+            *fchar_p = '6';
+            itemsize = 6;
+            break;
+        case '7':
+            *fchar_p = '7';
+            itemsize = 7;
+            break;
         case '8':
-            byteorder = is_swapped ? PAI_OTHER_ENDIAN : '=';
-            type = 'Q';
+            *fchar_p = '8';
             itemsize = 8;
             break;
+        case '9':
+            *fchar_p = '9';
+            itemsize = 9;
+            break;
         default:
-            PyErr_Format (PyExc_ValueError,
-                          "unsupported size %c in typestr",
-                          typestr[2]);
+            PyErr_Format (PyExc_ValueError, "unsupported typestr %s", typestr);
             Py_DECREF (s);
             return -1;
         }
-        break;
-    case 'f':
-        switch (typestr[2]) {
-
-        case '4':
-            byteorder = is_swapped ? PAI_OTHER_ENDIAN : '=';
-            type = 'f';
-            itemsize = 4;
-            break;
-        case '8':
-            byteorder = is_swapped ? PAI_OTHER_ENDIAN : '=';
-            type = 'd';
-            itemsize = 8;
-            break;
-        default:
-            PyErr_Format (PyExc_ValueError,
-                          "unsupported size %c in typestr",
-                          typestr[2]);
-            Py_DECREF (s);
-            return -1;
-        }
+        ++fchar_p;
+        *fchar_p = 'x';
         break;
     default:
-        PyErr_Format (PyExc_ValueError,
-                      "unsupported typekind %c in typestr",
-                      typestr[1]);
+        PyErr_Format (PyExc_ValueError, "unsupported typestr %s", typestr);
         Py_DECREF (s);
         return -1;
     }
-    if (byteorder != '\0') {
-        view->format[0] = byteorder;
-        view->format[1] = type;
-        view->format[2] = '\0';
-    }
-    else {
-        view->format[0] = type;
-        view->format[1] = '\0';
-    }
+    ++fchar_p;
+    *fchar_p = '\0';
     view->itemsize = itemsize;
     return 0;
 }

File src/surface.c

 /* To avoid problems with non-const Py_buffer format field */
 static char FormatUint8[] = "B";
 static char FormatUint16[] = "=H";
+static char FormatUint24[] = "3x";
 static char FormatUint32[] = "=I";
 
 typedef struct pg_bufferinternal_s {
                              "Surface data is not contiguous");
             return 0;
         }
-        if (format->BytesPerPixel == 3) {
-            return _raise_get_view_ndim_error (format->BytesPerPixel * 8,
-                                               view_kind);
-        }
         get_buffer = _get_buffer_1D;
         break;
     case VIEWKIND_2D:
-        if (format->BytesPerPixel == 3) {
-            return _raise_get_view_ndim_error (format->BytesPerPixel * 8,
-                                               view_kind);
-        }
         get_buffer = _get_buffer_2D;
         break;
     case VIEWKIND_3D:
     case 2:
         format = FormatUint16;
         break;
+    case 3:
+        format = FormatUint24;
+        break;
     case 4:
         format = FormatUint32;
         break;
     case 2:
         format = FormatUint16;
         break;
+    case 3:
+        format = FormatUint24;
+        break;
     case 4:
         format = FormatUint32;
         break;

File test/surface_test.py

         s = pygame.Surface((5, 7), 0, 24)
         self.assertRaises(Error, s.get_buffer, '0')
         self.assertRaises(Error, s.get_buffer, '1')
-        self.assertRaises(Error, s.get_buffer, '2')
+        v = s.get_buffer('2')
+        self.assertTrue(isinstance(v, BufferProxy))
         v = s.get_buffer('3')
         self.assert_(isinstance(v, BufferProxy))
 
         v = s.get_buffer('0')
         self.assert_(isinstance(v, BufferProxy))
         self.assertEqual(v.length, length)
+        v = s.get_buffer('1')
+        self.assertTrue(isinstance(v, BufferProxy))
+        self.assertEqual(v.length, length)
 
         s = pygame.Surface((5, 7), 0, 32)
         length = s.get_bytesize() * s.get_width() * s.get_height()