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

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



Ah, PixelArray. Of course! I'm actually using a PixelArray elsewhere in my code to extract the integer values of the surface which is fed to a dot matrix display. (My project is for a pinball machine.) My surface blits are used to create an on screen representation in a display window of the physical dot matrix display which is where I use the palette to set the colors. (The actual physical dot matrix display only has 16 shades of pixel brightness which is why I'm using an 8-bit surface.)

Anyway I don't know why I didn't think of using a PixelArray here, but yeah, that makes total sense. Awesome.

Thanks so much for the feedback and ideas!
Brian

On Wed, Dec 3, 2014 at 4:25 PM, Lenard Lindstrom <len-l@xxxxxxxxx> wrote:
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




--
Brian Madden
Mission Pinball (blog | twitterMPF software framework | sample games)