- Home /
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.
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.");
return;
}
//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
Answer by Bunny83 · Jun 19, 2019 at 01:33 AM
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
edit
I just tried using my PNGTools which I wrote for this question over here 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[i];
if (c.type == EPNGChunkType.gAMA ||
c.type == EPNGChunkType.pHYs ||
c.type == EPNGChunkType.sRBG)
{
png.chunks.RemoveAt(i);
}
}
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.
Note that when importing the png actually as asset through the Unity editor the colors are correct. So it seems to be an issue in the runtime png loader.
You absolute legend. I love the "I have a problem so I built a tool to fix it" approach.
I ended up "solving" it at 1am by reading the data in via the System.Drawing.Bitmap class and then using
Texture2D.LoadRawTextureData(byte[] data);Interesting note: The bitmap class seems to load things upside-down but you can then flip it
@bunny83 You saved me with this code, thank you so much. I had to change some things, I switched EPNGChunkType to EChunkType and the gA$$anonymous$$A uint value was different on my files so I changed it to 1766015824. It works perfectly thanks a lot for this tool
Uhm the value you just mentioned is not a gA$$anonymous$$A chunk. It's an iCCP chunk. The value 1766015824 is 0x69434350 in hex which literally represents the ascii characters "iCCP". All PNG chunk headers are 4 ascii characters. The iCCP chunk is an embedded color profile. So yes, it could also cause some color "errors".
hmm then my file did not have a gA$$anonymous$$A chunk. Your tool detected 6 chunks and 5 of them were in your enum EChunkType and the one that didnt print was gA$$anonymous$$A, your tool told me the id of the one unnacouted for was 1766015824 so i replaced it on gA$$anonymous$$A and it fixed the problem. Before I replaced that value the colors were still returning wrong so that chunk was definitely causing the problem.(maybe in conjunction with phys and srgb). Your tool is amazing by the way! Thanks again
Thank you so much! you saved me with this, the logic behind was wrong was way over my head, but luckily your fix solved it! :)
Your answer
Follow this Question
Related Questions
2D sprite renders a small part of another one with "Sprite Mode : Multiple" 0 Answers
"Could not create sprite" exception - copying and manipulating texture from SpriteAtlas 1 Answer
iOS Sprites in PVRTC? 1 Answer
Difference between Sprite and Default texture types? 0 Answers
White Edges on Sprites Loaded from PNG at Run-time 0 Answers