0

I can't find a way to easily and efficiently transform Surfaces in pygame. By transforming I mean freely reshape an image. For example: original to: transformed I've tried converting the Surface to a numpy array and apply the operation on it, the converting it back to Surface. However, this method seems to be very slow.

PunnyBunny
  • 185
  • 1
  • 10
  • No, the title does not describe the question. You have to be more specific. What transformations do you want to do? Pygame has the [`pygame.transform`](https://www.pygame.org/docs/ref/transform.html) module, [`pygame.surfarray`](https://www.pygame.org/docs/ref/surfarray.html) module and [`pygame.PixelArray`](https://www.pygame.org/docs/ref/pixelarray.html) object. A rect region of a _Surface_ can be get with [`pygame.Surface.subsurface`](https://www.pygame.org/docs/ref/surface.html#pygame.Surface.subsurface). If that doesn't suit your needs, you'll need to go through Numpy and OpenCV (cv2). – Rabbid76 Sep 21 '21 at 15:12
  • Possibly you are searching for [`pygame.mask.Mask`](https://www.pygame.org/docs/ref/mask.html). You can blend (clip) a _Surface_ with a _Mask_. `image.blit(mask.to_surface(), (0, 0), special_flags=pygame.BLEND_RGBA_MULT)` – Rabbid76 Sep 21 '21 at 15:14
  • @Rabbid76 Sorry for the ambiguity. I have clarified in the edit. – PunnyBunny Sep 21 '21 at 23:34
  • The answer is simple: You cannot do this with Pygame. Pygame is just for educational purpose. However you can try to use [`pygame.surfarray.pixels3d`](https://www.pygame.org/docs/ref/surfarray.html#pygame.surfarray.pixels3d) to references the pixel of a _Surface_ directly. It would be nice to see your code. Please read [How to create a Minimal, Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example). – Rabbid76 Sep 22 '21 at 04:56

1 Answers1

0

If you just want to clip an image, you can use the pygame.maks module.

Convert a pygame.mask.Mask to a pygame.Surface with pygame.mask.Mask.to_surface. Use setsurface to specify the source image and the unsetcolor argument to specify the transparent background:

def clip_surface(surf, mask):
    return mask.to_surface(setsurface = surf.convert_alpha(), unsetcolor = (0, 0, 0, 0))

Minimal example:

import pygame

def clip_surface(surf, mask):
    return mask.to_surface(setsurface = surf.convert_alpha(), unsetcolor = (0, 0, 0, 0))

def checker_image(ts, w, h, c1, c2):
    surf = pygame.Surface((w, h))
    [pygame.draw.rect(surf, c1 if (x+y) % 2 == 0 else c2, (x*ts, y*ts, ts, ts)) for x in range((w+ts-1)//ts) for y in range((h+ts-1)//ts)]
    return surf

pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()

background = checker_image(40, *window.get_size(), (129, 128, 128), (96, 96, 96))
image = checker_image(20, 200, 200, (255, 128, 128), (255, 64, 64))
mask_image = pygame.Surface(image.get_size(), pygame.SRCALPHA)
pygame.draw.polygon(mask_image, (255, 255, 255), [(100, 10), (10, 190), (190, 190)])
mask = pygame.mask.from_surface(mask_image)

clipped_image = clip_surface(image, mask)

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))
    image_rect = clipped_image.get_rect(center = window.get_rect().center)
    pygame.draw.rect(window, (0, 0, 0), image_rect, 3)
    window.blit(clipped_image, image_rect)
    pygame.display.flip()

pygame.quit()
exit()
Rabbid76
  • 202,892
  • 27
  • 131
  • 174