SFML community forums

Help => Graphics => Topic started by: SFMLNewGuy on October 13, 2020, 09:14:30 am

Title: Little help with 'getPixelPtr()'
Post by: SFMLNewGuy on October 13, 2020, 09:14:30 am
Hello everyone,

According the the SFML documents it says

getPixelPtr() -
The returned value points to an array of RGBA pixels made of 8 bits integers components. The size of the array is width * height * 4 (getSize().x * getSize().y * 4). Warning: the returned pointer may become invalid if you modify the image, so you should never store it for too long. If the image is empty, a null pointer is returned.

So I'm interested how this works and if someone can give a bare minimum example? I find this all very interesting, especially how you can load a raw png file for example.

At the moment I am doing some raycasting and trying to figure out a way to make it more efficient because it's a bit slow (from the transfer to RGBA from ARGB) and I was wondering if this would be something to look too? When I am setting a pixel color I'm doing:

(loop rays)
(drawCeiling)
(calculate offset)
(loop wallTop to wallBottom)
        auto texelColor = images[textureNum]->getPixel(textureOffsetX,textureOffsetY)
         drawPixels(x,y,texelColor)   // overloaded for sf::Uint32 and sf::Color
(draw floor)

----
        void drawPixel(int x, int y, sf::Uint32 color) {
                // The example I was learning from used ARGB so a nice gentleman gave me the convert
                const sf::Uint32 rgbaPixel =
                     ((color & 0xff000000) >> 24) | ((color & 0x00ffffff) << 8);        // or to an RGBA uint32:

                colorBuffer->setPixel(x,y,sf::Color(rgbaPixel));
        }
        void drawPixel(int x, int y, sf::Color color) {
                colorBuffer->setPixel(x, y, color);
        }
 
How would I do something simular using the Ptr function and I know it returns Uint8's, but the image it self is created 64x64 so I wouldn't multiple by 4 right?

Thanks, be safe and take care everyone.
Title: Re: Little help with 'getPixelPtr()'
Post by: fallahn on October 13, 2020, 01:56:14 pm
sf::Image is fundamentally a wrapper around an array of bytes representing the pixels, with a few utility functions to make setting pixel values a bit simpler. You could do the same thing yourself with a std::vector<sf::Uint8> and a function for setting the pixel values at a given index. To see how sf::Image does this you can look at the source: https://github.com/SFML/SFML/blob/master/src/SFML/Graphics/Image.cpp

Image file loading is a bit more involved - image files are rarely stored as raw colour data, usually opting for some kind of compression. What this compression is and how it's implemented varies wildly. Fortunately sf::Image hides that away for us, using a library called stb_image under the hood. The source for that library is publicly available and a quick glance at it will tell you most image loading is non-trivial to say the least. Hooray for sf::Image looking after that for us! https://github.com/nothings/stb/blob/master/stb_image.h

The reason the pixel array is W x H x 4 is because W and H are measured in *pixels* - not bytes - where each pixel has a value of 4 bytes - R,G,B and A. When dealing with the pixelPtr() function you'll always need to bear this in mind.

To your problem of performance: this is almost certainly down to the conversion process (as I mentioned in the previous topic). Ideally you want to eliminate this conversion completely. There are going to be two ways of doing this - make your destination ARGB compatible or make your source generate RGBA data. While it is technically possible to alter the format of sf::Texture it's hacky and convoluted, so in this case let's consider it not possible. This leaves us with modifying the source to generate RGBA data.

What I can say here is that SDL will support RGBA pixels, but I couldn't say much else without seeing the example code you're working from. I am pretty sure, however, that as you're converting code anyway it's totally possible to make it output RGBA pixels for your render target, and this is likely the way to go. Perhaps you could share the source you're working from?
Title: Re: Little help with 'getPixelPtr()'
Post by: SFMLNewGuy on October 19, 2020, 05:07:52 am
Hey Fallahn,

Sorry to get back late. Had a personal emergency that is all good now!

So I uploaded it to GitHub for you! I appreciate the help by the way and on the previous message!

https://github.com/LedLoaf/RaycastingSFML (https://github.com/LedLoaf/RaycastingSFML)
Title: Re: Little help with 'getPixelPtr()'
Post by: fallahn on October 19, 2020, 03:21:30 pm
Hi,

So I've had a look at this and here are a few comments:

Firstly the fact that you use sf::Image to load the textures is a good thing. It means that the images are automatically in RGBA format and no conversion is being done on them. In fact when I looked to see how often the conversion function was being called there were only two places: here (https://github.com/LedLoaf/RaycastingSFML/blob/main/Src/Source.cpp#L460) and here (https://github.com/LedLoaf/RaycastingSFML/blob/main/Src/Source.cpp#L489). The fact that the colour values are called explicitly on them makes it easy to remove the conversion - just change 0xff777777 to 0x777777ff! Hopefully my previous explanations should make this obvious as to why this is.

Elsewhere I noticed this function (https://github.com/LedLoaf/RaycastingSFML/blob/main/Src/Source.cpp#L435) which doesn't appear to be used anywhere, but should you decide to use it will have the bytes of the colour value in the wrong order. However this should be trivial to correct so I shall leave this as an exercise for you ;)

As for performance the only potential bottleneck I noticed was here (https://github.com/LedLoaf/RaycastingSFML/blob/main/Src/Source.cpp#L494). If you change colorBufferTexture->loadFromImage() to colorBufferTexture->update() you're likely to get an improvement as loadFromImage() probably recreates the texture each time, instead of updating the existing one.

I also noticed this line (https://github.com/LedLoaf/RaycastingSFML/blob/main/Src/Source.cpp#L505) which doesn't actually do anything to affect the output and can be removed completely, as the output of generate3DProjection() overwrites everything done by it. static_cast<sf::Uint8>(0xFF000000) is also somewhat redundant - you merely end up truncating a 32bit value to 00.

Overall, having run the program through visual studio's profiler (Debug->performance profiler), the hottest function appears to be generate3DProjection() - which is fair enough as that's where the pixel by pixel copies are happening. My benchmarks in release mode showed ~150fps or average of 6ms per frame. This seems reasonable considering most games target 60 or even 30fps, but without anything to compare it with, for example the performance of the original SDL version, I couldn't say if this is a really good benchmark or not.
Title: Re: Little help with 'getPixelPtr()'
Post by: SFMLNewGuy on October 22, 2020, 03:49:44 am
Thanks a lot! I appreciate all the information. I plan to mess around with it more this weekend!

Thanks again!
Title: Re: Little help with 'getPixelPtr()'
Post by: fallahn on October 23, 2020, 06:21:54 pm
You're welcome, I hope it's helpful. I haven't studied ray tracing techniques before, it was interesting to learn about  ;D