I am trying to use surface.blits with the area parameter to improve the performance of my code. When I use the area parameter for blits, I run into the following error:
SystemError: <'method 'blits' of 'pygame.Surface' objects> returned a result with an error set.
If I remove the area parameter, blits work as I would expect. Any ideas on what I could be doing wrong? Attached below is an example code my use case and error.
import sys
import random
import pygame
pygame.init()
tilemap = pygame.image.load('pattern.jpg')
tilesize = 64
size = 4
w, h = size*64, size*64
screen = pygame.display.set_mode((w, h))
while True:
screen.fill((0, 0, 0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
blit_list = []
for i in range(size):
for j in range(size):
xi, yi = random.randint(0, size), random.randint(0, size)
blit_args = (tilemap, (i*tilesize, j*tilesize),
(xi*tilesize, yi*tilesize, tilesize, tilesize))
# calling screen.blit here works correctly
screen.blit(*blit_args)
blit_list.append(blit_args)
# instead of using multiple blit calls above, calling screen.blits here fails
# remove the area argument (3rd arg) from each blit_arg tuple works
# screen.blits(blit_list)
pygame.display.flip()
# wait a second
pygame.time.wait(1000)
Here is the image I used (https://www.behance.net/gallery/19447645/Summer-patterns):
blit() — blit stands for Block Transfer—and it's going to copy the contents of one Surface onto another Surface . 00:17 The two surfaces in question are the screen that you created and the new Surface . So, . blit() will take that rectangular Surface and put it on top of the screen.
We need to get this surface (background) and draw it onto the window. To do this we will call screen. blit(background,(x,y)) where (x,y) is the position inside the window where we want the top left of the surface to be. This function says take the background surface and draw it onto the screen and position it at (x,y).
Creating a surface Creating surfaces in pygame is quite easy. We just have to pass the height and the width with a tuple to pygame. Surface() method. We can use various methods to format our surface as we want.
The screen object represents your game screen. It is a thin wrapper around a Pygame surface that allows you to easily draw images to the screen (“blit” them).
This is a bug in the C code. In surface.c line 2258, for surf_blits
there is the following test:
if (dest->flags & SDL_OPENGL &&
!(dest->flags & (SDL_OPENGLBLIT & ~SDL_OPENGL))) {
bliterrornum = BLITS_ERR_NO_OPENGL_SURF;
goto bliterror;
}
Whereas in surface.c line 2118, for surf_blit
the code is:
#if IS_SDLv1
if (dest->flags & SDL_OPENGL &&
!(dest->flags & (SDL_OPENGLBLIT & ~SDL_OPENGL)))
return RAISE(pgExc_SDLError,
"Cannot blit to OPENGL Surfaces (OPENGLBLIT is ok)");
#endif /* IS_SDLv1 */
Notice the #if IS_SDLv1
.
The problem seems to come from SDL_OPENGLBLIT
which is now deprecated.
Do not use the deprecated SDL_OPENGLBLIT mode which used to allow both blitting and using OpenGL. This flag is deprecated, for quite a few reasons. Under numerous circumstances, using SDL_OPENGLBLIT can corrupt your OpenGL state.
Unfortunately, I am no expert at OpenGL and I am not able to explain further. Hopefully someone can post a more precise answer.
What I know for sure is that I can raise the BLITS_ERR_SEQUENCE_SURF
just before (by giving a pygame.Rect
as the first object in blit_args
for example) and I am unable to raise BLITS_ERR_INVALID_DESTINATION
just after.
This leads me to think something is going on with the lines above.
EDIT
I can confirm that if I add #if IS_SDLv1
around the test above and recompile pygame, it works. No idea why though! ☺
I raised the issue on GitHub.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With