Texture2D.LoadImage color/colour problems

I’m doing some version of palette swapping for sprites, and I keep hitting a problem.
Unity seems to be altering the colours in PNG files when importing.

As you can see below, when the sprite is saved it has very specific values.
when I run the code below to immediately save the png after loading, I get different colours.

If anyone has any idea about what I’m doing wrong here, I’d love to know.
To avoid any arguments: I’m not here for a discussion about whether this is the best way to do this, I’m after an answer to a weird problem.

alt text

    byte[] spriteBytes = File.ReadAllBytes(fullPath);

    Texture2D temp = new Texture2D(2, 2, TextureFormat.RGBA32,false);
    //to get the proper size
    if (!temp.LoadImage(spriteBytes)) {
      Debug.LogError("image file:[" + fullPath + "] could not be read.");
    //import again at the correct size (apparently this is a thing).
    spriteSheetTexture = new Texture2D(temp.width, temp.height, TextureFormat.RGBA32,false);
    if (spriteSheetTexture.LoadImage(spriteBytes))  {
      File.WriteAllBytes("a path dont worry about it.png", spriteSheetTexture.EncodeToPNG());

unity version info: 2019.1.7f1personal
Visual studio: 2017 15.9.12 community

Well, I just did a few tests with LoadImage and EncodePNG. It looks like Unity has issues with loading png files that contain gamma information. When I create a 4k texture with all 16 million colors in Unity and export as png, the resulting file does not contain any of the usual png chunks. It only contains the IHDR as well as several IDAT chunk with the data. If I load this png dynamically through LoadImage, everything is right.

However when I open that png in mspaint and re-save it, the new png will have a sRGB, gAMA and pHYs chunk as usual. When we load this at runtime we get errors in the colors that is 0 at black (0) ramps up to “-9” at 128 and ramps back down to 0 as we go further up to 255. So it seems to be a wrong gamma correction that is going on since gamma correction is a power function. That’s why the error is largest at 128 ( 0.5 ) and 0 at 0 (0.0) and 255 (1.0).

I would recommend to file a bug report


I just tried using [my PNGTools][1] which I wrote for [this question over here][2] and removed the sRGB, gAMA and pHYs chunks before letting Unity load the png and it seems to work with my test image.

Specifically instead of

byte[] spriteBytes = File.ReadAllBytes(fullPath);

do this:

    byte[] spriteBytes;
    using (var fileStream = System.IO.File.OpenRead("C:\\Data\\ColorTest.png"))
    using (var reader = new System.IO.BinaryReader(fileStream))
        var png = PNGTools.ReadPNGFile(reader);
        for (int i = png.chunks.Count - 1; i >= 0; i--)
            var c = png.chunks*;*

if (c.type == EPNGChunkType.gAMA ||
c.type == EPNGChunkType.pHYs ||
c.type == EPNGChunkType.sRBG)
using (var mem = new MemoryStream())
PNGTools.WritePNGFile(png, new BinaryWriter(mem));
spriteBytes = mem.ToArray();
Note that this is not guaranteed to work with every kind of png file.
[1]: PNGTools.cs - Pastebin.com
[2]: EncodeToPng() with custom ppi - Questions & Answers - Unity Discussions