Search code examples
arrayspython-3.xnumpypygamepygame-surface

Blit 8-bit colour to screen by converting binary values in file to 3-3-2 colour


I have recently been working on a small emulated 8-bit CPU that I wish to have graphics capabilities. At the moment, it dumps the upper 16K of ram as a binary file every cycle for it to be read as "video memory." I've made a simple pygame-based program that reads the file and then attempts to convert it to a 128x128 grayscale 3d array for it to be directly blit as a surface. It does not work. I thought I could directly cut to the chase and just ask, Is there a way to read a binary file and convert its bytes into 3-3-2 colour, regardless of the file's actual content?

Here's my current code:

import pygame

import numpy as np

from time import sleep

class Viewer:
    def __init__(self, update_func, display_size):
        self.display_size = display_size
        self.update_func = update_func
        pygame.init()
        self.display = pygame.display.set_mode(display_size)

    def set_title(self, title):
        pygame.display.set_caption(title)

    def start(self):
        running = True
        while running:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    running = False

            z = self.update_func()
            surf = pygame.surfarray.make_surface(z)
            new = pygame.transform.scale(surf, self.display_size)
            self.display.blit(new, (0, 0))

            pygame.display.update()

        pygame.quit()


def update():
    file = open("core.bin", "rb")
    value = bytearray(file.read())
    lst = list(value)
    file.close()
    image = [[ [lst for col in range(len(lst))] for col in range(len(lst))] for row in range(len(lst))]
    print(image)
    sleep(0.25)
    return image


viewer = Viewer(update, (512, 512))
viewer.start()
update()

I've tried numpy.dstack in the past, and normal 3d arrays work fine with my code, just not binary files. And yes, my bin files are exactly 16k. No error messages either.

Not Working

My Intent


Solution

  • Made a 128 *128 8-bit image, for testing; assuming RRRGGGBB, as opposed to BBGGGRRR. Flip 'em around, if that's the case. Found that numpy's reshape() wrecked images. Injecting values directly into the expected numpy location gave better results. Should be faster, too.

    enter image description here

    #! /usr/bin/env python3
    import pygame
    import numpy as np
    from time import sleep
    from random import randint
    
    class Viewer:
        def __init__(self, update_func, display_size):
            self.display_size = display_size
            self.update_func = update_func
            pygame.init()
            self.display = pygame.display.set_mode(display_size)
    
        def set_title(self, title):
            pygame.display.set_caption(title)
    
        def start(self):
            running = True
            while running:
                for event in pygame.event.get():
                    if event.type == pygame.QUIT:
                        running = False
    
                z = self.update_func()
                surf = pygame.surfarray.make_surface(z)
                new = pygame.transform.scale(surf, self.display_size)
                self.display.blit(new, (0, 0))
    
                pygame.display.update()
    
            pygame.quit()
    
    '''
    32    111 000 00  = randint( 0, 7 ) << 5   r
    64    000 111 00  = randint( 0, 7 ) << 2   g
    96    000 000 11  = randint( 0, 3 )        b
    128   111 111 11  = randint( 0, 255 )      w
    '''
    
    screenshot = bytearray( 128 *128 )  ##  128 *128 8-bit image, just for testing
    
    for red   in range( 0,        128 *32 ):  screenshot[ red ] = randint( 0, 7 ) << 5
    for green in range( 128 *32,  128 *64 ):  screenshot[ green ] = randint( 0, 7 ) << 2
    for blue  in range( 128 *64,  128 *96 ):   screenshot[ blue ] = randint( 0, 3 )
    for white in range( 128 *96,  128 *128 ):  screenshot[ white ] = randint( 0, 255 )
    
    row, col = 0, 0
    arr = np .zeros( ( 128, 128, 3 ) )  ##  generate empty numpy array
    
    for byte in screenshot:
        rrr = int( ( ( byte & 0b11100000 ) >> 5 ) *36.4285714286  )  ##  red
        ggg = int( ( ( byte & 0b00011100 ) >> 2 ) *36.4285714286 )  ##  green
        bb = int( ( byte & 0b00000011 ) *85 )  ##  blue  --  multiplied to max 255 range
        
        arr[ col ][ row ][ 0 ] = rrr  ##  insert color values directly into numpy cells
        arr[ col ][ row ][ 1 ] = ggg
        arr[ col ][ row ][ 2 ] = bb
    
        col += 1
        if col == 128:
            col = 0  ##  \r  carriage return
            row += 1  ##  \n  newline
    
    
    def update():
        image = arr
        sleep(0.25)
        return image
    
    viewer = Viewer(update, (512, 512))
    viewer.start()
    update()