6

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):

pattern.jpg

KbiR
  • 4,047
  • 6
  • 37
  • 103
BradMcDanel
  • 543
  • 3
  • 15
  • Just a guess, but according to the [documentation](https://www.pygame.org/docs/ref/surface.html#pygame.Surface.blits) you're supposed to pass in a variadic number of tuples, and not a list of tuples. Does it work if you unpack the list in the method call? `screen.blits(*blit_list)` – Ted Klein Bergman Jan 19 '19 at 20:02
  • That's a good guess -- but it doesn't seem that the function is actually variadic. If I do this I get: `TypeError: function takes at most 2 arguments (16 given)` – BradMcDanel Jan 19 '19 at 20:08

1 Answers1

3

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.

Jacques Gaudin
  • 15,779
  • 10
  • 54
  • 75