Discussion:
Blitting to alpha channel or using another image's alpha channel
Weeble
2008-12-19 02:54:11 UTC
Permalink
Can somebody suggest how to do this? I want to render parts of an
image based on a dynamically generated mask. Right now I'm doing this
by drawing the mask in black and white on another surface, then
iterating over every pixel and changing the alpha value of the source
image to match the red value of the corresponding pixel in the mask
surface, then finally blitting this onto the target surface. This
seems a bit convoluted and I get the feeling there's probably a more
efficient way to do it. Is there a way either to 1. blit an 8-bit
greyscale image directly into the alpha channel of an image, or 2.
directly blit image A onto image B using image C as the alpha channel?
René Dudfield
2008-12-19 03:04:23 UTC
Permalink
hi,

I think you can do this with Surface.set_masks() and surface.set_shifts.

Get the r,g,b,a masks, and shifts, then change them so that only the a
part is valid any more.

Then, I think you could blit the A from one surface to another.



cheers,
Post by Weeble
Can somebody suggest how to do this? I want to render parts of an
image based on a dynamically generated mask. Right now I'm doing this
by drawing the mask in black and white on another surface, then
iterating over every pixel and changing the alpha value of the source
image to match the red value of the corresponding pixel in the mask
surface, then finally blitting this onto the target surface. This
seems a bit convoluted and I get the feeling there's probably a more
efficient way to do it. Is there a way either to 1. blit an 8-bit
greyscale image directly into the alpha channel of an image, or 2.
directly blit image A onto image B using image C as the alpha channel?
Ian Mallett
2008-12-19 03:11:48 UTC
Permalink
You could probably just take the mask surface (RGBA surface) and blit a RGB
surface over it, then take the resulting surface and get your effect:

psuedocode:

mask #RGBA surface
image #RGB surface
#both the same size

mask.blit(image,(0,0))
resultsurface = mask.copy()

#and so on...

Hope this is what you were looking for...
Ian
Weeble
2008-12-19 03:33:42 UTC
Permalink
Thanks for the suggestions! I gave them a shot, but I couldn't get
either to work properly.
Post by Ian Mallett
You could probably just take the mask surface (RGBA surface) and blit a RGB
Okay, I have the mask surface with the mask values in the alpha
channel. As far as I can tell, blitting an RGB image over it resets
the alpha channel to be entirely opaque.
Post by Ian Mallett
I think you can do this with Surface.set_masks() and surface.set_shifts.
Get the r,g,b,a masks, and shifts, then change them so that only the a
part is valid any more.
I tried this, but it seems that the parts of the pixels that aren't
valid just get overwritten. Here's one attempt:

newsurf=pygame.Surface((128,128),flags=pygame.SRCALPHA)
newsurf.blit(sourcesurf,(0,0))
rm,gm,bm,alm=newsurf.get_masks()
rs,gs,bs,als=newsurf.get_shifts()
newsurf.set_masks((alm,0,0,0))
newsurf.set_shifts((als,0,0,0))
masksurf.set_masks((alm,0,0,0))
masksurf.set_shifts((als,0,0,0))
newsurf.blit(masksurf,(0,0))
newsurf.set_masks((rm,gm,bm,alm))
newsurf.set_shifts((rs,gs,bs,als))

I tried to get the blit to treat both surfaces' alpha channels as if
they were red channels, and to ignore everything else. This does
transplant the alpha channel successfully, but it also seems to
overwrite all the other channels too. I tried other combinations, but
had no luck.
Weeble
2008-12-19 03:40:47 UTC
Permalink
Aha! I had better luck with the following:

newsurf=pygame.Surface((128,128),flags=pygame.SRCALPHA)
newsurf.blit(sourcesurf,(0,0))
newsurf.blit(masksurf,(0,0),None,pygame.BLEND_RGBA_MIN)

(This time the mask surface was entirely white with the alpha channel
storing the intended mask value.)
Lenard Lindstrom
2008-12-19 03:54:13 UTC
Permalink
Post by Weeble
newsurf=pygame.Surface((128,128),flags=pygame.SRCALPHA)
newsurf.blit(sourcesurf,(0,0))
newsurf.blit(masksurf,(0,0),None,pygame.BLEND_RGBA_MIN)
(This time the mask surface was entirely white with the alpha channel
storing the intended mask value.)
Yes, you beat me to it. Ignore my post. I incorrectly suggested the mask
have black pixels.
--
Lenard Lindstrom
<len-l-***@public.gmane.org>
Ian Mallett
2008-12-19 03:28:37 UTC
Permalink
You could probably just take the mask surface (RGBA surface) and blit a RGB
surface over it, then take the resulting surface and get your effect:

psuedocode:

mask #RGBA surface
image #RGB surface
#both the same size

mask.blit(image,(0,0))
resultsurface = mask.copy()

#and so on...

Hope this is what you were looking for...
Ian
Lenard Lindstrom
2008-12-19 03:47:51 UTC
Permalink
Post by Weeble
Can somebody suggest how to do this? I want to render parts of an
image based on a dynamically generated mask. Right now I'm doing this
by drawing the mask in black and white on another surface, then
iterating over every pixel and changing the alpha value of the source
image to match the red value of the corresponding pixel in the mask
surface, then finally blitting this onto the target surface. This
seems a bit convoluted and I get the feeling there's probably a more
efficient way to do it. Is there a way either to 1. blit an 8-bit
greyscale image directly into the alpha channel of an image, or 2.
directly blit image A onto image B using image C as the alpha channel?
You can use the BLEND_RGBA_MIN flag with a surface blit. Make sure the
mask is all black and that all the target surface pixels have an alpha
of 255. Before that was available (Python 1.8.1?) there was the
surfarray module. surfarray.pixels_alpha lets one get at the alpha plane
of a surface and manipulate it with array operations. See the shadowed
effects in the Pygame cookbook for an example:

http://www.pygame.org/wiki/ShadowEffects?parent=CookBook

But you need either NumPy or Numeric for this to work.
--
Lenard Lindstrom
<len-l-***@public.gmane.org>
Lenard Lindstrom
2008-12-19 03:55:03 UTC
Permalink
Post by Lenard Lindstrom
You can use the BLEND_RGBA_MIN flag with a surface blit. Make sure the
mask is all black and that all the target surface pixels have an alpha
of 255. Before that was available (Python 1.8.1?) there was the
surfarray module. surfarray.pixels_alpha lets one get at the alpha
plane of a surface and manipulate it with array operations. See the
http://www.pygame.org/wiki/ShadowEffects?parent=CookBook
But you need either NumPy or Numeric for this to work.
The advice with the black mask is wrong. It needs to be white.
--
Lenard Lindstrom
<len-l-***@public.gmane.org>
Michael George
2008-12-19 07:10:30 UTC
Permalink
I've had good luck using colorkey blitting, if the mask is just binary,
and if you have a color you know isn't in the image you want to blit.
For example, if you know the image doesn't have any magenta, blit the
mask in magenta over black, set the color key to black, then blit the
mask onto the image, then set the color key of the image to pink, and
then blit the image. Don't know if this is better or worse than the
other approaches people have mentioned.

--Mike
Post by Weeble
Can somebody suggest how to do this? I want to render parts of an
image based on a dynamically generated mask. Right now I'm doing this
by drawing the mask in black and white on another surface, then
iterating over every pixel and changing the alpha value of the source
image to match the red value of the corresponding pixel in the mask
surface, then finally blitting this onto the target surface. This
seems a bit convoluted and I get the feeling there's probably a more
efficient way to do it. Is there a way either to 1. blit an 8-bit
greyscale image directly into the alpha channel of an image, or 2.
directly blit image A onto image B using image C as the alpha channel?
Loading...