Pygame surface with alpha not blitting transparency - python

I'm trying to make a user interface thing transparent in my game when the mouse isn't hovering over it. But for some reason, when I set the alpha value of the image for it to become transparent, nothing happens. Here is some runnable code for it that replicates the problem:
import pygame
WHITE = (255, 255, 255)
class UI:
def __init__(self):
self.img = pygame.image.load("ink_bar_solid.png")
self.img.set_alpha(0)
self.ink_bar_rect = self.img.get_bounding_rect()
self.x, self.y = 0, 10
resolution = (500, 500)
screen = pygame.display.set_mode(resolution)
mouse = pygame.mouse.get_pos
ink_bar = UI()
run = True
def mouse_over():
if ink_bar.ink_bar_rect.collidepoint(mouse()):
ink_bar.img.set_alpha(255)
else:
ink_bar.img.set_alpha(0)
while run:
mouse_over()
screen.fill(WHITE)
screen.blit(ink_bar.img, (ink_bar.x, ink_bar.y))
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
break
pygame.display.flip()
pygame.quit()
Any help is greatly appreciated!
Edit: I got a comment from someone who said they used their own image and it worked fine... I'm getting this warning when I execute the program:
libpng warning: iCCP: known incorrect sRGB profile
Is the reason why it doesn't blit properly because of my file?

The set_alpha method doesn't seem to work for unconverted png files. Calling the convert method will also improve the blit performance drastically:
self.img = pygame.image.load("ink_bar_solid.png").convert()
It also doesn't work for per-pixel alpha surfaces (surfaces converted with convert_alpha or created with the pygame.SRCALPHA flag). The alpha of per-pixel surfaces can be changed by filling them with a transparent white color and passing the pygame.BLEND_RGBA_MULT special flag, e.g.:
image = pygame.image.load('an_image.png').convert_alpha()
# Make a copy so that the original doesn't get modified.
transparent_image = image.copy()
transparent_image.fill((255, 255, 255, 100), special_flags=pygame.BLEND_RGBA_MULT)

Related

Why are my images Overlapping for Pygame?

import pygame
import random
import os
import time
pygame.init()
#window and background
win = pygame.display.set_mode((1000,750))
pygame.display.set_caption("TD GaME")
background_img = pygame.image.load('best_backgroundv2.png')
background = pygame.transform.scale(background_img,(1000,750))
class Enemy(pygame.sprite.Sprite):
def __init__(self, x, y, scale):
pygame.sprite.Sprite.__init__(self)
img = pygame.image.load('Run/HeavyBandit_Run_0.png')
self.image = pygame.transform.scale(img, (int(img.get_width() * scale), int(img.get_height() * scale)))
self.rect = self.image.get_rect()
self.rect.center = (x, y)
def draw(self):
win.blit(self.image, self.rect)
e_1 = Enemy(400, 500, 3)
So I am relatively new and trying to make a enemy(image) appear but is seems it has been overlapped by my background. I think it is due to how poor my code is in reference to setting the "background"
Your issue is missing the bit of code you have in your program that explains the order you are adding background and images to the window. Lacking that, I came up with the additional code snippet that I added to the end of the code you included above.
while True:
win.blit(background,(0, 0)) # Place the background into the window first
e_1.draw() # Draw the bandit or other image(s) next
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
pygame.display.update()
pygame.quit()
The takeaway here is the order in which the background and image are placed into the window. In this sequence, I got the following screen.
I didn't have your actual image files so I substituted some image files. See if you can work with that.

Image PIL to Python Surface Image fromstring Size value issues [duplicate]

I had opened an image using PIL, as
image = Image.open("SomeImage.png")
Draw some text on it, as
draw = ImageDraw.Draw(image)
draw.text(Some parameters here)
and then saved it as
image.save("SomeOtherName.png")
to open it using pygame.image
this_image = pygame.image.load("SomeOtherName.png")
I just want to do it without saving.. Can that be possible? It is taking a lot of time to save and then load(0.12 sec Yes, that is more as I have multiple images which require this operation). Can that save method be surpassed?
Sadly the accepted answer doesn't work anymore, because Image.tostring() has been removed. It has been replaced by Image.tobytes(). See Pillow - Image Module.
Function to convert a PIL Image to a pygame.Surface object:
def pilImageToSurface(pilImage):
return pygame.image.fromstring(
pilImage.tobytes(), pilImage.size, pilImage.mode).convert()
It is recommended to convert() the Surface to have the same pixel format as the display Surface.
Minimal example:
import pygame
from PIL import Image
def pilImageToSurface(pilImage):
return pygame.image.fromstring(
pilImage.tobytes(), pilImage.size, pilImage.mode).convert()
pygame.init()
window = pygame.display.set_mode((500, 500))
clock = pygame.time.Clock()
pilImage = Image.open('myimage.png')
pygameSurface = pilImageToSurface(pilImage)
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window.fill(0)
window.blit(pygameSurface, pygameSurface.get_rect(center = (250, 250)))
pygame.display.flip()
You could use the fromstring() function from pygame.image. The following should work, according to the documentation:
image = Image.open("SomeImage.png")
draw = ImageDraw.Draw(image)
draw.text(Some parameters here)
mode = image.mode
size = image.size
data = image.tostring()
this_image = pygame.image.fromstring(data, size, mode)

PyGame OR Moviepy - Show a video with transparency

I have a pretty weird situation but i will try hard to explain everything the best i can. So im coding a game using PyGame. I used moviepy to show a video on the surface. Now im doing a combatscreen and i want to animate the attacks. Ill show a quick screenshot
When clicked on "Fireball" i want to show a fireball. The video itself has transparency but it fills it up with white.
The code i used previously to show cutscenes for example is following:
video = VideoFileClip('assets/Attacks/Frantz/fireball.webm')
video.preview()
When it plays the video it looks like this:
My initial file was a gif that i converted to an mp4. i found out that mp4 doesn't support alpha/transparency i tried using the gif by replacing video = VideoFileClip('assets/Attacks/Frantz/fireball.mp4') with video = VideoFileClip('assets/Attacks/Frantz/fireball.gif')
but the same thing happend with the white background (And yes the gif has 100% transparency)
I kinda don't know what to do. Should i try other file formats, if yes how do i remove the transparency but i think i need to change something in the code so i might be able to actually use a gif or something.
Heres the file of the gif btw
I know my issue if very weird but its for a school project and i would greatly appreciate some help
A movie is not what you want. What you want is an animated sprite. An animated sprite is made up of many different sprites that are displayed in consecutive frames. The source of these sprites can be a sprite sheet, an animated GIF, or a list of bitmaps.
There are various questions and answers on this topic. For example:
Animated sprite from few images
How can I load an animated GIF and get all of the individual frames in PyGame?
How do I create animated sprites using Sprite Sheets in Pygame?
Since your GIF is not transparent, you have to set the color key for the transparent color with set_colorkey():
pygameImage.set_colorkey((0, 0, 0))
Example:
import pygame
from PIL import Image, ImageSequence
def pilImageToSurface(pilImage):
return pygame.image.fromstring(
pilImage.tobytes(), pilImage.size, pilImage.mode).convert()
def pilImageToSurface(pilImage):
return pygame.image.fromstring(
pilImage.tobytes(), pilImage.size, pilImage.mode).convert()
def loadGIF(filename):
pilImage = Image.open(filename)
frames = []
if pilImage.format == 'GIF' and pilImage.is_animated:
for frame in ImageSequence.Iterator(pilImage):
pygameImage = pilImageToSurface(frame.convert('RGB'))
pygameImage.set_colorkey((0, 0, 0))
frames.append(pygameImage)
else:
frames.append(pilImageToSurface(pilImage))
return frames
pygame.init()
window = pygame.display.set_mode((500, 500))
clock = pygame.time.Clock()
background = pygame.Surface(window.get_size())
ts, w, h, c1, c2 = 50, *window.get_size(), (128, 128, 128), (64, 64, 64)
tiles = [((x*ts, y*ts, ts, ts), c1 if (x+y) % 2 == 0 else c2) for x in range((w+ts-1)//ts) for y in range((h+ts-1)//ts)]
for rect, color in tiles:#
pygame.draw.rect(background, color, rect)
gifFrameList = loadGIF('fire.gif')
currentFrame = 0
run = True
while run:
clock.tick(20)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window.blit(background, (0, 0))
rect = gifFrameList[currentFrame].get_rect(center = (250, 250))
window.blit(gifFrameList[currentFrame], rect)
currentFrame = (currentFrame + 1) % len(gifFrameList)
pygame.display.flip()
pygame.quit()
exit()

Pygame click on image (not a rectangle)

This is the part of my code, where the problem is:
button = pygame.image.load("button1.png")
screen.blit(button, (100, 100))
This image looks like this:
[
I need to increase a value of a variable, when the user clicks on the image.
I tryed some solutions, but most of them was drawing an "invisible" rectangle over the picture, and the variable's value vas increasing, even if someone clicked on the white space near the triangle.
It's quite easy with the mask module.
From the docs:
Useful for fast pixel perfect collision detection. A mask uses 1 bit per-pixel to store which parts collide.
First, create a Mask from the image
mask = pygame.mask.from_surface(button)
Then, when checking for the mouse click event, check if the point in the mask is set.
Here's a simple example:
import pygame
def main():
pygame.init()
screen = pygame.display.set_mode((480, 320))
button = pygame.image.load('button.png').convert_alpha()
button_pos = (100, 100)
mask = pygame.mask.from_surface(button)
x = 0
while True:
for e in pygame.event.get():
if e.type == pygame.QUIT:
return
if e.type == pygame.MOUSEBUTTONDOWN:
try:
if mask.get_at((e.pos[0]-button_pos[0], e.pos[1]-button_pos[1])):
x += 1
print(x)
except IndexError:
pass
screen.fill((80,80,80))
screen.blit(button, button_pos)
pygame.display.flip()
main()
Example button.png for testing:
There's no easy way to do this in pygame other than manually calculating where the mouse is and figuring out if it's in the triangle or not.
The image you're loading (button1.png) is a square image, and so there's no way for pygame or any other library to know what it's "actual" shape is. You'll either have to do it yourself or be okay with the user being able to click on the white space.
You could use Surface.get_at() to check the color of the pixel where the mouse clicks. If it's the background color (white in your case) you consider it outside, otherwise is inside and you trigger the action.
Here a working example. The insideimage function checks that the click is inside the surface button (the rectangle) and checks the color of the pixel at mouse coordinates. Returns True if the click is inside the surface and the color is not white.
This works if the background color is not used again inside the image.
import sys
import pygame
SCREENWIDTH = 500
SCREENHEIGHT = 500
pygame.init()
screen = pygame.display.set_mode((SCREENWIDTH, SCREENHEIGHT))
button = pygame.image.load("button1.png")
screen.blit(button, (100, 100))
def insideimage(pos, rsurf, refcolor):
"""rsurf: Surface which contains the image
refcolor: background color, if clicked on this color returns False
"""
refrect = rsurf.get_rect().move((100, 100))
pickedcol = screen.get_at(pos)
return refrect.collidepoint(pos) and pickedcol != refcolor
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.MOUSEBUTTONUP:
valid = insideimage(event.pos, button, (255, 255, 255, 255))
#(255, 255, 255, 255) this is color white with alpha channel opaque
print(valid)
pygame.display.update()

Is it possible to stretch an image in pygames?

Is it possible to stretch an image in pygame using an event to trigger it?
Like say I have a person and I want his eyes to popout like this
when I press a button and I am using surface.blit(eyes=pygame.image.load('eyes')) for the eyes.
Can i stretch the eye image like the picture in this link?
There is a solution to this problem that allows you to stretch the eyes to the exact width you want, but it may make the eyes very deformed... (nevermind, the original image has pretty deformed eyes anyway.)
From the Pygame doc on pygame.transform.scale:
scale(Surface, (width, height), DestSurface = None) -> Surface
We also use image.get_height() so the user does not have to get the height themselves.
So you would do something like this (wrapped in a function):
def stretchEyes(image, newWidth):
return pygame.transform.scale(image, (image.get_height(), newWidth))
eyes = stretchEyes(eyes, image.get_width()*3) # Stretch to three times width
# Blitting takes in the top left position, so we don't need to do any maths here!
screen.blit(eyes, (x,y))
A better approach would be to have two images one for the actual image and one where the eyes are stretched. Draw the second image whenever you need instead of the first image.
import pygame
from pygame.locals import *
screen = pygame.display.set_mode((540, 480))
runner1 = pygame.image.load('./runner1.jpg').convert()
runner1_rect = runner1.get_rect(center=(270, 240))
runner2 = pygame.image.load('./runner2.jpg').convert()
runner2_rect = runner2.get_rect(center=(270, 240))
screen.fill((0, 0, 0))
change = True
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
if event.type == MOUSEBUTTONDOWN:
screen.blit(runner2, runner2_rect)
pygame.display.update()
if event.type == KEYDOWN:
screen.blit(runner1, runner1_rect)
pygame.display.update()
As shown in the above example, we get images(sprites) of different postures and play them as and when we need to get the motion.
To begin and understand the pygame start from PUMMEL THE CHIMP and there are good API to handle sprites and their behavior through pygame.

Categories