Skip to content

Commit fc36cd5

Browse files
authored
Merge pull request #3371 from Starbuck5/surface-init-convert-sdl3
Restructure and port surface init and convert to SDL3
2 parents c1e8884 + 2e02ed4 commit fc36cd5

File tree

1 file changed

+334
-11
lines changed

1 file changed

+334
-11
lines changed

src_c/surface.c

+334-11
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,13 @@ surface_init(pgSurfaceObject *self, PyObject *args, PyObject *kwds)
483483
int bpp;
484484
Uint32 Rmask, Gmask, Bmask, Amask;
485485
SDL_Surface *surface;
486+
487+
#if SDL_VERSION_ATLEAST(3, 0, 0)
488+
PG_PixelFormatEnum format = SDL_PIXELFORMAT_UNKNOWN;
489+
#else
486490
SDL_PixelFormat default_format;
491+
default_format.palette = NULL;
492+
#endif
487493

488494
char *kwids[] = {"size", "flags", "depth", "masks", NULL};
489495
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|iOO", kwids, &size, &flags,
@@ -510,10 +516,172 @@ surface_init(pgSurfaceObject *self, PyObject *args, PyObject *kwds)
510516
return -1;
511517
}
512518

513-
default_format.palette = NULL;
514-
515519
surface_cleanup(self);
516520

521+
#if SDL_VERSION_ATLEAST(3, 0, 0)
522+
if (depth && masks) { /* all info supplied, most errorchecking
523+
* needed */
524+
if (pgSurface_Check(depth)) {
525+
PyErr_SetString(PyExc_ValueError,
526+
"cannot pass surface for depth and color masks");
527+
return -1;
528+
}
529+
if (!pg_IntFromObj(depth, &bpp)) {
530+
PyErr_SetString(PyExc_ValueError,
531+
"invalid bits per pixel depth argument");
532+
return -1;
533+
}
534+
if (!PySequence_Check(masks) || PySequence_Length(masks) != 4) {
535+
PyErr_SetString(PyExc_ValueError,
536+
"masks argument must be sequence of four numbers");
537+
return -1;
538+
}
539+
if (!pg_UintFromObjIndex(masks, 0, &Rmask) ||
540+
!pg_UintFromObjIndex(masks, 1, &Gmask) ||
541+
!pg_UintFromObjIndex(masks, 2, &Bmask) ||
542+
!pg_UintFromObjIndex(masks, 3, &Amask)) {
543+
PyErr_SetString(PyExc_ValueError,
544+
"invalid mask values in masks sequence");
545+
return -1;
546+
}
547+
548+
format = SDL_MasksToPixelFormatEnum(bpp, Rmask, Gmask, Bmask, Amask);
549+
}
550+
else if (depth && PyNumber_Check(depth)) { /* use default masks */
551+
if (!pg_IntFromObj(depth, &bpp)) {
552+
PyErr_SetString(PyExc_ValueError,
553+
"invalid bits per pixel depth argument");
554+
return -1;
555+
}
556+
if (flags & PGS_SRCALPHA) {
557+
switch (bpp) {
558+
case 16:
559+
format = SDL_PIXELFORMAT_ARGB4444;
560+
break;
561+
case 32:
562+
format = SDL_PIXELFORMAT_ARGB8888;
563+
break;
564+
default:
565+
PyErr_SetString(
566+
PyExc_ValueError,
567+
"no standard masks exist for given bitdepth with "
568+
"alpha");
569+
return -1;
570+
}
571+
}
572+
else {
573+
switch (bpp) {
574+
case 8:
575+
format = SDL_PIXELFORMAT_INDEX8;
576+
break;
577+
case 12:
578+
format = SDL_PIXELFORMAT_XRGB4444;
579+
break;
580+
case 15:
581+
format = SDL_PIXELFORMAT_XRGB1555;
582+
break;
583+
case 16:
584+
format = SDL_PIXELFORMAT_RGB565;
585+
break;
586+
case 24:
587+
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
588+
format = SDL_PIXELFORMAT_RGB24;
589+
#else
590+
format = SDL_PIXELFORMAT_BGR24;
591+
#endif
592+
break;
593+
case 32:
594+
format = SDL_PIXELFORMAT_XRGB8888;
595+
break;
596+
default:
597+
PyErr_SetString(PyExc_ValueError,
598+
"nonstandard bit depth given");
599+
return -1;
600+
}
601+
}
602+
}
603+
else { /* no depth or surface */
604+
if (depth && pgSurface_Check(depth)) {
605+
format = PG_SURF_FORMATENUM(((pgSurfaceObject *)depth)->surf);
606+
}
607+
else if (pg_GetDefaultWindowSurface()) {
608+
format = PG_SURF_FORMATENUM(
609+
pgSurface_AsSurface(pg_GetDefaultWindowSurface()));
610+
}
611+
else {
612+
format = SDL_PIXELFORMAT_XRGB8888;
613+
}
614+
615+
if (flags & PGS_SRCALPHA) {
616+
switch (SDL_BITSPERPIXEL(format)) {
617+
case 16:
618+
format = SDL_PIXELFORMAT_ARGB4444;
619+
break;
620+
case 24:
621+
// we automatically step up to 32 if video is 24, fall
622+
// through to case below
623+
case 32:
624+
format = SDL_PIXELFORMAT_ARGB8888;
625+
break;
626+
default:
627+
PyErr_SetString(
628+
PyExc_ValueError,
629+
"no standard masks exist for given bitdepth with "
630+
"alpha");
631+
return -1;
632+
}
633+
}
634+
}
635+
636+
if (format == SDL_PIXELFORMAT_UNKNOWN) {
637+
PyErr_SetString(PyExc_ValueError, "Invalid mask values");
638+
return -1;
639+
}
640+
641+
surface = PG_CreateSurface(width, height, format);
642+
if (!surface) {
643+
PyErr_SetString(pgExc_SDLError, SDL_GetError());
644+
return -1;
645+
}
646+
647+
if (!(flags & PGS_SRCALPHA)) {
648+
/* We ignore the error if any. */
649+
SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_NONE);
650+
651+
/* When the display format has a full alpha channel (macOS right now),
652+
* Surfaces may be created with an unreqested alpha channel, which
653+
* could cause issues.
654+
* pygame Surfaces are supposed to be (0, 0, 0, 255) by default.
655+
* This is a simple fix to fill it with (0, 0, 0, 255) if necessary.
656+
* See Github issue:
657+
* https://github.com/pygame-community/pygame-ce/issues/796
658+
*/
659+
PG_PixelFormat *surf_format;
660+
SDL_Palette *surf_palette;
661+
if (!PG_GetSurfaceDetails(surface, &surf_format, &surf_palette)) {
662+
PyErr_SetString(pgExc_SDLError, SDL_GetError());
663+
SDL_FreeSurface(surface);
664+
return -1;
665+
}
666+
667+
if (surf_format->Amask != 0) {
668+
SDL_FillRect(surface, NULL,
669+
PG_MapRGBA(surf_format, surf_palette, 0, 0, 0, 255));
670+
}
671+
}
672+
673+
if (SDL_ISPIXELFORMAT_INDEXED(PG_SURF_FORMATENUM(surface))) {
674+
/* Give the surface something other than an all white palette.
675+
* */
676+
SDL_Palette *surf_palette = SDL_CreateSurfacePalette(surface);
677+
if (SDL_SetPaletteColors(surf_palette, default_palette_colors, 0,
678+
default_palette_size - 1) != 0) {
679+
PyErr_SetString(pgExc_SDLError, SDL_GetError());
680+
SDL_FreeSurface(surface);
681+
return -1;
682+
}
683+
}
684+
#else
517685
if (depth && masks) { /* all info supplied, most errorchecking
518686
* needed */
519687
if (pgSurface_Check(depth)) {
@@ -614,11 +782,7 @@ surface_init(pgSurfaceObject *self, PyObject *args, PyObject *kwds)
614782
}
615783
else {
616784
pix = &default_format;
617-
#if SDL_VERSION_ATLEAST(3, 0, 0)
618-
pix->bits_per_pixel = 32;
619-
#else
620785
pix->BitsPerPixel = 32;
621-
#endif
622786
pix->Amask = 0;
623787
pix->Rmask = 0xFF0000;
624788
pix->Gmask = 0xFF00;
@@ -702,6 +866,7 @@ surface_init(pgSurfaceObject *self, PyObject *args, PyObject *kwds)
702866
return -1;
703867
}
704868
}
869+
#endif
705870

706871
if (surface) {
707872
self->surf = surface;
@@ -1495,6 +1660,168 @@ surf_convert(pgSurfaceObject *self, PyObject *args)
14951660

14961661
pgSurface_Prep(self);
14971662

1663+
#if SDL_VERSION_ATLEAST(3, 0, 0)
1664+
if ((has_colorkey = SDL_HasColorKey(surf))) {
1665+
PG_PixelFormat *surf_format;
1666+
SDL_Palette *surf_palette;
1667+
if (!PG_GetSurfaceDetails(surf, &surf_format, &surf_palette)) {
1668+
return RAISE(pgExc_SDLError, SDL_GetError());
1669+
}
1670+
1671+
SDL_GetColorKey(surf, &colorkey);
1672+
if (SDL_ISPIXELFORMAT_ALPHA(PG_SURF_FORMATENUM(surf))) {
1673+
PG_GetRGBA(colorkey, surf_format, surf_palette, &key_r, &key_g,
1674+
&key_b, &key_a);
1675+
}
1676+
else {
1677+
PG_GetRGB(colorkey, surf_format, surf_palette, &key_r, &key_g,
1678+
&key_b);
1679+
}
1680+
}
1681+
1682+
if (argobject) {
1683+
if (pgSurface_Check(argobject)) {
1684+
src = pgSurface_AsSurface(argobject);
1685+
newsurf = PG_ConvertSurface(surf, src->format);
1686+
}
1687+
else {
1688+
/* will be updated later, initialize to make static analyzer happy
1689+
*/
1690+
int bpp = 0;
1691+
SDL_Palette *palette = NULL;
1692+
PG_PixelFormatEnum format_enum = SDL_PIXELFORMAT_UNKNOWN;
1693+
1694+
// PATH 1 = from bpp
1695+
if (pg_IntFromObj(argobject, &bpp)) {
1696+
if (flags != UINT32_MAX && flags & PGS_SRCALPHA) {
1697+
switch (bpp) {
1698+
case 16:
1699+
format_enum = SDL_PIXELFORMAT_ARGB4444;
1700+
break;
1701+
case 32:
1702+
format_enum = SDL_PIXELFORMAT_ARGB8888;
1703+
break;
1704+
default:
1705+
return RAISE(PyExc_ValueError,
1706+
"no standard masks exist for given "
1707+
"bitdepth with alpha");
1708+
}
1709+
}
1710+
else {
1711+
switch (bpp) {
1712+
case 8:
1713+
format_enum = SDL_PIXELFORMAT_INDEX8;
1714+
break;
1715+
case 12:
1716+
format_enum = SDL_PIXELFORMAT_XRGB4444;
1717+
break;
1718+
case 15:
1719+
format_enum = SDL_PIXELFORMAT_XRGB1555;
1720+
break;
1721+
case 16:
1722+
format_enum = SDL_PIXELFORMAT_RGB565;
1723+
break;
1724+
case 24:
1725+
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
1726+
format_enum = SDL_PIXELFORMAT_RGB24;
1727+
#else
1728+
format_enum = SDL_PIXELFORMAT_BGR24;
1729+
#endif
1730+
break;
1731+
case 32:
1732+
format_enum = SDL_PIXELFORMAT_XRGB8888;
1733+
break;
1734+
default:
1735+
return RAISE(PyExc_ValueError,
1736+
"nonstandard bit depth given");
1737+
}
1738+
}
1739+
}
1740+
// PATH 2 = from masks only
1741+
else if (PySequence_Check(argobject) &&
1742+
PySequence_Size(argobject) == 4) {
1743+
Uint32 Rmask, Gmask, Bmask, Amask;
1744+
1745+
if (!pg_UintFromObjIndex(argobject, 0, &Rmask) ||
1746+
!pg_UintFromObjIndex(argobject, 1, &Gmask) ||
1747+
!pg_UintFromObjIndex(argobject, 2, &Bmask) ||
1748+
!pg_UintFromObjIndex(argobject, 3, &Amask)) {
1749+
pgSurface_Unprep(self);
1750+
return RAISE(PyExc_ValueError,
1751+
"invalid color masks given");
1752+
}
1753+
Uint32 mask = Rmask | Gmask | Bmask | Amask;
1754+
1755+
// This code shocked me. -Starbuck, Mar. 2025
1756+
// Like what if you have a hole in the mask?
1757+
// Like a blank alpha mask first-- it would just terminate
1758+
// the whole loop right?
1759+
// I think this whole code path should be deprecated.
1760+
for (bpp = 0; bpp < 32; ++bpp) {
1761+
if (!(mask >> bpp)) {
1762+
break;
1763+
}
1764+
}
1765+
1766+
format_enum = SDL_MasksToPixelFormatEnum(bpp, Rmask, Gmask,
1767+
Bmask, Amask);
1768+
}
1769+
else {
1770+
pgSurface_Unprep(self);
1771+
return RAISE(
1772+
PyExc_ValueError,
1773+
"invalid argument specifying new format to convert to");
1774+
}
1775+
1776+
// If the destination format is indexed, provide a new palette or
1777+
// copy over existing palette.
1778+
if (SDL_ISPIXELFORMAT_INDEXED(format_enum)) {
1779+
if (SDL_ISPIXELFORMAT_INDEXED(PG_SURF_FORMATENUM(surf))) {
1780+
palette = PG_GetSurfacePalette(surf);
1781+
}
1782+
else {
1783+
/* Give the surface something other than an all white
1784+
* palette.
1785+
*/
1786+
palette = SDL_AllocPalette(default_palette_size);
1787+
SDL_SetPaletteColors(palette, default_palette_colors, 0,
1788+
default_palette_size);
1789+
}
1790+
}
1791+
1792+
newsurf = SDL_ConvertSurfaceAndColorspace(
1793+
surf, format_enum, palette, SDL_GetSurfaceColorspace(surf), 0);
1794+
1795+
// In this scenario, we allocated the palette, so we also need
1796+
// to remove our reference to it.
1797+
if (SDL_ISPIXELFORMAT_INDEXED(format_enum) &&
1798+
!SDL_ISPIXELFORMAT_INDEXED(PG_SURF_FORMATENUM(surf))) {
1799+
SDL_FreePalette(palette);
1800+
}
1801+
1802+
SDL_SetSurfaceBlendMode(newsurf, SDL_BLENDMODE_NONE);
1803+
}
1804+
}
1805+
else {
1806+
newsurf = pg_DisplayFormat(surf);
1807+
if (newsurf) {
1808+
SDL_SetSurfaceBlendMode(newsurf, SDL_BLENDMODE_NONE);
1809+
}
1810+
}
1811+
1812+
if (newsurf == NULL) {
1813+
return RAISE(pgExc_SDLError, SDL_GetError());
1814+
}
1815+
1816+
if (has_colorkey) {
1817+
colorkey = SDL_MapSurfaceRGBA(newsurf, key_r, key_g, key_b, key_a);
1818+
if (SDL_SetColorKey(newsurf, SDL_TRUE, colorkey) != 0) {
1819+
PyErr_SetString(pgExc_SDLError, SDL_GetError());
1820+
SDL_FreeSurface(newsurf);
1821+
return NULL;
1822+
}
1823+
}
1824+
#else
14981825
if ((has_colorkey = SDL_HasColorKey(surf))) {
14991826
SDL_GetColorKey(surf, &colorkey);
15001827
if (SDL_ISPIXELFORMAT_ALPHA(PG_SURF_FORMATENUM(surf))) {
@@ -1607,13 +1934,8 @@ surf_convert(pgSurfaceObject *self, PyObject *args)
16071934
PyExc_ValueError,
16081935
"invalid argument specifying new format to convert to");
16091936
}
1610-
#if SDL_VERSION_ATLEAST(3, 0, 0)
1611-
format.bits_per_pixel = (Uint8)bpp;
1612-
format.bytes_per_pixel = (bpp + 7) / 8;
1613-
#else
16141937
format.BitsPerPixel = (Uint8)bpp;
16151938
format.BytesPerPixel = (bpp + 7) / 8;
1616-
#endif
16171939
if (PG_FORMAT_BitsPerPixel((&format)) > 8) {
16181940
/* Allow a 8 bit source surface with an empty palette to be
16191941
* converted to a format without a palette (pygame-ce issue
@@ -1662,6 +1984,7 @@ surf_convert(pgSurfaceObject *self, PyObject *args)
16621984
return NULL;
16631985
}
16641986
}
1987+
#endif
16651988

16661989
pgSurface_Unprep(self);
16671990

0 commit comments

Comments
 (0)