pygame直接修改Surface像素的alpha

Modify alpha of Surface pixels directly in pygame

我在 PyGame 中有一个 Surface。我想直接修改像素的 alpha 值。我已经尝试使用访问 alpha 值的各种方法来实现它,但它们似乎不起作用。

from screeninfo import get_monitors
import pygame, os, numpy.random, pygame.surfarray

pygame.init()
FPS = 60
CLOCK = pygame.time.Clock()
monitor_info = get_monitors()
x = 0
y = 0
width = monitor_info[0].width
height = monitor_info[0].height
if len(monitor_info) > 1:
    if monitor_info[0].x < 0:
        x = 0
    else:
        x = monitor_info[0].width
    width = monitor_info[1].width
    height = monitor_info[1].height

os.environ['SDL_VIDEO_WINDOW_POS'] = "{},0".format(x)
pygame.display.init()
pygame.mouse.set_visible(False)
screen_info = pygame.display.Info()
screen_size = width, height
base_screen = pygame.display.set_mode(screen_size, pygame.NOFRAME)
base_screen.fill([100, 100, 100])
board_size = (int(min(width, height)*0.75), int(min(width, height)*0.75))
surface = pygame.Surface(board_size, pygame.SRCALPHA)
surface.fill([255, 255, 255, 255])
base_screen.blit(surface, (0,0))
pygame.display.flip()
pixels = numpy.random.uniform(low=0, high=255, size=(board_size[0], board_size[1], 3))
transparency = numpy.random.uniform(low=0, high=255, size=board_size).astype('uint8')
while True:
    events = pygame.event.get()
    ms = CLOCK.tick(FPS)
    print('\r             {}                  '.format(ms), end='')
    # pygame.surfarray.blit_array(surface, pixels)
    aa = pygame.surfarray.pixels_alpha(surface)
    aa = numpy.random.uniform(low=0, high=255, size=board_size).astype('uint8')
    del aa
    # for i in range(board_size[0]):
    #     for j in range(board_size[1]):
    #         a = surface.get_at((i,j))
    #         a[3] = 0
    #         surface.set_at((i,j), a)
    base_screen.blit(surface, (0,0))
    pygame.display.flip()

我在循环中尝试了这两种方法(pixels_array 和 get_at/set_at),但都不起作用——图像只是保持白色(如果我将初始 alpha 设置为 0,它会保持透明的)。有谁知道如何为 Surface 设置每像素 alpha 值?

我发现了你的问题!!看不到alpha的原因是:

a) 你首先将 surface alpha 设置为 255, surface.fill([255, 255, 255, 255])

b) 我相信 aa = pygame.surfarray.pixels_alpha(surface) aa = numpy.random.uniform(low=0, high=255, size=board_size).astype('uint8') 不起作用,但是 pygame.surfarray.blit_array(surface, pixels) 确实起作用(产生颜色)但我认为它们没有任何实际的 Alpha。

c) 你需要填充 base_screen 和 THEN blit 你的表面。这是一个常见的错误,但这是主要问题。

最后,蒂姆·罗伯特 (Tim Robert) 关于 for 循环的评论,一定会让您大开眼界!

这里是重写的(没有 screeninfo 因为我目前没有那个库):

import pygame, os, numpy.random, pygame.surfarray
from random import randint

pygame.init()
FPS = 60
CLOCK = pygame.time.Clock()
x = 50
y = 50
width = 500
height = 500

os.environ['SDL_VIDEO_WINDOW_POS'] = "{},0".format(x)
#pygame.display.init(), don't actually need this
pygame.mouse.set_visible(False)
screen_info = pygame.display.Info()
screen_size = width, height
base_screen = pygame.display.set_mode(screen_size, pygame.NOFRAME)
base_screen.fill([100, 100, 100])
board_size = (int(min(width, height)*0.75), int(min(width, height)*0.75))
surface = pygame.Surface(board_size, pygame.SRCALPHA)
surface.fill([255, 0, 0]) ###could also be surface.fill([255,0,0,255]) to set the whole surface's alpha straight up if you didn't want to change each pixel later

while True:
    #just so you can quit this program
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            raise SystemExit()
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_q:
                pygame.quit()
                raise SystemExit()
                
    ms = CLOCK.tick(FPS)

    for i in range(board_size[0]):
         for j in range(board_size[1]):
             a = surface.get_at((i,j))
             a[3] = randint(0, 128)#OR SET TO REQUIRED ALPHA VALUE
             surface.set_at((i,j), a)

    ##################################################
    base_screen.fill([100, 100, 100]) #NEED TO DO THIS
    ##################################################

    base_screen.blit(surface, (0,0))
    pygame.display.flip()

(顺便说一句,我使用红色作为第二表面颜色,因为我认为它会更突出)

编辑

正如 Eternal Ambiguity 在评论中所述,这里是 much 更快的版本,代替了 for 循环:

aa = pygame.surfarray.pixels_alpha(surface)
aa[:] = numpy.random.uniform(low=0, high=255, size=board_size).astype('uint8')
del aa

pygame.Surface.blit() 不会用源表面替换目标表面中的像素。它混合(混合)表面的像素。实际上,您在每一帧都将新表面与以前的旧表面混合。这意味着表面区域会在几毫秒内呈现出均匀的颜色。每一帧都要清屏:

while True:
    # [...]

    base_screen.fill((0, 0, 0))
    base_screen.blit(surface, (0,0))
    pygame.display.flip()

最小示例:

import pygame

pygame.init()

width, height = 200, 200
base_screen = pygame.display.set_mode((width, height))
clock = pygame.time.Clock()

board_size = (int(min(width, height)*0.75), int(min(width, height)*0.75))
surface = pygame.Surface(board_size, pygame.SRCALPHA)
surface.fill([255, 0, 0])

for x in range(board_size[0]):
    for y in range(board_size[1]):
        a = surface.get_at((x, y))
        a[3] = round(y / board_size[1] * 255)
        surface.set_at((x, y), a)

run = True
while run:
    clock.tick(100)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
                
    base_screen.fill((100, 100, 100))
    base_screen.blit(surface, surface.get_rect(center = base_screen.get_rect().center))
    pygame.display.flip()