python - functions - Cómo enmascarar eficazmente una superficie en pygame
pygame rect (1)
Estoy enmascarando una superficie en pygame como lo sugirió nkorth en respuesta a la pregunta : ¿hay alguna manera de modificar o actualizar solo en una máscara , actualizando la máscara y las superficies enmascaradas de cada fotograma? Y si bien es posible lograr esto utilizando su método, la velocidad de cuadros cae inmediatamente, y eso es solo con una instancia de esta superficie representada en la pantalla en cualquier momento.
Aquí está la imagen que quiero enmascarar, y así es como debería verse en la aplicación. Estos otros círculos no son importantes, se crean de forma secuencial y se dibujan unos encima de otros. Intenté comprimir la imagen a 100 kb (más de 10 veces su tamaño original), pero eso solo ayudó un poco. Todavía funciona mal.
¿Es esto posible en pygame? Si es así, ¿cómo?
Aquí está la clase respectiva para esto:
class NoiseCircle(Circle):
def __init__(self, center=SCREEN_CENTER, radius=5):
# init code
...
def draw(self, surf):
self.masked.blit(self.noise_image, (0, 0))
self.mask.fill((0, 0, 0, 0))
pygame.draw.circle(self.mask, (255, 255, 255), self.center, self.radius)
self.masked.blit(self.mask, (0, 0), None, pygame.BLEND_RGBA_MULT)
pygame.draw.circle(self.masked, (0, 0, 0), self.center, self.radius, 1)
surf.blit(self.masked, (0, 0))
El bucle principal pasa la superficie de visualización para dibujar () y eso se ocupa de la representación del objeto.
Es difícil saber dónde está el cuello de botella en su programa. Probablemente sea draw.circle
. Prueba con mi código, implementa mi propio algoritmo de dibujo circular (nota, no es un círculo preciso). Probado con pygame 1.9.2 en Windows, computadora bastante lenta - Atom 1.6 GHz, y me da alrededor de 40 milisegundos (vea DT en el código). Luego pruebe su implementación y vea si es más rápido o más lento. Sería interesante comparar Por cierto, ¿qué hay de usar colorkey?
def circle(r, x0, y0, dest, value):
x1 = int(r / math.sqrt(2))
h = []
x = x1
while(1):
x += 1
if x == r:
h.append(ch/3)
break
ch = int(math.sqrt(r**2-x**2))
h.append(ch)
p = 0
dest[x0-x1:x0+x1+1, y0-x1:y0+x1+1] = value
while p < len(h):
dest[x0+x1+p+1, y0-h[p]:y0+h[p]+1] = value
dest[x0-x1-p-1, y0-h[p]:y0+h[p]+1] = value
dest[x0-h[p]:x0+h[p]+1, y0-x1-p-1] = value
dest[x0-h[p]:x0+h[p]+1, y0+x1+p+1] = value
p += 1
def put_alpha(Dest, Src): # write alpha values
ref = pygame.surfarray.pixels_alpha (Dest)
numpy.copyto(ref, Src)
del ref
Layer = pygame.Surface ((w,h),flags = pygame.SRCALPHA)
Layer.fill(0xff4B432E)
Mask = numpy.zeros((w,h), dtype = numpy.uint8)
Mask[:] = 255
cur_time = pygame.time.get_ticks()
old_time = cur_time
circle(125, 400, 300, Mask, 125)
circle(75, 400, 300, Mask, 255)
put_alpha(Layer, Mask)
cur_time = pygame.time.get_ticks()
DT = cur_time - old_time
print DT
Actualización : Primero, la función de círculo personalizado funciona ca 5 veces más lento que el de pygame, por lo que no es lo que puede ralentizar las cosas. He resumido algunos resultados de rendimiento con varias variantes. En primer lugar, asegúrese de utilizar superficies de 24 bits en todas partes, a menos que desee una transparencia total de 256 grados, esto solo puede dar mejores resultados. Si ejecuta el siguiente ejemplo, la salida debe dar "24 32 24". Esas son profundidades de bit de superficies correspondientes. Esto sería óptimo para este caso particular.
pygame.init()
w = 800
h = 600
DISP = pygame.display.set_mode((w, h), 0, 24)
clock = pygame.time.Clock( )
Noise = pygame.Surface ((w,h))
Noise.fill((110,0,10))
color24bit = (169, 163, 144) # r g b
color = (169, 163, 144, 255) # r g b a
color_a = (169, 163, 144, 112) # r g b a
Layer = pygame.Surface ((w,h), flags = pygame.SRCALPHA)
Layer.fill(color) # ( 169, 163, 144, 255)
Layer_colorkey = pygame.Surface ((w,h))
Layer_colorkey.fill(color24bit)
color_key = (50, 50, 50)
Layer_colorkey.set_colorkey(color_key)
print Noise.get_bitsize(), Layer.get_bitsize(), Layer_colorkey.get_bitsize()
Mask = numpy.zeros((w,h), dtype = numpy.uint8)
Mask[:] = 255
t=0
cur_time = pygame.time.get_ticks()
old_time = cur_time
# 1. blit with per-pixel alpha and custom Mask array
while t < 0:
circle(296, 400, 300, Mask, 112)
circle(15, 400, 300, Mask, 255)
put_alpha(Layer, Mask)
Noise.blit(Layer,(0,0))
DISP.blit(Noise,(0,0))
t += 1
# 2. blit with per-pixel alpha and draw functions
while t < 0:
pygame.draw.circle(Layer, color_a, (400,300), 296, 0)
pygame.draw.circle(Layer, color, (400,300), 15, 0)
Noise.blit(Layer,(0,0))
DISP.blit(Noise,(0,0))
t += 1
# 3. blit with colorkey
while t < 1:
pygame.draw.circle(Layer_colorkey, color_key, (400,300), 296, 0)
pygame.draw.circle(Layer_colorkey, color, (400,300), 15, 0)
Noise.blit(Layer_colorkey,(0,0))
DISP.blit(Noise,(0,0))
t += 1
cur_time = pygame.time.get_ticks()
DT = cur_time - old_time
print "time:", DT
Conclusión:
1. Alfa completo con la matriz de máscaras personalizada aplicada: más lento, pero usted tiene el control total de la forma de la máscara y puede hacer efectos geniales con transparencia.
2. Full alpha + solo usa las funciones de dibujo integradas, simplemente define el color con el valor alpha necesario. Un poco más rápido (ITPC), pero no hay control directo sobre los valores de color.
3. Superficie normal de 24 bits con colorkey. 2 veces más rápido que el anterior, pero sin transparencia personalizada.