Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce size of app icon file #169

Closed
lwouis opened this issue Mar 10, 2020 · 6 comments
Closed

Reduce size of app icon file #169

lwouis opened this issue Mar 10, 2020 · 6 comments
Labels
need breakthrough Need a breakthrough idea to move forwards performance

Comments

@lwouis
Copy link
Owner

lwouis commented Mar 10, 2020

The app icon is a 1024x1024 export from Figma. It weights 562kB. Yet, after I used apple's iconutil --convert icns resources/icons/app-icon.iconset to generate the icns file, it weights 859kB.

There may be a way to avoid this unjustified bloating.

@lwouis lwouis added enhancement New feature or request performance labels Mar 10, 2020
@lwouis
Copy link
Owner Author

lwouis commented Mar 10, 2020

I noticed that the Autoupdate.app file embedded into the Sparkle framework is only 36kB even though it has multiple png within, one of them being 512x512. Looking at their repo, I found this very interesting Makefile.

@akx
Copy link
Contributor

akx commented Mar 30, 2020

Running pngquant (as in the linked makefile) even on the lightest, gentlest settings leads to some unfortunate artifacts on our icon:

Screenshot 2020-03-30 at 10 09 07

OxiPNG (via ImageOptim) has lossless savings of 800kb -> 580kb, but I'm quite unable to get a ICNS smaller than the current one. Speaking of which, how was the .icns file generated in the first place though? I see no script (nor the original PNG file, but it was easy to extract from the icns) in the repo.

@lwouis
Copy link
Owner Author

lwouis commented Mar 31, 2020

@akx I'm very happy to see a fellow performance enthusiast! Too often people add megabytes and bloat apps for no reason. Let me try to explain the icon situation a bit more. There is some behind the scenes that's not documented anywhere, unfortunately.

SVG-world and Figma

The original icon was designed by @fturcheti in Figma. Here lies the first issue: Figma uses some advanced SVG features that have wildly varying implementation in popular engines. Me and you tried most of the popular engines: Inkscape, Preview.app, imagemagick, graphicsmagick, librsvg, sharp, etc. In the end, only the export from Figma doesn't deteriorate the image. Thus it is my starting point. I exported the PNG directly from Figma.

Which icon sizes to ship

I originally follow the XCode assets catalog template, and shipped a 1024x1024 image. 2 things to note here:

  • I didn't ship smaller versions of the image, as I believe it is a waste of resources if all I do is downscale the biggest image. The OS will downscale on its own at runtime in that case. It is fast, and avoid shipping more unnecessary bytes. I think the intent of the multi-sizes asset catalog is that you manually crafts different icons at different sizes. Especially at small sizes, you are supposed to change the proportions and level-of-details of the icons, to help readability. I found online though that most people don't think twice and just render at the biggest size, then downscale all variants. I don't think it is the way to go.
  • Later I realized that 1024x1024 was intended for the AppStore. I thought since there is no way AltTab ever gets into the AppStore as it is using private APIs and SPIs, then I could skip that resolution and only use 512x512. Later I found out that apparently on retina screens, 1024x1024 can be displayed. This stuff is not properly documented, and the trade-off of saving size and having that 1 user with fullscreen icons in the finder find the icon blurry was tempting, but I played it safe and shipped 1024x1024.

Assets catalog (i.e. .xcassets)

This technology from Apple is really designed for their ecosystem, especially the AppStore. If you ship on the AppStore, than you get App thinning, App slicing, Bitcode, etc. This is possible because in the assets catalog, you document sizes and target hardware, and the AppStore can thin the payload depending on which user/hardware is asking for the download. Since we don't use the AppStore, none of this has benefits for us. It actually has downsides:

How I made the current .icns

I just ran iconutil on a local folder where I had put the 1024x1024 Figma PNG export. Something like this, without the 512x512 I believe:

image

Before that, I ran a pass of ImageOptim first to compress the PNG. I played like you with lossful/lossless, and went with lossless at 562kb which seems to match your findings.

Going forwards

I think going forward on this ticket, it would be nice to find a way to generate the .icns without bloating its size. .icns I learned, is an Apple format. There exists other format with the .icns extension, but to run on the mac, we need to follow that specific Apple format. The iconutil tools follows it, but there are other more obscure tools that seem to have retro-engineered the format, and may be able to provide more efficient conversion. I would like to experiment more with this one and others.

The big picture

When optimizing performance, it's important to look at the big picture. I opened this ticket to revisit this topic later, but I realized that the payload is mostly wasting space with other concerns:

image

The main dead-weight is coming from using Swift. Swift actually requires to embed a runtime as until Catalina, Apple was not shipping that runtime with macOS, as it was its ABI was not stable I believe. This means that we get punished for using the new language, which sucks. The same code in C/C++/objc would ship an app 15MB smaller. I would like in the future to be able to offer alternate downloads for Catalina+ users, without the swift runtime (see #158). This is essentially doing App thinning in a static way, on our own, outside the AppStore.

The second target for size reduction is the SF Pro font. Here I would like to use some tool to trim the font to keep only the characters we use (see #168):

image

Other things I explored:

  • .plist files: I switch them from text to binary. It is a more space efficient encoding, and it's a simple XCode config flag.
  • Localization files: they use a specific 16-bit Unicode encoding. There is an XCode config flag STRINGS_FILE_OUTPUT_ENCODING to change that, but I didn't notice any size improvement
  • Removing whitespace / trimming text files. I wrote some scripts to minify all whitespace from xml, plists, strings, etc. It is not worth the effort as the gains are minimal and it adds complexity and potential failure points. A big target I had here were code signature. These files are around 15kB each for some reason.

Hope this shed some light on the payload situation! :)

@fturcheti
Copy link

fturcheti commented Mar 31, 2020

Hello, @lwouis. Seeing this outcome, it seems that choosing Figma as the design tool to generate the icon was a poor decision.

If the icon was created in Inkscape, would these results be better? If the answer is yes, something to consider is remaking the icon in Inkscape. It could be an easier fix for this situation.

Also, removing some details from the design or trying to recreate them in other ways, like that yellow light that seems to cause a lot of problems in these conversions, should be considered too. Good design should be beautiful, but it should work too.

@lwouis
Copy link
Owner Author

lwouis commented Apr 1, 2020

it seems that choosing Figma as the design tool to generate the icon was a poor decision

I wouldn't say that. The issue with Figma is simple: it's not free software. It creates a barrier of entry for contributors to come and edit the icon in the future. I tried to mitigate that by exporting the SVG and storing it on github here. However, I soon realized that SVG is an untamed beast with poor interoperability. This means that there is lock-in on Figma, and people can't use free tools such as Inkscape.

We could recreate/fix the SVG in another free tool. I think it would clearly be an improvement since then we would escape Figma's paid lock-in. We would probably be dependent on the specific tool though, as interop is not very good with SVG, so I guess we should agree beforehand on which tool.

That being said, this current ticket is about technical file size optimizations. It's not directly connected to the above. It would help because then I could document/script the process from SVG to PNG/ICNS since I wouldn't rely on a step that's "I went on Figma, and clicked the export button" which obviously can't be scripted and requires a paid account.

So in conclusion, it would be good to port the existing SVG to a free tool. I think the port may be easy as only the "yellow light" as you mention seems to be the issue. That would let me script/document the SVG -> ICNS process, which would help other people such as @akx understand the current situation, and thus improve it

@akx
Copy link
Contributor

akx commented Apr 2, 2020

Seeing this outcome, it seems that choosing Figma as the design tool to generate the icon was a poor decision.

I also agree with @lwouis and wouldn't say that! SVG is a fickle beast, especially when it veers into raster territory such as here, where we're using raster blend modes for blurred and gradated shapes, and we end up with a situation where different SVG rasterizers have different views on how to render things.

Although it's suboptimal that we have a source SVG that can't currently be reliably rendered except with proprietary software (although I'm sure given enough effort someone might get to the bottom of this – heck, might be worth looping in some of the Inkscape or Batik folks since they're more informed on all things SVG, to see if they can figure out where libre renderers get things differently), it's not an end-of-the-world issue. In the end, I bet a lot of software only has their logos in raster format to begin with, so having an SVG that's not perfect is already an improvement!

Also, macOS doesn't use vector icons (I think Haiku is the only current OS that does?), so just having a very-large-indeed (e.g. 8192x8192?) PNG of it in the repository that we can use as the master file for derivatives is enough in my opinion.

The issue here is to have a reliable pipeline that takes that huge PNG and squashes it into an .icns that's small and works for our use cases.

@akx akx mentioned this issue Apr 3, 2020
@lwouis lwouis removed the enhancement New feature or request label Jun 10, 2020
@lwouis lwouis added the need breakthrough Need a breakthrough idea to move forwards label Jul 22, 2020
@lwouis lwouis closed this as completed in bb49302 Aug 6, 2020
lwouis pushed a commit that referenced this issue Aug 6, 2020
# [4.15.0](v4.14.0...v4.15.0) (2020-08-06)

### Features

* add new colorful menubar icon ([8f5c2a0](8f5c2a0))

### Performance Improvements

* reduce size of app icon (closes [#169](#169)) ([bb49302](bb49302))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
need breakthrough Need a breakthrough idea to move forwards performance
Projects
None yet
Development

No branches or pull requests

3 participants