From 5b249e3767f2332ea88ac0e66ee6a5bbbd8dc95b Mon Sep 17 00:00:00 2001 From: zoldalma <46655437+zoldalma999@users.noreply.github.com> Date: Sun, 23 Jun 2024 13:12:22 +0200 Subject: [PATCH] Add ABGR format to frombytes and tobytes --- buildconfig/stubs/pygame/image.pyi | 4 +- docs/reST/ref/image.rst | 9 ++- src_c/image.c | 105 ++++++++++++++++++++++++++++- test/image_test.py | 2 +- 4 files changed, 113 insertions(+), 7 deletions(-) diff --git a/buildconfig/stubs/pygame/image.pyi b/buildconfig/stubs/pygame/image.pyi index a9a00a8504..5a6bf0ad9a 100644 --- a/buildconfig/stubs/pygame/image.pyi +++ b/buildconfig/stubs/pygame/image.pyi @@ -7,10 +7,10 @@ from ._common import FileArg, Literal, IntCoordinate, Coordinate _BufferStyle = Union[BufferProxy, bytes, bytearray, memoryview] _to_string_format = Literal[ - "P", "RGB", "RGBX", "RGBA", "ARGB", "BGRA", "RGBA_PREMULT", "ARGB_PREMULT" + "P", "RGB", "RGBX", "RGBA", "ARGB", "BGRA", "ABGR", "RGBA_PREMULT", "ARGB_PREMULT" ] _from_buffer_format = Literal["P", "RGB", "BGR", "BGRA", "RGBX", "RGBA", "ARGB"] -_from_string_format = Literal["P", "RGB", "RGBX", "RGBA", "ARGB", "BGRA"] +_from_string_format = Literal["P", "RGB", "RGBX", "RGBA", "ARGB", "BGRA", "ABGR"] def load(file: FileArg, namehint: str = "") -> Surface: ... def load_sized_svg(file: FileArg, size: Coordinate) -> Surface: ... diff --git a/docs/reST/ref/image.rst b/docs/reST/ref/image.rst index f964f86642..3086f5b999 100644 --- a/docs/reST/ref/image.rst +++ b/docs/reST/ref/image.rst @@ -228,13 +228,15 @@ following formats. * ``RGBA``, 32-bit image with an alpha channel * ``ARGB``, 32-bit image with alpha channel first - - * ``BGRA``, 32-bit image with alpha channel, red and blue channels swapped + + * ``BGRA``, 32-bit image with alpha channel, red and blue channels swapped + + * ``ABGR``, 32-bit image with alpha channel, reverse order * ``RGBA_PREMULT``, 32-bit image with colors scaled by alpha channel * ``ARGB_PREMULT``, 32-bit image with colors scaled by alpha channel, alpha channel first - + The 'pitch' argument can be used to specify the pitch/stride per horizontal line of the image in bytes. It must be equal to or greater than how many bytes the pixel data of each horizontal line in the image bytes occupies without any @@ -248,6 +250,7 @@ following formats. .. versionadded:: 2.1.3 .. versionchanged:: 2.2.0 Now supports keyword arguments. .. versionchanged:: 2.5.0 Added a 'pitch' argument. + .. versionchanged:: 2.5.1 Added support for ABGR image format .. ## pygame.image.tobytes ## diff --git a/src_c/image.c b/src_c/image.c index 481b6a99fa..1332f1a73d 100644 --- a/src_c/image.c +++ b/src_c/image.c @@ -554,7 +554,7 @@ image_tobytes(PyObject *self, PyObject *arg, PyObject *kwarg) byte_width = surf->w * 4; } else if (!strcmp(format, "RGBX") || !strcmp(format, "ARGB") || - !strcmp(format, "BGRA")) { + !strcmp(format, "BGRA") || !strcmp(format, "ABGR")) { byte_width = surf->w * 4; } else if (!strcmp(format, "RGBA_PREMULT") || @@ -878,6 +878,83 @@ image_tobytes(PyObject *self, PyObject *arg, PyObject *kwarg) } pgSurface_Unlock(surfobj); } + else if (!strcmp(format, "ABGR")) { + pgSurface_Lock(surfobj); + switch (PG_SURF_BytesPerPixel(surf)) { + case 1: + for (h = 0; h < surf->h; ++h) { + Uint8 *ptr = (Uint8 *)DATAROW(surf->pixels, h, surf->pitch, + surf->h, flipped); + for (w = 0; w < surf->w; ++w) { + color = *ptr++; + data[3] = (char)surf->format->palette->colors[color].r; + data[2] = (char)surf->format->palette->colors[color].g; + data[1] = (char)surf->format->palette->colors[color].b; + data[0] = (char)255; + data += 4; + } + pad(&data, padding); + } + break; + case 2: + for (h = 0; h < surf->h; ++h) { + Uint16 *ptr = (Uint16 *)DATAROW( + surf->pixels, h, surf->pitch, surf->h, flipped); + for (w = 0; w < surf->w; ++w) { + color = *ptr++; + data[3] = (char)(((color & Rmask) >> Rshift) << Rloss); + data[2] = (char)(((color & Gmask) >> Gshift) << Gloss); + data[1] = (char)(((color & Bmask) >> Bshift) << Bloss); + data[0] = (char)(Amask ? (((color & Amask) >> Ashift) + << Aloss) + : 255); + data += 4; + } + pad(&data, padding); + } + break; + case 3: + for (h = 0; h < surf->h; ++h) { + Uint8 *ptr = (Uint8 *)DATAROW(surf->pixels, h, surf->pitch, + surf->h, flipped); + for (w = 0; w < surf->w; ++w) { +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + color = ptr[0] + (ptr[1] << 8) + (ptr[2] << 16); +#else + color = ptr[2] + (ptr[1] << 8) + (ptr[0] << 16); +#endif + ptr += 3; + data[3] = (char)(((color & Rmask) >> Rshift) << Rloss); + data[2] = (char)(((color & Gmask) >> Gshift) << Gloss); + data[1] = (char)(((color & Bmask) >> Bshift) << Bloss); + data[0] = (char)(Amask ? (((color & Amask) >> Ashift) + << Aloss) + : 255); + data += 4; + } + pad(&data, padding); + } + break; + case 4: + for (h = 0; h < surf->h; ++h) { + Uint32 *ptr = (Uint32 *)DATAROW( + surf->pixels, h, surf->pitch, surf->h, flipped); + for (w = 0; w < surf->w; ++w) { + color = *ptr++; + data[3] = (char)(((color & Rmask) >> Rshift) << Rloss); + data[2] = (char)(((color & Gmask) >> Gshift) << Gloss); + data[1] = (char)(((color & Bmask) >> Bshift) << Bloss); + data[0] = (char)(Amask ? (((color & Amask) >> Ashift) + << Aloss) + : 255); + data += 4; + } + pad(&data, padding); + } + break; + } + pgSurface_Unlock(surfobj); + } else if (!strcmp(format, "RGBA_PREMULT")) { pgSurface_Lock(surfobj); switch (PG_SURF_BytesPerPixel(surf)) { @@ -1224,6 +1301,32 @@ image_frombytes(PyObject *self, PyObject *arg, PyObject *kwds) } SDL_UnlockSurface(surf); } + else if (!strcmp(format, "ABGR")) { + if (pitch == -1) { + pitch = w * 4; + } + else if (pitch < w * 4) { + return RAISE(PyExc_ValueError, + "Pitch must be greater than or equal to the width * " + "4 as per the format"); + } + + if (len != (Py_ssize_t)pitch * h) + return RAISE( + PyExc_ValueError, + "Bytes length does not equal format and resolution size"); + surf = PG_CreateSurface(w, h, SDL_PIXELFORMAT_ABGR32); + if (!surf) + return RAISE(pgExc_SDLError, SDL_GetError()); + SDL_LockSurface(surf); + for (looph = 0; looph < h; ++looph) { + Uint32 *pix = (Uint32 *)DATAROW(surf->pixels, looph, surf->pitch, + h, flipped); + memcpy(pix, data, w * sizeof(Uint32)); + data += pitch; + } + SDL_UnlockSurface(surf); + } else return RAISE(PyExc_ValueError, "Unrecognized type of format"); diff --git a/test/image_test.py b/test/image_test.py index e412c40508..a49c83af6c 100644 --- a/test/image_test.py +++ b/test/image_test.py @@ -570,7 +570,7 @@ def test_frombytes__and_tobytes(self): import itertools - fmts = ("RGBA", "ARGB", "BGRA") + fmts = ("RGBA", "ARGB", "BGRA", "ABGR") fmt_permutations = itertools.permutations(fmts, 2) fmt_combinations = itertools.combinations(fmts, 2)