3
\$\begingroup\$

I'm a bit puzzled about pygame's Surface.convert():

It's common sense that I should convert a surface after loading an image to it (presumably a jpg/png/etc file), but what about surfaces that I only use pygame's "primitives" like pygame.draw.circle() or Surface.fill()?

Official documentation is very vague about it: it says "fastest format for blitting. It is a good idea to convert all Surfaces before they are blitted many times".

Consider this simple ball-bouncing code:

import pygame

position = [100, 200]
velocity = [7, 17]
radius = 50

pygame.init()
clock = pygame.time.Clock()

screen = pygame.display.set_mode((800, 600))
background = pygame.Surface(screen.get_size())
screen.blit(background, (0,0))

ball = pygame.Surface(2*[radius*2])
pygame.draw.circle(ball, (0, 255, 0), 2*(radius,), radius)
bounds = (screen.get_size()[0] - 2 * radius,
          screen.get_size()[1] - 2 * radius)
rect = ball.get_rect()
rect.x = position[0]
rect.y = position[1]

done = False
while not done:
    for event in pygame.event.get():
        if event.type in [pygame.QUIT, pygame.KEYDOWN]:
            done = True

    for i in [0, 1]:
        position[i] += velocity[i]
        if position[i] < 0 or position[i] > bounds[i]:
            velocity[i] *= -1

    screen.blit(background, rect)
    rect.x = position[0]
    rect.y = position[1]
    screen.blit(ball, rect)
    pygame.display.update()
    clock.tick(60)

pygame.quit()

It has 3 surfaces: screen, background and ball. They are blitted a lot, as source or dest. Would any of these surfaces benefit from a .convert()?

If yes, then should I simply append a .convert() when creating any surface? Like

surface = pygame.Surface(...).convert()

A lot of pygame tutorials do that, even for a background that is just .fill()'ed. But this feels so... redundant. Why isn't the so called "fastest pixel format" the default when creating new surfaces?

And if such surfaces would benefit from a convert, when should I do so? After a .fill(), before, or it doesn't matter? Is once enough or should I "reconvert" after each draw? What if I load an image?

References and sources about proper .convert() usage are highly appreciated, thanks!

\$\endgroup\$
8
\$\begingroup\$

convert() is used to convert the pygame.Surface to the same pixel format as the one you use for final display, the same one created from pygame.display.set_mode(). If you don't call it, then every time you blit a surface to your display surface, a pixel conversion will be needed - this is a per pixel operation, very slow - instead of a series of memory copies. You may not feel the difference on your octa-core development PC, but when you're on constrained devices like handhelds (or just bring up your CPU counter), it makes a big difference. The recommendation is to do it as early as possible, preferably when you load or create your assets, or at least outside your game loop.

Why don't surfaces match the display by default? Good question, as it's an obvious newbie trap. I don't know the considerations that went into this design, but one issue is that you don't know until run time what your display pixel format will be. The user might specify 16-bit pixels and it might end up as some wacky format like BGR565. If you do some image processing at low bit depths, you might get results that you weren't expecting.

Another issue is that your display format most likely doesn't have alpha. If all surfaces matched the display format by default, then every time you try to blit semi-transparent surfaces, or ones loaded from PNGs or GIFs with transparent pixels, you end up with images over a black rectangle. If you need transparency, you can use convert_alpha(), which is marginally faster than unconverted but not nearly as fast as convert() - alpha blending is expensive. Pygame can't tell whether you need alpha surfaces or not, so you need to specify yourself.

Of course all this may be moot because if you really cared about performance, you would use

  • a faster language than Python
  • a framework that was GPU accelerated (instead of pygame which is over SDL 1.X)
\$\endgroup\$
1
\$\begingroup\$

The official pygame docs say:

http://www.pygame.org/docs/ref/surface.html#pygame.Surface.convert

It is a good idea to convert all Surfaces before they are blitted many times. The converted Surface will have no pixel alphas. They will be stripped if the original had them

So, as far as I can tell you should always .convert() them when you are going to blit them in the main loop, but it might cause some alpha problems (never happened to me).

If you try to draw many images on the screen at the same time you can see the performance difference by converting or not those.

Hope that helps.

\$\endgroup\$
  • 1
    \$\begingroup\$ Thanks for the help, but you're just quoting the same sentence I've already cited (using the same URL I've mentioned too). I see no improvement by converting background even if I screen.blit(background, (0,0)) on every frame! \$\endgroup\$ – MestreLion Aug 3 '14 at 22:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.