7

Note: MCVE at the end

I updated the AppIcon and Document icon for my mac app. The icons have changed but they also show as grayscale. The sources files (PNGs) are colored. The icons appear colored inside Xcode but in context (AppIcon in the dock and Document icons in Finder) are grayscale. I've tried the obvious stuff like cleaning the build folder, relaunching Xcode and relaunching Finder but I don't know what else to try.

This is how the icons appear in context:

Icon in the dock Icon on desktop

This is how the icons appear in Xcode:

Icon in Xcode

All I did was update some PNGs! Everything still functions correctly. The app still launches and double clicking on a document opens the application. The problem is that the icons are gray.


In the git commit that changes the icons, the only things that change are some PNGs and a few documentation files (a link in the README, a screenshot and some other stuff). If I checkout the commit before changing the icons, everything is fine. If I checkout the commit that changes the icons, they're gray.

I've been looking at the PNGs themselves to try to figure this out. I'm using pngcheck to inspect the files.

Old (working) 16x16 icon:

chunk IHDR at offset 0x0000c, length 13
  16 x 16 image, 32-bit RGB+alpha, non-interlaced
chunk bKGD at offset 0x00025, length 6
  red = 0x00ff, green = 0x00ff, blue = 0x00ff
chunk IDAT at offset 0x00037, length 142
  zlib: deflated, 2K window, maximum compression
  row filters (0 none, 1 sub, 2 up, 3 avg, 4 paeth):
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 (16 out of 16)
chunk IEND at offset 0x000d1, length 0

New (broken) 16x16 icon:

chunk IHDR at offset 0x0000c, length 13
  16 x 16 image, 32-bit RGB+alpha, non-interlaced
chunk IDAT at offset 0x00025, length 109
  zlib: deflated, 2K window, default compression
  row filters (0 none, 1 sub, 2 up, 3 avg, 4 paeth):
    0 1 1 1 2 2 4 1 1 4 2 2 1 1 0 0 (16 out of 16)
chunk IEND at offset 0x0009e, length 0

Ok, so maybe the bKGD chunk is required? Nope. I passed the image through gm convert (with no arguments) which adds the bKGD and also changes the compression level and it's still being displayed in grayscale.

New (broken) 16x16 icon after gm convert:

chunk IHDR at offset 0x0000c, length 13
  16 x 16 image, 32-bit RGB+alpha, non-interlaced
chunk bKGD at offset 0x00025, length 6
  red = 0x00ff, green = 0x00ff, blue = 0x00ff
chunk IDAT at offset 0x00037, length 106
  zlib: deflated, 2K window, maximum compression
  row filters (0 none, 1 sub, 2 up, 3 avg, 4 paeth):
    0 1 1 1 2 2 4 1 1 4 2 2 1 1 0 0 (16 out of 16)
chunk IEND at offset 0x000ad, length 0

So now it looks like the only difference between the broken PNGs and the working ones are the row filters (and of course the image data itself). WHY ON EARTH WOULD THIS CAUSE A PROBLEM?! I'm not aware of a way to set the row filters using the command line alone. Is this even possible to do with libpng? Whenever I use libpng, I just let the library choose the filters. Would I have to write my own png encoder to solve this?

I feel like I must be barking up the wrong tree here but the only thing that changed (that affects the build) are a handful of PNG files. This is insane.


I played around with this some more and found that the 128@2x icon (not the 256 icon!) is the one that matters. Replacing this image with the old icon causes the old icon to be shown in color. Replacing this image with the new icon cases the new icon to be shown in grayscale. The difference between these two PNG files is what's causing the issue. This is the most ridiculous problem that I've ever encountered.

I'm trying to bring these images closer together to try to make the new one work or the old one break but I just can't figure it out. I passed the old image through the same tool that I used to make the new image (which happens to be the application for which this icon is for!) AND IT STILL SHOWS UP IN COLOR! Here's the pngcheck output.

Old (working) 128x128@2x icon after animera:

chunk IHDR at offset 0x0000c, length 13
  256 x 256 image, 32-bit RGB+alpha, non-interlaced
chunk IDAT at offset 0x00025, length 965
  zlib: deflated, 32K window, default compression
  row filters (0 none, 1 sub, 2 up, 3 avg, 4 paeth):
    1 2 2 2 2 2 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
    2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 1 2
    2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2
    2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 4 2 2 2
    2 2 2 2 1 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 1 2 2 2 2
    2 2 2 4 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 4 2 2 2 2 2
    2 2 4 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
    2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
    1 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 0
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 (256 out of 256)
chunk IEND at offset 0x003f6, length 0

New (broken) 128x128@2x icon after animera:

chunk IHDR at offset 0x0000c, length 13
  256 x 256 image, 32-bit RGB+alpha, non-interlaced
chunk IDAT at offset 0x00025, length 830
  zlib: deflated, 32K window, default compression
  row filters (0 none, 1 sub, 2 up, 3 avg, 4 paeth):
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 2 2 2 2 2 2
    2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2
    2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
    2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 4 2 2 2
    2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2
    2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 4 2 2 2 2 2
    2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
    2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2
    2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 0
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 (256 out of 256)
chunk IEND at offset 0x0036f, length 0

This is ridiculous! I'm beginning to wonder if I've found a very strange bug in macOS.


In case you want to inspect the images yourself, these are the two images from the previous section. I think imgur re-encodes images so I converted them to data URIs so you can paste them into the URL bar and download them.

Old (working) 128x128@2x icon after animera:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAADxUlEQVR4nO3dQW6kMBRAQRjl/ldmbgALx7KdV7XNIg7debL0DdzPdd3Xi/u6nrefA+f6t3oBwDoCAGECAGECAGECAGECAGECAAAAAAAAAAAAAAAAAAAAAAAbeX0nAOs9h7+X4fYd25onAkGYAECYAECYAECYAECYAECYAECYGe1kp8/xV3OOYC47AAgTAAgTAAgTAAgTAAgTAAgTAAgzYx1kzr+WcwJj7AAgTAAgTAAgTAAgTAAgTAAgTAAgzAz1gzn/2ZwTeGcHAGECAGECAGECAGECAGECAGECAGECAGECAGECAGECAGECAGECAGECAGECAGH5e6Xd799Wf16AHQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCECQCE/axewHTP9axewpDj316//eX/WOB9/Cfwxg4AwgQAwgQAwgQAwgQAwgQAwgQAwvafcU6e428/pR41/An/7Ss0/x9g73MEdgAQJgAQJgAQJgAQJgAQJgAQJgAQtn5Gufn9+lsv7jfcf/svXP8F/7L2nIAdAIQJAIQJAIQJAIQJAIQJAIQJAITNn0FuPuf/cvTir+sa/gv2H6S/Onz51+xzAnYAECYAECYAECYAECYAECYAECYAEPazegG7+xrCnn9O4Gznz/nXsgOAMAGAMAGAMAGAMAGAMAGAMAGAsPEx6uH3+882/+IsvvyTB/Hm/F/GnhdgBwBhAgBhAgBhAgBhAgBhAgBhAgBhngcw2egc+/RDFub4e7MDgDABgDABgDABgDABgDABgDABgDDnADZnjs5MdgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQNv/188/1TP8dvBi8/PO/Iby6p34CdgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQJgAQtv5ub88LWOt2+deae7//FzsACBMACBMACBMACBMACBMACBMACFt/DuCLcwJj7tHP+HH9h6yd83+xA4AwAYAwAYAwAYAwAYAwAYAwAYCwrWeUv+L0cwTDc/zVTj9HsPccf5QdAIQJAIQJAIQJAIQJAIQJAIQJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvPgPQZExnSU6E9oAAAAASUVORK5CYII=

New (broken) 128x128@2x icon after animera:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAADPklEQVR4nO3cwZEDIQwAQbhy/injDO7DQ6amOwJ5dz3FR6wFAAAAAAAAAAAAAAAAAAAA/Jo9PUDdWetMzzBp+wZH/U0PAMwRAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAizi32pvs8/zX0Cd5wAIEwAIEwAIEwAIEwAIEwAIEwAIEwAIEwAIEwAIEwAIEwAIEwAIEwAIEwAICy/S22fv61+n4ATAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIQJAIR9pge4dtaZHmHUnh7g+cd/+QP2+Bu44QQAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYfO7zI/v8z89/Fpr7bd/wfwHfGv2PgEnAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAgTAAi730V+fJ9/2v3DG378l1/Q+/v80+7uE3ACgDABgDABgDABgDABgDABgDABgDABgDABgDABgDABgDABgDABgDABgDABgLDP9AB19uGZ5AQAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYQIAYXt6gHXWmR4hbXv8s/bof9AJAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMIEAMLm7wO4Vb9PYE+/w9N+/sP7/LecACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4B9fjX8anopwslQAAAAASUVORK5CYII=

I really hope someone can figure this out!


I have created a MCVE of this problem (on github). I created a new mac app. All I did was set the 128@2x icon and I'm able to reproduce this problem. The "working" icon is in color. The "broken" icon is gray (see the data URIs from the previous section). You can edit the filename in the Contents.json file (followed by a clean build) to switch between them. It's beginning to seem like I've found a bug in macOS! Although, if it is a bug then it probably won't be fixed in Mojave.


I sent a bug report to Apple. I would still like a workaround for this as I haven't been able to find one.

Indiana Kernick
  • 5,041
  • 2
  • 20
  • 50
  • PNG ok, but what about the .icns file? – Richard Barber Mar 29 '20 at 23:59
  • @RichardBarber `Resources/AppIcon.icns` is in color – Indiana Kernick Mar 30 '20 at 00:00
  • Is it marked as a template image somehow? – Ken Thomases Mar 30 '20 at 02:25
  • @KenThomases I don't know what you mean – Indiana Kernick Mar 30 '20 at 02:25
  • I was wondering if the image had "template" in the name, but I guess not. You might try logging out and back in. Also, copy the app to another Mac, if you have one available, and see if it's grayscale there. – Ken Thomases Mar 30 '20 at 02:40
  • @KenThomases The images don't have "template" in the name. I've restarted my computer and that hasn't resolved the issue. I don't have another mac to test on. – Indiana Kernick Mar 30 '20 at 02:43
  • what is the color space of the PNGs? – Richard Barber Mar 30 '20 at 09:29
  • @RichardBarber It's in the `pngcheck` output: `32-bit RGB+alpha` – Indiana Kernick Mar 30 '20 at 09:30
  • That is the pixel format. The color space can be seen in the Get Info… box. – Richard Barber Mar 30 '20 at 18:44
  • @RichardBarber According to the Get Info box, the color space is RGB and an alpha channel is present. The PNG files don’t store any color space information other than the pixel format so the Get Info box is just putting `32-bit RGB+alpha` into different words. As shown in the `pngcheck` output, none of the PNGs (working or not working) have gAMA, cHRM, sRGB or iCCP chunks. If there was any color space information, `pngcheck` would have mentioned it. – Indiana Kernick Mar 30 '20 at 21:48
  • In converting the color information to a gamma colorspace, then back to 8-bits per RGBA channel, you end up quantizing to zero... so grays. Also you may need to reset your finder icon caches each time you test a new `.icns`. – Richard Barber Mar 31 '20 at 08:13
  • @RichardBarber Neither the working image, nor the broken one have any gamma information associated with them (as shown in the `pngcheck` output). I don't really understand how that could cause an image to be gray anyway. If you look at the grayscale icon, you'll notice that what should be blue is darker than what should be green. The only way that I can see that happening is by something doing the "natural gray" calculation on the RGB color values. Both images use exactly the same set of colors, just a different shape. – Indiana Kernick Mar 31 '20 at 08:20
  • 1
    FWIW I can confirm that the dock and app switcher icon for this app is gray even though the icon in the asset catalog is in color. – matt Apr 03 '20 at 02:11

1 Answers1

5

The problem can be reproduced on macOS Catalina with Xcode 11.3.1.

Narrowing down the problem

The asset catalog (.xcassets) is compiled by Xcode to an Asset.car file.

To take a look into the compiled Asset.car and even extract an image resource from it, you would need an additional tool, e.g. the Asset Catalog Tinkerer.

Installation of this tool can be achieved by:

brew cask install asset-catalog-tinkerer 

With the tool the image can be extracted from the Asset.car. It's named 256_broken_Normal@2x.png and it is a grayscale image.

One can check this visually or with the command line call:

sips -g all 256_broken_Normal@2x.png    

The output of the tool is:

/Users/stephan/tmp/256_broken_Normal@2x.png
  pixelWidth: 256
  pixelHeight: 256
  typeIdentifier: public.png
  format: png
  formatOptions: default
  dpiWidth: 72.000
  dpiHeight: 72.000
  samplesPerPixel: 2
  bitsPerSample: 8
  hasAlpha: yes
  space: Gray
  profile: Generic Gray Gamma 2.2 Profile

So you can see that it is no longer RGBA32 but a grayscale image with alpha channel (see samplesPerPixel, hasAlpha and color space properties).

First Result

So this probably isn't a bug in macOS, but rather the creation of the Asset.car file.

Content Analysis I

Interestingly, you can't fix the problem by removing and re-adding color profiles, nor by loading the image into an image processor and rewriting it.

So could it be the pixel content values of the image?

Next try: change a single pixel in the red area of the image editing software from #0xff0000ff to #0xff0100ff. So this is a change that the human eye should not be able to see visually, but in this case you can see it: the image is no longer grayscale, but colored.

Let's analyze the colors further and interessting: each value in the 4 channels has either the value 0x00 or 0xff which gives 16 different combinations.

Create Random Image

That sounds like a lead. So next try: Create a small program that randomly uses one of the 8 possible opaque colors (all combinations of 0x00 or 0xff per rgb channel, alpha = 0xff).

The outcome is a color image.

Content Analysis II

Let's look at the picture again. Could it be that the colors are evenly distributed?

Now let us analyze with a small program that counts how often the R G B A values 0xff occur, the result is:

   R     G      B      A
212992 212992 212992 540672

It is indeed the case that the R, G and B with the value 0xff occur in exactly the same number.

Hypothesis

To further substantiate this hypothesis, one can now programmatically generate a stripe pattern in which the colors occur equally often.

And indeed, with this image we see a grayscale image in the Dock and in the App Switcher, see in the screenshot the assets in the background and the grayscale app icon when using the App Switcher via CMD+TAB in the foreground.

stripe pattern

Now we make another attempt with a semi-random distribution of small squares of the same color. And again, when using that image in Xcode we get a grayscale app icon, see screenshot.

semi-random image

Test

Whenever changing the image, the first thing you have to do in Xcode is a <Product/Clean Build Folder> (SHIFT-CMD-K).

If one would like to test that on your own, here are the images:

https://www.software7.biz/iertzzlmbkjdkjrk/256_randomWorking.png (working -> color) https://www.software7.biz/iertzzlmbkjdkjrk/256_brokenStripes.png (broken -> grayscale) https://www.software7.biz/iertzzlmbkjdkjrk/256_semiRandomBroken.png (borken -> grayscale)

Summary

So it doesn't seem to be a bug in macOS, but during compilation/optimization of the assets into the Asset.car file. It seems to happen when the number of R,G,B values equal to 0xff is the same. There may be other cases.

A simple workaround seems to be to simply change the value of one pixel very slightly. This cannot be seen by the human eye.

Stephan Schlecht
  • 26,556
  • 1
  • 33
  • 47
  • 2
    Very nice! Have you reported this to Apple? They rely heavily on the "optimization" (as you rightly call it) that takes place during complation to a _.car_, so if there's a bug in that routine, they need to know about it! – matt Apr 04 '20 at 22:55
  • By the way: this reminds me of a somewhat similar bug that arose after they "optimized" graphics contexts. Same thing: if the colors were just so, you wound up with a greyscale image, because they tried to save space by using a greyscale version of the context. – matt Apr 04 '20 at 22:57
  • 2
    Ok I can reproduce this with your 256_broken image, in an iOS app with no code at all. https://github.com/mattneub/greyscaleAssetCatalogBug – matt Apr 04 '20 at 23:00
  • Interesting, I didn't come up with the idea to test this with iOS, but it makes sense to me, the same mechanism is used when developing for iOS – Stephan Schlecht Apr 04 '20 at 23:06
  • 1
    @matt I have also reported this to Apple using the Feedback Assistant. – Stephan Schlecht Apr 04 '20 at 23:26
  • Feel free to include or reference my demo project! – matt Apr 04 '20 at 23:39
  • This is an excellent analysis! I've successfully applied the workaround to my icons. – Indiana Kernick Apr 04 '20 at 23:49