Search code examples
pythonpython-3.xpygamepixel

Pygame Scaling 28x28 Pixels to 420x420


Hello I ran into a bit of a problem. I am trying to create a GUI for the Mnist dataset and I need a window with 28x28 pixels however that is roughly 420x420 in screenspace. Which means every pixel should appear 15 times it's original size. I also need to draw to the Screen. I haven't found anything so far that actually works.

Thanks in advance. (If you need clarification please just leave a comment)

Pygame version 1.9.6

Python version 3.6.7


Solution

  • A trivial answer to this is to simply scale the bitmap with pygame.transform.smoothscale(), then blit() that to the screen. However this scaling is computationally expensive.

    It's fairly easy to define a "pixel" sprite that scales and positions itself based on a sub-size of the screen. When the pixel is set to an (x,y), this is simply translated to the scaled position on the screen.

    I parameterised the screen size and pixel-count to easily scale the pixels to whatever is needed.

    The code uses a list of co-ordinates to draw a bunch of pixel-sprites to the screen.

    import pygame
    import sys
    
    # Window size
    WINDOW_WIDTH  = 420
    WINDOW_HEIGHT = 420
    FPS           = 60
    PIXELS_WIDTH  = 28  # How many big-pixels vertically
    PIXELS_HEIGHT = 28  # How many big-pixels horizontally
    # background & colours
    INKY_BLACK    = ( 28,  28,  58)
    EGG_YELLOW    = (255, 233, 132)
    
    class PixelSprite( pygame.sprite.Sprite ):
        def __init__( self, x, y, width=WINDOW_WIDTH//PIXELS_WIDTH, height=WINDOW_HEIGHT//PIXELS_HEIGHT ):
            pygame.sprite.Sprite.__init__( self )
            self.image  = pygame.Surface( ( width, height ), pygame.SRCALPHA )
            self.rect   = self.image.get_rect()
            self.rect.x = x * WINDOW_WIDTH  // PIXELS_WIDTH
            self.rect.y = y * WINDOW_HEIGHT // PIXELS_HEIGHT
            self.fill( INKY_BLACK )  # off
    
        def update( self ):
            pass
    
        def fill( self, colour ):
            self.image.fill( colour )
    
    
    
    ### MAIN
    pygame.init()
    pygame.font.init()
    SURFACE = pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE
    WINDOW  = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), SURFACE )
    pygame.display.set_caption("Big-Pixel Sprite Test")
    
    
    pixels = pygame.sprite.Group() 
    for coords in [ ( 8,10 ), ( 18, 10 ), (6,15), (7,16), (8,17), (9,17), (10,17), (11,18), (12,18), (13,18), (14,18), (15,18), (16,17), (17,17), (18,17), (19,16), (20,15) ] :
        x,y = coords
        pixel = PixelSprite( x, y )
        pixel.fill( EGG_YELLOW )
        pixels.add( pixel )
    
    clock = pygame.time.Clock()
    done  = False
    while not done:
    
        # Handle user-input
        for event in pygame.event.get():
            if ( event.type == pygame.QUIT ):
                done = True
            elif ( event.type == pygame.KEYDOWN ):
                if ( event.unicode == 'q' or event.scancode == pygame.K_q ):
                    done = True
    
        # Repaint the screen
        pixels.update()
        WINDOW.fill( INKY_BLACK )
        pixels.draw( WINDOW )
    
        pygame.display.flip()
        # Update the window, but not more than 60fps
        clock.tick_busy_loop( FPS )
    
    pygame.quit()
    

    pixel-sprite-output.png