[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

Re: [pygame] Pygame blitting 8-bit surfaces doesn't ignore palettes?



Hi Brian,

On 14-12-02 09:56 AM, Brian Madden wrote:
Hi. I'm working with 8-bit surfaces in Python 2.7 with Pygame 1.9.2pre. Everything I've read says that if I blit an 8-bit surface to another 8-bit surface, Pygame will ignore both palettes and only copy each pixel's 8-bit integer value from the source surface to the destination surface.

A Pygame Surface lets the programmer work with SDL surfaces without the concern of how the pixel data is actually represented in memory. A Pygame Surface pixel is the decoded--unmapped--(R, G, B, A) representation of the color encoded--mapped--into the pixel's actual integer value. Since a Pygame Surface is a color representation its pixel data, it is natural that Surface methods do high level color transformations rather than direct integer value manipulation. So, Surface.blit() actually blits unmapped pixel colors, with the result mapped back into the destination surface binary format (Of course, shortcuts are taken when possible.)

My experience so far is that this is *not* what happens. Rather, I'm seeing that that when blitting, Pygame will use the source surface's palette to get the 24-bit entry for each pixel, then look for that 24-bit value in the destination surface's palette, and then write the 8-bit integer value from the destination surface's palette as the pixel value in the destination surface. If there are no matching 24-bit palette entries for the palettes for both surfaces, then the resulting pixel's integer value in the destination surface is 0.
For two 8-bit surfaces without surface alpha or a color key the Surface.blit() method has SDL do the blit.


I'm just curious as to whether this is this a bug, or is my understanding of how Pygame works with 8-bit surfaces not correct? (In my case I have multiple surfaces each with their own palettes, and if I blit a pixel value of "1" then I want it to be "1" on my destination surface regardless of what any of the palettes are! :)

Yes, the Pygame.blit() method description lacks detail. It assumes the reader has some understanding of the SDL library. But this is no longer a reasonable expectation. The pygame.Surface documentation needs updating.


It is possible to do a direct integer copy with pygame.PixelArray. Here is a basic example:

def pxcopy(source, target, dest=None):
    """Copy raw surface pixels from source to target

       Argument dest is the (x, y) coordinates within the target surface
       where the source surface will be copied. The default is (0, 0).
       This simple example fails if the source rect does not fit entirely
       within the target boundary."""

    if dest is None:
        dest = (0, 0)
    x, y = dest
    s = PixelArray(source)
    w, h = s.shape
    t = PixelArray(target)
    t[x:x+w, y:y+h] = s

>>> import pygame
>>> pygame.display.init()  # Need this to enable Surface.set_palette()
>>> from pygame import Surface, PixelArray
>>> palette = [(i, i, i) for i in range(256)]
>>> target = Surface((100, 100), 0, 8)
>>> target.set_palette(palette)
>>> target.set_palette(palette)
>>> target.fill((30, 30, 30))
 <rect(0, 0, 100, 100)>
>>> target.get_at((10, 15))
(30, 30, 30, 255)
>>> target.get_at_mapped((10, 15))
30
>>> source = Surface((20, 30), 0, 8)
>>> source.fill((255, 0, 0))
<rect(0, 0, 20, 30)>
>>> source.get_at((0, 0))
(255, 0, 0, 255)
>>> source.get_at_mapped((0, 0))
96
>>> pxcopy(source, target, (10, 15))
>>> target.get_at_mapped((10, 15))
96
>>> target.get_at((10, 15))
(96, 96, 96, 255)
>>> target.get_at((0, 0))
(30, 30, 30, 255)
>>>


Lenard Lindstrom