Drawing non pixelated circles in pygame [duplicate] - python

Like pygame.draw.circle(Surface, color, pos, radius, width), I want to get antialiased circle.
But, there is no option about width on pygame.gfxdraw.aacircle().
Anyone knows alternative way?

Neither the pygame.draw module nor the
pygame.gfxdraw module provide a function for an antialiased circle with an scalable thickness. While pygame.draw.circle() can draw a circle with different width, pygame.gfxdraw.circle() can draw a thin, antialiased circle.
You can try to stitch the circle with a pygame.draw.circle() for the body and pygame.gfxdraw.circle() on the edges. However, the quality is low and can depend on the system:
def drawAACircle(surf, color, center, radius, width):
pygame.gfxdraw.aacircle(surf, *center, 100, color)
pygame.gfxdraw.aacircle(surf, *center, 100-width, color)
pygame.draw.circle(surf, color, center, radius, width)
I recommend drawing a image with an antialiased circle and blit the image. You can create the image using OpenCV (opencv-python). See OpenCV - Drawing Functions.
Minimal example:
import pygame
import cv2
import numpy
def drawAACircle(surf, color, center, radius, width):
circle_image = numpy.zeros((radius*2+4, radius*2+4, 4), dtype = numpy.uint8)
circle_image = cv2.circle(circle_image, (radius+2, radius+2), radius-width//2, (*color, 255), width, lineType=cv2.LINE_AA)
circle_surface = pygame.image.frombuffer(circle_image.flatten(), (radius*2+4, radius*2+4), 'RGBA')
surf.blit(circle_surface, circle_surface.get_rect(center = center))
pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window.fill((32, 32, 32))
drawAACircle(window, (255, 0, 0), window.get_rect().center, 100, 20)
pygame.display.flip()

Related

In Pygame, how would I fill a drawn circle with an image instead of a colour? [duplicate]

I need to create a surface that has a bounding circle. Anything drawn on that surface should not be visible outside that bounding circle. I've tried using masks, subsurfaces, srcalpha, etc., but nothing seems to work.
My attempt:
w = ss.get_width ()
h = ss.get_height ()
TRANSPARENT = (255, 255, 255, 0)
OPAQUE = ( 0, 0, 0, 225)
crop = pygame.Surface ((w, h), pygame.SRCALPHA, ss)
crop.fill (TRANSPARENT)
c = round (w / 2), round (h / 2)
r = 1
pygame.gfxdraw. aacircle (crop, *c, r, OPAQUE)
pygame.gfxdraw.filled_circle (crop, *c, r, OPAQUE)
ss = crop.subsurface (crop.get_rect ())
App.set_subsurface (self, ss)
Later...
self.ss.fill (BLACK)
self.ss.blit (self.background, ORIGIN)
The background is a square image. It should be cropped into the shape of a circle and rendered on screen
Solution based on notes from Rabbid76:
def draw_scene (self, temp=None):
if temp is None: temp = self.ss
# 1. Draw everything on a surface with the same size as the window (background and scene).
size = temp.get_size ()
temp = pygame.Surface (size)
self.draw_cropped_scene (temp)
# 2. create the surface with the white circle.
self.cropped_background = pygame.Surface (size, pygame.SRCALPHA)
self.crop ()
# 3. blit the former surface on the white circle.
self.cropped_background.blit (temp, ORIGIN, special_flags=pygame.BLEND_RGBA_MIN)
# 4. blit the whole thing on the window.
self.ss.blit (self.cropped_background, ORIGIN)
def draw_cropped_scene (self, temp): App.draw_scene (self, temp)
An example implementation of crop() is:
def crop (self):
o, bounds = self.bounds
bounds = tr (bounds) # round elements of tuple
pygame.gfxdraw. aaellipse (self.cropped_background, *bounds, *bounds, OPAQUE)
pygame.gfxdraw.filled_ellipse (self.cropped_background, *bounds, *bounds, OPAQUE)
The background is a square image. It should be cropped into the shape of a circle and rendered on screen
You can achieve this by using the blend mode BLEND_RGBA_MIN (see pygame.Surface.blit).
Create a transparent pygame.Surface with the same size as self.background. Draw a whit circle in the middle of the Surface and blend the background on this Surface using the blend mode BLEND_RGBA_MIN. Finally you can blit it on the screen:
size = self.background.get_size()
self.cropped_background = pygame.Surface(size, pygame.SRCALPHA)
pygame.draw.ellipse(self.cropped_background, (255, 255, 255, 255), (0, 0, *size))
self.cropped_background.blit(self.background, (0, 0), special_flags=pygame.BLEND_RGBA_MIN)
self.ss.fill(BLACK)
self.ss.blit(self.cropped_background, ORIGIN)
Minimal example: repl.it/#Rabbid76/PyGame-ClipCircularRegion-1
import pygame
pygame.init()
window = pygame.display.set_mode((250, 250))
background = pygame.Surface(window.get_size())
for x in range(5):
for y in range(5):
color = (255, 255, 255) if (x+y) % 2 == 0 else (255, 0, 0)
pygame.draw.rect(background, color, (x*50, y*50, 50, 50))
size = background.get_size()
cropped_background = pygame.Surface(size, pygame.SRCALPHA)
pygame.draw.ellipse(cropped_background, (255, 255, 255, 255), (0, 0, *size))
cropped_background.blit(background, (0, 0), special_flags=pygame.BLEND_RGBA_MIN)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window.fill(0)
window.blit(cropped_background, (0, 0))
pygame.display.flip()
See Also How to fill only certain circular parts of the window in pygame?

pygame, saving a image with low opacity

im making some images in my game and sometimes drawing colors with low opacity, but when i try to save it with;
pygame.image.save(the_img, the_name)
it's saves the image like i painted it with 255 opacity.
so how can i save a .png image that has colors with low opacity. in this image the blue color has 128 opacity (50%) but it's saves it like the opacity 255?
when i blit this image to my display it's shows it right but when i download it it's wrong??
The image format must be one that supports an alpha channel (e.g. PNG):
pygame.image.save(the_img, "my_image.png")
If you have a Surface with a color key (set_colorkey), you have to change the pixel format of the image including per pixel alphas with pygame.Surface.convert_alpha:
pygame.image.save(the_img.convert_alpha(), the_name)
If you have a surface with a global alpha value (set_alpha), you need to blit the Surface on a transparent Surface (pygame.SRCALPHA) of the same size and store that transparent Surface (this also works for a Surface with a color key):
the_img.set_colorkey((0, 0, 0))
the_img_alpha = pygame.Surface(the_img.get_size(), pygame.SRCALPHA)
the_img_alpha.blit(the_img, (0, 0))
pygame.image.save(the_img_alpha, the_name)
Minimla example:
import pygame
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
the_img = pygame.Surface((200, 200))
pygame.draw.circle(the_img, (255, 0, 0), (100, 100), 100)
the_img.set_alpha(127)
the_img.set_colorkey((0, 0, 0))
the_img_alpha = pygame.Surface(the_img.get_size(), pygame.SRCALPHA)
the_img_alpha.blit(the_img, (0, 0))
pygame.image.save(the_img_alpha, "the_img.png")
run = True
while run:
clock.tick(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window_center = window.get_rect().center
window.fill((127, 127, 127))
window.blit(the_img, the_img.get_rect(center = window.get_rect().center))
pygame.display.flip()
pygame.quit()
exit()

Pygame drawing antialiased shape and then blitting to main window

I want to draw antialiased shapes. I know you can use the pygame's gfxdraw module to accomplish this. However, it does seem to work only when drawing directly on the main window which is not suitable for me because I intend to use masks for collision checking.
Therefore a different surface is needed to create a mask that represents the circle.
How can you achieve this in pygame?
Minimal working example:
import pygame as pg
from pygame import gfxdraw
WIDHT, HEIGHT = 1200, 800
WIN = pg.display.set_mode((WIDHT, HEIGHT))
RADIUS = 80
WHITE = (255, 255, 255)
GREEN = (0, 200, 0)
RED = (200, 0, 0)
TRANS = (1, 1, 1)
class Circle(pg.sprite.Sprite):
def __init__(self,
radius: int,
pos: tuple[int, int],
color: tuple[int, int, int]):
super().__init__()
self.radius = radius
self.color = color
self.image = pg.surface.Surface((radius*2, radius*2))
self.image.fill(TRANS)
self.image.set_colorkey(TRANS)
self.rect = self.image.get_rect(center=(pos[0], pos[1]))
pg.gfxdraw.aacircle(self.image, self.rect.width//2, self.rect.height//2, radius, color)
pg.gfxdraw.filled_circle(self.image, self.rect.width//2, self.rect.height//2, radius, color)
self.mask = pg.mask.from_surface(self.image)
def draw(self):
WIN.blit(self.image, self.rect)
def main():
circle_1 = Circle(RADIUS, (500, 500), GREEN)
running = True
while running:
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
WIN.fill(WHITE)
circle_1.draw()
pg.gfxdraw.filled_circle(WIN, 700, 500, RADIUS, RED)
pg.gfxdraw.aacircle(WIN, 700, 500, RADIUS, RED)
pg.display.update()
if __name__ == "__main__":
main()
You need to create a transparent pygame.Surface with the per pixel alpha format. Use the SRCALPHA flag:
self.image = pg.surface.Surface((radius*2, radius*2))
self.image = pg.surface.Surface((radius*2, radius*2), pg.SRCALPHA)
However, for the highest quality, I suggest using OpenCV/cv2 (e.g. How to make a circular countdown timer in Pygame?)

How do you draw an antialiased circular line of a certain thickness? How to set width on pygame.gfx.aacircle()?

Like pygame.draw.circle(Surface, color, pos, radius, width), I want to get antialiased circle.
But, there is no option about width on pygame.gfxdraw.aacircle().
Anyone knows alternative way?
Neither the pygame.draw module nor the
pygame.gfxdraw module provide a function for an antialiased circle with an scalable thickness. While pygame.draw.circle() can draw a circle with different width, pygame.gfxdraw.circle() can draw a thin, antialiased circle.
You can try to stitch the circle with a pygame.draw.circle() for the body and pygame.gfxdraw.circle() on the edges. However, the quality is low and can depend on the system:
def drawAACircle(surf, color, center, radius, width):
pygame.gfxdraw.aacircle(surf, *center, 100, color)
pygame.gfxdraw.aacircle(surf, *center, 100-width, color)
pygame.draw.circle(surf, color, center, radius, width)
I recommend drawing a image with an antialiased circle and blit the image. You can create the image using OpenCV (opencv-python). See OpenCV - Drawing Functions.
Minimal example:
import pygame
import cv2
import numpy
def drawAACircle(surf, color, center, radius, width):
circle_image = numpy.zeros((radius*2+4, radius*2+4, 4), dtype = numpy.uint8)
circle_image = cv2.circle(circle_image, (radius+2, radius+2), radius-width//2, (*color, 255), width, lineType=cv2.LINE_AA)
circle_surface = pygame.image.frombuffer(circle_image.flatten(), (radius*2+4, radius*2+4), 'RGBA')
surf.blit(circle_surface, circle_surface.get_rect(center = center))
pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window.fill((32, 32, 32))
drawAACircle(window, (255, 0, 0), window.get_rect().center, 100, 20)
pygame.display.flip()

How to fade in a text or an image with PyGame

Can someone help me on this ? :
I try to make a title that appear smoothly on the user screen :
def image(name,x,y,u):
screen.blit(name,(x,y))
if u = 1
pygame.display.update()
Window == 'main'
While windows = 'main':
image(background,0,0)
image(title, 640, 120)
pygame.display.update()
But texte appaers suddently and not as I would like...
You want to use transparency, and pygame uses three different types:
There are three types of transparency supported in pygame: colorkeys, surface alphas, and pixel alphas. Surface alphas can be mixed with colorkeys, but an image with per pixel alphas cannot use the other modes. Colorkey transparency makes a single color value transparent. Any pixels matching the colorkey will not be drawn. The surface alpha value is a single value that changes the transparency for the entire image. A surface alpha of 255 is opaque, and a value of 0 is completely transparent.
Per pixel alphas are different because they store a transparency value for every pixel. This allows for the most precise transparency effects, but it also the slowest. Per pixel alphas cannot be mixed with surface alpha and colorkeys.
So let's use colorkey to create a transparent Surface and blit some text to it, and then use surface alpha to create the fade in effect by calling set_alpha and the Surface:
import pygame
def main():
screen = pygame.display.set_mode((300, 100))
FONT = pygame.font.SysFont(None, 64)
text = FONT.render('Hello World', False, pygame.Color('darkorange'))
surf = pygame.Surface(text.get_rect().size)
surf.set_colorkey((1,1,1))
surf.fill((1,1,1))
surf.blit(text, (0, 0))
clock = pygame.time.Clock()
alpha = 0
while True:
for e in pygame.event.get():
if e.type == pygame.QUIT:
return
alpha = (alpha + 1) % 256
surf.set_alpha(alpha)
screen.fill(pygame.Color('dodgerblue'))
screen.blit(surf, (20, 20))
clock.tick(120)
print(alpha)
pygame.display.update()
if __name__ == '__main__':
pygame.init()
main()
pygame.Surface.blit() already does what you want. It bends a surface to an other surface, depended on its alpha channel. All you have to do is to set the alpha channel by pygame.Surface.set_alpha():
image.set_alpha(min(1.0,alpha)*255)
Write a function, which "blends" a surface to the screen. In the following function BlendSurface, the parameter alpha is the opacity value of the surface in range[0, 1]. 0.0 would generate a completely transparent (invisible) surface. 1.0 would generate a completely opaque surface:
def BlendSurface(image, pos, alpha):
image.set_alpha(min(1.0,alpha)*255)
screen.blit(image, pos)
See the simple demo program:
import pygame
import pygame.font
pygame.init()
BLUE = ( 0, 0, 255)
YELLOW = (255, 255, 0)
size = (800,600)
screen = pygame.display.set_mode(size)
def BlendSurface(image, pos, alpha):
image.set_alpha(min(1.0,alpha)*255)
screen.blit(blendImage, pos)
clock = pygame.time.Clock()
font = pygame.font.SysFont('Times New Roman', 100)
text = font.render('blend text', False, YELLOW)
i = 0
done = False
while not done:
clock.tick(60)
i += 1
if i > 200:
i = 0
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill(BLUE)
BlendSurface(text, (100, 100), i/200)
pygame.display.flip()

Categories