How to get the Difference blending mode? - python

I'm trying to make a night effect for my game in Pygame. So I'm gonna blit a black image on the screen then, change its blending mode to Difference just like in Photoshop in order to make the screen look darker. However, I still don't know how to do that as I haven't used the blending modes in Pygame yet. Any help ?

The blending mode can be changed by setting the optional special_flags argument of pygame.Surface.blit:
blit(source, dest, area=None, special_flags=0) -> Rect
[...]
New in pygame 1.8: Optional special_flags: BLEND_ADD, BLEND_SUB, BLEND_MULT, BLEND_MIN, BLEND_MAX.
New in pygame 1.8.1: Optional special_flags: BLEND_RGBA_ADD, BLEND_RGBA_SUB, BLEND_RGBA_MULT, BLEND_RGBA_MIN, BLEND_RGBA_MAX, BLEND_RGB_ADD, BLEND_RGB_SUB, BLEND_RGB_MULT, BLEND_RGB_MIN, BLEND_RGB_MAX.
New in pygame 1.9.2: Optional special_flags: BLEND_PREMULTIPLIED
New in pygame 2.0.0: Optional special_flags: BLEND_ALPHA_SDL2 [...]
e.g.:
screen.blit(image, (x, y), special_flags = pygame.BLEND_RGBA_SUB)
Unfortunately, Pygame doesn't have a blending mode that gives the absolute difference of 2 images. However it can be achieved with
MAX(SUB(image1, imgage2), SUB(image2, image1))
e.g.:
image1 = pygame.image.load('image2.png')
image2 = pygame.image.load('image1.png')
temp_image = image1.copy()
temp_image.blit(image2, (0, 0), special_flags = pygame.BLEND_RGBA_SUB)
final_image = image2.copy()
final_image.blit(image1, (0, 0), special_flags = pygame.BLEND_RGBA_SUB)
final_image.blit(temp_image, (0, 0), special_flags = pygame.BLEND_RGBA_MAX)

Related

Transparent Image in Pygame

I have a sprite in Pygame that is a blue circle. I want this image to be drawn to the screen "faded", e.g. translucent. However, I don't want a translucent rectangle to be drawn over it; instead, I want the actual image to be modified and made translucent. Any help is greatly appreciated!
Right now I have:
Class Circle(pygame.sprite.Sprite):
self.image = self.image = pygame.image.load("circle.png")
circle = Circle()
and eventually...
window.blit(pygame.transform.scale(circle.image, (zoom, zoom)), (100, 100))
How the circle.png looks:
How I want the image to look after making it transparent:
I am blitting the image onto the window, which is a white background.
First, your image/surface needs to use per-pixel alpha, therefore call the convert_alpha() method when you load it. If you want to create a new surface (as in the example), you can also pass pygame.SRCALPHA to pygame.Surface.
The second step is to create another surface (called alpha_surface here) which you fill with white and the desired alpha value (the fourth element of the color tuple).
Finally, you have to blit the alpha_surface onto your image and pass pygame.BLEND_RGBA_MULT as the special_flags argument. That will make
the opaque parts of the image translucent.
import pygame as pg
pg.init()
screen = pg.display.set_mode((800, 600))
clock = pg.time.Clock()
BLUE = pg.Color('dodgerblue2')
BLACK = pg.Color('black')
# Load your image and use the convert_alpha method to use
# per-pixel alpha.
# IMAGE = pygame.image.load('circle.png').convert_alpha()
# A surface with per-pixel alpha for demonstration purposes.
IMAGE = pg.Surface((300, 300), pg.SRCALPHA)
pg.draw.circle(IMAGE, BLACK, (150, 150), 150)
pg.draw.circle(IMAGE, BLUE, (150, 150), 130)
alpha_surface = pg.Surface(IMAGE.get_size(), pg.SRCALPHA)
# Fill the surface with white and use the desired alpha value
# here (the fourth element).
alpha_surface.fill((255, 255, 255, 90))
# Now blit the transparent surface onto your image and pass
# BLEND_RGBA_MULT as the special_flags argument.
IMAGE.blit(alpha_surface, (0, 0), special_flags=pg.BLEND_RGBA_MULT)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
screen.fill((50, 50, 50))
pg.draw.rect(screen, (250, 120, 0), (100, 300, 200, 100))
screen.blit(IMAGE, (150, 150))
pg.display.flip()
clock.tick(60)
pg.quit()
Create a new Surface with per-pixel alpha.
surf = pygame.Surface((circle_width, circle_height), pygame.SRCALPHA)
Make the surface transparent
surf.set_alpha(128) # alpha value
Draw the circle to that surface at (x=0, y=0)
surf.blit(pygame.transform.scale(circle.image, (zoom, zoom)), (0, 0))
Draw the surface to the window
window.blit(surf, (circle_x, circle_y))
As the Surface.set_alpha() documentation says, you can have surfaces with "homogeneous alpha" or per pixel alpha, but not both, wich I believe is what you want. It might work with colorkey transparency, but I'm not sure (I didn't tested that yet). Any non RGBA (without active alpha channel) pixel format might work if you use set_colorkey() and set_alpha() before blitting.
So, the code might look like:
class Circle(pygame.sprite.Sprite):
def __init__(self)
self.image = pygame.image.load("circle.png")
# get the surface's top-left pixel color and use it as colorkey
colorkey = self.image.get_at((0, 0))
self.image.set_colorkey(colorkey)
At some point in your code (immediately before rendering) you might want to set the transparency by calling:
circle.image.set_alpha(some_int_val)
And then you can scale and blit it as intended.

Fill pygame font with custom pattern

I'm currently working on a (first) project in Python/Pygame and I'm trying to display text with a pattern overlay. The pattern consists vertical lines (1 pixel width), 2 alternating colors .
I'm creating this pattern using pygame.draw.line(), and I can create rectangles with a custom function I created. But now I want this pattern on my font.
I'm new to Python and am wondering: is there a way to create some sort of mask so that the pattern will appear on the font characters only, not outside of them? I've searched the web for some time but cannot find anything.
Many thanks in advance!
You could make use of the different blend modes to get the effect you desire.
Here's a simple example (where t.bmp is the image with your pattern, but of course you can just use any other surface):
import pygame
pygame.init()
screen = pygame.display.set_mode((490, 160))
font = pygame.font.SysFont('Arial', 150)
pattern = pygame.image.load('t.bmp').convert()
text = font.render('FooBar', True, (255, 255, 255), (0, 0, 0))
pattern.blit(text, (0, 0), special_flags = pygame.BLEND_MULT)
screen.blit(pattern, (0, 0))
pygame.display.flip();
while True:
if pygame.event.get(pygame.QUIT):
break
Result:

pygame - trying to move a rectangle

I am codding a drum game (based on dtx-Mania).
I got an issue on how to draw a rectangle and move it. I can make a static one, but not a moving one. I currently used a line but it seams pygame draw it as a rectangle so far if it as an impact i will change back to rectangle.
My goal is to draw the rectangle and move it at a slow enough pace so that it takes about 1 second to get to a line.
I know I still have a lot to learn, this is what I got to test so far.
#!/usr/bin/env python3
#-*- coding:utf-8 -*-
#small exemple of a moving rectangle
import pygame, sys
pygame.init()
fpsClock = pygame.time.Clock()
windowsSurfaceObj = pygame.display.set_mode((640, 480))
pygame.display.set_caption('moving rectangle test')
white = pygame.Color(255, 255, 255)
black = pygame.Color(0, 0, 0)
step = pygame.draw.line(windowsSurfaceObj, white, (233, 0), (269, 0), 6)
step
while True:
windowsSurfaceObj.fill(black)
#the coordonate are moved but the rectangle is now drew
step.move(0, -1)
#this is the target line (where the moving object must go to)
pygame.draw.line(windowsSurfaceObj, white, (90, 420), (390, 420), 6)
pygame.display.update()
fpsClock.tick(30)
Thank you for your help.
It is always good to read the docs. From the pygame docs:
line(Surface, color, start_pos, end_pos, width=1) -> Rect
Draw a straight line segment on a Surface. There are no endcaps, the
ends are squared off for thick lines.
In the description text of the whole module:
The functions return a rectangle representing the bounding area of changed pixels.
So you draw your first line, and move a rectangle representing a bounding are of changed pixels. Since you do not redraw your line, your first line disappears.
To solve this, you need to draw the line in the while loop after moving.
step.move(0, -1)
pygame.draw.rect(windowsSurfaceObj, white,step, 6)

Drawn surface transparency in pygame?

I'm writing a predator-prey simulation using python and pygame for the graphical representation. I'm making it so you can actually "interact" with a creature (kill it, select it and follow it arround the world, etc). Right now, when you click a creature, a thick circle(made of various anti-aliased circles from the gfxdraw class) sorrounds it, meaning that you have succesfully selected it.
My goal is to make that circle transparent, but according to the documentation you can't set an alpha value for drawn surface. I have seen solutions for rectangles (by creating a separate semitransparent surface, blitting it, and then drawing the rectangle on it), but not for a semi-filled circle.
What do you suggest? Thank's :)
Have a look at the following example code:
import pygame
pygame.init()
screen = pygame.display.set_mode((300, 300))
ck = (127, 33, 33)
size = 25
while True:
if pygame.event.get(pygame.MOUSEBUTTONDOWN):
s = pygame.Surface((50, 50))
# first, "erase" the surface by filling it with a color and
# setting this color as colorkey, so the surface is empty
s.fill(ck)
s.set_colorkey(ck)
pygame.draw.circle(s, (255, 0, 0), (size, size), size, 2)
# after drawing the circle, we can set the
# alpha value (transparency) of the surface
s.set_alpha(75)
x, y = pygame.mouse.get_pos()
screen.blit(s, (x-size, y-size))
pygame.event.poll()
pygame.display.flip()

PyGame: translucent sprites with per pixel alpha

Is it possible to display PyGame surfaces with controllable alpha? I would like to take a surface with its own per pixel alpha and display it with variable level of translucency without affecting the surface data and keep the transparency intact i.e. the objects on the surface would keep their shapes but their "contents" becoming more or less translucent.
In other words I want to combine per-pixel alpha from the source image with per-surface alpha calculated at the runtime.
The Pygame documentation says that you can't combine surface alpha with per-pixel alpha, but if you're using Pygame 1.8.1 or newer, you can work around this by using the special_flags parameter to .blit().
Here's how I did it:
# Load an image with per-pixel alpha
s = pygame.image.load('spam.png')
s = s.convert_alpha()
# Simulate s.set_alpha(alpha)
alpha_img = pygame.Surface(s.get_rect().size, pygame.SRCALPHA)
alpha_img.fill((255, 255, 255, alpha))
s.blit(alpha_img, (0, 0), special_flags=pygame.BLEND_RGBA_MULT)
The way this works is to create a second surface the same size as the first, and blit it on to the first using RGBA multiplication. By having the R, G and B components of the second image equal to 255, the multiplication won't affect the colour of the image, but it will scale the alpha channel by the given alpha factor.
Note that the method above differs from calling set_alpha() in that set_alpha() can be reversed by calling set_alpha(255). If you use the method above it will result in the pixel alphas of every pixel being changed, so the process cannot be straightforwardly reversed.
Yes you can. The documentation of the Surface class explains how to do it. It boils down to two cases only:
Either you set a flag during the creation of the Surface object:
s = pygame.Surface((16, 16), flags=pygame.SRCALPHA)
Or you give an alpha channel to a surface that doesn't have one yet:
s = pygame.image.load('spam.png')
s.convert_alpha()
The documentation of the pygame.image module says that applying convert_alpha is necessary for PNGs with transparency.
If you want you can modify the alpha value of each pixel with the modules draw and surfarray. When specifying a color, use then a tuple of four integers: (red, green, blue, alpha). The alpha parameter ranges from 0 (totally transparent) to 255 (totally opaque).
import pygame
pygame.init()
screen = pygame.display.set_mode((320, 200))
screen.fill((200, 100, 200))
s = pygame.Surface((64, 64), flags=pygame.SRCALPHA)
for i in range(64):
pygame.draw.line(s, (0, 0, 0, i*4), (0, i), (64, i))
screen.blit(s, (50, 30))
pygame.display.flip()
After checking both PyGame and SDL documentations I came to conclusion that what I asked wasn't doable in PyGame using standard functions.
SDL docs state that per-pixel alpha and per-surface alpha cannot be combined with the former always taking the precedence. The only way to achieve the effect I want is by writing a code which updates per-pixel alpha values of the source surface before the blit.
If your input image has per-pixel alpha enabled, but is only using a single bit for transparency (e.g., .png sprites with a transparent background or text), you can convert the transparent background to a colorkey-alpha background pretty easily, and then use set_alpha() to blend the whole texture onto whatever.
This is a quick and dirty helper function I used for fading text effects and other things:
def convert_to_colorkey_alpha(surf, colorkey=pygame.color.Color("magenta")):
newsurf = surface.Surface(surf.get_size())
newsurf.fill(colorkey)
newsurf.blit(surf, (0, 0))
newsurf.set_colorkey(colorkey)
return newsurf
A couple of other tricks potentially solve this problem might be:
Doing per-pixel editing to modify the alpha channel before blitting
Blitting a white or black-filled, semi-transparent surface with special_flags=BLEND_RGBA_MULT or BLEND_RGBA_SUB onto your source surface.

Categories