Rotating An Image In Pygame Without Distortion - python

I have been trying to make an image rotate in pygame, using python 3.6, however when I do it either distorts the image into an unrecognizable image, or when it rotates it bumps all over the place
Just using pygame.transform.rotate(image, angle) makes the distorted mess.
And using something like:
pygame.draw.rect(gameDisplay, self.color, [self.x, self.y, self.width, self.height]) makes the image bump all over the place.
I have looked at many questions on this site and others and so far none of them have worked perfectly.
To anyone who is interested here is the link to my code so far.
https://pastebin.com/UQJJFNTy
My image is 64x64.
Thanks in advance!

Per the docs (http://www.pygame.org/docs/ref/transform.html):
Some of the transforms are considered destructive. These means every time they are performed they lose pixel data. Common examples of this are resizing and rotating. For this reason, it is better to re-transform the original surface than to keep transforming an image multiple times.
Each time you call transform.rotate you need to do it on the original image, not on the previously rotated one. For example, if I want the image rotated 10 degrees each frame:
image = pygame.image.load("myimage.png").convert()
image_clean = image.copy()
rot = 0
Then in your game loop (or object's update):
rot += 10
image = pygame.transform.rotate(image_clean, rot)

Here's a complete example. Don't modify the original image and in the while loop use pygame.transform.rotate or rotozoom to get a new rotated surface and assign it to another name. Use a rect to keep the center.
import sys
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
BG_COLOR = pg.Color('darkslategray')
# Here I just create an image with per-pixel alpha and draw
# some shapes on it so that we can better see the rotation effects.
ORIG_IMAGE = pg.Surface((240, 180), pg.SRCALPHA)
pg.draw.rect(ORIG_IMAGE, pg.Color('aquamarine3'), (80, 0, 80, 180))
pg.draw.rect(ORIG_IMAGE, pg.Color('gray16'), (60, 0, 120, 40))
pg.draw.circle(ORIG_IMAGE, pg.Color('gray16'), (120, 180), 50)
def main():
clock = pg.time.Clock()
# The rect where we'll blit the image.
rect = ORIG_IMAGE.get_rect(center=(300, 220))
angle = 0
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
# Increment the angle, then rotate the image.
angle += 2
# image = pg.transform.rotate(ORIG_IMAGE, angle) # rotate often looks ugly.
image = pg.transform.rotozoom(ORIG_IMAGE, angle, 1) # rotozoom is smoother.
# The center of the new rect is the center of the old rect.
rect = image.get_rect(center=rect.center)
screen.fill(BG_COLOR)
screen.blit(image, rect)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()
sys.exit()

Related

The image drawn in pygame does not match the location it is given

Hello everyone I am learning the basics of pygame.
I recently ran into a problem;
I decided to load an image and gave it a random location well within the pygame window.But the problem was that sometimes it just wont appear in the window. So I drew a black pointer at the place where the image was to be loaded. Then I found out that the black pointer doesnot coincide with the image and hence the image is not appearing in the location I want it to be in.
So I want help solving this problem.
Thank you in advance.
Code:
import pygame
import random
pygame.init()
#Pygame starters
display_width = 800
display_height = 600
game_display = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption("Blob runner")
clock = pygame.time.Clock()
targetpng = pygame.image.load("target.png")
#Quit checker
crashed = False
#COLOURS
white = (255, 255, 255)
black = (0, 0, 0)
def draw_environment(x,y):
game_display.fill(white)
#Image
game_display.blit(targetpng,(x, y))
#Black pointer
pygame.draw.circle(game_display, black, (x, y), 5 )
pygame.display.update()
x, y = random.randrange(0,display_width), random.randrange(0,display_height)
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
# print(event)
draw_environment(x,y)
clock.tick(30)
pygame.quit()
quit()
Image:
The image has a size. it may happen that the image is out of the window at the right or at the bottom. The origin of the blit puts the the top left corner of the image to the specified position.
If you wan that the image is centered at (x, y), the you have to get a pygame.Rect with the size of the image (get_rect) and set the center of the rectangle to the specified position (keyword argument). Use the rectangle to specify the position for the blit operation.
img_rect = targetpng.get_rect(center = (x, y))
game_display.blit(targetpng, img_rect)
Th image has a size, thus the random position for the image has to be in range [image_size/2, window_sizw - image_size/2].
The width and height of a pygame.Surface can be get by get_width()/get_height() or get_size(). For instance:
img_size = targetpng.get_size()
x = random.randrange(img_size[0]//2, display_width - img_size[0]//2)
y = random.randrange(img_size[1]//2, display_height - img_size[1]//2)
I now understand what went wrong with the insight provided by #Rabbid76.
The thing is I drew the picture in paint and then opened the file in gimp to convert whites in the image to alpha(the checkerboard-like background) . But the problem occurred when I did not clear of the unwanted alpha as I thought the computer will not recognise the alpha but as matter of fact it does.
So when I loaded the image, the corner of the image is not of the actual "target spot" but of the entire image, so the corner will be on the alpha. The black dot was pointing at the corner of the image all along and the image sometimes did not appear because the corner was too close to the border so the image would trespass to the un-displayed portion of the window.
So the fix is to clear of the alpha or the white background to minimum and only then convert it into alpha. Then do as #Rabbid76 says :D.

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.

How to rotate a surface in pygame, without changing its shape

I'm writing a class in pygame to create a sprite object, and I'd like to be able to rotate it. It works fine with an image, and rotates without issue. But when rotating a surface with a plain colour, the box appears to grow and shrink. I know that this is a result of the surface changing size to fit the vertices of the rectangle inside, but how do I stop it? I'd like to see a visual rotation.
I've created some sample code to show the problem that I'm facing, running it causes the box to simply change in size.
import sys, pygame
from pygame.locals import *
pygame.init()
SCREEN = pygame.display.set_mode((200, 200))
CLOCK = pygame.time.Clock()
surface = pygame.Surface((50 , 50))
surface.fill((0, 0, 0))
rotated_surface = surface
rect = surface.get_rect()
angle = 0
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
SCREEN.fill((255, 255, 255))
angle += 5
rotated_surface = pygame.transform.rotate(surface, angle)
rect = rotated_surface.get_rect(center = (100, 100))
SCREEN.blit(rotated_surface, (rect.x, rect.y))
pygame.display.update()
CLOCK.tick(30)
How do I fix this issue, to make the surface rotate how I want?
Any help would be appreciated!
You have to create the Surface objects you create to stamp on the display surface in a way they use transparency information (That is - they have to have an alpha channel).
To do that, is just a question of passing the appropriate flag when creating your surface objects - simply replace this:
surface = pygame.Surface((50 , 50))
with:
surface = pygame.Surface((50 , 50), pygame.SRCALPHA)
and it should work.

pygame.error: Couldn't allocate memory for PNG file or incompatible PNG dll [duplicate]

I'm trying to blit a PNG image onto a surface, but the transparent part of the image turns black for some reason, here's the simple code:
screen = pygame.display.set_mode((800, 600), pygame.DOUBLEBUF, 32)
world = pygame.Surface((800, 600), pygame.SRCALPHA, 32)
treeImage = pygame.image.load("tree.png")
world.blit(treeImage, (0,0), (0,0,64,64))
screen.blit(world, pygame.rect.Rect(0,0, 800, 600))
What do I have to do to solve the problem?
The image has alpha transparency, I've opened it up in PhotoShop and the background turns transparent, not black or white or any other color.
Thank you for your support :)
http://www.pygame.org/docs/ref/image.html recommends:
For alpha transparency, like in .png images use the convert_alpha() method after loading so that the image has per pixel transparency.
You did not flip the doublebuffer.
import pygame
from pygame.locals import Color
screen = pygame.display.set_mode((800, 600))
treeImage = pygame.image.load("tree.png").convert_alpha()
white = Color('white')
while(True):
screen.fill(white)
screen.blit(treeImage, pygame.rect.Rect(0,0, 128, 128))
pygame.display.flip()
This should work for your problem.
Images are represented by "pygame.Surface" objects. A Surface can be created from an image with pygame.image.load:
my_image_surface = pygame.load.image('my_image.jpg')
However, the pygame documentation notes that:
The returned Surface will contain the same color format, colorkey and alpha transparency as the file it came from. You will often want to call convert() with no arguments, to create a copy that will draw more quickly on the screen.
For alpha transparency, like in .png images, use the convert_alpha() method after loading so that the image has per pixel transparency.
Use the convert_alpha() method for best performance:
alpha_image_surface = pygame.load.image('my_icon.png').convert_alpha()
A Surface can be drawn on or blended with another Surface using the blit method. The first argument to blit is the Surface that should be drawn. The second argument is either a tuple (x, y) representing the upper left corner or a rectangle. With a rectangle, only the upper left corner of the rectangle is taken into account. It should be mentioned that the window respectively display is also represented by a Surface. Therefore, drawing a Surface in the window is the same as drawing a Surface on a Surface:
window_surface.blit(image_surface, (x, y))
window_surface.blit(image_surface,
image_surface.get_rect(center = window_surface.get_rect().center))
Minimal example: repl.it/#Rabbid76/PyGame-LoadTransparentImage
import pygame
pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
pygameSurface = pygame.image.load('Porthole.png').convert_alpha()
background = pygame.Surface(window.get_size())
ts, w, h, c1, c2 = 50, *window.get_size(), (160, 160, 160), (192, 192, 192)
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)
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window.blit(background, (0, 0))
window.blit(pygameSurface, pygameSurface.get_rect(center = window.get_rect().center))
pygame.display.flip()
pygame.quit()
exit()
Your code looks like it should be correct. The SDL library does not support alpha to alpha blitting like this, but Pygame added support for it awhile ago. In Pygame 1.8 support was added for custom blending modes, and I wonder if that removed Pygame's internal alpha-to-alpha blitter?
Alas, further investigation will be required.

How do I blit a PNG with some transparency onto a surface in Pygame?

I'm trying to blit a PNG image onto a surface, but the transparent part of the image turns black for some reason, here's the simple code:
screen = pygame.display.set_mode((800, 600), pygame.DOUBLEBUF, 32)
world = pygame.Surface((800, 600), pygame.SRCALPHA, 32)
treeImage = pygame.image.load("tree.png")
world.blit(treeImage, (0,0), (0,0,64,64))
screen.blit(world, pygame.rect.Rect(0,0, 800, 600))
What do I have to do to solve the problem?
The image has alpha transparency, I've opened it up in PhotoShop and the background turns transparent, not black or white or any other color.
Thank you for your support :)
http://www.pygame.org/docs/ref/image.html recommends:
For alpha transparency, like in .png images use the convert_alpha() method after loading so that the image has per pixel transparency.
You did not flip the doublebuffer.
import pygame
from pygame.locals import Color
screen = pygame.display.set_mode((800, 600))
treeImage = pygame.image.load("tree.png").convert_alpha()
white = Color('white')
while(True):
screen.fill(white)
screen.blit(treeImage, pygame.rect.Rect(0,0, 128, 128))
pygame.display.flip()
This should work for your problem.
Images are represented by "pygame.Surface" objects. A Surface can be created from an image with pygame.image.load:
my_image_surface = pygame.load.image('my_image.jpg')
However, the pygame documentation notes that:
The returned Surface will contain the same color format, colorkey and alpha transparency as the file it came from. You will often want to call convert() with no arguments, to create a copy that will draw more quickly on the screen.
For alpha transparency, like in .png images, use the convert_alpha() method after loading so that the image has per pixel transparency.
Use the convert_alpha() method for best performance:
alpha_image_surface = pygame.load.image('my_icon.png').convert_alpha()
A Surface can be drawn on or blended with another Surface using the blit method. The first argument to blit is the Surface that should be drawn. The second argument is either a tuple (x, y) representing the upper left corner or a rectangle. With a rectangle, only the upper left corner of the rectangle is taken into account. It should be mentioned that the window respectively display is also represented by a Surface. Therefore, drawing a Surface in the window is the same as drawing a Surface on a Surface:
window_surface.blit(image_surface, (x, y))
window_surface.blit(image_surface,
image_surface.get_rect(center = window_surface.get_rect().center))
Minimal example: repl.it/#Rabbid76/PyGame-LoadTransparentImage
import pygame
pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
pygameSurface = pygame.image.load('Porthole.png').convert_alpha()
background = pygame.Surface(window.get_size())
ts, w, h, c1, c2 = 50, *window.get_size(), (160, 160, 160), (192, 192, 192)
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)
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window.blit(background, (0, 0))
window.blit(pygameSurface, pygameSurface.get_rect(center = window.get_rect().center))
pygame.display.flip()
pygame.quit()
exit()
Your code looks like it should be correct. The SDL library does not support alpha to alpha blitting like this, but Pygame added support for it awhile ago. In Pygame 1.8 support was added for custom blending modes, and I wonder if that removed Pygame's internal alpha-to-alpha blitter?
Alas, further investigation will be required.

Categories