Pygame: Draw single pixel

The usual method of drawing a point on a Surface or the display is to use [`pygame.Surface.set_at']:

window_surface.set_at((x, y), my_color)

However, this function is very slow and leads to a massive lack of performance if more than 1 point is to be drawn.

Minimal example where each pixel is set separately: repl.it/@Rabbid76/PyGame-DrawPixel-1

import pygame

pygame.init()
window = pygame.display.set_mode((300, 300))

run = True
while run:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    window.fill(0)

    rect = pygame.Rect(window.get_rect().center, (0, 0)).inflate(*([min(window.get_size())//2]*2))
    
    for x in range(rect.width):
        u = x / (rect.width - 1)
        color = (round(u*255), 0, round((1-u)*255))
        for y in range(rect.height):
            window.set_at((rect.left + x, rect.top + y), color) 
    
    pygame.display.flip()

pygame.quit()
exit()

Another option is to use a "pygame.PixelArray" object. This object enables direct pixel access to Surface objects. A PixelArray pixel item can be assigned directly. The pixel can be accessed by subscription. The PixelArray locks the Surface, You have to close() it when you have changed the pixel:

pixel_array = pygame.PixelArray(window_surface)

pixel_array[x, y] = my_color
pixel_array[start_x:end_x, start_y:end_y] = my_color

pixel_array.close()

Minimal example that set one line of pixels at once: repl.it/@Rabbid76/PyGame-DrawPixel-2

import pygame

pygame.init()
window = pygame.display.set_mode((300, 300))

run = True
while run:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    window.fill(0)

    rect = pygame.Rect(window.get_rect().center, (0, 0)).inflate(*([min(window.get_size())//2]*2))

    pixel_array = pygame.PixelArray(window)
    
    for x in range(rect.width):
        u = x / (rect.width - 1)
        color = (round(u*255), 0, round((1-u)*255))
        pixel_array[rect.left + x, rect.top:rect.bottom] = color 
    
    pixel_array.close()
    
    pygame.display.flip()

pygame.quit()
exit()

One way of doing that is to draw a line staring and ending at the same point.

pygame.draw.line(surface, (255,255,255), (x,y), (x,y))

For those who are interested in a more modern answer to the question you can use pygame.draw.circle() to draw a single pixel at a given position (or center).

pygame.draw.circle(surface, color, center, 0)

The documentation specifically says:

radius (int or float) -- radius of the circle, measured from the center parameter, a radius of 0 will only draw the center pixel


You can do this with surface.set_at():

surface.set_at((x, y), color)

You can also use pygame.gfxdraw.pixel():

from pygame import gfxdraw
gfxdraw.pixel(surface, x, y, color)

Do note, however, the warning:

EXPERIMENTAL!: meaning this api may change, or dissapear in later pygame releases. If you use this, your code will break with the next pygame release.

You could use surface.fill() to do the job too:

def pixel(surface, color, pos):
    surface.fill(color, (pos, (1, 1)))

You can also simply draw a line with the start and end points as the same:

def pixel(surface, color, pos):
    pygame.draw.line(surface, color, pos, pos)

Tags:

Python

Pygame