1 /*
2   Copyright (C) 2002-2007 Ulf Ekstrom except for the bitcount function.
3   This wrapper code was originally written by Danny van Bruggen(?) for
4   the SCAM library, it was then converted by Ulf Ekstrom to wrap the
5   bitmask library, a spinoff from SCAM.
6 
7   This library is free software; you can redistribute it and/or
8   modify it under the terms of the GNU Library General Public
9   License as published by the Free Software Foundation; either
10   version 2 of the License, or (at your option) any later version.
11 
12   This library is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15   Library General Public License for more details.
16 
17   You should have received a copy of the GNU Library General Public
18   License along with this library; if not, write to the Free
19   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 
21 */
22 
23 /* a couple of print debugging helpers */
24 /*
25 #define CALLLOG2(x,y) fprintf(stderr, (x), (y));
26 #define CALLLOG(x) fprintf(stderr, (x));
27 */
28 
29 #define PYGAMEAPI_MASK_INTERNAL 1
30 #include "mask.h"
31 
32 #include "pygame.h"
33 
34 #include "pgcompat.h"
35 
36 #include "doc/mask_doc.h"
37 
38 #include "structmember.h"
39 
40 #include <math.h>
41 
42 #ifndef M_PI
43 #define M_PI 3.14159265358979323846
44 #endif
45 
46 /* Macro to create mask objects. This will call the type's tp_new and tp_init.
47  * Params:
48  *     w: width of mask
49  *     h: height of mask
50  *     f: fill, 1 is used to set all the bits (to 1) and 0 is used to clear
51  *        all the bits (to 0)
52  */
53 #define CREATE_MASK_OBJ(w, h, f)                                             \
54     (pgMaskObject *)PyObject_CallFunction((PyObject *)&pgMask_Type, "(ii)i", \
55                                           (w), (h), (f))
56 
57 /* Prototypes */
58 static PyTypeObject pgMask_Type;
59 static PG_INLINE pgMaskObject *
60 create_mask_using_bitmask(bitmask_t *bitmask);
61 static PG_INLINE pgMaskObject *
62 create_mask_using_bitmask_and_type(bitmask_t *bitmask, PyTypeObject *ob_type);
63 
64 /********** mask helper functions **********/
65 
66 /* Calculate the absolute difference between 2 Uint32s. */
67 static PG_INLINE Uint32
abs_diff_uint32(Uint32 a,Uint32 b)68 abs_diff_uint32(Uint32 a, Uint32 b)
69 {
70     return (a > b) ? a - b : b - a;
71 }
72 
73 /********** mask object methods **********/
74 
75 /* Copies the given mask. */
76 static PyObject *
mask_copy(PyObject * self,PyObject * args)77 mask_copy(PyObject *self, PyObject *args)
78 {
79     bitmask_t *new_bitmask = bitmask_copy(pgMask_AsBitmap(self));
80 
81     if (NULL == new_bitmask) {
82         return RAISE(PyExc_MemoryError, "cannot allocate memory for bitmask");
83     }
84 
85     return (PyObject *)create_mask_using_bitmask_and_type(new_bitmask,
86                                                           self->ob_type);
87 }
88 
89 /* Redirects mask.copy() to mask.__copy__(). This is done to allow
90  * subclasses that override the __copy__() method to also override the copy()
91  * method automatically. */
92 static PyObject *
mask_call_copy(PyObject * self,PyObject * args)93 mask_call_copy(PyObject *self, PyObject *args)
94 {
95     return PyObject_CallMethodObjArgs(self, Text_FromUTF8("__copy__"), args);
96 }
97 
98 static PyObject *
mask_get_size(PyObject * self,PyObject * args)99 mask_get_size(PyObject *self, PyObject *args)
100 {
101     bitmask_t *mask = pgMask_AsBitmap(self);
102 
103     if (!PyArg_ParseTuple(args, ""))
104         return NULL;
105 
106     return Py_BuildValue("(ii)", mask->w, mask->h);
107 }
108 
109 /* Creates a Rect object based on the given mask's size. The rect's
110  * attributes can be altered via the kwargs.
111  *
112  * Returns:
113  *     Rect object or NULL to indicate a fail
114  *
115  * Ref: src_c/surface.c surf_get_rect()
116  */
117 static PyObject *
mask_get_rect(PyObject * self,PyObject * args,PyObject * kwargs)118 mask_get_rect(PyObject *self, PyObject *args, PyObject *kwargs)
119 {
120     PyObject *rect = NULL;
121     bitmask_t *bitmask = pgMask_AsBitmap(self);
122 
123     if (0 != PyTuple_GET_SIZE(args)) {
124         return RAISE(PyExc_TypeError,
125                      "get_rect only supports keyword arguments");
126     }
127 
128     rect = pgRect_New4(0, 0, bitmask->w, bitmask->h);
129 
130     if (NULL == rect) {
131         return RAISE(PyExc_MemoryError, "cannot allocate memory for rect");
132     }
133 
134     if (NULL != kwargs) {
135         PyObject *key = NULL, *value = NULL;
136         Py_ssize_t pos = 0;
137 
138         while (PyDict_Next(kwargs, &pos, &key, &value)) {
139             if ((-1 == PyObject_SetAttr(rect, key, value))) {
140                 Py_DECREF(rect);
141                 return NULL;
142             }
143         }
144     }
145 
146     return rect;
147 }
148 
149 static PyObject *
mask_get_at(PyObject * self,PyObject * args,PyObject * kwargs)150 mask_get_at(PyObject *self, PyObject *args, PyObject *kwargs)
151 {
152     bitmask_t *mask = pgMask_AsBitmap(self);
153     int x, y, val;
154     PyObject* pos = NULL;
155     static char *keywords[] = {"pos", NULL};
156 
157     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", keywords, &pos))
158         return NULL;
159 
160     if (!pg_TwoIntsFromObj(pos, &x, &y)) {
161         return RAISE(PyExc_TypeError, "pos must be two numbers");
162     }
163 
164     if (x >= 0 && x < mask->w && y >= 0 && y < mask->h) {
165         val = bitmask_getbit(mask, x, y);
166     }
167     else {
168         PyErr_Format(PyExc_IndexError, "%d, %d is out of bounds", x, y);
169         return NULL;
170     }
171 
172     return PyInt_FromLong(val);
173 }
174 
175 static PyObject *
mask_set_at(PyObject * self,PyObject * args,PyObject * kwargs)176 mask_set_at(PyObject *self, PyObject *args, PyObject *kwargs)
177 {
178     bitmask_t *mask = pgMask_AsBitmap(self);
179     int x, y, value = 1;
180     PyObject* pos = NULL;
181     static char *keywords[] = {"pos", "value", NULL};
182 
183     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|i", keywords, &pos, &value))
184         return NULL;
185 
186     if (!pg_TwoIntsFromObj(pos, &x, &y)) {
187         return RAISE(PyExc_TypeError, "pos must be two numbers");
188     }
189 
190     if (x >= 0 && x < mask->w && y >= 0 && y < mask->h) {
191         if (value) {
192             bitmask_setbit(mask, x, y);
193         }
194         else {
195             bitmask_clearbit(mask, x, y);
196         }
197     }
198     else {
199         PyErr_Format(PyExc_IndexError, "%d, %d is out of bounds", x, y);
200         return NULL;
201     }
202     Py_INCREF(Py_None);
203     return Py_None;
204 }
205 
206 static PyObject *
mask_overlap(PyObject * self,PyObject * args,PyObject * kwargs)207 mask_overlap(PyObject *self, PyObject *args, PyObject *kwargs)
208 {
209     bitmask_t *mask = pgMask_AsBitmap(self);
210     bitmask_t *othermask;
211     PyObject *maskobj;
212     int x, y, val;
213     int xp, yp;
214     PyObject* offset = NULL;
215     static char *keywords[] = {"other", "offset", NULL};
216 
217     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O", keywords, &pgMask_Type, &maskobj, &offset))
218         return NULL;
219 
220     othermask = pgMask_AsBitmap(maskobj);
221 
222     if (!pg_TwoIntsFromObj(offset, &x, &y)) {
223         return RAISE(PyExc_TypeError, "offset must be two numbers");
224     }
225 
226     val = bitmask_overlap_pos(mask, othermask, x, y, &xp, &yp);
227     if (val) {
228         return Py_BuildValue("(ii)", xp, yp);
229     }
230     else {
231         Py_INCREF(Py_None);
232         return Py_None;
233     }
234 }
235 
236 static PyObject *
mask_overlap_area(PyObject * self,PyObject * args,PyObject * kwargs)237 mask_overlap_area(PyObject *self, PyObject *args, PyObject *kwargs)
238 {
239     bitmask_t *mask = pgMask_AsBitmap(self);
240     bitmask_t *othermask;
241     PyObject *maskobj;
242     int x, y, val;
243     PyObject* offset = NULL;
244     static char *keywords[] = {"other", "offset", NULL};
245 
246     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O", keywords, &pgMask_Type, &maskobj, &offset)) {
247         return NULL;
248     }
249 
250     othermask = pgMask_AsBitmap(maskobj);
251 
252     if (!pg_TwoIntsFromObj(offset, &x, &y)) {
253         return RAISE(PyExc_TypeError, "offset must be two numbers");
254     }
255 
256     val = bitmask_overlap_area(mask, othermask, x, y);
257     return PyInt_FromLong(val);
258 }
259 
260 static PyObject *
mask_overlap_mask(PyObject * self,PyObject * args,PyObject * kwargs)261 mask_overlap_mask(PyObject *self, PyObject *args, PyObject *kwargs)
262 {
263     int x, y;
264     bitmask_t *bitmask = pgMask_AsBitmap(self);
265     PyObject *maskobj = NULL;
266     pgMaskObject *output_maskobj = NULL;
267     PyObject* offset = NULL;
268     static char *keywords[] = {"other", "offset", NULL};
269 
270     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O", keywords, &pgMask_Type, &maskobj, &offset)) {
271         return NULL; /* Exception already set. */
272     }
273 
274     output_maskobj = CREATE_MASK_OBJ(bitmask->w, bitmask->h, 0);
275 
276     if (!pg_TwoIntsFromObj(offset, &x, &y)) {
277         return RAISE(PyExc_TypeError, "offset must be two numbers");
278     }
279 
280     if (NULL == output_maskobj) {
281         return NULL; /* Exception already set. */
282     }
283 
284     bitmask_overlap_mask(bitmask, pgMask_AsBitmap(maskobj),
285                          output_maskobj->mask, x, y);
286 
287     return (PyObject *)output_maskobj;
288 }
289 
290 static PyObject *
mask_fill(PyObject * self,PyObject * args)291 mask_fill(PyObject *self, PyObject *args)
292 {
293     bitmask_t *mask = pgMask_AsBitmap(self);
294 
295     bitmask_fill(mask);
296 
297     Py_RETURN_NONE;
298 }
299 
300 static PyObject *
mask_clear(PyObject * self,PyObject * args)301 mask_clear(PyObject *self, PyObject *args)
302 {
303     bitmask_t *mask = pgMask_AsBitmap(self);
304 
305     bitmask_clear(mask);
306 
307     Py_RETURN_NONE;
308 }
309 
310 static PyObject *
mask_invert(PyObject * self,PyObject * args)311 mask_invert(PyObject *self, PyObject *args)
312 {
313     bitmask_t *mask = pgMask_AsBitmap(self);
314 
315     bitmask_invert(mask);
316 
317     Py_RETURN_NONE;
318 }
319 
320 static PyObject *
mask_scale(PyObject * self,PyObject * args,PyObject * kwargs)321 mask_scale(PyObject *self, PyObject *args, PyObject *kwargs)
322 {
323     int x, y;
324     bitmask_t *bitmask = NULL;
325     PyObject* scale = NULL;
326     static char *keywords[] = {"scale", NULL};
327 
328     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", keywords, &scale)) {
329         return NULL; /* Exception already set. */
330     }
331 
332     if (!pg_TwoIntsFromObj(scale, &x, &y)) {
333         return RAISE(PyExc_TypeError, "scale must be two numbers");
334     }
335 
336     if (x < 0 || y < 0) {
337         return RAISE(PyExc_ValueError, "cannot scale mask to negative size");
338     }
339 
340     bitmask = bitmask_scale(pgMask_AsBitmap(self), x, y);
341 
342     if (NULL == bitmask) {
343         return RAISE(PyExc_MemoryError, "cannot allocate memory for bitmask");
344     }
345 
346     return (PyObject *)create_mask_using_bitmask(bitmask);
347 }
348 
349 static PyObject *
mask_draw(PyObject * self,PyObject * args,PyObject * kwargs)350 mask_draw(PyObject *self, PyObject *args, PyObject *kwargs)
351 {
352     bitmask_t *mask = pgMask_AsBitmap(self);
353     bitmask_t *othermask;
354     PyObject *maskobj;
355     int x, y;
356     PyObject* offset = NULL;
357     static char *keywords[] = {"other", "offset", NULL};
358 
359     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O", keywords, &pgMask_Type, &maskobj, &offset)) {
360         return NULL;
361     }
362 
363     if (!pg_TwoIntsFromObj(offset, &x, &y)) {
364         return RAISE(PyExc_TypeError, "offset must be two numbers");
365     }
366 
367     othermask = pgMask_AsBitmap(maskobj);
368 
369     bitmask_draw(mask, othermask, x, y);
370 
371     Py_RETURN_NONE;
372 }
373 
374 static PyObject *
mask_erase(PyObject * self,PyObject * args,PyObject * kwargs)375 mask_erase(PyObject *self, PyObject *args, PyObject *kwargs)
376 {
377     bitmask_t *mask = pgMask_AsBitmap(self);
378     bitmask_t *othermask;
379     PyObject *maskobj;
380     int x, y;
381     PyObject* offset = NULL;
382     static char *keywords[] = {"other", "offset", NULL};
383 
384     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O", keywords, &pgMask_Type, &maskobj, &offset)) {
385         return NULL;
386     }
387 
388     if (!pg_TwoIntsFromObj(offset, &x, &y)) {
389         return RAISE(PyExc_TypeError, "offset must be two numbers");
390     }
391 
392     othermask = pgMask_AsBitmap(maskobj);
393 
394     bitmask_erase(mask, othermask, x, y);
395 
396     Py_RETURN_NONE;
397 }
398 
399 static PyObject *
mask_count(PyObject * self,PyObject * args)400 mask_count(PyObject *self, PyObject *args)
401 {
402     bitmask_t *m = pgMask_AsBitmap(self);
403 
404     return PyInt_FromLong(bitmask_count(m));
405 }
406 
407 static PyObject *
mask_centroid(PyObject * self,PyObject * args)408 mask_centroid(PyObject *self, PyObject *args)
409 {
410     bitmask_t *mask = pgMask_AsBitmap(self);
411     int x, y;
412     long int m10, m01, m00;
413     PyObject *xobj, *yobj;
414 
415     m10 = m01 = m00 = 0;
416 
417     for (x = 0; x < mask->w; x++) {
418         for (y = 0; y < mask->h; y++) {
419             if (bitmask_getbit(mask, x, y)) {
420                 m10 += x;
421                 m01 += y;
422                 m00++;
423             }
424         }
425     }
426 
427     if (m00) {
428         xobj = PyInt_FromLong(m10 / m00);
429         yobj = PyInt_FromLong(m01 / m00);
430     }
431     else {
432         xobj = PyInt_FromLong(0);
433         yobj = PyInt_FromLong(0);
434     }
435 
436     return Py_BuildValue("(NN)", xobj, yobj);
437 }
438 
439 static PyObject *
mask_angle(PyObject * self,PyObject * args)440 mask_angle(PyObject *self, PyObject *args)
441 {
442     bitmask_t *mask = pgMask_AsBitmap(self);
443     int x, y;
444     long int m10, m01, m00, m20, m02, m11;
445 
446     m10 = m01 = m00 = m20 = m02 = m11 = 0;
447 
448     for (x = 0; x < mask->w; x++) {
449         for (y = 0; y < mask->h; y++) {
450             if (bitmask_getbit(mask, x, y)) {
451                 m10 += x;
452                 m20 += (long)x * x;
453                 m11 += (long)x * y;
454                 m02 += (long)y * y;
455                 m01 += y;
456                 m00++;
457             }
458         }
459     }
460 
461     if (m00) {
462         int xc = m10 / m00;
463         int yc = m01 / m00;
464         double theta = -90.0 *
465                 atan2(2 * (m11 / m00 - (long)xc * yc),
466                       (m20 / m00 - (long)xc * xc) - (m02 / m00 - (long)yc * yc)) /
467                 M_PI;
468         return PyFloat_FromDouble(theta);
469     }
470     else {
471         return PyFloat_FromDouble(0);
472     }
473 }
474 
475 static PyObject *
mask_outline(PyObject * self,PyObject * args,PyObject * kwargs)476 mask_outline(PyObject *self, PyObject *args, PyObject *kwargs)
477 {
478     bitmask_t *c = pgMask_AsBitmap(self);
479     bitmask_t *m = NULL;
480     PyObject *plist = NULL;
481     PyObject *value = NULL;
482     int x, y, firstx, firsty, secx, secy, currx, curry, nextx, nexty, n;
483     int e, every = 1;
484     int a[] = {1, 1, 0, -1, -1, -1,  0,  1, 1, 1, 0, -1, -1, -1};
485     int b[] = {0, 1, 1,  1,  0, -1, -1, -1, 0, 1, 1,  1,  0, -1};
486     static char *keywords[] = {"every", NULL};
487 
488     n = firstx = firsty = secx = x = 0;
489 
490     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", keywords, &every)) {
491         return NULL;
492     }
493 
494     plist = PyList_New(0);
495     if (!plist) {
496         return RAISE(PyExc_MemoryError,
497                      "outline cannot allocate memory for list");
498     }
499 
500     if (!c->w || !c->h) {
501         return plist;
502     }
503 
504     /* Copying to a larger mask to avoid border checking. */
505     m = bitmask_create(c->w + 2, c->h + 2);
506     if (!m) {
507         Py_DECREF(plist);
508         return RAISE(PyExc_MemoryError,
509                      "outline cannot allocate memory for mask");
510     }
511 
512     bitmask_draw(m, c, 1, 1);
513 
514     /* find the first set pixel in the mask */
515     for (y = 1; y < m->h - 1; y++) {
516         for (x = 1; x < m->w - 1; x++) {
517             if (bitmask_getbit(m, x, y)) {
518                 firstx = x;
519                 firsty = y;
520                 value = Py_BuildValue("(ii)", x - 1, y - 1);
521 
522                 if (NULL == value) {
523                     Py_DECREF(plist);
524                     bitmask_free(m);
525 
526                     return NULL; /* Exception already set. */
527                 }
528 
529                 if (0 != PyList_Append(plist, value)) {
530                     Py_DECREF(value);
531                     Py_DECREF(plist);
532                     bitmask_free(m);
533 
534                     return NULL; /* Exception already set. */
535                 }
536 
537                 Py_DECREF(value);
538                 break;
539             }
540         }
541         if (bitmask_getbit(m, x, y))
542             break;
543     }
544 
545     /* covers the mask having zero pixels set or only the final pixel */
546     if ((x == m->w - 1) && (y == m->h - 1)) {
547         bitmask_free(m);
548         return plist;
549     }
550 
551     e = every;
552 
553     /* check just the first pixel for neighbors */
554     for (n = 0; n < 8; n++) {
555         if (bitmask_getbit(m, x + a[n], y + b[n])) {
556             currx = secx = x + a[n];
557             curry = secy = y + b[n];
558             e--;
559             if (!e) {
560                 e = every;
561                 value = Py_BuildValue("(ii)", secx - 1, secy - 1);
562 
563                 if (NULL == value) {
564                     Py_DECREF(plist);
565                     bitmask_free(m);
566 
567                     return NULL; /* Exception already set. */
568                 }
569 
570                 if (0 != PyList_Append(plist, value)) {
571                     Py_DECREF(value);
572                     Py_DECREF(plist);
573                     bitmask_free(m);
574 
575                     return NULL; /* Exception already set. */
576                 }
577 
578                 Py_DECREF(value);
579             }
580             break;
581         }
582     }
583 
584     /* if there are no neighbors, return */
585     if (!secx) {
586         bitmask_free(m);
587         return plist;
588     }
589 
590     /* the outline tracing loop */
591     for (;;) {
592         /* look around the pixel, it has to have a neighbor */
593         for (n = (n + 6) & 7;; n++) {
594             if (bitmask_getbit(m, currx + a[n], curry + b[n])) {
595                 nextx = currx + a[n];
596                 nexty = curry + b[n];
597                 e--;
598                 if (!e) {
599                     e = every;
600                     if ((curry == firsty && currx == firstx) &&
601                         (secx == nextx && secy == nexty)) {
602                         break;
603                     }
604 
605                     value = Py_BuildValue("(ii)", nextx - 1, nexty - 1);
606 
607                     if (NULL == value) {
608                         Py_DECREF(plist);
609                         bitmask_free(m);
610 
611                         return NULL; /* Exception already set. */
612                     }
613 
614                     if (0 != PyList_Append(plist, value)) {
615                         Py_DECREF(value);
616                         Py_DECREF(plist);
617                         bitmask_free(m);
618 
619                         return NULL; /* Exception already set. */
620                     }
621 
622                     Py_DECREF(value);
623                 }
624                 break;
625             }
626         }
627         /* if we are back at the first pixel, and the next one will be the
628            second one we visited, we are done */
629         if ((curry == firsty && currx == firstx) &&
630             (secx == nextx && secy == nexty)) {
631             break;
632         }
633 
634         curry = nexty;
635         currx = nextx;
636     }
637 
638     bitmask_free(m);
639 
640     return plist;
641 }
642 
643 static PyObject *
mask_convolve(PyObject * aobj,PyObject * args,PyObject * kwargs)644 mask_convolve(PyObject *aobj, PyObject *args, PyObject *kwargs)
645 {
646     PyObject *bobj = NULL;
647     PyObject *oobj = Py_None;
648     bitmask_t *a = NULL, *b = NULL;
649     int xoffset = 0, yoffset = 0;
650     PyObject* offset = NULL;
651     static char *keywords[] = {"other", "output", "offset", NULL};
652 
653     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|OO", keywords, &pgMask_Type, &bobj, &oobj,
654                           &offset)) {
655         return NULL; /* Exception already set. */
656     }
657 
658     if (offset && !pg_TwoIntsFromObj(offset, &xoffset, &yoffset)) {
659         return RAISE(PyExc_TypeError, "offset must be two numbers");
660     }
661 
662     a = pgMask_AsBitmap(aobj);
663     b = pgMask_AsBitmap(bobj);
664 
665     if (oobj != Py_None) {
666         /* Use this mask for the output. */
667         Py_INCREF(oobj);
668     }
669     else {
670         pgMaskObject *maskobj = CREATE_MASK_OBJ(MAX(0, a->w + b->w - 1),
671                                                 MAX(0, a->h + b->h - 1), 0);
672 
673         if (NULL == maskobj) {
674             return NULL; /* Exception already set. */
675         }
676 
677         oobj = (PyObject *)maskobj;
678     }
679 
680     bitmask_convolve(a, b, pgMask_AsBitmap(oobj), xoffset, yoffset);
681 
682     return oobj;
683 }
684 
685 /* Gets the color of a given pixel.
686  *
687  * Params:
688  *     pixel: pixel to get the color of
689  *     bpp: bytes per pixel
690  *
691  * Returns:
692  *     pixel color
693  */
694 static PG_INLINE Uint32
get_pixel_color(Uint8 * pixel,Uint8 bpp)695 get_pixel_color(Uint8 *pixel, Uint8 bpp)
696 {
697     switch (bpp) {
698         case 1:
699             return *((Uint8 *)pixel);
700 
701         case 2:
702             return *((Uint16 *)pixel);
703 
704         case 3:
705 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
706             return (pixel[0]) + (pixel[1] << 8) + (pixel[2] << 16);
707 #else  /* SDL_BIG_ENDIAN */
708             return (pixel[2]) + (pixel[1] << 8) + (pixel[0] << 16);
709 #endif /* SDL_BIG_ENDIAN */
710 
711         default: /* case 4: */
712             return *((Uint32 *)pixel);
713     }
714 }
715 
716 /* Sets the color of a given pixel.
717  *
718  * Params:
719  *     pixel: pixel to set the color of
720  *     bpp: bytes per pixel
721  *     color: color to set
722  *
723  * Ref: src_c/draw.c set_pixel_32()
724  */
725 static void
set_pixel_color(Uint8 * pixel,Uint8 bpp,Uint32 color)726 set_pixel_color(Uint8 *pixel, Uint8 bpp, Uint32 color)
727 {
728     switch (bpp) {
729         case 1:
730             *pixel = color;
731             break;
732 
733         case 2:
734             *(Uint16 *)pixel = color;
735             break;
736 
737         case 3:
738 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
739             *(Uint16 *)pixel = color;
740             pixel[2] = color >> 16;
741 #else  /* != SDL_LIL_ENDIAN */
742             pixel[2] = color;
743             pixel[1] = color >> 8;
744             pixel[0] = color >> 16;
745 #endif /* SDL_LIL_ENDIAN */
746             break;
747 
748         default: /* case 4: */
749             *(Uint32 *)pixel = color;
750             break;
751     }
752 }
753 
754 /* For each surface pixel's alpha that is greater than the threshold,
755  * the corresponding bitmask bit is set.
756  *
757  * Params:
758  *     surf: surface
759  *     bitmask: bitmask to alter
760  *     threshold: threshold used check surface pixels (alpha) against
761  *
762  * Returns:
763  *     void
764  */
765 static void
set_from_threshold(SDL_Surface * surf,bitmask_t * bitmask,int threshold)766 set_from_threshold(SDL_Surface *surf, bitmask_t *bitmask, int threshold)
767 {
768     SDL_PixelFormat *format = surf->format;
769     Uint8 bpp = format->BytesPerPixel;
770     Uint8 *pixel = NULL;
771     Uint8 rgba[4];
772     int x, y;
773 
774     for (y = 0; y < surf->h; ++y) {
775         pixel = (Uint8 *)surf->pixels + y * surf->pitch;
776 
777         for (x = 0; x < surf->w; ++x, pixel += bpp) {
778             SDL_GetRGBA(get_pixel_color(pixel, bpp), format, rgba, rgba + 1,
779                         rgba + 2, rgba + 3);
780             if (rgba[3] > threshold) {
781                 bitmask_setbit(bitmask, x, y);
782             }
783         }
784     }
785 }
786 
787 /* For each surface pixel's color that is not equal to the colorkey, the
788  * corresponding bitmask bit is set.
789  *
790  * Params:
791  *     surf: surface
792  *     bitmask: bitmask to alter
793  *     colorkey: color used to check surface pixels against
794  *
795  * Returns:
796  *     void
797  */
798 static void
set_from_colorkey(SDL_Surface * surf,bitmask_t * bitmask,Uint32 colorkey)799 set_from_colorkey(SDL_Surface *surf, bitmask_t *bitmask, Uint32 colorkey)
800 {
801     Uint8 bpp = surf->format->BytesPerPixel;
802     Uint8 *pixel = NULL;
803     int x, y;
804 
805     for (y = 0; y < surf->h; ++y) {
806         pixel = (Uint8 *)surf->pixels + y * surf->pitch;
807 
808         for (x = 0; x < surf->w; ++x, pixel += bpp) {
809             if (get_pixel_color(pixel, bpp) != colorkey) {
810                 bitmask_setbit(bitmask, x, y);
811             }
812         }
813     }
814 }
815 
816 /* Creates a mask from a given surface.
817  *
818  * Returns:
819  *     Mask object or NULL to indicate a fail
820  */
821 static PyObject *
mask_from_surface(PyObject * self,PyObject * args,PyObject * kwargs)822 mask_from_surface(PyObject *self, PyObject *args, PyObject *kwargs)
823 {
824     SDL_Surface *surf = NULL;
825     pgSurfaceObject *surfobj = NULL;
826     pgMaskObject *maskobj = NULL;
827     Uint32 colorkey;
828     int threshold = 127; /* default value */
829     int use_thresh = 1;
830     static char *keywords[] = {"surface", "threshold", NULL};
831 
832     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|i", keywords, &pgSurface_Type, &surfobj,
833                           &threshold)) {
834         return NULL; /* Exception already set. */
835     }
836 
837     surf = pgSurface_AsSurface(surfobj);
838 
839     if (surf->w < 0 || surf->h < 0) {
840         return RAISE(PyExc_ValueError,
841                      "cannot create mask with negative size");
842     }
843 
844     maskobj = CREATE_MASK_OBJ(surf->w, surf->h, 0);
845 
846     if (NULL == maskobj) {
847         return NULL; /* Exception already set. */
848     }
849 
850     if (surf->w == 0 || surf->h == 0) {
851         /* Nothing left to do for 0 sized surfaces. */
852         return (PyObject *)maskobj;
853     }
854 
855     if (!pgSurface_Lock(surfobj)) {
856         Py_DECREF((PyObject *)maskobj);
857         return RAISE(PyExc_RuntimeError, "cannot lock surface");
858     }
859 
860     Py_BEGIN_ALLOW_THREADS; /* Release the GIL. */
861 
862     use_thresh = (SDL_GetColorKey(surf, &colorkey) == -1);
863 
864     if (use_thresh) {
865         set_from_threshold(surf, maskobj->mask, threshold);
866     }
867     else {
868         set_from_colorkey(surf, maskobj->mask, colorkey);
869     }
870 
871     Py_END_ALLOW_THREADS; /* Obtain the GIL. */
872 
873     if (!pgSurface_Unlock(surfobj)) {
874         Py_DECREF((PyObject *)maskobj);
875         return RAISE(PyExc_RuntimeError, "cannot unlock surface");
876     }
877 
878     return (PyObject *)maskobj;
879 }
880 
881 /*
882 
883 palette_colors - this only affects surfaces with a palette
884     if true we look at the colors from the palette,
885     otherwise we threshold the pixel values.  This is useful if
886     the surface is actually greyscale colors, and not palette colors.
887 
888 */
889 
890 void
bitmask_threshold(bitmask_t * m,SDL_Surface * surf,SDL_Surface * surf2,Uint32 color,Uint32 threshold,int palette_colors)891 bitmask_threshold(bitmask_t *m, SDL_Surface *surf, SDL_Surface *surf2,
892                   Uint32 color, Uint32 threshold, int palette_colors)
893 {
894     int x, y, rshift, gshift, bshift, rshift2, gshift2, bshift2;
895     int rloss, gloss, bloss, rloss2, gloss2, bloss2;
896     Uint8 *pixels, *pixels2;
897     SDL_PixelFormat *format, *format2;
898     Uint32 the_color, the_color2, rmask, gmask, bmask, rmask2, gmask2, bmask2;
899     Uint8 *pix;
900     Uint8 r, g, b, a;
901     Uint8 tr, tg, tb, ta;
902     int bpp1, bpp2;
903 
904     format = surf->format;
905     rmask = format->Rmask;
906     gmask = format->Gmask;
907     bmask = format->Bmask;
908     rshift = format->Rshift;
909     gshift = format->Gshift;
910     bshift = format->Bshift;
911     rloss = format->Rloss;
912     gloss = format->Gloss;
913     bloss = format->Bloss;
914     bpp1 = surf->format->BytesPerPixel;
915 
916     if (surf2) {
917         format2 = surf2->format;
918         rmask2 = format2->Rmask;
919         gmask2 = format2->Gmask;
920         bmask2 = format2->Bmask;
921         rshift2 = format2->Rshift;
922         gshift2 = format2->Gshift;
923         bshift2 = format2->Bshift;
924         rloss2 = format2->Rloss;
925         gloss2 = format2->Gloss;
926         bloss2 = format2->Bloss;
927         pixels2 = (Uint8 *)surf2->pixels;
928         bpp2 = surf->format->BytesPerPixel;
929     }
930     else { /* make gcc stop complaining */
931         rmask2 = gmask2 = bmask2 = 0;
932         rshift2 = gshift2 = bshift2 = 0;
933         rloss2 = gloss2 = bloss2 = 0;
934         format2 = NULL;
935         pixels2 = NULL;
936         bpp2 = 0;
937     }
938 
939     SDL_GetRGBA(color, format, &r, &g, &b, &a);
940     SDL_GetRGBA(threshold, format, &tr, &tg, &tb, &ta);
941 
942     for (y = 0; y < surf->h; y++) {
943         pixels = (Uint8 *)surf->pixels + y * surf->pitch;
944         if (surf2) {
945             pixels2 = (Uint8 *)surf2->pixels + y * surf2->pitch;
946         }
947         for (x = 0; x < surf->w; x++) {
948             /* the_color = surf->get_at(x,y) */
949             switch (bpp1) {
950                 case 1:
951                     the_color = (Uint32) * ((Uint8 *)pixels);
952                     pixels++;
953                     break;
954                 case 2:
955                     the_color = (Uint32) * ((Uint16 *)pixels);
956                     pixels += 2;
957                     break;
958                 case 3:
959                     pix = ((Uint8 *)pixels);
960                     pixels += 3;
961 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
962                     the_color = (pix[0]) + (pix[1] << 8) + (pix[2] << 16);
963 #else
964                     the_color = (pix[2]) + (pix[1] << 8) + (pix[0] << 16);
965 #endif
966                     break;
967                 default: /* case 4: */
968                     the_color = *((Uint32 *)pixels);
969                     pixels += 4;
970                     break;
971             }
972 
973             if (surf2) {
974                 switch (bpp2) {
975                     case 1:
976                         the_color2 = (Uint32) * ((Uint8 *)pixels2);
977                         pixels2++;
978                         break;
979                     case 2:
980                         the_color2 = (Uint32) * ((Uint16 *)pixels2);
981                         pixels2 += 2;
982                         break;
983                     case 3:
984                         pix = ((Uint8 *)pixels2);
985                         pixels2 += 3;
986 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
987                         the_color2 = (pix[0]) + (pix[1] << 8) + (pix[2] << 16);
988 #else
989                         the_color2 = (pix[2]) + (pix[1] << 8) + (pix[0] << 16);
990 #endif
991                         break;
992                     default: /* case 4: */
993                         the_color2 = *((Uint32 *)pixels2);
994                         pixels2 += 4;
995                         break;
996                 }
997                 /* TODO: will need to handle surfaces with palette colors.
998                  */
999                 if ((bpp2 == 1) && (bpp1 == 1) && (!palette_colors)) {
1000                     /* Don't look at the color of the surface, just use the
1001                        value. This is useful for 8bit images that aren't
1002                        actually using the palette.
1003                     */
1004                     if (abs_diff_uint32(the_color2, the_color) < tr) {
1005                         /* this pixel is within the threshold of othersurface.
1006                          */
1007                         bitmask_setbit(m, x, y);
1008                     }
1009                 }
1010                 else if ((abs_diff_uint32(
1011                               (((the_color2 & rmask2) >> rshift2) << rloss2),
1012                               (((the_color & rmask) >> rshift) << rloss)) <
1013                           tr) &&
1014                          (abs_diff_uint32(
1015                               (((the_color2 & gmask2) >> gshift2) << gloss2),
1016                               (((the_color & gmask) >> gshift) << gloss)) <
1017                           tg) &&
1018                          (abs_diff_uint32(
1019                               (((the_color2 & bmask2) >> bshift2) << bloss2),
1020                               (((the_color & bmask) >> bshift) << bloss)) <
1021                           tb)) {
1022                     /* this pixel is within the threshold of othersurface. */
1023                     bitmask_setbit(m, x, y);
1024                 }
1025 
1026                 /* TODO: will need to handle surfaces with palette colors.
1027                    TODO: will need to handle the case where palette_colors == 0
1028                 */
1029             }
1030             else if ((abs_diff_uint32(
1031                           (((the_color & rmask) >> rshift) << rloss), r) <
1032                       tr) &&
1033                      (abs_diff_uint32(
1034                           (((the_color & gmask) >> gshift) << gloss), g) <
1035                       tg) &&
1036                      (abs_diff_uint32(
1037                           (((the_color & bmask) >> bshift) << bloss), b) <
1038                       tb)) {
1039                 /* this pixel is within the threshold of the color. */
1040                 bitmask_setbit(m, x, y);
1041             }
1042         }
1043     }
1044 }
1045 
1046 static PyObject *
mask_from_threshold(PyObject * self,PyObject * args,PyObject * kwargs)1047 mask_from_threshold(PyObject *self, PyObject *args, PyObject *kwargs)
1048 {
1049     pgSurfaceObject *surfobj = NULL;
1050     pgSurfaceObject *surfobj2 = NULL;
1051     pgMaskObject *maskobj = NULL;
1052     SDL_Surface *surf = NULL, *surf2 = NULL;
1053     PyObject *rgba_obj_color, *rgba_obj_threshold = NULL;
1054     Uint8 rgba_color[4];
1055     Uint8 rgba_threshold[4] = {0, 0, 0, 255};
1056     Uint32 color;
1057     Uint32 color_threshold;
1058     int palette_colors = 1;
1059     static char* keywords[] = {"surface", "color", "threshold", "othersurface",
1060                                "palette_colors", NULL};
1061 
1062     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O|OO!i", keywords, &pgSurface_Type,
1063                                      &surfobj, &rgba_obj_color, &rgba_obj_threshold,
1064                                      &pgSurface_Type, &surfobj2, &palette_colors))
1065         return NULL;
1066 
1067     surf = pgSurface_AsSurface(surfobj);
1068     if (surfobj2) {
1069         surf2 = pgSurface_AsSurface(surfobj2);
1070     }
1071 
1072     if (PyInt_Check(rgba_obj_color)) {
1073         color = (Uint32)PyInt_AsLong(rgba_obj_color);
1074     }
1075     else if (PyLong_Check(rgba_obj_color)) {
1076         color = (Uint32)PyLong_AsUnsignedLong(rgba_obj_color);
1077     }
1078     else if (pg_RGBAFromColorObj(rgba_obj_color, rgba_color)) {
1079         color = SDL_MapRGBA(surf->format, rgba_color[0], rgba_color[1],
1080                             rgba_color[2], rgba_color[3]);
1081     }
1082     else {
1083         return RAISE(PyExc_TypeError, "invalid color argument");
1084     }
1085 
1086     if (rgba_obj_threshold) {
1087         if (PyInt_Check(rgba_obj_threshold))
1088             color_threshold = (Uint32)PyInt_AsLong(rgba_obj_threshold);
1089         else if (PyLong_Check(rgba_obj_threshold))
1090             color_threshold =
1091                 (Uint32)PyLong_AsUnsignedLong(rgba_obj_threshold);
1092         else if (pg_RGBAFromColorObj(rgba_obj_threshold, rgba_threshold))
1093             color_threshold =
1094                 SDL_MapRGBA(surf->format, rgba_threshold[0], rgba_threshold[1],
1095                             rgba_threshold[2], rgba_threshold[3]);
1096         else
1097             return RAISE(PyExc_TypeError, "invalid threshold argument");
1098     }
1099     else {
1100         color_threshold =
1101             SDL_MapRGBA(surf->format, rgba_threshold[0], rgba_threshold[1],
1102                         rgba_threshold[2], rgba_threshold[3]);
1103     }
1104 
1105     maskobj = CREATE_MASK_OBJ(surf->w, surf->h, 0);
1106 
1107     if (NULL == maskobj) {
1108         return NULL; /* Exception already set. */
1109     }
1110 
1111     pgSurface_Lock(surfobj);
1112     if (surfobj2) {
1113         pgSurface_Lock(surfobj2);
1114     }
1115 
1116     Py_BEGIN_ALLOW_THREADS;
1117     bitmask_threshold(maskobj->mask, surf, surf2, color, color_threshold,
1118                       palette_colors);
1119     Py_END_ALLOW_THREADS;
1120 
1121     pgSurface_Unlock(surfobj);
1122     if (surfobj2) {
1123         pgSurface_Unlock(surfobj2);
1124     }
1125 
1126     return (PyObject *)maskobj;
1127 }
1128 
1129 /* The initial labelling phase of the connected components algorithm.
1130  *
1131  * Connected component labeling based on the SAUF algorithm by Kesheng Wu,
1132  * Ekow Otoo, and Kenji Suzuki. The algorithm is best explained by their
1133  * paper, "Two Strategies to Speed up Connected Component Labeling Algorithms",
1134  * but in summary, it is a very efficient two pass method for 8-connected
1135  * components. It uses a decision tree to minimize the number of neighbors that
1136  * need to be checked. It stores equivalence information in an array based
1137  * union-find.
1138  *
1139  * Params:
1140  *     input - the input mask
1141  *     image - an array to store labelled pixels
1142  *     ufind - the union-find label equivalence array
1143  *     largest - an array to store the number of pixels for each label
1144  *
1145  * Returns:
1146  *     the highest label in the labelled image
1147  */
1148 unsigned int
cc_label(bitmask_t * input,unsigned int * image,unsigned int * ufind,unsigned int * largest)1149 cc_label(bitmask_t *input, unsigned int *image, unsigned int *ufind,
1150          unsigned int *largest)
1151 {
1152     unsigned int *buf;
1153     unsigned int x, y, w, h, root, aroot, croot, temp, label;
1154 
1155     label = 0;
1156     w = input->w;
1157     h = input->h;
1158 
1159     ufind[0] = 0;
1160     buf = image;
1161 
1162     /* special case for first pixel */
1163     if (bitmask_getbit(input, 0, 0)) { /* process for a new connected comp: */
1164         label++;                       /* create a new label */
1165         *buf = label;                  /* give the pixel the label */
1166         ufind[label] = label; /* put the label in the equivalence array */
1167         largest[label] = 1;   /* the label has 1 pixel associated with it */
1168     }
1169     else {
1170         *buf = 0;
1171     }
1172     buf++;
1173 
1174     /* special case for first row.
1175            Go over the first row except the first pixel.
1176     */
1177     for (x = 1; x < w; x++) {
1178         if (bitmask_getbit(input, x, 0)) {
1179             if (*(buf - 1)) { /* d label */
1180                 *buf = *(buf - 1);
1181             }
1182             else { /* create label */
1183                 label++;
1184                 *buf = label;
1185                 ufind[label] = label;
1186                 largest[label] = 0;
1187             }
1188             largest[*buf]++;
1189         }
1190         else {
1191             *buf = 0;
1192         }
1193         buf++;
1194     }
1195 
1196     /* the rest of the image */
1197     for (y = 1; y < h; y++) {
1198         /* first pixel of the row */
1199         if (bitmask_getbit(input, 0, y)) {
1200             if (*(buf - w)) { /* b label */
1201                 *buf = *(buf - w);
1202             }
1203             else if (*(buf - w + 1)) { /* c label */
1204                 *buf = *(buf - w + 1);
1205             }
1206             else { /* create label */
1207                 label++;
1208                 *buf = label;
1209                 ufind[label] = label;
1210                 largest[label] = 0;
1211             }
1212             largest[*buf]++;
1213         }
1214         else {
1215             *buf = 0;
1216         }
1217         buf++;
1218         /* middle pixels of the row */
1219         for (x = 1; x < (w - 1); x++) {
1220             if (bitmask_getbit(input, x, y)) {
1221                 if (*(buf - w)) { /* b label */
1222                     *buf = *(buf - w);
1223                 }
1224                 else if (*(buf - w + 1)) { /* c branch of tree */
1225                     if (*(buf - w - 1)) {  /* union(c, a) */
1226                         croot = root = *(buf - w + 1);
1227                         while (ufind[root] < root) { /* find root */
1228                             root = ufind[root];
1229                         }
1230                         if (croot != *(buf - w - 1)) {
1231                             temp = aroot = *(buf - w - 1);
1232                             while (ufind[aroot] < aroot) { /* find root */
1233                                 aroot = ufind[aroot];
1234                             }
1235                             if (root > aroot) {
1236                                 root = aroot;
1237                             }
1238                             while (ufind[temp] > root) { /* set root */
1239                                 aroot = ufind[temp];
1240                                 ufind[temp] = root;
1241                                 temp = aroot;
1242                             }
1243                         }
1244                         while (ufind[croot] > root) { /* set root */
1245                             temp = ufind[croot];
1246                             ufind[croot] = root;
1247                             croot = temp;
1248                         }
1249                         *buf = root;
1250                     }
1251                     else if (*(buf - 1)) { /* union(c, d) */
1252                         croot = root = *(buf - w + 1);
1253                         while (ufind[root] < root) { /* find root */
1254                             root = ufind[root];
1255                         }
1256                         if (croot != *(buf - 1)) {
1257                             temp = aroot = *(buf - 1);
1258                             while (ufind[aroot] < aroot) { /* find root */
1259                                 aroot = ufind[aroot];
1260                             }
1261                             if (root > aroot) {
1262                                 root = aroot;
1263                             }
1264                             while (ufind[temp] > root) { /* set root */
1265                                 aroot = ufind[temp];
1266                                 ufind[temp] = root;
1267                                 temp = aroot;
1268                             }
1269                         }
1270                         while (ufind[croot] > root) { /* set root */
1271                             temp = ufind[croot];
1272                             ufind[croot] = root;
1273                             croot = temp;
1274                         }
1275                         *buf = root;
1276                     }
1277                     else { /* c label */
1278                         *buf = *(buf - w + 1);
1279                     }
1280                 }
1281                 else if (*(buf - w - 1)) { /* a label */
1282                     *buf = *(buf - w - 1);
1283                 }
1284                 else if (*(buf - 1)) { /* d label */
1285                     *buf = *(buf - 1);
1286                 }
1287                 else { /* create label */
1288                     label++;
1289                     *buf = label;
1290                     ufind[label] = label;
1291                     largest[label] = 0;
1292                 }
1293                 largest[*buf]++;
1294             }
1295             else {
1296                 *buf = 0;
1297             }
1298             buf++;
1299         }
1300         /* last pixel of the row, if its not also the first pixel of the row */
1301         if (w > 1) {
1302             if (bitmask_getbit(input, x, y)) {
1303                 if (*(buf - w)) { /* b label */
1304                     *buf = *(buf - w);
1305                 }
1306                 else if (*(buf - w - 1)) { /* a label */
1307                     *buf = *(buf - w - 1);
1308                 }
1309                 else if (*(buf - 1)) { /* d label */
1310                     *buf = *(buf - 1);
1311                 }
1312                 else { /* create label */
1313                     label++;
1314                     *buf = label;
1315                     ufind[label] = label;
1316                     largest[label] = 0;
1317                 }
1318                 largest[*buf]++;
1319             }
1320             else {
1321                 *buf = 0;
1322             }
1323             buf++;
1324         }
1325     }
1326 
1327     return label;
1328 }
1329 
1330 /* Creates a bounding rect for each connected component in the given mask.
1331  *
1332  * Allocates memory for rects.
1333  *
1334  * NOTE: Caller is responsible for freeing the "ret_rects" memory.
1335  *
1336  * Params:
1337  *     input - mask to search in for the connected components to bound
1338  *     num_bounding_boxes - passes back the number of bounding rects found
1339  *     ret_rects - passes back the bounding rects that are found with the first
1340  *         rect at index 1, memory is allocated
1341  *
1342  * Returns:
1343  *     0 on success
1344  *     -2 on memory allocation error
1345  */
1346 static int
get_bounding_rects(bitmask_t * input,int * num_bounding_boxes,GAME_Rect ** ret_rects)1347 get_bounding_rects(bitmask_t *input, int *num_bounding_boxes,
1348                    GAME_Rect **ret_rects)
1349 {
1350     unsigned int *image, *ufind, *largest, *buf;
1351     unsigned int x_uf, label = 0;
1352     int x, y, w, h, temp, relabel;
1353     GAME_Rect *rects;
1354 
1355     rects = NULL;
1356 
1357     w = input->w;
1358     h = input->h;
1359 
1360     if (!w || !h) {
1361         *ret_rects = rects;
1362         return 0;
1363     }
1364     /* a temporary image to assign labels to each bit of the mask */
1365     image = (unsigned int *)malloc(sizeof(int) * w * h);
1366     if (!image) {
1367         return -2;
1368     }
1369 
1370     /* allocate enough space for the maximum possible connected components */
1371     /* the union-find array. see wikipedia for info on union find */
1372     ufind = (unsigned int *)malloc(sizeof(int) * (w / 2 + 1) * (h / 2 + 1));
1373     if (!ufind) {
1374         free(image);
1375         return -2;
1376     }
1377 
1378     largest = (unsigned int *)malloc(sizeof(int) * (w / 2 + 1) * (h / 2 + 1));
1379     if (!largest) {
1380         free(image);
1381         free(ufind);
1382         return -2;
1383     }
1384 
1385     /* do the initial labelling */
1386     label = cc_label(input, image, ufind, largest);
1387 
1388     relabel = 0;
1389     /* flatten and relabel the union-find equivalence array.  Start at label 1
1390        because label 0 indicates an unset pixel.  For this reason, we also use
1391        <= label rather than < label. */
1392     for (x_uf = 1; x_uf <= label; ++x_uf) {
1393         if (ufind[x_uf] < x_uf) {             /* is it a union find root? */
1394             ufind[x_uf] = ufind[ufind[x_uf]]; /* relabel it to its root */
1395         }
1396         else { /* its a root */
1397             relabel++;
1398             ufind[x_uf] = relabel; /* assign the lowest label available */
1399         }
1400     }
1401 
1402     *num_bounding_boxes = relabel;
1403 
1404     if (relabel == 0) {
1405         /* early out, as we didn't find anything. */
1406         free(image);
1407         free(ufind);
1408         free(largest);
1409         *ret_rects = rects;
1410         return 0;
1411     }
1412 
1413     /* the bounding rects, need enough space for the number of labels */
1414     rects = (GAME_Rect *)malloc(sizeof(GAME_Rect) * (relabel + 1));
1415     if (!rects) {
1416         free(image);
1417         free(ufind);
1418         free(largest);
1419         return -2;
1420     }
1421 
1422     for (temp = 0; temp <= relabel; temp++) {
1423         rects[temp].h = 0; /* so we know if its a new rect or not */
1424     }
1425 
1426     /* find the bounding rect of each connected component */
1427     buf = image;
1428     for (y = 0; y < h; y++) {
1429         for (x = 0; x < w; x++) {
1430             if (ufind[*buf]) { /* if the pixel is part of a component */
1431                 if (rects[ufind[*buf]].h) { /* the component has a rect */
1432                     temp = rects[ufind[*buf]].x;
1433                     rects[ufind[*buf]].x = MIN(x, temp);
1434                     rects[ufind[*buf]].y = MIN(y, rects[ufind[*buf]].y);
1435                     rects[ufind[*buf]].w =
1436                         MAX(rects[ufind[*buf]].w + temp, x + 1) -
1437                         rects[ufind[*buf]].x;
1438                     rects[ufind[*buf]].h = MAX(rects[ufind[*buf]].h,
1439                                                y - rects[ufind[*buf]].y + 1);
1440                 }
1441                 else { /* otherwise, start the rect */
1442                     rects[ufind[*buf]].x = x;
1443                     rects[ufind[*buf]].y = y;
1444                     rects[ufind[*buf]].w = 1;
1445                     rects[ufind[*buf]].h = 1;
1446                 }
1447             }
1448             buf++;
1449         }
1450     }
1451 
1452     free(image);
1453     free(ufind);
1454     free(largest);
1455     *ret_rects = rects;
1456 
1457     return 0;
1458 }
1459 
1460 static PyObject *
mask_get_bounding_rects(PyObject * self,PyObject * args)1461 mask_get_bounding_rects(PyObject *self, PyObject *args)
1462 {
1463     GAME_Rect *regions;
1464     GAME_Rect *aregion;
1465     int num_bounding_boxes, i, r;
1466     PyObject *rect_list;
1467     PyObject *rect;
1468 
1469     bitmask_t *mask = pgMask_AsBitmap(self);
1470 
1471     rect_list = NULL;
1472     regions = NULL;
1473     aregion = NULL;
1474 
1475     num_bounding_boxes = 0;
1476 
1477     Py_BEGIN_ALLOW_THREADS;
1478 
1479     r = get_bounding_rects(mask, &num_bounding_boxes, &regions);
1480 
1481     Py_END_ALLOW_THREADS;
1482 
1483     if (r == -2) {
1484         /* memory out failure */
1485         return RAISE(PyExc_MemoryError,
1486                      "Not enough memory to get bounding rects. \n");
1487     }
1488 
1489     rect_list = PyList_New(0);
1490 
1491     if (!rect_list) {
1492         free(regions);
1493 
1494         return NULL; /* Exception already set. */
1495     }
1496 
1497     /* build a list of rects to return.  Starts at 1 because we never use 0. */
1498     for (i = 1; i <= num_bounding_boxes; i++) {
1499         aregion = regions + i;
1500         rect = pgRect_New4(aregion->x, aregion->y, aregion->w, aregion->h);
1501 
1502         if (NULL == rect) {
1503             Py_DECREF(rect_list);
1504             free(regions);
1505 
1506             return RAISE(PyExc_MemoryError,
1507                          "cannot allocate memory for bounding rects");
1508         }
1509 
1510         if (0 != PyList_Append(rect_list, rect)) {
1511             Py_DECREF(rect);
1512             Py_DECREF(rect_list);
1513             free(regions);
1514 
1515             return NULL; /* Exception already set. */
1516         }
1517 
1518         Py_DECREF(rect);
1519     }
1520 
1521     free(regions);
1522 
1523     return rect_list;
1524 }
1525 
1526 /* Finds all the connected components in a given mask.
1527  *
1528  * Allocates memory for components.
1529  *
1530  * NOTE: Caller is responsible for freeing the "components" memory.
1531  *
1532  * Params:
1533  *     mask - mask to search in for the connected components
1534  *     components - passes back an array of connected component masks with the
1535  *         first component at index 1, memory is allocated
1536  *     min - minimum number of pixels for a component to be considered,
1537  *         defaults to 0 for negative values
1538  *
1539  * Returns:
1540  *     the number of connected components (>= 0)
1541  *     -2 on memory allocation error
1542  */
1543 static int
get_connected_components(bitmask_t * mask,bitmask_t *** components,int min)1544 get_connected_components(bitmask_t *mask, bitmask_t ***components, int min)
1545 {
1546     unsigned int *image, *ufind, *largest, *buf;
1547     unsigned int x_uf, min_cc, label = 0;
1548     int x, y, w, h, relabel;
1549     bitmask_t **comps;
1550 
1551     w = mask->w;
1552     h = mask->h;
1553 
1554     if (!w || !h) {
1555         return 0;
1556     }
1557 
1558     /* a temporary image to assign labels to each bit of the mask */
1559     image = (unsigned int *)malloc(sizeof(int) * w * h);
1560     if (!image) {
1561         return -2;
1562     }
1563 
1564     /* allocate enough space for the maximum possible connected components */
1565     /* the union-find array. see wikipedia for info on union find */
1566     ufind = (unsigned int *)malloc(sizeof(int) * (w / 2 + 1) * (h / 2 + 1));
1567     if (!ufind) {
1568         free(image);
1569         return -2;
1570     }
1571 
1572     largest = (unsigned int *)malloc(sizeof(int) * (w / 2 + 1) * (h / 2 + 1));
1573     if (!largest) {
1574         free(image);
1575         free(ufind);
1576         return -2;
1577     }
1578 
1579     /* do the initial labelling */
1580     label = cc_label(mask, image, ufind, largest);
1581 
1582     for (x_uf = 1; x_uf <= label; ++x_uf) {
1583         if (ufind[x_uf] < x_uf) {
1584             largest[ufind[x_uf]] += largest[x_uf];
1585         }
1586     }
1587 
1588     relabel = 0;
1589     min_cc = (0 < min) ? (unsigned int)min : 0;
1590 
1591     /* flatten and relabel the union-find equivalence array.  Start at label 1
1592        because label 0 indicates an unset pixel.  For this reason, we also use
1593        <= label rather than < label. */
1594     for (x_uf = 1; x_uf <= label; ++x_uf) {
1595         if (ufind[x_uf] < x_uf) {             /* is it a union find root? */
1596             ufind[x_uf] = ufind[ufind[x_uf]]; /* relabel it to its root */
1597         }
1598         else { /* its a root */
1599             if (largest[x_uf] >= min_cc) {
1600                 relabel++;
1601                 ufind[x_uf] = relabel; /* assign the lowest label available */
1602             }
1603             else {
1604                 ufind[x_uf] = 0;
1605             }
1606         }
1607     }
1608 
1609     if (relabel == 0) {
1610         /* early out, as we didn't find anything. */
1611         free(image);
1612         free(ufind);
1613         free(largest);
1614         return 0;
1615     }
1616 
1617     /* allocate space for the mask array */
1618     comps = (bitmask_t **)malloc(sizeof(bitmask_t *) * (relabel + 1));
1619     if (!comps) {
1620         free(image);
1621         free(ufind);
1622         free(largest);
1623         return -2;
1624     }
1625 
1626     /* create the empty masks */
1627     for (x = 1; x <= relabel; x++) {
1628         comps[x] = bitmask_create(w, h);
1629     }
1630 
1631     /* set the bits in each mask */
1632     buf = image;
1633     for (y = 0; y < h; y++) {
1634         for (x = 0; x < w; x++) {
1635             if (ufind[*buf]) { /* if the pixel is part of a component */
1636                 bitmask_setbit(comps[ufind[*buf]], x, y);
1637             }
1638             buf++;
1639         }
1640     }
1641 
1642     free(image);
1643     free(ufind);
1644     free(largest);
1645 
1646     *components = comps;
1647 
1648     return relabel;
1649 }
1650 
1651 static PyObject *
mask_connected_components(PyObject * self,PyObject * args,PyObject * kwargs)1652 mask_connected_components(PyObject *self, PyObject *args, PyObject *kwargs)
1653 {
1654     PyObject *mask_list = NULL;
1655     pgMaskObject *maskobj = NULL;
1656     bitmask_t **components = NULL;
1657     bitmask_t *mask = pgMask_AsBitmap(self);
1658     int i, m, num_components, min = 0; /* Default min value. */
1659     static char *keywords[] = {"minimum", NULL};
1660 
1661     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", keywords, &min)) {
1662         return NULL; /* Exception already set. */
1663     }
1664 
1665     Py_BEGIN_ALLOW_THREADS;
1666     num_components = get_connected_components(mask, &components, min);
1667     Py_END_ALLOW_THREADS;
1668 
1669     if (num_components == -2) {
1670         return RAISE(PyExc_MemoryError,
1671                      "cannot allocate memory for connected components");
1672     }
1673 
1674     mask_list = PyList_New(0);
1675     if (!mask_list) {
1676         /* Components were allocated starting at index 1. */
1677         for (i = 1; i <= num_components; ++i) {
1678             bitmask_free(components[i]);
1679         }
1680 
1681         free(components);
1682         return NULL; /* Exception already set. */
1683     }
1684 
1685     /* Components were allocated starting at index 1. */
1686     for (i = 1; i <= num_components; ++i) {
1687         maskobj = create_mask_using_bitmask(components[i]);
1688 
1689         if (NULL == maskobj) {
1690             /* Starting freeing with the current index. */
1691             for (m = i; m <= num_components; ++m) {
1692                 bitmask_free(components[m]);
1693             }
1694 
1695             free(components);
1696             Py_DECREF(mask_list);
1697             return NULL; /* Exception already set. */
1698         }
1699 
1700         if (0 != PyList_Append(mask_list, (PyObject *)maskobj)) {
1701             /* Can't append to the list. Starting freeing with the next index
1702              * as maskobj contains the component from the current index. */
1703             for (m = i + 1; m <= num_components; ++m) {
1704                 bitmask_free(components[m]);
1705             }
1706 
1707             free(components);
1708             Py_DECREF((PyObject *)maskobj);
1709             Py_DECREF(mask_list);
1710             return NULL; /* Exception already set. */
1711         }
1712 
1713         Py_DECREF((PyObject *)maskobj);
1714     }
1715 
1716     free(components);
1717     return mask_list;
1718 }
1719 
1720 /* Finds the largest connected component in a given mask.
1721  *
1722  * Tracks the number of pixels in each label, finding the biggest one while
1723  * flattening the union-find equivalence array. It then writes an output mask
1724  * containing only the largest connected component.
1725  *
1726  * Params:
1727  *     input - mask to search in for the largest connected component
1728  *     output - this mask is updated with the largest connected component
1729  *     ccx - x index, if < 0 then the largest connected component in the input
1730  *         mask is found and copied to the output mask, otherwise the connected
1731  *         component at (ccx, ccy) is copied to the output mask
1732  *     ccy - y index
1733  *
1734  * Returns:
1735  *     0 on success
1736  *     -2 on memory allocation error
1737  */
1738 static int
largest_connected_comp(bitmask_t * input,bitmask_t * output,int ccx,int ccy)1739 largest_connected_comp(bitmask_t *input, bitmask_t *output, int ccx, int ccy)
1740 {
1741     unsigned int *image, *ufind, *largest, *buf;
1742     unsigned int max, x, y, w, h, label;
1743 
1744     w = input->w;
1745     h = input->h;
1746 
1747     if (!w || !h) {
1748         return 0;
1749     }
1750 
1751     /* a temporary image to assign labels to each bit of the mask */
1752     image = (unsigned int *)malloc(sizeof(int) * w * h);
1753     if (!image) {
1754         return -2;
1755     }
1756     /* allocate enough space for the maximum possible connected components */
1757     /* the union-find array. see wikipedia for info on union find */
1758     ufind = (unsigned int *)malloc(sizeof(int) * (w / 2 + 1) * (h / 2 + 1));
1759     if (!ufind) {
1760         free(image);
1761         return -2;
1762     }
1763     /* an array to track the number of pixels associated with each label */
1764     largest = (unsigned int *)malloc(sizeof(int) * (w / 2 + 1) * (h / 2 + 1));
1765     if (!largest) {
1766         free(image);
1767         free(ufind);
1768         return -2;
1769     }
1770 
1771     /* do the initial labelling */
1772     label = cc_label(input, image, ufind, largest);
1773 
1774     max = 1;
1775     /* flatten the union-find equivalence array */
1776     for (x = 2; x <= label; x++) {
1777         if (ufind[x] != x) {                 /* is it a union find root? */
1778             largest[ufind[x]] += largest[x]; /* add its pixels to its root */
1779             ufind[x] = ufind[ufind[x]];      /* relabel it to its root */
1780         }
1781         if (largest[ufind[x]] > largest[max]) { /* is it the new biggest? */
1782             max = ufind[x];
1783         }
1784     }
1785 
1786     /* write out the final image */
1787     buf = image;
1788     if (ccx >= 0)
1789         max = ufind[*(buf + ccy * w + ccx)];
1790     for (y = 0; y < h; y++) {
1791         for (x = 0; x < w; x++) {
1792             if (ufind[*buf] == max) {         /* if the label is the max one */
1793                 bitmask_setbit(output, x, y); /* set the bit in the mask */
1794             }
1795             buf++;
1796         }
1797     }
1798 
1799     free(image);
1800     free(ufind);
1801     free(largest);
1802 
1803     return 0;
1804 }
1805 
1806 static PyObject *
mask_connected_component(PyObject * self,PyObject * args,PyObject * kwargs)1807 mask_connected_component(PyObject *self, PyObject *args, PyObject *kwargs)
1808 {
1809     bitmask_t *input = pgMask_AsBitmap(self);
1810     pgMaskObject *output_maskobj = NULL;
1811     int x = -1, y = -1;
1812     Py_ssize_t args_exist = PyTuple_Size(args);
1813     PyObject* pos = NULL;
1814     static char *keywords[] = {"pos", NULL};
1815 
1816     if (kwargs)
1817         args_exist += PyDict_Size(kwargs);
1818 
1819     if (args_exist) {
1820         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O", keywords, &pos)) {
1821             return NULL; /* Exception already set. */
1822         }
1823 
1824         if (!pg_TwoIntsFromObj(pos, &x, &y)) {
1825             return RAISE(PyExc_TypeError, "pos must be two numbers");
1826         }
1827 
1828         if (x < 0 || x >= input->w || y < 0 || y >= input->h) {
1829             return PyErr_Format(PyExc_IndexError, "%d, %d is out of bounds", x,
1830                                 y);
1831         }
1832     }
1833 
1834     output_maskobj = CREATE_MASK_OBJ(input->w, input->h, 0);
1835 
1836     if (NULL == output_maskobj) {
1837         return NULL; /* Exception already set. */
1838     }
1839 
1840     /* If a pixel index is provided and the indexed bit is not set, then the
1841      * returned mask is empty.
1842      */
1843     if (!args_exist || bitmask_getbit(input, x, y)) {
1844         if (largest_connected_comp(input, output_maskobj->mask, x, y) == -2) {
1845             Py_DECREF(output_maskobj);
1846             return RAISE(PyExc_MemoryError,
1847                          "cannot allocate memory for connected component");
1848         }
1849     }
1850 
1851     return (PyObject *)output_maskobj;
1852 }
1853 
1854 /* Extract the color data from a color object.
1855  *
1856  * Params:
1857  *     surf: surface that color will be mapped from
1858  *     color_obj: color object to extract color data from
1859  *     rbga_color: rbga array, contains default color if color_obj is NULL
1860  *     color: color value extracted from the color_obj (or from the default
1861  *         value of rbga_color)
1862  *
1863  * Returns:
1864  *     int: 1, means the color data extraction was successful and the color
1865  *             parameter contains a valid color value
1866  *          0, means the color data extraction has failed and an exception has
1867  *             been set
1868  */
1869 static int
extract_color(SDL_Surface * surf,PyObject * color_obj,Uint8 rgba_color[],Uint32 * color)1870 extract_color(SDL_Surface *surf, PyObject *color_obj, Uint8 rgba_color[],
1871               Uint32 *color)
1872 {
1873     if (NULL == color_obj) {
1874         *color = SDL_MapRGBA(surf->format, rgba_color[0], rgba_color[1],
1875                              rgba_color[2], rgba_color[3]);
1876         return 1;
1877     }
1878 
1879     if (PyInt_Check(color_obj)) {
1880         long intval = PyInt_AsLong(color_obj);
1881 
1882         if ((-1 == intval && PyErr_Occurred()) || intval > (long)0xFFFFFFFF) {
1883             PyErr_SetString(PyExc_ValueError, "invalid color argument");
1884             return 0;
1885         }
1886 
1887         *color = (Uint32)intval;
1888         return 1;
1889     }
1890 
1891     if (PyLong_Check(color_obj)) {
1892         unsigned long longval = PyLong_AsUnsignedLong(color_obj);
1893 
1894         if (PyErr_Occurred() || longval > 0xFFFFFFFF) {
1895             PyErr_SetString(PyExc_ValueError, "invalid color argument");
1896             return 0;
1897         }
1898 
1899         *color = (Uint32)longval;
1900         return 1;
1901     }
1902 
1903     if (pg_RGBAFromFuzzyColorObj(color_obj, rgba_color)) {
1904         *color = SDL_MapRGBA(surf->format, rgba_color[0], rgba_color[1],
1905                              rgba_color[2], rgba_color[3]);
1906         return 1;
1907     }
1908 
1909     return 0; /* Exception already set. */
1910 }
1911 
1912 /* Draws a mask on a surface.
1913  *
1914  * Params:
1915  *     surf: surface to draw on
1916  *     bitmask: bitmask to draw
1917  *     x_dest: x position on surface of where to start drawing
1918  *     y_dest: y position on surface of where to start drawing
1919  *     draw_setbits: if non-zero then draw the set bits (bits==1)
1920  *     draw_unsetbits: if non-zero then draw the unset bits (bits==0)
1921  *     setsurf: use colors from this surface for set bits (bits==1)
1922  *     unsetsurf: use colors from this surface for unset bits (bits==0)
1923  *     setcolor: color for set bits, setsurf takes precedence (bits==1)
1924  *     unsetcolor: color for unset bits, unsetsurf takes precedence (bits==0)
1925  *
1926  * Assumptions:
1927  *     - surf and bitmask are non-NULL
1928  *     - all surfaces have the same pixel format
1929  *     - all surfaces that are non-NULL are locked
1930  */
1931 static void
draw_to_surface(SDL_Surface * surf,bitmask_t * bitmask,int x_dest,int y_dest,int draw_setbits,int draw_unsetbits,SDL_Surface * setsurf,SDL_Surface * unsetsurf,Uint32 * setcolor,Uint32 * unsetcolor)1932 draw_to_surface(SDL_Surface *surf, bitmask_t *bitmask, int x_dest, int y_dest,
1933                 int draw_setbits, int draw_unsetbits, SDL_Surface *setsurf,
1934                 SDL_Surface *unsetsurf, Uint32 *setcolor, Uint32 *unsetcolor)
1935 {
1936     Uint8 *pixel = NULL;
1937     Uint8 bpp;
1938     int x, y, x_end, y_end, x_start, y_start; /* surf indexing */
1939     int xm, ym, xm_start, ym_start; /* bitmask/setsurf/unsetsurf indexing */
1940 
1941     /* There is nothing to do when any of these conditions exist:
1942      * - surface has a width or height of <= 0
1943      * - mask has a width or height of <= 0
1944      * - draw_setbits and draw_unsetbits are both 0 */
1945     if ((surf->h <= 0) || (surf->w <= 0) || (bitmask->h <= 0) ||
1946         (bitmask->w <= 0) || (!draw_setbits && !draw_unsetbits)) {
1947         return;
1948     }
1949 
1950     /* There is also nothing to do when the destination position is such that
1951      * nothing will be drawn on the surface. */
1952     if ((x_dest >= surf->w) || (y_dest >= surf->h) || (-x_dest > bitmask->w) ||
1953         (-y_dest > bitmask->h)) {
1954         return;
1955     }
1956 
1957     bpp = surf->format->BytesPerPixel;
1958 
1959     xm_start = (x_dest < 0) ? -x_dest : 0;
1960     x_start = (x_dest > 0) ? x_dest : 0;
1961     x_end = MIN(surf->w, bitmask->w + x_dest);
1962 
1963     ym_start = (y_dest < 0) ? -y_dest : 0;
1964     y_start = (y_dest > 0) ? y_dest : 0;
1965     y_end = MIN(surf->h, bitmask->h + y_dest);
1966 
1967     if (NULL == setsurf && NULL == unsetsurf) {
1968         /* Draw just using color values. No surfaces. */
1969         draw_setbits = draw_setbits && NULL != setcolor;
1970         draw_unsetbits = draw_unsetbits && NULL != unsetcolor;
1971 
1972         for (y = y_start, ym = ym_start; y < y_end; ++y, ++ym) {
1973             pixel = (Uint8 *)surf->pixels + y * surf->pitch + x_start * bpp;
1974 
1975             for (x = x_start, xm = xm_start; x < x_end;
1976                  ++x, ++xm, pixel += bpp) {
1977                 if (bitmask_getbit(bitmask, xm, ym)) {
1978                     if (draw_setbits) {
1979                         set_pixel_color(pixel, bpp, *setcolor);
1980                     }
1981                 }
1982                 else if (draw_unsetbits) {
1983                     set_pixel_color(pixel, bpp, *unsetcolor);
1984                 }
1985             }
1986         }
1987     }
1988     else if (NULL == setcolor && NULL == unsetcolor && NULL != setsurf &&
1989              NULL != unsetsurf && setsurf->h + y_dest >= y_end &&
1990              setsurf->w + x_dest >= x_end && unsetsurf->h + y_dest >= y_end &&
1991              unsetsurf->w + x_dest >= x_end) {
1992         /* Draw using surfaces that are as big (or bigger) as what is being
1993          * drawn and no color values are being used. */
1994         Uint8 *setpixel = NULL, *unsetpixel = NULL;
1995 
1996         for (y = y_start, ym = ym_start; y < y_end; ++y, ++ym) {
1997             pixel = (Uint8 *)surf->pixels + y * surf->pitch + x_start * bpp;
1998             setpixel = (Uint8 *)setsurf->pixels + ym * setsurf->pitch +
1999                        xm_start * bpp;
2000             unsetpixel = (Uint8 *)unsetsurf->pixels + ym * unsetsurf->pitch +
2001                          xm_start * bpp;
2002 
2003             for (x = x_start, xm = xm_start; x < x_end;
2004                  ++x, ++xm, pixel += bpp, setpixel += bpp, unsetpixel += bpp) {
2005                 if (bitmask_getbit(bitmask, xm, ym)) {
2006                     if (draw_setbits) {
2007                         set_pixel_color(pixel, bpp,
2008                                         get_pixel_color(setpixel, bpp));
2009                     }
2010                 }
2011                 else if (draw_unsetbits) {
2012                     set_pixel_color(pixel, bpp,
2013                                     get_pixel_color(unsetpixel, bpp));
2014                 }
2015             }
2016         }
2017     }
2018     else {
2019         /* Draw using surfaces and color values. */
2020         Uint8 *setpixel = NULL, *unsetpixel = NULL;
2021         int use_setsurf, use_unsetsurf;
2022 
2023         /* Looping over each bit in the mask and deciding whether to use a
2024          * color from setsurf/unsetsurf or from setcolor/unsetcolor. */
2025         for (y = y_start, ym = ym_start; y < y_end; ++y, ++ym) {
2026             pixel = (Uint8 *)surf->pixels + y * surf->pitch + x_start * bpp;
2027             use_setsurf =
2028                 draw_setbits && NULL != setsurf && setsurf->h > ym;
2029             use_unsetsurf =
2030                 draw_unsetbits && NULL != unsetsurf && unsetsurf->h > ym;
2031 
2032             if (use_setsurf) {
2033                 setpixel = (Uint8 *)setsurf->pixels + ym * setsurf->pitch +
2034                            xm_start * bpp;
2035             }
2036 
2037             if (use_unsetsurf) {
2038                 unsetpixel = (Uint8 *)unsetsurf->pixels +
2039                              ym * unsetsurf->pitch + xm_start * bpp;
2040             }
2041 
2042             for (x = x_start, xm = xm_start; x < x_end;
2043                  ++x, ++xm, pixel += bpp) {
2044                 if (bitmask_getbit(bitmask, xm, ym)) {
2045                     if (draw_setbits) {
2046                         if (use_setsurf && setsurf->w > xm) {
2047                             set_pixel_color(pixel, bpp,
2048                                             get_pixel_color(setpixel, bpp));
2049                         }
2050                         else if (NULL != setcolor) {
2051                             set_pixel_color(pixel, bpp, *setcolor);
2052                         }
2053                     }
2054                 }
2055                 else if (draw_unsetbits) {
2056                     if (use_unsetsurf && unsetsurf->w > xm) {
2057                         set_pixel_color(pixel, bpp,
2058                                         get_pixel_color(unsetpixel, bpp));
2059                     }
2060                     else if (NULL != unsetcolor) {
2061                         set_pixel_color(pixel, bpp, *unsetcolor);
2062                     }
2063                 }
2064 
2065                 if (use_setsurf) {
2066                     setpixel += bpp;
2067                 }
2068 
2069                 if (use_unsetsurf) {
2070                     unsetpixel += bpp;
2071                 }
2072             }
2073         }
2074     }
2075 }
2076 
2077 /* Checks if the surfaces have the same pixel formats.
2078  *
2079  * Params:
2080  *     surf: surface to check against
2081  *     check_surf: surface to check
2082  *
2083  * Returns:
2084  *     int: 0 to indicate surfaces don't have the same format
2085  *          1 to indicate the surfaces have the same format
2086  *
2087  * Assumptions:
2088  *     - both parameters are non-NULL
2089  *     - these checks are enough to assume the pixel formats are the same
2090  */
2091 static int
check_surface_pixel_format(SDL_Surface * surf,SDL_Surface * check_surf)2092 check_surface_pixel_format(SDL_Surface *surf, SDL_Surface *check_surf)
2093 {
2094     if ((surf->format->BytesPerPixel != check_surf->format->BytesPerPixel) ||
2095         (surf->format->BitsPerPixel != check_surf->format->BitsPerPixel)
2096         || (surf->format->format != check_surf->format->format)
2097     ) {
2098         return 0;
2099     }
2100 
2101     return 1;
2102 }
2103 
2104 /* Draws a mask on a surface.
2105  *
2106  * Returns:
2107  *     Surface object or NULL to indicate a fail.
2108  */
2109 static PyObject *
mask_to_surface(PyObject * self,PyObject * args,PyObject * kwargs)2110 mask_to_surface(PyObject *self, PyObject *args, PyObject *kwargs)
2111 {
2112     PyObject *surfobj = Py_None, *setcolorobj = NULL, *unsetcolorobj = NULL;
2113     PyObject *setsurfobj = Py_None, *unsetsurfobj = Py_None;
2114     PyObject *destobj = NULL;
2115     SDL_Surface *surf = NULL, *setsurf = NULL, *unsetsurf = NULL;
2116     bitmask_t *bitmask = pgMask_AsBitmap(self);
2117     Uint32 *setcolor_ptr = NULL, *unsetcolor_ptr = NULL;
2118     Uint32 setcolor, unsetcolor;
2119     int draw_setbits = 0, draw_unsetbits = 0;
2120     int created_surfobj = 0; /* Set to 1 if this func creates the surfobj. */
2121     int x_dest = 0, y_dest = 0; /* Default destination coordinates. */
2122     Uint8 dflt_setcolor[] = {255, 255, 255, 255}; /* Default set color. */
2123     Uint8 dflt_unsetcolor[] = {0, 0, 0, 255};     /* Default unset color. */
2124 
2125     static char *keywords[] = {"surface",  "setsurface", "unsetsurface",
2126                                "setcolor", "unsetcolor", "dest",
2127                                NULL};
2128 
2129     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOOOO", keywords,
2130                                      &surfobj, &setsurfobj, &unsetsurfobj,
2131                                      &setcolorobj, &unsetcolorobj, &destobj)) {
2132         return NULL; /* Exception already set. */
2133     }
2134 
2135     if (Py_None == surfobj) {
2136         surfobj = PyObject_CallFunction((PyObject *)&pgSurface_Type, "(ii)ii",
2137                                         bitmask->w, bitmask->h,
2138                                         PGS_SRCALPHA,
2139                                         32);
2140 
2141         if (NULL == surfobj) {
2142             if (!PyErr_Occurred()) {
2143                 return RAISE(PyExc_RuntimeError, "unable to create surface");
2144             }
2145             return NULL;
2146         }
2147 
2148         created_surfobj = 1;
2149     }
2150     else if (!pgSurface_Check(surfobj)) {
2151         return RAISE(PyExc_TypeError, "invalid surface argument");
2152     }
2153 
2154     surf = pgSurface_AsSurface(surfobj);
2155 
2156     if (Py_None != setsurfobj) {
2157         if (!pgSurface_Check(setsurfobj)) {
2158             PyErr_SetString(PyExc_TypeError, "invalid setsurface argument");
2159             goto to_surface_error;
2160         }
2161 
2162         setsurf = pgSurface_AsSurface(setsurfobj);
2163 
2164         if (0 == check_surface_pixel_format(surf, setsurf)) {
2165             /* Needs to have the same format settings as surface. */
2166             PyErr_SetString(PyExc_ValueError,
2167                             "setsurface needs to have same "
2168                             "bytesize/bitsize/alpha format as surface");
2169             goto to_surface_error;
2170         }
2171         else if ((setsurf->h <= 0) || (setsurf->w <= 0)) {
2172             /* Surface has no usable color positions, so ignore it. */
2173             setsurf = NULL;
2174         }
2175         else {
2176             draw_setbits = 1;
2177         }
2178     }
2179 
2180     if (Py_None != unsetsurfobj) {
2181         if (!pgSurface_Check(unsetsurfobj)) {
2182             PyErr_SetString(PyExc_TypeError, "invalid unsetsurface argument");
2183             goto to_surface_error;
2184         }
2185 
2186         unsetsurf = pgSurface_AsSurface(unsetsurfobj);
2187 
2188         if (0 == check_surface_pixel_format(surf, unsetsurf)) {
2189             /* Needs to have the same format settings as surface. */
2190             PyErr_SetString(PyExc_ValueError,
2191                             "unsetsurface needs to have same "
2192                             "bytesize/bitsize/alpha format as surface");
2193             goto to_surface_error;
2194         }
2195         else if ((unsetsurf->h <= 0) || (unsetsurf->w <= 0)) {
2196             /* Surface has no usable color positions, so ignore it. */
2197             unsetsurf = NULL;
2198         }
2199         else {
2200             draw_unsetbits = 1;
2201         }
2202     }
2203 
2204     if (Py_None != setcolorobj) {
2205         if (!extract_color(surf, setcolorobj, dflt_setcolor, &setcolor)) {
2206             goto to_surface_error; /* Exception already set. */
2207         }
2208 
2209         setcolor_ptr = &setcolor;
2210         draw_setbits = 1;
2211     }
2212 
2213     if (Py_None != unsetcolorobj) {
2214         if (!extract_color(surf, unsetcolorobj, dflt_unsetcolor,
2215                            &unsetcolor)) {
2216             goto to_surface_error; /* Exception already set. */
2217         }
2218 
2219         unsetcolor_ptr = &unsetcolor;
2220         draw_unsetbits = 1;
2221     }
2222 
2223     if (NULL != destobj) {
2224         int tempx, tempy;
2225 
2226         /* Destination coordinates can be extracted from:
2227          * - lists/tuples with 2 items
2228          * - Rect (or Rect like) objects (uses x, y values) */
2229         if (pg_TwoIntsFromObj(destobj, &tempx, &tempy)) {
2230             x_dest = tempx;
2231             y_dest = tempy;
2232         }
2233         else {
2234             GAME_Rect temp_rect;
2235             GAME_Rect *dest_rect = pgRect_FromObject(destobj, &temp_rect);
2236 
2237             if (NULL != dest_rect) {
2238                 x_dest = dest_rect->x;
2239                 y_dest = dest_rect->y;
2240             }
2241             else {
2242                 PyErr_SetString(PyExc_TypeError, "invalid dest argument");
2243                 goto to_surface_error;
2244             }
2245         }
2246     }
2247 
2248     if (!pgSurface_Lock((pgSurfaceObject *)surfobj)) {
2249         PyErr_SetString(PyExc_RuntimeError, "cannot lock surface");
2250         goto to_surface_error;
2251     }
2252 
2253     /* Only lock the setsurface if it is being used.
2254      * i.e. setsurf is non-NULL */
2255     if (NULL != setsurf && !pgSurface_Lock((pgSurfaceObject *)setsurfobj)) {
2256         PyErr_SetString(PyExc_RuntimeError, "cannot lock setsurface");
2257         goto to_surface_error;
2258     }
2259 
2260     /* Only lock the unsetsurface if it is being used.
2261      * i.e.. unsetsurf is non-NULL. */
2262     if (NULL != unsetsurf &&
2263         !pgSurface_Lock((pgSurfaceObject *)unsetsurfobj)) {
2264         PyErr_SetString(PyExc_RuntimeError, "cannot lock unsetsurface");
2265         goto to_surface_error;
2266     }
2267 
2268     Py_BEGIN_ALLOW_THREADS; /* Release the GIL. */
2269 
2270     draw_to_surface(surf, bitmask, x_dest, y_dest, draw_setbits,
2271                     draw_unsetbits, setsurf, unsetsurf, setcolor_ptr,
2272                     unsetcolor_ptr);
2273 
2274     Py_END_ALLOW_THREADS; /* Obtain the GIL. */
2275 
2276     if (NULL != unsetsurf &&
2277         !pgSurface_Unlock((pgSurfaceObject *)unsetsurfobj)) {
2278         PyErr_SetString(PyExc_RuntimeError, "cannot unlock unsetsurface");
2279         goto to_surface_error;
2280     }
2281 
2282     if (NULL != setsurf &&
2283         !pgSurface_Unlock((pgSurfaceObject *)setsurfobj)) {
2284         PyErr_SetString(PyExc_RuntimeError, "cannot unlock setsurface");
2285         goto to_surface_error;
2286     }
2287 
2288     if (!pgSurface_Unlock((pgSurfaceObject *)surfobj)) {
2289         PyErr_SetString(PyExc_RuntimeError, "cannot unlock surface");
2290         goto to_surface_error;
2291     }
2292 
2293     if (!created_surfobj) {
2294         /* Only increase ref count if this func didn't create the surfobj. */
2295         Py_INCREF(surfobj);
2296     }
2297 
2298     return surfobj;
2299 
2300 /* Handles the cleanup for fail cases. */
2301 to_surface_error:
2302     if (created_surfobj) {
2303         /* Only decrease ref count if this func created the surfobj. */
2304         Py_DECREF(surfobj);
2305     }
2306 
2307     return NULL;
2308 }
2309 
2310 static PyMethodDef mask_methods[] = {
2311     {"__copy__", mask_copy, METH_NOARGS, DOC_MASKCOPY},
2312     {"copy", mask_call_copy, METH_NOARGS, DOC_MASKCOPY},
2313     {"get_size", mask_get_size, METH_VARARGS, DOC_MASKGETSIZE},
2314     {"get_rect", (PyCFunction)mask_get_rect, METH_VARARGS | METH_KEYWORDS,
2315      DOC_MASKGETRECT},
2316     {"get_at", (PyCFunction)mask_get_at, METH_VARARGS | METH_KEYWORDS,
2317     DOC_MASKGETAT},
2318     {"set_at", (PyCFunction)mask_set_at, METH_VARARGS | METH_KEYWORDS,
2319     DOC_MASKSETAT},
2320     {"overlap", (PyCFunction)mask_overlap, METH_VARARGS | METH_KEYWORDS,
2321     DOC_MASKOVERLAP},
2322     {"overlap_area", (PyCFunction)mask_overlap_area, METH_VARARGS | METH_KEYWORDS,
2323      DOC_MASKOVERLAPAREA},
2324     {"overlap_mask", (PyCFunction)mask_overlap_mask, METH_VARARGS | METH_KEYWORDS,
2325      DOC_MASKOVERLAPMASK},
2326     {"fill", mask_fill, METH_NOARGS, DOC_MASKFILL},
2327     {"clear", mask_clear, METH_NOARGS, DOC_MASKCLEAR},
2328     {"invert", mask_invert, METH_NOARGS, DOC_MASKINVERT},
2329     {"scale", (PyCFunction)mask_scale, METH_VARARGS | METH_KEYWORDS,
2330     DOC_MASKSCALE},
2331     {"draw", (PyCFunction)mask_draw, METH_VARARGS | METH_KEYWORDS,
2332     DOC_MASKDRAW},
2333     {"erase", (PyCFunction)mask_erase, METH_VARARGS | METH_KEYWORDS,
2334     DOC_MASKERASE},
2335     {"count", mask_count, METH_NOARGS, DOC_MASKCOUNT},
2336     {"centroid", mask_centroid, METH_NOARGS, DOC_MASKCENTROID},
2337     {"angle", mask_angle, METH_NOARGS, DOC_MASKANGLE},
2338     {"outline", (PyCFunction)mask_outline, METH_VARARGS | METH_KEYWORDS,
2339      DOC_MASKOUTLINE},
2340     {"convolve", (PyCFunction)mask_convolve, METH_VARARGS | METH_KEYWORDS,
2341     DOC_MASKCONVOLVE},
2342     {"connected_component", (PyCFunction)mask_connected_component, METH_VARARGS | METH_KEYWORDS,
2343      DOC_MASKCONNECTEDCOMPONENT},
2344     {"connected_components", (PyCFunction)mask_connected_components, METH_VARARGS | METH_KEYWORDS,
2345      DOC_MASKCONNECTEDCOMPONENTS},
2346     {"get_bounding_rects", mask_get_bounding_rects, METH_NOARGS,
2347      DOC_MASKGETBOUNDINGRECTS},
2348     {"to_surface", (PyCFunction)mask_to_surface, METH_VARARGS | METH_KEYWORDS,
2349      DOC_MASKTOSURFACE},
2350 
2351     {NULL, NULL, 0, NULL}};
2352 
2353 /*mask object internals*/
2354 
2355 /* This is a helper function for internal use only.
2356  * Creates a mask object using an existing bitmask.
2357  *
2358  * Params:
2359  *     bitmask: pointer to the bitmask to use
2360  *
2361  * Returns:
2362  *     Mask object or NULL to indicate a fail
2363  */
2364 static PG_INLINE pgMaskObject *
create_mask_using_bitmask(bitmask_t * bitmask)2365 create_mask_using_bitmask(bitmask_t *bitmask)
2366 {
2367     return create_mask_using_bitmask_and_type(bitmask, &pgMask_Type);
2368 }
2369 
2370 /* This is a helper function for internal use only.
2371  * Creates a mask object using an existing bitmask and a type.
2372  *
2373  * Params:
2374  *     bitmask: pointer to the bitmask to use
2375  *     ob_type: pointer to the mask object type to create
2376  *
2377  * Returns:
2378  *     Mask object or NULL to indicate a fail
2379  */
2380 static PG_INLINE pgMaskObject *
create_mask_using_bitmask_and_type(bitmask_t * bitmask,PyTypeObject * ob_type)2381 create_mask_using_bitmask_and_type(bitmask_t *bitmask, PyTypeObject *ob_type)
2382 {
2383     /* tp_init is not needed as the bitmask has already been created. */
2384     pgMaskObject *maskobj =
2385         (pgMaskObject *)pgMask_Type.tp_new(ob_type, NULL, NULL);
2386 
2387     if (NULL == maskobj) {
2388         return (pgMaskObject *)RAISE(PyExc_MemoryError,
2389                                      "cannot allocate memory for mask");
2390     }
2391 
2392     maskobj->mask = bitmask;
2393     return maskobj;
2394 }
2395 
2396 static void
mask_dealloc(PyObject * self)2397 mask_dealloc(PyObject *self)
2398 {
2399     bitmask_t *bitmask = pgMask_AsBitmap(self);
2400 
2401     if (NULL != bitmask) {
2402         /* Free up the bitmask. */
2403         bitmask_free(bitmask);
2404     }
2405 
2406     /* Free up the mask. */
2407     Py_TYPE(self)->tp_free(self);
2408 }
2409 
2410 static PyObject *
mask_repr(PyObject * self)2411 mask_repr(PyObject *self)
2412 {
2413     bitmask_t *mask = pgMask_AsBitmap(self);
2414     return Text_FromFormat("<Mask(%dx%d)>", mask->w, mask->h);
2415 }
2416 
2417 static PyObject *
mask_new(PyTypeObject * subtype,PyObject * args,PyObject * kwargs)2418 mask_new(PyTypeObject *subtype, PyObject *args, PyObject *kwargs)
2419 {
2420     pgMaskObject *maskobj = (pgMaskObject *)subtype->tp_alloc(subtype, 0);
2421 
2422     if (NULL == maskobj) {
2423         return RAISE(PyExc_MemoryError, "cannot allocate memory for mask");
2424     }
2425 
2426     maskobj->mask = NULL;
2427     return (PyObject *)maskobj;
2428 }
2429 
2430 static int
mask_init(PyObject * self,PyObject * args,PyObject * kwargs)2431 mask_init(PyObject *self, PyObject *args, PyObject *kwargs)
2432 {
2433     bitmask_t *bitmask = NULL;
2434     PyObject* size = NULL;
2435     int w, h;
2436     int fill = 0; /* Default is false. */
2437     char *keywords[] = {"size", "fill", NULL};
2438     const char *format = "O|p";
2439 
2440     if (!PyArg_ParseTupleAndKeywords(args, kwargs, format, keywords, &size,
2441                                      &fill)) {
2442         return -1;
2443     }
2444 
2445     if (!pg_TwoIntsFromObj(size, &w, &h)) {
2446         PyErr_SetString(PyExc_TypeError, "size must be two numbers");
2447         return -1;
2448     }
2449 
2450     if (w < 0 || h < 0) {
2451         PyErr_SetString(PyExc_ValueError,
2452                         "cannot create mask with negative size");
2453         return -1;
2454     }
2455 
2456     bitmask = bitmask_create(w, h);
2457 
2458     if (NULL == bitmask) {
2459         PyErr_SetString(PyExc_MemoryError,
2460                         "cannot allocate memory for bitmask");
2461         return -1;
2462     }
2463 
2464     if (fill) {
2465         bitmask_fill(bitmask);
2466     }
2467 
2468     ((pgMaskObject *)self)->mask = bitmask;
2469     return 0;
2470 }
2471 
2472 typedef struct {
2473     int numbufs;
2474     Py_ssize_t shape[2];
2475     Py_ssize_t strides[2];
2476 } mask_bufinfo;
2477 
2478 static int
pgMask_GetBuffer(pgMaskObject * self,Py_buffer * view,int flags)2479 pgMask_GetBuffer(pgMaskObject *self, Py_buffer *view, int flags)
2480 {
2481     bitmask_t *m = self->mask;
2482     mask_bufinfo *bufinfo = (mask_bufinfo*)self->bufdata;
2483 
2484     if (bufinfo == NULL) {
2485         bufinfo = PyMem_RawMalloc(sizeof(mask_bufinfo));
2486         if (bufinfo == NULL) {
2487             PyErr_NoMemory();
2488             return -1;
2489         }
2490         bufinfo->numbufs = 1;
2491 
2492         bufinfo->shape[0] = (m->w - 1) / BITMASK_W_LEN + 1;
2493         bufinfo->shape[1] = m->h;
2494 
2495         bufinfo->strides[0] = m->h * sizeof(BITMASK_W);
2496         bufinfo->strides[1] = sizeof(BITMASK_W);
2497 
2498         self->bufdata = bufinfo;
2499     }
2500     else {
2501         bufinfo->numbufs++;
2502     }
2503 
2504     view->buf = m->bits;
2505     view->len = m->h * ((m->w - 1) / BITMASK_W_LEN + 1) * sizeof(BITMASK_W);
2506     view->readonly = 0;
2507     view->itemsize = sizeof(BITMASK_W);
2508     view->ndim = 2;
2509     view->internal = bufinfo;
2510     view->shape = (flags & PyBUF_ND) ? bufinfo->shape : NULL;
2511     view->strides = (flags & PyBUF_STRIDES) ? bufinfo->strides : NULL;
2512     if (flags & PyBUF_FORMAT) {
2513         view->format = "L"; /* L = unsigned long */
2514     }
2515     else {
2516         view->format = NULL;
2517     }
2518     view->suboffsets = NULL;
2519 
2520     Py_INCREF(self);
2521     view->obj = (PyObject *)self;
2522 
2523     return 0;
2524 }
2525 
2526 static void
pgMask_ReleaseBuffer(pgMaskObject * self,Py_buffer * view)2527 pgMask_ReleaseBuffer(pgMaskObject *self, Py_buffer *view)
2528 {
2529     mask_bufinfo *bufinfo = (mask_bufinfo*)view->internal;
2530 
2531     bufinfo->numbufs--;
2532     if (bufinfo->numbufs == 0) {
2533         PyMem_RawFree(bufinfo);
2534         self->bufdata = NULL;
2535     }
2536 }
2537 
2538 static PyBufferProcs pgMask_BufferProcs = {
2539     (getbufferproc)pgMask_GetBuffer,
2540     (releasebufferproc)pgMask_ReleaseBuffer
2541 };
2542 
2543 
2544 static PyTypeObject pgMask_Type = {
2545     PyVarObject_HEAD_INIT(NULL,0)
2546     "pygame.mask.Mask",   /* tp_name */
2547     sizeof(pgMaskObject), /* tp_basicsize */
2548     0,                    /* tp_itemsize */
2549     mask_dealloc,         /* tp_dealloc */
2550     0,                    /* tp_print */
2551     0,                    /* tp_getattr */
2552     0,                    /* tp_setattr */
2553     0,                    /* tp_as_async (formerly tp_compare/tp_reserved) */
2554     (reprfunc)mask_repr,  /* tp_repr */
2555     0,                    /* tp_as_number */
2556     NULL,                 /* tp_as_sequence */
2557     0,                    /* tp_as_mapping */
2558     (hashfunc)NULL,       /* tp_hash */
2559     (ternaryfunc)NULL,    /* tp_call */
2560     (reprfunc)NULL,       /* tp_str */
2561     0L,                   /* tp_getattro */
2562     0L,                   /* tp_setattro */
2563     &pgMask_BufferProcs,  /* tp_as_buffer */
2564     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
2565     DOC_PYGAMEMASKMASK, /* Documentation string */
2566     0,                  /* tp_traverse */
2567     0,                  /* tp_clear */
2568     0,                  /* tp_richcompare */
2569     0,                  /* tp_weaklistoffset */
2570     0,                  /* tp_iter */
2571     0,                  /* tp_iternext */
2572     mask_methods,       /* tp_methods */
2573     0,                  /* tp_members */
2574     0,                  /* tp_getset */
2575     0,                  /* tp_base */
2576     0,                  /* tp_dict */
2577     0,                  /* tp_descr_get */
2578     0,                  /* tp_descr_set */
2579     0,                  /* tp_dictoffset */
2580     mask_init,          /* tp_init */
2581     0,                  /* tp_alloc */
2582     mask_new,           /* tp_new */
2583 };
2584 
2585 /*mask module methods*/
2586 static PyMethodDef _mask_methods[] = {
2587     {"from_surface", (PyCFunction)mask_from_surface, METH_VARARGS | METH_KEYWORDS,
2588      DOC_PYGAMEMASKFROMSURFACE},
2589     {"from_threshold", (PyCFunction)mask_from_threshold, METH_VARARGS | METH_KEYWORDS,
2590      DOC_PYGAMEMASKFROMTHRESHOLD},
2591     {NULL, NULL, 0, NULL}};
2592 
MODINIT_DEFINE(mask)2593 MODINIT_DEFINE(mask)
2594 {
2595     PyObject *module, *dict, *apiobj;
2596     static void *c_api[PYGAMEAPI_MASK_NUMSLOTS];
2597 
2598     static struct PyModuleDef _module = {PyModuleDef_HEAD_INIT,
2599                                          "mask",
2600                                          DOC_PYGAMEMASK,
2601                                          -1,
2602                                          _mask_methods,
2603                                          NULL,
2604                                          NULL,
2605                                          NULL,
2606                                          NULL};
2607 
2608     /* imported needed apis; Do this first so if there is an error
2609        the module is not loaded.
2610     */
2611     import_pygame_base();
2612     if (PyErr_Occurred()) {
2613         MODINIT_ERROR;
2614     }
2615     import_pygame_color();
2616     if (PyErr_Occurred()) {
2617         MODINIT_ERROR;
2618     }
2619     import_pygame_surface();
2620     if (PyErr_Occurred()) {
2621         MODINIT_ERROR;
2622     }
2623     import_pygame_rect();
2624     if (PyErr_Occurred()) {
2625         MODINIT_ERROR;
2626     }
2627 
2628     /* create the mask type */
2629     if (PyType_Ready(&pgMask_Type) < 0) {
2630         MODINIT_ERROR;
2631     }
2632 
2633     /* create the module */
2634     module = PyModule_Create(&_module);
2635     if (module == NULL) {
2636         MODINIT_ERROR;
2637     }
2638     dict = PyModule_GetDict(module);
2639     if (PyDict_SetItemString(dict, "MaskType", (PyObject *)&pgMask_Type) ==
2640         -1) {
2641         DECREF_MOD(module);
2642         MODINIT_ERROR;
2643     }
2644 
2645     if (PyDict_SetItemString(dict, "Mask", (PyObject *)&pgMask_Type) == -1) {
2646         DECREF_MOD(module);
2647         MODINIT_ERROR;
2648     }
2649 
2650     /* export the c api */
2651     c_api[0] = &pgMask_Type;
2652     apiobj = encapsulate_api(c_api, "mask");
2653     if (apiobj == NULL) {
2654         DECREF_MOD(module);
2655         MODINIT_ERROR;
2656     }
2657     if (PyModule_AddObject(module, PYGAMEAPI_LOCAL_ENTRY, apiobj) == -1) {
2658         Py_DECREF(apiobj);
2659         DECREF_MOD(module);
2660         MODINIT_ERROR;
2661     }
2662     MODINIT_RETURN(module);
2663 }
2664