Texture.FRgba32 to Pygame.Surface with wrong colors.(solved)

Hi all,

In a script I wrote, a Texture is created using base.win.addRenderTexture to recuperate the screen output.
I set the image format: image.setFormat(Texture.FRgba32)
then I recuperate the string: getRamImage().getData()
which I load with pygame.image.fromstring(data, size, “RGBA”)
The result of this is most surprising:
Instead of being loaded as RGBA the image seems to have been loaded in BGRA(that doesnt exist right :wink: ), I mean by that:
-Red became Blue
-Blue became Red
-Yellow became Teal (G+B instead of G+R)

I wondered if there was actually a problem with Panda3d, or with pygame.
Btw: Panda doesnt seem to have Format flag for standard 24 byte RGB… anyone knows why?

Right, Panda stores texture images in BGRA order. It does this because this is the way modern 3-D graphics cards typically store their texture images internally, so this is a convenient format for loading textures quickly into graphics memory.

Note that the Texture.Format mode has more to do with the size of the individual components, not their ordering. It describes the size of the texture components as they appear in graphics memory, and has little to do with the size or ordering of the components in system memory. This is why there’s no FRgb24: because 3-D graphics cards typically don’t support this mode. (But if you didn’t want the alpha channel, you could use FRgb, which is the same idea.)

You will either have to convert the pixels by hand, or find some way to tell pygame that you are loading images in BGR order. Or, you can just render everything in the opposite color from the final desired output. :slight_smile:

David

Well, I think I understand now.

But here is the only solution I’ve found:

self.screenTexture.setFormat(Texture.FRgb332)
            data = self.screenTexture.getRamImage().getData()
            sx=self.screenTexture.getXSize()
            sy=self.screenTexture.getYSize()
            b,g,r,a = Image.fromstring("RGBA",(sx,sy),data).split()
            image= pygame.image.fromstring(Image.merge("RGBA",
                                                       (r,g,b,a)).tostring(),
                                           (sx,sy),
                                           "RGBA",
                                           True)
            self.screen.fill((0,0,0))
            self.screen.blit(image,(0,0))
            pygame.display.flip()

This slows the FPS by half.
Would It be reasonable to write my own decoder that would set pixel by pixel in pygame?

You just need a C routine that reverses every three bytes of a string. You might be able to use the Numeric library cleverly, or I bet there exists some image-processing library that will do this quickly.

I assume you’ve seen this thread? https://discourse.panda3d.org/viewtopic.php?t=4323

Hi,

check out the Surface.set_shifts() and Surface.set_masks() methods added in pygame 1.8.1

With this you can tell pygame to treat the surface as BGRA even though it is RGBA in memory.

r,g,b,a = surf.get_shifts()
rm,gm,bm,am = surf.get_masks()

surf.set_shifts((b,g,r,a))
surf.set_masks((bm,gm,rm,am))

then you could use the Surface.get_buffer() method, or the image.tostring function.

cheers,