2

I need to create a 8-bit surface programmatically from an a list array. This is a constraint I have. So far, I got it working with this solution:

width = int(4)
height = int(4)
image_data_raw = ['\xf1', '\xf1', '\xf1', '\xf1',
              '\xf1', '\x00', '\x00', '\xf1',
              '\xf1', '\x00', '\x00', '\xf1',
              '\xf1', '\xf1', '\xf1', '\xf1']

def make_surface_from_raw_data():
    global image_data_raw
    global width
    global height

image_data_raw_bytes = [ord(i) for i in image_data_raw]

test_surface = pygame.Surface((width, height))
pxarray = pygame.PixelArray(test_surface)

i = int(0)
for y in range(height):
    for x in range(width):
        pxarray[x, y] = pygame.Color(image_data_raw_bytes[i],
                                     image_data_raw_bytes[i],
                                     image_data_raw_bytes[i])
        i += 1

new_surface = pxarray[:, :].make_surface()
image_data_2d_surface = new_surface
return image_data_2d_surface

However, I am not happy with it because it is too slow.

My "image_raw_data" will be much larger than 4x4 and streamed from a serial source. Also they are always 8-bit gray scale. Therefore, there is no need to convert from 8-bit to rbga.

I'd like to know if there is faster way to accomplish this task. In my loop there are several conversions from char to 8-bit int then to rgba.

thank you very much.

gmmo
  • 2,577
  • 3
  • 30
  • 56

1 Answers1

2

If you have NumPy installed, using something like is significantly faster:

def make_surface_from_raw_data():
    global image_data_raw
    global width
    global height

    # we create a numpy array from image_data_raw
    # and do some bit shifting so that the single value is used for each color channel
    # then we have to reshape that array into the right size
    data = np.fromiter((x | x << 8 | x << 16 for x in (ord(i) for i in image_data_raw)), dtype=np.uint32).reshape(width, -width)

    # now let's create a Surface in the right dimension 
    # that's faster than letting pygame figure out the size from the array (with the make_surface function)
    surface = pygame.Surface((width, height))

    # blit_array is the fastet way to get the value of the array into the Surface
    # (faster than creating an array from the Surface and updating it)
    pygame.surfarray.blit_array(surface, data)

    return surface  
sloth
  • 99,095
  • 21
  • 171
  • 219