pygame-community / pygame-ce

🐍🎮 pygame - Community Edition is a FOSS Python library for multimedia applications (like games). Built on top of the excellent SDL library.
https://pyga.me
862 stars 141 forks source link

premul_alpha doesn't respect weird surface pitches #2750

Open Starbuck5 opened 6 months ago

Starbuck5 commented 6 months ago

I found this while reviewing the code of #2615

If the surface pitch is not aligned to 4 bytes, it doesn't properly skip through rows.

For example:

import pygame

surf_height = 5

def create_surface_from_byte_width(byte_width):
    byte_data = bytes(byte_width * surf_height)
    surf_width = byte_width // 4

    dest = pygame.image.frombuffer(
        byte_data, (surf_width, surf_height), "RGBA", pitch=byte_width
    )
    dest.fill((120, 50, 70, 200))
    return dest

dest = create_surface_from_byte_width(8)  # 2 pixels wide
print("Surface 1: pitch =", dest.get_pitch())
print("Before premul:", dest.get_at((dest.get_width() - 1, dest.get_height() - 1)))
dest = dest.premul_alpha()
print("After premul:", dest.get_at((dest.get_width() - 1, dest.get_height() - 1)))

dest = create_surface_from_byte_width(10)  # 2.5 pixels wide
print("Surface 2: pitch =", dest.get_pitch())
print("Before premul:", dest.get_at((dest.get_width() - 1, dest.get_height() - 1)))
dest = dest.premul_alpha()
print("After premul:", dest.get_at((dest.get_width() - 1, dest.get_height() - 1)))
Surface 1: pitch = 8
Before premul: Color(120, 50, 70, 200)
After premul: Color(94, 39, 55, 200)
Surface 2: pitch = 10
Before premul: Color(120, 50, 70, 200)
After premul: Color(0, 0, 0, 0)

The final pixels of the 10-byte wide surface are never reached by the premultiply algorithm.