Commits

illume committed 13856f8

[BUG] Fixed up pygame.mask.from_surface, got color key, and perpixel alpha
modes reversed.

Also added better test to the mask test.
Also it wasn't testing pygame.mask.from_surface at all!

Added pygame.sprite.collide_mask to join the mask_* collision functions.
Started adding test for pygame.sprite.spritecollide.

  • Participants
  • Parent commits a787fef

Comments (0)

Files changed (4)

     # Optimizations...
     def __contains__(self, sprite): return (self.__sprite is sprite)
 
+
+
+
+
+# some different collision detection functions that could be used.
+
 def collide_rect( left, right ):
     return left.rect.colliderect( right.rect )
 
     distanceSquared = xDistance ** 2 + yDistance ** 2
     return distanceSquared < get_radius_squared( left ) + get_radius_squared( right )
 
+
+def collide_mask( left, right ):
+
+    offset_x = left.rect[0] - right.rect[0]
+    offset_y = left.rect[1] - right.rect[1]
+    # See if the two masks at the offset are overlapping.
+    overlap = left.mask.overlap(right.mask, (offset_x, offset_y))
+    return overlap
+    
+
+
+
+
 def spritecollide(sprite, group, dokill, collided = None):
     """pygame.sprite.spritecollide(sprite, group, dokill) -> list
        collision detection between sprite and group
 
 static PyObject* mask_get_size(PyObject* self, PyObject* args)
 {
-	bitmask_t *mask = PyMask_AsBitmap(self);
+    bitmask_t *mask = PyMask_AsBitmap(self);
 
-	if(!PyArg_ParseTuple(args, ""))
-		return NULL;
+    if(!PyArg_ParseTuple(args, ""))
+        return NULL;
 
-	return Py_BuildValue("(ii)", mask->w, mask->h);
+    return Py_BuildValue("(ii)", mask->w, mask->h);
 }
 
 static PyObject* mask_get_at(PyObject* self, PyObject* args)
 {
-	bitmask_t *mask = PyMask_AsBitmap(self);
-        int x, y, val;
+    bitmask_t *mask = PyMask_AsBitmap(self);
+    int x, y, val;
 
-	if(!PyArg_ParseTuple(args, "(ii)", &x, &y))
-		return NULL;
-	if (x >= 0 && x < mask->w && y >= 0 && y < mask->h)
-	  val = bitmask_getbit(mask, x, y);
-	else
-	  {
-            PyErr_Format(PyExc_IndexError, "%d, %d is out of bounds", x, y);
-	    return NULL;
-	  }
+    if(!PyArg_ParseTuple(args, "(ii)", &x, &y))
+            return NULL;
+    if (x >= 0 && x < mask->w && y >= 0 && y < mask->h) {
+        val = bitmask_getbit(mask, x, y);
+    } else {
+        PyErr_Format(PyExc_IndexError, "%d, %d is out of bounds", x, y);
+        return NULL;
+    }
 
-        return PyInt_FromLong(val);
+    return PyInt_FromLong(val);
 }
 
 static PyObject* mask_set_at(PyObject* self, PyObject* args)
 {
-	bitmask_t *mask = PyMask_AsBitmap(self);
-        int x, y, value = 1;
+    bitmask_t *mask = PyMask_AsBitmap(self);
+    int x, y, value = 1;
 
-	if(!PyArg_ParseTuple(args, "(ii)|i", &x, &y, &value))
-		return NULL;
-	if (x >= 0 && x < mask->w && y >= 0 && y < mask->h)
-	  {
-	    if (value)
-	      bitmask_setbit(mask, x, y);
-	    else
-	      bitmask_clearbit(mask, x, y);
-	  }
-	else
-	  {
-            PyErr_Format(PyExc_IndexError, "%d, %d is out of bounds", x, y);
-	    return NULL;
-	  }
-	Py_INCREF(Py_None);
-        return Py_None;
+    if(!PyArg_ParseTuple(args, "(ii)|i", &x, &y, &value))
+            return NULL;
+    if (x >= 0 && x < mask->w && y >= 0 && y < mask->h) {
+        if (value) {
+            bitmask_setbit(mask, x, y);
+        } else {
+          bitmask_clearbit(mask, x, y);
+        }
+    } else {
+        PyErr_Format(PyExc_IndexError, "%d, %d is out of bounds", x, y);
+        return NULL;
+    }
+    Py_INCREF(Py_None);
+    return Py_None;
 }
 
 static PyObject* mask_overlap(PyObject* self, PyObject* args)
 {
-	bitmask_t *mask = PyMask_AsBitmap(self);
-        bitmask_t *othermask;
-        PyObject *maskobj;
-        int x, y, val;
-	int xp,yp;
+    bitmask_t *mask = PyMask_AsBitmap(self);
+    bitmask_t *othermask;
+    PyObject *maskobj;
+    int x, y, val;
+    int xp,yp;
 
-	if(!PyArg_ParseTuple(args, "O!(ii)", &PyMask_Type, &maskobj, &x, &y))
-		return NULL;
-	othermask = PyMask_AsBitmap(maskobj);
+    if(!PyArg_ParseTuple(args, "O!(ii)", &PyMask_Type, &maskobj, &x, &y))
+            return NULL;
+    othermask = PyMask_AsBitmap(maskobj);
 
-	val = bitmask_overlap_pos(mask, othermask, x, y, &xp, &yp);
-	if (val)
-	  return Py_BuildValue("(ii)", xp,yp);
-	else
-	  {
-	    Py_INCREF(Py_None);
-	    return Py_None;	  
-	  }
+    val = bitmask_overlap_pos(mask, othermask, x, y, &xp, &yp);
+    if (val) {
+      return Py_BuildValue("(ii)", xp,yp);
+    } else {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
 }
 
 
 static PyObject* mask_overlap_area(PyObject* self, PyObject* args)
 {
-	bitmask_t *mask = PyMask_AsBitmap(self);
-        bitmask_t *othermask;
-        PyObject *maskobj;
-        int x, y, val;
+    bitmask_t *mask = PyMask_AsBitmap(self);
+    bitmask_t *othermask;
+    PyObject *maskobj;
+    int x, y, val;
 
-	if(!PyArg_ParseTuple(args, "O!(ii)", &PyMask_Type, &maskobj, &x, &y))
-		return NULL;
-	othermask = PyMask_AsBitmap(maskobj);
+    if(!PyArg_ParseTuple(args, "O!(ii)", &PyMask_Type, &maskobj, &x, &y)) {
+        return NULL;
+    }
+    othermask = PyMask_AsBitmap(maskobj);
 
-	val = bitmask_overlap_area(mask, othermask, x, y);
-        return PyInt_FromLong(val);
+    val = bitmask_overlap_area(mask, othermask, x, y);
+    return PyInt_FromLong(val);
 }
 
 /*
 
 static PyObject* mask_from_surface(PyObject* self, PyObject* args)
 {
-	bitmask_t *mask;
-	SDL_Surface* surf;
+    bitmask_t *mask;
+    SDL_Surface* surf;
 
     PyObject* surfobj;
     PyMaskObject *maskobj;
      *   surface, threshold
      */
 
-    if (!PyArg_ParseTuple (args, "O!|i", &PySurface_Type, &surfobj, &threshold))
+    if (!PyArg_ParseTuple (args, "O!|i", &PySurface_Type, &surfobj, &threshold)) {
         return NULL;
+    }
 
-	surf = PySurface_AsSurface(surfobj);
+    surf = PySurface_AsSurface(surfobj);
 
     /* lock the surface, release the GIL. */
     PySurface_Lock (surfobj);
     mask = bitmask_create(surf->w, surf->h);
 
 
-	if(!mask) {
+    if(!mask) {
         //Py_END_ALLOW_THREADS;
         return NULL; /*RAISE(PyExc_Error, "cannot create bitmask");*/
     }
 
 
     /* TODO: this is the slow, but easy to code way.  Could make the loop 
-     *         just increment a pointer depending on the format.  
+     *         just increment a pointer depending on the format & duff unroll.
      *         It's faster than in python anyhow.
      */
     pixels = (Uint8 *) surf->pixels;
         for(x=0; x < surf->w; x++) {
             switch (format->BytesPerPixel)
             {
-            case 1:
-                color = (Uint32)*((Uint8 *) pixels + y * surf->pitch + x);
-                break;
-            case 2:
-                color = (Uint32)*((Uint16 *) (pixels + y * surf->pitch) + x);
-                break;
-            case 3:
-                pix = ((Uint8 *) (pixels + y * surf->pitch) + x * 3);
-        #if SDL_BYTEORDER == SDL_LIL_ENDIAN
-                color = (pix[0]) + (pix[1] << 8) + (pix[2] << 16);
-        #else
-                color = (pix[2]) + (pix[1] << 8) + (pix[0] << 16);
-        #endif
-                break;
-            default:                  /* case 4: */
-                color = *((Uint32 *) (pixels + y * surf->pitch) + x);
-                break;
+                case 1:
+                    color = (Uint32)*((Uint8 *) pixels + y * surf->pitch + x);
+                    break;
+                case 2:
+                    color = (Uint32)*((Uint16 *) (pixels + y * surf->pitch) + x);
+                    break;
+                case 3:
+                    pix = ((Uint8 *) (pixels + y * surf->pitch) + x * 3);
+                #if SDL_BYTEORDER == SDL_LIL_ENDIAN
+                    color = (pix[0]) + (pix[1] << 8) + (pix[2] << 16);
+                #else
+                    color = (pix[2]) + (pix[1] << 8) + (pix[0] << 16);
+                #endif
+                    break;
+                default:                  /* case 4: */
+                    color = *((Uint32 *) (pixels + y * surf->pitch) + x);
+                    break;
             }
 
 
-            if (surf->flags & SDL_SRCCOLORKEY) {
+            if (!(surf->flags & SDL_SRCCOLORKEY)) {
 
                 SDL_GetRGBA (color, format, &r, &g, &b, &a);
 
         maskobj->mask = mask;
 
 
-	return (PyObject*)maskobj;
+    return (PyObject*)maskobj;
 }
 
 
 
 
 
-	bitmask_t *mask = PyMask_AsBitmap(self);
+    bitmask_t *mask = PyMask_AsBitmap(self);
 
 
     ret = NULL;
     num_bounding_boxes = 0;
 
 
-	if(!PyArg_ParseTuple(args, ""))
-		return NULL;
+    if(!PyArg_ParseTuple(args, ""))
+        return NULL;
 
     ret = PyList_New (0);
     if (!ret)
     free(regions);
 
 
-	return ret;
+    return ret;
 }
 
 
 
 static PyMethodDef maskobj_builtins[] =
 {
-	{ "get_size", mask_get_size, METH_VARARGS, DOC_PYGAMEMASKGETSIZE},
-	{ "get_at", mask_get_at, METH_VARARGS, DOC_PYGAMEMASKGETAT },
-	{ "set_at", mask_set_at, METH_VARARGS, DOC_PYGAMEMASKSETAT },
-	{ "overlap", mask_overlap, METH_VARARGS, DOC_PYGAMEMASKOVERLAP },
-	{ "overlap_area", mask_overlap_area, METH_VARARGS,
-          DOC_PYGAMEMASKOVERLAPAREA },
-	{ "get_bounding_rects", mask_get_bounding_rects, METH_VARARGS,
-          DOC_PYGAMEMASKGETBOUNDINGRECTS },
+    { "get_size", mask_get_size, METH_VARARGS, DOC_PYGAMEMASKGETSIZE},
+    { "get_at", mask_get_at, METH_VARARGS, DOC_PYGAMEMASKGETAT },
+    { "set_at", mask_set_at, METH_VARARGS, DOC_PYGAMEMASKSETAT },
+    { "overlap", mask_overlap, METH_VARARGS, DOC_PYGAMEMASKOVERLAP },
+    { "overlap_area", mask_overlap_area, METH_VARARGS,
+      DOC_PYGAMEMASKOVERLAPAREA },
+    { "get_bounding_rects", mask_get_bounding_rects, METH_VARARGS,
+      DOC_PYGAMEMASKGETBOUNDINGRECTS },
 
-	{ NULL, NULL, 0, NULL }
+    { NULL, NULL, 0, NULL }
 };
 
 
 
 static void mask_dealloc(PyObject* self)
 {
-	bitmask_t *mask = PyMask_AsBitmap(self);
-	bitmask_free(mask);
-	PyObject_DEL(self);
+    bitmask_t *mask = PyMask_AsBitmap(self);
+    bitmask_free(mask);
+    PyObject_DEL(self);
 }
 
 
 static PyObject* mask_getattr(PyObject* self, char* attrname)
 {
-	return Py_FindMethod(maskobj_builtins, self, attrname);
+    return Py_FindMethod(maskobj_builtins, self, attrname);
 }
 
 
 static PyTypeObject PyMask_Type = 
 {
- 	PyObject_HEAD_INIT(NULL)
-        0,
-        "Mask",
-        sizeof(PyMaskObject),
-        0,
-        mask_dealloc,
-        0,
-        mask_getattr,
-        0,
-        0,
-        0,
-        0,
-        NULL,
-        0, 
-        (hashfunc)NULL,
-        (ternaryfunc)NULL,
-        (reprfunc)NULL,
-        0L,0L,0L,0L,
-        DOC_PYGAMEMASK /* Documentation string */
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "Mask",
+    sizeof(PyMaskObject),
+    0,
+    mask_dealloc,
+    0,
+    mask_getattr,
+    0,
+    0,
+    0,
+    0,
+    NULL,
+    0, 
+    (hashfunc)NULL,
+    (ternaryfunc)NULL,
+    (reprfunc)NULL,
+    0L,0L,0L,0L,
+    DOC_PYGAMEMASK /* Documentation string */
 };
 
 
 
 static PyObject* Mask(PyObject* self, PyObject* args)
 {
-	bitmask_t *mask;
-	int w,h;
-        PyMaskObject *maskobj;
-	if(!PyArg_ParseTuple(args, "(ii)", &w, &h))
-		return NULL;
-        mask = bitmask_create(w,h);
+    bitmask_t *mask;
+    int w,h;
+    PyMaskObject *maskobj;
+    if(!PyArg_ParseTuple(args, "(ii)", &w, &h))
+        return NULL;
+    mask = bitmask_create(w,h);
 
-	if(!mask)
-	  return NULL; /*RAISE(PyExc_Error, "cannot create bitmask");*/
+    if(!mask)
+      return NULL; /*RAISE(PyExc_Error, "cannot create bitmask");*/
         
         /*create the new python object from mask*/        
-	maskobj = PyObject_New(PyMaskObject, &PyMask_Type);
-        if(maskobj)
-        	maskobj->mask = mask;
-	return (PyObject*)maskobj;
+    maskobj = PyObject_New(PyMaskObject, &PyMask_Type);
+    if(maskobj)
+        maskobj->mask = mask;
+    return (PyObject*)maskobj;
 }
 
 
 
 static PyMethodDef mask_builtins[] =
 {
-	{ "Mask", Mask, METH_VARARGS, DOC_PYGAMEMASK },
-	{ "from_surface", mask_from_surface, METH_VARARGS,
-          DOC_PYGAMEMASKPYGAMEMASKFROMSURFACE},
-	{ NULL, NULL, 0, NULL }
+    { "Mask", Mask, METH_VARARGS, DOC_PYGAMEMASK },
+    { "from_surface", mask_from_surface, METH_VARARGS,
+      DOC_PYGAMEMASKPYGAMEMASKFROMSURFACE},
+    { NULL, NULL, 0, NULL }
 };
 
 void initmask(void)

test/mask_test.py

         """  Does the mask.from_surface() work correctly?
         """
 
-        mask_from_surface = maskFromSurface
+        mask_from_surface = pygame.mask.from_surface
 
         surf = pygame.Surface((70,70), SRCALPHA, 32)
 
         self.assertEqual(amask.get_at((2,0)), 0)
         self.assertEqual(amask.get_at((3,0)), 1)
 
+        surf.fill((255,255,255,0))
+        amask = mask_from_surface(surf)
+        self.assertEqual(amask.get_at((0,0)), 0)
+
+        #TODO: test a color key surface.
+
 
 
     def test_get_bounding_rects(self):

test/sprite_test.py

 
 
 import unittest
+import pygame
 from pygame import sprite
 
 class SpriteTest( unittest.TestCase ):
 
 
 
+    def test_spritecollide(self):
+
+        ag = sprite.AbstractGroup()
+        ag2 = sprite.AbstractGroup()
+        s1 = sprite.Sprite(ag)
+        s2 = sprite.Sprite(ag2)
+        s1.image = pygame.Surface((10,10), pygame.SRCALPHA, 32)
+        s2.image = pygame.Surface((10,10), pygame.SRCALPHA, 32)
+        
+        s1.rect = s1.image.get_rect()
+        s2.rect = s2.image.get_rect()
+        
+        r = sprite.spritecollide(s1, ag2, dokill = False, collided = None)
+        self.assertTrue(r)
+        
+        
+        # need to pass a function.
+        self.assertRaises(TypeError, sprite.spritecollide, s1, ag2, dokill = False, collided = 1)
+
+        self.assertTrue( sprite.spritecollide( s1, ag2, dokill = False, collided = sprite.collide_rect) )
+
+        # if there are no mask attributes.
+        self.assertRaises( AttributeError, sprite.spritecollide, s1, ag2, dokill = False, collided = sprite.collide_mask)
+        
+        # make some sprites that are fully transparent, so they won't collide.
+        s1.image.fill((255,255,255,0))
+        s2.image.fill((255,255,255,0))
+        
+        s1.mask = pygame.mask.from_surface(s1.image, 255)
+        s2.mask = pygame.mask.from_surface(s2.image, 255)
+        
+        self.assertFalse( sprite.spritecollide( s1, ag2, dokill = False, collided = sprite.collide_mask) )
+        
+        # make some fully opaque sprites that will collide with masks.
+        s1.image.fill((255,255,255,255))
+        s2.image.fill((255,255,255,255))
+        
+        s1.mask = pygame.mask.from_surface(s1.image)
+        s2.mask = pygame.mask.from_surface(s2.image)
+        
+        self.assertTrue( sprite.spritecollide( s1, ag2, dokill = False, collided = sprite.collide_mask) )
+        
+        
+
+
+
 
 import pygame
+
 import unittest
 import pygame.sprite as FastRenderGroup
 from pygame.sprite import LayeredUpdates as LayeredRenderGroup