Search code examples
c++pngsdlsdl-image

SDL2 / SDL Image strange PNG behavior with RGB values


I found an interesting thing that I'm not able to debug but I'd like to solve because it's crucial for the development of a small tool I need.

Basically all reduces to the fact that I load into an SDL_Surface a PNG loaded with IMG_Load("filename.png"). The skeleton of the code is

surface = IMG_Load("filename.png");

Then this tool should copy this surface pixel by pixel to another by applying a color map (which is just an unordered_map<u32,u32>). This basically works, but many pixel have a slight change in their RGB values so this substitution fails.

For example, while a pixel 255,255,255 is correctly stored as 0xFFFFFFFF in the surface, another pixel, let's say 81,60,48 becomes 82,62,51. I thought about gamma correction but the image itself is not drawn since the color switch is applied before two surfaces, and the latter gets drawn to a texture just afterwards.

Any clues? I'm not interested in finding another solution to my problem since I require to be able to do precise pixel color switching with a fixed map, I'd just like to understand why this is happening (since PNG should just contain exact values, being lossless) and solve it.


Solution

  • Ok, I found the cause and fixed it. The problem resides in how the PNG is loaded through the OS X frameworks. According to this thread, this is related to the embedded calibration which is somehow taked into account while loading the image. The proposed fix is the following:

    CGFloat whitePoint[3] = { 1, 1, 1 };
    CGFloat blackPoint[3] = { 0, 0, 0 };
    CGFloat gamma[3] = { 2.2, 2.2, 2.2 };
    CGFloat matrix[9] = {
        1, 1, 1,
        1, 1, 1,
        1, 1, 1
    };
    CGColorSpaceRef color_space =
        CGColorSpaceCreateCalibratedRGB(
                                    whitePoint, blackPoint, gamma, matrix
                                    );
    

    By patching ImageIO.m of SDL_Image and rebuilding it from sources the problem resolved itself. I wonder why it hasn't been included in SDL_Image (or rather, just a version with custom values).