Using image transaprency make appear black "things" - python

I'm having troubles to correctly load and blit PNG image with Pygame.
Code is actually working, but it display some strange and black "things" around my sprite :
black things around my sprite
To load tileset, I do:
def TilesRessourceFile(self, filename=None, tileSize=None, tiles=None):
logging.info("Loading ressources from %s", filename)
self._tileSize = tileSize
res = pygame.image.load(filename)
res.convert_alpha()
for tile in tiles:
name, (x, y), alpha = tile.values()
surface = pygame.Surface((tileSize, tileSize))
surface.blit(res, (0, 0), area=(x * tileSize, y * tileSize, (x + 1) * tileSize, (y + 1) * tileSize))
surface.convert_alpha() # set_colorkey(alpha)
self._tiles.update({name: surface})
Then I blit the sprite like this
def _implGameObjectRender(self, screen):
# logging.info("Render map %s", self._mapping)
for i in range(len(self._mapping)):
for j in range(len(self._mapping[i])):
screen.blit(self._mapping[i][j], (j * 128, i * 128))
It's probably not much, but I don't find the solution by myself.
I already tried to check :
how to load PNG with pygame
transparency with pygame (convert and convert_alpha)
I'm using this tileset : https://www.gamedevmarket.net/asset/2d-hand-painted-town-tileset-6626/
The tileset provide a json file to load with Tiled. Also tried this, and it perfectly works so I guess the problem is on my side
Thanks for helping me !
Python 3.9.1
Pygame 2.0.1 (SDL 2.0.14)

convert_alpha() does not convert the format of the surface itself, but creates a new surface with a format that provides a per pixel alpha format.
either
surface = pygame.Surface((tileSize, tileSize))
surface = surface.convert_alpha()
or
surface = pygame.Surface((tileSize, tileSize)).convert_alpha()
There are 3 ways to create a transparent Surface:
Set a transparent color key with set_colorkey()
The color key specifies the color that is treated as transparent. For example, if you have an image with a black background that should be transparent, set a black color key:
surface.set_colorkey((0, 0, 0))
You can enable additional functions when creating a new surface. Set the SRCALPHA flag to create a surface with an image format that includes a per-pixel alpha. The initial value of the pixels is (0, 0, 0, 0):
surface = pygame.Surface((tileSize, tileSize), pygame.SRCALPHA)
Use convert_alpha() to create a copy of the Surface with an image format that provides alpha per pixel.
However, if you create a new surface and use convert_alpha(), the alpha channels are initially set to maximum. The initial value of the pixels is (0, 0, 0, 255). You need to fill the entire surface with a transparent color before you can draw anything on it:
surface = pygame.Surface((tileSize, tileSize))
surface = surface.convert_alpha()
surface.fill((0, 0, 0, 0))

Related

Selectively reset the color of the surface after blitting PyGame

I have two green surfaces
s1 = Surface((100, 100))
s2 = Surface((100, 100))
s1.fill((0, 255, 0))
s2.fill((0, 255, 0))
Then i blit them on main surface with beautiful background
screen.blit(image.load("ocean.png").convert_alpha(), (0, 0))
screen.blit(s1, (0, 100))
screen.blit(s2, (200, 100))
Result:
Question: How can i selectively reset the color of one of the green surfaces after i have already blitted this surface on another surface? As if this surface never existed.
Desired result:
Note:
PyGame methods set_alpha and set_colorkey don't change anything. In addition, they will not allow to reset the surface selectively.
screen.set_alpha(0) # no changes
Or
sreen.set_colorkey((0, 255, 0)) # no changes
How can i selectively reset the color of one of the green surfaces...?
This is impossible
Each object in the scene is drawn to the pygame.Surface object associated with the display. screen is a pygame.Surface object and blit is short for block image transfer.
You cannot "reset" the color to a previous state. Each pixel of a surface knows only one color, the current color. However, you can blit a part of the original background on the rectangular area:
background = image.load("ocean.png").convert_alpha()
screen.blit(background, (200, 100), area=pygame.Rect(200, 100, 100, 100))

How do you clip a circle (or any non-Rect) from an image in pygame?

I am using Pygame and have an image. I can clip a rectangle from it:
image = pygame.transform.scale(pygame.image.load('example.png'), (32, 32))
handle_surface = image.copy()
handle_surface.set_clip(pygame.Rect(0, 0, 32, 16))
clipped_image = surface.subsurface(handle_surface.get_clip())
I have tried to use subsurface by passing a Surface:
handle_surface = image.copy()
hole = pygame.Surface((32, 32))
pygame.draw.circle(hole, (255, 255, 255), (0, 0), 32)
handle_surface.set_clip(hole)
image = surface.subsurface(handle_surface.get_clip())
surf = image.copy()
But I get the error:
ValueError: invalid rectstyle object
This error is because despite its name, subsurface expects a Rect, not a Surface. Is there a way to clip another shape from this image and have collidepoint work correctly?
You cannot use pygame.Surface.subsurface because a Surface is always rectangular and cannot have a circular shape. pygame.Rect.collidepoint detects if a point is inside a rectangular area and therefore cannot help you either.
Collision detection between a circle and a point can be calculated using the distance between the pointer and the center of the circle. Calculate the square of the Euclidean distance (dx*dx + dy*dy) from the point to the center of the circle. Check that the square of the distance is less than the square of the radius. In the following code the coordinates of the point are (px, py) and the circle is defined by its center (cx, cy) and its radius (radius).
dx = px - cx
dy = py - cy
if dx*dx + dy*dy <= radius*radius:
print('hit')
An alternative solution could be PyGame collision with masks. Also pygame.sprite.collide_circle could help, but then you would have to create a pygame.sprite.Sprite object for the point with radius 1, which seems to overcomplicate the problem.
If you want to clip a circular area from a pygame.Surface, see:
how to make circular surface in PyGame
How do I focus light or how do I only draw certain circular parts of the window in pygame?
How do I display a large black rectangle with a moveable transparent circle in pygame?
Can I use an image on a moving object within Pygame as opposed to to a color?
Short instruction:
Create a rectangular partial area from the image (only if the circle does not fill the whole image). However, the image must have an alpha channel. If it does not have one, this can be achieved with convert_alpha.
Create a transparent (pygame.SRCALPHA) mask with the size of the image
Draw a white opaque circle on the mask (I use pygame.draw.ellipse here, because it is easier to set the dimension)
Blend the circular mask with the image using the blend mode pygame.BLEND_RGBA_MIN. (see pygame.Surface.blit)
sub_image = image.subsurface(pygame.Rect(x, y, w, h)).convert_alpha()
mask_image = pygame.Surface(sub_image.get_size(), pygame.SRCALPHA)
pygame.draw.ellipse(mask_image, (255, 255, 255, 255), sub_image.get_rect())
sub_image.blit(mask_image, (0, 0), special_flags=pygame.BLEND_RGBA_MIN)

Python Pygame Trasparency showing as black [duplicate]

This question already has answers here:
How to make a surface with a transparent background in pygame
(3 answers)
Closed 2 years ago.
I am trying to blit an image with transparencies on top of a Surface with the rest of the map. ( This is the second layer. ) When I blit it, it shows with the transparency as black. Is there a way to fix this. I included the code related to it.
lily_tex = spritesheet.get_sprite(1, 4).convert_alpha()
This gets the image from the spritesheet.
img = pygame.Surface((self.tilesize, self.tilesize))
img.blit(self.img, (0, 0), (x, y, self.tilesize, self.tilesize))
return img.convert()
And this is what pulls it from the sprite sheet.
Below is what blits it to a surface to be blitted to the screen buffer.
def create_map(self):
for map_data in self.map_data:
for row in range(len(map_data)):
for column in range(len(map_data[row])):
if map_data[row][column] == 0:
continue
texture = self.key.get(map_data[row][column])
self.map_img.blit(texture, (column * self.tilesize, row * self.tilesize))
Thank you
When you call convert() the pixel format of the image is changed. If the image format has per alpha pixel, this information will be lost.
Use convert_alpha() instead of convert():
return img.convert()
return img.convert_alpha()
If the image has no per pixel alpha format you must set the transparent color key with set_colorkey():
img = pygame.Surface((self.tilesize, self.tilesize))
img.blit(self.img, (0, 0), (x, y, self.tilesize, self.tilesize))
img.set_colorkey((0, 0, 0))
Alternatively you can create a surface with per pixel alpha format with the SRCALPHA flag:
img = pygame.Surface((self.tilesize, self.tilesize), pygame.SRCALPHA)
img.blit(self.img, (0, 0), (x, y, self.tilesize, self.tilesize))

Trying to make sections of sprite change colour, but whole sprite changes instead

I have a few sprites in my game that need specific parts to be able to change colour.
My process I am trying to to have a pure white sprite image that is transparent everywhere the colour does not need to be. I am blitting a coloured square on top of that, and then that on top of the main sprite, however the main sprite then changes colour everywhere, but while respecting the main sprite transparency. The part that confuses me most is that the masked colour image does look correct when I put it on the main screen.
# Load main sprite and mask sprite
self.image = pygame.image.load("Enemy.png").convert_alpha()
self.mask = pygame.image.load("EnemyMask.png").convert_alpha()
# Create coloured image the size of the entire sprite
self.coloured_image = pygame.Surface([self.width, self.height])
self.coloured_image.fill(self.colour)
# Mask off the coloured image with the transparency of the masked image, this part works
self.masked = self.mask.copy()
self.masked.blit(self.coloured_image, (0, 0), None, pygame.BLEND_RGBA_MULT)
# Put the masked image on top of the main sprite
self.image.blit(self.masked, (0, 0), None, pygame.BLEND_MULT)
Enemy.png
EnemyMask.png (It's white so can't be seen)
Masked colour Masked Colour
Final Failed Sprite Failed Sprite
Can't post images, not enough reputation
I get no error, but only the white part of the shield is supposed to be green
self.image is the loaded image, where you want to change specific regions by a certain color and self.mask is a mask which defines the regions.
And you create an image masked, which contains the regions which are specified in mask tinted in a specific color.
So all you've to do is to .blit the tinted mask (masked) on the image without any special_flags set:
self.image.blit(self.masked, (0, 0))
See the example, where the red rectangle is changed to a blue rectangle:
repl.it/#Rabbid76/PyGame-ChangeColorOfSurfaceArea
Minimal example: repl.it/#Rabbid76/PyGame-ChangeColorOfSurfaceArea-3
Sprite:
Mask:
import pygame
def changColor(image, maskImage, newColor):
colouredImage = pygame.Surface(image.get_size())
colouredImage.fill(newColor)
masked = maskImage.copy()
masked.set_colorkey((0, 0, 0))
masked.blit(colouredImage, (0, 0), None, pygame.BLEND_RGBA_MULT)
finalImage = image.copy()
finalImage.blit(masked, (0, 0), None)
return finalImage
pygame.init()
window = pygame.display.set_mode((404, 84))
image = pygame.image.load('avatar64.png').convert_alpha()
maskImage = pygame.image.load('avatar64mask.png').convert_alpha()
colors = []
for hue in range (0, 360, 60):
colors.append(pygame.Color(0))
colors[-1].hsla = (hue, 100, 50, 100)
images = [changColor(image, maskImage, c) for c in colors]
clock = pygame.time.Clock()
nextColorTime = 0
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window.fill((255, 255, 255))
for i, image in enumerate(images):
window.blit(image, (10 + i * 64, 10))
pygame.display.flip()
pygame.quit()
exit()

How to make a surface with a transparent background in pygame

Can someone give me some example code that creates a surface with a transparent background in pygame?
This should do it:
image = pygame.Surface([640,480], pygame.SRCALPHA, 32)
image = image.convert_alpha()
Make sure that the color depth (32) stays explicitly set else this will not work.
You can also give it a colorkey, much like GIF file transparency. This is the most common way to make sprites. The original bitmap has the artwork, and has a certain color as background that will not be drawn, which is the colorkey:
surf.set_colorkey((255,0,255)) // Sets the colorkey to that hideous purple
Surfaces that uses colorkey instead of alpha are a lot faster to blit since they don't require any blend math. The SDL surface uses a simple bitmask when it has a colorkey set, which blits practically without overhead.
You have 3 possibilities:
Set a transparent color key with set_colorkey()
The color key specifies the color that is treated as transparent. For example, if you have an image with a black background that should be transparent, set a black color key:
my_surface.set_colorkey((0, 0, 0))
You can enable additional functions when creating a new surface. Set the SRCALPHA flag to create a surface with an image format that includes a per-pixel alpha. The initial value of the pixels is (0, 0, 0, 0):
my_surface = pygame.Surface((width, height), pygame.SRCALPHA)
Use convert_alpha() to create a copy of the Surface with an image format that provides alpha per pixel.
However, if you create a new surface and use convert_alpha(), the alpha channels are initially set to maximum. The initial value of the pixels is (0, 0, 0, 255). You need to fill the entire surface with a transparent color before you can draw anything on it:
my_surface = pygame.Surface((width, height))
my_surface = my_surface.convert_alpha()
my_surface.fill((0, 0, 0, 0))

Categories

Resources