Question: And so, I wrote a test function to invert an image in a specific area and shape with the ability to change the location and size. The only problem is that the larger the size of the mask, the slower the mask moves or enlarges. Are there any solutions to speed up the code? import pygame fps = pygame.time.Clock() pygame.init() pygame.event.set_allowed([pygame.QUIT]) # declare all necessary vars image = pygame.image.load('...\\image.png') org_mask_inversion = pygame.image.load('...\\mask_inversion.png') ix, iy = 0, 0 size_x, size_y = 100, 100 current_ix, current_iy = ix, iy current_size_x, current_size_y = size_x, size_y screen = pygame.display.set_mode([960, 720]) mask_inversion = pygame.transform.scale(org_mask_inversion, (size_x, size_y)) image2 = image # function that inverts color values def inversion_of_color(r, g, b): return [255 - r, 255 - g, 255 - b] # image inversion function def inversion_of_surface(surface, sx, sy): # it creates a new surface of the same size as the one specified in the function inverted_surface = pygame.surface.Surface(mask_inversion.get_size(), pygame.SRCALPHA) # passes over the entire size of the mask for x in range(mask_inversion.get_width()): for y in range(mask_inversion.get_height()): # takes the color of the mask pixel m_color = mask_inversion.get_at((x, y)) # checks if the pixel is green color if m_color.g == 255: # checks if the mask extends beyond the screen area if 0 <= sx + x <= screen.get_width() - 1 and 0 <= sy + y <= screen.get_height() - 1: # takes the color of a surface pixel color = surface.get_at((x + sx, y + sy)) # inverts the pixel color and inserts the inverted pixel into the created surface inverted_color = inversion_of_color(color.r, color.g, color.b) inverted_surface.set_at((x, y), inverted_color) return inverted_surface running = True while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False fps.tick(60) keys = pygame.key.get_pressed() pygame.display.set_caption(f'Inverted Image {round(fps.get_fps())} x:{ix} y:{iy}') if keys[pygame.K_LEFT]: ix -= 6 elif keys[pygame.K_RIGHT]: ix += 6 if keys[pygame.K_UP]: iy -= 6 elif keys[pygame.K_DOWN]: iy += 6 if keys[pygame.K_a]: size_x -= 6 size_y -= 6 mask_inversion = pygame.transform.scale(org_mask_inversion, (size_x, size_y)) elif keys[pygame.K_d]: size_x += 6 size_y += 6 mask_inversion = pygame.transform.scale(org_mask_inversion, (size_x, size_y)) # checks if the size or location of the mask has changed if current_ix != ix or current_iy != iy or current_size_x != size_y or current_size_x != size_x: image2 = inversion_of_surface(image, ix, iy) current_ix, current_iy = ix, iy current_size_x, current_size_y = size_x, size_y screen.blit(image, (0, 0)) screen.blit(image2, (ix, iy)) pygame.display.update() pygame.quit()  Here's the image of the mask itself enter image description here I tried different ways to cache the result, I tried to use numpy arrays, and tried to check the already changed pixels, but all to no avail.

Answer: You can achieve what you want with Pygame functions alone and without for loops:  Use pygame.mask.from_surface and pygame.mask.Mask.to_surface to prepare the mask. The mask must consist of two colors, opaque white (255, 255, 255, 255) and transparent black (0, 0, 0, 0):  mask = pygame.mask.from_surface(inversionMaskImage) inversionMask = mask.to_surface(setcolor=(255, 255, 255, 255), unsetcolor=(0, 0, 0, 0))   Get the effected area with pygame.Surface.subsurface  subSurface = surface.subsurface(pygame.Rect((sx, sy), mask.get_size()))   Create a copy of the mask and blend the inverted surface with the mask using the BLEND_SUB mode (see pygame.Surface.blit)  finalImage = mask.copy() finalImage.blit(invertedArea, (0, 0), special_flags = pygame.BLEND_MULT)  See also Blending and transparency and Clipping  Minimal example  import pygame pygame.init() screen = pygame.display.set_mode((1024, 683)) clock = pygame.time.Clock() image = pygame.image.load('image/parrot1.png').convert_alpha() inversionMaskImage = pygame.Surface((200, 200), pygame.SRCALPHA) pygame.draw.circle(inversionMaskImage, (255, 255, 255), inversionMaskImage.get_rect().center, inversionMaskImage.get_width()//2) mask = pygame.mask.from_surface(inversionMaskImage) inversionMask = mask.to_surface(setcolor=(255, 255, 255, 255), unsetcolor=(0, 0, 0, 0)) def invert_surface(surface, mask, sx, sy): areaRect = pygame.Rect((sx, sy), mask.get_size()) clipRect = areaRect.clip(surface.get_rect()) subSurface = surface.subsurface(clipRect) finalImage = mask.copy() finalImage.blit(subSurface, (clipRect.x - areaRect.x, clipRect.y - areaRect.y), special_flags = pygame.BLEND_SUB) return finalImage run = True while run: clock.tick(100) for event in pygame.event.get(): if event.type == pygame.QUIT: run = False areaRect = pygame.Rect(pygame.mouse.get_pos(), (0, 0)).inflate(inversionMask.get_size()) invertedArea = invert_surface(image, inversionMask, areaRect.x, areaRect.y) screen.fill('black') screen.blit(image, (0, 0)) screen.blit(invertedArea, areaRect) pygame.display.flip() pygame.quit() exit()