Refactor _img_sdl2.pyx load_from_surface (cleanup)

* Fix loading of SDL_PIXELFORMAT_BGR24 surfaces (BMP2, BMP3)
* Fix loading of (some) PNG formats with binary alpha
* Fix byte order for big-endian arch (untested)

More info: 
This commit is contained in:
Terje Skjaeveland 2017-07-23 17:29:52 +02:00 committed by Gabriel Pettier
parent ae7646c641
commit a4442a9795
2 changed files with 72 additions and 41 deletions
kivy

View File

@ -58,57 +58,83 @@ def save(filename, w, h, fmt, pixels, flipped):
IMG_SavePNG(image, c_filename)
SDL_FreeSurface(image)
# NOTE: This must be kept up to date with ImageData supported formats. If you
# add support for converting/uploading (for example) ARGB, you must ensure
# that it is returned unmodified below to avoid converting to RGB/RGBA.
#
# FIXME:
# - Some PNG48 images with binary transparency load incorrectly
# - (Some?) 8/16 bit grayscale PNGs load incorrectly (likely SDL_Image problem)
cdef load_from_surface(SDL_Surface *image):
cdef SDL_Surface *image2 = NULL
cdef SDL_Surface *fimage = NULL
cdef SDL_PixelFormat pf
cdef int want_rgba = 0, want_bgra = 0, target_fmt = 0, n = 0
cdef bytes pixels
try:
if image == NULL:
return None
if image == NULL:
Logger.warn('ImageSDL2: load_from_surface() with NULL surface')
return None
fmt = ''
if image.format.BytesPerPixel == 3:
fmt = 'rgb'
elif image.format.BytesPerPixel == 4:
fmt = 'rgba'
# SDL 2.0.5 now has endian-agnostic 32-bit pixel formats like RGB24,
# but we can't count on that yet (RGB24 is available since 2.0)
if SDL_BYTEORDER == SDL_BIG_ENDIAN:
want_rgba = SDL_PIXELFORMAT_RGBA8888
want_bgra = SDL_PIXELFORMAT_BGRA8888
else:
want_rgba = SDL_PIXELFORMAT_ABGR8888
want_bgra = SDL_PIXELFORMAT_ARGB8888
# FIXME the format might be 3 or 4, but it doesn't mean it's rgb/rgba.
# It could be argb, bgra etc. it needs to be detected correctly. I guess
# we could even let the original pass, bgra / argb support exists in
# some opengl card.
# Output format (string) - supported by ImageData. If the surface is
# already in a supported pixel format, no conversion is done.
fmt = ''
if fmt not in ('rgb', 'rgba'):
if fmt == 'rgb':
pf.format = SDL_PIXELFORMAT_BGR888
fmt = 'rgb'
else:
pf.format = SDL_PIXELFORMAT_ABGR8888
# 32-bit rgba and bgra can be used directly
if image.format.format == want_rgba:
fmt = 'rgba'
elif image.format.format == want_bgra:
fmt = 'bgra'
# Alpha mask or colorkey must be converted to rgba
elif image.format.Amask or SDL_GetColorKey(image, NULL) == 0:
fmt = 'rgba'
target_fmt = want_rgba
# Palette with alpha is converted to rgba
elif image.format.palette != NULL:
for n in xrange(0, image.format.palette.ncolors):
if image.format.palette.colors[n].a < 0xFF:
fmt = 'rgba'
target_fmt = want_rgba
break
image2 = SDL_ConvertSurfaceFormat(image, pf.format, 0)
# 24bpp RGB/BGR without colorkey can be used directly.
elif image.format.format == SDL_PIXELFORMAT_RGB24:
fmt = 'rgb'
elif image.format.format == SDL_PIXELFORMAT_BGR24:
fmt = 'bgr'
# Everything else is converted to RGB
if not fmt:
fmt = 'rgb'
target_fmt = SDL_PIXELFORMAT_RGB24
# Convert if needed, and return a copy of the raw pixel data
try:
fimage = image
if target_fmt != 0:
with nogil:
image2 = SDL_ConvertSurfaceFormat(image, target_fmt, 0)
if image2 == NULL:
return
fimage = image2
else:
if (image.format.Rshift > image.format.Bshift):
memset(&pf, 0, sizeof(pf))
pf.BitsPerPixel = 32
pf.Rmask = 0x000000FF
pf.Gmask = 0x0000FF00
pf.Bmask = 0x00FF0000
pf.Amask = 0xFF000000
image2 = SDL_ConvertSurface(image, &pf, 0)
fimage = image2
Logger.warn('ImageSDL2: error converting {} to {}: {}'.format(
SDL_GetPixelFormatName(image.format.format),
SDL_GetPixelFormatName(target_fmt),
SDL_GetError()))
return None
else:
fimage = image
fimage = image2
pixels = (<char *>fimage.pixels)[:fimage.pitch * fimage.h]
return (fimage.w, fimage.h, fmt, pixels, fimage.pitch)
finally:
if image2:
SDL_FreeSurface(image2)

View File

@ -28,11 +28,12 @@ cdef extern from "SDL.h":
int SDL_WINDOWPOS_UNDEFINED
ctypedef enum:
SDL_PIXELFORMAT_BGRA8888
SDL_PIXELFORMAT_ARGB8888
SDL_PIXELFORMAT_RGBA8888
SDL_PIXELFORMAT_RGB888
SDL_PIXELFORMAT_ABGR8888
SDL_PIXELFORMAT_BGR888
SDL_PIXELFORMAT_RGB24
SDL_PIXELFORMAT_BGR24
ctypedef enum SDL_GLattr:
SDL_GL_RED_SIZE
@ -481,10 +482,9 @@ cdef extern from "SDL.h":
cdef int SDL_SetTextureBlendMode(SDL_Texture * texture, SDL_BlendMode blendMode)
cdef int SDL_GetTextureBlendMode(SDL_Texture * texture, SDL_BlendMode *blendMode)
cdef SDL_Surface * SDL_CreateRGBSurfaceFrom(void *pixels, int width, int height, int depth, int pitch, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask)
cdef SDL_Surface* SDL_ConvertSurface(SDL_Surface* src, SDL_PixelFormat* fmt, Uint32 flags)
cdef SDL_Surface* SDL_ConvertSurfaceFormat(SDL_Surface* src, Uint32
pixel_format, Uint32 flags)
cdef SDL_Surface* SDL_ConvertSurfaceFormat(SDL_Surface* src, Uint32 pixel_format, Uint32 flags) nogil
cdef const char* SDL_GetPixelFormatName(Uint32 format)
cdef int SDL_GetColorKey(SDL_Surface *surface, Uint32 *key)
cdef int SDL_Init(Uint32 flags)
cdef void SDL_Quit()
cdef int SDL_EnableUNICODE(int enable)
@ -627,6 +627,11 @@ cdef extern from "SDL.h":
Uint16 AUDIO_F32MSB #0x9120 /**< As above, but big-endian byte order */
Uint16 AUDIO_F32 #AUDIO_F32LSB
# Endianness
Uint16 SDL_BYTEORDER
Uint16 SDL_LIL_ENDIAN
Uint16 SDL_BIG_ENDIAN
cdef extern from "SDL_shape.h":
cdef SDL_Window * SDL_CreateShapedWindow(
char *title,