Search code examples
pythonpygamepaint

Copying the surface and bliting it on the screen


So I am working on my pygame version of Paint and am currently working on UNDO property. I have figured it out where I would save the screen (was thinking to use deque) but can't figure it out how to save the content on the screen, what is the method for saving the content on the screen so I can append it on the deque every time there is a change made on the screen and get back to the old screen if user clicks undo button. I have tried copy() method but I guess I am not implementing it right..

It would be of much help if you give me an example how to blit the copy of the old surface.

Hope I was clear enough.


Solution

  • Here's a quick example of making a copy of the screen surface.

    It calls window.copy() to make an "undo point" on start, and whenever the user clicks on [KEEP]. If the user clicks on [UNDO], the copied surface is re-blitted to the screen to erase the existing one.

    undo-example

    I'm not sure if keeping a copy of the screen is a great way to do this, it uses a lot of memory. A better approach would be to store the drawing primitives in a list, then only re-draw up to N-1 entry on undo.

    import pygame
    
    # Window size
    WINDOW_WIDTH    = 400
    WINDOW_HEIGHT   = 400
    WINDOW_SURFACE  = pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE
    
    DARK_BLUE = (   3,   5,  54 )
    YELLOW    = ( 250, 250,   0 )
    RED       = ( 200,   0,   0 )
    
    
    ### initialisation
    pygame.init()
    pygame.mixer.init()
    window = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), WINDOW_SURFACE )
    pygame.display.set_caption("Undo Example")
    
    
    font = pygame.font.SysFont('freesansbold.ttf', 30)
    undo_text = font.render( "[UNDO]", True, YELLOW )
    undo_rect = undo_text.get_rect()
    undo_rect.x = 50
    keep_text = font.render( "[KEEP]", True, YELLOW )
    keep_rect = keep_text.get_rect()
    keep_rect.x = 200
    
    window.fill( DARK_BLUE )
    
    ### The undo buffer
    undo_save = None
    
    ### Main Loop
    clock = pygame.time.Clock()
    pen_down = False
    last_point = None
    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.MOUSEMOTION ):
                mouse_pos = event.pos
                if ( pen_down ):
                    if ( last_point != None ):
                        pygame.draw.line( window, RED, last_point, mouse_pos )
                        last_point = mouse_pos
            elif ( event.type == pygame.MOUSEBUTTONDOWN ):
                pen_down = True
                if ( last_point == None ):
                    last_point = event.pos
            elif ( event.type == pygame.MOUSEBUTTONUP ):
                pen_down = False
                last_point = None
    
                ### Handle the KEEP/UNDO button presses
                if ( undo_rect.collidepoint( event.pos ) ):
                    if ( undo_save != None ):
                        window.blit( undo_save, ( 0, 0 ) )
                        print( "UNDO Image Restored" )
                if ( keep_rect.collidepoint( event.pos ) ):
                    undo_save = window.copy()
                    print( "UNDO Image Saved" )
    
        window.blit( undo_text, undo_rect )
        window.blit( keep_text, keep_rect )
    
        # Update the window, but not more than 60fps
        pygame.display.flip()
    
        # make the first undo automatically
        if ( undo_save == None ):
            undo_save = window.copy()
    
        # Clamp FPS
        clock.tick(60)
    
    
    pygame.quit()