Skip to content

A way to replace multiple Surface colors #2929

Open
@itzpr3d4t0r

Description

@itzpr3d4t0r

With the recent addition of transform.hsl() (#2398) we now have 5 ways to interact with pixels from a surface, that are:

  • Surface.get_at() / Surface.set_at()
  • Surface.blit() with different blend flags
  • using surfarrays and numpy
  • transform.hsl()
  • Surface.fill()

While these options cover most needs, there's still room for improvement. Specifically, there's no built-in, easy, and fast way to replace all occurrences of one color with another on a surface:

  • Using get_at/set_at is incredibly slow.
  • Tinting a surface with a color using .blit() isn't the desired effect.
  • Creating a mask and then blitting with alpha can work, but it's a multi-stage process that hinders performance, especially if you need to replace multiple colors.
  • transform.hsl shifts all pixels, potentially changing some that we don't want
  • Surface.fill covers the entire surface or a sub-rect, so it's not really what we want again

These methods either compromise performance, ease of use or miss the point completely. Therefore, I propose adding a new method/function that can replace a set of colors with another set of corresponding colors:

  • Surface.replace_colors(sequence: Sequence[Tuple[ColorValue, ColorValue]])
  • pygame.transform.replace_colors(sequence: Sequence[Tuple[ColorValue, ColorValue]], dest: Optional[Surface] = None)

So you could have something like this:

# would replace all red pixels with green and black with white
surf.replace_colors([((255, 0, 0), (0, 255, 0)), ((0, 0, 0), (255, 255, 255))]  

Which could be a really cool addition to quickly change the look of your sprites and an easy way.

My internal tests show that a custom C implementation of this feature would be:

  • 500 times faster than a naive get_at/set_at strategy
  • 20 times faster than a surfarray strategy without SIMD, and 100 times faster with an AVX2 implementation
  • 2500 times faster than a naive get_at/set_at strategy if using AVX2

There have also been discussions about the need for these operations, indicating a genuine demand for such a feature:
https://discord.com/channels/772505616680878080/1245854714504024104

Metadata

Metadata

Assignees

No one assigned

    Labels

    New APIThis pull request may need extra debate as it adds a new class or function to pygameSurfacepygame.Surfacetransformpygame.transform

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions