pygame, saving a image with low opacity - python

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()

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?

Padding for text using pygame SysFont

I'm trying to use pygame's SysFont to display some text on the screen but I want the background rectangle to be slightly wider than the actual text.
When I use the following code the background seems to start exactly where the first letter starts and finishes exactly where the last letter ends.
font = pygame.font.SysFont("Helvetica", 30)
img = font.render("ABC", True, "white", "blue1")
img_width = img.get_width()
img_height = img.get_height()
screen.blit(img, (200, 200, img_width, img_height))
Is there a way to have some padding to the left and right of the text in the background colour? I thought that perhaps adding something to img_width might help but it didn't.
You need to render the text on Surface larger than the text Surface:
Render the text with a transparent background
Create a Surface larger the the text Surface (see pygame.Rect.inflate)
Fill the Surface with the background color
blit the text in the center of the Surface
Minimal example:
import pygame
pygame.init()
window = pygame.display.set_mode((300, 200))
clock = pygame.time.Clock()
def create_text_box(font, text, text_color, box_color, margin_x, margin_y):
text_surf = font.render("ABC", True, text_color, "blue1")
box_surf = pygame.Surface(text_surf.get_rect().inflate(margin_x, margin_y).size)
box_surf.fill(box_color)
box_surf.blit(text_surf, text_surf.get_rect(center = box_surf.get_rect().center))
return box_surf
font = pygame.font.SysFont(None, 100)
text_surf = create_text_box(font, "ABC", "white", "blue1", 10, 0)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window.fill(0)
window.blit(text_surf, text_surf.get_rect(center = window.get_rect().center))
pygame.display.flip()
clock.tick(60)
pygame.quit()
exit()
See also How to separately change the opacity of a text on a button pygame?

Drawing non pixelated circles in pygame [duplicate]

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 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