Skip to content

Commit dbc6622

Browse files
Optimized mask.from_surface() when converting an alpha surface. (#2895)
* Optimized mask.from_surface when converting an alpha surface. * Fix issues wit 16-bit surfaces, tests for invalid threshold values * revert checks for threshold value * add comment and small refactor * fix * make fast path more generic * add early exit for threshold > 255 * Update src_c/mask.c Co-authored-by: Ankith <[email protected]> * Update src_c/mask.c Co-authored-by: Ankith <[email protected]> --------- Co-authored-by: Ankith <[email protected]>
1 parent 2b088c9 commit dbc6622

File tree

1 file changed

+70
-13
lines changed

1 file changed

+70
-13
lines changed

src_c/mask.c

+70-13
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,24 @@
4343
#define M_PI 3.14159265358979323846
4444
#endif
4545

46+
/* Pretty good idea from Tom Duff :-). */
47+
#ifndef LOOP_UNROLLED4
48+
#define LOOP_UNROLLED4(code, n, width) \
49+
n = (width + 3) / 4; \
50+
switch (width & 3) { \
51+
case 0: \
52+
do { \
53+
code; \
54+
case 3: \
55+
code; \
56+
case 2: \
57+
code; \
58+
case 1: \
59+
code; \
60+
} while (--n > 0); \
61+
}
62+
#endif
63+
4664
/* Macro to create mask objects. This will call the type's tp_new and tp_init.
4765
* Params:
4866
* w: width of mask
@@ -770,22 +788,61 @@ set_pixel_color(Uint8 *pixel, Uint8 bpp, Uint32 color)
770788
static void
771789
set_from_threshold(SDL_Surface *surf, bitmask_t *bitmask, int threshold)
772790
{
773-
SDL_PixelFormat *format = surf->format;
774-
Uint8 bpp = PG_FORMAT_BytesPerPixel(format);
775-
Uint8 *pixel = NULL;
776-
Uint8 rgba[4];
777-
int x, y;
778-
779-
for (y = 0; y < surf->h; ++y) {
780-
pixel = (Uint8 *)surf->pixels + y * surf->pitch;
791+
/* This function expects surf to be non-zero sized. */
792+
SDL_PixelFormat *fmt = surf->format;
793+
const Uint8 bpp = PG_FORMAT_BytesPerPixel(fmt);
794+
int x, y, n;
795+
Uint8 *srcp;
796+
const int src_skip = surf->pitch - surf->w * bpp;
797+
798+
if (threshold >= 255) {
799+
return;
800+
}
781801

782-
for (x = 0; x < surf->w; ++x, pixel += bpp) {
783-
SDL_GetRGBA(get_pixel_color(pixel, bpp), format, rgba, rgba + 1,
784-
rgba + 2, rgba + 3);
785-
if (rgba[3] > threshold) {
786-
bitmask_setbit(bitmask, x, y);
802+
if (threshold < 0 || !SDL_ISPIXELFORMAT_ALPHA(fmt->format)) {
803+
bitmask_fill(bitmask);
804+
return;
805+
}
806+
else if (bpp < 3) {
807+
Uint8 r, g, b, a;
808+
srcp = (Uint8 *)surf->pixels;
809+
for (y = 0; y < surf->h; ++y) {
810+
for (x = 0; x < surf->w; ++x, srcp += bpp) {
811+
SDL_GetRGBA(bpp == 1 ? *srcp : *((Uint16 *)srcp), fmt, &r, &g,
812+
&b, &a);
813+
if (a > threshold)
814+
bitmask_setbit(bitmask, x, y);
787815
}
816+
srcp += src_skip;
788817
}
818+
return;
819+
}
820+
821+
/* With this strategy we avoid to get the rgb channels that we don't need
822+
* and instead we just jump from alpha channel to alpha channel, comparing
823+
* it with the threshold. */
824+
825+
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
826+
const char _a_off = fmt->Ashift >> 3;
827+
#else
828+
const char _a_off = 3 - (fmt->Ashift >> 3);
829+
#endif
830+
831+
srcp = (Uint8 *)surf->pixels + _a_off;
832+
const Uint8 u_threshold = (Uint8)threshold;
833+
834+
for (y = 0; y < surf->h; ++y) {
835+
x = 0;
836+
LOOP_UNROLLED4(
837+
{
838+
if ((*srcp) > u_threshold)
839+
bitmask_setbit(bitmask, x, y);
840+
srcp += bpp;
841+
x++;
842+
},
843+
n, surf->w);
844+
845+
srcp += src_skip;
789846
}
790847
}
791848

0 commit comments

Comments
 (0)